This is an automated email from the ASF dual-hosted git repository. ddekany pushed a commit to branch 2.3-gae in repository https://gitbox.apache.org/repos/asf/freemarker.git
commit 44e30e0ae281d400e9e4975884b15d751b464256 Author: ddekany <ddek...@apache.org> AuthorDate: Sat Jul 20 17:38:23 2019 +0200 (Moved IntermediateStreamOperationLikeBuiltIn out of BuiltInsForSequences, as it should be usable for FTL hash targets as well.) --- src/main/java/freemarker/core/BuiltIn.java | 2 +- .../java/freemarker/core/BuiltInsForSequences.java | 191 ------------------ .../IntermediateStreamOperationLikeBuiltIn.java | 221 +++++++++++++++++++++ 3 files changed, 222 insertions(+), 192 deletions(-) diff --git a/src/main/java/freemarker/core/BuiltIn.java b/src/main/java/freemarker/core/BuiltIn.java index 4e26f50..5322eed 100644 --- a/src/main/java/freemarker/core/BuiltIn.java +++ b/src/main/java/freemarker/core/BuiltIn.java @@ -397,7 +397,7 @@ abstract class BuiltIn extends Expression implements Cloneable { * If the built-in supports a lazily generated value as its left operand (the target). * Don't confuse this with what's allowed for result of the built-in itself; that's influenced by * {@link Expression#enableLazilyGeneratedResult()} (and so - * {@link BuiltInsForSequences.IntermediateStreamOperationLikeBuiltIn#isLazilyGeneratedTargetResultSupported()}). + * {@link IntermediateStreamOperationLikeBuiltIn#isLazilyGeneratedTargetResultSupported()}). */ protected boolean isLazilyGeneratedTargetResultSupported() { return false; diff --git a/src/main/java/freemarker/core/BuiltInsForSequences.java b/src/main/java/freemarker/core/BuiltInsForSequences.java index b0cc82e..7c8afc5 100644 --- a/src/main/java/freemarker/core/BuiltInsForSequences.java +++ b/src/main/java/freemarker/core/BuiltInsForSequences.java @@ -37,7 +37,6 @@ import freemarker.template.TemplateCollectionModelEx; import freemarker.template.TemplateDateModel; import freemarker.template.TemplateException; import freemarker.template.TemplateHashModel; -import freemarker.template.TemplateMethodModel; import freemarker.template.TemplateMethodModelEx; import freemarker.template.TemplateModel; import freemarker.template.TemplateModelException; @@ -1001,196 +1000,6 @@ class BuiltInsForSequences { } - /** - * Built-in that's similar to a Java 8 Stream intermediate operation. To be on the safe side, by default these - * are eager, and just produce a {@link TemplateSequenceModel}. But when circumstances allow, they become lazy, - * similarly to Java 8 Stream intermediate operations. Another characteristic of these built-ins is that they - * usually accept lambda expressions as parameters. - */ - static abstract class IntermediateStreamOperationLikeBuiltIn extends BuiltInWithParseTimeParameters { - - private Expression elementTransformerExp; - private ElementTransformer precreatedElementTransformer; - private boolean lazilyGeneratedResultEnabled; - - @Override - void bindToParameters(List<Expression> parameters, Token openParen, Token closeParen) throws ParseException { - // At the moment all built-ins of this kind requires 1 parameter. - if (parameters.size() != 1) { - throw newArgumentCountException("requires exactly 1", openParen, closeParen); - } - this.elementTransformerExp = parameters.get(0); - if (elementTransformerExp instanceof LocalLambdaExpression) { - LocalLambdaExpression localLambdaExp = (LocalLambdaExpression) elementTransformerExp; - checkLocalLambdaParamCount(localLambdaExp, 1); - // We can't do this with other kind of expressions, like a function or method reference, as they - // need to be evaluated on runtime: - precreatedElementTransformer = new LocalLambdaElementTransformer(localLambdaExp); - } - } - - @Override - protected final boolean isLocalLambdaParameterSupported() { - return true; - } - - @Override - final void enableLazilyGeneratedResult() { - this.lazilyGeneratedResultEnabled = true; - } - - /** Tells if {@link #enableLazilyGeneratedResult()} was called. */ - protected final boolean isLazilyGeneratedResultEnabled() { - return lazilyGeneratedResultEnabled; - } - - @Override - protected final boolean isLazilyGeneratedTargetResultSupported() { - return true; - } - - protected List<Expression> getArgumentsAsList() { - return Collections.singletonList(elementTransformerExp); - } - - protected int getArgumentsCount() { - return 1; - } - - protected Expression getArgumentParameterValue(int argIdx) { - if (argIdx != 0) { - throw new IndexOutOfBoundsException(); - } - return elementTransformerExp; - } - - protected Expression getElementTransformerExp() { - return elementTransformerExp; - } - - protected void cloneArguments( - Expression clone, String replacedIdentifier, Expression replacement, ReplacemenetState replacementState) { - ((IntermediateStreamOperationLikeBuiltIn) clone).elementTransformerExp - = elementTransformerExp.deepCloneWithIdentifierReplaced( - replacedIdentifier, replacement, replacementState); - } - - TemplateModel _eval(Environment env) throws TemplateException { - TemplateModel targetValue = target.eval(env); - - final TemplateModelIterator targetIterator; - final boolean targetIsSequence; - { - if (targetValue instanceof TemplateCollectionModel) { - targetIterator = isLazilyGeneratedResultEnabled() - ? new LazyCollectionTemplateModelIterator((TemplateCollectionModel) targetValue) - : ((TemplateCollectionModel) targetValue).iterator(); - targetIsSequence = targetValue instanceof LazilyGeneratedCollectionModel - ? ((LazilyGeneratedCollectionModel) targetValue).isSequence() - : targetValue instanceof TemplateSequenceModel; - } else if (targetValue instanceof TemplateSequenceModel) { - targetIterator = new LazySequenceIterator((TemplateSequenceModel) targetValue); - targetIsSequence = true; - } else { - throw new NonSequenceOrCollectionException(target, targetValue, env); - } - } - - return calculateResult( - targetIterator, targetValue, targetIsSequence, - evalElementTransformerExp(env), - env); - } - - private ElementTransformer evalElementTransformerExp(Environment env) throws TemplateException { - if (precreatedElementTransformer != null) { - return precreatedElementTransformer; - } - - TemplateModel elementTransformerModel = elementTransformerExp.eval(env); - if (elementTransformerModel instanceof TemplateMethodModel) { - return new MethodElementTransformer((TemplateMethodModel) elementTransformerModel); - } else if (elementTransformerModel instanceof Macro) { - return new FunctionElementTransformer((Macro) elementTransformerModel, elementTransformerExp); - } else { - throw new NonMethodException(elementTransformerExp, elementTransformerModel, true, true, null, env); - } - } - - /** - * @param lhoIterator Use this to read the elements of the left hand operand - * @param lho Maybe needed for operations specific to the built-in, like getting the size, otherwise use the - * {@code lhoIterator} only. - * @param lhoIsSequence See {@link LazilyGeneratedCollectionModel#isSequence} - * @param elementTransformer The argument to the built-in (typically a lambda expression) - * - * @return {@link TemplateSequenceModel} or {@link TemplateCollectionModel} or {@link TemplateModelIterator}. - */ - protected abstract TemplateModel calculateResult( - TemplateModelIterator lhoIterator, TemplateModel lho, boolean lhoIsSequence, - ElementTransformer elementTransformer, - Environment env) throws TemplateException; - - /** - * Wraps the built-in argument that specifies how to transform the elements of the sequence, to hide the - * complexity of doing that. - */ - interface ElementTransformer { - TemplateModel transformElement(TemplateModel element, Environment env) throws TemplateException; - } - - /** {@link ElementTransformer} that wraps a local lambda expression. */ - private static class LocalLambdaElementTransformer implements ElementTransformer { - private final LocalLambdaExpression elementTransformerExp; - - public LocalLambdaElementTransformer(LocalLambdaExpression elementTransformerExp) { - this.elementTransformerExp = elementTransformerExp; - } - - public TemplateModel transformElement(TemplateModel element, Environment env) throws TemplateException { - return elementTransformerExp.invokeLambdaDefinedFunction(element, env); - } - } - - /** {@link ElementTransformer} that wraps a (Java) method call. */ - private static class MethodElementTransformer implements ElementTransformer { - private final TemplateMethodModel elementTransformer; - - public MethodElementTransformer(TemplateMethodModel elementTransformer) { - this.elementTransformer = elementTransformer; - } - - public TemplateModel transformElement(TemplateModel element, Environment env) - throws TemplateModelException { - Object result = elementTransformer.exec(Collections.singletonList(element)); - return result instanceof TemplateModel ? (TemplateModel) result : env.getObjectWrapper().wrap(result); - } - } - - /** {@link ElementTransformer} that wraps a call to an FTL function (things defined with {@code #function}). */ - private static class FunctionElementTransformer implements ElementTransformer { - private final Macro templateTransformer; - private final Expression elementTransformerExp; - - public FunctionElementTransformer(Macro templateTransformer, Expression elementTransformerExp) { - this.templateTransformer = templateTransformer; - this.elementTransformerExp = elementTransformerExp; - } - - public TemplateModel transformElement(TemplateModel element, Environment env) throws - TemplateException { - // #function-s were originally designed to be called from templates directly, so they expect an - // Expression as argument. So we have to create a fake one. - ExpressionWithFixedResult functionArgExp = new ExpressionWithFixedResult( - element, elementTransformerExp); - return env.invokeFunction(env, templateTransformer, - Collections.singletonList(functionArgExp), - elementTransformerExp); - } - } - - } - static class filterBI extends IntermediateStreamOperationLikeBuiltIn { protected TemplateModel calculateResult( diff --git a/src/main/java/freemarker/core/IntermediateStreamOperationLikeBuiltIn.java b/src/main/java/freemarker/core/IntermediateStreamOperationLikeBuiltIn.java new file mode 100644 index 0000000..a270276 --- /dev/null +++ b/src/main/java/freemarker/core/IntermediateStreamOperationLikeBuiltIn.java @@ -0,0 +1,221 @@ +/* + * 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 freemarker.core; + +import java.util.Collections; +import java.util.List; + +import freemarker.template.TemplateCollectionModel; +import freemarker.template.TemplateException; +import freemarker.template.TemplateMethodModel; +import freemarker.template.TemplateModel; +import freemarker.template.TemplateModelException; +import freemarker.template.TemplateModelIterator; +import freemarker.template.TemplateSequenceModel; + +/** + * Built-in that's similar to a Java 8 Stream intermediate operation. To be on the safe side, by default these + * are eager, and just produce a {@link TemplateSequenceModel}. But when circumstances allow, they become lazy, + * similarly to Java 8 Stream intermediate operations. Another characteristic of these built-ins is that they + * usually accept lambda expressions as parameters. + */ +abstract class IntermediateStreamOperationLikeBuiltIn extends BuiltInWithParseTimeParameters { + + private Expression elementTransformerExp; + private ElementTransformer precreatedElementTransformer; + private boolean lazilyGeneratedResultEnabled; + + @Override + void bindToParameters(List<Expression> parameters, Token openParen, Token closeParen) throws ParseException { + // At the moment all built-ins of this kind requires 1 parameter. + if (parameters.size() != 1) { + throw newArgumentCountException("requires exactly 1", openParen, closeParen); + } + this.elementTransformerExp = parameters.get(0); + if (elementTransformerExp instanceof LocalLambdaExpression) { + LocalLambdaExpression localLambdaExp = (LocalLambdaExpression) elementTransformerExp; + checkLocalLambdaParamCount(localLambdaExp, 1); + // We can't do this with other kind of expressions, like a function or method reference, as they + // need to be evaluated on runtime: + precreatedElementTransformer = new LocalLambdaElementTransformer(localLambdaExp); + } + } + + @Override + protected final boolean isLocalLambdaParameterSupported() { + return true; + } + + @Override + final void enableLazilyGeneratedResult() { + this.lazilyGeneratedResultEnabled = true; + } + + /** Tells if {@link #enableLazilyGeneratedResult()} was called. */ + protected final boolean isLazilyGeneratedResultEnabled() { + return lazilyGeneratedResultEnabled; + } + + @Override + protected final boolean isLazilyGeneratedTargetResultSupported() { + return true; + } + + protected List<Expression> getArgumentsAsList() { + return Collections.singletonList(elementTransformerExp); + } + + protected int getArgumentsCount() { + return 1; + } + + protected Expression getArgumentParameterValue(int argIdx) { + if (argIdx != 0) { + throw new IndexOutOfBoundsException(); + } + return elementTransformerExp; + } + + protected Expression getElementTransformerExp() { + return elementTransformerExp; + } + + protected void cloneArguments( + Expression clone, String replacedIdentifier, Expression replacement, ReplacemenetState replacementState) { + ((IntermediateStreamOperationLikeBuiltIn) clone).elementTransformerExp + = elementTransformerExp.deepCloneWithIdentifierReplaced( + replacedIdentifier, replacement, replacementState); + } + + TemplateModel _eval(Environment env) throws TemplateException { + TemplateModel targetValue = target.eval(env); + + final TemplateModelIterator targetIterator; + final boolean targetIsSequence; + { + if (targetValue instanceof TemplateCollectionModel) { + targetIterator = isLazilyGeneratedResultEnabled() + ? new LazyCollectionTemplateModelIterator((TemplateCollectionModel) targetValue) + : ((TemplateCollectionModel) targetValue).iterator(); + targetIsSequence = targetValue instanceof LazilyGeneratedCollectionModel + ? ((LazilyGeneratedCollectionModel) targetValue).isSequence() + : targetValue instanceof TemplateSequenceModel; + } else if (targetValue instanceof TemplateSequenceModel) { + targetIterator = new LazySequenceIterator((TemplateSequenceModel) targetValue); + targetIsSequence = true; + } else { + throw new NonSequenceOrCollectionException(target, targetValue, env); + } + } + + return calculateResult( + targetIterator, targetValue, targetIsSequence, + evalElementTransformerExp(env), + env); + } + + private ElementTransformer evalElementTransformerExp(Environment env) throws TemplateException { + if (precreatedElementTransformer != null) { + return precreatedElementTransformer; + } + + TemplateModel elementTransformerModel = elementTransformerExp.eval(env); + if (elementTransformerModel instanceof TemplateMethodModel) { + return new MethodElementTransformer((TemplateMethodModel) elementTransformerModel); + } else if (elementTransformerModel instanceof Macro) { + return new FunctionElementTransformer((Macro) elementTransformerModel, elementTransformerExp); + } else { + throw new NonMethodException(elementTransformerExp, elementTransformerModel, true, true, null, env); + } + } + + /** + * @param lhoIterator Use this to read the elements of the left hand operand + * @param lho Maybe needed for operations specific to the built-in, like getting the size, otherwise use the + * {@code lhoIterator} only. + * @param lhoIsSequence See {@link LazilyGeneratedCollectionModel#isSequence} + * @param elementTransformer The argument to the built-in (typically a lambda expression) + * + * @return {@link TemplateSequenceModel} or {@link TemplateCollectionModel} or {@link TemplateModelIterator}. + */ + protected abstract TemplateModel calculateResult( + TemplateModelIterator lhoIterator, TemplateModel lho, boolean lhoIsSequence, + ElementTransformer elementTransformer, + Environment env) throws TemplateException; + + /** + * Wraps the built-in argument that specifies how to transform the elements of the sequence, to hide the + * complexity of doing that. + */ + interface ElementTransformer { + TemplateModel transformElement(TemplateModel element, Environment env) throws TemplateException; + } + + /** {@link ElementTransformer} that wraps a local lambda expression. */ + private static class LocalLambdaElementTransformer implements ElementTransformer { + private final LocalLambdaExpression elementTransformerExp; + + public LocalLambdaElementTransformer(LocalLambdaExpression elementTransformerExp) { + this.elementTransformerExp = elementTransformerExp; + } + + public TemplateModel transformElement(TemplateModel element, Environment env) throws TemplateException { + return elementTransformerExp.invokeLambdaDefinedFunction(element, env); + } + } + + /** {@link ElementTransformer} that wraps a (Java) method call. */ + private static class MethodElementTransformer implements ElementTransformer { + private final TemplateMethodModel elementTransformer; + + public MethodElementTransformer(TemplateMethodModel elementTransformer) { + this.elementTransformer = elementTransformer; + } + + public TemplateModel transformElement(TemplateModel element, Environment env) + throws TemplateModelException { + Object result = elementTransformer.exec(Collections.singletonList(element)); + return result instanceof TemplateModel ? (TemplateModel) result : env.getObjectWrapper().wrap(result); + } + } + + /** {@link ElementTransformer} that wraps a call to an FTL function (things defined with {@code #function}). */ + private static class FunctionElementTransformer implements ElementTransformer { + private final Macro templateTransformer; + private final Expression elementTransformerExp; + + public FunctionElementTransformer(Macro templateTransformer, Expression elementTransformerExp) { + this.templateTransformer = templateTransformer; + this.elementTransformerExp = elementTransformerExp; + } + + public TemplateModel transformElement(TemplateModel element, Environment env) throws + TemplateException { + // #function-s were originally designed to be called from templates directly, so they expect an + // Expression as argument. So we have to create a fake one. + ExpressionWithFixedResult functionArgExp = new ExpressionWithFixedResult( + element, elementTransformerExp); + return env.invokeFunction(env, templateTransformer, + Collections.singletonList(functionArgExp), + elementTransformerExp); + } + } + +}