freemarker-notifications mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ddek...@apache.org
Subject [34/54] [partial] incubator-freemarker git commit: Unifying the o.a.f.core and o.a.f.core.ast
Date Thu, 23 Feb 2017 21:36:01 GMT
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_CoreAPI.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_CoreAPI.java b/src/main/java/org/apache/freemarker/core/_CoreAPI.java
new file mode 100644
index 0000000..bcff837
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/_CoreAPI.java
@@ -0,0 +1,227 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+import java.util.TreeSet;
+
+import org.apache.freemarker.core.model.TemplateDirectiveBody;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.util._ClassUtil;
+
+
+/**
+ * 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 class _CoreAPI {
+    
+    public static final String ERROR_MESSAGE_HR = "----";
+
+    // Can't be instantiated
+    private _CoreAPI() { }
+
+    private static void addName(Set<String> allNames, Set<String> lcNames, Set<String> ccNames,
+            String commonName) {
+        allNames.add(commonName);
+        lcNames.add(commonName);
+        ccNames.add(commonName);
+    }
+    
+    private static void addName(Set<String> allNames, Set<String> lcNames, Set<String> ccNames,
+            String lcName, String ccName) {
+        allNames.add(lcName);
+        allNames.add(ccName);
+        lcNames.add(lcName);
+        ccNames.add(ccName);
+    }
+    
+    public static final Set<String> ALL_BUILT_IN_DIRECTIVE_NAMES;
+    public static final Set<String> LEGACY_BUILT_IN_DIRECTIVE_NAMES;
+    public static final Set<String> CAMEL_CASE_BUILT_IN_DIRECTIVE_NAMES;
+    static {
+        Set<String> allNames = new TreeSet();
+        Set<String> lcNames = new TreeSet();
+        Set<String> ccNames = new TreeSet();
+        
+        addName(allNames, lcNames, ccNames, "assign");
+        addName(allNames, lcNames, ccNames, "attempt");
+        addName(allNames, lcNames, ccNames, "autoesc", "autoEsc");
+        addName(allNames, lcNames, ccNames, "break");
+        addName(allNames, lcNames, ccNames, "call");
+        addName(allNames, lcNames, ccNames, "case");
+        addName(allNames, lcNames, ccNames, "comment");
+        addName(allNames, lcNames, ccNames, "compress");
+        addName(allNames, lcNames, ccNames, "default");
+        addName(allNames, lcNames, ccNames, "else");
+        addName(allNames, lcNames, ccNames, "elseif", "elseIf");
+        addName(allNames, lcNames, ccNames, "escape");
+        addName(allNames, lcNames, ccNames, "fallback");
+        addName(allNames, lcNames, ccNames, "flush");
+        addName(allNames, lcNames, ccNames, "foreach", "forEach");
+        addName(allNames, lcNames, ccNames, "ftl");
+        addName(allNames, lcNames, ccNames, "function");
+        addName(allNames, lcNames, ccNames, "global");
+        addName(allNames, lcNames, ccNames, "if");
+        addName(allNames, lcNames, ccNames, "import");
+        addName(allNames, lcNames, ccNames, "include");
+        addName(allNames, lcNames, ccNames, "items");
+        addName(allNames, lcNames, ccNames, "list");
+        addName(allNames, lcNames, ccNames, "local");
+        addName(allNames, lcNames, ccNames, "lt");
+        addName(allNames, lcNames, ccNames, "macro");
+        addName(allNames, lcNames, ccNames, "nested");
+        addName(allNames, lcNames, ccNames, "noautoesc", "noAutoEsc");
+        addName(allNames, lcNames, ccNames, "noescape", "noEscape");
+        addName(allNames, lcNames, ccNames, "noparse", "noParse");
+        addName(allNames, lcNames, ccNames, "nt");
+        addName(allNames, lcNames, ccNames, "outputformat", "outputFormat");
+        addName(allNames, lcNames, ccNames, "recover");
+        addName(allNames, lcNames, ccNames, "recurse");
+        addName(allNames, lcNames, ccNames, "return");
+        addName(allNames, lcNames, ccNames, "rt");
+        addName(allNames, lcNames, ccNames, "sep");
+        addName(allNames, lcNames, ccNames, "setting");
+        addName(allNames, lcNames, ccNames, "stop");
+        addName(allNames, lcNames, ccNames, "switch");
+        addName(allNames, lcNames, ccNames, "t");
+        addName(allNames, lcNames, ccNames, "transform");
+        addName(allNames, lcNames, ccNames, "visit");
+        
+        ALL_BUILT_IN_DIRECTIVE_NAMES = Collections.unmodifiableSet(allNames);
+        LEGACY_BUILT_IN_DIRECTIVE_NAMES = Collections.unmodifiableSet(lcNames);
+        CAMEL_CASE_BUILT_IN_DIRECTIVE_NAMES = Collections.unmodifiableSet(ccNames);
+    }
+    
+    /**
+     * Returns the names of the currently supported "built-ins" ({@code expr?builtin_name}-like things).
+     * 
+     * @param namingConvention
+     *            One of {@link Configuration#AUTO_DETECT_NAMING_CONVENTION},
+     *            {@link Configuration#LEGACY_NAMING_CONVENTION}, and
+     *            {@link Configuration#CAMEL_CASE_NAMING_CONVENTION}. If it's
+     *            {@link Configuration#AUTO_DETECT_NAMING_CONVENTION} then the union of the names in all the naming
+     *            conventions is returned.
+     */
+    public static Set<String> getSupportedBuiltInNames(int namingConvention) {
+        Set<String> names;
+        if (namingConvention == Configuration.AUTO_DETECT_NAMING_CONVENTION) {
+            names = ASTExpBuiltIn.BUILT_INS_BY_NAME.keySet();
+        } else if (namingConvention == Configuration.LEGACY_NAMING_CONVENTION) {
+            names = ASTExpBuiltIn.SNAKE_CASE_NAMES;
+        } else if (namingConvention == Configuration.CAMEL_CASE_NAMING_CONVENTION) {
+            names = ASTExpBuiltIn.CAMEL_CASE_NAMES;
+        } else {
+            throw new IllegalArgumentException("Unsupported naming convention constant: " + namingConvention);
+        }
+        return Collections.unmodifiableSet(names);
+    }
+    
+    public static void appendInstructionStackItem(_ASTElement stackEl, StringBuilder sb) {
+        Environment.appendInstructionStackItem(stackEl, sb);
+    }
+    
+    public static _ASTElement[] getInstructionStackSnapshot(Environment env) {
+        return env.getInstructionStackSnapshot();
+    }
+    
+    public static void outputInstructionStack(
+            _ASTElement[] instructionStackSnapshot, boolean terseMode, Writer pw) {
+        Environment.outputInstructionStack(instructionStackSnapshot, terseMode, pw);
+    }
+
+    /**
+     * ATTENTION: This is used by https://github.com/kenshoo/freemarker-online. Don't break backward
+     * compatibility without updating that project too! 
+     */
+    static final public void addThreadInterruptedChecks(Template template) {
+        try {
+            new ThreadInterruptionSupportTemplatePostProcessor().postProcess(template);
+        } catch (TemplatePostProcessorException e) {
+            throw new RuntimeException("Template post-processing failed", e);
+        }
+    }
+    
+    static final public void checkHasNoNestedContent(TemplateDirectiveBody body)
+            throws NestedContentNotSupportedException {
+        NestedContentNotSupportedException.check(body);
+    }
+    
+    static final public void replaceText(ASTStaticText textBlock, String text) {
+        textBlock.replaceText(text);
+    }
+
+    /**
+     * @throws IllegalArgumentException
+     *             if the type of the some of the values isn't as expected
+     */
+    public static void checkSettingValueItemsType(String somethingsSentenceStart, Class<?> expectedClass,
+            Collection<? extends Object> values) {
+        if (values == null) return;
+        for (Object value : values) {
+            if (!expectedClass.isInstance(value)) {
+                throw new IllegalArgumentException(somethingsSentenceStart + " must be instances of "
+                        + _ClassUtil.getShortClassName(expectedClass) + ", but one of them was a(n) "
+                        + _ClassUtil.getShortClassNameOfObject(value) + ".");
+            }
+        }
+    }
+    
+    /**
+     * The work around the problematic cases where we should throw a {@link TemplateException}, but we are inside
+     * a {@link TemplateModel} method and so we can only throw {@link TemplateModelException}-s.  
+     */
+    public static TemplateModelException ensureIsTemplateModelException(String modelOpMsg, TemplateException e) {
+        if (e instanceof TemplateModelException) {
+            return (TemplateModelException) e;
+        } else {
+            return new _TemplateModelException(
+                    _TemplateAPI.getBlamedExpression(e), e.getCause(), e.getEnvironment(), modelOpMsg);
+        }
+    }
+    
+    public static _ASTElement getParentElement(_ASTElement te) {
+        return te.getParentElement();
+    }
+
+    public static _ASTElement getChildElement(_ASTElement te, int index) {
+        return te.getChild(index);
+    }
+    
+    public static FMParser newFMParser(Template template, Reader reader, ParserConfiguration pCfg,
+            TemplateSpecifiedEncodingHandler templateSpecifiedEncodingHandler) {
+        return new FMParser(template, reader, pCfg, templateSpecifiedEncodingHandler);
+    }
+    
+    public static boolean isMacroOrFunction(TemplateModel m) {
+        return m instanceof ASTDirMacro;
+    }
+    
+    public static boolean isFunction(TemplateModel m) {
+        return m instanceof ASTDirMacro && ((ASTDirMacro) m).isFunction();
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_DelayedAOrAn.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_DelayedAOrAn.java b/src/main/java/org/apache/freemarker/core/_DelayedAOrAn.java
new file mode 100644
index 0000000..af65878
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/_DelayedAOrAn.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+/** Don't use this; used internally by FreeMarker, might changes without notice. */
+public class _DelayedAOrAn extends _DelayedConversionToString {
+
+    public _DelayedAOrAn(Object object) {
+        super(object);
+    }
+
+    @Override
+    protected String doConversion(Object obj) {
+        String s = obj.toString();
+        return MessageUtil.getAOrAn(s) + " " + s;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_DelayedConversionToString.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_DelayedConversionToString.java b/src/main/java/org/apache/freemarker/core/_DelayedConversionToString.java
new file mode 100644
index 0000000..fac045f
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/_DelayedConversionToString.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+/** Don't use this; used internally by FreeMarker, might changes without notice. */
+public abstract class _DelayedConversionToString {
+
+    private static final String NOT_SET = new String();
+
+    private Object object;
+    private volatile String stringValue = NOT_SET;
+
+    public _DelayedConversionToString(Object object) {
+        this.object = object;
+    }
+
+    @Override
+    public String toString() {
+        String stringValue = this.stringValue;
+        if (stringValue == NOT_SET) {
+            synchronized (this) {
+                stringValue = this.stringValue;
+                if (stringValue == NOT_SET) {
+                    stringValue = doConversion(object);
+                    this.stringValue = stringValue;
+                    object = null;
+                }
+            }
+        }
+        return stringValue;
+    }
+
+    protected abstract String doConversion(Object obj);
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_DelayedFTLTypeDescription.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_DelayedFTLTypeDescription.java b/src/main/java/org/apache/freemarker/core/_DelayedFTLTypeDescription.java
new file mode 100644
index 0000000..73141b5
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/_DelayedFTLTypeDescription.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.util.FTLUtil;
+
+/** Don't use this; used internally by FreeMarker, might changes without notice. */
+public class _DelayedFTLTypeDescription extends _DelayedConversionToString {
+    
+    public _DelayedFTLTypeDescription(TemplateModel tm) {
+        super(tm);
+    }
+
+    @Override
+    protected String doConversion(Object obj) {
+        return FTLUtil.getTypeDescription((TemplateModel) obj);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_DelayedGetCanonicalForm.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_DelayedGetCanonicalForm.java b/src/main/java/org/apache/freemarker/core/_DelayedGetCanonicalForm.java
new file mode 100644
index 0000000..9b43b42
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/_DelayedGetCanonicalForm.java
@@ -0,0 +1,39 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+
+/** Don't use this; used internally by FreeMarker, might changes without notice. */
+public class _DelayedGetCanonicalForm extends _DelayedConversionToString {
+    
+    public _DelayedGetCanonicalForm(ASTNode obj) {
+        super(obj);
+    }
+
+    @Override
+    protected String doConversion(Object obj) {
+        try {
+            return ((ASTNode) obj).getCanonicalForm();
+        } catch (Exception e) {
+            return "{Error getting canonical form}";
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_DelayedGetMessage.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_DelayedGetMessage.java b/src/main/java/org/apache/freemarker/core/_DelayedGetMessage.java
new file mode 100644
index 0000000..14f7daf
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/_DelayedGetMessage.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+/** Don't use this; used internally by FreeMarker, might changes without notice. */
+public class _DelayedGetMessage extends _DelayedConversionToString {
+
+    public _DelayedGetMessage(Throwable exception) {
+        super(exception);
+    }
+
+    @Override
+    protected String doConversion(Object obj) {
+        final String message = ((Throwable) obj).getMessage();
+        return message == null || message.length() == 0 ? "[No exception message]" : message;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_DelayedGetMessageWithoutStackTop.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_DelayedGetMessageWithoutStackTop.java b/src/main/java/org/apache/freemarker/core/_DelayedGetMessageWithoutStackTop.java
new file mode 100644
index 0000000..f6acbdb
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/_DelayedGetMessageWithoutStackTop.java
@@ -0,0 +1,34 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+/** Don't use this; used internally by FreeMarker, might changes without notice. */
+public class _DelayedGetMessageWithoutStackTop extends _DelayedConversionToString {
+
+    public _DelayedGetMessageWithoutStackTop(TemplateException exception) {
+        super(exception);
+    }
+
+    @Override
+    protected String doConversion(Object obj) {
+        return ((TemplateException) obj).getMessageWithoutStackTop();
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_DelayedJQuote.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_DelayedJQuote.java b/src/main/java/org/apache/freemarker/core/_DelayedJQuote.java
new file mode 100644
index 0000000..bdec097
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/_DelayedJQuote.java
@@ -0,0 +1,36 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import org.apache.freemarker.core.util._StringUtil;
+
+/** Don't use this; used internally by FreeMarker, might changes without notice. */
+public class _DelayedJQuote extends _DelayedConversionToString {
+
+    public _DelayedJQuote(Object object) {
+        super(object);
+    }
+
+    @Override
+    protected String doConversion(Object obj) {
+        return _StringUtil.jQuote(_ErrorDescriptionBuilder.toString(obj));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_DelayedJoinWithComma.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_DelayedJoinWithComma.java b/src/main/java/org/apache/freemarker/core/_DelayedJoinWithComma.java
new file mode 100644
index 0000000..b852db4
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/_DelayedJoinWithComma.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+/** Don't use this; used internally by FreeMarker, might changes without notice. */
+public class _DelayedJoinWithComma extends _DelayedConversionToString {
+
+    public _DelayedJoinWithComma(String[] items) {
+        super(items);
+    }
+
+    @Override
+    protected String doConversion(Object obj) {
+        String[] items = (String[]) obj;
+        
+        int totalLength = 0;
+        for (int i = 0; i < items.length; i++) {
+            if (i != 0) totalLength += 2;
+            totalLength += items[i].length();
+        }
+        
+        StringBuilder sb = new StringBuilder(totalLength);
+        for (int i = 0; i < items.length; i++) {
+            if (i != 0) sb.append(", ");
+            sb.append(items[i]);
+        }
+        
+        return sb.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_DelayedOrdinal.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_DelayedOrdinal.java b/src/main/java/org/apache/freemarker/core/_DelayedOrdinal.java
new file mode 100644
index 0000000..7af841d
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/_DelayedOrdinal.java
@@ -0,0 +1,47 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+/** 1 to "1st", 2 to "2nd", etc. */
+public class _DelayedOrdinal extends _DelayedConversionToString {
+
+    public _DelayedOrdinal(Object object) {
+        super(object);
+    }
+
+    @Override
+    protected String doConversion(Object obj) {
+        if (obj instanceof Number) {
+            long n = ((Number) obj).longValue();
+            if (n % 10 == 1 && n % 100 != 11) {
+                return n + "st";
+            } else if (n % 10 == 2 && n % 100 != 12) {
+                return n + "nd";
+            } else if (n % 10 == 3 && n % 100 != 13) {
+                return n + "rd";
+            } else {
+                return n + "th";
+            }
+        } else {
+            return "" + obj;
+        }
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_DelayedShortClassName.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_DelayedShortClassName.java b/src/main/java/org/apache/freemarker/core/_DelayedShortClassName.java
new file mode 100644
index 0000000..4b8845d
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/_DelayedShortClassName.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import org.apache.freemarker.core.util._ClassUtil;
+
+public class _DelayedShortClassName extends _DelayedConversionToString {
+
+    public _DelayedShortClassName(Class pClass) {
+        super(pClass);
+    }
+
+    @Override
+    protected String doConversion(Object obj) {
+        return _ClassUtil.getShortClassName((Class) obj, true);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_DelayedToString.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_DelayedToString.java b/src/main/java/org/apache/freemarker/core/_DelayedToString.java
new file mode 100644
index 0000000..5c23cd6
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/_DelayedToString.java
@@ -0,0 +1,37 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+public class _DelayedToString extends _DelayedConversionToString {
+
+    public _DelayedToString(Object object) {
+        super(object);
+    }
+
+    public _DelayedToString(int object) {
+        super(Integer.valueOf(object));
+    }
+    
+    @Override
+    protected String doConversion(Object obj) {
+        return String.valueOf(obj);
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java b/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java
new file mode 100644
index 0000000..9dc789a
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/_ErrorDescriptionBuilder.java
@@ -0,0 +1,357 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+
+import org.apache.freemarker.core.model.impl.beans._MethodUtil;
+import org.apache.freemarker.core.util._ClassUtil;
+import org.apache.freemarker.core.util._StringUtil;
+import org.slf4j.Logger;
+
+/**
+ * Used internally only, might changes without notice!
+ * Packs a structured from of the error description from which the error message can be rendered on-demand.
+ * Note that this class isn't serializable, thus the containing exception should render the message before it's
+ * serialized.
+ */
+public class _ErrorDescriptionBuilder {
+
+    private static final Logger LOG = _CoreLogs.RUNTIME;
+
+    private final String description;
+    private final Object[] descriptionParts;
+    private ASTExpression blamed;
+    private boolean showBlamer;
+    private Object/*String|Object[]*/ tip;
+    private Object[]/*String[]|Object[][]*/ tips;
+    private Template template;
+
+    public _ErrorDescriptionBuilder(String description) {
+        this.description = description;
+        descriptionParts = null;
+    }
+
+    /**
+     * @param descriptionParts These will be concatenated to a single {@link String} in {@link #toString()}.
+     *      {@link String} array items that look like FTL tag (must start with {@code "&lt;"} and end with {@code ">"})
+     *      will be converted to the actual template syntax if {@link #blamed} or {@link #template} was set.
+     */
+    public _ErrorDescriptionBuilder(Object... descriptionParts) {
+        this.descriptionParts = descriptionParts;
+        description = null;
+    }
+
+    @Override
+    public String toString() {
+        return toString(null, true);
+    }
+    
+    public String toString(_ASTElement parentElement, boolean showTips) {
+        if (blamed == null && tips == null && tip == null && descriptionParts == null) return description;
+
+        StringBuilder sb = new StringBuilder(200);
+        
+        if (parentElement != null && blamed != null && showBlamer) {
+            try {
+                Blaming blaming = findBlaming(parentElement, blamed, 0);
+                if (blaming != null) {
+                    sb.append("For ");
+                    String nss = blaming.blamer.getNodeTypeSymbol();
+                    char q = nss.indexOf('"') == -1 ? '\"' : '`';
+                    sb.append(q).append(nss).append(q);
+                    sb.append(" ").append(blaming.roleOfblamed).append(": ");
+                }
+            } catch (Throwable e) {
+                // Should not happen. But we rather give a not-so-good error message than replace it with another...
+                // So we ignore this.
+                LOG.error("Error when searching blamer for better error message.", e);
+            }
+        }
+        
+        if (description != null) {
+            sb.append(description);
+        } else {
+            appendParts(sb, descriptionParts);
+        }
+
+        String extraTip = null;
+        if (blamed != null) {
+            // Right-trim:
+            for (int idx = sb.length() - 1; idx >= 0 && Character.isWhitespace(sb.charAt(idx)); idx --) {
+                sb.deleteCharAt(idx);
+            }
+            
+            char lastChar = sb.length() > 0 ? (sb.charAt(sb.length() - 1)) : 0;
+            if (lastChar != 0) {
+                sb.append('\n');
+            }
+            if (lastChar != ':') {
+                sb.append("The blamed expression:\n");
+            }
+            
+            String[] lines = splitToLines(blamed.toString());
+            for (int i = 0; i < lines.length; i++) {
+                sb.append(i == 0 ? "==> " : "\n    ");
+                sb.append(lines[i]);
+            }
+            
+            sb.append("  [");
+            sb.append(blamed.getStartLocation());
+            sb.append(']');
+            
+            
+            if (containsSingleInterpolatoinLiteral(blamed, 0)) {
+                extraTip = "It has been noticed that you are using ${...} as the sole content of a quoted string. That "
+                        + "does nothing but forcably converts the value inside ${...} to string (as it inserts it into "
+                        + "the enclosing string). "
+                        + "If that's not what you meant, just remove the quotation marks, ${ and }; you don't need "
+                        + "them. If you indeed wanted to convert to string, use myExpression?string instead.";
+            }
+        }
+        
+        if (showTips) {
+            int allTipsLen = (tips != null ? tips.length : 0) + (tip != null ? 1 : 0) + (extraTip != null ? 1 : 0);
+            Object[] allTips;
+            if (tips != null && allTipsLen == tips.length) {
+                allTips = tips;
+            } else {
+                allTips = new Object[allTipsLen];
+                int dst = 0;
+                if (tip != null) allTips[dst++] = tip; 
+                if (tips != null) {
+                    for (Object t : tips) {
+                        allTips[dst++] = t;
+                    }
+                }
+                if (extraTip != null) allTips[dst++] = extraTip; 
+            }
+            if (allTips != null && allTips.length > 0) {
+                sb.append("\n\n");
+                for (int i = 0; i < allTips.length; i++) {
+                    if (i != 0) sb.append('\n');
+                    sb.append(_CoreAPI.ERROR_MESSAGE_HR).append('\n');
+                    sb.append("Tip: ");
+                    Object tip = allTips[i];
+                    if (!(tip instanceof Object[])) {
+                        sb.append(allTips[i]);
+                    } else {
+                        appendParts(sb, (Object[]) tip);
+                    }
+                }
+                sb.append('\n').append(_CoreAPI.ERROR_MESSAGE_HR);
+            }
+        }
+        
+        return sb.toString();
+    }
+
+    private boolean containsSingleInterpolatoinLiteral(ASTExpression exp, int recursionDepth) {
+        if (exp == null) return false;
+        
+        // Just in case a loop ever gets into the AST somehow, try not fill the stack and such: 
+        if (recursionDepth > 20) return false;
+        
+        if (exp instanceof ASTExpStringLiteral && ((ASTExpStringLiteral) exp).isSingleInterpolationLiteral()) return true;
+        
+        int paramCnt = exp.getParameterCount();
+        for (int i = 0; i < paramCnt; i++) {
+            Object paramValue = exp.getParameterValue(i);
+            if (paramValue instanceof ASTExpression) {
+                boolean result = containsSingleInterpolatoinLiteral((ASTExpression) paramValue, recursionDepth + 1);
+                if (result) return true;
+            }
+        }
+        
+        return false;
+    }
+
+    private Blaming findBlaming(ASTNode parent, ASTExpression blamed, int recursionDepth) {
+        // Just in case a loop ever gets into the AST somehow, try not fill the stack and such: 
+        if (recursionDepth > 50) return null;
+        
+        int paramCnt = parent.getParameterCount();
+        for (int i = 0; i < paramCnt; i++) {
+            Object paramValue = parent.getParameterValue(i);
+            if (paramValue == blamed) {
+                Blaming blaming = new Blaming();
+                blaming.blamer = parent;
+                blaming.roleOfblamed = parent.getParameterRole(i);
+                return blaming;
+            } else if (paramValue instanceof ASTNode) {
+                Blaming blaming = findBlaming((ASTNode) paramValue, blamed, recursionDepth + 1);
+                if (blaming != null) return blaming;
+            }
+        }
+        return null;
+    }
+
+    private void appendParts(StringBuilder sb, Object[] parts) {
+        Template template = this.template != null ? this.template : (blamed != null ? blamed.getTemplate() : null);
+        for (Object partObj : parts) {
+            if (partObj instanceof Object[]) {
+                appendParts(sb, (Object[]) partObj);
+            } else {
+                String partStr;
+                partStr = tryToString(partObj);
+                if (partStr == null) {
+                    partStr = "null";
+                }
+
+                if (template != null) {
+                    if (partStr.length() > 4
+                            && partStr.charAt(0) == '<'
+                            && (
+                            (partStr.charAt(1) == '#' || partStr.charAt(1) == '@')
+                                    || (partStr.charAt(1) == '/') && (partStr.charAt(2) == '#' || partStr.charAt(2) == '@')
+                    )
+                            && partStr.charAt(partStr.length() - 1) == '>') {
+                        if (template.getActualTagSyntax() == Configuration.SQUARE_BRACKET_TAG_SYNTAX) {
+                            sb.append('[');
+                            sb.append(partStr.substring(1, partStr.length() - 1));
+                            sb.append(']');
+                        } else {
+                            sb.append(partStr);
+                        }
+                    } else {
+                        sb.append(partStr);
+                    }
+                } else {
+                    sb.append(partStr);
+                }
+            }
+        }
+    }
+
+    /**
+     * A twist on Java's toString that generates more appropriate results for generating error messages.
+     */
+    public static String toString(Object partObj) {
+        return toString(partObj, false);
+    }
+
+    public static String tryToString(Object partObj) {
+        return toString(partObj, true);
+    }
+    
+    private static String toString(Object partObj, boolean suppressToStringException) {
+        final String partStr;
+        if (partObj == null) {
+            return null;
+        } else if (partObj instanceof Class) {
+            partStr = _ClassUtil.getShortClassName((Class) partObj);
+        } else if (partObj instanceof Method || partObj instanceof Constructor) {
+            partStr = _MethodUtil.toString((Member) partObj);
+        } else {
+            partStr = suppressToStringException ? _StringUtil.tryToString(partObj) : partObj.toString();
+        }
+        return partStr;
+    }
+
+    private String[] splitToLines(String s) {
+        s = _StringUtil.replace(s, "\r\n", "\n");
+        s = _StringUtil.replace(s, "\r", "\n");
+        String[] lines = _StringUtil.split(s, '\n');
+        return lines;
+    }
+    
+    /**
+     * Needed for description <em>parts</em> that look like an FTL tag to be converted, if there's no {@link #blamed}.
+     */
+    public _ErrorDescriptionBuilder template(Template template) {
+        this.template = template;
+        return this;
+    }
+
+    public _ErrorDescriptionBuilder blame(ASTExpression blamed) {
+        this.blamed = blamed;
+        return this;
+    }
+    
+    public _ErrorDescriptionBuilder showBlamer(boolean showBlamer) {
+        this.showBlamer = showBlamer;
+        return this;
+    }
+    
+    public _ErrorDescriptionBuilder tip(String tip) {
+        tip((Object) tip);
+        return this;
+    }
+    
+    public _ErrorDescriptionBuilder tip(Object... tip) {
+        tip((Object) tip);
+        return this;
+    }
+    
+    private _ErrorDescriptionBuilder tip(Object tip) {
+        if (tip == null) {
+            return this;
+        }
+        
+        if (this.tip == null) {
+            this.tip = tip;
+        } else {
+            if (tips == null) {
+                tips = new Object[] { tip };
+            } else {
+                final int origTipsLen = tips.length;
+                
+                Object[] newTips = new Object[origTipsLen + 1];
+                for (int i = 0; i < origTipsLen; i++) {
+                    newTips[i] = tips[i];
+                }
+                newTips[origTipsLen] = tip;
+                tips = newTips;
+            }
+        }
+        return this;
+    }
+    
+    public _ErrorDescriptionBuilder tips(Object... tips) {
+        if (tips == null || tips.length == 0) {
+            return this;
+        }
+        
+        if (this.tips == null) {
+            this.tips = tips;
+        } else {
+            final int origTipsLen = this.tips.length;
+            final int additionalTipsLen = tips.length;
+            
+            Object[] newTips = new Object[origTipsLen + additionalTipsLen];
+            for (int i = 0; i < origTipsLen; i++) {
+                newTips[i] = this.tips[i];
+            }
+            for (int i = 0; i < additionalTipsLen; i++) {
+                newTips[origTipsLen + i] = tips[i];
+            }
+            this.tips = newTips;
+        }
+        return this;
+    }
+    
+    private static class Blaming {
+        ASTNode blamer;
+        ParameterRole roleOfblamed;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_MiscTemplateException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_MiscTemplateException.java b/src/main/java/org/apache/freemarker/core/_MiscTemplateException.java
new file mode 100644
index 0000000..ec0b29c
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/_MiscTemplateException.java
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+/**
+ * For internal use only; don't depend on this, there's no backward compatibility guarantee at all!
+ * {@link TemplateException}-s that don't fit into any category that warrant its own class. In fact, this was added
+ * because the API of {@link TemplateException} is too simple for the purposes of the core, but it can't be
+ * extended without breaking backward compatibility and exposing internals.  
+ */
+public class _MiscTemplateException extends TemplateException {
+
+    // -----------------------------------------------------------------------------------------------------------------
+    // Permutation group:
+    
+    public _MiscTemplateException(String description) {
+        super(description, null);
+    }
+
+    public _MiscTemplateException(Environment env, String description) {
+        super(description, env);
+    }
+    
+    // -----------------------------------------------------------------------------------------------------------------
+    // Permutation group:
+
+    public _MiscTemplateException(Throwable cause, String description) {
+        this(cause, null, description);
+    }
+
+    public _MiscTemplateException(Throwable cause, Environment env) {
+        this(cause, env, (String) null);
+    }
+
+    public _MiscTemplateException(Throwable cause) {
+        this(cause, null, (String) null);
+    }
+    
+    public _MiscTemplateException(Throwable cause, Environment env, String description) {
+        super(description, cause, env);
+    }
+
+    // -----------------------------------------------------------------------------------------------------------------
+    // Permutation group:
+    
+    public _MiscTemplateException(_ErrorDescriptionBuilder description) {
+        this(null, description);
+    }
+
+    public _MiscTemplateException(Environment env, _ErrorDescriptionBuilder description) {
+        this(null, env, description);
+    }
+
+    public _MiscTemplateException(Throwable cause, Environment env, _ErrorDescriptionBuilder description) {
+        super(cause, env, null, description);
+    }
+
+    // -----------------------------------------------------------------------------------------------------------------
+    // Permutation group:
+    
+    public _MiscTemplateException(Object... descriptionParts) {
+        this((Environment) null, descriptionParts);
+    }
+
+    public _MiscTemplateException(Environment env, Object... descriptionParts) {
+        this((Throwable) null, env, descriptionParts);
+    }
+
+    public _MiscTemplateException(Throwable cause, Object... descriptionParts) {
+        this(cause, null, descriptionParts);
+    }
+
+    public _MiscTemplateException(Throwable cause, Environment env, Object... descriptionParts) {
+        super(cause, env, null, new _ErrorDescriptionBuilder(descriptionParts));
+    }
+
+    // -----------------------------------------------------------------------------------------------------------------
+    // Permutation group:
+    
+    public _MiscTemplateException(ASTExpression blamed, Object... descriptionParts) {
+        this(blamed, null, descriptionParts);
+    }
+
+    public _MiscTemplateException(ASTExpression blamed, Environment env, Object... descriptionParts) {
+        this(blamed, null, env, descriptionParts);
+    }
+
+    public _MiscTemplateException(ASTExpression blamed, Throwable cause, Environment env, Object... descriptionParts) {
+        super(cause, env, blamed, new _ErrorDescriptionBuilder(descriptionParts).blame(blamed));
+    }
+
+    // -----------------------------------------------------------------------------------------------------------------
+    // Permutation group:
+    
+    public _MiscTemplateException(ASTExpression blamed, String description) {
+        this(blamed, null, description);
+    }
+
+    public _MiscTemplateException(ASTExpression blamed, Environment env, String description) {
+        this(blamed, null, env, description);
+    }
+
+    public _MiscTemplateException(ASTExpression blamed, Throwable cause, Environment env, String description) {
+        super(cause, env, blamed, new _ErrorDescriptionBuilder(description).blame(blamed));
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluationException.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluationException.java b/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluationException.java
new file mode 100644
index 0000000..28495ab
--- /dev/null
+++ b/src/main/java/org/apache/freemarker/core/_ObjectBuilderSettingEvaluationException.java
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ * 
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core;
+
+import org.apache.freemarker.core.util._StringUtil;
+
+/**
+ * Don't use this; used internally by FreeMarker, might changes without notice.
+ * Thrown by {@link _ObjectBuilderSettingEvaluator}.
+ */
+public class _ObjectBuilderSettingEvaluationException extends Exception {
+    
+    public _ObjectBuilderSettingEvaluationException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public _ObjectBuilderSettingEvaluationException(String message) {
+        super(message);
+    }
+
+    public _ObjectBuilderSettingEvaluationException(String expected, String src, int location) {
+        super("Expression syntax error: Expected a(n) " + expected + ", but "
+                + (location < src.length()
+                        ? "found character " + _StringUtil.jQuote("" + src.charAt(location)) + " at position "
+                            + (location + 1) + "."
+                        : "the end of the parsed string was reached.") );
+    }
+    
+}


Mime
View raw message