http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/TemplatePostProcessor.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplatePostProcessor.java b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplatePostProcessor.java new file mode 100644 index 0000000..c664d01 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplatePostProcessor.java @@ -0,0 +1,31 @@ +/* + * 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; + +/** + * Note yet public; will change in 2.4 (as it has to process {@code UnboundTemplate}-s). + */ +abstract class TemplatePostProcessor { + + public abstract void postProcess(Template e) throws TemplatePostProcessorException; + + // TODO: getPriority, getPhase, getMustBeBefore, getMustBeAfter + +}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/TemplatePostProcessorException.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/TemplatePostProcessorException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplatePostProcessorException.java new file mode 100644 index 0000000..cebaf36 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/TemplatePostProcessorException.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; + +/** + * Not yet public; subject to change. + */ +class TemplatePostProcessorException extends Exception { + + public TemplatePostProcessorException(String message, Throwable cause) { + super(message, cause); + } + + public TemplatePostProcessorException(String message) { + super(message); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java new file mode 100644 index 0000000..d05ba08 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java @@ -0,0 +1,140 @@ +/* + * 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.IOException; + +import org.apache.freemarker.core.model.TemplateDateModel; +import org.apache.freemarker.core.model.TemplateDirectiveBody; + +/** + * Not yet public; subject to change. + * + * <p> + * Known compatibility risks when using this post-processor: + * <ul> + * <li>{@link TemplateDateModel}-s that care to explicitly check if their nested content is {@code null} might start to + * complain that you have specified a body despite that the directive doesn't support that. Directives should use + * {@link NestedContentNotSupportedException#check(TemplateDirectiveBody)} instead of a simple + * {@code null}-check to avoid this problem.</li> + * <li> + * Software that uses {@link DirectiveCallPlace#isNestedOutputCacheable()} will always get {@code false}, because + * interruption checks ({@link ASTThreadInterruptionCheck} elements) are, obviously, not cacheable. This should only + * impact the performance. + * <li> + * Software that investigates the AST will see the injected {@link ASTThreadInterruptionCheck} elements. As of this + * writing the AST API-s aren't published, also such software need to be able to deal with new kind of elements + * anyway, so this shouldn't be a problem. + * </ul> + */ +class ThreadInterruptionSupportTemplatePostProcessor extends TemplatePostProcessor { + + @Override + public void postProcess(Template t) throws TemplatePostProcessorException { + final ASTElement te = t.getRootASTNode(); + addInterruptionChecks(te); + } + + private void addInterruptionChecks(final ASTElement te) throws TemplatePostProcessorException { + if (te == null) { + return; + } + + final int childCount = te.getChildCount(); + for (int i = 0; i < childCount; i++) { + addInterruptionChecks(te.getChild(i)); + } + + if (te.isNestedBlockRepeater()) { + try { + te.addChild(0, new ASTThreadInterruptionCheck(te)); + } catch (ParseException e) { + throw new TemplatePostProcessorException("Unexpected error; see cause", e); + } + } + } + + /** + * AST directive-like node: Checks if the current thread's "interrupted" flag is set, and throws + * {@link TemplateProcessingThreadInterruptedException} if it is. We inject this to some points into the AST. + */ + static class ASTThreadInterruptionCheck extends ASTElement { + + private ASTThreadInterruptionCheck(ASTElement te) throws ParseException { + setLocation(te.getTemplate(), te.beginColumn, te.beginLine, te.beginColumn, te.beginLine); + } + + @Override + ASTElement[] accept(Environment env) throws TemplateException, IOException { + // As the API doesn't allow throwing InterruptedException here (nor anywhere else, most importantly, + // Template.process can't throw it), we must not clear the "interrupted" flag of the thread. + if (Thread.currentThread().isInterrupted()) { + throw new TemplateProcessingThreadInterruptedException(); + } + return null; + } + + @Override + protected String dump(boolean canonical) { + return canonical ? "" : "<#--" + getNodeTypeSymbol() + "--#>"; + } + + @Override + String getNodeTypeSymbol() { + return "##threadInterruptionCheck"; + } + + @Override + int getParameterCount() { + return 0; + } + + @Override + Object getParameterValue(int idx) { + throw new IndexOutOfBoundsException(); + } + + @Override + ParameterRole getParameterRole(int idx) { + throw new IndexOutOfBoundsException(); + } + + @Override + boolean isNestedBlockRepeater() { + return false; + } + + } + + /** + * Indicates that the template processing thread's "interrupted" flag was found to be set. + * + * <p>ATTENTION: This is used by https://github.com/kenshoo/freemarker-online. Don't break backward + * compatibility without updating that project too! + */ + static class TemplateProcessingThreadInterruptedException extends RuntimeException { + + TemplateProcessingThreadInterruptedException() { + super("Template processing thread \"interrupted\" flag was set."); + } + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/TokenMgrError.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/TokenMgrError.java b/freemarker-core/src/main/java/org/apache/freemarker/core/TokenMgrError.java new file mode 100644 index 0000000..4587358 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/TokenMgrError.java @@ -0,0 +1,249 @@ +/* + * 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; + +/** + * Exception thrown on lower (lexical) level parsing errors. Shouldn't reach normal FreeMarker users, as FreeMarker + * usually catches this and wraps it into a {@link ParseException}. + * + * This is a modified version of file generated by JavaCC from FTL.jj. + * You can modify this class to customize the error reporting mechanisms so long as the public interface + * remains compatible with the original. + * + * @see ParseException + */ +class TokenMgrError extends Error { + /* + * Ordinals for various reasons why an Error of this type can be thrown. + */ + + /** + * Lexical error occurred. + */ + static final int LEXICAL_ERROR = 0; + + /** + * An attempt was made to invoke a second instance of a static token manager. + */ + static final int STATIC_LEXER_ERROR = 1; + + /** + * Tried to change to an invalid lexical state. + */ + static final int INVALID_LEXICAL_STATE = 2; + + /** + * Detected (and bailed out of) an infinite loop in the token manager. + */ + static final int LOOP_DETECTED = 3; + + /** + * Indicates the reason why the exception is thrown. It will have + * one of the above 4 values. + */ + int errorCode; + + private String detail; + private Integer lineNumber, columnNumber; + private Integer endLineNumber, endColumnNumber; + + /** + * Replaces unprintable characters by their espaced (or unicode escaped) + * equivalents in the given string + */ + protected static String addEscapes(String str) { + StringBuilder retval = new StringBuilder(); + char ch; + for (int i = 0; i < str.length(); i++) { + switch (str.charAt(i)) + { + case 0 : + continue; + case '\b': + retval.append("\\b"); + continue; + case '\t': + retval.append("\\t"); + continue; + case '\n': + retval.append("\\n"); + continue; + case '\f': + retval.append("\\f"); + continue; + case '\r': + retval.append("\\r"); + continue; + case '\"': + retval.append("\\\""); + continue; + case '\'': + retval.append("\\\'"); + continue; + case '\\': + retval.append("\\\\"); + continue; + default: + if ((ch = str.charAt(i)) < 0x20 || ch > 0x7e) { + String s = "0000" + Integer.toString(ch, 16); + retval.append("\\u" + s.substring(s.length() - 4, s.length())); + } else { + retval.append(ch); + } + continue; + } + } + return retval.toString(); + } + + /** + * Returns a detailed message for the Error when it's thrown by the + * token manager to indicate a lexical error. + * Parameters : + * EOFSeen : indicates if EOF caused the lexicl error + * curLexState : lexical state in which this error occurred + * errorLine : line number when the error occurred + * errorColumn : column number when the error occurred + * errorAfter : prefix that was seen before this error occurred + * curchar : the offending character + * Note: You can customize the lexical error message by modifying this method. + */ + protected static String LexicalError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar) { + return("Lexical error: encountered " + + (EOFSeen ? "<EOF> " : ("\"" + addEscapes(String.valueOf(curChar)) + "\"") + " (" + (int) curChar + "), ") + + "after \"" + addEscapes(errorAfter) + "\"."); + } + + /** + * You can also modify the body of this method to customize your error messages. + * For example, cases like LOOP_DETECTED and INVALID_LEXICAL_STATE are not + * of end-users concern, so you can return something like : + * + * "Internal Error : Please file a bug report .... " + * + * from this method for such cases in the release version of your parser. + */ + @Override +public String getMessage() { + return super.getMessage(); + } + + /* + * Constructors of various flavors follow. + */ + + public TokenMgrError() { + } + + public TokenMgrError(String detail, int reason) { + super(detail); // the "detail" must not contain location information, the "message" might does + this.detail = detail; + errorCode = reason; + } + + /** + * @since 2.3.21 + */ + public TokenMgrError(String detail, int reason, + int errorLine, int errorColumn, + int endLineNumber, int endColumnNumber) { + super(detail); // the "detail" must not contain location information, the "message" might does + this.detail = detail; + errorCode = reason; + + lineNumber = Integer.valueOf(errorLine); // In J2SE there was no Integer.valueOf(int) + columnNumber = Integer.valueOf(errorColumn); + this.endLineNumber = Integer.valueOf(endLineNumber); + this.endColumnNumber = Integer.valueOf(endColumnNumber); + } + + /** + * Overload for JavaCC 6 compatibility. + * + * @since 2.3.24 + */ + TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, int curChar, int reason) { + this(EOFSeen, lexState, errorLine, errorColumn, errorAfter, (char) curChar, reason); + } + + public TokenMgrError(boolean EOFSeen, int lexState, int errorLine, int errorColumn, String errorAfter, char curChar, int reason) { + this(LexicalError(EOFSeen, lexState, errorLine, errorColumn, errorAfter, curChar), reason); + + lineNumber = Integer.valueOf(errorLine); // In J2SE there was no Integer.valueOf(int) + columnNumber = Integer.valueOf(errorColumn); + // We blame the single character that can't be the start of a legal token: + endLineNumber = lineNumber; + endColumnNumber = columnNumber; + } + + /** + * 1-based line number of the unexpected character(s). + * + * @since 2.3.20 + */ + public Integer getLineNumber() { + return lineNumber; + } + + /** + * 1-based column number of the unexpected character(s). + * + * @since 2.3.20 + */ + public Integer getColumnNumber() { + return columnNumber; + } + + /** + * Returns the 1-based line at which the last character of the wrong section is. This will be usually (but not + * always) the same as {@link #getLineNumber()} because the lexer can only point to the single character that + * doesn't match any patterns. + * + * @since 2.3.21 + */ + public Integer getEndLineNumber() { + return endLineNumber; + } + + /** + * Returns the 1-based column at which the last character of the wrong section is. This will be usually (but not + * always) the same as {@link #getColumnNumber()} because the lexer can only point to the single character that + * doesn't match any patterns. + * + * @since 2.3.21 + */ + public Integer getEndColumnNumber() { + return endColumnNumber; + } + + public String getDetail() { + return detail; + } + + public ParseException toParseException(Template template) { + return new ParseException(getDetail(), + template, + getLineNumber() != null ? getLineNumber().intValue() : 0, + getColumnNumber() != null ? getColumnNumber().intValue() : 0, + getEndLineNumber() != null ? getEndLineNumber().intValue() : 0, + getEndColumnNumber() != null ? getEndColumnNumber().intValue() : 0); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java b/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java new file mode 100644 index 0000000..8b253a2 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/TopLevelConfiguration.java @@ -0,0 +1,194 @@ +/* + * 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.util.Map; + +import org.apache.freemarker.core.templateresolver.CacheStorage; +import org.apache.freemarker.core.templateresolver.TemplateConfigurationFactory; +import org.apache.freemarker.core.templateresolver.TemplateLoader; +import org.apache.freemarker.core.templateresolver.TemplateLookupStrategy; +import org.apache.freemarker.core.templateresolver.TemplateNameFormat; +import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateLookupStrategy; +import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormat; +import org.apache.freemarker.core.templateresolver.impl.DefaultTemplateNameFormatFM2; +import org.apache.freemarker.core.templateresolver.impl.FileTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.MultiTemplateLoader; +import org.apache.freemarker.core.templateresolver.impl.SoftCacheStorage; + +/** + * Implemented by FreeMarker core classes (not by you) that provide {@link Configuration}-level settings. <b>New + * methods may be added any time in future FreeMarker versions, so don't try to implement this interface yourself!</b> + * + * @see ParsingAndProcessingConfiguration + */ +public interface TopLevelConfiguration extends ParsingAndProcessingConfiguration { + + /** + * The {@link TemplateLoader} that is used to look up and load templates. + * By providing your own {@link TemplateLoader} implementation, you can load templates from whatever kind of + * storages, like from relational databases, NoSQL-storages, etc. + * + * <p>You can chain several {@link TemplateLoader}-s together with {@link MultiTemplateLoader}. + * + * <p>Default value: You should always set the template loader instead of relying on the default value. + * (But if you still care what it is, before "incompatible improvements" 2.3.21 it's a {@link FileTemplateLoader} + * that uses the current directory as its root; as it's hard tell what that directory will be, it's not very useful + * and dangerous. Starting with "incompatible improvements" 2.3.21 the default is {@code null}.) + */ + TemplateLoader getTemplateLoader(); + + /** + * Tells if this setting was explicitly set (otherwise its value will be the default value). + */ + boolean isTemplateLoaderSet(); + + /** + * The {@link TemplateLookupStrategy} that is used to look up templates based on the requested name, locale and + * custom lookup condition. Its default is {@link DefaultTemplateLookupStrategy#INSTANCE}. + */ + TemplateLookupStrategy getTemplateLookupStrategy(); + + /** + * Tells if this setting was explicitly set (otherwise its value will be the default value). + */ + boolean isTemplateLookupStrategySet(); + + /** + * The template name format used; see {@link TemplateNameFormat}. The default is + * {@link DefaultTemplateNameFormatFM2#INSTANCE}, while the recommended value for new projects is + * {@link DefaultTemplateNameFormat#INSTANCE}. + */ + TemplateNameFormat getTemplateNameFormat(); + + /** + * Tells if this setting was explicitly set (otherwise its value will be the default value). + */ + boolean isTemplateNameFormatSet(); + + /** + * The {@link TemplateConfigurationFactory} that will configure individual templates where their settings differ + * from those coming from the common {@link Configuration} object. A typical use case for that is specifying the + * {@link #getOutputFormat() outputFormat} or {@link #getSourceEncoding() sourceEncoding} for templates based on + * their file extension or parent directory. + * <p> + * Note that the settings suggested by standard file extensions are stronger than that you set here. See + * {@link #getRecognizeStandardFileExtensions()} for more information about standard file extensions. + * <p> + * See "Template configurations" in the FreeMarker Manual for examples. + */ + TemplateConfigurationFactory getTemplateConfigurations(); + + /** + * Tells if this setting was explicitly set (otherwise its value will be the default value). + */ + boolean isTemplateConfigurationsSet(); + + /** + * The map-like object used for caching templates to avoid repeated loading and parsing of the template "files". + * Its {@link Configuration}-level default is a {@link SoftCacheStorage}. + */ + CacheStorage getCacheStorage(); + + /** + * Tells if this setting was explicitly set (otherwise its value will be the default value). + */ + boolean isCacheStorageSet(); + + /** + * The time in milliseconds that must elapse before checking whether there is a newer version of a template + * "file" than the cached one. Defaults to 5000 ms. + */ + long getTemplateUpdateDelayMilliseconds(); + + /** + * Tells if this setting was explicitly set (otherwise its value will be the default value). + */ + boolean isTemplateUpdateDelayMillisecondsSet(); + + /** + * Returns the value of the "incompatible improvements" setting; this is the FreeMarker version number where the + * not 100% backward compatible bug fixes and improvements that you want to enable were already implemented. In + * new projects you should set this to the FreeMarker version that you are actually using. In older projects it's + * also usually better to keep this high, however you better check the changes activated (find them below), at + * least if not only the 3rd version number (the micro version) of {@code incompatibleImprovements} is increased. + * Generally, as far as you only increase the last version number of this setting, the changes are always low + * risk. + * <p> + * Bugfixes and improvements that are fully backward compatible, also those that are important security fixes, + * are enabled regardless of the incompatible improvements setting. + * <p> + * An important consequence of setting this setting is that now your application will check if the stated minimum + * FreeMarker version requirement is met. Like if you set this setting to 3.0.1, but accidentally the + * application is deployed with FreeMarker 3.0.0, then FreeMarker will fail, telling that a higher version is + * required. After all, the fixes/improvements you have requested aren't available on a lower version. + * <p> + * Note that as FreeMarker's minor (2nd) or major (1st) version number increments, it's possible that emulating + * some of the old bugs will become unsupported, that is, even if you set this setting to a low value, it + * silently wont bring back the old behavior anymore. Information about that will be present here. + * + * <p>Currently the effects of this setting are: + * <ul> + * <li><p> + * 3.0.0: This is the lowest supported value in FreeMarker 3. + * </li> + * </ul> + * + * @return Never {@code null}. + */ + @Override + Version getIncompatibleImprovements(); + + /** + * Whether localized template lookup is enabled. Enabled by default. + * + * <p> + * With the default {@link TemplateLookupStrategy}, localized lookup works like this: Let's say your locale setting + * is {@code Locale("en", "AU")}, and you call {@link Configuration#getTemplate(String) cfg.getTemplate("foo.ftl")}. + * Then FreeMarker will look for the template under these names, stopping at the first that exists: + * {@code "foo_en_AU.ftl"}, {@code "foo_en.ftl"}, {@code "foo.ftl"}. See the description of the default value at + * {@link #getTemplateLookupStrategy()} for a more details. If you need to generate different + * template names, set your own a {@link TemplateLookupStrategy} implementation as the value of the + * {@link #getTemplateLookupStrategy() templateLookupStrategy} setting. + */ + boolean getLocalizedLookup(); + + /** + * Tells if this setting was explicitly set (otherwise its value will be the default value). + */ + boolean isLocalizedLookupSet(); + + /** + * Shared variables are variables that are visible as top-level variables for all templates, except where the data + * model contains a variable with the same name (which then shadows the shared variable). + * + * @return Not {@code null}; the {@link Map} is possibly mutable in builders, but immutable in + * {@link Configuration}. + * + * @see Configuration.Builder#setSharedVariables(Map) + */ + Map<String, Object> getSharedVariables(); + + /** + * Tells if this setting was explicitly set (if not, the default value of the setting will be used). + */ + boolean isSharedVariablesSet(); + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/UnexpectedTypeException.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/UnexpectedTypeException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/UnexpectedTypeException.java new file mode 100644 index 0000000..0f9a013 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/UnexpectedTypeException.java @@ -0,0 +1,109 @@ +/* + * 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; + +/** + * The type of a value differs from what was expected. + * + * @since 2.3.20 + */ +public class UnexpectedTypeException extends TemplateException { + + public UnexpectedTypeException(Environment env, String description) { + super(description, env); + } + + UnexpectedTypeException(Environment env, _ErrorDescriptionBuilder description) { + super(null, env, null, description); + } + + UnexpectedTypeException( + ASTExpression blamed, TemplateModel model, String expectedTypesDesc, Class[] expectedTypes, Environment env) + throws InvalidReferenceException { + super(null, env, blamed, newDesciptionBuilder(blamed, null, model, expectedTypesDesc, expectedTypes, env)); + } + + UnexpectedTypeException( + ASTExpression blamed, TemplateModel model, String expectedTypesDesc, Class[] expectedTypes, String tip, + Environment env) + throws InvalidReferenceException { + super(null, env, blamed, newDesciptionBuilder(blamed, null, model, expectedTypesDesc, expectedTypes, env) + .tip(tip)); + } + + UnexpectedTypeException( + ASTExpression blamed, TemplateModel model, String expectedTypesDesc, Class[] expectedTypes, Object[] tips, + Environment env) + throws InvalidReferenceException { + super(null, env, blamed, newDesciptionBuilder(blamed, null, model, expectedTypesDesc, expectedTypes, env) + .tips(tips)); + } + + UnexpectedTypeException( + String blamedAssignmentTargetVarName, TemplateModel model, String expectedTypesDesc, Class[] expectedTypes, + Object[] tips, + Environment env) + throws InvalidReferenceException { + super(null, env, null, newDesciptionBuilder( + null, blamedAssignmentTargetVarName, model, expectedTypesDesc, expectedTypes, env).tips(tips)); + } + + /** + * @param blamedAssignmentTargetVarName + * Used for assignments that use {@code +=} and such, in which case the {@code blamed} expression + * parameter will be null {@code null} and this parameter will be non-{null}. + */ + private static _ErrorDescriptionBuilder newDesciptionBuilder( + ASTExpression blamed, String blamedAssignmentTargetVarName, + TemplateModel model, String expectedTypesDesc, Class[] expectedTypes, Environment env) + throws InvalidReferenceException { + if (model == null) throw InvalidReferenceException.getInstance(blamed, env); + + _ErrorDescriptionBuilder errorDescBuilder = new _ErrorDescriptionBuilder( + unexpectedTypeErrorDescription(expectedTypesDesc, blamed, blamedAssignmentTargetVarName, model)) + .blame(blamed).showBlamer(true); + if (model instanceof _UnexpectedTypeErrorExplainerTemplateModel) { + Object[] tip = ((_UnexpectedTypeErrorExplainerTemplateModel) model).explainTypeError(expectedTypes); + if (tip != null) { + errorDescBuilder.tip(tip); + } + } + return errorDescBuilder; + } + + private static Object[] unexpectedTypeErrorDescription( + String expectedTypesDesc, + ASTExpression blamed, String blamedAssignmentTargetVarName, + TemplateModel model) { + return new Object[] { + "Expected ", new _DelayedAOrAn(expectedTypesDesc), ", but ", + (blamedAssignmentTargetVarName == null + ? blamed != null ? "this" : "the expression" + : new Object[] { + "assignment target variable ", + new _DelayedJQuote(blamedAssignmentTargetVarName) }), + " has evaluated to ", + new _DelayedAOrAn(new _DelayedFTLTypeDescription(model)), + (blamedAssignmentTargetVarName == null ? ":" : ".")}; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/UnknownConfigurationSettingException.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/UnknownConfigurationSettingException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/UnknownConfigurationSettingException.java new file mode 100644 index 0000000..a4e562c --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/UnknownConfigurationSettingException.java @@ -0,0 +1,40 @@ +/* + * 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.Configuration.ExtendableBuilder; +import org.apache.freemarker.core.util._StringUtil; + +/** + * Thrown by {@link ExtendableBuilder#setSetting(String, String)} if the setting name was not recognized. + */ +@SuppressWarnings("serial") +public class UnknownConfigurationSettingException extends ConfigurationException { + + UnknownConfigurationSettingException(String name, String correctedName) { + super("Unknown FreeMarker configuration setting: " + _StringUtil.jQuote(name) + + (correctedName == null ? "" : ". You may meant: " + _StringUtil.jQuote(correctedName))); + } + + UnknownConfigurationSettingException(String name, Version removedInVersion) { + super("Unknown FreeMarker configuration setting: " + _StringUtil.jQuote(name) + + (removedInVersion == null ? "" : ". This setting was removed in version " + removedInVersion)); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/Version.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/Version.java b/freemarker-core/src/main/java/org/apache/freemarker/core/Version.java new file mode 100644 index 0000000..273f9f7 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Version.java @@ -0,0 +1,297 @@ +/* + * 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.Serializable; +import java.util.Date; + +import org.apache.freemarker.core.util._StringUtil; + +/** + * Represents a version number plus the further qualifiers and build info. This is + * mostly used for representing a FreeMarker version number, but should also be able + * to parse the version strings of 3rd party libraries. + * + * @see Configuration#getVersion() + * + * @since 2.3.20 + */ +public final class Version implements Serializable { + + private final int major; + private final int minor; + private final int micro; + private final String extraInfo; + private final String originalStringValue; + + private final Boolean gaeCompliant; + private final Date buildDate; + + private final int intValue; + private volatile String calculatedStringValue; // not final because it's calculated on demand + private int hashCode; // not final because it's calculated on demand + + /** + * @throws IllegalArgumentException if the version string is malformed + */ + public Version(String stringValue) { + this(stringValue, null, null); + } + + /** + * @throws IllegalArgumentException if the version string is malformed + */ + public Version(String stringValue, Boolean gaeCompliant, Date buildDate) { + stringValue = stringValue.trim(); + originalStringValue = stringValue; + + int[] parts = new int[3]; + String extraInfoTmp = null; + { + int partIdx = 0; + for (int i = 0; i < stringValue.length(); i++) { + char c = stringValue.charAt(i); + if (isNumber(c)) { + parts[partIdx] = parts[partIdx] * 10 + (c - '0'); + } else { + if (i == 0) { + throw new IllegalArgumentException( + "The version number string " + _StringUtil.jQuote(stringValue) + + " doesn't start with a number."); + } + if (c == '.') { + char nextC = i + 1 >= stringValue.length() ? 0 : stringValue.charAt(i + 1); + if (nextC == '.') { + throw new IllegalArgumentException( + "The version number string " + _StringUtil.jQuote(stringValue) + + " contains multiple dots after a number."); + } + if (partIdx == 2 || !isNumber(nextC)) { + extraInfoTmp = stringValue.substring(i); + break; + } else { + partIdx++; + } + } else { + extraInfoTmp = stringValue.substring(i); + break; + } + } + } + + if (extraInfoTmp != null) { + char firstChar = extraInfoTmp.charAt(0); + if (firstChar == '.' || firstChar == '-' || firstChar == '_') { + extraInfoTmp = extraInfoTmp.substring(1); + if (extraInfoTmp.length() == 0) { + throw new IllegalArgumentException( + "The version number string " + _StringUtil.jQuote(stringValue) + + " has an extra info section opened with \"" + firstChar + "\", but it's empty."); + } + } + } + } + extraInfo = extraInfoTmp; + + major = parts[0]; + minor = parts[1]; + micro = parts[2]; + intValue = calculateIntValue(); + + this.gaeCompliant = gaeCompliant; + this.buildDate = buildDate; + + } + + private boolean isNumber(char c) { + return c >= '0' && c <= '9'; + } + + public Version(int major, int minor, int micro) { + this(major, minor, micro, null, null, null); + } + + /** + * Creates an object based on the {@code int} value that uses the same kind of encoding as {@link #intValue()}. + * + * @since 2.3.24 + */ + public Version(int intValue) { + this.intValue = intValue; + + micro = intValue % 1000; + minor = (intValue / 1000) % 1000; + major = intValue / 1000000; + + extraInfo = null; + gaeCompliant = null; + buildDate = null; + originalStringValue = null; + } + + public Version(int major, int minor, int micro, String extraInfo, Boolean gaeCompliant, Date buildDate) { + this.major = major; + this.minor = minor; + this.micro = micro; + this.extraInfo = extraInfo; + this.gaeCompliant = gaeCompliant; + this.buildDate = buildDate; + intValue = calculateIntValue(); + originalStringValue = null; + } + + private int calculateIntValue() { + return intValueFor(major, minor, micro); + } + + static public int intValueFor(int major, int minor, int micro) { + return major * 1000000 + minor * 1000 + micro; + } + + private String getStringValue() { + if (originalStringValue != null) return originalStringValue; + + String calculatedStringValue = this.calculatedStringValue; + if (calculatedStringValue == null) { + synchronized (this) { + calculatedStringValue = this.calculatedStringValue; + if (calculatedStringValue == null) { + calculatedStringValue = major + "." + minor + "." + micro; + if (extraInfo != null) calculatedStringValue += "-" + extraInfo; + this.calculatedStringValue = calculatedStringValue; + } + } + } + return calculatedStringValue; + } + + /** + * Contains the major.minor.micor numbers and the extraInfo part, not the other information. + */ + @Override + public String toString() { + return getStringValue(); + } + + /** + * The 1st version number, like 1 in "1.2.3". + */ + public int getMajor() { + return major; + } + + /** + * The 2nd version number, like 2 in "1.2.3". + */ + public int getMinor() { + return minor; + } + + /** + * The 3rd version number, like 3 in "1.2.3". + */ + public int getMicro() { + return micro; + } + + /** + * The arbitrary string after the micro version number without leading dot, dash or underscore, + * like "RC03" in "2.4.0-RC03". + * This is usually a qualifier (RC, SNAPHOST, nightly, beta, etc) and sometimes build info (like + * date). + */ + public String getExtraInfo() { + return extraInfo; + } + + /** + * @return The Google App Engine compliance, or {@code null}. + */ + public Boolean isGAECompliant() { + return gaeCompliant; + } + + /** + * @return The build date if known, or {@code null}. + */ + public Date getBuildDate() { + return buildDate; + } + + /** + * @return major * 1000000 + minor * 1000 + micro. + */ + public int intValue() { + return intValue; + } + + @Override + public int hashCode() { + int r = hashCode; + if (r != 0) return r; + synchronized (this) { + if (hashCode == 0) { + final int prime = 31; + int result = 1; + result = prime * result + (buildDate == null ? 0 : buildDate.hashCode()); + result = prime * result + (extraInfo == null ? 0 : extraInfo.hashCode()); + result = prime * result + (gaeCompliant == null ? 0 : gaeCompliant.hashCode()); + result = prime * result + intValue; + if (result == 0) result = -1; // 0 is reserved for "not set" + hashCode = result; + } + return hashCode; + } + } + + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + + Version other = (Version) obj; + + if (intValue != other.intValue) return false; + + if (other.hashCode() != hashCode()) return false; + + if (buildDate == null) { + if (other.buildDate != null) return false; + } else if (!buildDate.equals(other.buildDate)) { + return false; + } + + if (extraInfo == null) { + if (other.extraInfo != null) return false; + } else if (!extraInfo.equals(other.extraInfo)) { + return false; + } + + if (gaeCompliant == null) { + if (other.gaeCompliant != null) return false; + } else if (!gaeCompliant.equals(other.gaeCompliant)) { + return false; + } + + return true; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/WrongTemplateCharsetException.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/WrongTemplateCharsetException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/WrongTemplateCharsetException.java new file mode 100644 index 0000000..799efb4 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/WrongTemplateCharsetException.java @@ -0,0 +1,63 @@ +/* + * 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.nio.charset.Charset; + +/** + * Thrown by the {@link Template} constructors that specify a non-{@code null} encoding whoch doesn't match the + * encoding specified in the {@code #ftl} header of the template. + */ +public class WrongTemplateCharsetException extends ParseException { + private static final long serialVersionUID = 1L; + + private final Charset templateSpecifiedEncoding; + private final Charset constructorSpecifiedEncoding; + + /** + * @since 2.3.22 + */ + public WrongTemplateCharsetException(Charset templateSpecifiedEncoding, Charset constructorSpecifiedEncoding) { + this.templateSpecifiedEncoding = templateSpecifiedEncoding; + this.constructorSpecifiedEncoding = constructorSpecifiedEncoding; + } + + @Override + public String getMessage() { + return "Encoding specified inside the template (" + templateSpecifiedEncoding + + ") doesn't match the encoding specified for the Template constructor" + + (constructorSpecifiedEncoding != null ? " (" + constructorSpecifiedEncoding + ")." : "."); + } + + /** + * @since 2.3.22 + */ + public Charset getTemplateSpecifiedEncoding() { + return templateSpecifiedEncoding; + } + + /** + * @since 2.3.22 + */ + public Charset getConstructorSpecifiedEncoding() { + return constructorSpecifiedEncoding; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/_CharsetBuilder.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_CharsetBuilder.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_CharsetBuilder.java new file mode 100644 index 0000000..3234de8 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_CharsetBuilder.java @@ -0,0 +1,41 @@ +/* + * 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.nio.charset.Charset; + +import org.apache.freemarker.core.util._NullArgumentException; + +/** + * For internal use only; don't depend on this, there's no backward compatibility guarantee at all! + */ +public class _CharsetBuilder { + + private final String name; + + public _CharsetBuilder(String name) { + _NullArgumentException.check(name); + this.name = name; + } + + public Charset build() { + return Charset.forName(name); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/_CoreAPI.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_CoreAPI.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_CoreAPI.java new file mode 100644 index 0000000..b575901 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_CoreAPI.java @@ -0,0 +1,88 @@ +/* + * 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.model.TemplateModelException; +import org.apache.freemarker.core.util._NullArgumentException; + +/** + * For internal use only; don't depend on this, there's no backward compatibility guarantee at all! + * This class is to work around the lack of module system in Java, i.e., so that other FreeMarker packages can + * access things inside this package that users shouldn't. + */ +public final class _CoreAPI { + + public static final int VERSION_INT_3_0_0 = Configuration.VERSION_3_0_0.intValue(); + + // Can't be instantiated + private _CoreAPI() { } + + /** + * ATTENTION: This is used by https://github.com/kenshoo/freemarker-online. Don't break backward + * compatibility without updating that project too! + */ + static public void addThreadInterruptedChecks(Template template) { + try { + new ThreadInterruptionSupportTemplatePostProcessor().postProcess(template); + } catch (TemplatePostProcessorException e) { + throw new RuntimeException("Template post-processing failed", e); + } + } + + /** + * 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. + */ + // [FM3] Get rid of this problem, then delete this method + public static TemplateModelException ensureIsTemplateModelException(String modelOpMsg, TemplateException e) { + if (e instanceof TemplateModelException) { + return (TemplateModelException) e; + } else { + return new _TemplateModelException( + e.getBlamedExpression(), e.getCause(), e.getEnvironment(), modelOpMsg); + } + } + + // [FM3] Should become unnecessary as custom directive classes are reworked + public static boolean isMacroOrFunction(TemplateModel m) { + return m instanceof ASTDirMacro; + } + + // [FM3] Should become unnecessary as custom directive classes are reworked + public static boolean isFunction(TemplateModel m) { + return m instanceof ASTDirMacro && ((ASTDirMacro) m).isFunction(); + } + + public static void checkVersionNotNullAndSupported(Version incompatibleImprovements) { + _NullArgumentException.check("incompatibleImprovements", incompatibleImprovements); + int iciV = incompatibleImprovements.intValue(); + if (iciV > Configuration.getVersion().intValue()) { + throw new IllegalArgumentException("The FreeMarker version requested by \"incompatibleImprovements\" was " + + incompatibleImprovements + ", but the installed FreeMarker version is only " + + Configuration.getVersion() + ". You may need to upgrade FreeMarker in your project."); + } + if (iciV < VERSION_INT_3_0_0) { + throw new IllegalArgumentException("\"incompatibleImprovements\" must be at least 3.0.0, but was " + + incompatibleImprovements); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/_CoreLogs.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_CoreLogs.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_CoreLogs.java new file mode 100644 index 0000000..9179d7c --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_CoreLogs.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.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * For internal use only; don't depend on this, there's no backward compatibility guarantee at all! + * This class is to work around the lack of module system in Java, i.e., so that other FreeMarker packages can + * access things inside this package that users shouldn't. + */ +public final class _CoreLogs { + + // [FM3] Why "Runtime"? "TemplateProcessing" maybe? + public static final Logger RUNTIME = LoggerFactory.getLogger("org.apache.freemarker.core.Runtime"); + public static final Logger ATTEMPT = LoggerFactory.getLogger("org.apache.freemarker.core.Runtime.Attempt"); + public static final Logger SECURITY = LoggerFactory.getLogger("org.apache.freemarker.core.Security"); + public static final Logger OBJECT_WRAPPER = LoggerFactory.getLogger("org.apache.freemarker.core.model" + + ".ObjectWrapper"); + public static final Logger TEMPLATE_RESOLVER = LoggerFactory.getLogger( + "org.apache.freemarker.core.templateresolver"); + public static final Logger DEBUG_SERVER = LoggerFactory.getLogger("org.apache.freemarker.core.debug.server"); + public static final Logger DEBUG_CLIENT = LoggerFactory.getLogger("org.apache.freemarker.core.debug.client"); + + private _CoreLogs() { + // Not meant to be instantiated + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/_Debug.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_Debug.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_Debug.java new file mode 100644 index 0000000..e15374b --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_Debug.java @@ -0,0 +1,122 @@ +/* + * 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.util.ArrayList; +import java.util.Enumeration; +import java.util.List; + +/** + * Created by Ãn on 2/26/2017. + */ +public final class _Debug { + + private _Debug() { + // + } + + + public static void insertDebugBreak(Template t, int line) { + ASTElement te = findTemplateElement(t.getRootASTNode(), line); + if (te == null) { + return; + } + ASTElement parent = te.getParent(); + ASTDebugBreak db = new ASTDebugBreak(te); + // TODO: Ensure there always is a parent by making sure + // that the root element in the template is always a ASTImplicitParent + // Also make sure it doesn't conflict with anyone's code. + parent.setChildAt(parent.getIndex(te), db); + } + + public static void removeDebugBreak(Template t, int line) { + ASTElement te = findTemplateElement(t.getRootASTNode(), line); + if (te == null) { + return; + } + ASTDebugBreak db = null; + while (te != null) { + if (te instanceof ASTDebugBreak) { + db = (ASTDebugBreak) te; + break; + } + te = te.getParent(); + } + if (db == null) { + return; + } + ASTElement parent = db.getParent(); + parent.setChildAt(parent.getIndex(db), db.getChild(0)); + } + + private static ASTElement findTemplateElement(ASTElement te, int line) { + if (te.getBeginLine() > line || te.getEndLine() < line) { + return null; + } + // Find the narrowest match + List childMatches = new ArrayList(); + for (Enumeration children = te.children(); children.hasMoreElements(); ) { + ASTElement child = (ASTElement) children.nextElement(); + ASTElement childmatch = findTemplateElement(child, line); + if (childmatch != null) { + childMatches.add(childmatch); + } + } + //find a match that exactly matches the begin/end line + ASTElement bestMatch = null; + for (int i = 0; i < childMatches.size(); i++) { + ASTElement e = (ASTElement) childMatches.get(i); + + if ( bestMatch == null ) { + bestMatch = e; + } + + if ( e.getBeginLine() == line && e.getEndLine() > line ) { + bestMatch = e; + } + + if ( e.getBeginLine() == e.getEndLine() && e.getBeginLine() == line) { + bestMatch = e; + break; + } + } + if ( bestMatch != null) { + return bestMatch; + } + // If no child provides narrower match, return this + return te; + } + + public static void removeDebugBreaks(Template t) { + removeDebugBreaks(t.getRootASTNode()); + } + + private static void removeDebugBreaks(ASTElement te) { + int count = te.getChildCount(); + for (int i = 0; i < count; ++i) { + ASTElement child = te.getChild(i); + while (child instanceof ASTDebugBreak) { + ASTElement dbchild = child.getChild(0); + te.setChildAt(i, dbchild); + child = dbchild; + } + removeDebugBreaks(child); + } + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedAOrAn.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedAOrAn.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedAOrAn.java new file mode 100644 index 0000000..630fa26 --- /dev/null +++ b/freemarker-core/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/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedConversionToString.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedConversionToString.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedConversionToString.java new file mode 100644 index 0000000..4fbe13f --- /dev/null +++ b/freemarker-core/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/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedFTLTypeDescription.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedFTLTypeDescription.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedFTLTypeDescription.java new file mode 100644 index 0000000..21b6d55 --- /dev/null +++ b/freemarker-core/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/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedGetCanonicalForm.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedGetCanonicalForm.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedGetCanonicalForm.java new file mode 100644 index 0000000..38a4cd8 --- /dev/null +++ b/freemarker-core/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/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedGetMessage.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedGetMessage.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedGetMessage.java new file mode 100644 index 0000000..7bef399 --- /dev/null +++ b/freemarker-core/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/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedGetMessageWithoutStackTop.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedGetMessageWithoutStackTop.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedGetMessageWithoutStackTop.java new file mode 100644 index 0000000..7694c15 --- /dev/null +++ b/freemarker-core/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/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedJQuote.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedJQuote.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedJQuote.java new file mode 100644 index 0000000..4caf71b --- /dev/null +++ b/freemarker-core/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/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedJoinWithComma.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedJoinWithComma.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedJoinWithComma.java new file mode 100644 index 0000000..7ae1da3 --- /dev/null +++ b/freemarker-core/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/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedOrdinal.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedOrdinal.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedOrdinal.java new file mode 100644 index 0000000..443210d --- /dev/null +++ b/freemarker-core/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/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedShortClassName.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedShortClassName.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedShortClassName.java new file mode 100644 index 0000000..d9769b9 --- /dev/null +++ b/freemarker-core/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/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedToString.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedToString.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_DelayedToString.java new file mode 100644 index 0000000..5eb5c54 --- /dev/null +++ b/freemarker-core/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); + } + +}
