Skip to content

Commit

Permalink
Merge aa4ef2f into 38f2998
Browse files Browse the repository at this point in the history
  • Loading branch information
mfelsche committed Nov 11, 2015
2 parents 38f2998 + aa4ef2f commit 5c80582
Show file tree
Hide file tree
Showing 36 changed files with 692 additions and 315 deletions.
31 changes: 15 additions & 16 deletions core/src/main/java/io/crate/geo/GeoJSONUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -204,25 +204,24 @@ public void accept(Object input) {
}

private static void validateCoordinate(Object coordinate) {
double x;
double y;
if (coordinate.getClass().isArray()) {
Preconditions.checkArgument(Array.getLength(coordinate) == 2, invalidGeoJSON("invalid coordinate"));
x = ((Number)Array.get(coordinate, 0)).doubleValue();
y = ((Number)Array.get(coordinate, 1)).doubleValue();
} else if (coordinate instanceof Collection) {
Preconditions.checkArgument(((Collection) coordinate).size() == 2, invalidGeoJSON("invalid coordinate"));
Iterator iter = ((Collection) coordinate).iterator();
x = ((Number)iter.next()).doubleValue();
y = ((Number)iter.next()).doubleValue();
} else {
throw new IllegalArgumentException(invalidGeoJSON("invalid coordinate"));
}

try {
double x;
double y;
if (coordinate.getClass().isArray()) {
Preconditions.checkArgument(Array.getLength(coordinate) == 2, invalidGeoJSON("invalid coordinate"));
x = ((Number)Array.get(coordinate, 0)).doubleValue();
y = ((Number)Array.get(coordinate, 1)).doubleValue();
} else if (coordinate instanceof Collection) {
Preconditions.checkArgument(((Collection) coordinate).size() == 2, invalidGeoJSON("invalid coordinate"));
Iterator iter = ((Collection) coordinate).iterator();
x = ((Number)iter.next()).doubleValue();
y = ((Number)iter.next()).doubleValue();
} else {
throw new IllegalArgumentException(invalidGeoJSON("invalid coordinate"));
}
JtsSpatialContext.GEO.verifyX(x);
JtsSpatialContext.GEO.verifyY(y);
} catch (InvalidShapeException e) {
} catch (InvalidShapeException|ClassCastException e) {
throw new IllegalArgumentException(invalidGeoJSON("invalid coordinate"), e);
}
}
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/io/crate/types/DataTypes.java
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ public DataType<?> create(DataType innerType) {

private static final Set<DataType> NUMBER_CONVERSIONS = ImmutableSet.<DataType>builder()
.addAll(NUMERIC_PRIMITIVE_TYPES)
.add(BOOLEAN)
.add(STRING, TIMESTAMP, IP)
.build();
// allowed conversion from key to one of the value types
Expand All @@ -143,6 +144,7 @@ public DataType<?> create(DataType innerType) {
.addAll(NUMBER_CONVERSIONS)
.add(GEO_SHAPE)
.add(GEO_POINT)
.add(BOOLEAN)
.build())
.put(IP.id(), ImmutableSet.<DataType>of(STRING))
.put(TIMESTAMP.id(), ImmutableSet.<DataType>of(LONG))
Expand Down
29 changes: 29 additions & 0 deletions core/src/test/java/io/crate/geo/GeoJSONUtilsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,35 @@ public void testValidateInvalidCoordinates() throws Exception {
GeoJSONUtils.COORDINATES_FIELD, "ABC"
)
);
}

@Test
public void testInvalidNestedCoordinates() throws Exception {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Invalid GeoJSON: invalid coordinate");
GeoJSONUtils.validateGeoJson(
ImmutableMap.of(
GeoJSONUtils.TYPE_FIELD, GeoJSONUtils.POINT,
GeoJSONUtils.COORDINATES_FIELD, new double[][] {
{0.0, 1.0},
{1.0, 0.0}
}
)
);
}

@Test
public void testInvalidDepthNestedCoordinates() throws Exception {
expectedException.expect(IllegalArgumentException.class);
expectedException.expectMessage("Invalid GeoJSON: invalid coordinate");
GeoJSONUtils.validateGeoJson(
ImmutableMap.of(
GeoJSONUtils.TYPE_FIELD, GeoJSONUtils.POLYGON,
GeoJSONUtils.COORDINATES_FIELD, new double[][] {
{0.0, 1.0},
{1.0, 0.0}
}
)
);
}
}
19 changes: 18 additions & 1 deletion sql/src/main/java/io/crate/analyze/symbol/SymbolFormatter.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.apache.lucene.util.BytesRef;

import javax.annotation.Nullable;
import java.lang.reflect.Array;
import java.util.List;
import java.util.Locale;
import java.util.Map;
Expand Down Expand Up @@ -129,8 +130,10 @@ private StringBuilder formatValue(Object value, StringBuilder builder) {
formatMap((Map<String, Object>) value, builder);
} else if (value instanceof List) {
formatList((List<?>) value, builder);
} else if (value instanceof Object[]) {
formatList(ImmutableList.copyOf((Object[])value), builder);
} else if (value.getClass().isArray()) {
formatList(ImmutableList.copyOf((Object[]) value), builder);
formatArray(value, builder);
} else if (value instanceof CharSequence || value instanceof Character) {
builder.append("'").append(value.toString()).append("'");
} else if (value instanceof BytesRef) {
Expand All @@ -156,6 +159,20 @@ private void formatMap(Map<String, Object> map, StringBuilder builder) {
builder.append("}");
}

private void formatArray(Object array, StringBuilder builder) {
builder.append('[');
boolean first = true;
for (int i = 0, length=Array.getLength(array); i < length; i++) {
if (!first) {
builder.append(", ");
} else {
first = false;
}
formatValue(Array.get(array, i), builder);
}
builder.append(']');
}

private void formatList(List<?> value, StringBuilder builder) {
builder.append('[');
boolean first = true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import io.crate.metadata.FunctionImplementation;
import io.crate.operation.scalar.arithmetic.*;
import io.crate.operation.scalar.cast.ToArrayFunction;
import io.crate.operation.scalar.cast.ToGeoFunction;
import io.crate.operation.scalar.cast.ToPrimitiveFunction;
import io.crate.operation.scalar.cast.TryCastScalarFunction;
import io.crate.operation.scalar.geo.DistanceFunction;
Expand Down Expand Up @@ -88,6 +89,7 @@ protected void configure() {
DateFormatFunction.register(this);
ToPrimitiveFunction.register(this);
ToArrayFunction.register(this);
ToGeoFunction.register(this);
TryCastScalarFunction.register(this);

ConcatFunction.register(this);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Licensed to Crate under one or more contributor license agreements.
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership. Crate licenses this file
* to you under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may
* obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
* implied. See the License for the specific language governing
* permissions and limitations under the License.
*
* However, if you have executed another commercial license agreement
* with Crate these terms will supersede the license and you may use the
* software solely pursuant to the terms of the relevant commercial
* agreement.
*/

package io.crate.operation.scalar.cast;

import com.google.common.base.Preconditions;
import com.google.common.base.Throwables;
import io.crate.analyze.symbol.Function;
import io.crate.analyze.symbol.Literal;
import io.crate.analyze.symbol.Symbol;
import io.crate.analyze.symbol.SymbolFormatter;
import io.crate.exceptions.ConversionException;
import io.crate.metadata.DynamicFunctionResolver;
import io.crate.metadata.FunctionImplementation;
import io.crate.metadata.FunctionInfo;
import io.crate.metadata.Scalar;
import io.crate.operation.Input;
import io.crate.types.DataType;

import java.util.List;
import java.util.Locale;

public abstract class AbstractCastFunction<From, To> extends Scalar<To,From> {

protected final DataType<To> returnType;
protected final FunctionInfo info;

protected AbstractCastFunction(FunctionInfo info) {
this.info = info;
this.returnType = info.returnType();
}

@Override
public To evaluate(Input<From>... args) {
assert args.length == 1 : "Number of arguments must be 1";
From value = args[0].value();
try {
return returnType.value(value);
} catch (ClassCastException | IllegalArgumentException | ConversionException e) {
return onEvaluateException(value, e);
}
}

@Override
public FunctionInfo info() {
return info;
}

@Override
public Symbol normalizeSymbol(Function symbol) {
assert symbol.arguments().size() == 1 : "Number of arguments must be 1";
Symbol argument = symbol.arguments().get(0);
if (argument.symbolType().isValueSymbol()) {
Object value = ((Input) argument).value();
try {
return Literal.newLiteral(returnType, returnType.value(value));
} catch (ClassCastException | IllegalArgumentException | ConversionException e) {
return onNormalizeException(argument, e);
}
}
return symbol;
}

protected Symbol onNormalizeException(Symbol argument, Throwable t) {
throw new IllegalArgumentException(
String.format(Locale.ENGLISH, "cannot cast %s to %s",
SymbolFormatter.format(argument), returnType), t);
}

protected To onEvaluateException(From argument, Throwable t) {
Throwables.propagate(t);
return null;
}

protected abstract static class Resolver implements DynamicFunctionResolver {

protected final String name;
protected final DataType dataType;

protected Resolver(DataType dataType, String name) {
this.name = name;
this.dataType = dataType;
}

protected void checkPreconditions(List<DataType> dataTypes) {
Preconditions.checkArgument(dataTypes.size() == 1,
"invalid size of arguments, 1 expected");
DataType convertFrom = dataTypes.get(0);
Preconditions.checkArgument(convertFrom.isConvertableTo(dataType), "type '%s' not supported for conversion to '%s'", convertFrom, dataType);
}

protected abstract FunctionImplementation<Function> createInstance(List<DataType> types);

@Override
public FunctionImplementation<Function> getForTypes(List<DataType> dataTypes) throws IllegalArgumentException {
checkPreconditions(dataTypes);
return createInstance(dataTypes);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ public static class FunctionNames {
public static final String TO_FLOAT_ARRAY = "toFloatArray";
public static final String TO_SHORT_ARRAY = "toShortArray";
public static final String TO_IP_ARRAY = "toIpArray";
public static final String TO_GEO_POINT = "toGeoPoint";
public static final String TO_GEO_SHAPE = "toGeoShape";
}

static final ImmutableMap<DataType, String> primitiveFunctionMap = new ImmutableMap.Builder<DataType, String>()
Expand All @@ -74,6 +76,11 @@ public static class FunctionNames {
.put(DataTypes.IP, FunctionNames.TO_IP)
.build();

static final ImmutableMap<DataType, String> geoFunctionMap = new ImmutableMap.Builder<DataType, String>()
.put(DataTypes.GEO_POINT, FunctionNames.TO_GEO_POINT)
.put(DataTypes.GEO_SHAPE, FunctionNames.TO_GEO_SHAPE)
.build();

static final ImmutableMap<DataType, String> arrayFunctionMap = new ImmutableMap.Builder<DataType, String>()
.put(new ArrayType(DataTypes.STRING), FunctionNames.TO_STRING_ARRAY)
.put(new ArrayType(DataTypes.LONG), FunctionNames.TO_LONG_ARRAY)
Expand All @@ -89,6 +96,7 @@ public static class FunctionNames {
// TODO: register all type conversion functions here
private static final ImmutableMap<DataType, String> functionMap = new ImmutableMap.Builder<DataType, String>()
.putAll(primitiveFunctionMap)
.putAll(geoFunctionMap)
.putAll(arrayFunctionMap)
.build();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,83 +21,37 @@

package io.crate.operation.scalar.cast;

import com.google.common.base.Preconditions;
import io.crate.analyze.symbol.Function;
import io.crate.analyze.symbol.Literal;
import io.crate.analyze.symbol.Symbol;
import io.crate.metadata.*;
import io.crate.operation.Input;
import io.crate.metadata.FunctionIdent;
import io.crate.metadata.FunctionImplementation;
import io.crate.metadata.FunctionInfo;
import io.crate.operation.scalar.ScalarFunctionModule;
import io.crate.types.ArrayType;
import io.crate.types.DataType;
import io.crate.types.DataTypes;

import java.util.List;
import java.util.Locale;
import java.util.Map;

public class ToArrayFunction extends Scalar<Object[], Object> {

private final DataType returnType;
private final FunctionInfo info;
public class ToArrayFunction extends AbstractCastFunction<Object, Object[]> {

private ToArrayFunction(FunctionInfo functionInfo) {
this.returnType = functionInfo.returnType();
this.info = functionInfo;
super(functionInfo);
}

public static void register(ScalarFunctionModule module) {
for (Map.Entry<DataType, String> function : CastFunctionResolver.arrayFunctionMap.entrySet()) {
module.register(function.getValue(), new Resolver(function.getKey(), function.getValue()));
module.register(function.getValue(), new ArrayResolver(function.getKey(), function.getValue()));
}
}

private static class Resolver implements DynamicFunctionResolver {

private final String name;
private final DataType dataType;
private static class ArrayResolver extends Resolver {

protected Resolver(DataType dataType, String name) {
this.name = name;
this.dataType = dataType;
protected ArrayResolver(DataType dataType, String name) {
super(dataType, name);
}

@Override
public FunctionImplementation<Function> getForTypes(List<DataType> dataTypes) throws IllegalArgumentException {
ToArrayFunction.checkPreconditions(dataTypes);
protected FunctionImplementation<Function> createInstance(List<DataType> dataTypes) {
return new ToArrayFunction(new FunctionInfo(new FunctionIdent(name, dataTypes), dataType));
}
}

protected static void checkPreconditions(List<DataType> dataTypes) throws IllegalArgumentException {
Preconditions.checkArgument(dataTypes.size() == 1, "Invalid number of arguments");
Preconditions.checkArgument(dataTypes.get(0) instanceof ArrayType, "Argument must be an array type");
ArrayType arrayType = (ArrayType) dataTypes.get(0);
Preconditions.checkArgument(DataTypes.PRIMITIVE_TYPES.contains(arrayType.innerType()),
String.format(Locale.ENGLISH, "Array inner type '%s' not supported for conversion",
arrayType.innerType().getName()));
}

@Override
public FunctionInfo info() {
return info;
}

@Override
public Symbol normalizeSymbol(Function symbol) {
final int size = symbol.arguments().size();
assert size == 1 : "Invalid number of arguments";

if (anyNonLiterals(symbol.arguments())) {
return symbol;
}
final Object inputValue = ((Input) symbol.arguments().get(0)).value();
return Literal.newLiteral(returnType, returnType.value(inputValue));
}

@Override
public Object[] evaluate(Input[] args) {
assert args.length == 1 : "Number of arguments must be 1";
return (Object[]) returnType.value(args[0].value());
}
}
Loading

0 comments on commit 5c80582

Please sign in to comment.