http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/cache/WebAppTemplateLoader.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/cache/WebAppTemplateLoader.java b/src/main/java/freemarker/cache/WebAppTemplateLoader.java deleted file mode 100644 index 1ddaa68..0000000 --- a/src/main/java/freemarker/cache/WebAppTemplateLoader.java +++ /dev/null @@ -1,296 +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 freemarker.cache; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.Serializable; -import java.lang.reflect.Method; -import java.net.MalformedURLException; -import java.net.URL; -import java.net.URLConnection; -import java.util.Objects; - -import javax.servlet.ServletContext; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import freemarker.template.Configuration; -import freemarker.template.utility.CollectionUtils; -import freemarker.template.utility.NullArgumentException; -import freemarker.template.utility.StringUtil; - -/** - * A {@link TemplateLoader} that uses streams reachable through {@link ServletContext#getResource(String)} as its source - * of templates. - */ -public class WebAppTemplateLoader implements TemplateLoader { - - private static final Logger LOG = LoggerFactory.getLogger("freemarker.cache"); - - private final ServletContext servletContext; - private final String subdirPath; - - private Boolean urlConnectionUsesCaches = false; - - private boolean attemptFileAccess = true; - - /** - * Creates a template loader that will use the specified servlet context to load the resources. It will use - * the base path of <code>"/"</code> meaning templates will be resolved relative to the servlet context root - * location. - * - * @param servletContext - * the servlet context whose {@link ServletContext#getResource(String)} will be used to load the - * templates. - */ - public WebAppTemplateLoader(ServletContext servletContext) { - this(servletContext, "/"); - } - - /** - * Creates a template loader that will use the specified servlet context to load the resources. It will use the - * specified base path, which is interpreted relatively to the context root (does not mater if you start it with "/" - * or not). Path components should be separated by forward slashes independently of the separator character used by - * the underlying operating system. - * - * @param servletContext - * the servlet context whose {@link ServletContext#getResource(String)} will be used to load the - * templates. - * @param subdirPath - * the base path to template resources. - */ - public WebAppTemplateLoader(ServletContext servletContext, String subdirPath) { - NullArgumentException.check("servletContext", servletContext); - NullArgumentException.check("subdirPath", subdirPath); - - subdirPath = subdirPath.replace('\\', '/'); - if (!subdirPath.endsWith("/")) { - subdirPath += "/"; - } - if (!subdirPath.startsWith("/")) { - subdirPath = "/" + subdirPath; - } - this.subdirPath = subdirPath; - this.servletContext = servletContext; - } - - /** - * Getter pair of {@link #setURLConnectionUsesCaches(Boolean)}. - * - * @since 2.3.21 - */ - public Boolean getURLConnectionUsesCaches() { - return urlConnectionUsesCaches; - } - - /** - * It does the same as {@link URLTemplateLoader#setURLConnectionUsesCaches(Boolean)}; see there. - * - * @since 2.3.21 - */ - public void setURLConnectionUsesCaches(Boolean urlConnectionUsesCaches) { - this.urlConnectionUsesCaches = urlConnectionUsesCaches; - } - - /** - * Show class name and some details that are useful in template-not-found errors. - * - * @since 2.3.21 - */ - @Override - public String toString() { - return TemplateLoaderUtils.getClassNameForToString(this) - + "(subdirPath=" + StringUtil.jQuote(subdirPath) - + ", servletContext={contextPath=" + StringUtil.jQuote(getContextPath()) - + ", displayName=" + StringUtil.jQuote(servletContext.getServletContextName()) + "})"; - } - - /** Gets the context path if we are on Servlet 2.5+, or else returns failure description string. */ - private String getContextPath() { - try { - Method m = servletContext.getClass().getMethod("getContextPath", CollectionUtils.EMPTY_CLASS_ARRAY); - return (String) m.invoke(servletContext, CollectionUtils.EMPTY_OBJECT_ARRAY); - } catch (Throwable e) { - return "[can't query before Serlvet 2.5]"; - } - } - - /** - * Getter pair of {@link #setAttemptFileAccess(boolean)}. - * - * @since 2.3.23 - */ - public boolean getAttemptFileAccess() { - return attemptFileAccess; - } - - /** - * Specifies that before loading templates with {@link ServletContext#getResource(String)}, it should try to load - * the template as {@link File}; default is {@code true}, though it's not always recommended anymore. This is a - * workaround for the case when the servlet container doesn't show template modifications after the template was - * already loaded earlier. But it's certainly better to counter this problem by disabling the URL connection cache - * with {@link #setURLConnectionUsesCaches(Boolean)}, which is also the default behavior with - * {@link Configuration#setIncompatibleImprovements(freemarker.template.Version) incompatible_improvements} 2.3.21 - * and later. - * - * @since 2.3.23 - */ - public void setAttemptFileAccess(boolean attemptLoadingFromFile) { - this.attemptFileAccess = attemptLoadingFromFile; - } - - @Override - public TemplateLoaderSession createSession() { - return null; - } - - @Override - public TemplateLoadingResult load(String name, TemplateLoadingSource ifSourceDiffersFrom, - Serializable ifVersionDiffersFrom, TemplateLoaderSession session) throws IOException { - WebAppTemplateLoadingSource source = createSource(name); - if (source == null) { - return TemplateLoadingResult.NOT_FOUND; - } - - if (source.url != null) { - URLConnection conn = source.url.openConnection(); - Boolean urlConnectionUsesCaches = getURLConnectionUsesCaches(); - if (urlConnectionUsesCaches != null) { - conn.setUseCaches(urlConnectionUsesCaches); - } - - // To prevent clustering issues, getLastModified(fallbackToJarLMD=false) - long lmd = URLTemplateLoader.getLastModified(conn, false); - Long version = lmd != -1 ? lmd : null; - - if (ifSourceDiffersFrom != null && ifSourceDiffersFrom.equals(source) - && Objects.equals(ifVersionDiffersFrom, version)) { - return TemplateLoadingResult.NOT_MODIFIED; - } - - return new TemplateLoadingResult(source, version, conn.getInputStream(), null); - } else { // source.file != null - long lmd = source.file.lastModified(); - Long version = lmd != -1 ? lmd : null; - - if (ifSourceDiffersFrom != null && ifSourceDiffersFrom.equals(source) - && Objects.equals(ifVersionDiffersFrom, version)) { - return TemplateLoadingResult.NOT_MODIFIED; - } - - return new TemplateLoadingResult(source, version, new FileInputStream(source.file), null); - } - } - - private WebAppTemplateLoadingSource createSource(String name) { - String fullPath = subdirPath + name; - - if (attemptFileAccess) { - // First try to open as plain file (to bypass servlet container resource caches). - try { - String realPath = servletContext.getRealPath(fullPath); - if (realPath != null) { - File file = new File(realPath); - if (file.canRead() && file.isFile()) { - return new WebAppTemplateLoadingSource(file); - } - } - } catch (SecurityException e) { - ;// ignore - } - } - - // If it fails, try to open it with servletContext.getResource. - URL url = null; - try { - url = servletContext.getResource(fullPath); - } catch (MalformedURLException e) { - if (LOG.isWarnEnabled()) { - LOG.warn("Could not retrieve resource " + StringUtil.jQuoteNoXSS(fullPath), e); - } - return null; - } - return url == null ? null : new WebAppTemplateLoadingSource(url); - } - - @Override - public void resetState() { - // Do nothing - } - - @SuppressWarnings("serial") - private class WebAppTemplateLoadingSource implements TemplateLoadingSource { - private final File file; - private final URL url; - - WebAppTemplateLoadingSource(File file) { - this.file = file; - this.url = null; - } - - WebAppTemplateLoadingSource(URL url) { - this.file = null; - this.url = url; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + getOuterType().hashCode(); - result = prime * result + ((file == null) ? 0 : file.hashCode()); - result = prime * result + ((url == null) ? 0 : url.hashCode()); - return result; - } - - @Override - public boolean equals(Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (getClass() != obj.getClass()) - return false; - WebAppTemplateLoadingSource other = (WebAppTemplateLoadingSource) obj; - if (!getOuterType().equals(other.getOuterType())) - return false; - if (file == null) { - if (other.file != null) - return false; - } else if (!file.equals(other.file)) - return false; - if (url == null) { - if (other.url != null) - return false; - } else if (!url.equals(other.url)) - return false; - return true; - } - - private WebAppTemplateLoader getOuterType() { - return WebAppTemplateLoader.this; - } - - } - -} \ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/cache/_CacheAPI.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/cache/_CacheAPI.java b/src/main/java/freemarker/cache/_CacheAPI.java deleted file mode 100644 index 638be92..0000000 --- a/src/main/java/freemarker/cache/_CacheAPI.java +++ /dev/null @@ -1,45 +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 freemarker.cache; - -import freemarker.template.MalformedTemplateNameException; - -/** - * For internal use only; don't depend on this, there's no backward compatibility guarantee at all! - * This class is to work around the lack of module system in Java, i.e., so that other FreeMarker packages can - * access things inside this package that users shouldn't. - */ -public final class _CacheAPI { - - private _CacheAPI() { - // Not meant to be instantiated - } - - public static String toRootBasedName(TemplateNameFormat templateNameFormat, String baseName, String targetName) - throws MalformedTemplateNameException { - return templateNameFormat.toRootBasedName(baseName, targetName); - } - - public static String normalizeRootBasedName(TemplateNameFormat templateNameFormat, String name) - throws MalformedTemplateNameException { - return templateNameFormat.normalizeRootBasedName(name); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/cache/package.html ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/cache/package.html b/src/main/java/freemarker/cache/package.html deleted file mode 100644 index bf806df..0000000 --- a/src/main/java/freemarker/cache/package.html +++ /dev/null @@ -1,30 +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. ---> -<html> -<head> -</head> -<body bgcolor="white"> -<p>Template <em>loading</em> and caching. -Beside the actual template cache, it contains loaders that can load template -files from the file system, from the classpath, or from a web application -context. If you have specific needs, you can plug custom template loaders into -the system by implementing the template loader interface. -</p> -</body> -</html> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/core/APINotSupportedTemplateException.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/core/APINotSupportedTemplateException.java b/src/main/java/freemarker/core/APINotSupportedTemplateException.java deleted file mode 100644 index d218beb..0000000 --- a/src/main/java/freemarker/core/APINotSupportedTemplateException.java +++ /dev/null @@ -1,76 +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 freemarker.core; - -import freemarker.template.DefaultObjectWrapper; -import freemarker.template.ObjectWrapper; -import freemarker.template.SimpleHash; -import freemarker.template.SimpleSequence; -import freemarker.template.TemplateException; -import freemarker.template.TemplateModel; -import freemarker.template._TemplateAPI; - -/** - * Thrown when {@code ?api} is not supported by a value. - */ -class APINotSupportedTemplateException extends TemplateException { - - APINotSupportedTemplateException(Environment env, Expression blamedExpr, TemplateModel model) { - super(null, env, blamedExpr, buildDescription(env, blamedExpr, model)); - } - - protected static _ErrorDescriptionBuilder buildDescription(Environment env, Expression blamedExpr, - TemplateModel tm) { - final _ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder( - "The value doesn't support ?api. See requirements in the FreeMarker Manual. (" - + "FTL type: ", new _DelayedFTLTypeDescription(tm), - ", TemplateModel class: ", new _DelayedShortClassName(tm.getClass()), - ", ObjectWapper: ", new _DelayedToString(env.getObjectWrapper()), ")" - ).blame(blamedExpr); - - if (blamedExpr.isLiteral()) { - desc.tip("Only adapted Java objects can possibly have API, not values created inside templates."); - } else { - ObjectWrapper ow = env.getObjectWrapper(); - if (ow instanceof DefaultObjectWrapper - && (tm instanceof SimpleHash || tm instanceof SimpleSequence)) { - DefaultObjectWrapper dow = (DefaultObjectWrapper) ow; - if (!dow.getUseAdaptersForContainers()) { - desc.tip("In the FreeMarker configuration, \"", Configurable.OBJECT_WRAPPER_KEY, - "\" is a DefaultObjectWrapper with its \"useAdaptersForContainers\" property set to " - + "false. Setting it to true might solves this problem."); - if (dow.getIncompatibleImprovements().intValue() < _TemplateAPI.VERSION_INT_2_3_22) { - desc.tip("Setting DefaultObjectWrapper's \"incompatibleImprovements\" to 2.3.22 or higher will " - + "change the default value of \"useAdaptersForContainers\" to true."); - } - } else if (tm instanceof SimpleSequence && dow.getForceLegacyNonListCollections()) { - desc.tip("In the FreeMarker configuration, \"", - Configurable.OBJECT_WRAPPER_KEY, - "\" is a DefaultObjectWrapper with its \"forceLegacyNonListCollections\" property set " - + "to true. If you are trying to access the API of a non-List Collection, setting the " - + "\"forceLegacyNonListCollections\" property to false might solves this problem."); - } - } - } - - return desc; - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/core/AddConcatExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/core/AddConcatExpression.java b/src/main/java/freemarker/core/AddConcatExpression.java deleted file mode 100644 index 5523118..0000000 --- a/src/main/java/freemarker/core/AddConcatExpression.java +++ /dev/null @@ -1,306 +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 freemarker.core; - -import java.util.HashSet; -import java.util.Set; - -import freemarker.template.SimpleNumber; -import freemarker.template.SimpleScalar; -import freemarker.template.SimpleSequence; -import freemarker.template.TemplateCollectionModel; -import freemarker.template.TemplateException; -import freemarker.template.TemplateHashModel; -import freemarker.template.TemplateHashModelEx; -import freemarker.template.TemplateModel; -import freemarker.template.TemplateModelException; -import freemarker.template.TemplateModelIterator; -import freemarker.template.TemplateNumberModel; -import freemarker.template.TemplateScalarModel; -import freemarker.template.TemplateSequenceModel; - -/** - * An operator for the + operator. Note that this is treated - * separately from the other 4 arithmetic operators, - * since + is overloaded to mean string concatenation. - */ -final class AddConcatExpression extends Expression { - - private final Expression left; - private final Expression right; - - AddConcatExpression(Expression left, Expression right) { - this.left = left; - this.right = right; - } - - @Override - TemplateModel _eval(Environment env) throws TemplateException { - return _eval(env, this, left, left.eval(env), right, right.eval(env)); - } - - /** - * @param leftExp - * Used for error messages only; can be {@code null} - * @param rightExp - * Used for error messages only; can be {@code null} - */ - static TemplateModel _eval(Environment env, - TemplateObject parent, - Expression leftExp, TemplateModel leftModel, - Expression rightExp, TemplateModel rightModel) - throws TemplateModelException, TemplateException, NonStringException { - if (leftModel instanceof TemplateNumberModel && rightModel instanceof TemplateNumberModel) { - Number first = EvalUtil.modelToNumber((TemplateNumberModel) leftModel, leftExp); - Number second = EvalUtil.modelToNumber((TemplateNumberModel) rightModel, rightExp); - return _evalOnNumbers(env, parent, first, second); - } else if (leftModel instanceof TemplateSequenceModel && rightModel instanceof TemplateSequenceModel) { - return new ConcatenatedSequence((TemplateSequenceModel) leftModel, (TemplateSequenceModel) rightModel); - } else { - boolean hashConcatPossible - = leftModel instanceof TemplateHashModel && rightModel instanceof TemplateHashModel; - try { - // We try string addition first. If hash addition is possible, then instead of throwing exception - // we return null and do hash addition instead. (We can't simply give hash addition a priority, like - // with sequence addition above, as FTL strings are often also FTL hashes.) - Object leftOMOrStr = EvalUtil.coerceModelToStringOrMarkup( - leftModel, leftExp, /* returnNullOnNonCoercableType = */ hashConcatPossible, (String) null, - env); - if (leftOMOrStr == null) { - return _eval_concatenateHashes(leftModel, rightModel); - } - - // Same trick with null return as above. - Object rightOMOrStr = EvalUtil.coerceModelToStringOrMarkup( - rightModel, rightExp, /* returnNullOnNonCoercableType = */ hashConcatPossible, (String) null, - env); - if (rightOMOrStr == null) { - return _eval_concatenateHashes(leftModel, rightModel); - } - - if (leftOMOrStr instanceof String) { - if (rightOMOrStr instanceof String) { - return new SimpleScalar(((String) leftOMOrStr).concat((String) rightOMOrStr)); - } else { // rightOMOrStr instanceof TemplateMarkupOutputModel - TemplateMarkupOutputModel<?> rightMO = (TemplateMarkupOutputModel<?>) rightOMOrStr; - return EvalUtil.concatMarkupOutputs(parent, - rightMO.getOutputFormat().fromPlainTextByEscaping((String) leftOMOrStr), - rightMO); - } - } else { // leftOMOrStr instanceof TemplateMarkupOutputModel - TemplateMarkupOutputModel<?> leftMO = (TemplateMarkupOutputModel<?>) leftOMOrStr; - if (rightOMOrStr instanceof String) { // markup output - return EvalUtil.concatMarkupOutputs(parent, - leftMO, - leftMO.getOutputFormat().fromPlainTextByEscaping((String) rightOMOrStr)); - } else { // rightOMOrStr instanceof TemplateMarkupOutputModel - return EvalUtil.concatMarkupOutputs(parent, - leftMO, - (TemplateMarkupOutputModel<?>) rightOMOrStr); - } - } - } catch (NonStringOrTemplateOutputException e) { - // 2.4: Remove this catch; it's for BC, after reworking hash addition so it doesn't rely on this. But - // user code might throws this (very unlikely), and then in 2.3.x we did catch that too, incorrectly. - if (hashConcatPossible) { - return _eval_concatenateHashes(leftModel, rightModel); - } else { - throw e; - } - } - } - } - - private static TemplateModel _eval_concatenateHashes(TemplateModel leftModel, TemplateModel rightModel) - throws TemplateModelException { - if (leftModel instanceof TemplateHashModelEx && rightModel instanceof TemplateHashModelEx) { - TemplateHashModelEx leftModelEx = (TemplateHashModelEx) leftModel; - TemplateHashModelEx rightModelEx = (TemplateHashModelEx) rightModel; - if (leftModelEx.size() == 0) { - return rightModelEx; - } else if (rightModelEx.size() == 0) { - return leftModelEx; - } else { - return new ConcatenatedHashEx(leftModelEx, rightModelEx); - } - } else { - return new ConcatenatedHash((TemplateHashModel) leftModel, - (TemplateHashModel) rightModel); - } - } - - static TemplateModel _evalOnNumbers(Environment env, TemplateObject parent, Number first, Number second) - throws TemplateException { - ArithmeticEngine ae = EvalUtil.getArithmeticEngine(env, parent); - return new SimpleNumber(ae.add(first, second)); - } - - @Override - boolean isLiteral() { - return constantValue != null || (left.isLiteral() && right.isLiteral()); - } - - @Override - protected Expression deepCloneWithIdentifierReplaced_inner( - String replacedIdentifier, Expression replacement, ReplacemenetState replacementState) { - return new AddConcatExpression( - left.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), - right.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState)); - } - - @Override - public String getCanonicalForm() { - return left.getCanonicalForm() + " + " + right.getCanonicalForm(); - } - - @Override - String getNodeTypeSymbol() { - return "+"; - } - - @Override - int getParameterCount() { - return 2; - } - - @Override - Object getParameterValue(int idx) { - return idx == 0 ? left : right; - } - - @Override - ParameterRole getParameterRole(int idx) { - return ParameterRole.forBinaryOperatorOperand(idx); - } - - private static final class ConcatenatedSequence - implements - TemplateSequenceModel { - private final TemplateSequenceModel left; - private final TemplateSequenceModel right; - - ConcatenatedSequence(TemplateSequenceModel left, TemplateSequenceModel right) { - this.left = left; - this.right = right; - } - - public int size() - throws TemplateModelException { - return left.size() + right.size(); - } - - public TemplateModel get(int i) - throws TemplateModelException { - int ls = left.size(); - return i < ls ? left.get(i) : right.get(i - ls); - } - } - - private static class ConcatenatedHash - implements TemplateHashModel { - protected final TemplateHashModel left; - protected final TemplateHashModel right; - - ConcatenatedHash(TemplateHashModel left, TemplateHashModel right) { - this.left = left; - this.right = right; - } - - public TemplateModel get(String key) - throws TemplateModelException { - TemplateModel model = right.get(key); - return (model != null) ? model : left.get(key); - } - - public boolean isEmpty() - throws TemplateModelException { - return left.isEmpty() && right.isEmpty(); - } - } - - private static final class ConcatenatedHashEx - extends ConcatenatedHash - implements TemplateHashModelEx { - private CollectionAndSequence keys; - private CollectionAndSequence values; - private int size; - - ConcatenatedHashEx(TemplateHashModelEx left, TemplateHashModelEx right) { - super(left, right); - } - - public int size() throws TemplateModelException { - initKeys(); - return size; - } - - public TemplateCollectionModel keys() - throws TemplateModelException { - initKeys(); - return keys; - } - - public TemplateCollectionModel values() - throws TemplateModelException { - initValues(); - return values; - } - - private void initKeys() - throws TemplateModelException { - if (keys == null) { - HashSet keySet = new HashSet(); - SimpleSequence keySeq = new SimpleSequence(32); - addKeys(keySet, keySeq, (TemplateHashModelEx) this.left); - addKeys(keySet, keySeq, (TemplateHashModelEx) this.right); - size = keySet.size(); - keys = new CollectionAndSequence(keySeq); - } - } - - private static void addKeys(Set set, SimpleSequence keySeq, TemplateHashModelEx hash) - throws TemplateModelException { - TemplateModelIterator it = hash.keys().iterator(); - while (it.hasNext()) { - TemplateScalarModel tsm = (TemplateScalarModel) it.next(); - if (set.add(tsm.getAsString())) { - // The first occurence of the key decides the index; - // this is consisten with stuff like java.util.LinkedHashSet. - keySeq.add(tsm); - } - } - } - - private void initValues() - throws TemplateModelException { - if (values == null) { - SimpleSequence seq = new SimpleSequence(size()); - // Note: size() invokes initKeys() if needed. - - int ln = keys.size(); - for (int i = 0; i < ln; i++) { - seq.add(get(((TemplateScalarModel) keys.get(i)).getAsString())); - } - values = new CollectionAndSequence(seq); - } - } - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/core/AliasTargetTemplateValueFormatException.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/core/AliasTargetTemplateValueFormatException.java b/src/main/java/freemarker/core/AliasTargetTemplateValueFormatException.java deleted file mode 100644 index bf37e66..0000000 --- a/src/main/java/freemarker/core/AliasTargetTemplateValueFormatException.java +++ /dev/null @@ -1,36 +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 freemarker.core; - -/** - * Can't create a template format that the template format refers to (typically thrown by alias template formats). - * - * @since 2.3.24 - */ -class AliasTargetTemplateValueFormatException extends TemplateValueFormatException { - - public AliasTargetTemplateValueFormatException(String message, Throwable cause) { - super(message, cause); - } - - public AliasTargetTemplateValueFormatException(String message) { - super(message); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/core/AliasTemplateDateFormatFactory.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/core/AliasTemplateDateFormatFactory.java b/src/main/java/freemarker/core/AliasTemplateDateFormatFactory.java deleted file mode 100644 index b8db181..0000000 --- a/src/main/java/freemarker/core/AliasTemplateDateFormatFactory.java +++ /dev/null @@ -1,91 +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 freemarker.core; - -import java.util.Locale; -import java.util.Map; -import java.util.TimeZone; - -import freemarker.template.utility.StringUtil; - -/** - * Creates an alias to another format, so that the format can be referred to with a simple name in the template, rather - * than as a concrete pattern or other kind of format string. - * - * @since 2.3.24 - */ -public final class AliasTemplateDateFormatFactory extends TemplateDateFormatFactory { - - private final String defaultTargetFormatString; - private final Map<Locale, String> localizedTargetFormatStrings; - - /** - * @param targetFormatString - * The format string this format will be an alias to. - */ - public AliasTemplateDateFormatFactory(String targetFormatString) { - this.defaultTargetFormatString = targetFormatString; - localizedTargetFormatStrings = null; - } - - /** - * @param defaultTargetFormatString - * The format string this format will be an alias to if there's no locale-specific format string for the - * requested locale in {@code localizedTargetFormatStrings} - * @param localizedTargetFormatStrings - * Maps {@link Locale}-s to format strings. If the desired locale doesn't occur in the map, a less - * specific locale is tried, repeatedly until only the language part remains. For example, if locale is - * {@code new Locale("en", "US", "Linux")}, then these keys will be attempted untol a match is found, in - * this order: {@code new Locale("en", "US", "Linux")}, {@code new Locale("en", "US")}, - * {@code new Locale("en")}. If there's still no matching key, the value of the - * {@code targetFormatString} will be used. - */ - public AliasTemplateDateFormatFactory( - String defaultTargetFormatString, Map<Locale, String> localizedTargetFormatStrings) { - this.defaultTargetFormatString = defaultTargetFormatString; - this.localizedTargetFormatStrings = localizedTargetFormatStrings; - } - - @Override - public TemplateDateFormat get(String params, int dateType, Locale locale, TimeZone timeZone, boolean zonelessInput, - Environment env) throws TemplateValueFormatException { - TemplateFormatUtil.checkHasNoParameters(params); - try { - String targetFormatString; - if (localizedTargetFormatStrings != null) { - Locale lookupLocale = locale; - targetFormatString = localizedTargetFormatStrings.get(lookupLocale); - while (targetFormatString == null - && (lookupLocale = _CoreLocaleUtils.getLessSpecificLocale(lookupLocale)) != null) { - targetFormatString = localizedTargetFormatStrings.get(lookupLocale); - } - } else { - targetFormatString = null; - } - if (targetFormatString == null) { - targetFormatString = this.defaultTargetFormatString; - } - return env.getTemplateDateFormat(targetFormatString, dateType, locale, timeZone, zonelessInput); - } catch (TemplateValueFormatException e) { - throw new AliasTargetTemplateValueFormatException("Failed to create format based on target format string, " - + StringUtil.jQuote(params) + ". Reason given: " + e.getMessage(), e); - } - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/core/AliasTemplateNumberFormatFactory.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/core/AliasTemplateNumberFormatFactory.java b/src/main/java/freemarker/core/AliasTemplateNumberFormatFactory.java deleted file mode 100644 index 69d3ece..0000000 --- a/src/main/java/freemarker/core/AliasTemplateNumberFormatFactory.java +++ /dev/null @@ -1,90 +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 freemarker.core; - -import java.util.Locale; -import java.util.Map; - -import freemarker.template.utility.StringUtil; - -/** - * Creates an alias to another format, so that the format can be referred to with a simple name in the template, rather - * than as a concrete pattern or other kind of format string. - * - * @since 2.3.24 - */ -public final class AliasTemplateNumberFormatFactory extends TemplateNumberFormatFactory { - - private final String defaultTargetFormatString; - private final Map<Locale, String> localizedTargetFormatStrings; - - /** - * @param targetFormatString - * The format string this format will be an alias to - */ - public AliasTemplateNumberFormatFactory(String targetFormatString) { - this.defaultTargetFormatString = targetFormatString; - localizedTargetFormatStrings = null; - } - - /** - * @param defaultTargetFormatString - * The format string this format will be an alias to if there's no locale-specific format string for the - * requested locale in {@code localizedTargetFormatStrings} - * @param localizedTargetFormatStrings - * Maps {@link Locale}-s to format strings. If the desired locale doesn't occur in the map, a less - * specific locale is tried, repeatedly until only the language part remains. For example, if locale is - * {@code new Locale("en", "US", "Linux")}, then these keys will be attempted untol a match is found, in - * this order: {@code new Locale("en", "US", "Linux")}, {@code new Locale("en", "US")}, - * {@code new Locale("en")}. If there's still no matching key, the value of the - * {@code targetFormatString} will be used. - */ - public AliasTemplateNumberFormatFactory( - String defaultTargetFormatString, Map<Locale, String> localizedTargetFormatStrings) { - this.defaultTargetFormatString = defaultTargetFormatString; - this.localizedTargetFormatStrings = localizedTargetFormatStrings; - } - - @Override - public TemplateNumberFormat get(String params, Locale locale, Environment env) - throws TemplateValueFormatException { - TemplateFormatUtil.checkHasNoParameters(params); - try { - String targetFormatString; - if (localizedTargetFormatStrings != null) { - Locale lookupLocale = locale; - targetFormatString = localizedTargetFormatStrings.get(lookupLocale); - while (targetFormatString == null - && (lookupLocale = _CoreLocaleUtils.getLessSpecificLocale(lookupLocale)) != null) { - targetFormatString = localizedTargetFormatStrings.get(lookupLocale); - } - } else { - targetFormatString = null; - } - if (targetFormatString == null) { - targetFormatString = this.defaultTargetFormatString; - } - return env.getTemplateNumberFormat(targetFormatString, locale); - } catch (TemplateValueFormatException e) { - throw new AliasTargetTemplateValueFormatException("Failed to create format based on target format string, " - + StringUtil.jQuote(params) + ". Reason given: " + e.getMessage(), e); - } - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/core/AndExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/core/AndExpression.java b/src/main/java/freemarker/core/AndExpression.java deleted file mode 100644 index c44b816..0000000 --- a/src/main/java/freemarker/core/AndExpression.java +++ /dev/null @@ -1,81 +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 freemarker.core; - -import freemarker.template.TemplateException; - -final class AndExpression extends BooleanExpression { - - private final Expression lho; - private final Expression rho; - - AndExpression(Expression lho, Expression rho) { - this.lho = lho; - this.rho = rho; - } - - @Override - boolean evalToBoolean(Environment env) throws TemplateException { - return lho.evalToBoolean(env) && rho.evalToBoolean(env); - } - - @Override - public String getCanonicalForm() { - return lho.getCanonicalForm() + " && " + rho.getCanonicalForm(); - } - - @Override - String getNodeTypeSymbol() { - return "&&"; - } - - @Override - boolean isLiteral() { - return constantValue != null || (lho.isLiteral() && rho.isLiteral()); - } - - @Override - protected Expression deepCloneWithIdentifierReplaced_inner( - String replacedIdentifier, Expression replacement, ReplacemenetState replacementState) { - return new AndExpression( - lho.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), - rho.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState)); - } - - @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); - } - -} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/core/ArithmeticEngine.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/core/ArithmeticEngine.java b/src/main/java/freemarker/core/ArithmeticEngine.java deleted file mode 100644 index 7ce6347..0000000 --- a/src/main/java/freemarker/core/ArithmeticEngine.java +++ /dev/null @@ -1,550 +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 freemarker.core; - -import java.math.BigDecimal; -import java.math.BigInteger; -import java.util.HashMap; -import java.util.Map; - -import freemarker.template.TemplateException; -import freemarker.template.utility.NumberUtil; -import freemarker.template.utility.OptimizerUtil; -import freemarker.template.utility.StringUtil; - -/** - * Class to perform arithmetic operations. - */ - -public abstract class ArithmeticEngine { - - /** - * Arithmetic engine that converts all numbers to {@link BigDecimal} and - * then operates on them. This is FreeMarker's default arithmetic engine. - */ - public static final BigDecimalEngine BIGDECIMAL_ENGINE = new BigDecimalEngine(); - /** - * Arithmetic engine that uses (more-or-less) the widening conversions of - * Java language to determine the type of result of operation, instead of - * converting everything to BigDecimal up front. - */ - public static final ConservativeEngine CONSERVATIVE_ENGINE = new ConservativeEngine(); - - public abstract int compareNumbers(Number first, Number second) throws TemplateException; - public abstract Number add(Number first, Number second) throws TemplateException; - public abstract Number subtract(Number first, Number second) throws TemplateException; - public abstract Number multiply(Number first, Number second) throws TemplateException; - public abstract Number divide(Number first, Number second) throws TemplateException; - public abstract Number modulus(Number first, Number second) throws TemplateException; - - /** - * Should be able to parse all FTL numerical literals, Java Double toString results, and XML Schema numbers. - * This means these should be parsed successfully, except if the arithmetical engine - * couldn't support the resulting value anyway (such as NaN, infinite, even non-integers): - * {@code -123.45}, {@code 1.5e3}, {@code 1.5E3}, {@code 0005}, {@code +0}, {@code -0}, {@code NaN}, - * {@code INF}, {@code -INF}, {@code Infinity}, {@code -Infinity}. - */ - public abstract Number toNumber(String s); - - protected int minScale = 12; - protected int maxScale = 12; - protected int roundingPolicy = BigDecimal.ROUND_HALF_UP; - - /** - * Sets the minimal scale to use when dividing BigDecimal numbers. Default - * value is 12. - */ - public void setMinScale(int minScale) { - if (minScale < 0) { - throw new IllegalArgumentException("minScale < 0"); - } - this.minScale = minScale; - } - - /** - * Sets the maximal scale to use when multiplying BigDecimal numbers. - * Default value is 100. - */ - public void setMaxScale(int maxScale) { - if (maxScale < minScale) { - throw new IllegalArgumentException("maxScale < minScale"); - } - this.maxScale = maxScale; - } - - public void setRoundingPolicy(int roundingPolicy) { - if (roundingPolicy != BigDecimal.ROUND_CEILING - && roundingPolicy != BigDecimal.ROUND_DOWN - && roundingPolicy != BigDecimal.ROUND_FLOOR - && roundingPolicy != BigDecimal.ROUND_HALF_DOWN - && roundingPolicy != BigDecimal.ROUND_HALF_EVEN - && roundingPolicy != BigDecimal.ROUND_HALF_UP - && roundingPolicy != BigDecimal.ROUND_UNNECESSARY - && roundingPolicy != BigDecimal.ROUND_UP) { - throw new IllegalArgumentException("invalid rounding policy"); - } - - this.roundingPolicy = roundingPolicy; - } - - /** - * This is the default arithmetic engine in FreeMarker. It converts every - * number it receives into {@link BigDecimal}, then operates on these - * converted {@link BigDecimal}s. - */ - public static class BigDecimalEngine - extends - ArithmeticEngine { - @Override - public int compareNumbers(Number first, Number second) { - // We try to find the result based on the sign (+/-/0) first, because: - // - It's much faster than converting to BigDecial, and comparing to 0 is the most common comparison. - // - It doesn't require any type conversions, and thus things like "Infinity > 0" won't fail. - int firstSignum = NumberUtil.getSignum(first); - int secondSignum = NumberUtil.getSignum(second); - if (firstSignum != secondSignum) { - return firstSignum < secondSignum ? -1 : (firstSignum > secondSignum ? 1 : 0); - } else if (firstSignum == 0 && secondSignum == 0) { - return 0; - } else { - BigDecimal left = toBigDecimal(first); - BigDecimal right = toBigDecimal(second); - return left.compareTo(right); - } - } - - @Override - public Number add(Number first, Number second) { - BigDecimal left = toBigDecimal(first); - BigDecimal right = toBigDecimal(second); - return left.add(right); - } - - @Override - public Number subtract(Number first, Number second) { - BigDecimal left = toBigDecimal(first); - BigDecimal right = toBigDecimal(second); - return left.subtract(right); - } - - @Override - public Number multiply(Number first, Number second) { - BigDecimal left = toBigDecimal(first); - BigDecimal right = toBigDecimal(second); - BigDecimal result = left.multiply(right); - if (result.scale() > maxScale) { - result = result.setScale(maxScale, roundingPolicy); - } - return result; - } - - @Override - public Number divide(Number first, Number second) { - BigDecimal left = toBigDecimal(first); - BigDecimal right = toBigDecimal(second); - return divide(left, right); - } - - @Override - public Number modulus(Number first, Number second) { - long left = first.longValue(); - long right = second.longValue(); - return Long.valueOf(left % right); - } - - @Override - public Number toNumber(String s) { - return toBigDecimalOrDouble(s); - } - - private BigDecimal divide(BigDecimal left, BigDecimal right) { - int scale1 = left.scale(); - int scale2 = right.scale(); - int scale = Math.max(scale1, scale2); - scale = Math.max(minScale, scale); - return left.divide(right, scale, roundingPolicy); - } - } - - /** - * An arithmetic engine that conservatively widens the operation arguments - * to extent that they can hold the result of the operation. Widening - * conversions occur in following situations: - * <ul> - * <li>byte and short are always widened to int (alike to Java language).</li> - * <li>To preserve magnitude: when operands are of different types, the - * result type is the type of the wider operand.</li> - * <li>to avoid overflows: if add, subtract, or multiply would overflow on - * integer types, the result is widened from int to long, or from long to - * BigInteger.</li> - * <li>to preserve fractional part: if a division of integer types would - * have a fractional part, int and long are converted to double, and - * BigInteger is converted to BigDecimal. An operation on a float and a - * long results in a double. An operation on a float or double and a - * BigInteger results in a BigDecimal.</li> - * </ul> - */ - public static class ConservativeEngine extends ArithmeticEngine { - private static final int INTEGER = 0; - private static final int LONG = 1; - private static final int FLOAT = 2; - private static final int DOUBLE = 3; - private static final int BIGINTEGER = 4; - private static final int BIGDECIMAL = 5; - - private static final Map classCodes = createClassCodesMap(); - - @Override - public int compareNumbers(Number first, Number second) throws TemplateException { - switch(getCommonClassCode(first, second)) { - case INTEGER: { - int n1 = first.intValue(); - int n2 = second.intValue(); - return n1 < n2 ? -1 : (n1 == n2 ? 0 : 1); - } - case LONG: { - long n1 = first.longValue(); - long n2 = second.longValue(); - return n1 < n2 ? -1 : (n1 == n2 ? 0 : 1); - } - case FLOAT: { - float n1 = first.floatValue(); - float n2 = second.floatValue(); - return n1 < n2 ? -1 : (n1 == n2 ? 0 : 1); - } - case DOUBLE: { - double n1 = first.doubleValue(); - double n2 = second.doubleValue(); - return n1 < n2 ? -1 : (n1 == n2 ? 0 : 1); - } - case BIGINTEGER: { - BigInteger n1 = toBigInteger(first); - BigInteger n2 = toBigInteger(second); - return n1.compareTo(n2); - } - case BIGDECIMAL: { - BigDecimal n1 = toBigDecimal(first); - BigDecimal n2 = toBigDecimal(second); - return n1.compareTo(n2); - } - } - // Make the compiler happy. getCommonClassCode() is guaranteed to - // return only above codes, or throw an exception. - throw new Error(); - } - - @Override - public Number add(Number first, Number second) throws TemplateException { - switch(getCommonClassCode(first, second)) { - case INTEGER: { - int n1 = first.intValue(); - int n2 = second.intValue(); - int n = n1 + n2; - return - ((n ^ n1) < 0 && (n ^ n2) < 0) // overflow check - ? Long.valueOf(((long) n1) + n2) - : Integer.valueOf(n); - } - case LONG: { - long n1 = first.longValue(); - long n2 = second.longValue(); - long n = n1 + n2; - return - ((n ^ n1) < 0 && (n ^ n2) < 0) // overflow check - ? toBigInteger(first).add(toBigInteger(second)) - : Long.valueOf(n); - } - case FLOAT: { - return Float.valueOf(first.floatValue() + second.floatValue()); - } - case DOUBLE: { - return Double.valueOf(first.doubleValue() + second.doubleValue()); - } - case BIGINTEGER: { - BigInteger n1 = toBigInteger(first); - BigInteger n2 = toBigInteger(second); - return n1.add(n2); - } - case BIGDECIMAL: { - BigDecimal n1 = toBigDecimal(first); - BigDecimal n2 = toBigDecimal(second); - return n1.add(n2); - } - } - // Make the compiler happy. getCommonClassCode() is guaranteed to - // return only above codes, or throw an exception. - throw new Error(); - } - - @Override - public Number subtract(Number first, Number second) throws TemplateException { - switch(getCommonClassCode(first, second)) { - case INTEGER: { - int n1 = first.intValue(); - int n2 = second.intValue(); - int n = n1 - n2; - return - ((n ^ n1) < 0 && (n ^ ~n2) < 0) // overflow check - ? (Number) Long.valueOf(((long) n1) - n2) - : (Number) Integer.valueOf(n); - } - case LONG: { - long n1 = first.longValue(); - long n2 = second.longValue(); - long n = n1 - n2; - return - ((n ^ n1) < 0 && (n ^ ~n2) < 0) // overflow check - ? (Number) toBigInteger(first).subtract(toBigInteger(second)) - : (Number) Long.valueOf(n); - } - case FLOAT: { - return Float.valueOf(first.floatValue() - second.floatValue()); - } - case DOUBLE: { - return Double.valueOf(first.doubleValue() - second.doubleValue()); - } - case BIGINTEGER: { - BigInteger n1 = toBigInteger(first); - BigInteger n2 = toBigInteger(second); - return n1.subtract(n2); - } - case BIGDECIMAL: { - BigDecimal n1 = toBigDecimal(first); - BigDecimal n2 = toBigDecimal(second); - return n1.subtract(n2); - } - } - // Make the compiler happy. getCommonClassCode() is guaranteed to - // return only above codes, or throw an exception. - throw new Error(); - } - - @Override - public Number multiply(Number first, Number second) throws TemplateException { - switch(getCommonClassCode(first, second)) { - case INTEGER: { - int n1 = first.intValue(); - int n2 = second.intValue(); - int n = n1 * n2; - return - n1 == 0 || n / n1 == n2 // overflow check - ? (Number) Integer.valueOf(n) - : (Number) Long.valueOf(((long) n1) * n2); - } - case LONG: { - long n1 = first.longValue(); - long n2 = second.longValue(); - long n = n1 * n2; - return - n1 == 0L || n / n1 == n2 // overflow check - ? (Number) Long.valueOf(n) - : (Number) toBigInteger(first).multiply(toBigInteger(second)); - } - case FLOAT: { - return Float.valueOf(first.floatValue() * second.floatValue()); - } - case DOUBLE: { - return Double.valueOf(first.doubleValue() * second.doubleValue()); - } - case BIGINTEGER: { - BigInteger n1 = toBigInteger(first); - BigInteger n2 = toBigInteger(second); - return n1.multiply(n2); - } - case BIGDECIMAL: { - BigDecimal n1 = toBigDecimal(first); - BigDecimal n2 = toBigDecimal(second); - BigDecimal r = n1.multiply(n2); - return r.scale() > maxScale ? r.setScale(maxScale, roundingPolicy) : r; - } - } - // Make the compiler happy. getCommonClassCode() is guaranteed to - // return only above codes, or throw an exception. - throw new Error(); - } - - @Override - public Number divide(Number first, Number second) throws TemplateException { - switch(getCommonClassCode(first, second)) { - case INTEGER: { - int n1 = first.intValue(); - int n2 = second.intValue(); - if (n1 % n2 == 0) { - return Integer.valueOf(n1 / n2); - } - return Double.valueOf(((double) n1) / n2); - } - case LONG: { - long n1 = first.longValue(); - long n2 = second.longValue(); - if (n1 % n2 == 0) { - return Long.valueOf(n1 / n2); - } - return Double.valueOf(((double) n1) / n2); - } - case FLOAT: { - return Float.valueOf(first.floatValue() / second.floatValue()); - } - case DOUBLE: { - return Double.valueOf(first.doubleValue() / second.doubleValue()); - } - case BIGINTEGER: { - BigInteger n1 = toBigInteger(first); - BigInteger n2 = toBigInteger(second); - BigInteger[] divmod = n1.divideAndRemainder(n2); - if (divmod[1].equals(BigInteger.ZERO)) { - return divmod[0]; - } else { - BigDecimal bd1 = new BigDecimal(n1); - BigDecimal bd2 = new BigDecimal(n2); - return bd1.divide(bd2, minScale, roundingPolicy); - } - } - case BIGDECIMAL: { - BigDecimal n1 = toBigDecimal(first); - BigDecimal n2 = toBigDecimal(second); - int scale1 = n1.scale(); - int scale2 = n2.scale(); - int scale = Math.max(scale1, scale2); - scale = Math.max(minScale, scale); - return n1.divide(n2, scale, roundingPolicy); - } - } - // Make the compiler happy. getCommonClassCode() is guaranteed to - // return only above codes, or throw an exception. - throw new Error(); - } - - @Override - public Number modulus(Number first, Number second) throws TemplateException { - switch(getCommonClassCode(first, second)) { - case INTEGER: { - return Integer.valueOf(first.intValue() % second.intValue()); - } - case LONG: { - return Long.valueOf(first.longValue() % second.longValue()); - } - case FLOAT: { - return Float.valueOf(first.floatValue() % second.floatValue()); - } - case DOUBLE: { - return Double.valueOf(first.doubleValue() % second.doubleValue()); - } - case BIGINTEGER: { - BigInteger n1 = toBigInteger(first); - BigInteger n2 = toBigInteger(second); - return n1.mod(n2); - } - case BIGDECIMAL: { - throw new _MiscTemplateException("Can't calculate remainder on BigDecimals"); - } - } - // Make the compiler happy. getCommonClassCode() is guaranteed to - // return only above codes, or throw an exception. - throw new BugException(); - } - - @Override - public Number toNumber(String s) { - Number n = toBigDecimalOrDouble(s); - return n instanceof BigDecimal ? OptimizerUtil.optimizeNumberRepresentation(n) : n; - } - - private static Map createClassCodesMap() { - Map map = new HashMap(17); - Integer intcode = Integer.valueOf(INTEGER); - map.put(Byte.class, intcode); - map.put(Short.class, intcode); - map.put(Integer.class, intcode); - map.put(Long.class, Integer.valueOf(LONG)); - map.put(Float.class, Integer.valueOf(FLOAT)); - map.put(Double.class, Integer.valueOf(DOUBLE)); - map.put(BigInteger.class, Integer.valueOf(BIGINTEGER)); - map.put(BigDecimal.class, Integer.valueOf(BIGDECIMAL)); - return map; - } - - private static int getClassCode(Number num) throws TemplateException { - try { - return ((Integer) classCodes.get(num.getClass())).intValue(); - } catch (NullPointerException e) { - if (num == null) { - throw new _MiscTemplateException("The Number object was null."); - } else { - throw new _MiscTemplateException("Unknown number type ", num.getClass().getName()); - } - } - } - - private static int getCommonClassCode(Number num1, Number num2) throws TemplateException { - int c1 = getClassCode(num1); - int c2 = getClassCode(num2); - int c = c1 > c2 ? c1 : c2; - // If BigInteger is combined with a Float or Double, the result is a - // BigDecimal instead of BigInteger in order not to lose the - // fractional parts. If Float is combined with Long, the result is a - // Double instead of Float to preserve the bigger bit width. - switch(c) { - case FLOAT: { - if ((c1 < c2 ? c1 : c2) == LONG) { - return DOUBLE; - } - break; - } - case BIGINTEGER: { - int min = c1 < c2 ? c1 : c2; - if (min == DOUBLE || min == FLOAT) { - return BIGDECIMAL; - } - break; - } - } - return c; - } - - private static BigInteger toBigInteger(Number num) { - return num instanceof BigInteger ? (BigInteger) num : new BigInteger(num.toString()); - } - } - - private static BigDecimal toBigDecimal(Number num) { - try { - return num instanceof BigDecimal ? (BigDecimal) num : new BigDecimal(num.toString()); - } catch (NumberFormatException e) { - // The exception message is useless, so we add a new one: - throw new NumberFormatException("Can't parse this as BigDecimal number: " + StringUtil.jQuote(num)); - } - } - - private static Number toBigDecimalOrDouble(String s) { - if (s.length() > 2) { - char c = s.charAt(0); - if (c == 'I' && (s.equals("INF") || s.equals("Infinity"))) { - return Double.valueOf(Double.POSITIVE_INFINITY); - } else if (c == 'N' && s.equals("NaN")) { - return Double.valueOf(Double.NaN); - } else if (c == '-' && s.charAt(1) == 'I' && (s.equals("-INF") || s.equals("-Infinity"))) { - return Double.valueOf(Double.NEGATIVE_INFINITY); - } - } - return new BigDecimal(s); - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/core/ArithmeticExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/core/ArithmeticExpression.java b/src/main/java/freemarker/core/ArithmeticExpression.java deleted file mode 100644 index b33e960..0000000 --- a/src/main/java/freemarker/core/ArithmeticExpression.java +++ /dev/null @@ -1,129 +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 freemarker.core; - -import freemarker.template.SimpleNumber; -import freemarker.template.TemplateException; -import freemarker.template.TemplateModel; - -/** - * An operator for arithmetic operations. Note that the + operator is in {@link AddConcatExpression}, because its - * overloaded (does string concatenation and more). - */ -final class ArithmeticExpression extends Expression { - - static final int TYPE_SUBSTRACTION = 0; - static final int TYPE_MULTIPLICATION = 1; - static final int TYPE_DIVISION = 2; - static final int TYPE_MODULO = 3; - - private static final char[] OPERATOR_IMAGES = new char[] { '-', '*', '/', '%' }; - - private final Expression lho; - private final Expression rho; - private final int operator; - - ArithmeticExpression(Expression lho, Expression rho, int operator) { - this.lho = lho; - this.rho = rho; - this.operator = operator; - } - - @Override - TemplateModel _eval(Environment env) throws TemplateException { - return _eval(env, this, lho.evalToNumber(env), operator, rho.evalToNumber(env)); - } - - static TemplateModel _eval(Environment env, TemplateObject parent, Number lhoNumber, int operator, Number rhoNumber) - throws TemplateException, _MiscTemplateException { - ArithmeticEngine ae = EvalUtil.getArithmeticEngine(env, parent); - switch (operator) { - case TYPE_SUBSTRACTION : - return new SimpleNumber(ae.subtract(lhoNumber, rhoNumber)); - case TYPE_MULTIPLICATION : - return new SimpleNumber(ae.multiply(lhoNumber, rhoNumber)); - case TYPE_DIVISION : - return new SimpleNumber(ae.divide(lhoNumber, rhoNumber)); - case TYPE_MODULO : - return new SimpleNumber(ae.modulus(lhoNumber, rhoNumber)); - default: - if (parent instanceof Expression) { - throw new _MiscTemplateException((Expression) parent, - "Unknown operation: ", Integer.valueOf(operator)); - } else { - throw new _MiscTemplateException("Unknown operation: ", Integer.valueOf(operator)); - } - } - } - - @Override - public String getCanonicalForm() { - return lho.getCanonicalForm() + ' ' + getOperatorSymbol(operator) + ' ' + rho.getCanonicalForm(); - } - - @Override - String getNodeTypeSymbol() { - return String.valueOf(getOperatorSymbol(operator)); - } - - static char getOperatorSymbol(int operator) { - return OPERATOR_IMAGES[operator]; - } - - @Override - boolean isLiteral() { - return constantValue != null || (lho.isLiteral() && rho.isLiteral()); - } - - @Override - protected Expression deepCloneWithIdentifierReplaced_inner( - String replacedIdentifier, Expression replacement, ReplacemenetState replacementState) { - return new ArithmeticExpression( - lho.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), - rho.deepCloneWithIdentifierReplaced(replacedIdentifier, replacement, replacementState), - operator); - } - - @Override - int getParameterCount() { - return 3; - } - - @Override - Object getParameterValue(int idx) { - switch (idx) { - case 0: return lho; - case 1: return rho; - case 2: return Integer.valueOf(operator); - default: throw new IndexOutOfBoundsException(); - } - } - - @Override - ParameterRole getParameterRole(int idx) { - switch (idx) { - case 0: return ParameterRole.LEFT_HAND_OPERAND; - case 1: return ParameterRole.RIGHT_HAND_OPERAND; - case 2: return ParameterRole.AST_NODE_SUBTYPE; - default: throw new IndexOutOfBoundsException(); - } - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/ecb4e230/src/main/java/freemarker/core/Assignment.java ---------------------------------------------------------------------- diff --git a/src/main/java/freemarker/core/Assignment.java b/src/main/java/freemarker/core/Assignment.java deleted file mode 100644 index dd2114e..0000000 --- a/src/main/java/freemarker/core/Assignment.java +++ /dev/null @@ -1,278 +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 freemarker.core; - -import freemarker.template.TemplateException; -import freemarker.template.TemplateModel; -import freemarker.template.TemplateNumberModel; - -/** - * An instruction that makes a single assignment, like [#local x=1]. - * This is also used as the child of {@link AssignmentInstruction}, if there are multiple assignments in the same tag, - * like in [#local x=1 x=2]. - */ -final class Assignment extends TemplateElement { - - // These must not clash with ArithmeticExpression.TYPE_... constants: - private static final int OPERATOR_TYPE_EQUALS = 0x10000; - private static final int OPERATOR_TYPE_PLUS_EQUALS = 0x10001; - private static final int OPERATOR_TYPE_PLUS_PLUS = 0x10002; - private static final int OPERATOR_TYPE_MINUS_MINUS = 0x10003; - - private final int/*enum*/ scope; - private final String variableName; - private final int operatorType; - private final Expression valueExp; - private Expression namespaceExp; - - static final int NAMESPACE = 1; - static final int LOCAL = 2; - static final int GLOBAL = 3; - - private static final Number ONE = Integer.valueOf(1); - - /** - * @param variableName the variable name to assign to. - * @param valueExp the expression to assign. - * @param scope the scope of the assignment, one of NAMESPACE, LOCAL, or GLOBAL - */ - Assignment(String variableName, - int operator, - Expression valueExp, - int scope) { - this.scope = scope; - - this.variableName = variableName; - - if (operator == FMParserConstants.EQUALS) { - operatorType = OPERATOR_TYPE_EQUALS; - } else { - switch (operator) { - case FMParserConstants.PLUS_PLUS: - operatorType = OPERATOR_TYPE_PLUS_PLUS; - break; - case FMParserConstants.MINUS_MINUS: - operatorType = OPERATOR_TYPE_MINUS_MINUS; - break; - case FMParserConstants.PLUS_EQUALS: - operatorType = OPERATOR_TYPE_PLUS_EQUALS; - break; - case FMParserConstants.MINUS_EQUALS: - operatorType = ArithmeticExpression.TYPE_SUBSTRACTION; - break; - case FMParserConstants.TIMES_EQUALS: - operatorType = ArithmeticExpression.TYPE_MULTIPLICATION; - break; - case FMParserConstants.DIV_EQUALS: - operatorType = ArithmeticExpression.TYPE_DIVISION; - break; - case FMParserConstants.MOD_EQUALS: - operatorType = ArithmeticExpression.TYPE_MODULO; - break; - default: - throw new BugException(); - } - } - - this.valueExp = valueExp; - } - - void setNamespaceExp(Expression namespaceExp) { - if (scope != NAMESPACE && namespaceExp != null) throw new BugException(); - this.namespaceExp = namespaceExp; - } - - @Override - TemplateElement[] accept(Environment env) throws TemplateException { - final Environment.Namespace namespace; - if (namespaceExp == null) { - switch (scope) { - case LOCAL: - namespace = null; - break; - case GLOBAL: - namespace = env.getGlobalNamespace(); - break; - case NAMESPACE: - namespace = env.getCurrentNamespace(); - break; - default: - throw new BugException("Unexpected scope type: " + scope); - } - } else { - TemplateModel namespaceTM = namespaceExp.eval(env); - try { - namespace = (Environment.Namespace) namespaceTM; - } catch (ClassCastException e) { - throw new NonNamespaceException(namespaceExp, namespaceTM, env); - } - if (namespace == null) { - throw InvalidReferenceException.getInstance(namespaceExp, env); - } - } - - TemplateModel value; - if (operatorType == OPERATOR_TYPE_EQUALS) { - value = valueExp.eval(env); - valueExp.assertNonNull(value, env); - } else { - TemplateModel lhoValue; - if (namespace == null) { - lhoValue = env.getLocalVariable(variableName); - } else { - lhoValue = namespace.get(variableName); - } - - if (operatorType == OPERATOR_TYPE_PLUS_EQUALS) { // Add or concat operation - if (lhoValue == null) { - throw InvalidReferenceException.getInstance( - variableName, getOperatorTypeAsString(), env); - } - - value = valueExp.eval(env); - valueExp.assertNonNull(value, env); - value = AddConcatExpression._eval(env, namespaceExp, null, lhoValue, valueExp, value); - } else { // Numerical operation - Number lhoNumber; - if (lhoValue instanceof TemplateNumberModel) { - lhoNumber = EvalUtil.modelToNumber((TemplateNumberModel) lhoValue, null); - } else if (lhoValue == null) { - throw InvalidReferenceException.getInstance(variableName, getOperatorTypeAsString(), env); - } else { - throw new NonNumericalException(variableName, lhoValue, null, env); - } - - if (operatorType == OPERATOR_TYPE_PLUS_PLUS) { - value = AddConcatExpression._evalOnNumbers(env, getParentElement(), lhoNumber, ONE); - } else if (operatorType == OPERATOR_TYPE_MINUS_MINUS) { - value = ArithmeticExpression._eval( - env, getParentElement(), lhoNumber, ArithmeticExpression.TYPE_SUBSTRACTION, ONE); - } else { // operatorType == ArithmeticExpression.TYPE_... - Number rhoNumber = valueExp.evalToNumber(env); - value = ArithmeticExpression._eval(env, this, lhoNumber, operatorType, rhoNumber); - } - } - } - - if (namespace == null) { - env.setLocalVariable(variableName, value); - } else { - namespace.put(variableName, value); - } - return null; - } - - @Override - protected String dump(boolean canonical) { - StringBuilder buf = new StringBuilder(); - String dn = getParentElement() instanceof AssignmentInstruction ? null : getNodeTypeSymbol(); - if (dn != null) { - if (canonical) buf.append("<"); - buf.append(dn); - buf.append(' '); - } - - buf.append(_CoreStringUtils.toFTLTopLevelTragetIdentifier(variableName)); - - if (valueExp != null) { - buf.append(' '); - } - buf.append(getOperatorTypeAsString()); - if (valueExp != null) { - buf.append(' '); - buf.append(valueExp.getCanonicalForm()); - } - if (dn != null) { - if (namespaceExp != null) { - buf.append(" in "); - buf.append(namespaceExp.getCanonicalForm()); - } - if (canonical) buf.append(">"); - } - String result = buf.toString(); - return result; - } - - @Override - String getNodeTypeSymbol() { - return getDirectiveName(scope); - } - - static String getDirectiveName(int scope) { - if (scope == Assignment.LOCAL) { - return "#local"; - } else if (scope == Assignment.GLOBAL) { - return "#global"; - } else if (scope == Assignment.NAMESPACE) { - return "#assign"; - } else { - return "#{unknown_assignment_type}"; - } - } - - @Override - int getParameterCount() { - return 5; - } - - @Override - Object getParameterValue(int idx) { - switch (idx) { - case 0: return variableName; - case 1: return getOperatorTypeAsString(); - case 2: return valueExp; - case 3: return Integer.valueOf(scope); - case 4: return namespaceExp; - default: throw new IndexOutOfBoundsException(); - } - } - - @Override - ParameterRole getParameterRole(int idx) { - switch (idx) { - case 0: return ParameterRole.ASSIGNMENT_TARGET; - case 1: return ParameterRole.ASSIGNMENT_OPERATOR; - case 2: return ParameterRole.ASSIGNMENT_SOURCE; - case 3: return ParameterRole.VARIABLE_SCOPE; - case 4: return ParameterRole.NAMESPACE; - default: throw new IndexOutOfBoundsException(); - } - } - - @Override - boolean isNestedBlockRepeater() { - return false; - } - - private String getOperatorTypeAsString() { - if (operatorType == OPERATOR_TYPE_EQUALS) { - return "="; - } else if (operatorType == OPERATOR_TYPE_PLUS_EQUALS) { - return "+="; - } else if (operatorType == OPERATOR_TYPE_PLUS_PLUS) { - return "++"; - } else if (operatorType == OPERATOR_TYPE_MINUS_MINUS) { - return "--"; - } else { - return ArithmeticExpression.getOperatorSymbol(operatorType) + "="; - } - } - -}
