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) + "=";
-        }
-    }
-    
-}

Reply via email to