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