http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/JavaTemplateDateFormat.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/JavaTemplateDateFormat.java b/src/main/java/org/apache/freemarker/core/JavaTemplateDateFormat.java new file mode 100644 index 0000000..949cc9e --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/JavaTemplateDateFormat.java @@ -0,0 +1,72 @@ +/* + * 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.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +import org.apache.freemarker.core.model.TemplateDateModel; +import org.apache.freemarker.core.model.TemplateModelException; + +/** + * Java {@link DateFormat}-based format. + */ +class JavaTemplateDateFormat extends TemplateDateFormat { + + private final DateFormat javaDateFormat; + + public JavaTemplateDateFormat(DateFormat javaDateFormat) { + this.javaDateFormat = javaDateFormat; + } + + @Override + public String formatToPlainText(TemplateDateModel dateModel) throws TemplateModelException { + return javaDateFormat.format(TemplateFormatUtil.getNonNullDate(dateModel)); + } + + @Override + public Date parse(String s, int dateType) throws UnparsableValueException { + try { + return javaDateFormat.parse(s); + } catch (ParseException e) { + throw new UnparsableValueException(e.getMessage(), e); + } + } + + @Override + public String getDescription() { + return javaDateFormat instanceof SimpleDateFormat + ? ((SimpleDateFormat) javaDateFormat).toPattern() + : javaDateFormat.toString(); + } + + @Override + public boolean isLocaleBound() { + return true; + } + + @Override + public boolean isTimeZoneBound() { + return true; + } + +}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/JavaTemplateDateFormatFactory.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/JavaTemplateDateFormatFactory.java b/src/main/java/org/apache/freemarker/core/JavaTemplateDateFormatFactory.java new file mode 100644 index 0000000..331a0ca --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/JavaTemplateDateFormatFactory.java @@ -0,0 +1,173 @@ +/* + * 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.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Locale; +import java.util.StringTokenizer; +import java.util.TimeZone; +import java.util.concurrent.ConcurrentHashMap; + +import org.apache.freemarker.core.model.TemplateDateModel; +import org.slf4j.Logger; + +class JavaTemplateDateFormatFactory extends TemplateDateFormatFactory { + + static final JavaTemplateDateFormatFactory INSTANCE = new JavaTemplateDateFormatFactory(); + + private static final Logger LOG = _CoreLogs.RUNTIME; + + private static final ConcurrentHashMap<CacheKey, DateFormat> GLOBAL_FORMAT_CACHE + = new ConcurrentHashMap<>(); + private static final int LEAK_ALERT_NUMBER_FORMAT_CACHE_SIZE = 1024; + + private JavaTemplateDateFormatFactory() { + // Can't be instantiated + } + + /** + * @param zonelessInput + * Has no effect in this implementation. + */ + @Override + public TemplateDateFormat get(String params, int dateType, Locale locale, TimeZone timeZone, boolean zonelessInput, + Environment env) throws UnknownDateTypeFormattingUnsupportedException, InvalidFormatParametersException { + return new JavaTemplateDateFormat(getJavaDateFormat(dateType, params, locale, timeZone)); + } + + /** + * Returns a "private" copy (not in the global cache) for the given format. + */ + private DateFormat getJavaDateFormat(int dateType, String nameOrPattern, Locale locale, TimeZone timeZone) + throws UnknownDateTypeFormattingUnsupportedException, InvalidFormatParametersException { + + // Get DateFormat from global cache: + CacheKey cacheKey = new CacheKey(dateType, nameOrPattern, locale, timeZone); + DateFormat jFormat; + + jFormat = GLOBAL_FORMAT_CACHE.get(cacheKey); + if (jFormat == null) { + // Add format to global format cache. + StringTokenizer tok = new StringTokenizer(nameOrPattern, "_"); + int tok1Style = tok.hasMoreTokens() ? parseDateStyleToken(tok.nextToken()) : DateFormat.DEFAULT; + if (tok1Style != -1) { + switch (dateType) { + case TemplateDateModel.UNKNOWN: { + throw new UnknownDateTypeFormattingUnsupportedException(); + } + case TemplateDateModel.TIME: { + jFormat = DateFormat.getTimeInstance(tok1Style, cacheKey.locale); + break; + } + case TemplateDateModel.DATE: { + jFormat = DateFormat.getDateInstance(tok1Style, cacheKey.locale); + break; + } + case TemplateDateModel.DATETIME: { + int tok2Style = tok.hasMoreTokens() ? parseDateStyleToken(tok.nextToken()) : tok1Style; + if (tok2Style != -1) { + jFormat = DateFormat.getDateTimeInstance(tok1Style, tok2Style, cacheKey.locale); + } + break; + } + } + } + if (jFormat == null) { + try { + jFormat = new SimpleDateFormat(nameOrPattern, cacheKey.locale); + } catch (IllegalArgumentException e) { + final String msg = e.getMessage(); + throw new InvalidFormatParametersException( + msg != null ? msg : "Invalid SimpleDateFormat pattern", e); + } + } + jFormat.setTimeZone(cacheKey.timeZone); + + if (GLOBAL_FORMAT_CACHE.size() >= LEAK_ALERT_NUMBER_FORMAT_CACHE_SIZE) { + boolean triggered = false; + synchronized (JavaTemplateNumberFormatFactory.class) { + if (GLOBAL_FORMAT_CACHE.size() >= LEAK_ALERT_NUMBER_FORMAT_CACHE_SIZE) { + triggered = true; + GLOBAL_FORMAT_CACHE.clear(); + } + } + if (triggered) { + LOG.warn("Global Java DateFormat cache has exceeded {} entries => cache flushed. " + + "Typical cause: Some template generates high variety of format pattern strings.", + LEAK_ALERT_NUMBER_FORMAT_CACHE_SIZE); + } + } + + DateFormat prevJFormat = GLOBAL_FORMAT_CACHE.putIfAbsent(cacheKey, jFormat); + if (prevJFormat != null) { + jFormat = prevJFormat; + } + } // if cache miss + + return (DateFormat) jFormat.clone(); // For thread safety + } + + private static final class CacheKey { + private final int dateType; + private final String pattern; + private final Locale locale; + private final TimeZone timeZone; + + CacheKey(int dateType, String pattern, Locale locale, TimeZone timeZone) { + this.dateType = dateType; + this.pattern = pattern; + this.locale = locale; + this.timeZone = timeZone; + } + + @Override + public boolean equals(Object o) { + if (o instanceof CacheKey) { + CacheKey fk = (CacheKey) o; + return dateType == fk.dateType && fk.pattern.equals(pattern) && fk.locale.equals(locale) + && fk.timeZone.equals(timeZone); + } + return false; + } + + @Override + public int hashCode() { + return dateType ^ pattern.hashCode() ^ locale.hashCode() ^ timeZone.hashCode(); + } + } + + private int parseDateStyleToken(String token) { + if ("short".equals(token)) { + return DateFormat.SHORT; + } + if ("medium".equals(token)) { + return DateFormat.MEDIUM; + } + if ("long".equals(token)) { + return DateFormat.LONG; + } + if ("full".equals(token)) { + return DateFormat.FULL; + } + return -1; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/JavaTemplateNumberFormat.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/JavaTemplateNumberFormat.java b/src/main/java/org/apache/freemarker/core/JavaTemplateNumberFormat.java new file mode 100644 index 0000000..9e37c6f --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/JavaTemplateNumberFormat.java @@ -0,0 +1,66 @@ +/* + * 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.text.NumberFormat; + +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.model.TemplateNumberModel; + +final class JavaTemplateNumberFormat extends BackwardCompatibleTemplateNumberFormat { + + private final String formatString; + private final NumberFormat javaNumberFormat; + + public JavaTemplateNumberFormat(NumberFormat javaNumberFormat, String formatString) { + this.formatString = formatString; + this.javaNumberFormat = javaNumberFormat; + } + + @Override + public String formatToPlainText(TemplateNumberModel numberModel) throws UnformattableValueException, TemplateModelException { + Number number = TemplateFormatUtil.getNonNullNumber(numberModel); + return format(number); + } + + @Override + public boolean isLocaleBound() { + return true; + } + + @Override + String format(Number number) throws UnformattableValueException { + try { + return javaNumberFormat.format(number); + } catch (ArithmeticException e) { + throw new UnformattableValueException( + "This format can't format the " + number + " number. Reason: " + e.getMessage(), e); + } + } + + public NumberFormat getJavaNumberFormat() { + return javaNumberFormat; + } + + @Override + public String getDescription() { + return formatString; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/JavaTemplateNumberFormatFactory.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/JavaTemplateNumberFormatFactory.java b/src/main/java/org/apache/freemarker/core/JavaTemplateNumberFormatFactory.java new file mode 100644 index 0000000..e73e2ec --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/JavaTemplateNumberFormatFactory.java @@ -0,0 +1,120 @@ +/* + * 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.text.NumberFormat; +import java.text.ParseException; +import java.util.Locale; +import java.util.concurrent.ConcurrentHashMap; + +import org.slf4j.Logger; + +/** + * Deals with {@link TemplateNumberFormat}-s that just wrap a Java {@link NumberFormat}. + */ +class JavaTemplateNumberFormatFactory extends TemplateNumberFormatFactory { + + static final JavaTemplateNumberFormatFactory INSTANCE = new JavaTemplateNumberFormatFactory(); + + private static final Logger LOG = _CoreLogs.RUNTIME; + + private static final ConcurrentHashMap<CacheKey, NumberFormat> GLOBAL_FORMAT_CACHE + = new ConcurrentHashMap<>(); + private static final int LEAK_ALERT_NUMBER_FORMAT_CACHE_SIZE = 1024; + + private JavaTemplateNumberFormatFactory() { + // Not meant to be instantiated + } + + @Override + public TemplateNumberFormat get(String params, Locale locale, Environment env) + throws InvalidFormatParametersException { + CacheKey cacheKey = new CacheKey(params, locale); + NumberFormat jFormat = GLOBAL_FORMAT_CACHE.get(cacheKey); + if (jFormat == null) { + if ("number".equals(params)) { + jFormat = NumberFormat.getNumberInstance(locale); + } else if ("currency".equals(params)) { + jFormat = NumberFormat.getCurrencyInstance(locale); + } else if ("percent".equals(params)) { + jFormat = NumberFormat.getPercentInstance(locale); + } else if ("computer".equals(params)) { + jFormat = env.getCNumberFormat(); + } else { + try { + jFormat = ExtendedDecimalFormatParser.parse(params, locale); + } catch (ParseException e) { + String msg = e.getMessage(); + throw new InvalidFormatParametersException( + msg != null ? msg : "Invalid DecimalFormat pattern", e); + } + } + + if (GLOBAL_FORMAT_CACHE.size() >= LEAK_ALERT_NUMBER_FORMAT_CACHE_SIZE) { + boolean triggered = false; + synchronized (JavaTemplateNumberFormatFactory.class) { + if (GLOBAL_FORMAT_CACHE.size() >= LEAK_ALERT_NUMBER_FORMAT_CACHE_SIZE) { + triggered = true; + GLOBAL_FORMAT_CACHE.clear(); + } + } + if (triggered) { + LOG.warn("Global Java NumberFormat cache has exceeded {} entries => cache flushed. " + + "Typical cause: Some template generates high variety of format pattern strings.", + LEAK_ALERT_NUMBER_FORMAT_CACHE_SIZE); + } + } + + NumberFormat prevJFormat = GLOBAL_FORMAT_CACHE.putIfAbsent(cacheKey, jFormat); + if (prevJFormat != null) { + jFormat = prevJFormat; + } + } // if cache miss + + // JFormat-s aren't thread-safe; must clone it + jFormat = (NumberFormat) jFormat.clone(); + + return new JavaTemplateNumberFormat(jFormat, params); + } + + private static final class CacheKey { + private final String pattern; + private final Locale locale; + + CacheKey(String pattern, Locale locale) { + this.pattern = pattern; + this.locale = locale; + } + + @Override + public boolean equals(Object o) { + if (o instanceof CacheKey) { + CacheKey fk = (CacheKey) o; + return fk.pattern.equals(pattern) && fk.locale.equals(locale); + } + return false; + } + + @Override + public int hashCode() { + return pattern.hashCode() ^ locale.hashCode(); + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/ListableRightUnboundedRangeModel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/ListableRightUnboundedRangeModel.java b/src/main/java/org/apache/freemarker/core/ListableRightUnboundedRangeModel.java new file mode 100644 index 0000000..2130318 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/ListableRightUnboundedRangeModel.java @@ -0,0 +1,97 @@ +/* + * 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.math.BigInteger; + +import org.apache.freemarker.core.model.TemplateCollectionModel; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.model.TemplateModelIterator; +import org.apache.freemarker.core.model.impl.SimpleNumber; + +/** + * This is the model used for right-unbounded ranges since Incompatible Improvements 2.3.21. + * + * @since 2.3.21 + */ +final class ListableRightUnboundedRangeModel extends RightUnboundedRangeModel implements TemplateCollectionModel { + + ListableRightUnboundedRangeModel(int begin) { + super(begin); + } + + @Override + public int size() throws TemplateModelException { + return Integer.MAX_VALUE; + } + + @Override + public TemplateModelIterator iterator() throws TemplateModelException { + return new TemplateModelIterator() { + boolean needInc; + int nextType = 1; + int nextInt = getBegining(); + long nextLong; + BigInteger nextBigInteger; + + @Override + public TemplateModel next() throws TemplateModelException { + if (needInc) { + switch (nextType) { + case 1: + if (nextInt < Integer.MAX_VALUE) { + nextInt++; + } else { + nextType = 2; + nextLong = nextInt + 1L; + } + break; + + case 2: + if (nextLong < Long.MAX_VALUE) { + nextLong++; + } else { + nextType = 3; + nextBigInteger = BigInteger.valueOf(nextLong); + nextBigInteger = nextBigInteger.add(BigInteger.ONE); + } + break; + + default: // 3 + nextBigInteger = nextBigInteger.add(BigInteger.ONE); + } + } + needInc = true; + return nextType == 1 ? new SimpleNumber(nextInt) + : (nextType == 2 ? new SimpleNumber(nextLong) + : new SimpleNumber(nextBigInteger)); + } + + @Override + public boolean hasNext() throws TemplateModelException { + return true; + } + + }; + + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/LocalContext.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/LocalContext.java b/src/main/java/org/apache/freemarker/core/LocalContext.java new file mode 100644 index 0000000..b8717b7 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/LocalContext.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 java.util.Collection; + +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelException; + +/** + * An interface that represents a local context. This is used as the abstraction for + * the context of a ASTDirMacro invocation, a loop, or the nested block call from within + * a macro. + */ +public interface LocalContext { + TemplateModel getLocalVariable(String name) throws TemplateModelException; + Collection getLocalVariableNames() throws TemplateModelException; + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/LocalContextStack.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/LocalContextStack.java b/src/main/java/org/apache/freemarker/core/LocalContextStack.java new file mode 100644 index 0000000..26aacf3 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/LocalContextStack.java @@ -0,0 +1,57 @@ +/* + * 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; + +/** + * Class that's a little bit more efficient than using an {@code ArrayList<LocalContext>}. + * + * @since 2.3.24 + */ +final class LocalContextStack { + + private LocalContext[] buffer = new LocalContext[8]; + private int size; + + void push(LocalContext localContext) { + final int newSize = ++size; + LocalContext[] buffer = this.buffer; + if (buffer.length < newSize) { + final LocalContext[] newBuffer = new LocalContext[newSize * 2]; + for (int i = 0; i < buffer.length; i++) { + newBuffer[i] = buffer[i]; + } + buffer = newBuffer; + this.buffer = newBuffer; + } + buffer[newSize - 1] = localContext; + } + + void pop() { + buffer[--size] = null; + } + + public LocalContext get(int index) { + return buffer[index]; + } + + public int size() { + return size; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/MarkReleaserTemplateSpecifiedEncodingHandler.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/MarkReleaserTemplateSpecifiedEncodingHandler.java b/src/main/java/org/apache/freemarker/core/MarkReleaserTemplateSpecifiedEncodingHandler.java new file mode 100644 index 0000000..555bbf3 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/MarkReleaserTemplateSpecifiedEncodingHandler.java @@ -0,0 +1,56 @@ +/* + * 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.InputStream; + +import org.apache.freemarker.core.Template.WrongEncodingException; + +/** + * A {@link TemplateSpecifiedEncodingHandler} that discards the mark of the specified {@link InputStream} when + * the template parsing gets to a point where it's known that we can't receive a template specified encoding anymore. + * This allows freeing up the mark buffer early during parsing. + * + * @since 2.3.16 + */ +public class MarkReleaserTemplateSpecifiedEncodingHandler implements TemplateSpecifiedEncodingHandler { + + private final InputStream markedInputStream; + + /** + * @param markedInputStream Input stream with marked template content start position + */ + public MarkReleaserTemplateSpecifiedEncodingHandler(InputStream markedInputStream) { + this.markedInputStream = markedInputStream; + } + + @Override + public void handle(String templateSpecificEncoding, String constructorSpecifiedEncoding) + throws WrongEncodingException { + TemplateSpecifiedEncodingHandler.DEFAULT.handle(templateSpecificEncoding, constructorSpecifiedEncoding); + + // There was no WrongEncodingException exception, release mark buffer: + markedInputStream.mark(0); // + } + + public InputStream getMarkedInputStream() { + return markedInputStream; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/MarkupOutputFormat.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/MarkupOutputFormat.java b/src/main/java/org/apache/freemarker/core/MarkupOutputFormat.java new file mode 100644 index 0000000..5966f6f --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/MarkupOutputFormat.java @@ -0,0 +1,131 @@ +/* + * 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; + +/** + * 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: + * {@link Configuration#setOutputFormat(OutputFormat)}.) + * + * <p> + * An {@link OutputFormat} 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#setAutoEscapingPolicy(int) + */ + public abstract boolean isAutoEscapedByDefault(); + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/MarkupOutputFormatBoundBuiltIn.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/MarkupOutputFormatBoundBuiltIn.java b/src/main/java/org/apache/freemarker/core/MarkupOutputFormatBoundBuiltIn.java new file mode 100644 index 0000000..b97663f --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/MarkupOutputFormatBoundBuiltIn.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.freemarker.core; + +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.util._NullArgumentException; + +abstract class MarkupOutputFormatBoundBuiltIn extends SpecialBuiltIn { + + protected MarkupOutputFormat outputFormat; + + void bindToMarkupOutputFormat(MarkupOutputFormat outputFormat) { + _NullArgumentException.check(outputFormat); + this.outputFormat = outputFormat; + } + + @Override + TemplateModel _eval(Environment env) throws TemplateException { + if (outputFormat == null) { + // The parser should prevent this situation + throw new NullPointerException("outputFormat was null"); + } + return calculateResult(env); + } + + protected abstract TemplateModel calculateResult(Environment env) + throws TemplateException; + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/MessageUtil.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/MessageUtil.java b/src/main/java/org/apache/freemarker/core/MessageUtil.java new file mode 100644 index 0000000..6787411 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/MessageUtil.java @@ -0,0 +1,350 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.freemarker.core; + +import java.util.ArrayList; + +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelException; +import org.apache.freemarker.core.util._StringUtil; + +/** + * Utilities for creating error messages (and other messages). + */ +class MessageUtil { + + static final String UNKNOWN_DATE_TO_STRING_ERROR_MESSAGE + = "Can't convert the date-like value to string because it isn't " + + "known if it's a date (no time part), time or date-time value."; + + static final String UNKNOWN_DATE_PARSING_ERROR_MESSAGE + = "Can't parse the string to date-like value because it isn't " + + "known if it's desired result should be a date (no time part), a time, or a date-time value."; + + static final String UNKNOWN_DATE_TYPE_ERROR_TIP = + "Use ?date, ?time, or ?datetime to tell FreeMarker the exact type."; + + static final Object[] UNKNOWN_DATE_TO_STRING_TIPS = { + UNKNOWN_DATE_TYPE_ERROR_TIP, + "If you need a particular format only once, use ?string(pattern), like ?string('dd.MM.yyyy HH:mm:ss'), " + + "to specify which fields to display. " + }; + + static final String EMBEDDED_MESSAGE_BEGIN = "---begin-message---\n"; + + static final String EMBEDDED_MESSAGE_END = "\n---end-message---"; + + // Can't be instantiated + private MessageUtil() { } + + static String formatLocationForSimpleParsingError(Template template, int line, int column) { + return formatLocation("in", template, line, column); + } + + static String formatLocationForSimpleParsingError(String templateSourceName, int line, int column) { + return formatLocation("in", templateSourceName, line, column); + } + + static String formatLocationForDependentParsingError(Template template, int line, int column) { + return formatLocation("on", template, line, column); + } + + static String formatLocationForDependentParsingError(String templateSourceName, int line, int column) { + return formatLocation("on", templateSourceName, line, column); + } + + static String formatLocationForEvaluationError(Template template, int line, int column) { + return formatLocation("at", template, line, column); + } + + static String formatLocationForEvaluationError(ASTDirMacro macro, int line, int column) { + Template t = macro.getTemplate(); + return formatLocation("at", t != null ? t.getSourceName() : null, macro.getName(), macro.isFunction(), line, column); + } + + static String formatLocationForEvaluationError(String templateSourceName, int line, int column) { + return formatLocation("at", templateSourceName, line, column); + } + + private static String formatLocation(String preposition, Template template, int line, int column) { + return formatLocation(preposition, template != null ? template.getSourceName() : null, line, column); + } + + private static String formatLocation(String preposition, String templateSourceName, int line, int column) { + return formatLocation( + preposition, templateSourceName, + null, false, + line, column); + } + + private static String formatLocation( + String preposition, String templateSourceName, + String macroOrFuncName, boolean isFunction, + int line, int column) { + String templateDesc; + if (line < 0) { + templateDesc = "?eval-ed string"; + macroOrFuncName = null; + } else { + templateDesc = templateSourceName != null + ? "template " + _StringUtil.jQuoteNoXSS(templateSourceName) + : "nameless template"; + } + return "in " + templateDesc + + (macroOrFuncName != null + ? " in " + (isFunction ? "function " : "macro ") + _StringUtil.jQuote(macroOrFuncName) + : "") + + " " + + preposition + " " + formatPosition(line, column); + } + + static String formatPosition(int line, int column) { + return "line " + (line >= 0 ? line : line - (ASTNode.RUNTIME_EVAL_LINE_DISPLACEMENT - 1)) + + ", column " + column; + } + + /** + * Returns a single line string that is no longer than {@code maxLength}. + * If will truncate the string at line-breaks too. + * The truncation is always signaled with a a {@code "..."} at the end of the result string. + */ + static String shorten(String s, int maxLength) { + if (maxLength < 5) maxLength = 5; + + boolean isTruncated = false; + + int brIdx = s.indexOf('\n'); + if (brIdx != -1) { + s = s.substring(0, brIdx); + isTruncated = true; + }; + brIdx = s.indexOf('\r'); + if (brIdx != -1) { + s = s.substring(0, brIdx); + isTruncated = true; + } + + if (s.length() > maxLength) { + s = s.substring(0, maxLength - 3); + isTruncated = true; + } + + if (!isTruncated) { + return s; + } else { + if (s.endsWith(".")) { + if (s.endsWith("..")) { + if (s.endsWith("...")) { + return s; + } else { + return s + "."; + } + } else { + return s + ".."; + } + } else { + return s + "..."; + } + } + } + + static StringBuilder appendExpressionAsUntearable(StringBuilder sb, ASTExpression argExp) { + boolean needParen = + !(argExp instanceof ASTExpNumberLiteral) + && !(argExp instanceof ASTExpStringLiteral) + && !(argExp instanceof ASTExpBooleanLiteral) + && !(argExp instanceof ASTExpListLiteral) + && !(argExp instanceof ASTExpHashLiteral) + && !(argExp instanceof ASTExpVariable) + && !(argExp instanceof ASTExpDot) + && !(argExp instanceof ASTExpDynamicKeyName) + && !(argExp instanceof ASTExpMethodCall) + && !(argExp instanceof ASTExpBuiltIn); + if (needParen) sb.append('('); + sb.append(argExp.getCanonicalForm()); + if (needParen) sb.append(')'); + return sb; + } + + static TemplateModelException newArgCntError(String methodName, int argCnt, int expectedCnt) { + return newArgCntError(methodName, argCnt, expectedCnt, expectedCnt); + } + + static TemplateModelException newArgCntError(String methodName, int argCnt, int minCnt, int maxCnt) { + ArrayList/*<Object>*/ desc = new ArrayList(20); + + desc.add(methodName); + + desc.add("("); + if (maxCnt != 0) desc.add("..."); + desc.add(") expects "); + + if (minCnt == maxCnt) { + if (maxCnt == 0) { + desc.add("no"); + } else { + desc.add(Integer.valueOf(maxCnt)); + } + } else if (maxCnt - minCnt == 1) { + desc.add(Integer.valueOf(minCnt)); + desc.add(" or "); + desc.add(Integer.valueOf(maxCnt)); + } else { + desc.add(Integer.valueOf(minCnt)); + if (maxCnt != Integer.MAX_VALUE) { + desc.add(" to "); + desc.add(Integer.valueOf(maxCnt)); + } else { + desc.add(" or more (unlimited)"); + } + } + desc.add(" argument"); + if (maxCnt > 1) desc.add("s"); + + desc.add(" but has received "); + if (argCnt == 0) { + desc.add("none"); + } else { + desc.add(Integer.valueOf(argCnt)); + } + desc.add("."); + + return new _TemplateModelException(desc.toArray()); + } + + static TemplateModelException newMethodArgMustBeStringException(String methodName, int argIdx, TemplateModel arg) { + return newMethodArgUnexpectedTypeException(methodName, argIdx, "string", arg); + } + + static TemplateModelException newMethodArgMustBeNumberException(String methodName, int argIdx, TemplateModel arg) { + return newMethodArgUnexpectedTypeException(methodName, argIdx, "number", arg); + } + + static TemplateModelException newMethodArgMustBeBooleanException(String methodName, int argIdx, TemplateModel arg) { + return newMethodArgUnexpectedTypeException(methodName, argIdx, "boolean", arg); + } + + static TemplateModelException newMethodArgMustBeExtendedHashException( + String methodName, int argIdx, TemplateModel arg) { + return newMethodArgUnexpectedTypeException(methodName, argIdx, "extended hash", arg); + } + + static TemplateModelException newMethodArgMustBeSequenceException( + String methodName, int argIdx, TemplateModel arg) { + return newMethodArgUnexpectedTypeException(methodName, argIdx, "sequence", arg); + } + + static TemplateModelException newMethodArgMustBeSequenceOrCollectionException( + String methodName, int argIdx, TemplateModel arg) { + return newMethodArgUnexpectedTypeException(methodName, argIdx, "sequence or collection", arg); + } + + static TemplateModelException newMethodArgUnexpectedTypeException( + String methodName, int argIdx, String expectedType, TemplateModel arg) { + return new _TemplateModelException( + methodName, "(...) expects ", new _DelayedAOrAn(expectedType), " as argument #", Integer.valueOf(argIdx + 1), + ", but received ", new _DelayedAOrAn(new _DelayedFTLTypeDescription(arg)), "."); + } + + /** + * The type of the argument was good, but it's value wasn't. + */ + static TemplateModelException newMethodArgInvalidValueException( + String methodName, int argIdx, Object... details) { + return new _TemplateModelException( + methodName, "(...) argument #", Integer.valueOf(argIdx + 1), + " had invalid value: ", details); + } + + /** + * The type of the argument was good, but the values of two or more arguments are inconsistent with each other. + */ + static TemplateModelException newMethodArgsInvalidValueException( + String methodName, Object... details) { + return new _TemplateModelException(methodName, "(...) arguments have invalid value: ", details); + } + + static TemplateException newInstantiatingClassNotAllowedException(String className, Environment env) { + return new _MiscTemplateException(env, + "Instantiating ", className, " is not allowed in the template for security reasons."); + } + + static _TemplateModelException newCantFormatUnknownTypeDateException( + ASTExpression dateSourceExpr, UnknownDateTypeFormattingUnsupportedException cause) { + return new _TemplateModelException(cause, null, new _ErrorDescriptionBuilder( + MessageUtil.UNKNOWN_DATE_TO_STRING_ERROR_MESSAGE) + .blame(dateSourceExpr) + .tips(MessageUtil.UNKNOWN_DATE_TO_STRING_TIPS)); + } + + static TemplateException newCantFormatDateException(TemplateDateFormat format, ASTExpression dataSrcExp, + TemplateValueFormatException e, boolean useTempModelExc) { + _ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder( + "Failed to format date/time/datetime with format ", new _DelayedJQuote(format.getDescription()), ": ", + e.getMessage()) + .blame(dataSrcExp); + return useTempModelExc + ? new _TemplateModelException(e, null, desc) + : new _MiscTemplateException(e, null, desc); + } + + static TemplateException newCantFormatNumberException(TemplateNumberFormat format, ASTExpression dataSrcExp, + TemplateValueFormatException e, boolean useTempModelExc) { + _ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder( + "Failed to format number with format ", new _DelayedJQuote(format.getDescription()), ": ", + e.getMessage()) + .blame(dataSrcExp); + return useTempModelExc + ? new _TemplateModelException(e, null, desc) + : new _MiscTemplateException(e, null, desc); + } + + /** + * @return "a" or "an" or "a(n)" (or "" for empty string) for an FTL type name + */ + static String getAOrAn(String s) { + if (s == null) return null; + if (s.length() == 0) return ""; + + char fc = Character.toLowerCase(s.charAt(0)); + if (fc == 'a' || fc == 'e' || fc == 'i') { + return "an"; + } else if (fc == 'h') { + String ls = s.toLowerCase(); + if (ls.startsWith("has") || ls.startsWith("hi")) { + return "a"; + } else if (ls.startsWith("ht")) { + return "an"; + } else { + return "a(n)"; + } + } else if (fc == 'u' || fc == 'o') { + return "a(n)"; + } else { + char sc = (s.length() > 1) ? s.charAt(1) : '\0'; + if (fc == 'x' && !(sc == 'a' || sc == 'e' || sc == 'i' || sc == 'a' || sc == 'o' || sc == 'u')) { + return "an"; + } else { + return "a"; + } + } + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/MiscUtil.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/MiscUtil.java b/src/main/java/org/apache/freemarker/core/MiscUtil.java new file mode 100644 index 0000000..f3ba2bc --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/MiscUtil.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; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +/** + * Utilities that didn't fit elsewhere. + */ +class MiscUtil { + + // Can't be instatiated + private MiscUtil() { } + + static final String C_FALSE = "false"; + static final String C_TRUE = "true"; + + /** + * Returns the map entries in source code order of the ASTExpression values. + */ + static List/*Map.Entry*/ sortMapOfExpressions(Map/*<?, ASTExpression>*/ map) { + ArrayList res = new ArrayList(map.entrySet()); + Collections.sort(res, + new Comparator() { // for sorting to source code order + @Override + public int compare(Object o1, Object o2) { + Map.Entry ent1 = (Map.Entry) o1; + ASTExpression exp1 = (ASTExpression) ent1.getValue(); + + Map.Entry ent2 = (Map.Entry) o2; + ASTExpression exp2 = (ASTExpression) ent2.getValue(); + + int res = exp1.beginLine - exp2.beginLine; + if (res != 0) return res; + res = exp1.beginColumn - exp2.beginColumn; + if (res != 0) return res; + + if (ent1 == ent2) return 0; + + // Should never reach this + return ((String) ent1.getKey()).compareTo((String) ent1.getKey()); + } + + }); + return res; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/NestedContentNotSupportedException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/NestedContentNotSupportedException.java b/src/main/java/org/apache/freemarker/core/NestedContentNotSupportedException.java new file mode 100644 index 0000000..26504bd --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/NestedContentNotSupportedException.java @@ -0,0 +1,67 @@ +/* + * 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.Environment.NestedElementTemplateDirectiveBody; +import org.apache.freemarker.core.ThreadInterruptionSupportTemplatePostProcessor.ASTThreadInterruptionCheck; +import org.apache.freemarker.core.model.TemplateDirectiveBody; +import org.apache.freemarker.core.util._StringUtil; + +/** + * [2.4] Should become public somehow; this is more intelligent than a {@code null} check, for example, when the body + * only contains a thread interruption check, it treats it as valid. Indicates that the directive shouldn't have + * nested content, but it had. This will probably become public when the directive/method stuff was reworked. + */ +class NestedContentNotSupportedException extends TemplateException { + + public static void check(TemplateDirectiveBody body) throws NestedContentNotSupportedException { + if (body == null) { + return; + } + if (body instanceof NestedElementTemplateDirectiveBody) { + _ASTElement[] tes = ((NestedElementTemplateDirectiveBody) body).getChildrenBuffer(); + if (tes == null || tes.length == 0 + || tes[0] instanceof ASTThreadInterruptionCheck && (tes.length == 1 || tes[1] == null)) { + return; + } + } + throw new NestedContentNotSupportedException(Environment.getCurrentEnvironment()); + } + + + private NestedContentNotSupportedException(Environment env) { + this(null, null, env); + } + + private NestedContentNotSupportedException(Exception cause, Environment env) { + this(null, cause, env); + } + + private NestedContentNotSupportedException(String description, Environment env) { + this(description, null, env); + } + + private NestedContentNotSupportedException(String description, Exception cause, Environment env) { + super( "Nested content (body) not supported." + + (description != null ? " " + _StringUtil.jQuote(description) : ""), + cause, env); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/NonBooleanException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/NonBooleanException.java b/src/main/java/org/apache/freemarker/core/NonBooleanException.java new file mode 100644 index 0000000..2627ad0 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/NonBooleanException.java @@ -0,0 +1,62 @@ +/* + * 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.TemplateBooleanModel; +import org.apache.freemarker.core.model.TemplateModel; + +/** + * Indicates that a {@link TemplateBooleanModel} value was expected, but the value had a different type. + */ +public class NonBooleanException extends UnexpectedTypeException { + + private static final Class[] EXPECTED_TYPES = new Class[] { TemplateBooleanModel.class }; + + public NonBooleanException(Environment env) { + super(env, "Expecting boolean value here"); + } + + public NonBooleanException(String description, Environment env) { + super(env, description); + } + + NonBooleanException(Environment env, _ErrorDescriptionBuilder description) { + super(env, description); + } + + NonBooleanException( + ASTExpression blamed, TemplateModel model, Environment env) + throws InvalidReferenceException { + super(blamed, model, "boolean", EXPECTED_TYPES, env); + } + + NonBooleanException( + ASTExpression blamed, TemplateModel model, String tip, + Environment env) + throws InvalidReferenceException { + super(blamed, model, "boolean", EXPECTED_TYPES, tip, env); + } + + NonBooleanException( + ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException { + super(blamed, model, "boolean", EXPECTED_TYPES, tips, env); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/NonDateException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/NonDateException.java b/src/main/java/org/apache/freemarker/core/NonDateException.java new file mode 100644 index 0000000..8014c34 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/NonDateException.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; + +import org.apache.freemarker.core.model.TemplateDateModel; +import org.apache.freemarker.core.model.TemplateModel; + +/** + * Indicates that a {@link TemplateDateModel} value was expected, but the value had a different type. + */ +public class NonDateException extends UnexpectedTypeException { + + private static final Class[] EXPECTED_TYPES = new Class[] { TemplateDateModel.class }; + + public NonDateException(Environment env) { + super(env, "Expecting date/time value here"); + } + + public NonDateException(String description, Environment env) { + super(env, description); + } + + NonDateException( + ASTExpression blamed, TemplateModel model, Environment env) + throws InvalidReferenceException { + super(blamed, model, "date/time", EXPECTED_TYPES, env); + } + + NonDateException( + ASTExpression blamed, TemplateModel model, String tip, + Environment env) + throws InvalidReferenceException { + super(blamed, model, "date/time", EXPECTED_TYPES, tip, env); + } + + NonDateException( + ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException { + super(blamed, model, "date/time", EXPECTED_TYPES, tips, env); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/NonExtendedHashException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/NonExtendedHashException.java b/src/main/java/org/apache/freemarker/core/NonExtendedHashException.java new file mode 100644 index 0000000..e50d956 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/NonExtendedHashException.java @@ -0,0 +1,62 @@ +/* + * 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.TemplateHashModelEx; +import org.apache.freemarker.core.model.TemplateModel; + +/** + * Indicates that a {@link TemplateHashModelEx} value was expected, but the value had a different type. + */ +public class NonExtendedHashException extends UnexpectedTypeException { + + private static final Class[] EXPECTED_TYPES = new Class[] { TemplateHashModelEx.class }; + + public NonExtendedHashException(Environment env) { + super(env, "Expecting extended hash value here"); + } + + public NonExtendedHashException(String description, Environment env) { + super(env, description); + } + + NonExtendedHashException(Environment env, _ErrorDescriptionBuilder description) { + super(env, description); + } + + NonExtendedHashException( + ASTExpression blamed, TemplateModel model, Environment env) + throws InvalidReferenceException { + super(blamed, model, "extended hash", EXPECTED_TYPES, env); + } + + NonExtendedHashException( + ASTExpression blamed, TemplateModel model, String tip, + Environment env) + throws InvalidReferenceException { + super(blamed, model, "extended hash", EXPECTED_TYPES, tip, env); + } + + NonExtendedHashException( + ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException { + super(blamed, model, "extended hash", EXPECTED_TYPES, tips, env); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/NonExtendedNodeException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/NonExtendedNodeException.java b/src/main/java/org/apache/freemarker/core/NonExtendedNodeException.java new file mode 100644 index 0000000..134e7b6 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/NonExtendedNodeException.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.freemarker.core; + +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateNodeModelEx; + +/** + * Indicates that a {@link TemplateNodeModelEx} value was expected, but the value had a different type. + * + * @since 2.3.26 + */ +public class NonExtendedNodeException extends UnexpectedTypeException { + + private static final Class<?>[] EXPECTED_TYPES = new Class[] { TemplateNodeModelEx.class }; + + public NonExtendedNodeException(Environment env) { + super(env, "Expecting extended node value here"); + } + + public NonExtendedNodeException(String description, Environment env) { + super(env, description); + } + + NonExtendedNodeException(Environment env, _ErrorDescriptionBuilder description) { + super(env, description); + } + + NonExtendedNodeException( + ASTExpression blamed, TemplateModel model, Environment env) + throws InvalidReferenceException { + super(blamed, model, "extended node", EXPECTED_TYPES, env); + } + + NonExtendedNodeException( + ASTExpression blamed, TemplateModel model, String tip, + Environment env) + throws InvalidReferenceException { + super(blamed, model, "extended node", EXPECTED_TYPES, tip, env); + } + + NonExtendedNodeException( + ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException { + super(blamed, model, "extended node", EXPECTED_TYPES, tips, env); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/NonHashException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/NonHashException.java b/src/main/java/org/apache/freemarker/core/NonHashException.java new file mode 100644 index 0000000..48f28fa --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/NonHashException.java @@ -0,0 +1,64 @@ +/* + * 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.TemplateHashModel; +import org.apache.freemarker.core.model.TemplateModel; + +/** + * Indicates that a {@link TemplateHashModel} value was expected, but the value had a different type. + * + * @since 2.3.21 + */ +public class NonHashException extends UnexpectedTypeException { + + private static final Class[] EXPECTED_TYPES = new Class[] { TemplateHashModel.class }; + + public NonHashException(Environment env) { + super(env, "Expecting hash value here"); + } + + public NonHashException(String description, Environment env) { + super(env, description); + } + + NonHashException(Environment env, _ErrorDescriptionBuilder description) { + super(env, description); + } + + NonHashException( + ASTExpression blamed, TemplateModel model, Environment env) + throws InvalidReferenceException { + super(blamed, model, "hash", EXPECTED_TYPES, env); + } + + NonHashException( + ASTExpression blamed, TemplateModel model, String tip, + Environment env) + throws InvalidReferenceException { + super(blamed, model, "hash", EXPECTED_TYPES, tip, env); + } + + NonHashException( + ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException { + super(blamed, model, "hash", EXPECTED_TYPES, tips, env); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/NonMarkupOutputException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/NonMarkupOutputException.java b/src/main/java/org/apache/freemarker/core/NonMarkupOutputException.java new file mode 100644 index 0000000..6f69980 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/NonMarkupOutputException.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.freemarker.core; + +import org.apache.freemarker.core.model.TemplateModel; + +/** + * Indicates that a {@link TemplateMarkupOutputModel} value was expected, but the value had a different type. + * + * @since 2.3.24 + */ +public class NonMarkupOutputException extends UnexpectedTypeException { + + private static final Class[] EXPECTED_TYPES = new Class[] { TemplateMarkupOutputModel.class }; + + public NonMarkupOutputException(Environment env) { + super(env, "Expecting markup output value here"); + } + + public NonMarkupOutputException(String description, Environment env) { + super(env, description); + } + + NonMarkupOutputException(Environment env, _ErrorDescriptionBuilder description) { + super(env, description); + } + + NonMarkupOutputException( + ASTExpression blamed, TemplateModel model, Environment env) + throws InvalidReferenceException { + super(blamed, model, "markup output", EXPECTED_TYPES, env); + } + + NonMarkupOutputException( + ASTExpression blamed, TemplateModel model, String tip, + Environment env) + throws InvalidReferenceException { + super(blamed, model, "markup output", EXPECTED_TYPES, tip, env); + } + + NonMarkupOutputException( + ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException { + super(blamed, model, "markup output", EXPECTED_TYPES, tips, env); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/NonMethodException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/NonMethodException.java b/src/main/java/org/apache/freemarker/core/NonMethodException.java new file mode 100644 index 0000000..d8f4d5a --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/NonMethodException.java @@ -0,0 +1,64 @@ +/* + * 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.TemplateMethodModel; +import org.apache.freemarker.core.model.TemplateModel; + +/** + * Indicates that a {@link TemplateMethodModel} value was expected, but the value had a different type. + * + * @since 2.3.21 + */ +public class NonMethodException extends UnexpectedTypeException { + + private static final Class[] EXPECTED_TYPES = new Class[] { TemplateMethodModel.class }; + + public NonMethodException(Environment env) { + super(env, "Expecting method value here"); + } + + public NonMethodException(String description, Environment env) { + super(env, description); + } + + NonMethodException(Environment env, _ErrorDescriptionBuilder description) { + super(env, description); + } + + NonMethodException( + ASTExpression blamed, TemplateModel model, Environment env) + throws InvalidReferenceException { + super(blamed, model, "method", EXPECTED_TYPES, env); + } + + NonMethodException( + ASTExpression blamed, TemplateModel model, String tip, + Environment env) + throws InvalidReferenceException { + super(blamed, model, "method", EXPECTED_TYPES, tip, env); + } + + NonMethodException( + ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException { + super(blamed, model, "method", EXPECTED_TYPES, tips, env); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/NonNamespaceException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/NonNamespaceException.java b/src/main/java/org/apache/freemarker/core/NonNamespaceException.java new file mode 100644 index 0000000..8834483 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/NonNamespaceException.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.freemarker.core; + +import org.apache.freemarker.core.model.TemplateModel; + +/** + * Indicates that a {@link Environment.Namespace} value was expected, but the value had a different type. + * + * @since 2.3.21 + */ +class NonNamespaceException extends UnexpectedTypeException { + + private static final Class[] EXPECTED_TYPES = new Class[] { Environment.Namespace.class }; + + public NonNamespaceException(Environment env) { + super(env, "Expecting namespace value here"); + } + + public NonNamespaceException(String description, Environment env) { + super(env, description); + } + + NonNamespaceException(Environment env, _ErrorDescriptionBuilder description) { + super(env, description); + } + + NonNamespaceException( + ASTExpression blamed, TemplateModel model, Environment env) + throws InvalidReferenceException { + super(blamed, model, "namespace", EXPECTED_TYPES, env); + } + + NonNamespaceException( + ASTExpression blamed, TemplateModel model, String tip, + Environment env) + throws InvalidReferenceException { + super(blamed, model, "namespace", EXPECTED_TYPES, tip, env); + } + + NonNamespaceException( + ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException { + super(blamed, model, "namespace", EXPECTED_TYPES, tips, env); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/NonNodeException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/NonNodeException.java b/src/main/java/org/apache/freemarker/core/NonNodeException.java new file mode 100644 index 0000000..8ef9213 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/NonNodeException.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.freemarker.core; + +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateNodeModel; + +/** + * Indicates that a {@link TemplateNodeModel} value was expected, but the value had a different type. + * + * @since 2.3.21 + */ +public class NonNodeException extends UnexpectedTypeException { + + private static final Class[] EXPECTED_TYPES = new Class[] { TemplateNodeModel.class }; + + public NonNodeException(Environment env) { + super(env, "Expecting node value here"); + } + + public NonNodeException(String description, Environment env) { + super(env, description); + } + + NonNodeException(Environment env, _ErrorDescriptionBuilder description) { + super(env, description); + } + + NonNodeException( + ASTExpression blamed, TemplateModel model, Environment env) + throws InvalidReferenceException { + super(blamed, model, "node", EXPECTED_TYPES, env); + } + + NonNodeException( + ASTExpression blamed, TemplateModel model, String tip, + Environment env) + throws InvalidReferenceException { + super(blamed, model, "node", EXPECTED_TYPES, tip, env); + } + + NonNodeException( + ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException { + super(blamed, model, "node", EXPECTED_TYPES, tips, env); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/7d784b2b/src/main/java/org/apache/freemarker/core/NonNumericalException.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/freemarker/core/NonNumericalException.java b/src/main/java/org/apache/freemarker/core/NonNumericalException.java new file mode 100644 index 0000000..cd13f05 --- /dev/null +++ b/src/main/java/org/apache/freemarker/core/NonNumericalException.java @@ -0,0 +1,74 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.freemarker.core; + +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateNumberModel; + +/** + * Indicates that a {@link TemplateNumberModel} value was expected, but the value had a different type. + */ +public class NonNumericalException extends UnexpectedTypeException { + + private static final Class[] EXPECTED_TYPES = new Class[] { TemplateNumberModel.class }; + + public NonNumericalException(Environment env) { + super(env, "Expecting numerical value here"); + } + + public NonNumericalException(String description, Environment env) { + super(env, description); + } + + NonNumericalException(_ErrorDescriptionBuilder description, Environment env) { + super(env, description); + } + + NonNumericalException( + ASTExpression blamed, TemplateModel model, Environment env) + throws InvalidReferenceException { + super(blamed, model, "number", EXPECTED_TYPES, env); + } + + NonNumericalException( + ASTExpression blamed, TemplateModel model, String tip, + Environment env) + throws InvalidReferenceException { + super(blamed, model, "number", EXPECTED_TYPES, tip, env); + } + + NonNumericalException( + ASTExpression blamed, TemplateModel model, String[] tips, Environment env) throws InvalidReferenceException { + super(blamed, model, "number", EXPECTED_TYPES, tips, env); + } + + NonNumericalException( + String assignmentTargetVarName, TemplateModel model, String[] tips, Environment env) + throws InvalidReferenceException { + super(assignmentTargetVarName, model, "number", EXPECTED_TYPES, tips, env); + } + static NonNumericalException newMalformedNumberException(ASTExpression blamed, String text, Environment env) { + return new NonNumericalException( + new _ErrorDescriptionBuilder("Can't convert this string to number: ", new _DelayedJQuote(text)) + .blame(blamed), + env); + } + +}
