http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/TemplateSpecifiedEncodingHandler.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/TemplateSpecifiedEncodingHandler.java b/src/main/java/org/apache/freemarker/core/TemplateSpecifiedEncodingHandler.java new file mode 100644 index 0000000..35a22f5 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/TemplateSpecifiedEncodingHandler.java @@ -0,0 +1,60 @@ +/* + * 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.Template.WrongEncodingException; + +/** + * Specifies the behavior when the template specifies its own encoding (via {@code <#ftl encoding=...>}) in the template + * content itself, and also when it doesn't do that. + */ +public interface TemplateSpecifiedEncodingHandler { + + TemplateSpecifiedEncodingHandler DEFAULT = new TemplateSpecifiedEncodingHandler() { + + @Override + public void handle(String templateSpecificEncoding, String constructorSpecifiedEncoding) + throws WrongEncodingException { + if (constructorSpecifiedEncoding != null && templateSpecificEncoding != null + && !constructorSpecifiedEncoding.equalsIgnoreCase(templateSpecificEncoding)) { + throw new Template.WrongEncodingException(templateSpecificEncoding, constructorSpecifiedEncoding); + } + } + + }; + + /** + * Called once during template parsing, either when the {@code #ftl} directive is processed, or near the beginning + * of the template processing when there's no {@code #ftl} directive in the template. + * + * @param templateSpecificEncoding + * The encoding specified via {@code <#ftl encoding=...>}, or {@code null} if that was missing (either + * the {@code encoding} parameter or the whole {@code #ftl} directive). + * @param constructorSpecifiedEncoding + * The encoding specified to the {@link Template} constructor; also the value of + * {@link Template#getEncoding()}. If there was an encoding used for decoding the template file, it + * should be that, or if there was no encoding, it should be {@code null}. + * + * @throws WrongEncodingException + * If the template "file" has to be re-read and the {@link Template} re-created with the encoding + * specified in the constructor of {@link WrongEncodingException}. + */ + void handle(String templateSpecificEncoding, String constructorSpecifiedEncoding) throws WrongEncodingException; + +}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/TemplateValueFormat.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/TemplateValueFormat.java b/src/main/java/org/apache/freemarker/core/TemplateValueFormat.java new file mode 100644 index 0000000..cc2833c --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/TemplateValueFormat.java @@ -0,0 +1,33 @@ +/* + * 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; + +/** + * Superclass of all value format objects; objects that convert values to strings, or parse strings. + * + * @since 2.3.24 + */ +public abstract class TemplateValueFormat { + + /** + * Meant to be used in error messages to tell what format the parsed string didn't fit. + */ + public abstract String getDescription(); + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/TemplateValueFormatException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/TemplateValueFormatException.java b/src/main/java/org/apache/freemarker/core/TemplateValueFormatException.java new file mode 100644 index 0000000..f952369 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/TemplateValueFormatException.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; + +/** + * Error while getting, creating or applying {@link TemplateValueFormat}-s (including its subclasses, like + * {@link TemplateNumberFormat}). + * + * @since 2.3.24 + */ +public abstract class TemplateValueFormatException extends Exception { + + public TemplateValueFormatException(String message, Throwable cause) { + super(message, cause); + } + + public TemplateValueFormatException(String message) { + super(message); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/TemplateValueFormatFactory.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/TemplateValueFormatFactory.java b/src/main/java/org/apache/freemarker/core/TemplateValueFormatFactory.java new file mode 100644 index 0000000..3c504de --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/TemplateValueFormatFactory.java @@ -0,0 +1,28 @@ +/* + * 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; + +/** + * Superclass of all format factories. + * + * @since 2.3.24 + */ +public abstract class TemplateValueFormatFactory { + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/TemplateXHTMLOutputModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/TemplateXHTMLOutputModel.java b/src/main/java/org/apache/freemarker/core/TemplateXHTMLOutputModel.java new file mode 100644 index 0000000..3871e58 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/TemplateXHTMLOutputModel.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; + +/** + * Stores HTML markup to be printed; used with {@link HTMLOutputFormat}. + * + * @since 2.3.24 + */ +public final class TemplateXHTMLOutputModel extends CommonTemplateMarkupOutputModel<TemplateXHTMLOutputModel> { + + /** + * See {@link CommonTemplateMarkupOutputModel#CommonTemplateMarkupOutputModel(String, String)}. + */ + TemplateXHTMLOutputModel(String plainTextContent, String markupContent) { + super(plainTextContent, markupContent); + } + + @Override + public XHTMLOutputFormat getOutputFormat() { + return XHTMLOutputFormat.INSTANCE; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/TemplateXMLOutputModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/TemplateXMLOutputModel.java b/src/main/java/org/apache/freemarker/core/TemplateXMLOutputModel.java new file mode 100644 index 0000000..38d2fe1 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/TemplateXMLOutputModel.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; + +/** + * Stores XML markup to be printed; used with {@link XMLOutputFormat}. + * + * @since 2.3.24 + */ +public final class TemplateXMLOutputModel extends CommonTemplateMarkupOutputModel<TemplateXMLOutputModel> { + + /** + * See {@link CommonTemplateMarkupOutputModel#CommonTemplateMarkupOutputModel(String, String)}. + */ + TemplateXMLOutputModel(String plainTextContent, String markupContent) { + super(plainTextContent, markupContent); + } + + @Override + public XMLOutputFormat getOutputFormat() { + return XMLOutputFormat.INSTANCE; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java b/src/main/java/org/apache/freemarker/core/ThreadInterruptionSupportTemplatePostProcessor.java new file mode 100644 index 0000000..c24f2f2 --- /dev/null +++ b/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.getRootTreeNode(); + 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/7d784b2b/src/main/java/org/apache/freemarker/core/TokenMgrError.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/TokenMgrError.java b/src/main/java/org/apache/freemarker/core/TokenMgrError.java new file mode 100644 index 0000000..adddcf7 --- /dev/null +++ b/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 + */ +public 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 create 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 final 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/7d784b2b/src/main/java/org/apache/freemarker/core/UndefinedCustomFormatException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/UndefinedCustomFormatException.java b/src/main/java/org/apache/freemarker/core/UndefinedCustomFormatException.java new file mode 100644 index 0000000..26561f3 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/UndefinedCustomFormatException.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; + +/** + * @since 2.3.24 + */ +public class UndefinedCustomFormatException extends InvalidFormatStringException { + + public UndefinedCustomFormatException(String message, Throwable cause) { + super(message, cause); + } + + public UndefinedCustomFormatException(String message) { + super(message); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/UndefinedOutputFormat.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/UndefinedOutputFormat.java b/src/main/java/org/apache/freemarker/core/UndefinedOutputFormat.java new file mode 100644 index 0000000..6b6d46e --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/UndefinedOutputFormat.java @@ -0,0 +1,55 @@ +/* + * 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; + +/** + * Represents the output format used when the template output format is undecided. This is the default output format if + * FreeMarker can't select anything more specific (see + * {@link Configuration#setTemplateConfigurations(org.apache.freemarker.core.templateresolver.TemplateConfigurationFactory)}). This format doesn't + * support auto-escaping ({@link Configuration#setAutoEscapingPolicy(int)}). It will print + * {@link TemplateMarkupOutputModel}-s as is (doesn't try to convert them). + * + * @see PlainTextOutputFormat + * + * @since 2.3.24 + */ +public final class UndefinedOutputFormat extends OutputFormat { + + public static final UndefinedOutputFormat INSTANCE = new UndefinedOutputFormat(); + + private UndefinedOutputFormat() { + // Only to decrease visibility + } + + @Override + public boolean isOutputFormatMixingAllowed() { + return true; + } + + @Override + public String getName() { + return "undefined"; + } + + @Override + public String getMimeType() { + return null; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/UnexpectedTypeException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/UnexpectedTypeException.java b/src/main/java/org/apache/freemarker/core/UnexpectedTypeException.java new file mode 100644 index 0000000..638b0c5 --- /dev/null +++ b/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/7d784b2b/src/main/java/org/apache/freemarker/core/UnformattableValueException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/UnformattableValueException.java b/src/main/java/org/apache/freemarker/core/UnformattableValueException.java new file mode 100644 index 0000000..cb30c92 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/UnformattableValueException.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 org.apache.freemarker.core.model.TemplateModel; + +/** + * Thrown when a {@link TemplateModel} can't be formatted because of the value/properties of it are outside of that the + * {@link TemplateValueFormat} supports. For example, a formatter may not support dates before year 1, or can't format + * NaN. The most often used subclass is {@link UnknownDateTypeFormattingUnsupportedException}. + * + * @since 2.3.24 + */ +public class UnformattableValueException extends TemplateValueFormatException { + + public UnformattableValueException(String message, Throwable cause) { + super(message, cause); + } + + public UnformattableValueException(String message) { + super(message); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/UnknownDateTypeFormattingUnsupportedException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/UnknownDateTypeFormattingUnsupportedException.java b/src/main/java/org/apache/freemarker/core/UnknownDateTypeFormattingUnsupportedException.java new file mode 100644 index 0000000..aceca07 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/UnknownDateTypeFormattingUnsupportedException.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.model.TemplateDateModel; + +/** + * Thrown when a {@link TemplateDateModel} can't be formatted because its type is {@link TemplateDateModel#UNKNOWN}. + * + * @since 2.3.24 + */ +public final class UnknownDateTypeFormattingUnsupportedException extends UnformattableValueException { + + public UnknownDateTypeFormattingUnsupportedException() { + super(MessageUtil.UNKNOWN_DATE_TO_STRING_ERROR_MESSAGE); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/UnknownDateTypeParsingUnsupportedException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/UnknownDateTypeParsingUnsupportedException.java b/src/main/java/org/apache/freemarker/core/UnknownDateTypeParsingUnsupportedException.java new file mode 100644 index 0000000..a1173b6 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/UnknownDateTypeParsingUnsupportedException.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.model.TemplateDateModel; + +/** + * Thrown when a string can't be parsed to {@link TemplateDateModel}, because the provided target type is + * {@link TemplateDateModel#UNKNOWN}. + * + * @since 2.3.24 + */ +public final class UnknownDateTypeParsingUnsupportedException extends UnformattableValueException { + + public UnknownDateTypeParsingUnsupportedException() { + super(MessageUtil.UNKNOWN_DATE_PARSING_ERROR_MESSAGE); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/UnparsableValueException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/UnparsableValueException.java b/src/main/java/org/apache/freemarker/core/UnparsableValueException.java new file mode 100644 index 0000000..37d979d --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/UnparsableValueException.java @@ -0,0 +1,38 @@ +/* + * 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; + +/** + * Thrown when the content of the string that should be parsed by the {@link TemplateValueFormat} doesn't match what the + * format expects. + * + * @since 2.3.24 + */ +public class UnparsableValueException extends TemplateValueFormatException { + + public UnparsableValueException(String message, Throwable cause) { + super(message, cause); + } + + public UnparsableValueException(String message) { + this(message, null); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/UnregisteredOutputFormatException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/UnregisteredOutputFormatException.java b/src/main/java/org/apache/freemarker/core/UnregisteredOutputFormatException.java new file mode 100644 index 0000000..dd5a18a --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/UnregisteredOutputFormatException.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; + +/** + * @since 2.3.24 + */ +public class UnregisteredOutputFormatException extends Exception { + + public UnregisteredOutputFormatException(String message) { + this(message, null); + } + + public UnregisteredOutputFormatException(String message, Throwable cause) { + super(message, cause); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/XHTMLOutputFormat.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/XHTMLOutputFormat.java b/src/main/java/org/apache/freemarker/core/XHTMLOutputFormat.java new file mode 100644 index 0000000..101d056 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/XHTMLOutputFormat.java @@ -0,0 +1,75 @@ +/* + * 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 java.io.Writer; + +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.util._StringUtil; + +/** + * Represents the XML output format (MIME type "application/xhtml+xml", name "XHTML"). This format escapes by default + * (via {@link _StringUtil#XHTMLEnc(String)}). The {@code ?xml} built-in silently bypasses template output values of the + * type produced by this output format ({@link TemplateXHTMLOutputModel}). + * + * @since 2.3.24 + */ +public final class XHTMLOutputFormat extends CommonMarkupOutputFormat<TemplateXHTMLOutputModel> { + + /** + * The only instance (singleton) of this {@link OutputFormat}. + */ + public static final XHTMLOutputFormat INSTANCE = new XHTMLOutputFormat(); + + private XHTMLOutputFormat() { + // Only to decrease visibility + } + + @Override + public String getName() { + return "XHTML"; + } + + @Override + public String getMimeType() { + return "application/xhtml+xml"; + } + + @Override + public void output(String textToEsc, Writer out) throws IOException, TemplateModelException { + _StringUtil.XHTMLEnc(textToEsc, out); + } + + @Override + public String escapePlainText(String plainTextContent) { + return _StringUtil.XHTMLEnc(plainTextContent); + } + + @Override + public boolean isLegacyBuiltInBypassed(String builtInName) { + return builtInName.equals("html") || builtInName.equals("xml") || builtInName.equals("xhtml"); + } + + @Override + protected TemplateXHTMLOutputModel newTemplateMarkupOutputModel(String plainTextContent, String markupContent) { + return new TemplateXHTMLOutputModel(plainTextContent, markupContent); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/XMLOutputFormat.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/XMLOutputFormat.java b/src/main/java/org/apache/freemarker/core/XMLOutputFormat.java new file mode 100644 index 0000000..898b569 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/XMLOutputFormat.java @@ -0,0 +1,75 @@ +/* + * 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 java.io.Writer; + +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.util._StringUtil; + +/** + * Represents the XML output format (MIME type "application/xml", name "XML"). This format escapes by default (via + * {@link _StringUtil#XMLEnc(String)}). The {@code ?html}, {@code ?xhtml} and {@code ?xml} built-ins silently bypass + * template output values of the type produced by this output format ({@link TemplateXHTMLOutputModel}). + * + * @since 2.3.24 + */ +public final class XMLOutputFormat extends CommonMarkupOutputFormat<TemplateXMLOutputModel> { + + /** + * The only instance (singleton) of this {@link OutputFormat}. + */ + public static final XMLOutputFormat INSTANCE = new XMLOutputFormat(); + + private XMLOutputFormat() { + // Only to decrease visibility + } + + @Override + public String getName() { + return "XML"; + } + + @Override + public String getMimeType() { + return "application/xml"; + } + + @Override + public void output(String textToEsc, Writer out) throws IOException, TemplateModelException { + _StringUtil.XMLEnc(textToEsc, out); + } + + @Override + public String escapePlainText(String plainTextContent) { + return _StringUtil.XMLEnc(plainTextContent); + } + + @Override + public boolean isLegacyBuiltInBypassed(String builtInName) { + return builtInName.equals("xml"); + } + + @Override + protected TemplateXMLOutputModel newTemplateMarkupOutputModel(String plainTextContent, String markupContent) { + return new TemplateXMLOutputModel(plainTextContent, markupContent); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/XSTemplateDateFormat.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/XSTemplateDateFormat.java b/src/main/java/org/apache/freemarker/core/XSTemplateDateFormat.java new file mode 100644 index 0000000..56135f3 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/XSTemplateDateFormat.java @@ -0,0 +1,91 @@ +/* + * 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.Date; +import java.util.TimeZone; + +import org.apache.freemarker.core.util._DateUtil; +import org.apache.freemarker.core.util._DateUtil.CalendarFieldsToDateConverter; +import org.apache.freemarker.core.util._DateUtil.DateParseException; +import org.apache.freemarker.core.util._DateUtil.DateToISO8601CalendarFactory; + +/** + * XML Schema format. + */ +final class XSTemplateDateFormat extends ISOLikeTemplateDateFormat { + + XSTemplateDateFormat( + String settingValue, int parsingStart, + int dateType, + boolean zonelessInput, + TimeZone timeZone, + ISOLikeTemplateDateFormatFactory factory, + Environment env) + throws UnknownDateTypeFormattingUnsupportedException, InvalidFormatParametersException { + super(settingValue, parsingStart, dateType, zonelessInput, timeZone, factory, env); + } + + @Override + protected String format(Date date, boolean datePart, boolean timePart, boolean offsetPart, int accuracy, + TimeZone timeZone, DateToISO8601CalendarFactory calendarFactory) { + return _DateUtil.dateToXSString( + date, datePart, timePart, offsetPart, accuracy, timeZone, calendarFactory); + } + + @Override + protected Date parseDate(String s, TimeZone tz, CalendarFieldsToDateConverter calToDateConverter) + throws DateParseException { + return _DateUtil.parseXSDate(s, tz, calToDateConverter); + } + + @Override + protected Date parseTime(String s, TimeZone tz, CalendarFieldsToDateConverter calToDateConverter) + throws DateParseException { + return _DateUtil.parseXSTime(s, tz, calToDateConverter); + } + + @Override + protected Date parseDateTime(String s, TimeZone tz, + CalendarFieldsToDateConverter calToDateConverter) throws DateParseException { + return _DateUtil.parseXSDateTime(s, tz, calToDateConverter); + } + + @Override + protected String getDateDescription() { + return "W3C XML Schema date"; + } + + @Override + protected String getTimeDescription() { + return "W3C XML Schema time"; + } + + @Override + protected String getDateTimeDescription() { + return "W3C XML Schema dateTime"; + } + + @Override + protected boolean isXSMode() { + return true; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/XSTemplateDateFormatFactory.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/XSTemplateDateFormatFactory.java b/src/main/java/org/apache/freemarker/core/XSTemplateDateFormatFactory.java new file mode 100644 index 0000000..9421cc0 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/XSTemplateDateFormatFactory.java @@ -0,0 +1,43 @@ +/* + * 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.Locale; +import java.util.TimeZone; + +class XSTemplateDateFormatFactory extends ISOLikeTemplateDateFormatFactory { + + static final XSTemplateDateFormatFactory INSTANCE = new XSTemplateDateFormatFactory(); + + private XSTemplateDateFormatFactory() { + // Not meant to be instantiated + } + + @Override + public TemplateDateFormat get(String params, int dateType, Locale locale, TimeZone timeZone, boolean zonelessInput, + Environment env) throws UnknownDateTypeFormattingUnsupportedException, InvalidFormatParametersException { + // We don't cache these as creating them is cheap (only 10% speedup of ${d?string.xs} with caching) + return new XSTemplateDateFormat( + params, 2, + dateType, zonelessInput, + timeZone, this, env); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_ASTDebugBreak.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/_ASTDebugBreak.java b/src/main/java/org/apache/freemarker/core/_ASTDebugBreak.java new file mode 100644 index 0000000..00f1ef7 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/_ASTDebugBreak.java @@ -0,0 +1,90 @@ +/* + * 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.debug.impl.DebuggerService; + +/** + * Don't use this; used internally by FreeMarker, might changes without notice. + * A debug breakpoint inserted into the template + */ +public class _ASTDebugBreak extends _ASTElement { + public _ASTDebugBreak(_ASTElement nestedBlock) { + addChild(nestedBlock); + copyLocationFrom(nestedBlock); + } + + @Override + protected _ASTElement[] accept(Environment env) throws TemplateException, IOException { + if (!DebuggerService.suspendEnvironment( + env, getTemplate().getSourceName(), getChild(0).getBeginLine())) { + return getChild(0).accept(env); + } else { + throw new StopException(env, "Stopped by debugger"); + } + } + + @Override + protected String dump(boolean canonical) { + if (canonical) { + StringBuilder sb = new StringBuilder(); + sb.append("<#-- "); + sb.append("debug break"); + if (getChildCount() == 0) { + sb.append(" /-->"); + } else { + sb.append(" -->"); + sb.append(getChild(0).getCanonicalForm()); + sb.append("<#--/ debug break -->"); + } + return sb.toString(); + } else { + return "debug break"; + } + } + + @Override + String getNodeTypeSymbol() { + return "#debug_break"; + } + + @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; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/_ASTElement.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/_ASTElement.java b/src/main/java/org/apache/freemarker/core/_ASTElement.java new file mode 100644 index 0000000..af7e9c1 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/_ASTElement.java @@ -0,0 +1,473 @@ +/* + * 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 java.util.Collections; +import java.util.Enumeration; + +import org.apache.freemarker.core.model.TemplateNodeModel; +import org.apache.freemarker.core.model.TemplateSequenceModel; +import org.apache.freemarker.core.model.impl.SimpleSequence; +import org.apache.freemarker.core.util._ArrayEnumeration; + +/** + * AST non-expression node superclass: Superclass of directive calls, interpolations, static text, top-level comments, + * or other such non-expression node in the parsed template. Some information that can be found here can be accessed + * through the {@link Environment#getCurrentDirectiveCallPlace()}, which is a published API, and thus promises backward + * compatibility. + */ +// TODO [FM3] Get rid of "public" and thus the "_" prefix +abstract public class _ASTElement extends ASTNode { + + private static final int INITIAL_CHILD_BUFFER_CAPACITY = 6; + + private _ASTElement parent; + + /** + * Contains 1 or more nested elements with optional trailing {@code null}-s, or is {@code null} exactly if there are + * no nested elements. + */ + private _ASTElement[] childBuffer; + + /** + * Contains the number of elements in the {@link #childBuffer}, not counting the trailing {@code null}-s. If this is + * 0, then and only then {@link #childBuffer} must be {@code null}. + */ + private int childCount; + + /** + * The index of the element in the parent's {@link #childBuffer} array. + * + * @since 2.3.23 + */ + private int index; + + /** + * Executes this {@link _ASTElement}. Usually should not be called directly, but through + * {@link Environment#visit(_ASTElement)} or a similar {@link Environment} method. + * + * @param env + * The runtime environment + * + * @return The template elements to execute (meant to be used for nested elements), or {@code null}. Can have + * <em>trailing</em> {@code null}-s (unused buffer capacity). Returning the nested elements instead of + * executing them inside this method is a trick used for decreasing stack usage when there's nothing to do + * after the children was processed anyway. + */ + abstract _ASTElement[] accept(Environment env) throws TemplateException, IOException; + + /** + * One-line description of the element, that contain all the information that is used in {@link #getCanonicalForm()} + * , except the nested content (elements) of the element. The expressions inside the element (the parameters) has to + * be shown. Meant to be used for stack traces, also for tree views that don't go down to the expression-level. + * There are no backward-compatibility guarantees regarding the format used ATM, but it must be regular enough to be + * machine-parseable, and it must contain all information necessary for restoring an AST equivalent to the original. + * + * This final implementation calls {@link #dump(boolean) dump(false)}. + * + * @see #getCanonicalForm() + * @see #getNodeTypeSymbol() + */ + public final String getDescription() { + return dump(false); + } + + /** + * This final implementation calls {@link #dump(boolean) dump(false)}. + */ + @Override + public final String getCanonicalForm() { + return dump(true); + } + + final String getChildrenCanonicalForm() { + return getChildrenCanonicalForm(childBuffer); + } + + static String getChildrenCanonicalForm(_ASTElement[] children) { + if (children == null) { + return ""; + } + StringBuilder sb = new StringBuilder(); + for (_ASTElement child : children) { + if (child == null) { + break; + } + sb.append(child.getCanonicalForm()); + } + return sb.toString(); + } + + /** + * Tells if the element should show up in error stack traces. Note that this will be ignored for the top (current) + * element of a stack trace, as that's always shown. + */ + boolean isShownInStackTrace() { + return false; + } + + /** + * Tells if this element possibly executes its nested content for many times. This flag is useful when a template + * AST is modified for running time limiting (see {@link ThreadInterruptionSupportTemplatePostProcessor}). Elements + * that use {@link #childBuffer} should not need this, as the insertion of the timeout checks is impossible there, + * given their rigid nested element schema. + */ + abstract boolean isNestedBlockRepeater(); + + /** + * Brings the implementation of {@link #getCanonicalForm()} and {@link #getDescription()} to a single place. Don't + * call those methods in method on {@code this}, because that will result in infinite recursion! + * + * @param canonical + * if {@code true}, it calculates the return value of {@link #getCanonicalForm()}, otherwise of + * {@link #getDescription()}. + */ + abstract protected String dump(boolean canonical); + + // Methods to implement TemplateNodeModel + + public TemplateNodeModel getParentNode() { + // return parent; + return null; + } + + public String getNodeNamespace() { + return null; + } + + public String getNodeType() { + return "element"; + } + + public TemplateSequenceModel getChildNodes() { + if (childBuffer != null) { + final SimpleSequence seq = new SimpleSequence(childCount); + for (int i = 0; i < childCount; i++) { + seq.add(childBuffer[i]); + } + return seq; + } else { + return new SimpleSequence(0); + } + } + + public String getNodeName() { + String className = getClass().getName(); + int shortNameOffset = className.lastIndexOf('.') + 1; + return className.substring(shortNameOffset); + } + + // Methods so that we can implement the Swing TreeNode API. + + public boolean isLeaf() { + return childCount == 0; + } + + public int getIndex(_ASTElement node) { + for (int i = 0; i < childCount; i++) { + if (childBuffer[i].equals(node)) { + return i; + } + } + return -1; + } + + public int getChildCount() { + return childCount; + } + + /** + * Note: For element with {@code #nestedBlock}, this will hide the {@code #nestedBlock} when that's a + * {@link ASTImplicitParent}. + */ + public Enumeration children() { + return childBuffer != null + ? new _ArrayEnumeration(childBuffer, childCount) + : Collections.enumeration(Collections.EMPTY_LIST); + } + + public void setChildAt(int index, _ASTElement element) { + if (index < childCount && index >= 0) { + childBuffer[index] = element; + element.index = index; + element.parent = this; + } else { + throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + childCount); + } + } + + /** + * The element whose child this element is, or {@code null} if this is the root node. + */ + final _ASTElement getParentElement() { + return parent; + } + + final void setChildBufferCapacity(int capacity) { + int ln = childCount; + _ASTElement[] newChildBuffer = new _ASTElement[capacity]; + for (int i = 0; i < ln; i++) { + newChildBuffer[i] = childBuffer[i]; + } + childBuffer = newChildBuffer; + } + + /** + * Inserts a new nested element after the last nested element. + */ + final void addChild(_ASTElement nestedElement) { + addChild(childCount, nestedElement); + } + + /** + * Inserts a new nested element at the given index, which can also be one higher than the current highest index. + */ + final void addChild(int index, _ASTElement nestedElement) { + final int lChildCount = childCount; + + _ASTElement[] lChildBuffer = childBuffer; + if (lChildBuffer == null) { + lChildBuffer = new _ASTElement[INITIAL_CHILD_BUFFER_CAPACITY]; + childBuffer = lChildBuffer; + } else if (lChildCount == lChildBuffer.length) { + setChildBufferCapacity(lChildCount != 0 ? lChildCount * 2 : 1); + lChildBuffer = childBuffer; + } + // At this point: nestedElements == this.nestedElements, and has sufficient capacity. + + for (int i = lChildCount; i > index; i--) { + _ASTElement movedElement = lChildBuffer[i - 1]; + movedElement.index = i; + lChildBuffer[i] = movedElement; + } + nestedElement.index = index; + nestedElement.parent = this; + lChildBuffer[index] = nestedElement; + childCount = lChildCount + 1; + } + + final _ASTElement getChild(int index) { + return childBuffer[index]; + } + + /** + * @return Array containing 1 or more nested elements with optional trailing {@code null}-s, or is {@code null} + * exactly if there are no nested elements. + */ + final _ASTElement[] getChildBuffer() { + return childBuffer; + } + + /** + * @param buffWithCnt Maybe {@code null} + * + * @since 2.3.24 + */ + final void setChildren(TemplateElements buffWithCnt) { + _ASTElement[] childBuffer = buffWithCnt.getBuffer(); + int childCount = buffWithCnt.getCount(); + for (int i = 0; i < childCount; i++) { + _ASTElement child = childBuffer[i]; + child.index = i; + child.parent = this; + } + this.childBuffer = childBuffer; + this.childCount = childCount; + } + + final int getIndex() { + return index; + } + + /** + * This is a special case, because a root element is not contained in another element, so we couldn't set the + * private fields. + */ + final void setFieldsForRootElement() { + index = 0; + parent = null; + } + + /** + * Walk the AST subtree rooted by this element, and do simplifications where possible, also removes superfluous + * whitespace. + * + * @param stripWhitespace + * whether to remove superfluous whitespace + * + * @return The element this element should be replaced with in the parent. If it's the same as this element, no + * actual replacement will happen. Note that adjusting the {@link #parent} and {@link #index} of the result + * is the duty of the caller, not of this method. + */ + _ASTElement postParseCleanup(boolean stripWhitespace) throws ParseException { + int childCount = this.childCount; + if (childCount != 0) { + for (int i = 0; i < childCount; i++) { + _ASTElement te = childBuffer[i]; + + /* + // Assertion: + if (te.getIndex() != i) { + throw new BugException("Invalid index " + te.getIndex() + " (expected: " + + i + ") for: " + te.dump(false)); + } + if (te.getParent() != this) { + throw new BugException("Invalid parent " + te.getParent() + " (expected: " + + this.dump(false) + ") for: " + te.dump(false)); + } + */ + + te = te.postParseCleanup(stripWhitespace); + childBuffer[i] = te; + te.parent = this; + te.index = i; + } + for (int i = 0; i < childCount; i++) { + _ASTElement te = childBuffer[i]; + if (te.isIgnorable(stripWhitespace)) { + childCount--; + // As later isIgnorable calls might investigates the siblings, we have to move all the items now. + for (int j = i; j < childCount; j++) { + final _ASTElement te2 = childBuffer[j + 1]; + childBuffer[j] = te2; + te2.index = j; + } + childBuffer[childCount] = null; + this.childCount = childCount; + i--; + } + } + if (childCount == 0) { + childBuffer = null; + } else if (childCount < childBuffer.length + && childCount <= childBuffer.length * 3 / 4) { + _ASTElement[] trimmedChildBuffer = new _ASTElement[childCount]; + for (int i = 0; i < childCount; i++) { + trimmedChildBuffer[i] = childBuffer[i]; + } + childBuffer = trimmedChildBuffer; + } + } + return this; + } + + boolean isIgnorable(boolean stripWhitespace) { + return false; + } + + // The following methods exist to support some fancier tree-walking + // and were introduced to support the whitespace cleanup feature in 2.2 + + _ASTElement prevTerminalNode() { + _ASTElement prev = previousSibling(); + if (prev != null) { + return prev.getLastLeaf(); + } else if (parent != null) { + return parent.prevTerminalNode(); + } + return null; + } + + _ASTElement nextTerminalNode() { + _ASTElement next = nextSibling(); + if (next != null) { + return next.getFirstLeaf(); + } else if (parent != null) { + return parent.nextTerminalNode(); + } + return null; + } + + _ASTElement previousSibling() { + if (parent == null) { + return null; + } + return index > 0 ? parent.childBuffer[index - 1] : null; + } + + _ASTElement nextSibling() { + if (parent == null) { + return null; + } + return index + 1 < parent.childCount ? parent.childBuffer[index + 1] : null; + } + + private _ASTElement getFirstChild() { + return childCount == 0 ? null : childBuffer[0]; + } + + private _ASTElement getLastChild() { + final int childCount = this.childCount; + return childCount == 0 ? null : childBuffer[childCount - 1]; + } + + private _ASTElement getFirstLeaf() { + _ASTElement te = this; + while (!te.isLeaf() && !(te instanceof ASTDirMacro) && !(te instanceof ASTDirCapturingAssignment)) { + // A macro or macro invocation is treated as a leaf here for special reasons + te = te.getFirstChild(); + } + return te; + } + + private _ASTElement getLastLeaf() { + _ASTElement te = this; + while (!te.isLeaf() && !(te instanceof ASTDirMacro) && !(te instanceof ASTDirCapturingAssignment)) { + // A macro or macro invocation is treated as a leaf here for special reasons + te = te.getLastChild(); + } + return te; + } + + /** + * Tells if executing this element has output that only depends on the template content and that has no side + * effects. + */ + boolean isOutputCacheable() { + return false; + } + + boolean isChildrenOutputCacheable() { + int ln = childCount; + for (int i = 0; i < ln; i++) { + if (!childBuffer[i].isOutputCacheable()) { + return false; + } + } + return true; + } + + /** + * determines whether this element's presence on a line indicates that we should not strip opening whitespace in the + * post-parse whitespace gobbling step. + */ + boolean heedsOpeningWhitespace() { + return false; + } + + /** + * determines whether this element's presence on a line indicates that we should not strip trailing whitespace in + * the post-parse whitespace gobbling step. + */ + boolean heedsTrailingWhitespace() { + return false; + } +}
