http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/UnsafeMethods.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/UnsafeMethods.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/UnsafeMethods.java new file mode 100644 index 0000000..e3270c3 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/UnsafeMethods.java @@ -0,0 +1,112 @@ +/* + * 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.model.impl; + +import java.io.InputStream; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import java.util.StringTokenizer; + +import org.apache.freemarker.core._CoreLogs; +import org.apache.freemarker.core.util._ClassUtil; +import org.slf4j.Logger; + +class UnsafeMethods { + + private static final Logger LOG = _CoreLogs.OBJECT_WRAPPER; + private static final String UNSAFE_METHODS_PROPERTIES = "unsafeMethods.properties"; + private static final Set UNSAFE_METHODS = createUnsafeMethodsSet(); + + private UnsafeMethods() { } + + static boolean isUnsafeMethod(Method method) { + return UNSAFE_METHODS.contains(method); + } + + private static Set createUnsafeMethodsSet() { + Properties props = new Properties(); + InputStream in = DefaultObjectWrapper.class.getResourceAsStream(UNSAFE_METHODS_PROPERTIES); + if (in == null) { + throw new IllegalStateException("Class loader resource not found: " + + DefaultObjectWrapper.class.getPackage().getName() + UNSAFE_METHODS_PROPERTIES); + } + String methodSpec = null; + try { + try { + props.load(in); + } finally { + in.close(); + } + Set set = new HashSet(props.size() * 4 / 3, 1f); + Map primClasses = createPrimitiveClassesMap(); + for (Iterator iterator = props.keySet().iterator(); iterator.hasNext(); ) { + methodSpec = (String) iterator.next(); + try { + set.add(parseMethodSpec(methodSpec, primClasses)); + } catch (ClassNotFoundException | NoSuchMethodException e) { + LOG.debug("Failed to get unsafe method", e); + } + } + return set; + } catch (Exception e) { + throw new RuntimeException("Could not load unsafe method " + methodSpec + " " + e.getClass().getName() + " " + e.getMessage()); + } + } + + private static Method parseMethodSpec(String methodSpec, Map primClasses) + throws ClassNotFoundException, + NoSuchMethodException { + int brace = methodSpec.indexOf('('); + int dot = methodSpec.lastIndexOf('.', brace); + Class clazz = _ClassUtil.forName(methodSpec.substring(0, dot)); + String methodName = methodSpec.substring(dot + 1, brace); + String argSpec = methodSpec.substring(brace + 1, methodSpec.length() - 1); + StringTokenizer tok = new StringTokenizer(argSpec, ","); + int argcount = tok.countTokens(); + Class[] argTypes = new Class[argcount]; + for (int i = 0; i < argcount; i++) { + String argClassName = tok.nextToken(); + argTypes[i] = (Class) primClasses.get(argClassName); + if (argTypes[i] == null) { + argTypes[i] = _ClassUtil.forName(argClassName); + } + } + return clazz.getMethod(methodName, argTypes); + } + + private static Map createPrimitiveClassesMap() { + Map map = new HashMap(); + map.put("boolean", Boolean.TYPE); + map.put("byte", Byte.TYPE); + map.put("char", Character.TYPE); + map.put("short", Short.TYPE); + map.put("int", Integer.TYPE); + map.put("long", Long.TYPE); + map.put("float", Float.TYPE); + map.put("double", Double.TYPE); + return map; + } + +}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java new file mode 100644 index 0000000..82da455 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_MethodUtil.java @@ -0,0 +1,319 @@ +/* + * 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.model.impl; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.HashSet; +import java.util.Set; + +import org.apache.freemarker.core._DelayedConversionToString; +import org.apache.freemarker.core._DelayedJQuote; +import org.apache.freemarker.core._TemplateModelException; +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.util.BugException; +import org.apache.freemarker.core.util._ClassUtil; + +/** + * For internal use only; don't depend on this, there's no backward compatibility guarantee at all! + */ +public final class _MethodUtil { + + private _MethodUtil() { + // Not meant to be instantiated + } + + /** + * Determines whether the type given as the 1st argument is convertible to the type given as the 2nd argument + * for method call argument conversion. This follows the rules of the Java reflection-based method call, except + * that since we don't have the value here, a boxed class is never seen as convertible to a primitive type. + * + * @return 0 means {@code false}, non-0 means {@code true}. + * That is, 0 is returned less specificity or incomparable specificity, also when if + * then method was aborted because of {@code ifHigherThan}. + * The absolute value of the returned non-0 number symbolizes how more specific it is: + * <ul> + * <li>1: The two classes are identical</li> + * <li>2: The 1st type is primitive, the 2nd type is the corresponding boxing class</li> + * <li>3: Both classes are numerical, and one is convertible into the other with widening conversion. + * E.g., {@code int} is convertible to {@code long} and {#code double}, hence {@code int} is more + * specific. + * This ignores primitive VS boxed mismatches, except that a boxed class is never seen as + * convertible to a primitive class.</li> + * <li>4: One class is {@code instanceof} of the other, but they aren't identical. + * But unlike in Java, primitive numerical types are {@code instanceof} {@link Number} here.</li> + * </ul> + */ + // TODO Seems that we don't use the full functionality of this anymore, so we could simplify this. See usages. + public static int isMoreOrSameSpecificParameterType(final Class specific, final Class generic, boolean bugfixed, + int ifHigherThan) { + if (ifHigherThan >= 4) return 0; + if (generic.isAssignableFrom(specific)) { + // Identity or widening reference conversion: + return generic == specific ? 1 : 4; + } else { + final boolean specificIsPrim = specific.isPrimitive(); + final boolean genericIsPrim = generic.isPrimitive(); + if (specificIsPrim) { + if (genericIsPrim) { + if (ifHigherThan >= 3) return 0; + return isWideningPrimitiveNumberConversion(specific, generic) ? 3 : 0; + } else { // => specificIsPrim && !genericIsPrim + if (bugfixed) { + final Class specificAsBoxed = _ClassUtil.primitiveClassToBoxingClass(specific); + if (specificAsBoxed == generic) { + // A primitive class is more specific than its boxing class, because it can't store null + return 2; + } else if (generic.isAssignableFrom(specificAsBoxed)) { + // Note: This only occurs if `specific` is a primitive numerical, and `generic == Number` + return 4; + } else if (ifHigherThan >= 3) { + return 0; + } else if (Number.class.isAssignableFrom(specificAsBoxed) + && Number.class.isAssignableFrom(generic)) { + return isWideningBoxedNumberConversion(specificAsBoxed, generic) ? 3 : 0; + } else { + return 0; + } + } else { + return 0; + } + } + } else { // => !specificIsPrim + if (ifHigherThan >= 3) return 0; + if (bugfixed && !genericIsPrim + && Number.class.isAssignableFrom(specific) && Number.class.isAssignableFrom(generic)) { + return isWideningBoxedNumberConversion(specific, generic) ? 3 : 0; + } else { + return 0; + } + } + } // of: !generic.isAssignableFrom(specific) + } + + private static boolean isWideningPrimitiveNumberConversion(final Class source, final Class target) { + if (target == Short.TYPE && (source == Byte.TYPE)) { + return true; + } else if (target == Integer.TYPE && + (source == Short.TYPE || source == Byte.TYPE)) { + return true; + } else if (target == Long.TYPE && + (source == Integer.TYPE || source == Short.TYPE || + source == Byte.TYPE)) { + return true; + } else if (target == Float.TYPE && + (source == Long.TYPE || source == Integer.TYPE || + source == Short.TYPE || source == Byte.TYPE)) { + return true; + } else if (target == Double.TYPE && + (source == Float.TYPE || source == Long.TYPE || + source == Integer.TYPE || source == Short.TYPE || + source == Byte.TYPE)) { + return true; + } else { + return false; + } + } + + private static boolean isWideningBoxedNumberConversion(final Class source, final Class target) { + if (target == Short.class && source == Byte.class) { + return true; + } else if (target == Integer.class && + (source == Short.class || source == Byte.class)) { + return true; + } else if (target == Long.class && + (source == Integer.class || source == Short.class || + source == Byte.class)) { + return true; + } else if (target == Float.class && + (source == Long.class || source == Integer.class || + source == Short.class || source == Byte.class)) { + return true; + } else if (target == Double.class && + (source == Float.class || source == Long.class || + source == Integer.class || source == Short.class || + source == Byte.class)) { + return true; + } else { + return false; + } + } + + /** + * Attention, this doesn't handle primitive classes correctly, nor numerical conversions. + */ + public static Set getAssignables(Class c1, Class c2) { + Set s = new HashSet(); + collectAssignables(c1, c2, s); + return s; + } + + private static void collectAssignables(Class c1, Class c2, Set s) { + if (c1.isAssignableFrom(c2)) { + s.add(c1); + } + Class sc = c1.getSuperclass(); + if (sc != null) { + collectAssignables(sc, c2, s); + } + Class[] itf = c1.getInterfaces(); + for (Class anItf : itf) { + collectAssignables(anItf, c2, s); + } + } + + public static Class[] getParameterTypes(Member member) { + if (member instanceof Method) { + return ((Method) member).getParameterTypes(); + } + if (member instanceof Constructor) { + return ((Constructor) member).getParameterTypes(); + } + throw new IllegalArgumentException("\"member\" must be Method or Constructor"); + } + + public static boolean isVarargs(Member member) { + if (member instanceof Method) { + return ((Method) member).isVarArgs(); + } + if (member instanceof Constructor) { + return ((Constructor) member).isVarArgs(); + } + throw new BugException(); + } + + /** + * Returns a more streamlined method or constructor description than {@code Member.toString()} does. + */ + public static String toString(Member member) { + if (!(member instanceof Method || member instanceof Constructor)) { + throw new IllegalArgumentException("\"member\" must be a Method or Constructor"); + } + + StringBuilder sb = new StringBuilder(); + + if ((member.getModifiers() & Modifier.STATIC) != 0) { + sb.append("static "); + } + + String className = _ClassUtil.getShortClassName(member.getDeclaringClass()); + if (className != null) { + sb.append(className); + sb.append('.'); + } + sb.append(member.getName()); + + sb.append('('); + Class[] paramTypes = _MethodUtil.getParameterTypes(member); + for (int i = 0; i < paramTypes.length; i++) { + if (i != 0) sb.append(", "); + String paramTypeDecl = _ClassUtil.getShortClassName(paramTypes[i]); + if (i == paramTypes.length - 1 && paramTypeDecl.endsWith("[]") && _MethodUtil.isVarargs(member)) { + sb.append(paramTypeDecl.substring(0, paramTypeDecl.length() - 2)); + sb.append("..."); + } else { + sb.append(paramTypeDecl); + } + } + sb.append(')'); + + return sb.toString(); + } + + public static Object[] invocationErrorMessageStart(Member member) { + return invocationErrorMessageStart(member, member instanceof Constructor); + } + + private static Object[] invocationErrorMessageStart(Object member, boolean isConstructor) { + return new Object[] { "Java ", isConstructor ? "constructor " : "method ", new _DelayedJQuote(member) }; + } + + public static TemplateModelException newInvocationTemplateModelException(Object object, Member member, Throwable e) { + return newInvocationTemplateModelException( + object, + member, + (member.getModifiers() & Modifier.STATIC) != 0, + member instanceof Constructor, + e); + } + + public static TemplateModelException newInvocationTemplateModelException(Object object, CallableMemberDescriptor callableMemberDescriptor, Throwable e) { + return newInvocationTemplateModelException( + object, + new _DelayedConversionToString(callableMemberDescriptor) { + @Override + protected String doConversion(Object callableMemberDescriptor) { + return ((CallableMemberDescriptor) callableMemberDescriptor).getDeclaration(); + } + }, + callableMemberDescriptor.isStatic(), + callableMemberDescriptor.isConstructor(), + e); + } + + private static TemplateModelException newInvocationTemplateModelException( + Object parentObject, Object member, boolean isStatic, boolean isConstructor, Throwable e) { + while (e instanceof InvocationTargetException) { + Throwable cause = ((InvocationTargetException) e).getTargetException(); + if (cause != null) { + e = cause; + } else { + break; + } + } + + return new _TemplateModelException(e, + invocationErrorMessageStart(member, isConstructor), + " threw an exception", + isStatic || isConstructor ? "" : new Object[] { + " when invoked on ", parentObject.getClass(), " object ", new _DelayedJQuote(parentObject) + }, + "; see cause exception in the Java stack trace."); + } + + /** + * Extracts the JavaBeans property from a reader method name, or returns {@code null} if the method name doesn't + * look like a reader method name. + */ + public static String getBeanPropertyNameFromReaderMethodName(String name, Class<?> returnType) { + int start; + if (name.startsWith("get")) { + start = 3; + } else if (returnType == boolean.class && name.startsWith("is")) { + start = 2; + } else { + return null; + } + int ln = name.length(); + + if (start == ln) { + return null; + } + char c1 = name.charAt(start); + + return start + 1 < ln && Character.isUpperCase(name.charAt(start + 1)) && Character.isUpperCase(c1) + ? name.substring(start) // getFOOBar => "FOOBar" (not lower case) according the JavaBeans spec. + : new StringBuilder(ln - start).append(Character.toLowerCase(c1)).append(name, start + 1, ln) + .toString(); + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_ModelAPI.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_ModelAPI.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_ModelAPI.java new file mode 100644 index 0000000..fecb0b0 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/_ModelAPI.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.model.impl; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.util._CollectionUtil; + +/** + * For internal use only; don't depend on this, there's no backward compatibility guarantee at all! + * This class is to work around the lack of module system in Java, i.e., so that other FreeMarker packages can + * access things inside this package that users shouldn't. + */ +public class _ModelAPI { + + private _ModelAPI() { } + + public static Object newInstance(Class<?> pClass, Object[] args, DefaultObjectWrapper ow) + throws NoSuchMethodException, IllegalArgumentException, InstantiationException, + IllegalAccessException, InvocationTargetException, TemplateModelException { + return newInstance(getConstructorDescriptor(pClass, args), args, ow); + } + + /** + * Gets the constructor that matches the types of the arguments the best. So this is more + * than what the Java reflection API provides in that it can handle overloaded constructors. This re-uses the + * overloaded method selection logic of {@link DefaultObjectWrapper}. + */ + private static CallableMemberDescriptor getConstructorDescriptor(Class<?> pClass, Object[] args) + throws NoSuchMethodException { + if (args == null) args = _CollectionUtil.EMPTY_OBJECT_ARRAY; + + final ArgumentTypes argTypes = new ArgumentTypes(args); + final List<ReflectionCallableMemberDescriptor> fixedArgMemberDescs + = new ArrayList<>(); + final List<ReflectionCallableMemberDescriptor> varArgsMemberDescs + = new ArrayList<>(); + for (Constructor<?> constr : pClass.getConstructors()) { + ReflectionCallableMemberDescriptor memberDesc = new ReflectionCallableMemberDescriptor(constr, constr.getParameterTypes()); + if (!_MethodUtil.isVarargs(constr)) { + fixedArgMemberDescs.add(memberDesc); + } else { + varArgsMemberDescs.add(memberDesc); + } + } + + MaybeEmptyCallableMemberDescriptor contrDesc = argTypes.getMostSpecific(fixedArgMemberDescs, false); + if (contrDesc == EmptyCallableMemberDescriptor.NO_SUCH_METHOD) { + contrDesc = argTypes.getMostSpecific(varArgsMemberDescs, true); + } + + if (contrDesc instanceof EmptyCallableMemberDescriptor) { + if (contrDesc == EmptyCallableMemberDescriptor.NO_SUCH_METHOD) { + throw new NoSuchMethodException( + "There's no public " + pClass.getName() + + " constructor with compatible parameter list."); + } else if (contrDesc == EmptyCallableMemberDescriptor.AMBIGUOUS_METHOD) { + throw new NoSuchMethodException( + "There are multiple public " + pClass.getName() + + " constructors that match the compatible parameter list with the same preferability."); + } else { + throw new NoSuchMethodException(); + } + } else { + return (CallableMemberDescriptor) contrDesc; + } + } + + private static Object newInstance(CallableMemberDescriptor constrDesc, Object[] args, DefaultObjectWrapper ow) + throws InstantiationException, IllegalAccessException, InvocationTargetException, IllegalArgumentException, + TemplateModelException { + if (args == null) args = _CollectionUtil.EMPTY_OBJECT_ARRAY; + + final Object[] packedArgs; + if (constrDesc.isVarargs()) { + // We have to put all the varargs arguments into a single array argument. + + final Class<?>[] paramTypes = constrDesc.getParamTypes(); + final int fixedArgCnt = paramTypes.length - 1; + + packedArgs = new Object[fixedArgCnt + 1]; + for (int i = 0; i < fixedArgCnt; i++) { + packedArgs[i] = args[i]; + } + + final Class<?> compType = paramTypes[fixedArgCnt].getComponentType(); + final int varArgCnt = args.length - fixedArgCnt; + final Object varArgsArray = Array.newInstance(compType, varArgCnt); + for (int i = 0; i < varArgCnt; i++) { + Array.set(varArgsArray, i, args[fixedArgCnt + i]); + } + packedArgs[fixedArgCnt] = varArgsArray; + } else { + packedArgs = args; + } + + return constrDesc.invokeConstructor(ow, packedArgs); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/package.html ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/package.html b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/package.html new file mode 100644 index 0000000..b3db746 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/package.html @@ -0,0 +1,26 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--> +<html> +<head> +</head> +<body> +<p>Data model and template language type system: Standard implementations. This package is part of the +published API, that is, user code can safely depend on it.</p> +</body> +</html> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/package.html ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/package.html b/freemarker-core/src/main/java/org/apache/freemarker/core/model/package.html new file mode 100644 index 0000000..a2a9cfe --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/package.html @@ -0,0 +1,25 @@ +<!-- + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, + software distributed under the License is distributed on an + "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + KIND, either express or implied. See the License for the + specific language governing permissions and limitations + under the License. +--> +<html> +<head> +</head> +<body> +<p>Data model and template language type system: Base classes/interfaces.</p> +</body> +</html> http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/CommonMarkupOutputFormat.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/CommonMarkupOutputFormat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/CommonMarkupOutputFormat.java new file mode 100644 index 0000000..760f28b --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/CommonMarkupOutputFormat.java @@ -0,0 +1,124 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.freemarker.core.outputformat; + +import java.io.IOException; +import java.io.Writer; + +import org.apache.freemarker.core.model.TemplateModelException; + +/** + * Common superclass for implementing {@link MarkupOutputFormat}-s that use a {@link CommonTemplateMarkupOutputModel} + * subclass. + * + * @since 2.3.24 + */ +public abstract class CommonMarkupOutputFormat<MO extends CommonTemplateMarkupOutputModel> + extends MarkupOutputFormat<MO> { + + protected CommonMarkupOutputFormat() { + // Only to decrease visibility + } + + @Override + public final MO fromPlainTextByEscaping(String textToEsc) throws TemplateModelException { + return newTemplateMarkupOutputModel(textToEsc, null); + } + + @Override + public final MO fromMarkup(String markupText) throws TemplateModelException { + return newTemplateMarkupOutputModel(null, markupText); + } + + @Override + public final void output(MO mo, Writer out) throws IOException, TemplateModelException { + String mc = mo.getMarkupContent(); + if (mc != null) { + out.write(mc); + } else { + output(mo.getPlainTextContent(), out); + } + } + + @Override + public abstract void output(String textToEsc, Writer out) throws IOException, TemplateModelException; + + @Override + public final String getSourcePlainText(MO mo) throws TemplateModelException { + return mo.getPlainTextContent(); + } + + @Override + public final String getMarkupString(MO mo) throws TemplateModelException { + String mc = mo.getMarkupContent(); + if (mc != null) { + return mc; + } + + mc = escapePlainText(mo.getPlainTextContent()); + mo.setMarkupContent(mc); + return mc; + } + + @Override + public final MO concat(MO mo1, MO mo2) throws TemplateModelException { + String pc1 = mo1.getPlainTextContent(); + String mc1 = mo1.getMarkupContent(); + String pc2 = mo2.getPlainTextContent(); + String mc2 = mo2.getMarkupContent(); + + String pc3 = pc1 != null && pc2 != null ? pc1 + pc2 : null; + String mc3 = mc1 != null && mc2 != null ? mc1 + mc2 : null; + if (pc3 != null || mc3 != null) { + return newTemplateMarkupOutputModel(pc3, mc3); + } + + if (pc1 != null) { + return newTemplateMarkupOutputModel(null, getMarkupString(mo1) + mc2); + } else { + return newTemplateMarkupOutputModel(null, mc1 + getMarkupString(mo2)); + } + } + + @Override + public boolean isEmpty(MO mo) throws TemplateModelException { + String s = mo.getPlainTextContent(); + if (s != null) { + return s.length() == 0; + } + return mo.getMarkupContent().length() == 0; + } + + @Override + public boolean isOutputFormatMixingAllowed() { + return false; + } + + @Override + public boolean isAutoEscapedByDefault() { + return true; + } + + /** + * Creates a new {@link CommonTemplateMarkupOutputModel} that's bound to this {@link OutputFormat} instance. + */ + protected abstract MO newTemplateMarkupOutputModel(String plainTextContent, String markupContent) + throws TemplateModelException; + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/CommonTemplateMarkupOutputModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/CommonTemplateMarkupOutputModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/CommonTemplateMarkupOutputModel.java new file mode 100644 index 0000000..c6a7894 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/CommonTemplateMarkupOutputModel.java @@ -0,0 +1,69 @@ +/* + * 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.outputformat; + +import org.apache.freemarker.core.model.TemplateMarkupOutputModel; + +/** + * Common superclass for implementing {@link TemplateMarkupOutputModel}-s that belong to a + * {@link CommonMarkupOutputFormat} subclass format. + * + * <p> + * Thread-safe after proper publishing. Calculated fields (typically, the markup calculated from plain text) might will + * be re-calculated for multiple times if accessed from multiple threads (this only affects performance, not + * functionality). + * + * @since 2.3.24 + */ +public abstract class CommonTemplateMarkupOutputModel<MO extends CommonTemplateMarkupOutputModel<MO>> + implements TemplateMarkupOutputModel<MO> { + + private final String plainTextContent; + private String markupContent; + + /** + * A least one of the parameters must be non-{@code null}! + */ + protected CommonTemplateMarkupOutputModel(String plainTextContent, String markupContent) { + this.plainTextContent = plainTextContent; + this.markupContent = markupContent; + } + + @Override + public abstract CommonMarkupOutputFormat<MO> getOutputFormat(); + + /** Maybe {@code null}, but then {@link #getMarkupContent()} isn't {@code null}. */ + final String getPlainTextContent() { + return plainTextContent; + } + + /** Maybe {@code null}, but then {@link #getPlainTextContent()} isn't {@code null}. */ + final String getMarkupContent() { + return markupContent; + } + + /** + * Use only to set the value calculated from {@link #getPlainTextContent()}, when {@link #getMarkupContent()} was + * still {@code null}! + */ + final void setMarkupContent(String markupContent) { + this.markupContent = markupContent; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/MarkupOutputFormat.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/MarkupOutputFormat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/MarkupOutputFormat.java new file mode 100644 index 0000000..aac7d54 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/MarkupOutputFormat.java @@ -0,0 +1,135 @@ +/* + * 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.outputformat; + +import java.io.IOException; +import java.io.Writer; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.model.TemplateMarkupOutputModel; +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.outputformat.impl.HTMLOutputFormat; +import org.apache.freemarker.core.outputformat.impl.TemplateHTMLOutputModel; + +/** + * Superclass of {@link OutputFormat}-s that represent a "markup" format, which is any format where certain character + * sequences have special meaning and thus may need escaping. (Escaping is important for FreeMarker, as typically it has + * to insert non-markup text from the data-model into the output markup. See also the + * {@link Configuration#getOutputFormat() outputFormat} configuration setting.) + * + * <p> + * A {@link MarkupOutputFormat} subclass always has a corresponding {@link TemplateMarkupOutputModel} subclass pair + * (like {@link HTMLOutputFormat} has {@link TemplateHTMLOutputModel}). The {@link OutputFormat} implements the + * operations related to {@link TemplateMarkupOutputModel} objects of that kind, while the + * {@link TemplateMarkupOutputModel} only encapsulates the data (the actual markup or text). + * + * <p> + * To implement a custom output format, you may want to extend {@link CommonMarkupOutputFormat}. + * + * @param <MO> + * The {@link TemplateMarkupOutputModel} class this output format can deal with. + * + * @since 2.3.24 + */ +public abstract class MarkupOutputFormat<MO extends TemplateMarkupOutputModel> extends OutputFormat { + + protected MarkupOutputFormat() { + // Only to decrease visibility + } + + /** + * Converts a {@link String} that's assumed to be plain text to {@link TemplateMarkupOutputModel}, by escaping any + * special characters in the plain text. This corresponds to {@code ?esc}, or, to outputting with auto-escaping if + * that wasn't using {@link #output(String, Writer)} as an optimization. + * + * @see #escapePlainText(String) + * @see #getSourcePlainText(TemplateMarkupOutputModel) + */ + public abstract MO fromPlainTextByEscaping(String textToEsc) throws TemplateModelException; + + /** + * Wraps a {@link String} that's already markup to {@link TemplateMarkupOutputModel} interface, to indicate its + * format. This corresponds to {@code ?noEsc}. (This methods is allowed to throw {@link TemplateModelException} if + * the parameter markup text is malformed, but it's unlikely that an implementation chooses to parse the parameter + * until, and if ever, that becomes necessary.) + * + * @see #getMarkupString(TemplateMarkupOutputModel) + */ + public abstract MO fromMarkup(String markupText) throws TemplateModelException; + + /** + * Prints the parameter model to the output. + */ + public abstract void output(MO mo, Writer out) throws IOException, TemplateModelException; + + /** + * Equivalent to calling {@link #fromPlainTextByEscaping(String)} and then + * {@link #output(TemplateMarkupOutputModel, Writer)}, but the implementation may uses a more efficient solution. + */ + public abstract void output(String textToEsc, Writer out) throws IOException, TemplateModelException; + + /** + * If this {@link TemplateMarkupOutputModel} was created with {@link #fromPlainTextByEscaping(String)}, it returns + * the original plain text, otherwise it returns {@code null}. Useful for converting between different types + * of markups, as if the source format can be converted to plain text without loss, then that just has to be + * re-escaped with the target format to do the conversion. + */ + public abstract String getSourcePlainText(MO mo) throws TemplateModelException; + + /** + * Returns the content as markup text; never {@code null}. If this {@link TemplateMarkupOutputModel} was created + * with {@link #fromMarkup(String)}, it might returns the original markup text literally, but this is not required + * as far as the returned markup means the same. If this {@link TemplateMarkupOutputModel} wasn't created + * with {@link #fromMarkup(String)} and it doesn't yet have the markup, it has to generate the markup now. + */ + public abstract String getMarkupString(MO mo) throws TemplateModelException; + + /** + * Returns a {@link TemplateMarkupOutputModel} that contains the content of both {@link TemplateMarkupOutputModel} + * objects concatenated. + */ + public abstract MO concat(MO mo1, MO mo2) throws TemplateModelException; + + /** + * Should give the same result as {@link #fromPlainTextByEscaping(String)} and then + * {@link #getMarkupString(TemplateMarkupOutputModel)}, but the implementation may uses a more efficient solution. + */ + public abstract String escapePlainText(String plainTextContent) throws TemplateModelException; + + /** + * Returns if the markup is empty (0 length). This is used by at least {@code ?hasContent}. + */ + public abstract boolean isEmpty(MO mo) throws TemplateModelException; + + /** + * Tells if a string built-in that can't handle a {@link TemplateMarkupOutputModel} left hand operand can bypass + * this object as is. A typical such case would be when a {@link TemplateHTMLOutputModel} of "HTML" format bypasses + * {@code ?html}. + */ + public abstract boolean isLegacyBuiltInBypassed(String builtInName) throws TemplateModelException; + + /** + * Tells if by default auto-escaping should be on for this format. It should be {@code true} if you need to escape + * on most of the places where you insert values. + * + * @see Configuration#getAutoEscapingPolicy() + */ + public abstract boolean isAutoEscapedByDefault(); + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/OutputFormat.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/OutputFormat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/OutputFormat.java new file mode 100644 index 0000000..8004ae2 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/OutputFormat.java @@ -0,0 +1,86 @@ +/* + * 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.outputformat; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.model.TemplateMarkupOutputModel; +import org.apache.freemarker.core.outputformat.impl.UndefinedOutputFormat; +import org.apache.freemarker.core.util._ClassUtil; +import org.apache.freemarker.core.util._StringUtil; + +/** + * Represents an output format. If you need auto-escaping, see its subclass, {@link MarkupOutputFormat}. + * + * @see Configuration#getOutputFormat() + * @see Configuration#getRegisteredCustomOutputFormats() + * @see MarkupOutputFormat + * + * @since 2.3.24 + */ +public abstract class OutputFormat { + + /** + * The short name used to refer to this format (like in the {@code #ftl} header). + */ + public abstract String getName(); + + /** + * Returns the MIME type of the output format. This might comes handy when generating a HTTP response. {@code null} + * {@code null} if this output format doesn't clearly corresponds to a specific MIME type. + */ + public abstract String getMimeType(); + + /** + * Tells if this output format allows inserting {@link TemplateMarkupOutputModel}-s of another output formats into + * it. If {@code true}, the foreign {@link TemplateMarkupOutputModel} will be inserted into the output as is (like + * if the surrounding output format was the same). This is usually a bad idea to allow, as such an event could + * indicate application bugs. If this method returns {@code false} (recommended), then FreeMarker will try to + * assimilate the inserted value by converting its format to this format, which will currently (2.3.24) cause + * exception, unless the inserted value is made by escaping plain text and the target format is non-escaping, in + * which case format conversion is trivially possible. (It's not impossible that conversions will be extended beyond + * this, if there will be demand for that.) + * + * <p> + * {@code true} value is used by {@link UndefinedOutputFormat}. + */ + public abstract boolean isOutputFormatMixingAllowed(); + + /** + * Returns the short description of this format, to be used in error messages. + * Override {@link #toStringExtraProperties()} to customize this. + */ + @Override + public final String toString() { + String extras = toStringExtraProperties(); + return getName() + "(" + + "mimeType=" + _StringUtil.jQuote(getMimeType()) + ", " + + "class=" + _ClassUtil.getShortClassNameOfObject(this, true) + + (extras.length() != 0 ? ", " : "") + extras + + ")"; + } + + /** + * Should be like {@code "foo=\"something\", bar=123"}; this will be inserted inside the parentheses in + * {@link #toString()}. Shouldn't return {@code null}; should return {@code ""} if there are no extra properties. + */ + protected String toStringExtraProperties() { + return ""; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/UnregisteredOutputFormatException.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/UnregisteredOutputFormatException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/UnregisteredOutputFormatException.java new file mode 100644 index 0000000..86dbfa3 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/UnregisteredOutputFormatException.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.outputformat; + +import org.apache.freemarker.core.Configuration; + +/** + * Thrown by {@link Configuration#getOutputFormat(String)}. + * + * @since 2.3.24 + */ +@SuppressWarnings("serial") +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/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/CSSOutputFormat.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/CSSOutputFormat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/CSSOutputFormat.java new file mode 100644 index 0000000..6a03d54 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/CSSOutputFormat.java @@ -0,0 +1,54 @@ +/* + * 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.outputformat.impl; + +import org.apache.freemarker.core.outputformat.OutputFormat; + +/** + * Represents the CSS output format (MIME type "text/css", name "CSS"). This format doesn't support escaping. + * + * @since 2.3.24 + */ +public class CSSOutputFormat extends OutputFormat { + + /** + * The only instance (singleton) of this {@link OutputFormat}. + */ + public static final CSSOutputFormat INSTANCE = new CSSOutputFormat(); + + private CSSOutputFormat() { + // Only to decrease visibility + } + + @Override + public String getName() { + return "CSS"; + } + + @Override + public String getMimeType() { + return "text/css"; + } + + @Override + public boolean isOutputFormatMixingAllowed() { + return false; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/CombinedMarkupOutputFormat.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/CombinedMarkupOutputFormat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/CombinedMarkupOutputFormat.java new file mode 100644 index 0000000..5239e3f --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/CombinedMarkupOutputFormat.java @@ -0,0 +1,108 @@ +/* + * 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.outputformat.impl; + +import java.io.IOException; +import java.io.Writer; + +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.outputformat.CommonMarkupOutputFormat; +import org.apache.freemarker.core.outputformat.MarkupOutputFormat; + +/** + * Represents two markup formats nested into each other. For example, markdown nested into HTML. + * + * @since 2.3.24 + */ +public final class CombinedMarkupOutputFormat extends CommonMarkupOutputFormat<TemplateCombinedMarkupOutputModel> { + + private final String name; + + private final MarkupOutputFormat outer; + private final MarkupOutputFormat inner; + + /** + * Same as {@link #CombinedMarkupOutputFormat(String, MarkupOutputFormat, MarkupOutputFormat)} with {@code null} as + * the {@code name} parameter. + */ + public CombinedMarkupOutputFormat(MarkupOutputFormat outer, MarkupOutputFormat inner) { + this(null, outer, inner); + } + + /** + * @param name + * Maybe {@code null}, in which case it defaults to + * <code>outer.getName() + "{" + inner.getName() + "}"</code>. + */ + public CombinedMarkupOutputFormat(String name, MarkupOutputFormat outer, MarkupOutputFormat inner) { + this.name = name != null ? null : outer.getName() + "{" + inner.getName() + "}"; + this.outer = outer; + this.inner = inner; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getMimeType() { + return outer.getMimeType(); + } + + @Override + public void output(String textToEsc, Writer out) throws IOException, TemplateModelException { + outer.output(inner.escapePlainText(textToEsc), out); + } + + @Override + public String escapePlainText(String plainTextContent) throws TemplateModelException { + return outer.escapePlainText(inner.escapePlainText(plainTextContent)); + } + + @Override + public boolean isLegacyBuiltInBypassed(String builtInName) throws TemplateModelException { + return outer.isLegacyBuiltInBypassed(builtInName); + } + + @Override + public boolean isAutoEscapedByDefault() { + return outer.isAutoEscapedByDefault(); + } + + @Override + public boolean isOutputFormatMixingAllowed() { + return outer.isOutputFormatMixingAllowed(); + } + + public MarkupOutputFormat getOuterOutputFormat() { + return outer; + } + + public MarkupOutputFormat getInnerOutputFormat() { + return inner; + } + + @Override + protected TemplateCombinedMarkupOutputModel newTemplateMarkupOutputModel( + String plainTextContent, String markupContent) { + return new TemplateCombinedMarkupOutputModel(plainTextContent, markupContent, this); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/HTMLOutputFormat.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/HTMLOutputFormat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/HTMLOutputFormat.java new file mode 100644 index 0000000..0cebf64 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/HTMLOutputFormat.java @@ -0,0 +1,77 @@ +/* + * 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.outputformat.impl; + +import java.io.IOException; +import java.io.Writer; + +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.outputformat.CommonMarkupOutputFormat; +import org.apache.freemarker.core.outputformat.OutputFormat; +import org.apache.freemarker.core.util._StringUtil; + +/** + * Represents the HTML output format (MIME type "text/html", name "HTML"). This format escapes by default (via + * {@link _StringUtil#XHTMLEnc(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 TemplateHTMLOutputModel}). + * + * @since 2.3.24 + */ +public final class HTMLOutputFormat extends CommonMarkupOutputFormat<TemplateHTMLOutputModel> { + + /** + * The only instance (singleton) of this {@link OutputFormat}. + */ + public static final HTMLOutputFormat INSTANCE = new HTMLOutputFormat(); + + private HTMLOutputFormat() { + // Only to decrease visibility + } + + @Override + public String getName() { + return "HTML"; + } + + @Override + public String getMimeType() { + return "text/html"; + } + + @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 TemplateHTMLOutputModel newTemplateMarkupOutputModel(String plainTextContent, String markupContent) { + return new TemplateHTMLOutputModel(plainTextContent, markupContent); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/JSONOutputFormat.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/JSONOutputFormat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/JSONOutputFormat.java new file mode 100644 index 0000000..c420e69 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/JSONOutputFormat.java @@ -0,0 +1,54 @@ +/* + * 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.outputformat.impl; + +import org.apache.freemarker.core.outputformat.OutputFormat; + +/** + * Represents the JSON output format (MIME type "application/json", name "JSON"). This format doesn't support escaping. + * + * @since 2.3.24 + */ +public class JSONOutputFormat extends OutputFormat { + + /** + * The only instance (singleton) of this {@link OutputFormat}. + */ + public static final JSONOutputFormat INSTANCE = new JSONOutputFormat(); + + private JSONOutputFormat() { + // Only to decrease visibility + } + + @Override + public String getName() { + return "JSON"; + } + + @Override + public String getMimeType() { + return "application/json"; + } + + @Override + public boolean isOutputFormatMixingAllowed() { + return false; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/JavaScriptOutputFormat.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/JavaScriptOutputFormat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/JavaScriptOutputFormat.java new file mode 100644 index 0000000..b2e8176 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/JavaScriptOutputFormat.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.outputformat.impl; + +import org.apache.freemarker.core.outputformat.OutputFormat; + +/** + * Represents the JavaScript output format (MIME type "application/javascript", name "JavaScript"). This format doesn't + * support escaping. + * + * @since 2.3.24 + */ +public class JavaScriptOutputFormat extends OutputFormat { + + /** + * The only instance (singleton) of this {@link OutputFormat}. + */ + public static final JavaScriptOutputFormat INSTANCE = new JavaScriptOutputFormat(); + + private JavaScriptOutputFormat() { + // Only to decrease visibility + } + + @Override + public String getName() { + return "JavaScript"; + } + + @Override + public String getMimeType() { + return "application/javascript"; + } + + @Override + public boolean isOutputFormatMixingAllowed() { + return false; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/PlainTextOutputFormat.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/PlainTextOutputFormat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/PlainTextOutputFormat.java new file mode 100644 index 0000000..13cddc8 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/PlainTextOutputFormat.java @@ -0,0 +1,58 @@ +/* + * 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.outputformat.impl; + +import org.apache.freemarker.core.outputformat.OutputFormat; + +/** + * Represents the plain text output format (MIME type "text/plain", name "plainText"). This format doesn't support + * escaping. This format doesn't allow mixing in template output values of other output formats. + * + * <p> + * The main difference from {@link UndefinedOutputFormat} is that this format doesn't allow inserting values of another + * output format into itself (unless they can be converted to plain text), while {@link UndefinedOutputFormat} would + * just insert the foreign "markup" as is. Also, this format has {"text/plain"} MIME type, while + * {@link UndefinedOutputFormat} has {@code null}. + * + * @since 2.3.24 + */ +public final class PlainTextOutputFormat extends OutputFormat { + + public static final PlainTextOutputFormat INSTANCE = new PlainTextOutputFormat(); + + private PlainTextOutputFormat() { + // Only to decrease visibility + } + + @Override + public boolean isOutputFormatMixingAllowed() { + return false; + } + + @Override + public String getName() { + return "plainText"; + } + + @Override + public String getMimeType() { + return "text/plain"; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/RTFOutputFormat.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/RTFOutputFormat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/RTFOutputFormat.java new file mode 100644 index 0000000..be38b89 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/RTFOutputFormat.java @@ -0,0 +1,77 @@ +/* + * 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.outputformat.impl; + +import java.io.IOException; +import java.io.Writer; + +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.outputformat.CommonMarkupOutputFormat; +import org.apache.freemarker.core.outputformat.OutputFormat; +import org.apache.freemarker.core.util._StringUtil; + +/** + * Represents the Rich Text Format output format (MIME type "application/rtf", name "RTF"). This format escapes by + * default (via {@link _StringUtil#RTFEnc(String)}). The {@code ?rtf} built-in silently bypasses template output values + * of the type produced by this output format ({@link TemplateRTFOutputModel}). + * + * @since 2.3.24 + */ +public final class RTFOutputFormat extends CommonMarkupOutputFormat<TemplateRTFOutputModel> { + + /** + * The only instance (singleton) of this {@link OutputFormat}. + */ + public static final RTFOutputFormat INSTANCE = new RTFOutputFormat(); + + private RTFOutputFormat() { + // Only to decrease visibility + } + + @Override + public String getName() { + return "RTF"; + } + + @Override + public String getMimeType() { + return "application/rtf"; + } + + @Override + public void output(String textToEsc, Writer out) throws IOException, TemplateModelException { + _StringUtil.RTFEnc(textToEsc, out); + } + + @Override + public String escapePlainText(String plainTextContent) { + return _StringUtil.RTFEnc(plainTextContent); + } + + @Override + public boolean isLegacyBuiltInBypassed(String builtInName) { + return builtInName.equals("rtf"); + } + + @Override + protected TemplateRTFOutputModel newTemplateMarkupOutputModel(String plainTextContent, String markupContent) { + return new TemplateRTFOutputModel(plainTextContent, markupContent); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateCombinedMarkupOutputModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateCombinedMarkupOutputModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateCombinedMarkupOutputModel.java new file mode 100644 index 0000000..345a197 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateCombinedMarkupOutputModel.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.outputformat.impl; + +import org.apache.freemarker.core.outputformat.CommonTemplateMarkupOutputModel; + +/** + * Stores combined markup to be printed; used with {@link CombinedMarkupOutputFormat}. + * + * @since 2.3.24 + */ +public final class TemplateCombinedMarkupOutputModel + extends CommonTemplateMarkupOutputModel<TemplateCombinedMarkupOutputModel> { + + private final CombinedMarkupOutputFormat outputFormat; + + /** + * See {@link CommonTemplateMarkupOutputModel#CommonTemplateMarkupOutputModel(String, String)}. + * + * @param outputFormat + * The {@link CombinedMarkupOutputFormat} format this value is bound to. Because + * {@link CombinedMarkupOutputFormat} has no singleton, we have to pass it in, unlike with most other + * {@link CommonTemplateMarkupOutputModel}-s. + */ + TemplateCombinedMarkupOutputModel(String plainTextContent, String markupContent, + CombinedMarkupOutputFormat outputFormat) { + super(plainTextContent, markupContent); + this.outputFormat = outputFormat; + } + + @Override + public CombinedMarkupOutputFormat getOutputFormat() { + return outputFormat; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateHTMLOutputModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateHTMLOutputModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateHTMLOutputModel.java new file mode 100644 index 0000000..7bff952 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateHTMLOutputModel.java @@ -0,0 +1,42 @@ +/* + * 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.outputformat.impl; + +import org.apache.freemarker.core.outputformat.CommonTemplateMarkupOutputModel; + +/** + * Stores HTML markup to be printed; used with {@link HTMLOutputFormat}. + * + * @since 2.3.24 + */ +public final class TemplateHTMLOutputModel extends CommonTemplateMarkupOutputModel<TemplateHTMLOutputModel> { + + /** + * See {@link CommonTemplateMarkupOutputModel#CommonTemplateMarkupOutputModel(String, String)}. + */ + TemplateHTMLOutputModel(String plainTextContent, String markupContent) { + super(plainTextContent, markupContent); + } + + @Override + public HTMLOutputFormat getOutputFormat() { + return HTMLOutputFormat.INSTANCE; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateRTFOutputModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateRTFOutputModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateRTFOutputModel.java new file mode 100644 index 0000000..f01ff07 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateRTFOutputModel.java @@ -0,0 +1,42 @@ +/* + * 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.outputformat.impl; + +import org.apache.freemarker.core.outputformat.CommonTemplateMarkupOutputModel; + +/** + * Stores RTF markup to be printed; used with {@link RTFOutputFormat}. + * + * @since 2.3.24 + */ +public final class TemplateRTFOutputModel extends CommonTemplateMarkupOutputModel<TemplateRTFOutputModel> { + + /** + * See {@link CommonTemplateMarkupOutputModel#CommonTemplateMarkupOutputModel(String, String)}. + */ + TemplateRTFOutputModel(String plainTextContent, String markupContent) { + super(plainTextContent, markupContent); + } + + @Override + public RTFOutputFormat getOutputFormat() { + return RTFOutputFormat.INSTANCE; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateXHTMLOutputModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateXHTMLOutputModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateXHTMLOutputModel.java new file mode 100644 index 0000000..f0fbf1d --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateXHTMLOutputModel.java @@ -0,0 +1,42 @@ +/* + * 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.outputformat.impl; + +import org.apache.freemarker.core.outputformat.CommonTemplateMarkupOutputModel; + +/** + * 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/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateXMLOutputModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateXMLOutputModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateXMLOutputModel.java new file mode 100644 index 0000000..62e7867 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/TemplateXMLOutputModel.java @@ -0,0 +1,42 @@ +/* + * 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.outputformat.impl; + +import org.apache.freemarker.core.outputformat.CommonTemplateMarkupOutputModel; + +/** + * 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/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/UndefinedOutputFormat.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/UndefinedOutputFormat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/UndefinedOutputFormat.java new file mode 100644 index 0000000..b5412e2 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/UndefinedOutputFormat.java @@ -0,0 +1,58 @@ +/* + * 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.outputformat.impl; + +import org.apache.freemarker.core.Configuration; +import org.apache.freemarker.core.model.TemplateMarkupOutputModel; +import org.apache.freemarker.core.outputformat.OutputFormat; + +/** + * 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#getTemplateConfigurations()}). This format + * doesn't support auto-escaping ({@link Configuration#getAutoEscapingPolicy()}). 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/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/XHTMLOutputFormat.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/XHTMLOutputFormat.java b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/XHTMLOutputFormat.java new file mode 100644 index 0000000..4334ba3 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/outputformat/impl/XHTMLOutputFormat.java @@ -0,0 +1,77 @@ +/* + * 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.outputformat.impl; + +import java.io.IOException; +import java.io.Writer; + +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.outputformat.CommonMarkupOutputFormat; +import org.apache.freemarker.core.outputformat.OutputFormat; +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); + } + +}
