private int repeatInternal( @NotNull PseudocodeImpl originalPseudocode, @Nullable Label startLabel, @Nullable Label finishLabel, int labelCount) { Integer startIndex = startLabel != null ? ((PseudocodeLabel) startLabel).getTargetInstructionIndex() : Integer.valueOf(0); assert startIndex != null; Integer finishIndex = finishLabel != null ? ((PseudocodeLabel) finishLabel).getTargetInstructionIndex() : Integer.valueOf(originalPseudocode.mutableInstructionList.size()); assert finishIndex != null; Map<Label, Label> originalToCopy = Maps.newLinkedHashMap(); Multimap<Instruction, Label> originalLabelsForInstruction = HashMultimap.create(); for (PseudocodeLabel label : originalPseudocode.labels) { Integer index = label.getTargetInstructionIndex(); if (index == null) continue; // label is not bounded yet if (label == startLabel || label == finishLabel) continue; if (startIndex <= index && index <= finishIndex) { originalToCopy.put(label, label.copy(labelCount++)); originalLabelsForInstruction.put(getJumpTarget(label), label); } } for (Label label : originalToCopy.values()) { labels.add((PseudocodeLabel) label); } for (int index = startIndex; index < finishIndex; index++) { Instruction originalInstruction = originalPseudocode.mutableInstructionList.get(index); repeatLabelsBindingForInstruction( originalInstruction, originalToCopy, originalLabelsForInstruction); Instruction copy = copyInstruction(originalInstruction, originalToCopy); addInstruction(copy); if (originalInstruction == originalPseudocode.errorInstruction && copy instanceof SubroutineExitInstruction) { errorInstruction = (SubroutineExitInstruction) copy; } if (originalInstruction == originalPseudocode.exitInstruction && copy instanceof SubroutineExitInstruction) { exitInstruction = (SubroutineExitInstruction) copy; } if (originalInstruction == originalPseudocode.sinkInstruction && copy instanceof SubroutineSinkInstruction) { sinkInstruction = (SubroutineSinkInstruction) copy; } } if (finishIndex < mutableInstructionList.size()) { repeatLabelsBindingForInstruction( originalPseudocode.mutableInstructionList.get(finishIndex), originalToCopy, originalLabelsForInstruction); } return labelCount; }
private void init( PName name, PTableType type, long timeStamp, long sequenceNumber, String pkName, List<PColumn> columns, PTableStats stats) { this.name = name; this.type = type; this.timeStamp = timeStamp; this.sequenceNumber = sequenceNumber; this.columnsByName = ArrayListMultimap.create(columns.size(), 1); this.pkName = pkName; List<PColumn> pkColumns = Lists.newArrayListWithExpectedSize(columns.size() - 1); PColumn[] allColumns = new PColumn[columns.size()]; RowKeySchemaBuilder builder = new RowKeySchemaBuilder(); for (int i = 0; i < allColumns.length; i++) { PColumn column = columns.get(i); allColumns[column.getPosition()] = column; PName familyName = column.getFamilyName(); if (familyName == null) { pkColumns.add(column); builder.addField(column); } columnsByName.put(column.getName().getString(), column); } this.pkColumns = ImmutableList.copyOf(pkColumns); this.rowKeySchema = builder.setMinNullable(pkColumns.size()).build(); this.allColumns = ImmutableList.copyOf(allColumns); // Two pass so that column order in column families matches overall column order // and to ensure that column family order is constant int maxExpectedSize = allColumns.length - pkColumns.size(); // Maintain iteration order so that column families are ordered as they are listed Map<PName, List<PColumn>> familyMap = Maps.newLinkedHashMap(); for (PColumn column : allColumns) { PName familyName = column.getFamilyName(); if (familyName != null) { List<PColumn> columnsInFamily = familyMap.get(familyName); if (columnsInFamily == null) { columnsInFamily = Lists.newArrayListWithExpectedSize(maxExpectedSize); familyMap.put(familyName, columnsInFamily); } columnsInFamily.add(column); } } Iterator<Map.Entry<PName, List<PColumn>>> iterator = familyMap.entrySet().iterator(); PColumnFamily[] families = new PColumnFamily[familyMap.size()]; ImmutableMap.Builder<String, PColumnFamily> familyByString = ImmutableMap.builder(); ImmutableSortedMap.Builder<byte[], PColumnFamily> familyByBytes = ImmutableSortedMap.orderedBy(Bytes.BYTES_COMPARATOR); for (int i = 0; i < families.length; i++) { Map.Entry<PName, List<PColumn>> entry = iterator.next(); PColumnFamily family = new PColumnFamilyImpl(entry.getKey(), entry.getValue()); families[i] = family; familyByString.put(family.getName().getString(), family); familyByBytes.put(family.getName().getBytes(), family); } this.families = ImmutableList.copyOf(families); this.familyByBytes = familyByBytes.build(); this.familyByString = familyByString.build(); this.stats = stats; }
public MutationState createTable(CreateTableStatement statement, byte[][] splits) throws SQLException { PTableType tableType = statement.getTableType(); boolean isView = tableType == PTableType.VIEW; if (isView && !statement.getProps().isEmpty()) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.VIEW_WITH_TABLE_CONFIG) .build() .buildException(); } connection.rollback(); boolean wasAutoCommit = connection.getAutoCommit(); try { connection.setAutoCommit(false); TableName tableNameNode = statement.getTableName(); String schemaName = tableNameNode.getSchemaName(); String tableName = tableNameNode.getTableName(); PrimaryKeyConstraint pkConstraint = statement.getPrimaryKeyConstraint(); String pkName = null; Set<String> pkColumns = Collections.<String>emptySet(); Iterator<String> pkColumnsIterator = Iterators.emptyIterator(); if (pkConstraint != null) { pkColumns = pkConstraint.getColumnNames(); pkColumnsIterator = pkColumns.iterator(); pkName = pkConstraint.getName(); } List<ColumnDef> colDefs = statement.getColumnDefs(); List<PColumn> columns = Lists.newArrayListWithExpectedSize(colDefs.size()); PreparedStatement colUpsert = connection.prepareStatement(INSERT_COLUMN); int columnOrdinal = 0; Map<String, PName> familyNames = Maps.newLinkedHashMap(); boolean isPK = false; for (ColumnDef colDef : colDefs) { if (colDef.isPK()) { if (isPK) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.PRIMARY_KEY_ALREADY_EXISTS) .setColumnName(colDef.getColumnDefName().getColumnName().getName()) .build() .buildException(); } isPK = true; } PColumn column = newColumn(columnOrdinal++, colDef, pkConstraint); if (SchemaUtil.isPKColumn(column)) { // TODO: remove this constraint? if (!pkColumns.isEmpty() && !column.getName().getString().equals(pkColumnsIterator.next())) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.PRIMARY_KEY_OUT_OF_ORDER) .setSchemaName(schemaName) .setTableName(tableName) .setColumnName(column.getName().getString()) .build() .buildException(); } } columns.add(column); if (colDef.getDataType() == PDataType.BINARY && colDefs.size() > 1) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.BINARY_IN_ROW_KEY) .setSchemaName(schemaName) .setTableName(tableName) .setColumnName(column.getName().getString()) .build() .buildException(); } if (column.getFamilyName() != null) { familyNames.put(column.getFamilyName().getString(), column.getFamilyName()); } } if (!isPK && pkColumns.isEmpty()) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.PRIMARY_KEY_MISSING) .setSchemaName(schemaName) .setTableName(tableName) .build() .buildException(); } List<Pair<byte[], Map<String, Object>>> familyPropList = Lists.newArrayListWithExpectedSize(familyNames.size()); Map<String, Object> commonFamilyProps = Collections.emptyMap(); Map<String, Object> tableProps = Collections.emptyMap(); if (!statement.getProps().isEmpty()) { if (statement.isView()) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.VIEW_WITH_PROPERTIES) .build() .buildException(); } for (String familyName : statement.getProps().keySet()) { if (!familyName.equals(QueryConstants.ALL_FAMILY_PROPERTIES_KEY)) { if (familyNames.get(familyName) == null) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.PROPERTIES_FOR_FAMILY) .setFamilyName(familyName) .build() .buildException(); } } } commonFamilyProps = Maps.newHashMapWithExpectedSize(statement.getProps().size()); tableProps = Maps.newHashMapWithExpectedSize(statement.getProps().size()); Collection<Pair<String, Object>> props = statement.getProps().get(QueryConstants.ALL_FAMILY_PROPERTIES_KEY); // Somewhat hacky way of determining if property is for HColumnDescriptor or // HTableDescriptor HColumnDescriptor defaultDescriptor = new HColumnDescriptor(QueryConstants.DEFAULT_COLUMN_FAMILY_BYTES); for (Pair<String, Object> prop : props) { if (defaultDescriptor.getValue(prop.getFirst()) != null) { commonFamilyProps.put(prop.getFirst(), prop.getSecond()); } else { tableProps.put(prop.getFirst(), prop.getSecond()); } } } for (PName familyName : familyNames.values()) { Collection<Pair<String, Object>> props = statement.getProps().get(familyName.getString()); if (props.isEmpty()) { familyPropList.add( new Pair<byte[], Map<String, Object>>(familyName.getBytes(), commonFamilyProps)); } else { Map<String, Object> combinedFamilyProps = Maps.newHashMapWithExpectedSize(props.size() + commonFamilyProps.size()); combinedFamilyProps.putAll(commonFamilyProps); for (Pair<String, Object> prop : props) { combinedFamilyProps.put(prop.getFirst(), prop.getSecond()); } familyPropList.add( new Pair<byte[], Map<String, Object>>(familyName.getBytes(), combinedFamilyProps)); } } // Bootstrapping for our SYSTEM.TABLE that creates itself before it exists if (tableType == PTableType.SYSTEM) { PTable table = new PTableImpl( new PNameImpl(tableName), tableType, MetaDataProtocol.MIN_TABLE_TIMESTAMP, 0, QueryConstants.SYSTEM_TABLE_PK_NAME, null, columns); connection.addTable(schemaName, table); } for (PColumn column : columns) { addColumnMutation(schemaName, tableName, column, colUpsert); } Integer saltBucketNum = (Integer) tableProps.remove(PhoenixDatabaseMetaData.SALT_BUCKETS); if (saltBucketNum != null && (saltBucketNum <= 0 || saltBucketNum > SaltingUtil.MAX_BUCKET_NUM)) { throw new SQLExceptionInfo.Builder(SQLExceptionCode.INVALID_BUCKET_NUM) .build() .buildException(); } PreparedStatement tableUpsert = connection.prepareStatement(CREATE_TABLE); tableUpsert.setString(1, schemaName); tableUpsert.setString(2, tableName); tableUpsert.setString(3, tableType.getSerializedValue()); tableUpsert.setInt(4, 0); tableUpsert.setInt(5, columnOrdinal); if (saltBucketNum != null) { tableUpsert.setInt(6, saltBucketNum); } else { tableUpsert.setNull(6, Types.INTEGER); } tableUpsert.setString(7, pkName); tableUpsert.execute(); final List<Mutation> tableMetaData = connection.getMutationState().toMutations(); connection.rollback(); MetaDataMutationResult result = connection .getQueryServices() .createTable(tableMetaData, isView, tableProps, familyPropList, splits); MutationCode code = result.getMutationCode(); switch (code) { case TABLE_ALREADY_EXISTS: connection.addTable(schemaName, result.getTable()); if (!statement.ifNotExists()) { throw new TableAlreadyExistsException(schemaName, tableName); } break; case NEWER_TABLE_FOUND: // TODO: add table if in result? throw new NewerTableAlreadyExistsException(schemaName, tableName); case UNALLOWED_TABLE_MUTATION: throw new SQLExceptionInfo.Builder(SQLExceptionCode.CANNOT_MUTATE_TABLE) .setSchemaName(schemaName) .setTableName(tableName) .build() .buildException(); default: PTable table = new PTableImpl( new PNameImpl(tableName), tableType, result.getMutationTime(), 0, pkName, saltBucketNum, columns); connection.addTable(schemaName, table); if (tableType == PTableType.USER) { connection.setAutoCommit(true); // Delete everything in the column. You'll still be able to do queries at earlier // timestamps Long scn = connection.getSCN(); long ts = (scn == null ? result.getMutationTime() : scn); PSchema schema = new PSchemaImpl( schemaName, ImmutableMap.<String, PTable>of(table.getName().getString(), table)); TableRef tableRef = new TableRef(null, table, schema, ts); byte[] emptyCF = SchemaUtil.getEmptyColumnFamily(table.getColumnFamilies()); MutationPlan plan = new PostDDLCompiler(connection).compile(tableRef, emptyCF, null, ts); return connection.getQueryServices().updateData(plan); } break; } return new MutationState(0, connection); } finally { connection.setAutoCommit(wasAutoCommit); } }
public class FunctionUtil { private FunctionUtil() {} public static FieldValue evaluate( Apply apply, List<FieldValue> values, EvaluationContext context) { String name = apply.getFunction(); Function function = getFunction(name); if (function == null) { DefineFunction defineFunction = context.resolveFunction(name); if (defineFunction == null) { throw new UnsupportedFeatureException(apply); } return evaluate(defineFunction, values, context); } return function.evaluate(values); } public static FieldValue evaluate( DefineFunction defineFunction, List<FieldValue> values, EvaluationContext context) { List<ParameterField> parameterFields = defineFunction.getParameterFields(); if (parameterFields.size() < 1) { throw new InvalidFeatureException(defineFunction); } // End if if (parameterFields.size() != values.size()) { throw new EvaluationException(); } FunctionEvaluationContext functionContext = new FunctionEvaluationContext(context); for (int i = 0; i < parameterFields.size(); i++) { ParameterField parameterField = parameterFields.get(i); FieldValue value = FieldValueUtil.refine(parameterField, values.get(i)); functionContext.declare(parameterField.getName(), value); } Expression expression = defineFunction.getExpression(); if (expression == null) { throw new InvalidFeatureException(defineFunction); } FieldValue result = ExpressionUtil.evaluate(expression, functionContext); return FieldValueUtil.refine(defineFunction.getDataType(), defineFunction.getOptype(), result); } public static Function getFunction(String name) { return FunctionUtil.functions.get(name); } public static void putFunction(String name, Function function) { FunctionUtil.functions.put(name, function); } private static void checkArguments(List<FieldValue> values, int size) { checkArguments(values, size, false); } private static void checkArguments(List<FieldValue> values, int size, boolean allowNulls) { boolean success = (values.size() == size) && (allowNulls ? true : !values.contains(null)); if (!success) { throw new EvaluationException(); } } private static void checkVariableArguments(List<FieldValue> values, int size) { checkVariableArguments(values, size, false); } private static void checkVariableArguments( List<FieldValue> values, int size, boolean allowNulls) { boolean success = (values.size() >= size) && (allowNulls ? true : !values.contains(null)); if (!success) { throw new EvaluationException(); } } private static Number cast(DataType dataType, Number number) { switch (dataType) { case INTEGER: if (number instanceof Integer) { return number; } return Integer.valueOf(number.intValue()); case FLOAT: if (number instanceof Float) { return number; } return Float.valueOf(number.floatValue()); case DOUBLE: if (number instanceof Double) { return number; } return Double.valueOf(number.doubleValue()); default: break; } throw new EvaluationException(); } private static DataType integerToDouble(DataType dataType) { switch (dataType) { case INTEGER: return DataType.DOUBLE; default: break; } return dataType; } private static final Map<String, Function> functions = Maps.newLinkedHashMap(); public interface Function { FieldValue evaluate(List<FieldValue> values); } public abstract static class ArithmeticFunction implements Function { public abstract Number evaluate(Number left, Number right); @Override public FieldValue evaluate(List<FieldValue> values) { if (values.size() != 2) { throw new EvaluationException(); } FieldValue left = values.get(0); FieldValue right = values.get(1); // "If one of the input fields of a simple arithmetic function is a missing value, the result // evaluates to missing value" if (left == null || right == null) { return null; } DataType dataType = TypeUtil.getResultDataType(left.getDataType(), right.getDataType()); Number result; try { result = evaluate(left.asNumber(), right.asNumber()); } catch (ArithmeticException ae) { throw new InvalidResultException(null); } return FieldValueUtil.create(cast(dataType, result)); } } static { putFunction( "+", new ArithmeticFunction() { @Override public Double evaluate(Number left, Number right) { return Double.valueOf(left.doubleValue() + right.doubleValue()); } }); putFunction( "-", new ArithmeticFunction() { @Override public Double evaluate(Number left, Number right) { return Double.valueOf(left.doubleValue() - right.doubleValue()); } }); putFunction( "*", new ArithmeticFunction() { @Override public Double evaluate(Number left, Number right) { return Double.valueOf(left.doubleValue() * right.doubleValue()); } }); putFunction( "/", new ArithmeticFunction() { @Override public Number evaluate(Number left, Number right) { if (left instanceof Integer && right instanceof Integer) { return Integer.valueOf(left.intValue() / right.intValue()); } return Double.valueOf(left.doubleValue() / right.doubleValue()); } }); } public abstract static class AggregateFunction implements Function { public abstract StorelessUnivariateStatistic createStatistic(); public DataType getResultType(DataType dataType) { return dataType; } @Override public FieldValue evaluate(List<FieldValue> values) { StorelessUnivariateStatistic statistic = createStatistic(); DataType dataType = null; for (FieldValue value : values) { // "Missing values in the input to an aggregate function are simply ignored" if (value == null) { continue; } statistic.increment((value.asNumber()).doubleValue()); if (dataType != null) { dataType = TypeUtil.getResultDataType(dataType, value.getDataType()); } else { dataType = value.getDataType(); } } if (statistic.getN() == 0) { throw new MissingResultException(null); } Object result = cast(getResultType(dataType), statistic.getResult()); return FieldValueUtil.create(result); } } static { putFunction( "min", new AggregateFunction() { @Override public Min createStatistic() { return new Min(); } }); putFunction( "max", new AggregateFunction() { @Override public Max createStatistic() { return new Max(); } }); putFunction( "avg", new AggregateFunction() { @Override public Mean createStatistic() { return new Mean(); } @Override public DataType getResultType(DataType dataType) { return integerToDouble(dataType); } }); putFunction( "sum", new AggregateFunction() { @Override public Sum createStatistic() { return new Sum(); } }); putFunction( "product", new AggregateFunction() { @Override public Product createStatistic() { return new Product(); } }); } public abstract static class MathFunction implements Function { public abstract Double evaluate(Number value); public DataType getResultType(DataType dataType) { return dataType; } @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 1); FieldValue value = values.get(0); Number result = cast(getResultType(value.getDataType()), evaluate(value.asNumber())); return FieldValueUtil.create(result); } } public abstract static class FpMathFunction extends MathFunction { @Override public DataType getResultType(DataType dataType) { return integerToDouble(dataType); } } static { putFunction( "log10", new FpMathFunction() { @Override public Double evaluate(Number value) { return Math.log10(value.doubleValue()); } }); putFunction( "ln", new FpMathFunction() { @Override public Double evaluate(Number value) { return Math.log(value.doubleValue()); } }); putFunction( "exp", new FpMathFunction() { @Override public Double evaluate(Number value) { return Math.exp(value.doubleValue()); } }); putFunction( "sqrt", new FpMathFunction() { @Override public Double evaluate(Number value) { return Math.sqrt(value.doubleValue()); } }); putFunction( "abs", new MathFunction() { @Override public Double evaluate(Number value) { return Math.abs(value.doubleValue()); } }); putFunction( "pow", new Function() { @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 2); FieldValue left = values.get(0); FieldValue right = values.get(1); DataType dataType = TypeUtil.getResultDataType(left.getDataType(), right.getDataType()); Double result = Math.pow((left.asNumber()).doubleValue(), (right.asNumber()).doubleValue()); return FieldValueUtil.create(cast(dataType, result)); } }); putFunction( "threshold", new Function() { @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 2); FieldValue left = values.get(0); FieldValue right = values.get(1); DataType dataType = TypeUtil.getResultDataType(left.getDataType(), right.getDataType()); Integer result = ((left.asNumber()).doubleValue() > (right.asNumber()).doubleValue()) ? 1 : 0; return FieldValueUtil.create(cast(dataType, result)); } }); putFunction( "floor", new MathFunction() { @Override public Double evaluate(Number number) { return Math.floor(number.doubleValue()); } }); putFunction( "ceil", new MathFunction() { @Override public Double evaluate(Number number) { return Math.ceil(number.doubleValue()); } }); putFunction( "round", new MathFunction() { @Override public Double evaluate(Number number) { return (double) Math.round(number.doubleValue()); } }); } public abstract static class ValueFunction implements Function { public abstract Boolean evaluate(FieldValue value); @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 1, true); FieldValue value = values.get(0); Boolean result = evaluate(value); return FieldValueUtil.create(result); } } static { putFunction( "isMissing", new ValueFunction() { @Override public Boolean evaluate(FieldValue value) { return Boolean.valueOf(value == null); } }); putFunction( "isNotMissing", new ValueFunction() { @Override public Boolean evaluate(FieldValue value) { return Boolean.valueOf(value != null); } }); } public abstract static class EqualityFunction implements Function { public abstract Boolean evaluate(boolean equals); @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 2); FieldValue left = values.get(0); FieldValue right = values.get(1); Boolean result = evaluate((left).equalsValue(right)); return FieldValueUtil.create(result); } } static { putFunction( "equal", new EqualityFunction() { @Override public Boolean evaluate(boolean equals) { return Boolean.valueOf(equals); } }); putFunction( "notEqual", new EqualityFunction() { @Override public Boolean evaluate(boolean equals) { return Boolean.valueOf(!equals); } }); } public abstract static class ComparisonFunction implements Function { public abstract Boolean evaluate(int order); @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 2); FieldValue left = values.get(0); FieldValue right = values.get(1); Boolean result = evaluate((left).compareToValue(right)); return FieldValueUtil.create(result); } } static { putFunction( "lessThan", new ComparisonFunction() { @Override public Boolean evaluate(int order) { return Boolean.valueOf(order < 0); } }); putFunction( "lessOrEqual", new ComparisonFunction() { @Override public Boolean evaluate(int order) { return Boolean.valueOf(order <= 0); } }); putFunction( "greaterThan", new ComparisonFunction() { @Override public Boolean evaluate(int order) { return Boolean.valueOf(order > 0); } }); putFunction( "greaterOrEqual", new ComparisonFunction() { @Override public Boolean evaluate(int order) { return Boolean.valueOf(order >= 0); } }); } public abstract static class BinaryBooleanFunction implements Function { public abstract Boolean evaluate(Boolean left, Boolean right); @Override public FieldValue evaluate(List<FieldValue> values) { checkVariableArguments(values, 2); Boolean result = (values.get(0)).asBoolean(); for (int i = 1; i < values.size(); i++) { result = evaluate(result, (values.get(i)).asBoolean()); } return FieldValueUtil.create(result); } } static { putFunction( "and", new BinaryBooleanFunction() { @Override public Boolean evaluate(Boolean left, Boolean right) { return Boolean.valueOf(left.booleanValue() & right.booleanValue()); } }); putFunction( "or", new BinaryBooleanFunction() { @Override public Boolean evaluate(Boolean left, Boolean right) { return Boolean.valueOf(left.booleanValue() | right.booleanValue()); } }); } public abstract static class UnaryBooleanFunction implements Function { public abstract Boolean evaluate(Boolean value); @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 1); FieldValue value = values.get(0); Boolean result = evaluate(value.asBoolean()); return FieldValueUtil.create(result); } } static { putFunction( "not", new UnaryBooleanFunction() { @Override public Boolean evaluate(Boolean value) { return Boolean.valueOf(!value.booleanValue()); } }); } public abstract static class ValueListFunction implements Function { public abstract Boolean evaluate(FieldValue value, List<FieldValue> values); @Override public FieldValue evaluate(List<FieldValue> values) { checkVariableArguments(values, 2); Boolean result = evaluate(values.get(0), values.subList(1, values.size())); return FieldValueUtil.create(result); } } static { putFunction( "isIn", new ValueListFunction() { @Override public Boolean evaluate(FieldValue value, List<FieldValue> values) { return value.equalsAnyValue(values); } }); putFunction( "isNotIn", new ValueListFunction() { @Override public Boolean evaluate(FieldValue value, List<FieldValue> values) { return !value.equalsAnyValue(values); } }); } static { putFunction( "if", new Function() { @Override public FieldValue evaluate(List<FieldValue> values) { if ((values.size() < 2 || values.size() > 3)) { throw new EvaluationException(); } FieldValue flag = values.get(0); if (flag == null) { throw new EvaluationException(); } // End if if (flag.asBoolean()) { FieldValue trueValue = values.get(1); // "The THEN part is required" if (trueValue == null) { throw new EvaluationException(); } return trueValue; } else { FieldValue falseValue = (values.size() > 2 ? values.get(2) : null); // "The ELSE part is optional. If the ELSE part is absent then a missing value is // returned" if (falseValue == null) { return null; } return falseValue; } } }); } public abstract static class StringFunction implements Function { public abstract String evaluate(String value); @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 1); FieldValue value = values.get(0); String result = evaluate(value.asString()); return FieldValueUtil.create(result); } } static { putFunction( "uppercase", new StringFunction() { @Override public String evaluate(String value) { return value.toUpperCase(); } }); putFunction( "lowercase", new StringFunction() { @Override public String evaluate(String value) { return value.toLowerCase(); } }); putFunction( "substring", new Function() { @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 3); String string = (values.get(0)).asString(); int position = (values.get(1)).asInteger(); int length = (values.get(2)).asInteger(); // "The first character of a string is located at position 1 (not position 0)" if (position <= 0 || length < 0) { throw new EvaluationException(); } String result = string.substring(position - 1, (position + length) - 1); return FieldValueUtil.create(result); } }); putFunction( "trimBlanks", new StringFunction() { @Override public String evaluate(String value) { return value.trim(); } }); } static { putFunction( "formatNumber", new Function() { @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 2); FieldValue value = values.get(0); FieldValue pattern = values.get(1); String result; // According to the java.util.Formatter javadoc, Java formatting is more strict than C's // printf formatting. // For example, in Java, if a conversion is incompatible with a flag, an exception will // be thrown. In C's printf, inapplicable flags are silently ignored. try { result = String.format(pattern.asString(), value.asNumber()); } catch (IllegalFormatException ife) { throw ife; } return FieldValueUtil.create(result); } }); putFunction( "formatDatetime", new Function() { @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 2); FieldValue value = values.get(0); FieldValue pattern = values.get(1); String result; try { result = String.format( translatePattern(pattern.asString()), (value.asDateTime()).toDate()); } catch (IllegalFormatException ife) { throw ife; } return FieldValueUtil.create(result); } private String translatePattern(String pattern) { StringBuilder sb = new StringBuilder(); for (int i = 0; i < pattern.length(); i++) { char c = pattern.charAt(i); sb.append(c); if (c == '%') { // Every %[conversion] has to become %1$t[conversion] // Here, "1$" denotes the first argument, and "t" denotes the prefix for date and // time conversion characters if (i < (pattern.length() - 1) && pattern.charAt(i + 1) != '%') { sb.append("1$t"); } } } return sb.toString(); } }); } static { putFunction( "dateDaysSinceYear", new Function() { @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 2); LocalDate instant = (values.get(0)).asLocalDate(); int year = (values.get(1)).asInteger(); DaysSinceDate period = new DaysSinceDate(year, instant); return FieldValueUtil.create(period.intValue()); } }); putFunction( "dateSecondsSinceMidnight", new Function() { @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 1); LocalTime instant = (values.get(0)).asLocalTime(); Seconds seconds = Seconds.seconds( instant.getHourOfDay() * 60 * 60 + instant.getMinuteOfHour() * 60 + instant.getSecondOfMinute()); SecondsSinceMidnight period = new SecondsSinceMidnight(seconds); return FieldValueUtil.create(period.intValue()); } }); putFunction( "dateSecondsSinceYear", new Function() { @Override public FieldValue evaluate(List<FieldValue> values) { checkArguments(values, 2); LocalDateTime instant = (values.get(0)).asLocalDateTime(); int year = (values.get(1)).asInteger(); SecondsSinceDate period = new SecondsSinceDate(year, instant); return FieldValueUtil.create(period.intValue()); } }); } }