http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/ast/CustomAttribute.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ast/CustomAttribute.java b/src/main/java/org/apache/freemarker/core/ast/CustomAttribute.java deleted file mode 100644 index 8eda14d..0000000 --- a/src/main/java/org/apache/freemarker/core/ast/CustomAttribute.java +++ /dev/null @@ -1,265 +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.freemarker.core.ast; - -import org.apache.freemarker.core.Configuration; -import org.apache.freemarker.core.Template; - -/** - * A class that allows one to associate custom data with a {@link Configuration}, a {@link Template}, or - * {@link Environment}. - * - * <p>This API has similar approach to that of {@link ThreadLocal} (which allows one to associate - * custom data with a thread). With an example:</p> - * - * <pre> - * // The object identity itself will serve as the attribute identifier; there's no attribute name String: - * public static final CustomAttribute MY_ATTR = new CustomAttribute(CustomAttribute.SCOPE_CONFIGURATION); - * ... - * // Set the attribute in this particular Configuration object: - * MY_ATTR.set(myAttrValue, cfg); - * ... - * // Read the attribute from this particular Configuration object: - * myAttrValue = MY_ATTR.get(cfg); - * </pre> - */ -// [2.4] Use generics; type parameter used for the type of the stored value -public class CustomAttribute { - - /** - * Constant used in the constructor specifying that this attribute is {@link Environment}-scoped. - */ - public static final int SCOPE_ENVIRONMENT = 0; - - /** - * Constant used in the constructor specifying that this attribute is {@link Template}-scoped. - */ - public static final int SCOPE_TEMPLATE = 1; - - /** - * Constant used in the constructor specifying that this attribute is {@link Configuration}-scoped. - */ - public static final int SCOPE_CONFIGURATION = 2; - - // We use an internal key instead of 'this' so that malicious subclasses - // overriding equals() and hashCode() can't gain access to other attribute - // values. That's also the reason why get() and set() are marked final. - private final Object key = new Object(); - private final int scope; - - /** - * Creates a new custom attribute with the specified scope - * @param scope one of <tt>SCOPE_</tt> constants. - */ - public CustomAttribute(int scope) { - if (scope != SCOPE_ENVIRONMENT && - scope != SCOPE_TEMPLATE && - scope != SCOPE_CONFIGURATION) { - throw new IllegalArgumentException(); - } - this.scope = scope; - } - - /** - * This method is invoked when {@link #get()} is invoked without - * {@link #set(Object)} being invoked before it to define the value in the - * current scope. Override it to create the attribute value on-demand. - * @return the initial value for the custom attribute. By default returns null. - */ - protected Object create() { - return null; - } - - /** - * Gets the attribute from the appropriate scope that's accessible through the specified {@link Environment}. If - * the attribute has {@link #SCOPE_ENVIRONMENT} scope, it will be get from the given {@link Environment} directly. - * If the attribute has {@link #SCOPE_TEMPLATE} scope, it will be get from the parent of the given - * {@link Environment} (that is, in {@link Environment#getParent()}) directly). If the attribute has - * {@link #SCOPE_CONFIGURATION} scope, it will be get from {@link Environment#getConfiguration()}. - * - * @throws NullPointerException - * If {@code env} is null - * - * @return The new value of the attribute (possibly {@code null}), or {@code null} if the attribute doesn't exist. - * - * @since 2.3.22 - */ - public final Object get(Environment env) { - return getScopeConfigurable(env).getCustomAttribute(key, this); - } - - /** - * Same as {@link #get(Environment)}, but uses {@link Environment#getCurrentEnvironment()} to fill the 2nd argument. - * - * @throws IllegalStateException - * If there is no current {@link Environment}, which is usually the case when the current thread isn't - * processing a template. - */ - public final Object get() { - return getScopeConfigurable(getRequiredCurrentEnvironment()).getCustomAttribute(key, this); - } - - /** - * Gets the value of a {@link Template}-scope attribute from the given {@link Template}. - * - * @throws UnsupportedOperationException - * If this custom attribute has different scope than {@link #SCOPE_TEMPLATE}. - * @throws NullPointerException - * If {@code template} is null - */ - public final Object get(Template template) { - if (scope != SCOPE_TEMPLATE) { - throw new UnsupportedOperationException("This is not a template-scope attribute"); - } - return ((Configurable) template).getCustomAttribute(key, this); - } - - /** - * Same as {@link #get(Template)}, but applies to a {@link TemplateConfiguration}. - * - * @since 2.3.24 - */ - public Object get(TemplateConfiguration templateConfiguration) { - if (scope != SCOPE_TEMPLATE) { - throw new UnsupportedOperationException("This is not a template-scope attribute"); - } - return templateConfiguration.getCustomAttribute(key, this); - } - - /** - * Gets the value of a {@link Configuration}-scope attribute from the given {@link Configuration}. - * - * @throws UnsupportedOperationException - * If this custom attribute has different scope than {@link #SCOPE_CONFIGURATION}. - * @throws NullPointerException - * If {@code cfg} is null - * - * @since 2.3.22 - */ - public final Object get(Configuration cfg) { - if (scope != SCOPE_CONFIGURATION) { - throw new UnsupportedOperationException("This is not a template-scope attribute"); - } - return ((Configurable) cfg).getCustomAttribute(key, this); - } - - /** - * Sets the attribute inside the appropriate scope that's accessible through the specified {@link Environment}. If - * the attribute has {@link #SCOPE_ENVIRONMENT} scope, it will be set in the given {@link Environment} directly. If - * the attribute has {@link #SCOPE_TEMPLATE} scope, it will be set in the parent of the given {@link Environment} - * (that is, in {@link Environment#getParent()}) directly). If the attribute has {@link #SCOPE_CONFIGURATION} scope, - * it will be set in {@link Environment#getConfiguration()}. - * - * @param value - * The new value of the attribute. Can be {@code null}. - * - * @throws NullPointerException - * If {@code env} is null - * - * @since 2.3.22 - */ - public final void set(Object value, Environment env) { - getScopeConfigurable(env).setCustomAttribute(key, value); - } - - /** - * Same as {@link #set(Object, Environment)}, but uses {@link Environment#getCurrentEnvironment()} to fill the 2nd - * argument. - * - * @throws IllegalStateException - * If there is no current {@link Environment}, which is usually the case when the current thread isn't - * processing a template. - */ - public final void set(Object value) { - getScopeConfigurable(getRequiredCurrentEnvironment()).setCustomAttribute(key, value); - } - - /** - * Sets the value of a {@link Template}-scope attribute in the given {@link Template}. - * - * @param value - * The new value of the attribute. Can be {@code null}. - * - * @throws UnsupportedOperationException - * If this custom attribute has different scope than {@link #SCOPE_TEMPLATE}. - * @throws NullPointerException - * If {@code template} is null - */ - public final void set(Object value, Template template) { - if (scope != SCOPE_TEMPLATE) { - throw new UnsupportedOperationException("This is not a template-scope attribute"); - } - ((Configurable) template).setCustomAttribute(key, value); - } - - /** - * Same as {@link #set(Object, Template)}, but applicable to a {@link TemplateConfiguration}. - * - * @since 2.3.24 - */ - public final void set(Object value, TemplateConfiguration templateConfiguration) { - if (scope != SCOPE_TEMPLATE) { - throw new UnsupportedOperationException("This is not a template-scope attribute"); - } - templateConfiguration.setCustomAttribute(key, value); - } - - /** - * Sets the value of a {@link Configuration}-scope attribute in the given {@link Configuration}. - * - * @param value - * The new value of the attribute. Can be {@code null}. - * - * @throws UnsupportedOperationException - * If this custom attribute has different scope than {@link #SCOPE_CONFIGURATION}. - * @throws NullPointerException - * If {@code cfg} is null - * - * @since 2.3.22 - */ - public final void set(Object value, Configuration cfg) { - if (scope != SCOPE_CONFIGURATION) { - throw new UnsupportedOperationException("This is not a configuration-scope attribute"); - } - ((Configurable) cfg).setCustomAttribute(key, value); - } - - private Environment getRequiredCurrentEnvironment() { - Environment c = Environment.getCurrentEnvironment(); - if (c == null) { - throw new IllegalStateException("No current environment"); - } - return c; - } - - private Configurable getScopeConfigurable(Environment env) throws Error { - switch (scope) { - case SCOPE_ENVIRONMENT: - return env; - case SCOPE_TEMPLATE: - return env.getParent(); - case SCOPE_CONFIGURATION: - return env.getParent().getParent(); - default: - throw new BugException(); - } - } - -}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/ast/DefaultToExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ast/DefaultToExpression.java b/src/main/java/org/apache/freemarker/core/ast/DefaultToExpression.java deleted file mode 100755 index f6aef0e..0000000 --- a/src/main/java/org/apache/freemarker/core/ast/DefaultToExpression.java +++ /dev/null @@ -1,145 +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.freemarker.core.ast; - - -import org.apache.freemarker.core.TemplateException; -import org.apache.freemarker.core.model.TemplateCollectionModel; -import org.apache.freemarker.core.model.TemplateHashModelEx; -import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.model.TemplateScalarModel; -import org.apache.freemarker.core.model.TemplateSequenceModel; -import org.apache.freemarker.core.model.impl.SimpleCollection; - -/** {@code exp!defExp}, {@code (exp)!defExp} and the same two with {@code (exp)!}. */ -class DefaultToExpression extends Expression { - - private static final TemplateCollectionModel EMPTY_COLLECTION = new SimpleCollection(new java.util.ArrayList(0)); - - static private class EmptyStringAndSequence - implements TemplateScalarModel, TemplateSequenceModel, TemplateHashModelEx { - @Override - public String getAsString() { - return ""; - } - @Override - public TemplateModel get(int i) { - return null; - } - @Override - public TemplateModel get(String s) { - return null; - } - @Override - public int size() { - return 0; - } - @Override - public boolean isEmpty() { - return true; - } - @Override - public TemplateCollectionModel keys() { - return EMPTY_COLLECTION; - } - @Override - public TemplateCollectionModel values() { - return EMPTY_COLLECTION; - } - - } - - static final TemplateModel EMPTY_STRING_AND_SEQUENCE = new EmptyStringAndSequence(); - - private final Expression lho, rho; - - DefaultToExpression(Expression lho, Expression rho) { - this.lho = lho; - this.rho = rho; - } - - @Override - TemplateModel _eval(Environment env) throws TemplateException { - TemplateModel left; - if (lho instanceof ParentheticalExpression) { - boolean lastFIRE = env.setFastInvalidReferenceExceptions(true); - try { - left = lho.eval(env); - } catch (InvalidReferenceException ire) { - left = null; - } finally { - env.setFastInvalidReferenceExceptions(lastFIRE); - } - } else { - left = lho.eval(env); - } - - if (left != null) return left; - else if (rho == null) return EMPTY_STRING_AND_SEQUENCE; - else return rho.eval(env); - } - - @Override - boolean isLiteral() { - return false; - } - - @Override - protected Expression deepCloneWithIdentifierReplaced_inner(String replacedIdentifier, Expression replacement, ReplacemenetState replacementState) { - return new DefaultToExpression( - lho.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), - rho != null - ? rho.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState) - : null); - } - - @Override - public String getCanonicalForm() { - if (rho == null) { - return lho.getCanonicalForm() + '!'; - } - return lho.getCanonicalForm() + '!' + rho.getCanonicalForm(); - } - - @Override - String getNodeTypeSymbol() { - return "...!..."; - } - - @Override - int getParameterCount() { - return 2; - } - - @Override - Object getParameterValue(int idx) { - switch (idx) { - case 0: return lho; - case 1: return rho; - default: throw new IndexOutOfBoundsException(); - } - } - - @Override - ParameterRole getParameterRole(int idx) { - return ParameterRole.forBinaryOperatorOperand(idx); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/ast/DirectiveCallPlace.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ast/DirectiveCallPlace.java b/src/main/java/org/apache/freemarker/core/ast/DirectiveCallPlace.java deleted file mode 100644 index e69d797..0000000 --- a/src/main/java/org/apache/freemarker/core/ast/DirectiveCallPlace.java +++ /dev/null @@ -1,139 +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.freemarker.core.ast; - -import java.util.IdentityHashMap; - -import org.apache.freemarker.core.Configuration; -import org.apache.freemarker.core.Template; -import org.apache.freemarker.core.model.TemplateDirectiveModel; -import org.apache.freemarker.core.model.TemplateTransformModel; -import org.apache.freemarker.core.util.ObjectFactory; - -/** - * Gives information about the place where a directive is called from, also lets you attach a custom data object to that - * place. Each directive call in a template has its own {@link DirectiveCallPlace} object (even when they call the same - * directive with the same parameters). The life cycle of the {@link DirectiveCallPlace} object is bound to the - * {@link Template} object that contains the directive call. Hence, the {@link DirectiveCallPlace} object and the custom - * data you put into it is cached together with the {@link Template} (and templates are normally cached - see - * {@link Configuration#getTemplate(String)}). The custom data is normally initialized on demand, that is, when the - * directive call is first executed, via {@link #getOrCreateCustomData(Object, ObjectFactory)}. - * - * <p> - * Currently this method doesn't give you access to the {@link Template} object, because it's probable that future - * versions of FreeMarker will be able to use the same parsed representation of a "file" for multiple {@link Template} - * objects. Then the call place will be bound to the parsed representation, not to the {@link Template} objects that are - * based on it. - * - * <p> - * <b>Don't implement this interface yourself</b>, as new methods can be added to it any time! It's only meant to be - * implemented by the FreeMarker core. - * - * <p> - * This interface is currently only used for custom directive calls (that is, a {@code <@...>} that calls a - * {@link TemplateDirectiveModel}, {@link TemplateTransformModel}, or a macro). - * - * @see Environment#getCurrentDirectiveCallPlace() - * - * @since 2.3.22 - */ -public interface DirectiveCallPlace { - - /** - * The 1-based column number of the first character of the directive call in the template source code, or -1 if it's - * not known. - */ - int getBeginColumn(); - - /** - * The 1-based line number of the first character of the directive call in the template source code, or -1 if it's - * not known. - */ - int getBeginLine(); - - /** - * The 1-based column number of the last character of the directive call in the template source code, or -1 if it's - * not known. If the directive has an end-tag ({@code </@...>}), then it points to the last character of that. - */ - int getEndColumn(); - - /** - * The 1-based line number of the last character of the directive call in the template source code, or -1 if it's - * not known. If the directive has an end-tag ({@code </@...>}), then it points to the last character of that. - */ - int getEndLine(); - - /** - * Returns the custom data, or if that's {@code null}, then it creates and stores it in an atomic operation then - * returns it. This method is thread-safe, however, it doesn't ensure thread safe (like synchronized) access to the - * custom data itself. See the top-level documentation of {@link DirectiveCallPlace} to understand the scope and - * life-cycle of the custom data. Be sure that the custom data only depends on things that get their final value - * during template parsing, not on runtime settings. - * - * <p> - * This method will block other calls while the {@code objectFactory} is executing, thus, the object will be - * <em>usually</em> created only once, even if multiple threads request the value when it's still {@code null}. It - * doesn't stand though when {@code providerIdentity} mismatches occur (see later). Furthermore, then it's also - * possible that multiple objects created by the same {@link ObjectFactory} will be in use on the same time, because - * of directive executions already running in parallel, and because of memory synchronization delays (hardware - * dependent) between the threads. - * - * @param providerIdentity - * This is usually the class of the {@link TemplateDirectiveModel} that creates (and uses) the custom - * data, or if you are using your own class for the custom data object (as opposed to a class from some - * more generic API), then that class. This is needed as the same call place might calls different - * directives depending on runtime conditions, and so it must be ensured that these directives won't - * accidentally read each other's custom data, ending up with class cast exceptions or worse. In the - * current implementation, if there's a {@code providerIdentity} mismatch (means, the - * {@code providerIdentity} object used when the custom data was last set isn't the exactly same object - * as the one provided with the parameter now), the previous custom data will be just ignored as if it - * was {@code null}. So if multiple directives that use the custom data feature use the same call place, - * the caching of the custom data can be inefficient, as they will keep overwriting each other's custom - * data. (In a more generic implementation the {@code providerIdentity} would be a key in a - * {@link IdentityHashMap}, but then this feature would be slower, while {@code providerIdentity} - * mismatches aren't occurring in most applications.) - * @param objectFactory - * Called when the custom data wasn't yet set, to create its initial value. If this parameter is - * {@code null} and the custom data wasn't set yet, then {@code null} will be returned. The returned - * value of {@link ObjectFactory#createObject()} can be any kind of object, but can't be {@code null}. - * - * @return The current custom data object, or possibly {@code null} if there was no {@link ObjectFactory} provided. - * - * @throws CallPlaceCustomDataInitializationException - * If the {@link ObjectFactory} had to be invoked but failed. - */ - Object getOrCreateCustomData(Object providerIdentity, ObjectFactory objectFactory) - throws CallPlaceCustomDataInitializationException; - - /** - * Tells if the nested content (the body) can be safely cached, as it only depends on the template content (not on - * variable values and such) and has no side-effects (other than writing to the output). Examples of cases that give - * {@code false}: {@code <@foo>Name: } <tt>${name}</tt>{@code</@foo>}, - * {@code <@foo>Name: <#if showIt>Joe</#if></@foo>}. Examples of cases that give {@code true}: - * {@code <@foo>Name: Joe</@foo>}, {@code <@foo />}. Note that we get {@code true} for no nested content, because - * that's equivalent with 0-length nested content in FTL. - * - * <p> - * This method returns a pessimistic result. For example, if it sees a custom directive call, it can't know what it - * does, so it will assume that it's not cacheable. - */ - boolean isNestedOutputCacheable(); - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/ast/DollarVariable.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ast/DollarVariable.java b/src/main/java/org/apache/freemarker/core/ast/DollarVariable.java deleted file mode 100644 index f892766..0000000 --- a/src/main/java/org/apache/freemarker/core/ast/DollarVariable.java +++ /dev/null @@ -1,149 +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.freemarker.core.ast; - -import java.io.IOException; -import java.io.Writer; - -import org.apache.freemarker.core.TemplateException; -import org.apache.freemarker.core.util.FTLUtil; - -/** - * An instruction that outputs the value of an <tt>Expression</tt>. - */ -final class DollarVariable extends Interpolation { - - private final Expression expression; - - /** For {@code #escape x as ...} (legacy auto-escaping) */ - private final Expression escapedExpression; - - /** For OutputFormat-based auto-escaping */ - private final OutputFormat outputFormat; - private final MarkupOutputFormat markupOutputFormat; - private final boolean autoEscape; - - DollarVariable( - Expression expression, Expression escapedExpression, - OutputFormat outputFormat, boolean autoEscape) { - this.expression = expression; - this.escapedExpression = escapedExpression; - this.outputFormat = outputFormat; - markupOutputFormat - = (MarkupOutputFormat) (outputFormat instanceof MarkupOutputFormat ? outputFormat : null); - this.autoEscape = autoEscape; - } - - /** - * Outputs the string value of the enclosed expression. - */ - @Override - TemplateElement[] accept(Environment env) throws TemplateException, IOException { - final Object moOrStr = calculateInterpolatedStringOrMarkup(env); - final Writer out = env.getOut(); - if (moOrStr instanceof String) { - final String s = (String) moOrStr; - if (autoEscape) { - markupOutputFormat.output(s, out); - } else { - out.write(s); - } - } else { - final TemplateMarkupOutputModel mo = (TemplateMarkupOutputModel) moOrStr; - final MarkupOutputFormat moOF = mo.getOutputFormat(); - // ATTENTION: Keep this logic in sync. ?esc/?noEsc's logic! - if (moOF != outputFormat && !outputFormat.isOutputFormatMixingAllowed()) { - final String srcPlainText; - // ATTENTION: Keep this logic in sync. ?esc/?noEsc's logic! - srcPlainText = moOF.getSourcePlainText(mo); - if (srcPlainText == null) { - throw new _TemplateModelException(escapedExpression, - "The value to print is in ", new _DelayedToString(moOF), - " format, which differs from the current output format, ", - new _DelayedToString(outputFormat), ". Format conversion wasn't possible."); - } - if (outputFormat instanceof MarkupOutputFormat) { - ((MarkupOutputFormat) outputFormat).output(srcPlainText, out); - } else { - out.write(srcPlainText); - } - } else { - moOF.output(mo, out); - } - } - return null; - } - - @Override - protected Object calculateInterpolatedStringOrMarkup(Environment env) throws TemplateException { - return EvalUtil.coerceModelToStringOrMarkup(escapedExpression.eval(env), escapedExpression, null, env); - } - - @Override - protected String dump(boolean canonical, boolean inStringLiteral) { - StringBuilder sb = new StringBuilder(); - sb.append("${"); - final String exprCF = expression.getCanonicalForm(); - sb.append(inStringLiteral ? FTLUtil.escapeStringLiteralPart(exprCF, '"') : exprCF); - sb.append("}"); - if (!canonical && expression != escapedExpression) { - sb.append(" auto-escaped"); - } - return sb.toString(); - } - - @Override - String getNodeTypeSymbol() { - return "${...}"; - } - - @Override - boolean heedsOpeningWhitespace() { - return true; - } - - @Override - boolean heedsTrailingWhitespace() { - return true; - } - - @Override - int getParameterCount() { - return 1; - } - - @Override - Object getParameterValue(int idx) { - if (idx != 0) throw new IndexOutOfBoundsException(); - return expression; - } - - @Override - ParameterRole getParameterRole(int idx) { - if (idx != 0) throw new IndexOutOfBoundsException(); - return ParameterRole.CONTENT; - } - - @Override - boolean isNestedBlockRepeater() { - return false; - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/ast/Dot.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ast/Dot.java b/src/main/java/org/apache/freemarker/core/ast/Dot.java deleted file mode 100644 index c3b3931..0000000 --- a/src/main/java/org/apache/freemarker/core/ast/Dot.java +++ /dev/null @@ -1,94 +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.freemarker.core.ast; - -import org.apache.freemarker.core.TemplateException; -import org.apache.freemarker.core.model.TemplateHashModel; -import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.util._StringUtil; - -/** - * The dot operator. Used to reference items inside a - * <code>TemplateHashModel</code>. - */ -final class Dot extends Expression { - private final Expression target; - private final String key; - - Dot(Expression target, String key) { - this.target = target; - this.key = key; - } - - @Override - TemplateModel _eval(Environment env) throws TemplateException { - TemplateModel leftModel = target.eval(env); - if (leftModel instanceof TemplateHashModel) { - return ((TemplateHashModel) leftModel).get(key); - } - throw new NonHashException(target, leftModel, env); - } - - @Override - public String getCanonicalForm() { - return target.getCanonicalForm() + getNodeTypeSymbol() + _StringUtil.toFTLIdentifierReferenceAfterDot(key); - } - - @Override - String getNodeTypeSymbol() { - return "."; - } - - @Override - boolean isLiteral() { - return target.isLiteral(); - } - - @Override - protected Expression deepCloneWithIdentifierReplaced_inner( - String replacedIdentifier, Expression replacement, ReplacemenetState replacementState) { - return new Dot( - target.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), - key); - } - - @Override - int getParameterCount() { - return 2; - } - - @Override - Object getParameterValue(int idx) { - return idx == 0 ? target : key; - } - - @Override - ParameterRole getParameterRole(int idx) { - return ParameterRole.forBinaryOperatorOperand(idx); - } - - String getRHO() { - return key; - } - - boolean onlyHasIdentifiers() { - return (target instanceof Identifier) || ((target instanceof Dot) && ((Dot) target).onlyHasIdentifiers()); - } -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/ast/DynamicKeyName.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ast/DynamicKeyName.java b/src/main/java/org/apache/freemarker/core/ast/DynamicKeyName.java deleted file mode 100644 index 38f4ee3..0000000 --- a/src/main/java/org/apache/freemarker/core/ast/DynamicKeyName.java +++ /dev/null @@ -1,288 +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.freemarker.core.ast; - -import java.util.ArrayList; - -import org.apache.freemarker.core.TemplateException; -import org.apache.freemarker.core.model.Constants; -import org.apache.freemarker.core.model.TemplateHashModel; -import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.model.TemplateNumberModel; -import org.apache.freemarker.core.model.TemplateScalarModel; -import org.apache.freemarker.core.model.TemplateSequenceModel; -import org.apache.freemarker.core.model.impl.SimpleScalar; -import org.apache.freemarker.core.model.impl.SimpleSequence; - -/** - * {@code target[keyExpression]}, where, in FM 2.3, {@code keyExpression} can be string, a number or a range, - * and {@code target} can be a hash or a sequence. - */ -final class DynamicKeyName extends Expression { - - private final Expression keyExpression; - private final Expression target; - - DynamicKeyName(Expression target, Expression keyExpression) { - this.target = target; - this.keyExpression = keyExpression; - } - - @Override - TemplateModel _eval(Environment env) throws TemplateException { - TemplateModel targetModel = target.eval(env); - target.assertNonNull(targetModel, env); - - TemplateModel keyModel = keyExpression.eval(env); - keyExpression.assertNonNull(keyModel, env); - if (keyModel instanceof TemplateNumberModel) { - int index = keyExpression.modelToNumber(keyModel, env).intValue(); - return dealWithNumericalKey(targetModel, index, env); - } - if (keyModel instanceof TemplateScalarModel) { - String key = EvalUtil.modelToString((TemplateScalarModel) keyModel, keyExpression, env); - return dealWithStringKey(targetModel, key, env); - } - if (keyModel instanceof RangeModel) { - return dealWithRangeKey(targetModel, (RangeModel) keyModel, env); - } - throw new UnexpectedTypeException(keyExpression, keyModel, "number, range, or string", - new Class[] { TemplateNumberModel.class, TemplateScalarModel.class, Range.class }, env); - } - - static private Class[] NUMERICAL_KEY_LHO_EXPECTED_TYPES; - static { - NUMERICAL_KEY_LHO_EXPECTED_TYPES = new Class[1 + NonStringException.STRING_COERCABLE_TYPES.length]; - NUMERICAL_KEY_LHO_EXPECTED_TYPES[0] = TemplateSequenceModel.class; - for (int i = 0; i < NonStringException.STRING_COERCABLE_TYPES.length; i++) { - NUMERICAL_KEY_LHO_EXPECTED_TYPES[i + 1] = NonStringException.STRING_COERCABLE_TYPES[i]; - } - } - - private TemplateModel dealWithNumericalKey(TemplateModel targetModel, - int index, - Environment env) - throws TemplateException { - if (targetModel instanceof TemplateSequenceModel) { - TemplateSequenceModel tsm = (TemplateSequenceModel) targetModel; - int size; - try { - size = tsm.size(); - } catch (Exception e) { - size = Integer.MAX_VALUE; - } - return index < size ? tsm.get(index) : null; - } - - try { - String s = target.evalAndCoerceToPlainText(env); - try { - return new SimpleScalar(s.substring(index, index + 1)); - } catch (IndexOutOfBoundsException e) { - if (index < 0) { - throw new _MiscTemplateException("Negative index not allowed: ", Integer.valueOf(index)); - } - if (index >= s.length()) { - throw new _MiscTemplateException( - "String index out of range: The index was ", Integer.valueOf(index), - " (0-based), but the length of the string is only ", Integer.valueOf(s.length()) , "."); - } - throw new RuntimeException("Can't explain exception", e); - } - } catch (NonStringException e) { - throw new UnexpectedTypeException( - target, targetModel, - "sequence or " + NonStringException.STRING_COERCABLE_TYPES_DESC, - NUMERICAL_KEY_LHO_EXPECTED_TYPES, - (targetModel instanceof TemplateHashModel - ? "You had a numberical value inside the []. Currently that's only supported for " - + "sequences (lists) and strings. To get a Map item with a non-string key, " - + "use myMap?api.get(myKey)." - : null), - env); - } - } - - private TemplateModel dealWithStringKey(TemplateModel targetModel, String key, Environment env) - throws TemplateException { - if (targetModel instanceof TemplateHashModel) { - return((TemplateHashModel) targetModel).get(key); - } - throw new NonHashException(target, targetModel, env); - } - - private TemplateModel dealWithRangeKey(TemplateModel targetModel, RangeModel range, Environment env) - throws UnexpectedTypeException, InvalidReferenceException, TemplateException { - final TemplateSequenceModel targetSeq; - final String targetStr; - if (targetModel instanceof TemplateSequenceModel) { - targetSeq = (TemplateSequenceModel) targetModel; - targetStr = null; - } else { - targetSeq = null; - try { - targetStr = target.evalAndCoerceToPlainText(env); - } catch (NonStringException e) { - throw new UnexpectedTypeException( - target, target.eval(env), - "sequence or " + NonStringException.STRING_COERCABLE_TYPES_DESC, - NUMERICAL_KEY_LHO_EXPECTED_TYPES, env); - } - } - - final int size = range.size(); - final boolean rightUnbounded = range.isRightUnbounded(); - final boolean rightAdaptive = range.isRightAdaptive(); - - // Right bounded empty ranges are accepted even if the begin index is out of bounds. That's because a such range - // produces an empty sequence, which thus doesn't contain any illegal indexes. - if (!rightUnbounded && size == 0) { - return emptyResult(targetSeq != null); - } - - final int firstIdx = range.getBegining(); - if (firstIdx < 0) { - throw new _MiscTemplateException(keyExpression, - "Negative range start index (", Integer.valueOf(firstIdx), - ") isn't allowed for a range used for slicing."); - } - - final int targetSize = targetStr != null ? targetStr.length() : targetSeq.size(); - final int step = range.getStep(); - - // Right-adaptive increasing ranges can start 1 after the last element of the target, because they are like - // ranges with exclusive end index of at most targetSize. Thence a such range is just an empty list of indexes, - // and thus it isn't out-of-bounds. - // Right-adaptive decreasing ranges has exclusive end -1, so it can't help on a to high firstIndex. - // Right-bounded ranges at this point aren't empty, so the right index surely can't reach targetSize. - if (rightAdaptive && step == 1 ? firstIdx > targetSize : firstIdx >= targetSize) { - throw new _MiscTemplateException(keyExpression, - "Range start index ", Integer.valueOf(firstIdx), " is out of bounds, because the sliced ", - (targetStr != null ? "string" : "sequence"), - " has only ", Integer.valueOf(targetSize), " ", (targetStr != null ? "character(s)" : "element(s)"), - ". ", "(Note that indices are 0-based)."); - } - - final int resultSize; - if (!rightUnbounded) { - final int lastIdx = firstIdx + (size - 1) * step; - if (lastIdx < 0) { - if (!rightAdaptive) { - throw new _MiscTemplateException(keyExpression, - "Negative range end index (", Integer.valueOf(lastIdx), - ") isn't allowed for a range used for slicing."); - } else { - resultSize = firstIdx + 1; - } - } else if (lastIdx >= targetSize) { - if (!rightAdaptive) { - throw new _MiscTemplateException(keyExpression, - "Range end index ", Integer.valueOf(lastIdx), " is out of bounds, because the sliced ", - (targetStr != null ? "string" : "sequence"), - " has only ", Integer.valueOf(targetSize), " ", (targetStr != null ? "character(s)" : "element(s)"), - ". (Note that indices are 0-based)."); - } else { - resultSize = Math.abs(targetSize - firstIdx); - } - } else { - resultSize = size; - } - } else { - resultSize = targetSize - firstIdx; - } - - if (resultSize == 0) { - return emptyResult(targetSeq != null); - } - if (targetSeq != null) { - ArrayList/*<TemplateModel>*/ list = new ArrayList(resultSize); - int srcIdx = firstIdx; - for (int i = 0; i < resultSize; i++) { - list.add(targetSeq.get(srcIdx)); - srcIdx += step; - } - // List items are already wrapped, so the wrapper will be null: - return new SimpleSequence(list, null); - } else { - final int exclEndIdx; - if (step < 0 && resultSize > 1) { - if (!(range.isAffactedByStringSlicingBug() && resultSize == 2)) { - throw new _MiscTemplateException(keyExpression, - "Decreasing ranges aren't allowed for slicing strings (as it would give reversed text). " - + "The index range was: first = ", Integer.valueOf(firstIdx), - ", last = ", Integer.valueOf(firstIdx + (resultSize - 1) * step)); - } else { - // Emulate the legacy bug, where "foo"[n .. n-1] gives "" instead of an error (if n >= 1). - // Fix this in FTL [2.4] - exclEndIdx = firstIdx; - } - } else { - exclEndIdx = firstIdx + resultSize; - } - - return new SimpleScalar(targetStr.substring(firstIdx, exclEndIdx)); - } - } - - private TemplateModel emptyResult(boolean seq) { - return seq ? Constants.EMPTY_SEQUENCE : TemplateScalarModel.EMPTY_STRING; - } - - @Override - public String getCanonicalForm() { - return target.getCanonicalForm() - + "[" - + keyExpression.getCanonicalForm() - + "]"; - } - - @Override - String getNodeTypeSymbol() { - return "...[...]"; - } - - @Override - boolean isLiteral() { - return constantValue != null || (target.isLiteral() && keyExpression.isLiteral()); - } - - @Override - int getParameterCount() { - return 2; - } - - @Override - Object getParameterValue(int idx) { - return idx == 0 ? target : keyExpression; - } - - @Override - ParameterRole getParameterRole(int idx) { - return idx == 0 ? ParameterRole.LEFT_HAND_OPERAND : ParameterRole.ENCLOSED_OPERAND; - } - - @Override - protected Expression deepCloneWithIdentifierReplaced_inner( - String replacedIdentifier, Expression replacement, ReplacemenetState replacementState) { - return new DynamicKeyName( - target.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), - keyExpression.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState)); - } -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/ast/ElseOfList.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ast/ElseOfList.java b/src/main/java/org/apache/freemarker/core/ast/ElseOfList.java deleted file mode 100644 index 38e1389..0000000 --- a/src/main/java/org/apache/freemarker/core/ast/ElseOfList.java +++ /dev/null @@ -1,77 +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.freemarker.core.ast; - -import java.io.IOException; - -import org.apache.freemarker.core.TemplateException; - -/** - * #else element that belongs to a #list, not to an #if. - */ -final class ElseOfList extends TemplateElement { - - ElseOfList(TemplateElements children) { - setChildren(children); - } - - @Override - TemplateElement[] accept(Environment env) throws TemplateException, IOException { - return getChildBuffer(); - } - - @Override - protected String dump(boolean canonical) { - if (canonical) { - StringBuilder buf = new StringBuilder(); - buf.append('<').append(getNodeTypeSymbol()).append('>'); - buf.append(getChildrenCanonicalForm()); - return buf.toString(); - } else { - return getNodeTypeSymbol(); - } - } - - @Override - String getNodeTypeSymbol() { - return "#else"; - } - - @Override - int getParameterCount() { - return 0; - } - - @Override - Object getParameterValue(int idx) { - throw new IndexOutOfBoundsException(); - } - - @Override - ParameterRole getParameterRole(int idx) { - throw new IndexOutOfBoundsException(); - } - - @Override - boolean isNestedBlockRepeater() { - return false; - } - -}