Own template render implementation: first draft
Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/040bfbce Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/040bfbce Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/040bfbce Branch: refs/heads/master Commit: 040bfbce6c2e79b1686f392964c6204477ec3e59 Parents: 55e3c97 Author: Nikita Timofeev <stari...@gmail.com> Authored: Tue Aug 8 18:51:47 2017 +0300 Committer: Nikita Timofeev <stari...@gmail.com> Committed: Wed Aug 16 18:29:35 2017 +0300 ---------------------------------------------------------------------- .../configuration/server/ServerModule.java | 4 +- .../template/CayenneSQLTemplateProcessor.java | 29 +- .../org/apache/cayenne/template/Context.java | 56 +- .../org/apache/cayenne/template/Directive.java | 31 - .../apache/cayenne/template/directive/Bind.java | 32 +- .../cayenne/template/directive/BindEqual.java | 41 + .../template/directive/BindNotEqual.java | 42 + .../template/directive/BindObjectEqual.java | 145 ++ .../template/directive/BindObjectNotEqual.java | 51 + .../cayenne/template/directive/Directive.java | 32 + .../cayenne/template/directive/Result.java | 126 ++ .../cayenne/template/parser/ASTArray.java | 50 + .../cayenne/template/parser/ASTDirective.java | 9 +- .../cayenne/template/parser/ASTExpression.java | 2 +- .../cayenne/template/parser/ASTMethod.java | 8 +- .../cayenne/template/parser/ASTVariable.java | 7 +- .../cayenne/template/parser/JavaCharStream.java | 10 +- .../cayenne/template/parser/ParseException.java | 312 ++-- .../template/parser/SQLTemplateParser.java | 253 +++- .../parser/SQLTemplateParserConstants.java | 93 +- .../parser/SQLTemplateParserTokenManager.java | 1383 ++++++++++-------- .../parser/SQLTemplateParserTreeConstants.java | 4 +- .../cayenne/template/parser/ScalarNode.java | 5 + .../template/parser/SQLTemplateParser.jjt | 233 ++- .../org/apache/cayenne/query/SQLSelectIT.java | 5 + .../org/apache/cayenne/query/SQLTemplateIT.java | 4 + .../CayenneSQLTemplateProcessorTest.java | 31 + .../template/parser/SQLTemplateParserTest.java | 181 +++ 28 files changed, 2252 insertions(+), 927 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java index 8bf1d6a..e951c49 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/configuration/server/ServerModule.java @@ -111,6 +111,7 @@ import org.apache.cayenne.map.EntitySorter; import org.apache.cayenne.access.types.ValueObjectType; import org.apache.cayenne.resource.ClassLoaderResourceLocator; import org.apache.cayenne.resource.ResourceLocator; +import org.apache.cayenne.template.CayenneSQLTemplateProcessor; import org.apache.cayenne.tx.DefaultTransactionFactory; import org.apache.cayenne.tx.DefaultTransactionManager; import org.apache.cayenne.tx.TransactionFactory; @@ -408,7 +409,8 @@ public class ServerModule implements Module { binder.bind(TransactionManager.class).to(DefaultTransactionManager.class); binder.bind(RowReaderFactory.class).to(DefaultRowReaderFactory.class); - binder.bind(SQLTemplateProcessor.class).to(VelocitySQLTemplateProcessor.class); +// binder.bind(SQLTemplateProcessor.class).to(VelocitySQLTemplateProcessor.class); + binder.bind(SQLTemplateProcessor.class).to(CayenneSQLTemplateProcessor.class); binder.bind(HandlerFactory.class).to(DefaultHandlerFactory.class); binder.bind(DataChannelMetaData.class).to(NoopDataChannelMetaData.class); http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/CayenneSQLTemplateProcessor.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/CayenneSQLTemplateProcessor.java b/cayenne-server/src/main/java/org/apache/cayenne/template/CayenneSQLTemplateProcessor.java index 062f1c5..0352417 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/template/CayenneSQLTemplateProcessor.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/template/CayenneSQLTemplateProcessor.java @@ -19,6 +19,7 @@ package org.apache.cayenne.template; +import java.io.BufferedReader; import java.io.StringReader; import java.util.HashMap; import java.util.List; @@ -27,9 +28,11 @@ import java.util.Map; import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.jdbc.SQLStatement; import org.apache.cayenne.access.jdbc.SQLTemplateProcessor; -import org.apache.cayenne.template.parser.ASTBlock; +import org.apache.cayenne.template.parser.Node; import org.apache.cayenne.template.parser.ParseException; import org.apache.cayenne.template.parser.SQLTemplateParser; +import org.apache.cayenne.template.parser.TokenMgrError; +import org.apache.cayenne.util.concurrentlinkedhashmap.ConcurrentLinkedHashMap; /** @@ -37,6 +40,9 @@ import org.apache.cayenne.template.parser.SQLTemplateParser; */ public class CayenneSQLTemplateProcessor implements SQLTemplateProcessor { + ConcurrentLinkedHashMap<String, Node> templateCache = new ConcurrentLinkedHashMap + .Builder<String, Node>().maximumWeightedCapacity(100).build(); + @Override public SQLStatement processTemplate(String template, Map<String, ?> parameters) { Context context = new Context(); @@ -46,7 +52,7 @@ public class CayenneSQLTemplateProcessor implements SQLTemplateProcessor { @Override public SQLStatement processTemplate(String template, List<Object> positionalParameters) { - Context context = new Context(); + Context context = new Context(true); Map<String, Object> parameters = new HashMap<>(); int i=0; for(Object param : positionalParameters) { @@ -57,13 +63,18 @@ public class CayenneSQLTemplateProcessor implements SQLTemplateProcessor { } protected SQLStatement process(String template, Context context) { - SQLTemplateParser parser = new SQLTemplateParser(new StringReader(template)); - try { - ASTBlock block = parser.template(); - String sql = block.evaluate(context); - return new SQLStatement(sql, context.getColumnDescriptors(), context.getParameterBindings()); - } catch (ParseException ex) { - throw new CayenneRuntimeException("Error parsing template '%s' : %s", template, ex.getMessage()); + Node node = templateCache.get(template); + if(node == null) { + SQLTemplateParser parser = new SQLTemplateParser(new BufferedReader(new StringReader(template))); + try { + node = parser.template(); + } catch (ParseException | TokenMgrError ex) { + throw new CayenneRuntimeException("Error parsing template '%s' : %s", template, ex.getMessage()); + } + templateCache.put(template, node); } + + String sql = node.evaluate(context); + return new SQLStatement(sql, context.getColumnDescriptors(), context.getParameterBindings()); } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/Context.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/Context.java b/cayenne-server/src/main/java/org/apache/cayenne/template/Context.java index 7ccb911..61c37b2 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/template/Context.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/template/Context.java @@ -23,11 +23,18 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.jdbc.ColumnDescriptor; import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.template.directive.Bind; +import org.apache.cayenne.template.directive.BindEqual; +import org.apache.cayenne.template.directive.BindNotEqual; +import org.apache.cayenne.template.directive.BindObjectEqual; +import org.apache.cayenne.template.directive.BindObjectNotEqual; +import org.apache.cayenne.template.directive.Directive; +import org.apache.cayenne.template.directive.Result; +import org.apache.cayenne.velocity.SQLTemplateRenderingUtils; /** * @since 4.1 @@ -38,12 +45,33 @@ public class Context { Map<String, Object> objects = new HashMap<>(); + Map<String, String> parameterAliases; + List<ParameterBinding> parameterBindings = new ArrayList<>(); List<ColumnDescriptor> columnDescriptors = new ArrayList<>(); + boolean positionalMode; + + int counter; + public Context() { - directives.put("bind", new Bind()); + directives.put( "bind", Bind.INSTANCE); + directives.put( "bindEqual", BindEqual.INSTANCE); + directives.put( "bindNotEqual", BindNotEqual.INSTANCE); + directives.put( "bindObjectEqual", BindObjectEqual.INSTANCE); + directives.put( "bindObjectNotEqual", BindObjectNotEqual.INSTANCE); + directives.put( "result", Result.INSTANCE); + + objects.put("helper", new SQLTemplateRenderingUtils()); + } + + public Context(boolean positionalMode) { + this(); + this.positionalMode = positionalMode; + if(positionalMode) { + parameterAliases = new HashMap<>(); + } } public Directive getDirective(String name) { @@ -51,7 +79,29 @@ public class Context { } public Object getObject(String name) { - return objects.get(name); + Object object = objects.get(name); + if(object != null) { + return object; + } + + if(positionalMode) { + String alias = parameterAliases.get(name); + if(alias == null) { + if(counter > objects.size() - 2) { + throw new CayenneRuntimeException("Too few parameters to bind template: " + (objects.size() - 1)); + } + alias = String.valueOf(counter++); + parameterAliases.put(name, alias); + } + // give next object on each invocation of method + return objects.get(alias); + } + + return null; + } + + public void addParameter(String name, Object value) { + objects.put(name, value); } public void addParameters(Map<String, ?> parameters) { http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/Directive.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/Directive.java b/cayenne-server/src/main/java/org/apache/cayenne/template/Directive.java deleted file mode 100644 index 94c0818..0000000 --- a/cayenne-server/src/main/java/org/apache/cayenne/template/Directive.java +++ /dev/null @@ -1,31 +0,0 @@ -/***************************************************************** - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF 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. - ****************************************************************/ - -package org.apache.cayenne.template; - -import org.apache.cayenne.template.parser.ASTExpression; - -/** - * @since 4.1 - */ -public interface Directive { - - String apply(Context context, ASTExpression... expressions); - -} http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/directive/Bind.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/directive/Bind.java b/cayenne-server/src/main/java/org/apache/cayenne/template/directive/Bind.java index b867072..8d2138f 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/template/directive/Bind.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/template/directive/Bind.java @@ -19,10 +19,12 @@ package org.apache.cayenne.template.directive; +import java.util.Collection; +import java.util.Iterator; + import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.dba.TypesMapping; import org.apache.cayenne.template.Context; -import org.apache.cayenne.template.Directive; import org.apache.cayenne.template.parser.ASTExpression; /** @@ -30,14 +32,17 @@ import org.apache.cayenne.template.parser.ASTExpression; */ public class Bind implements Directive { + public static final Bind INSTANCE = new Bind(); + @Override public String apply(Context context, ASTExpression... expressions) { - if(expressions.length < 2) { + if(expressions.length < 1) { throw new IllegalArgumentException(); } Object value = expressions[0].evaluateAsObject(context); - String jdbcTypeName = expressions[1].evaluate(context); + String jdbcTypeName = expressions.length < 2 ? null : expressions[1].evaluate(context); + int jdbcType; if (jdbcTypeName != null) { jdbcType = TypesMapping.getSqlTypeByName(jdbcTypeName); @@ -48,9 +53,24 @@ public class Bind implements Directive { } int scale = expressions.length < 3 ? -1 : (int)expressions[2].evaluateAsLong(context); - ParameterBinding binding = new ParameterBinding(value, jdbcType, scale); - context.addParameterBinding(binding); + StringBuilder builder = new StringBuilder(); + if (value instanceof Collection) { + Iterator<?> it = ((Collection) value).iterator(); + while (it.hasNext()) { + processBinding(context, builder, new ParameterBinding(it.next(), jdbcType, scale)); + if (it.hasNext()) { + builder.append(','); + } + } + } else { + processBinding(context, builder, new ParameterBinding(value, jdbcType, scale)); + } + + return builder.toString(); + } - return "?"; + protected void processBinding(Context context, StringBuilder builder, ParameterBinding binding) { + context.addParameterBinding(binding); + builder.append('?'); } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindEqual.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindEqual.java b/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindEqual.java new file mode 100644 index 0000000..1153675 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindEqual.java @@ -0,0 +1,41 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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. + ****************************************************************/ + +package org.apache.cayenne.template.directive; + +import org.apache.cayenne.access.translator.ParameterBinding; +import org.apache.cayenne.template.Context; + +/** + * @since 4.1 + */ +public class BindEqual extends Bind { + + public static final BindEqual INSTANCE = new BindEqual(); + + @Override + protected void processBinding(Context context, StringBuilder builder, ParameterBinding binding) { + if (binding.getValue() != null) { + context.addParameterBinding(binding); + builder.append("= ?"); + } else { + builder.append("IS NULL"); + } + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindNotEqual.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindNotEqual.java b/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindNotEqual.java new file mode 100644 index 0000000..09f70ed --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindNotEqual.java @@ -0,0 +1,42 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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. + ****************************************************************/ + +package org.apache.cayenne.template.directive; + +import org.apache.cayenne.access.translator.ParameterBinding; +import org.apache.cayenne.template.Context; + +/** + * @since 4.1 + */ +public class BindNotEqual extends Bind { + + public static final BindEqual INSTANCE = new BindEqual(); + + @Override + protected void processBinding(Context context, StringBuilder builder, ParameterBinding binding) { + if (binding.getValue() != null) { + context.addParameterBinding(binding); + builder.append("<> ?"); + } else { + builder.append("IS NOT NULL"); + } + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindObjectEqual.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindObjectEqual.java b/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindObjectEqual.java new file mode 100644 index 0000000..cea8b4f --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindObjectEqual.java @@ -0,0 +1,145 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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. + ****************************************************************/ + +package org.apache.cayenne.template.directive; + +import java.sql.Types; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import org.apache.cayenne.ObjectId; +import org.apache.cayenne.Persistent; +import org.apache.cayenne.access.translator.ParameterBinding; +import org.apache.cayenne.dba.TypesMapping; +import org.apache.cayenne.template.Context; +import org.apache.cayenne.template.parser.ASTExpression; +import org.apache.velocity.exception.ParseErrorException; + +/** + * @since 4.1 + */ +public class BindObjectEqual implements Directive { + + public static final BindObjectEqual INSTANCE = new BindObjectEqual(); + + @Override + public String apply(Context context, ASTExpression... expressions) { + + Object object = expressions[0].evaluateAsObject(context); + Map<String, Object> idMap = toIdMap(object); + + Object sqlColumns = null; + Object idColumns = null; + if(expressions.length > 1) { + sqlColumns = expressions[1].evaluateAsObject(context); + } + if(expressions.length > 2) { + idColumns = expressions[2].evaluateAsObject(context); + } + + if (idMap == null) { + // assume null object, and bind all null values + if (sqlColumns == null || idColumns == null) { + throw new ParseErrorException("Invalid parameters. " + + "Either object has to be set or sqlColumns and idColumns or both."); + } + + idMap = Collections.emptyMap(); + } else if (sqlColumns == null || idColumns == null) { + // infer SQL columns from ID columns + sqlColumns = idMap.keySet().toArray(); + idColumns = sqlColumns; + } + + String[] sqlColumnsArray = toArray(sqlColumns); + String[] idColumnsArray = toArray(idColumns); + + if (sqlColumnsArray.length != idColumnsArray.length) { + throw new ParseErrorException( + "SQL columns and ID columns arrays have different sizes."); + } + + StringBuilder builder = new StringBuilder(); + + for (int i = 0; i < sqlColumnsArray.length; i++) { + Object value = idMap.get(idColumnsArray[i]); + int jdbcType = (value != null) ? TypesMapping.getSqlTypeByJava(value.getClass()) : Types.INTEGER; + + renderColumn(sqlColumnsArray[i], i, builder); + render(context, builder, new ParameterBinding(value, jdbcType, -1)); + } + + return builder.toString(); + } + + protected void renderColumn(String columnName, int columnIndex, StringBuilder builder) { + if (columnIndex > 0) { + builder.append(" AND "); + } + + builder.append(columnName).append(' '); + } + + protected void render(Context context, StringBuilder builder, ParameterBinding binding) { + if (binding.getValue() != null) { + context.addParameterBinding(binding); + builder.append("= ?"); + } else { + builder.append("IS NULL"); + } + } + + @SuppressWarnings("unchecked") + protected String[] toArray(Object columns) { + if (columns instanceof Collection) { + String[] columnsAsStrings = new String[((Collection<Object>)columns).size()]; + int idx = 0; + for(Object column : (Collection<Object>)columns) { + columnsAsStrings[idx++] = column.toString(); + } + return columnsAsStrings; + } else if (columns.getClass().isArray()) { + String[] columnsAsStrings = new String[((Object[])columns).length]; + int idx = 0; + for(Object column : (Object[])columns) { + columnsAsStrings[idx++] = column.toString(); + } + return columnsAsStrings; + } else { + return new String[] { columns.toString() }; + } + } + + @SuppressWarnings("unchecked") + protected Map<String, Object> toIdMap(Object object) throws ParseErrorException { + if (object instanceof Persistent) { + return ((Persistent) object).getObjectId().getIdSnapshot(); + } else if (object instanceof ObjectId) { + return ((ObjectId) object).getIdSnapshot(); + } else if(object instanceof Map) { + return (Map<String, Object>) object; + } else if (object != null) { + throw new ParseErrorException( + "Invalid object parameter, expected Persistent or ObjectId or null: " + object); + } else { + return null; + } + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindObjectNotEqual.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindObjectNotEqual.java b/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindObjectNotEqual.java new file mode 100644 index 0000000..2e8879d --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/template/directive/BindObjectNotEqual.java @@ -0,0 +1,51 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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. + ****************************************************************/ + +package org.apache.cayenne.template.directive; + +import org.apache.cayenne.access.translator.ParameterBinding; +import org.apache.cayenne.template.Context; + +/** + * @since 4.1 + */ +public class BindObjectNotEqual extends BindObjectEqual { + + public static final BindObjectNotEqual INSTANCE = new BindObjectNotEqual(); + + @Override + protected void renderColumn(String columnName, int columnIndex, StringBuilder builder) { + if (columnIndex > 0) { + builder.append(" OR "); + } + + builder.append(columnName).append(' '); + } + + @Override + protected void render(Context context, StringBuilder builder, ParameterBinding binding) { + if (binding.getValue() != null) { + context.addParameterBinding(binding); + builder.append("<> ?"); + } else { + builder.append("IS NOT NULL"); + } + } + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/directive/Directive.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/directive/Directive.java b/cayenne-server/src/main/java/org/apache/cayenne/template/directive/Directive.java new file mode 100644 index 0000000..be2d6c9 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/template/directive/Directive.java @@ -0,0 +1,32 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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. + ****************************************************************/ + +package org.apache.cayenne.template.directive; + +import org.apache.cayenne.template.Context; +import org.apache.cayenne.template.parser.ASTExpression; + +/** + * @since 4.1 + */ +public interface Directive { + + String apply(Context context, ASTExpression... expressions); + +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/directive/Result.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/directive/Result.java b/cayenne-server/src/main/java/org/apache/cayenne/template/directive/Result.java new file mode 100644 index 0000000..dfcdd6f --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/template/directive/Result.java @@ -0,0 +1,126 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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. + ****************************************************************/ + +package org.apache.cayenne.template.directive; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Date; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.HashMap; +import java.util.Map; + +import org.apache.cayenne.access.jdbc.ColumnDescriptor; +import org.apache.cayenne.template.Context; +import org.apache.cayenne.template.parser.ASTExpression; +import org.apache.cayenne.util.Util; + +/** + * @since 4.1 + */ +public class Result implements Directive { + + public static final Result INSTANCE = new Result(); + + private static final Map<String, String> typesGuess; + + static { + // init default types + typesGuess = new HashMap<>(); + + // primitives + typesGuess.put("long", Long.class.getName()); + typesGuess.put("double", Double.class.getName()); + typesGuess.put("byte", Byte.class.getName()); + typesGuess.put("boolean", Boolean.class.getName()); + typesGuess.put("float", Float.class.getName()); + typesGuess.put("short", Short.class.getName()); + typesGuess.put("int", Integer.class.getName()); + + // numeric + typesGuess.put("Long", Long.class.getName()); + typesGuess.put("Double", Double.class.getName()); + typesGuess.put("Byte", Byte.class.getName()); + typesGuess.put("Boolean", Boolean.class.getName()); + typesGuess.put("Float", Float.class.getName()); + typesGuess.put("Short", Short.class.getName()); + typesGuess.put("Integer", Integer.class.getName()); + + // other + typesGuess.put("String", String.class.getName()); + typesGuess.put("Date", Date.class.getName()); + typesGuess.put("Time", Time.class.getName()); + typesGuess.put("Timestamp", Timestamp.class.getName()); + typesGuess.put("BigDecimal", BigDecimal.class.getName()); + typesGuess.put("BigInteger", BigInteger.class.getName()); + } + + @Override + public String apply(Context context, ASTExpression... expressions) { + + ColumnDescriptor columnDescriptor = new ColumnDescriptor(); + + String column = expressions[0].evaluate(context); + columnDescriptor.setName(column); + + if (expressions.length > 1) { + String type = expressions[1].evaluate(context); + columnDescriptor.setJavaClass(guessType(type)); + } + + String alias = null; + if (expressions.length > 2) { + alias = expressions[2].evaluate(context); + } + + String dataRowKey = null; + if (expressions.length > 3) { + dataRowKey = expressions[3].evaluate(context); + } + + // determine what we want to name this column in a resulting DataRow... + String label = (!Util.isEmptyString(dataRowKey)) ? dataRowKey : (!Util.isEmptyString(alias)) ? alias : null; + columnDescriptor.setDataRowKey(label); + + if (expressions.length > 4) { + int jdbcType = (int) expressions[4].evaluateAsLong(context); + columnDescriptor.setJdbcType(jdbcType); + } + + context.addColumnDescriptor(columnDescriptor); + + String result = column; + if (!Util.isEmptyString(alias) && !alias.equals(column)) { + result += " AS " + alias; + } + + return result; + } + + /** + * Converts "short" type notation to the fully qualified class name. Right + * now supports all major standard SQL types, including primitives. All + * other types are expected to be fully qualified, and are not converted. + */ + protected String guessType(String type) { + String guessed = typesGuess.get(type); + return guessed != null ? guessed : type; + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTArray.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTArray.java b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTArray.java new file mode 100644 index 0000000..3aed8ba --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTArray.java @@ -0,0 +1,50 @@ +/***************************************************************** + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF 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. + ****************************************************************/ + +package org.apache.cayenne.template.parser; + +import java.util.Arrays; + +import org.apache.cayenne.template.Context; + +public class ASTArray extends ASTExpression { + + public ASTArray(int id) { + super(id); + } + + @Override + public String evaluate(Context context) { + return Arrays.toString(evaluateAsArray(context)); + } + + @Override + public Object evaluateAsObject(Context context) { + return evaluateAsArray(context); + } + + protected Object[] evaluateAsArray(Context context) { + Object[] evaluated = new Object[jjtGetNumChildren()]; + for(int i=0; i<jjtGetNumChildren(); i++) { + ExpressionNode node = (ExpressionNode)jjtGetChild(i); + evaluated[i] = node.evaluateAsObject(context); + } + return evaluated; + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTDirective.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTDirective.java b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTDirective.java index 67f4f3c..3522123 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTDirective.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTDirective.java @@ -20,7 +20,7 @@ package org.apache.cayenne.template.parser; import org.apache.cayenne.template.Context; -import org.apache.cayenne.template.Directive; +import org.apache.cayenne.template.directive.Directive; /** * @since 4.1 @@ -38,6 +38,11 @@ public class ASTDirective extends IdentifierNode { return ""; } - return directive.apply(context, (ASTExpression[]) children); + ASTExpression[] expressions = new ASTExpression[children.length]; + for(int i=0; i<children.length; i++) { + expressions[i] = (ASTExpression)children[i]; + } + + return directive.apply(context, expressions); } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTExpression.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTExpression.java b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTExpression.java index 0ade66b..7ff1858 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTExpression.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTExpression.java @@ -41,7 +41,7 @@ public class ASTExpression extends SimpleNode implements ExpressionNode { @Override public Object evaluateAsObject(Context context) { - return getChildAsExpressionNode(0).evaluateAsLong(context); + return getChildAsExpressionNode(0).evaluateAsObject(context); } @Override http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTMethod.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTMethod.java b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTMethod.java index 50961c6..51e7358 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTMethod.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTMethod.java @@ -64,14 +64,16 @@ public class ASTMethod extends IdentifierNode { Object[] arguments = new Object[jjtGetNumChildren()]; for(Class<?> parameterType : m.getParameterTypes()) { ASTExpression child = (ASTExpression)jjtGetChild(i); - if(parameterType.isAssignableFrom(Double.class)) { + if(parameterType.isAssignableFrom(String.class)) { + arguments[i] = child.evaluate(context); + } else if(parameterType.isAssignableFrom(Double.class)) { arguments[i] = child.evaluateAsDouble(context); } else if(parameterType.isAssignableFrom(Long.class)) { arguments[i] = child.evaluateAsLong(context); + } else if(parameterType.isAssignableFrom(Integer.class)) { + arguments[i] = (int)child.evaluateAsLong(context); } else if(parameterType.isAssignableFrom(Boolean.class)) { arguments[i] = child.evaluateAsBoolean(context); - } else if(parameterType.isAssignableFrom(String.class)) { - arguments[i] = child.evaluate(context); } else { continue methodsLoop; } http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTVariable.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTVariable.java b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTVariable.java index b72d59b..f45f1f8 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTVariable.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ASTVariable.java @@ -32,6 +32,7 @@ public class ASTVariable extends IdentifierNode implements ExpressionNode { super(id); } + @Override public Object evaluateAsObject(Context context) { Object object = context.getObject(getIdentifier()); if(object == null) { @@ -68,6 +69,10 @@ public class ASTVariable extends IdentifierNode implements ExpressionNode { @Override public boolean evaluateAsBoolean(Context context) { - return (Boolean) Objects.requireNonNull(evaluateAsObject(context)); + Object object = evaluateAsObject(context); + if(object instanceof Boolean) { + return (Boolean)object; + } + return object != null; } } http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/parser/JavaCharStream.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/JavaCharStream.java b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/JavaCharStream.java index 535ed3b..d9e6eb5 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/JavaCharStream.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/JavaCharStream.java @@ -103,14 +103,6 @@ public class JavaCharStream { protected int inBuf = 0; protected int tabSize = 8; - protected void setTabSize(int i) { - tabSize = i; - } - - protected int getTabSize(int i) { - return tabSize; - } - protected void ExpandBuff(boolean wrapAround) { char[] newbuffer = new char[bufsize + 2048]; int newbufline[] = new int[bufsize + 2048]; @@ -450,7 +442,7 @@ public class JavaCharStream { */ public JavaCharStream(java.io.InputStream dstream, int startline, int startcolumn, int buffersize) { - this(new java.io.InputStreamReader(dstream), startline, startcolumn, 4096); + this(new java.io.InputStreamReader(dstream), startline, startcolumn, buffersize); } /** http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ParseException.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ParseException.java b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ParseException.java index a838ddd..6aeb7b6 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ParseException.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/ParseException.java @@ -1,3 +1,5 @@ +/* Generated By:JavaCC: Do not edit this line. ParseException.java Version 5.0 */ +/* JavaCCOptions:KEEP_LINE_COL=null */ /***************************************************************** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file @@ -24,169 +26,181 @@ package org.apache.cayenne.template.parser; * You can explicitly create objects of this exception type by * calling the method generateParseException in the generated * parser. - * <p> + * * You can modify this class to customize your error reporting * mechanisms so long as you retain the public fields. - * - * @since 4.1 */ public class ParseException extends Exception { - /** - * The version identifier for this Serializable class. - * Increment only if the <i>serialized</i> form of the - * class changes. - */ - private static final long serialVersionUID = 1L; + /** + * The version identifier for this Serializable class. + * Increment only if the <i>serialized</i> form of the + * class changes. + */ + private static final long serialVersionUID = 1L; - /** - * This is the last token that has been consumed successfully. If - * this object has been created due to a parse error, the token - * following this token will (therefore) be the first error token. - */ - public Token currentToken; + /** + * This constructor is used by the method "generateParseException" + * in the generated parser. Calling this constructor generates + * a new object of this type with the fields "currentToken", + * "expectedTokenSequences", and "tokenImage" set. + */ + public ParseException(Token currentTokenVal, + int[][] expectedTokenSequencesVal, + String[] tokenImageVal + ) + { + super(initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal)); + currentToken = currentTokenVal; + expectedTokenSequences = expectedTokenSequencesVal; + tokenImage = tokenImageVal; + } - /** - * Each entry in this array is an array of integers. Each array - * of integers represents a sequence of tokens (by their ordinal - * values) that is expected at this point of the parse. - */ - public int[][] expectedTokenSequences; + /** + * The following constructors are for use by you for whatever + * purpose you can think of. Constructing the exception in this + * manner makes the exception behave in the normal way - i.e., as + * documented in the class "Throwable". The fields "errorToken", + * "expectedTokenSequences", and "tokenImage" do not contain + * relevant information. The JavaCC generated code does not use + * these constructors. + */ - /** - * This is a reference to the "tokenImage" array of the generated - * parser within which the parse error occurred. This array is - * defined in the generated ...Constants interface. - */ - public String[] tokenImage; + public ParseException() { + super(); + } - /** - * This constructor is used by the method "generateParseException" - * in the generated parser. Calling this constructor generates - * a new object of this type with the fields "currentToken", - * "expectedTokenSequences", and "tokenImage" set. - */ - public ParseException(Token currentTokenVal, int[][] expectedTokenSequencesVal, String[] tokenImageVal) { - super(initialise(currentTokenVal, expectedTokenSequencesVal, tokenImageVal)); - currentToken = currentTokenVal; - expectedTokenSequences = expectedTokenSequencesVal; - tokenImage = tokenImageVal; - } + /** Constructor with message. */ + public ParseException(String message) { + super(message); + } - /** - * The following constructors are for use by you for whatever - * purpose you can think of. Constructing the exception in this - * manner makes the exception behave in the normal way - i.e., as - * documented in the class "Throwable". The fields "errorToken", - * "expectedTokenSequences", and "tokenImage" do not contain - * relevant information. The JavaCC generated code does not use - * these constructors. - */ - public ParseException() { - super(); - } + /** + * This is the last token that has been consumed successfully. If + * this object has been created due to a parse error, the token + * followng this token will (therefore) be the first error token. + */ + public Token currentToken; - /** - * Constructor with message. - */ - public ParseException(String message) { - super(message); - } + /** + * Each entry in this array is an array of integers. Each array + * of integers represents a sequence of tokens (by their ordinal + * values) that is expected at this point of the parse. + */ + public int[][] expectedTokenSequences; - /** - * It uses "currentToken" and "expectedTokenSequences" to generate a parse - * error message and returns it. If this object has been created - * due to a parse error, and you do not catch it (it gets thrown - * from the parser) the correct error message - * gets displayed. - */ - private static String initialise(Token currentToken, int[][] expectedTokenSequences, String[] tokenImage) { - String eol = System.getProperty("line.separator", "\n"); - StringBuilder expected = new StringBuilder(); - int maxSize = 0; - for (int[] expectedTokenSequence : expectedTokenSequences) { - if (maxSize < expectedTokenSequence.length) { - maxSize = expectedTokenSequence.length; - } - for (int anExpectedTokenSequence : expectedTokenSequence) { - expected.append(tokenImage[anExpectedTokenSequence]).append(' '); - } - if (expectedTokenSequence[expectedTokenSequence.length - 1] != 0) { - expected.append("..."); - } - expected.append(eol).append(" "); - } - StringBuilder retval = new StringBuilder("Encountered \""); - Token tok = currentToken.next; - for (int i = 0; i < maxSize; i++) { - if (i != 0) retval.append(" "); - if (tok.kind == 0) { - retval.append(tokenImage[0]); - break; - } - retval.append(" ").append(tokenImage[tok.kind]); - retval.append(" \""); - retval.append(add_escapes(tok.image)); - retval.append(" \""); - tok = tok.next; - } - retval.append("\" at line ").append(currentToken.next.beginLine).append(", column ").append(currentToken.next.beginColumn); - retval.append(".").append(eol); - if (expectedTokenSequences.length == 1) { - retval.append("Was expecting:").append(eol).append(" "); - } else { - retval.append("Was expecting one of:").append(eol).append(" "); - } - retval.append(expected.toString()); - return retval.toString(); + /** + * This is a reference to the "tokenImage" array of the generated + * parser within which the parse error occurred. This array is + * defined in the generated ...Constants interface. + */ + public String[] tokenImage; + + /** + * It uses "currentToken" and "expectedTokenSequences" to generate a parse + * error message and returns it. If this object has been created + * due to a parse error, and you do not catch it (it gets thrown + * from the parser) the correct error message + * gets displayed. + */ + private static String initialise(Token currentToken, + int[][] expectedTokenSequences, + String[] tokenImage) { + String eol = System.getProperty("line.separator", "\n"); + StringBuffer expected = new StringBuffer(); + int maxSize = 0; + for (int i = 0; i < expectedTokenSequences.length; i++) { + if (maxSize < expectedTokenSequences[i].length) { + maxSize = expectedTokenSequences[i].length; + } + for (int j = 0; j < expectedTokenSequences[i].length; j++) { + expected.append(tokenImage[expectedTokenSequences[i][j]]).append(' '); + } + if (expectedTokenSequences[i][expectedTokenSequences[i].length - 1] != 0) { + expected.append("..."); + } + expected.append(eol).append(" "); + } + String retval = "Encountered \""; + Token tok = currentToken.next; + for (int i = 0; i < maxSize; i++) { + if (i != 0) retval += " "; + if (tok.kind == 0) { + retval += tokenImage[0]; + break; + } + retval += " " + tokenImage[tok.kind]; + retval += " \""; + retval += add_escapes(tok.image); + retval += " \""; + tok = tok.next; + } + retval += "\" at line " + currentToken.next.beginLine + ", column " + currentToken.next.beginColumn; + retval += "." + eol; + if (expectedTokenSequences.length == 1) { + retval += "Was expecting:" + eol + " "; + } else { + retval += "Was expecting one of:" + eol + " "; } + retval += expected.toString(); + return retval; + } - /** - * Used to convert raw characters to their escaped version - * when these raw version cannot be used as part of an ASCII - * string literal. - */ - static String add_escapes(String str) { - StringBuilder retval = new StringBuilder(); - char ch; - for (int i = 0; i < str.length(); i++) { - switch (str.charAt(i)) { - case 0: - continue; - case '\b': - retval.append("\\b"); - continue; - case '\t': - retval.append("\\t"); - continue; - case '\n': - retval.append("\\n"); - continue; - case '\f': - retval.append("\\f"); - continue; - case '\r': - retval.append("\\r"); - continue; - case '\"': - retval.append("\\\""); - continue; - case '\'': - retval.append("\\\'"); - continue; - case '\\': - retval.append("\\\\"); - continue; - default: - if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { - String s = "0000" + Integer.toString(ch, 16); - retval.append("\\u").append(s.substring(s.length() - 4, s.length())); - } else { - retval.append(ch); - } - } + /** + * The end of line string for this machine. + */ + protected String eol = System.getProperty("line.separator", "\n"); + + /** + * Used to convert raw characters to their escaped version + * when these raw version cannot be used as part of an ASCII + * string literal. + */ + static String add_escapes(String str) { + StringBuffer retval = new StringBuffer(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) + { + case 0 : + continue; + case '\b': + retval.append("\\b"); + continue; + case '\t': + retval.append("\\t"); + continue; + case '\n': + retval.append("\\n"); + continue; + case '\f': + retval.append("\\f"); + continue; + case '\r': + retval.append("\\r"); + continue; + case '\"': + retval.append("\\\""); + continue; + case '\'': + retval.append("\\\'"); + continue; + case '\\': + retval.append("\\\\"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + String s = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + s.substring(s.length() - 4, s.length())); + } else { + retval.append(ch); + } + continue; } - return retval.toString(); - } + } + return retval.toString(); + } + } +/* JavaCC - OriginalChecksum=d5a90975d310c159e7a6a6335d8bf131 (do not edit this line) */ http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/parser/SQLTemplateParser.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/SQLTemplateParser.java b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/SQLTemplateParser.java index 952dd06..a175ae9 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/SQLTemplateParser.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/SQLTemplateParser.java @@ -28,13 +28,19 @@ package org.apache.cayenne.template.parser; public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeConstants, SQLTemplateParserConstants {/*@bgen(jjtree)*/ protected JJTSQLTemplateParserState jjtree = new JJTSQLTemplateParserState(); - final public ASTBlock template() throws ParseException { +/* + Entry function in parser +*/ + final public Node template() throws ParseException { block(); jj_consume_token(0); {if (true) return (ASTBlock) jjtree.rootNode();} throw new Error("Missing return statement in function"); } +/* + Top component of parsing tree +*/ final public void block() throws ParseException { /*@bgen(jjtree) Block */ ASTBlock jjtn000 = new ASTBlock(JJTBLOCK); @@ -46,7 +52,9 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case IF: case SHARP: + case DOLLAR: case TEXT: + case TEXT_OTHER: ; break; default: @@ -54,15 +62,19 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC break label_1; } switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { - case TEXT: - text(); - break; case IF: ifElse(); break; case SHARP: directive(); break; + case DOLLAR: + variable(); + break; + case TEXT: + case TEXT_OTHER: + text(); + break; default: jj_la1[1] = jj_gen; jj_consume_token(-1); @@ -90,16 +102,33 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC } } +/* + Plain text that is not processed in any way by render +*/ final public void text() throws ParseException { /*@bgen(jjtree) Text */ ASTText jjtn000 = new ASTText(JJTTEXT); boolean jjtc000 = true; jjtree.openNodeScope(jjtn000);Token t; try { - t = jj_consume_token(TEXT); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case TEXT: + t = jj_consume_token(TEXT); jjtree.closeNodeScope(jjtn000, true); jjtc000 = false; jjtn000.setValue(t.image); + break; + case TEXT_OTHER: + t = jj_consume_token(TEXT_OTHER); + jjtree.closeNodeScope(jjtn000, true); + jjtc000 = false; + jjtn000.setValue(t.image); + break; + default: + jj_la1[2] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } } finally { if (jjtc000) { jjtree.closeNodeScope(jjtn000, true); @@ -107,6 +136,9 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC } } +/* + Condition directive: #if(condition) ... #else ... #end +*/ final public void ifElse() throws ParseException { /*@bgen(jjtree) IfElse */ ASTIfElse jjtn000 = new ASTIfElse(JJTIFELSE); @@ -124,7 +156,7 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC block(); break; default: - jj_la1[2] = jj_gen; + jj_la1[3] = jj_gen; ; } jj_consume_token(END); @@ -149,6 +181,9 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC } } +/* + Directive in form of #directiveName(args list) +*/ final public void directive() throws ParseException { /*@bgen(jjtree) Directive */ ASTDirective jjtn000 = new ASTDirective(JJTDIRECTIVE); @@ -160,9 +195,10 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC jjtn000.setIdentifier(t.image); jj_consume_token(LBRACKET); switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case DOLLAR: case TRUE: case FALSE: - case DOLLAR: + case LSBRACKET: case SINGLE_QUOTED_STRING: case DOUBLE_QUOTED_STRING: case INT_LITERAL: @@ -171,19 +207,34 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC label_2: while (true) { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case DOLLAR: + case TRUE: + case FALSE: case COMMA: + case LSBRACKET: + case SINGLE_QUOTED_STRING: + case DOUBLE_QUOTED_STRING: + case INT_LITERAL: + case FLOAT_LITERAL: ; break; default: - jj_la1[3] = jj_gen; + jj_la1[4] = jj_gen; break label_2; } - jj_consume_token(COMMA); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COMMA: + jj_consume_token(COMMA); + break; + default: + jj_la1[5] = jj_gen; + ; + } expression(); } break; default: - jj_la1[4] = jj_gen; + jj_la1[6] = jj_gen; ; } jj_consume_token(RBRACKET); @@ -208,6 +259,10 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC } } +/* + valid expression in parameters of method or directive + can be scalar, variable (with methods calls) or array +*/ final public void expression() throws ParseException { /*@bgen(jjtree) Expression */ ASTExpression jjtn000 = new ASTExpression(JJTEXPRESSION); @@ -226,8 +281,11 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC case DOLLAR: variable(); break; + case LSBRACKET: + array(); + break; default: - jj_la1[5] = jj_gen; + jj_la1[7] = jj_gen; jj_consume_token(-1); throw new ParseException(); } @@ -252,6 +310,12 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC } } +/* + Single scalar value: String, long, double, boolean + String: single or double quoted + long: dec, hex and octo with sign + double: simple and exponential form +*/ final public void scalar() throws ParseException { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case SINGLE_QUOTED_STRING: @@ -345,12 +409,16 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC } break; default: - jj_la1[6] = jj_gen; + jj_la1[8] = jj_gen; jj_consume_token(-1); throw new ParseException(); } } +/* + Variable, optionally with some methods calls + $a or $a.method() or $a.method1().method2() +*/ final public void variable() throws ParseException { /*@bgen(jjtree) Variable */ ASTVariable jjtn000 = new ASTVariable(JJTVARIABLE); @@ -367,10 +435,9 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC ; break; default: - jj_la1[7] = jj_gen; + jj_la1[9] = jj_gen; break label_3; } - jj_consume_token(DOT); method(); } } catch (Throwable jjte000) { @@ -394,19 +461,25 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC } } +/* + Method call, valid only as part of variable, can be chain of methods + $a.method1($var).method2().method3('val') +*/ final public void method() throws ParseException { /*@bgen(jjtree) Method */ ASTMethod jjtn000 = new ASTMethod(JJTMETHOD); boolean jjtc000 = true; jjtree.openNodeScope(jjtn000);Token t; try { + jj_consume_token(DOT); t = jj_consume_token(IDENTIFIER); jjtn000.setIdentifier(t.image); jj_consume_token(LBRACKET); switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case DOLLAR: case TRUE: case FALSE: - case DOLLAR: + case LSBRACKET: case SINGLE_QUOTED_STRING: case DOUBLE_QUOTED_STRING: case INT_LITERAL: @@ -415,19 +488,34 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC label_4: while (true) { switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case DOLLAR: + case TRUE: + case FALSE: case COMMA: + case LSBRACKET: + case SINGLE_QUOTED_STRING: + case DOUBLE_QUOTED_STRING: + case INT_LITERAL: + case FLOAT_LITERAL: ; break; default: - jj_la1[8] = jj_gen; + jj_la1[10] = jj_gen; break label_4; } - jj_consume_token(COMMA); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COMMA: + jj_consume_token(COMMA); + break; + default: + jj_la1[11] = jj_gen; + ; + } expression(); } break; default: - jj_la1[9] = jj_gen; + jj_la1[12] = jj_gen; ; } jj_consume_token(RBRACKET); @@ -452,6 +540,111 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC } } +/* + Comma or space separated array of scalars and/or variables + valid values: [], ['a' 5], [$a, 'b', 5] +*/ + final public void array() throws ParseException { + /*@bgen(jjtree) Array */ + ASTArray jjtn000 = new ASTArray(JJTARRAY); + boolean jjtc000 = true; + jjtree.openNodeScope(jjtn000); + try { + jj_consume_token(LSBRACKET); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case DOLLAR: + case TRUE: + case FALSE: + case SINGLE_QUOTED_STRING: + case DOUBLE_QUOTED_STRING: + case INT_LITERAL: + case FLOAT_LITERAL: + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case TRUE: + case FALSE: + case SINGLE_QUOTED_STRING: + case DOUBLE_QUOTED_STRING: + case INT_LITERAL: + case FLOAT_LITERAL: + scalar(); + break; + case DOLLAR: + variable(); + break; + default: + jj_la1[13] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COMMA: + jj_consume_token(COMMA); + break; + default: + jj_la1[14] = jj_gen; + ; + } + label_5: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case DOLLAR: + case TRUE: + case FALSE: + case SINGLE_QUOTED_STRING: + case DOUBLE_QUOTED_STRING: + case INT_LITERAL: + case FLOAT_LITERAL: + ; + break; + default: + jj_la1[15] = jj_gen; + break label_5; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case TRUE: + case FALSE: + case SINGLE_QUOTED_STRING: + case DOUBLE_QUOTED_STRING: + case INT_LITERAL: + case FLOAT_LITERAL: + scalar(); + break; + case DOLLAR: + variable(); + break; + default: + jj_la1[16] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + break; + default: + jj_la1[17] = jj_gen; + ; + } + jj_consume_token(RSBRACKET); + } catch (Throwable jjte000) { + if (jjtc000) { + jjtree.clearNodeScope(jjtn000); + jjtc000 = false; + } else { + jjtree.popNode(); + } + if (jjte000 instanceof RuntimeException) { + {if (true) throw (RuntimeException)jjte000;} + } + if (jjte000 instanceof ParseException) { + {if (true) throw (ParseException)jjte000;} + } + {if (true) throw (Error)jjte000;} + } finally { + if (jjtc000) { + jjtree.closeNodeScope(jjtn000, true); + } + } + } + /** Generated Token Manager. */ public SQLTemplateParserTokenManager token_source; JavaCharStream jj_input_stream; @@ -461,7 +654,7 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC public Token jj_nt; private int jj_ntk; private int jj_gen; - final private int[] jj_la1 = new int[10]; + final private int[] jj_la1 = new int[18]; static private int[] jj_la1_0; static private int[] jj_la1_1; static { @@ -469,10 +662,10 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC jj_la1_init_1(); } private static void jj_la1_init_0() { - jj_la1_0 = new int[] {0x102,0x102,0x4,0x1000,0x39000230,0x39000230,0x39000030,0x2000,0x1000,0x39000230,}; + jj_la1_0 = new int[] {0x320,0x320,0x0,0x40,0x90006e00,0x2000,0x90004e00,0x90004e00,0x90000c00,0x20000,0x90006e00,0x2000,0x90004e00,0x90000e00,0x2000,0x90000e00,0x90000e00,0x90000e00,}; } private static void jj_la1_init_1() { - jj_la1_1 = new int[] {0x10,0x10,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,}; + jj_la1_1 = new int[] {0xc0,0xc0,0xc0,0x0,0x3,0x0,0x3,0x3,0x3,0x0,0x3,0x0,0x3,0x3,0x0,0x3,0x3,0x3,}; } /** Constructor with InputStream. */ @@ -486,7 +679,7 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 10; i++) jj_la1[i] = -1; + for (int i = 0; i < 18; i++) jj_la1[i] = -1; } /** Reinitialise. */ @@ -501,7 +694,7 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC jj_ntk = -1; jjtree.reset(); jj_gen = 0; - for (int i = 0; i < 10; i++) jj_la1[i] = -1; + for (int i = 0; i < 18; i++) jj_la1[i] = -1; } /** Constructor. */ @@ -511,7 +704,7 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 10; i++) jj_la1[i] = -1; + for (int i = 0; i < 18; i++) jj_la1[i] = -1; } /** Reinitialise. */ @@ -522,7 +715,7 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC jj_ntk = -1; jjtree.reset(); jj_gen = 0; - for (int i = 0; i < 10; i++) jj_la1[i] = -1; + for (int i = 0; i < 18; i++) jj_la1[i] = -1; } /** Constructor with generated Token Manager. */ @@ -531,7 +724,7 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC token = new Token(); jj_ntk = -1; jj_gen = 0; - for (int i = 0; i < 10; i++) jj_la1[i] = -1; + for (int i = 0; i < 18; i++) jj_la1[i] = -1; } /** Reinitialise. */ @@ -541,7 +734,7 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC jj_ntk = -1; jjtree.reset(); jj_gen = 0; - for (int i = 0; i < 10; i++) jj_la1[i] = -1; + for (int i = 0; i < 18; i++) jj_la1[i] = -1; } private Token jj_consume_token(int kind) throws ParseException { @@ -592,12 +785,12 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC /** Generate ParseException. */ public ParseException generateParseException() { jj_expentries.clear(); - boolean[] la1tokens = new boolean[37]; + boolean[] la1tokens = new boolean[40]; if (jj_kind >= 0) { la1tokens[jj_kind] = true; jj_kind = -1; } - for (int i = 0; i < 10; i++) { + for (int i = 0; i < 18; i++) { if (jj_la1[i] == jj_gen) { for (int j = 0; j < 32; j++) { if ((jj_la1_0[i] & (1<<j)) != 0) { @@ -609,7 +802,7 @@ public class SQLTemplateParser/*@bgen(jjtree)*/implements SQLTemplateParserTreeC } } } - for (int i = 0; i < 37; i++) { + for (int i = 0; i < 40; i++) { if (la1tokens[i]) { jj_expentry = new int[1]; jj_expentry[0] = i; http://git-wip-us.apache.org/repos/asf/cayenne/blob/040bfbce/cayenne-server/src/main/java/org/apache/cayenne/template/parser/SQLTemplateParserConstants.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/SQLTemplateParserConstants.java b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/SQLTemplateParserConstants.java index 4d1337a..0572956 100644 --- a/cayenne-server/src/main/java/org/apache/cayenne/template/parser/SQLTemplateParserConstants.java +++ b/cayenne-server/src/main/java/org/apache/cayenne/template/parser/SQLTemplateParserConstants.java @@ -30,104 +30,110 @@ public interface SQLTemplateParserConstants { /** End of File. */ int EOF = 0; /** RegularExpression Id. */ - int IF = 1; + int IF = 5; /** RegularExpression Id. */ - int ELSE = 2; + int ELSE = 6; /** RegularExpression Id. */ - int END = 3; + int END = 7; /** RegularExpression Id. */ - int TRUE = 4; - /** RegularExpression Id. */ - int FALSE = 5; + int SHARP = 8; /** RegularExpression Id. */ - int WHITESPACE = 6; + int DOLLAR = 9; /** RegularExpression Id. */ - int NEWLINE = 7; + int TRUE = 10; /** RegularExpression Id. */ - int SHARP = 8; + int FALSE = 11; /** RegularExpression Id. */ - int DOLLAR = 9; + int RBRACKET = 12; /** RegularExpression Id. */ - int LBRACKET = 10; + int COMMA = 13; /** RegularExpression Id. */ - int RBRACKET = 11; + int LSBRACKET = 14; /** RegularExpression Id. */ - int COMMA = 12; + int RSBRACKET = 15; /** RegularExpression Id. */ - int DOT = 13; + int LBRACKET = 16; /** RegularExpression Id. */ - int IDENTIFIER = 14; + int DOT = 17; /** RegularExpression Id. */ - int LETTER = 15; + int IDENTIFIER = 18; /** RegularExpression Id. */ - int DIGIT = 16; + int LETTER = 19; /** RegularExpression Id. */ - int SINGLE_LINE_COMMENT_END = 18; + int DIGIT = 20; /** RegularExpression Id. */ - int ESC = 22; + int SINGLE_LINE_COMMENT_END = 22; /** RegularExpression Id. */ - int SINGLE_QUOTED_STRING = 24; + int ESC = 26; /** RegularExpression Id. */ - int STRING_ESC = 25; + int SINGLE_QUOTED_STRING = 28; /** RegularExpression Id. */ - int DOUBLE_QUOTED_STRING = 27; + int STRING_ESC = 29; /** RegularExpression Id. */ - int INT_LITERAL = 28; + int DOUBLE_QUOTED_STRING = 31; /** RegularExpression Id. */ - int FLOAT_LITERAL = 29; + int INT_LITERAL = 32; /** RegularExpression Id. */ - int DEC_FLT = 30; + int FLOAT_LITERAL = 33; /** RegularExpression Id. */ - int DEC_DIGITS = 31; + int DEC_FLT = 34; /** RegularExpression Id. */ - int EXPONENT = 32; + int DEC_DIGITS = 35; /** RegularExpression Id. */ - int FLT_SUFF = 33; + int EXPONENT = 36; /** RegularExpression Id. */ - int DOUBLE_ESCAPE = 34; + int FLT_SUFF = 37; /** RegularExpression Id. */ - int ESCAPE = 35; + int TEXT = 38; /** RegularExpression Id. */ - int TEXT = 36; + int TEXT_OTHER = 39; /** Lexical state. */ int DEFAULT = 0; /** Lexical state. */ - int IN_SINGLE_LINE_COMMENT = 1; + int ARGS = 1; + /** Lexical state. */ + int NOT_TEXT = 2; /** Lexical state. */ - int WithinSingleQuoteLiteral = 2; + int IN_SINGLE_LINE_COMMENT = 3; /** Lexical state. */ - int WithinDoubleQuoteLiteral = 3; + int WithinSingleQuoteLiteral = 4; + /** Lexical state. */ + int WithinDoubleQuoteLiteral = 5; /** Literal token values. */ String[] tokenImage = { "<EOF>", + "\" \"", + "\"\\t\"", + "\"\\n\"", + "\"\\r\"", "\"#if\"", "\"#else\"", "\"#end\"", - "<TRUE>", - "<FALSE>", - "<WHITESPACE>", - "<NEWLINE>", "\"#\"", "\"$\"", - "\"(\"", + "<TRUE>", + "<FALSE>", "\")\"", "\",\"", + "\"[\"", + "\"]\"", + "\"(\"", "\".\"", "<IDENTIFIER>", "<LETTER>", "<DIGIT>", "\"##\"", "<SINGLE_LINE_COMMENT_END>", - "<token of kind 19>", + "<token of kind 23>", "\"\\\'\"", "\"\\\"\"", "<ESC>", - "<token of kind 23>", + "<token of kind 27>", "\"\\\'\"", "<STRING_ESC>", - "<token of kind 26>", + "<token of kind 30>", "\"\\\"\"", "<INT_LITERAL>", "<FLOAT_LITERAL>", @@ -135,9 +141,8 @@ public interface SQLTemplateParserConstants { "<DEC_DIGITS>", "<EXPONENT>", "<FLT_SUFF>", - "\"\\\\\\\\\"", - "\"\\\\\"", "<TEXT>", + "<TEXT_OTHER>", }; }