http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java index e36d767..c28b385 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForSequences.java @@ -31,16 +31,16 @@ import java.util.Date; import org.apache.freemarker.core.arithmetic.ArithmeticEngine; import org.apache.freemarker.core.model.ArgumentArrayLayout; import org.apache.freemarker.core.model.TemplateBooleanModel; -import org.apache.freemarker.core.model.TemplateCollectionModel; import org.apache.freemarker.core.model.TemplateDateModel; import org.apache.freemarker.core.model.TemplateFunctionModel; import org.apache.freemarker.core.model.TemplateHashModel; +import org.apache.freemarker.core.model.TemplateIterableModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelIterator; import org.apache.freemarker.core.model.TemplateNumberModel; -import org.apache.freemarker.core.model.TemplateStringModel; import org.apache.freemarker.core.model.TemplateSequenceModel; -import org.apache.freemarker.core.model.impl.CollectionAndSequence; +import org.apache.freemarker.core.model.TemplateStringModel; +import org.apache.freemarker.core.model.impl.SequenceTemplateModelIterator; import org.apache.freemarker.core.model.impl.SimpleNumber; import org.apache.freemarker.core.model.impl.SimpleString; import org.apache.freemarker.core.model.impl.TemplateModelListSequence; @@ -48,7 +48,7 @@ import org.apache.freemarker.core.util.BugException; import org.apache.freemarker.core.util._StringUtils; /** - * A holder for builtins that operate exclusively on sequence or collection left-hand value. + * A holder for builtins that operate on sequence (or some even on iterable) left-hand value. */ class BuiltInsForSequences { @@ -66,7 +66,9 @@ class BuiltInsForSequences { public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) throws TemplateException { int chunkSize = getNumberArgument(args, 0, this).intValue(); - + if (chunkSize < 1) { + newArgumentValueException(0, "The value must be at least 1", this); + } return new ChunkedSequence(tsm, chunkSize, args[1]); } @@ -89,19 +91,16 @@ class BuiltInsForSequences { private ChunkedSequence( TemplateSequenceModel wrappedTsm, int chunkSize, TemplateModel fillerItem) throws TemplateException { - if (chunkSize < 1) { - throw new TemplateException("The 1st argument to ?', key, ' (...) must be at least 1."); - } this.wrappedTsm = wrappedTsm; this.chunkSize = chunkSize; this.fillerItem = fillerItem; - numberOfChunks = (wrappedTsm.size() + chunkSize - 1) / chunkSize; + numberOfChunks = (wrappedTsm.getCollectionSize() + chunkSize - 1) / chunkSize; } @Override public TemplateModel get(final int chunkIndex) throws TemplateException { - if (chunkIndex >= numberOfChunks) { + if (chunkIndex >= numberOfChunks || chunkIndex < 0) { return null; } @@ -110,33 +109,51 @@ class BuiltInsForSequences { private final int baseIndex = chunkIndex * chunkSize; @Override - public TemplateModel get(int relIndex) - throws TemplateException { + public TemplateModel get(int relIndex) throws TemplateException { + if (relIndex < 0) { + return null; + } int absIndex = baseIndex + relIndex; - if (absIndex < wrappedTsm.size()) { + if (absIndex < wrappedTsm.getCollectionSize()) { return wrappedTsm.get(absIndex); } else { - return absIndex < numberOfChunks * chunkSize - ? fillerItem - : null; + return absIndex < numberOfChunks * chunkSize ? fillerItem : null; } } @Override - public int size() throws TemplateException { + public int getCollectionSize() throws TemplateException { return fillerItem != null || chunkIndex + 1 < numberOfChunks ? chunkSize - : wrappedTsm.size() - baseIndex; + : wrappedTsm.getCollectionSize() - baseIndex; + } + + @Override + public boolean isEmptyCollection() throws TemplateException { + return getCollectionSize() == 0; + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new SequenceTemplateModelIterator(this); } - }; } @Override - public int size() throws TemplateException { + public int getCollectionSize() throws TemplateException { return numberOfChunks; } - + + @Override + public boolean isEmptyCollection() throws TemplateException { + return numberOfChunks == 0; + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new SequenceTemplateModelIterator(this); + } } @Override @@ -146,56 +163,27 @@ class BuiltInsForSequences { } - static class firstBI extends ASTExpBuiltIn { + static class firstBI extends BuiltInForIterable { @Override - TemplateModel _eval(Environment env) - throws TemplateException { - TemplateModel model = target.eval(env); - // In 2.3.x only, we prefer TemplateSequenceModel for - // backward compatibility. In 2.4.x, we prefer TemplateCollectionModel. - if (model instanceof TemplateSequenceModel) { - return calculateResultForSequence((TemplateSequenceModel) model); - } else if (model instanceof TemplateCollectionModel) { - return calculateResultForColletion((TemplateCollectionModel) model); - } else { - throw MessageUtils.newUnexpectedOperandTypeException( - target, model, - MessageUtils.SEQUENCE_OR_COLLECTION, - MessageUtils.EXPECTED_TYPES_SEQUENCE_OR_COLLECTION, - null, env); - } - } - - private TemplateModel calculateResultForSequence(TemplateSequenceModel seq) - throws TemplateException { - if (seq.size() == 0) { - return null; - } - return seq.get(0); - } - - private TemplateModel calculateResultForColletion(TemplateCollectionModel coll) - throws TemplateException { - TemplateModelIterator iter = coll.iterator(); + TemplateModel calculateResult(TemplateIterableModel model) throws TemplateException { + TemplateModelIterator iter = model.iterator(); if (!iter.hasNext()) { return null; } return iter.next(); } - + } - static class joinBI extends ASTExpBuiltIn { + static class joinBI extends BuiltInForIterable { - private class BIMethodForCollection extends BuiltInCallableImpl implements TemplateFunctionModel { + private class BIMethodForIterable extends BuiltInCallableImpl implements TemplateFunctionModel { - private final Environment env; - private final TemplateCollectionModel coll; + private final TemplateIterableModel iterable; - private BIMethodForCollection(Environment env, TemplateCollectionModel coll) { - this.env = env; - this.coll = coll; + private BIMethodForIterable(TemplateIterableModel iterable) { + this.iterable = iterable; } @Override @@ -207,7 +195,7 @@ class BuiltInsForSequences { StringBuilder sb = new StringBuilder(); - TemplateModelIterator it = coll.iterator(); + TemplateModelIterator it = iterable.iterator(); int idx = 0; boolean hadItem = false; @@ -247,23 +235,12 @@ class BuiltInsForSequences { } @Override - TemplateModel _eval(Environment env) throws TemplateException { - TemplateModel model = target.eval(env); - if (model instanceof TemplateCollectionModel) { - if (model instanceof RightUnboundedRangeModel) { - throw new TemplateException( - "The sequence to join was right-unbounded numerical range, thus it's infinitely long."); - } - return new BIMethodForCollection(env, (TemplateCollectionModel) model); - } else if (model instanceof TemplateSequenceModel) { - return new BIMethodForCollection(env, new CollectionAndSequence((TemplateSequenceModel) model)); - } else { - throw MessageUtils.newUnexpectedOperandTypeException( - target, model, - MessageUtils.SEQUENCE_OR_COLLECTION, - MessageUtils.EXPECTED_TYPES_SEQUENCE_OR_COLLECTION, - null, env); + TemplateModel calculateResult(TemplateIterableModel model) throws TemplateException { + if (model instanceof RightUnboundedRangeModel) { + throw new TemplateException( + "The sequence to join was right-unbounded numerical range, thus it's infinitely long."); } + return new BIMethodForIterable((TemplateIterableModel) model); } } @@ -272,10 +249,11 @@ class BuiltInsForSequences { @Override TemplateModel calculateResult(TemplateSequenceModel tsm) throws TemplateException { - if (tsm.size() == 0) { + int size = tsm.getCollectionSize(); + if (size == 0) { return null; } - return tsm.get(tsm.size() - 1); + return tsm.get(size - 1); } } @@ -289,12 +267,22 @@ class BuiltInsForSequences { @Override public TemplateModel get(int index) throws TemplateException { - return seq.get(seq.size() - 1 - index); + return seq.get(seq.getCollectionSize() - 1 - index); + } + + @Override + public int getCollectionSize() throws TemplateException { + return seq.getCollectionSize(); } @Override - public int size() throws TemplateException { - return seq.size(); + public boolean isEmptyCollection() throws TemplateException { + return seq.isEmptyCollection(); + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new SequenceTemplateModelIterator(this); } } @@ -308,25 +296,24 @@ class BuiltInsForSequences { } } - static class seq_containsBI extends ASTExpBuiltIn { - private class BIMethodForCollection extends BuiltInCallableImpl implements TemplateFunctionModel { - private TemplateCollectionModel m_coll; - private Environment m_env; + static class seq_containsBI extends BuiltInForIterable { + private class BIMethod extends BuiltInCallableImpl implements TemplateFunctionModel { + private TemplateIterableModel iterable; - private BIMethodForCollection(TemplateCollectionModel coll, Environment env) { - m_coll = coll; - m_env = env; + private BIMethod(TemplateIterableModel coll) { + iterable = coll; } @Override public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) throws TemplateException { TemplateModel arg = args[0]; - TemplateModelIterator it = m_coll.iterator(); + TemplateModelIterator it = iterable.iterator(); int idx = 0; while (it.hasNext()) { - if (modelsEqual(idx, it.next(), arg, m_env)) + if (modelsEqual(idx, it.next(), arg, env)) { return TemplateBooleanModel.TRUE; + } idx++; } return TemplateBooleanModel.FALSE; @@ -339,111 +326,38 @@ class BuiltInsForSequences { } - private class BIMethodForSequence extends BuiltInCallableImpl implements TemplateFunctionModel { - private TemplateSequenceModel m_seq; - private Environment m_env; - - private BIMethodForSequence(TemplateSequenceModel seq, Environment env) { - m_seq = seq; - m_env = env; - } - - @Override - public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) - throws TemplateException { - TemplateModel arg = args[0]; - int size = m_seq.size(); - for (int i = 0; i < size; i++) { - if (modelsEqual(i, m_seq.get(i), arg, m_env)) - return TemplateBooleanModel.TRUE; - } - return TemplateBooleanModel.FALSE; - } - - @Override - public ArgumentArrayLayout getFunctionArgumentArrayLayout() { - return ArgumentArrayLayout.SINGLE_POSITIONAL_PARAMETER; - } - - } - @Override - TemplateModel _eval(Environment env) - throws TemplateException { - TemplateModel model = target.eval(env); - // In 2.3.x only, we prefer TemplateSequenceModel for - // backward compatibility. In 2.4.x, we prefer TemplateCollectionModel. - if (model instanceof TemplateSequenceModel) { - return new BIMethodForSequence((TemplateSequenceModel) model, env); - } else if (model instanceof TemplateCollectionModel) { - return new BIMethodForCollection((TemplateCollectionModel) model, env); - } else { - throw MessageUtils.newUnexpectedOperandTypeException( - target, model, - MessageUtils.SEQUENCE_OR_COLLECTION, - MessageUtils.EXPECTED_TYPES_SEQUENCE_OR_COLLECTION, - null, env); - } + TemplateModel calculateResult(TemplateIterableModel model) throws TemplateException { + return new BIMethod((TemplateIterableModel) model); } } - static class seq_index_ofBI extends ASTExpBuiltIn { + static class seq_index_ofBI extends BuiltInForIterable { private class BIMethod extends BuiltInCallableImpl implements TemplateFunctionModel { - final TemplateSequenceModel m_seq; - final TemplateCollectionModel m_col; - final Environment m_env; + final TemplateIterableModel iterable; - private BIMethod(Environment env) + private BIMethod(TemplateIterableModel iterable) throws TemplateException { - TemplateModel model = target.eval(env); - m_seq = model instanceof TemplateSequenceModel - ? (TemplateSequenceModel) model - : null; - // [FM3] Rework the below - // In 2.3.x only, we deny the possibility of collection - // access if there's sequence access. This is so to minimize - // the change of compatibility issues; without this, objects - // that implement both the sequence and collection interfaces - // would suddenly start using the collection interface, and if - // that's buggy that would surface now, breaking the application - // that despite its bugs has worked earlier. - m_col = m_seq == null && model instanceof TemplateCollectionModel - ? (TemplateCollectionModel) model - : null; - if (m_seq == null && m_col == null) { - throw MessageUtils.newUnexpectedOperandTypeException( - target, model, - MessageUtils.SEQUENCE_OR_COLLECTION, - MessageUtils.EXPECTED_TYPES_SEQUENCE_OR_COLLECTION, - null, env); - } - - m_env = env; + this.iterable = iterable; } @Override public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) throws TemplateException { - TemplateModel target = args[0]; - Number startIndex = getOptionalNumberArgument(args, 1, this); + TemplateModel searched = args[0]; + Integer startIndex = getOptionalIntArgument(args, 1, this); int foundAtIdx; if (startIndex != null) { - // TODO [FM3] Prefer Col? - // In 2.3.x only, we prefer TemplateSequenceModel for - // backward compatibility: - foundAtIdx = m_seq != null - ? findInSeq(target, startIndex.intValue()) - : findInCol(target, startIndex.intValue()); + foundAtIdx = iterable instanceof TemplateSequenceModel + ? findInSeq(searched, (TemplateSequenceModel) iterable, startIndex, env) + : findInIter(searched, startIndex, env); } else { - // TODO [FM3] Prefer Col? - // In 2.3.x only, we prefer TemplateSequenceModel for - // backward compatibility: - foundAtIdx = m_seq != null - ? findInSeq(target) - : findInCol(target); + foundAtIdx = iterable instanceof TemplateSequenceModel + ? findInSeq(searched, (TemplateSequenceModel) iterable, env) + : findInIter(searched, env); } return foundAtIdx == -1 ? TemplateNumberModel.MINUS_ONE : new SimpleNumber(foundAtIdx); } @@ -453,37 +367,41 @@ class BuiltInsForSequences { return ArgumentArrayLayout.TWO_POSITIONAL_PARAMETERS; } - int findInCol(TemplateModel target) throws TemplateException { - return findInCol(target, 0, Integer.MAX_VALUE); + int findInIter(TemplateModel searched, Environment env) throws TemplateException { + return findInIter(searched, 0, Integer.MAX_VALUE, env); } - int findInCol(TemplateModel target, int startIndex) + int findInIter(TemplateModel searched, int startIndex, Environment env) throws TemplateException { - if (m_dir == 1) { - return findInCol(target, startIndex, Integer.MAX_VALUE); + if (findFirst) { + return findInIter(searched, startIndex, Integer.MAX_VALUE, env); } else { - return findInCol(target, 0, startIndex); + return findInIter(searched, 0, startIndex, env); } } - int findInCol(TemplateModel target, - final int allowedRangeStart, final int allowedRangeEnd) + int findInIter(TemplateModel searched, + final int allowedRangeStart, final int allowedRangeEnd, Environment env) throws TemplateException { if (allowedRangeEnd < 0) return -1; - TemplateModelIterator it = m_col.iterator(); + TemplateModelIterator it = iterable.iterator(); int foundAtIdx = -1; // -1 is the return value for "not found" int idx = 0; searchItem: while (it.hasNext()) { - if (idx > allowedRangeEnd) break searchItem; + if (idx > allowedRangeEnd) { + break searchItem; + } TemplateModel current = it.next(); if (idx >= allowedRangeStart) { - if (modelsEqual(idx, current, target, m_env)) { + if (modelsEqual(idx, current, searched, env)) { foundAtIdx = idx; - if (m_dir == 1) break searchItem; // "find first" - // Otherwise it's "find last". + // Don't stop if it's "find last". + if (findFirst) { + break searchItem; + } } } idx++; @@ -491,25 +409,26 @@ class BuiltInsForSequences { return foundAtIdx; } - int findInSeq(TemplateModel target) + int findInSeq(TemplateModel searched, TemplateSequenceModel seq, Environment env) throws TemplateException { - final int seqSize = m_seq.size(); + final int seqSize = seq.getCollectionSize(); final int actualStartIndex; - - if (m_dir == 1) { + + if (findFirst) { actualStartIndex = 0; } else { actualStartIndex = seqSize - 1; } - - return findInSeq(target, actualStartIndex, seqSize); + + return findInSeq(searched, seq, actualStartIndex, seqSize, env); } - private int findInSeq(TemplateModel target, int startIndex) + private int findInSeq( + TemplateModel searched, TemplateSequenceModel seq, int startIndex, Environment env) throws TemplateException { - int seqSize = m_seq.size(); + int seqSize = seq.getCollectionSize(); - if (m_dir == 1) { + if (findFirst) { if (startIndex >= seqSize) { return -1; } @@ -525,19 +444,19 @@ class BuiltInsForSequences { } } - return findInSeq(target, startIndex, seqSize); + return findInSeq(searched, seq, startIndex, seqSize, env); } private int findInSeq( - TemplateModel target, int scanStartIndex, int seqSize) + TemplateModel searched, TemplateSequenceModel seq, int scanStartIndex, int seqSize, Environment env) throws TemplateException { - if (m_dir == 1) { + if (findFirst) { for (int i = scanStartIndex; i < seqSize; i++) { - if (modelsEqual(i, m_seq.get(i), target, m_env)) return i; + if (modelsEqual(i, seq.get(i), searched, env)) return i; } } else { for (int i = scanStartIndex; i >= 0; i--) { - if (modelsEqual(i, m_seq.get(i), target, m_env)) return i; + if (modelsEqual(i, seq.get(i), searched, env)) return i; } } return -1; @@ -545,16 +464,15 @@ class BuiltInsForSequences { } - private int m_dir; + private boolean findFirst; - seq_index_ofBI(int dir) { - m_dir = dir; + seq_index_ofBI(boolean findFirst) { + this.findFirst = findFirst; } @Override - TemplateModel _eval(Environment env) - throws TemplateException { - return new BIMethod(env); + TemplateModel calculateResult(TemplateIterableModel model) throws TemplateException { + return new BIMethod(model); } } @@ -567,7 +485,8 @@ class BuiltInsForSequences { } @Override - public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) + public TemplateModel execute( + TemplateModel[] args, CallPlace callPlace, Environment env) throws TemplateException { String[] subvars; TemplateModel obj = args[0]; @@ -575,10 +494,11 @@ class BuiltInsForSequences { subvars = new String[] { ((TemplateStringModel) obj).getAsString() }; } else if (obj instanceof TemplateSequenceModel) { TemplateSequenceModel seq = (TemplateSequenceModel) obj; - int ln = seq.size(); + int ln = seq.getCollectionSize(); subvars = new String[ln]; + TemplateModelIterator iter = seq.iterator(); for (int i = 0; i < ln; i++) { - TemplateModel item = seq.get(i); + TemplateModel item = iter.next(); if (!(item instanceof TemplateStringModel)) { throw new TemplateException( "The argument to ?", key, "(key), when it's a sequence, must be a " @@ -711,7 +631,7 @@ class BuiltInsForSequences { */ static TemplateSequenceModel sort(TemplateSequenceModel seq, String[] keyNames) throws TemplateException { - int ln = seq.size(); + int ln = seq.getCollectionSize(); if (ln == 0) return seq; ArrayList res = new ArrayList(ln); @@ -721,8 +641,9 @@ class BuiltInsForSequences { // Copy the Seq into a Java List[KVP] (also detects key type at the 1st item): int keyType = KEY_TYPE_NOT_YET_DETECTED; Comparator keyComparator = null; + TemplateModelIterator iter = seq.iterator(); for (int i = 0; i < ln; i++) { - final TemplateModel item = seq.get(i); + final TemplateModel item = iter.next(); TemplateModel key = item; for (int keyNameI = 0; keyNameI < keyNamesLn; keyNameI++) { try { @@ -757,8 +678,7 @@ class BuiltInsForSequences { } else if (key instanceof TemplateNumberModel) { keyType = KEY_TYPE_NUMBER; keyComparator = new NumericalKVPComparator( - Environment.getCurrentEnvironment() - .getArithmeticEngine()); + Environment.getCurrentEnvironment().getArithmeticEngine()); } else if (key instanceof TemplateDateModel) { keyType = KEY_TYPE_DATE; keyComparator = new DateKVPComparator(); @@ -877,8 +797,7 @@ class BuiltInsForSequences { } private static boolean modelsEqual( - int seqItemIndex, TemplateModel seqItem, TemplateModel searchedItem, - Environment env) + int seqItemIndex, TemplateModel seqItem, TemplateModel searchedItem, Environment env) throws TemplateException { try { return _EvalUtils.compare( @@ -894,7 +813,7 @@ class BuiltInsForSequences { " to the searched item:\n", new _DelayedGetMessage(ex)); } } - + // Can't be instantiated private BuiltInsForSequences() { }
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java index bbdb18f..80429b8 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsBasic.java @@ -95,7 +95,7 @@ class BuiltInsForStringsBasic { @Override TemplateModel _eval(Environment env) throws TemplateException { return new BIMethod(target.evalAndCoerceToStringOrUnsupportedMarkup(env, - "For sequences/collections (lists and such) use \"?seqContains\" instead.")); + "For iterables (like sequences) use \"?seqContains\" instead.")); } } @@ -252,7 +252,7 @@ class BuiltInsForStringsBasic { @Override TemplateModel _eval(Environment env) throws TemplateException { return new BIMethod(target.evalAndCoerceToStringOrUnsupportedMarkup(env, - "For sequences/collections (lists and such) use \"?seqIndexOf\" instead.")); + "For iterables (like seqiences) use \"?seqIndexOf\" instead.")); } } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java index d1c42cc..26821a3 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsMisc.java @@ -151,7 +151,7 @@ class BuiltInsForStringsMisc { if (model instanceof TemplateSequenceModel) { sourceExpr = ((ASTExpression) new ASTExpDynamicKeyName(target, new ASTExpNumberLiteral(0)) .copyLocationFrom(target)); - if (((TemplateSequenceModel) model).size() > 1) { + if (((TemplateSequenceModel) model).getCollectionSize() > 1) { id = ((ASTExpression) new ASTExpDynamicKeyName(target, new ASTExpNumberLiteral(1)) .copyLocationFrom(target)).evalAndCoerceToPlainText(env); } @@ -168,7 +168,6 @@ class BuiltInsForStringsMisc { final Template interpretedTemplate; try { - ParsingConfiguration pCfg = parentTemplate.getParsingConfiguration(); // pCfg.outputFormat+autoEscapingPolicy is exceptional: it's inherited from the lexical context interpretedTemplate = new Template( (parentTemplate.getLookupName() != null ? parentTemplate.getLookupName() : "nameless_template") + "->" + id, http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java index cb09e95..679b641 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/BuiltInsForStringsRegexp.java @@ -27,12 +27,12 @@ import java.util.regex.Pattern; import org.apache.freemarker.core.model.ArgumentArrayLayout; import org.apache.freemarker.core.model.TemplateBooleanModel; -import org.apache.freemarker.core.model.TemplateCollectionModel; import org.apache.freemarker.core.model.TemplateFunctionModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateModelIterator; -import org.apache.freemarker.core.model.TemplateStringModel; import org.apache.freemarker.core.model.TemplateSequenceModel; +import org.apache.freemarker.core.model.TemplateStringModel; +import org.apache.freemarker.core.model.impl.SequenceTemplateModelIterator; import org.apache.freemarker.core.model.impl.SimpleString; import org.apache.freemarker.core.util._StringUtils; @@ -152,8 +152,7 @@ class BuiltInsForStringsRegexp { // Represents the match - static class RegexMatchModel - implements TemplateBooleanModel, TemplateCollectionModel, TemplateSequenceModel { + static class RegexMatchModel implements TemplateBooleanModel, TemplateSequenceModel { static class MatchWithGroups implements TemplateStringModel { final String matchedInputPart; final String[] groups; @@ -172,15 +171,14 @@ class BuiltInsForStringsRegexp { return matchedInputPart; } } + final Pattern pattern; - final String input; + private Matcher firedEntireInputMatcher; private Boolean entireInputMatched; - private TemplateSequenceModel entireInputMatchGroups; - - private ArrayList matchingInputParts; + private ArrayList<TemplateModel> matchingInputParts; RegexMatchModel(Pattern pattern, String input) { this.pattern = pattern; @@ -189,17 +187,34 @@ class BuiltInsForStringsRegexp { @Override public TemplateModel get(int i) throws TemplateException { - ArrayList matchingInputParts = this.matchingInputParts; + if (i < 0) { + return null; + } + ArrayList<TemplateModel> matchingInputParts = this.matchingInputParts; if (matchingInputParts == null) { matchingInputParts = getMatchingInputPartsAndStoreResults(); } - return (TemplateModel) matchingInputParts.get(i); + return i < matchingInputParts.size() ? matchingInputParts.get(i) : null; } - + + @Override + public int getCollectionSize() throws TemplateException { + ArrayList<TemplateModel> matchingInputParts = this.matchingInputParts; + if (matchingInputParts == null) { + matchingInputParts = getMatchingInputPartsAndStoreResults(); + } + return matchingInputParts.size(); + } + + @Override + public boolean isEmptyCollection() throws TemplateException { + return getCollectionSize() == 0; + } + @Override public boolean getAsBoolean() { Boolean result = entireInputMatched; - return result != null ? result.booleanValue() : isEntrieInputMatchesAndStoreResults(); + return result != null ? result : isEntireInputMatchesAndStoreResults(); } TemplateModel getGroups() { @@ -207,7 +222,7 @@ class BuiltInsForStringsRegexp { if (entireInputMatchGroups == null) { Matcher t = firedEntireInputMatcher; if (t == null) { - isEntrieInputMatchesAndStoreResults(); + isEntireInputMatchesAndStoreResults(); t = firedEntireInputMatcher; } final Matcher firedEntireInputMatcher = t; @@ -217,8 +232,7 @@ class BuiltInsForStringsRegexp { @Override public TemplateModel get(int i) throws TemplateException { try { - // Avoid IndexOutOfBoundsException: - if (i > firedEntireInputMatcher.groupCount()) { + if (i < 0 || i > firedEntireInputMatcher.groupCount()) { return null; } @@ -229,22 +243,27 @@ class BuiltInsForStringsRegexp { } @Override - public int size() throws TemplateException { - try { - return firedEntireInputMatcher.groupCount() + 1; - } catch (Exception e) { - throw new TemplateException("Failed to get regular expression match group count", e); - } + public int getCollectionSize() throws TemplateException { + return firedEntireInputMatcher.groupCount() + 1; + } + + @Override + public boolean isEmptyCollection() throws TemplateException { + return getCollectionSize() == 0; + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new SequenceTemplateModelIterator(this); } - }; this.entireInputMatchGroups = entireInputMatchGroups; } return entireInputMatchGroups; } - private ArrayList getMatchingInputPartsAndStoreResults() throws TemplateException { - ArrayList matchingInputParts = new ArrayList(); + private ArrayList<TemplateModel> getMatchingInputPartsAndStoreResults() throws TemplateException { + ArrayList<TemplateModel> matchingInputParts = new ArrayList<>(); Matcher matcher = pattern.matcher(input); while (matcher.find()) { @@ -255,17 +274,17 @@ class BuiltInsForStringsRegexp { return matchingInputParts; } - private boolean isEntrieInputMatchesAndStoreResults() { + private boolean isEntireInputMatchesAndStoreResults() { Matcher matcher = pattern.matcher(input); boolean matches = matcher.matches(); firedEntireInputMatcher = matcher; - entireInputMatched = Boolean.valueOf(matches); + entireInputMatched = matches; return matches; } @Override public TemplateModelIterator iterator() { - final ArrayList matchingInputParts = this.matchingInputParts; + final ArrayList<TemplateModel> matchingInputParts = this.matchingInputParts; if (matchingInputParts == null) { final Matcher matcher = pattern.matcher(input); return new TemplateModelIterator() { @@ -275,7 +294,7 @@ class BuiltInsForStringsRegexp { @Override public boolean hasNext() { - final ArrayList matchingInputParts = RegexMatchModel.this.matchingInputParts; + final ArrayList<TemplateModel> matchingInputParts = RegexMatchModel.this.matchingInputParts; if (matchingInputParts == null) { return hasFindInfo; } else { @@ -285,7 +304,7 @@ class BuiltInsForStringsRegexp { @Override public TemplateModel next() throws TemplateException { - final ArrayList matchingInputParts = RegexMatchModel.this.matchingInputParts; + final ArrayList<TemplateModel> matchingInputParts = RegexMatchModel.this.matchingInputParts; if (matchingInputParts == null) { if (!hasFindInfo) { throw new TemplateException("There were no more regular expression matches"); @@ -295,11 +314,7 @@ class BuiltInsForStringsRegexp { hasFindInfo = matcher.find(); return result; } else { - try { - return (TemplateModel) matchingInputParts.get(nextIdx++); - } catch (IndexOutOfBoundsException e) { - throw new TemplateException("There were no more regular expression matches", e); - } + return matchingInputParts.get(nextIdx++); } } @@ -316,24 +331,12 @@ class BuiltInsForStringsRegexp { @Override public TemplateModel next() throws TemplateException { - try { - return (TemplateModel) matchingInputParts.get(nextIdx++); - } catch (IndexOutOfBoundsException e) { - throw new TemplateException("There were no more regular expression matches", e); - } + return matchingInputParts.get(nextIdx++); } }; } } - @Override - public int size() throws TemplateException { - ArrayList matchingInputParts = this.matchingInputParts; - if (matchingInputParts == null) { - matchingInputParts = getMatchingInputPartsAndStoreResults(); - } - return matchingInputParts.size(); - } } // Can't be instantiated http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java index c0af937..26d5a0e 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/Environment.java @@ -48,7 +48,7 @@ import org.apache.freemarker.core.arithmetic.ArithmeticEngine; import org.apache.freemarker.core.model.ArgumentArrayLayout; import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.model.TemplateCallableModel; -import org.apache.freemarker.core.model.TemplateCollectionModel; +import org.apache.freemarker.core.model.TemplateIterableModel; import org.apache.freemarker.core.model.TemplateDateModel; import org.apache.freemarker.core.model.TemplateDirectiveModel; import org.apache.freemarker.core.model.TemplateFunctionModel; @@ -669,8 +669,11 @@ public final class Environment extends MutableProcessingConfiguration<Environmen } } TemplateSequenceModel children = node.getChildNodes(); - if (children == null) return; - for (int i = 0; i < children.size(); i++) { + if (children == null) { + return; + } + int size = children.getCollectionSize(); + for (int i = 0; i < size; i++) { TemplateNodeModel child = (TemplateNodeModel) children.get(i); if (child != null) { invokeNodeHandlerFor(child, namespaces); @@ -2195,7 +2198,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen final TemplateHashModel result = new TemplateHashModel() { @Override - public boolean isEmpty() { + public boolean isEmptyHash() { return false; } @@ -2213,8 +2216,8 @@ public final class Environment extends MutableProcessingConfiguration<Environmen return new TemplateHashModelEx() { @Override - public boolean isEmpty() throws TemplateException { - return result.isEmpty(); + public boolean isEmptyHash() throws TemplateException { + return result.isEmptyHash(); } @Override @@ -2226,18 +2229,18 @@ public final class Environment extends MutableProcessingConfiguration<Environmen // configuration shared variables even though // the hash will return them, if only for BWC reasons @Override - public TemplateCollectionModel values() throws TemplateException { + public TemplateIterableModel values() throws TemplateException { return ((TemplateHashModelEx) rootDataModel).values(); } @Override - public TemplateCollectionModel keys() throws TemplateException { + public TemplateIterableModel keys() throws TemplateException { return ((TemplateHashModelEx) rootDataModel).keys(); } @Override - public int size() throws TemplateException { - return ((TemplateHashModelEx) rootDataModel).size(); + public int getHashSize() throws TemplateException { + return ((TemplateHashModelEx) rootDataModel).getHashSize(); } }; } @@ -2253,7 +2256,7 @@ public final class Environment extends MutableProcessingConfiguration<Environmen return new TemplateHashModel() { @Override - public boolean isEmpty() { + public boolean isEmptyHash() { return false; } @@ -2333,7 +2336,8 @@ public final class Environment extends MutableProcessingConfiguration<Environmen throws TemplateException { TemplateDirectiveModel result = null; int i; - for (i = startIndex; i < nodeNamespaces.size(); i++) { + int size = nodeNamespaces.getCollectionSize(); + for (i = startIndex; i < size; i++) { Namespace ns = null; try { ns = (Namespace) nodeNamespaces.get(i); @@ -2850,25 +2854,25 @@ public final class Environment extends MutableProcessingConfiguration<Environmen } @Override - public int size() { + public int getHashSize() { ensureInitializedRTE(); - return super.size(); + return super.getHashSize(); } @Override - public boolean isEmpty() { + public boolean isEmptyHash() { ensureInitializedRTE(); - return super.isEmpty(); + return super.isEmptyHash(); } @Override - public TemplateCollectionModel keys() { + public TemplateIterableModel keys() { ensureInitializedRTE(); return super.keys(); } @Override - public TemplateCollectionModel values() { + public TemplateIterableModel values() { ensureInitializedRTE(); return super.values(); } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/InvalidReferenceException.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/InvalidReferenceException.java b/freemarker-core/src/main/java/org/apache/freemarker/core/InvalidReferenceException.java index 20d2c10..2cd4fd1 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/InvalidReferenceException.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/InvalidReferenceException.java @@ -65,7 +65,7 @@ public class InvalidReferenceException extends TemplateException { private static final String TIP_JSP_TAGLIBS = "The \"JspTaglibs\" variable isn't a core FreeMarker feature; " + "it's only available when templates are invoked through org.apache.freemarker.servlet.FreemarkerServlet" - + " (or other custom FreeMarker-JSP integration solution)."; + + " (or through some other custom FreeMarker-JSP integration solution)."; /** * Creates and invalid reference exception that contains no information about what was missing or null. http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/ListableRightUnboundedRangeModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/ListableRightUnboundedRangeModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/ListableRightUnboundedRangeModel.java deleted file mode 100644 index 0b7e94e..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/ListableRightUnboundedRangeModel.java +++ /dev/null @@ -1,94 +0,0 @@ -/* - * 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.TemplateModelIterator; -import org.apache.freemarker.core.model.impl.SimpleNumber; - -/** - * This is the model used for right-unbounded ranges since Incompatible Improvements 2.3.21. - */ -final class ListableRightUnboundedRangeModel extends RightUnboundedRangeModel implements TemplateCollectionModel { - - ListableRightUnboundedRangeModel(int begin) { - super(begin); - } - - @Override - public int size() throws TemplateException { - return Integer.MAX_VALUE; - } - - @Override - public TemplateModelIterator iterator() throws TemplateException { - return new TemplateModelIterator() { - boolean needInc; - int nextType = 1; - int nextInt = getBegining(); - long nextLong; - BigInteger nextBigInteger; - - @Override - public TemplateModel next() throws TemplateException { - 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 TemplateException { - return true; - } - - }; - - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtils.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtils.java b/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtils.java index b8354d7..7b9952e 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtils.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/MessageUtils.java @@ -20,13 +20,11 @@ package org.apache.freemarker.core; import org.apache.freemarker.core.model.TemplateBooleanModel; -import org.apache.freemarker.core.model.TemplateCollectionModel; import org.apache.freemarker.core.model.TemplateDateModel; import org.apache.freemarker.core.model.TemplateMarkupOutputModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateNumberModel; import org.apache.freemarker.core.model.TemplateStringModel; -import org.apache.freemarker.core.model.TemplateSequenceModel; import org.apache.freemarker.core.util.BugException; import org.apache.freemarker.core.util.TemplateLanguageUtils; import org.apache.freemarker.core.util._StringUtils; @@ -80,13 +78,11 @@ class MessageUtils { EXPECTED_TYPES_STRING_COERCABLE_TYPES_AND_TOM[i] = TemplateMarkupOutputModel.class; } - static final String SEQUENCE_OR_COLLECTION = "sequence or collection"; - static final Class[] EXPECTED_TYPES_SEQUENCE_OR_COLLECTION = new Class[] { - TemplateSequenceModel.class, TemplateCollectionModel.class - }; + static final String EXPECTED_TYPE_ITERABLE_DESC = "iterable (like a sequence)"; - // Can't be instantiated - private MessageUtils() { } + private MessageUtils() { + // Not meant to be instantiated + } static String formatLocationForSimpleParsingError(String templateSourceOrLookupName, int line, int column) { return formatLocation("in", templateSourceOrLookupName, line, column); @@ -271,6 +267,15 @@ class MessageUtils { } static TemplateException newUnexpectedOperandTypeException( + ASTExpression blamed, TemplateModel model, String expectedTypesDesc, Class<? extends TemplateModel> expectedType, + Object[] tips, + Environment env) + throws InvalidReferenceException { + return newUnexpectedOperandTypeException( + blamed, model, expectedTypesDesc, new Class[] { expectedType }, tips, env); + } + + static TemplateException newUnexpectedOperandTypeException( ASTExpression blamed, TemplateModel model, String expectedTypesDesc, Class[] expectedTypes, Object[] tips, Environment env) throws InvalidReferenceException { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/NativeCollection.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NativeCollection.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeCollection.java new file mode 100644 index 0000000..da90371 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeCollection.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; + +import java.util.Collection; + +import org.apache.freemarker.core.model.ObjectWrapper; +import org.apache.freemarker.core.model.TemplateCollectionModel; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelIterator; + +/** + * A collection where each items is already a {@link TemplateModel}, so no {@link ObjectWrapper} need to be specified. + */ +class NativeCollection implements TemplateCollectionModel { + + private final Collection<TemplateModel> collection; + + public NativeCollection(Collection<TemplateModel> collection) { + this.collection = collection; + } + + @Override + public int getCollectionSize() { + return collection.size(); + } + + @Override + public boolean isEmptyCollection() { + return collection.isEmpty(); + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new NativeTemplateModelIterator(collection.iterator()); + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/NativeCollectionEx.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NativeCollectionEx.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeCollectionEx.java deleted file mode 100644 index 119cae5..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/NativeCollectionEx.java +++ /dev/null @@ -1,72 +0,0 @@ -/* - * 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 java.util.Iterator; - -import org.apache.freemarker.core.model.ObjectWrapper; -import org.apache.freemarker.core.model.TemplateCollectionModelEx; -import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.model.TemplateModelIterator; - -/** - * A collection where each items is already a {@link TemplateModel}, so no {@link ObjectWrapper} need to be specified. - */ -class NativeCollectionEx implements TemplateCollectionModelEx { - - private final Collection<TemplateModel> collection; - - public NativeCollectionEx(Collection<TemplateModel> collection) { - this.collection = collection; - } - - @Override - public int size() { - return collection.size(); - } - - @Override - public boolean isEmpty() { - return collection.isEmpty(); - } - - @Override - public TemplateModelIterator iterator() throws TemplateException { - return new TemplateModelIterator() { - - private final Iterator<TemplateModel> iterator = collection.iterator(); - - @Override - public TemplateModel next() throws TemplateException { - if (!iterator.hasNext()) { - throw new TemplateException("The collection has no more items."); - } - - return iterator.next(); - } - - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - }; - } -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/NativeHashEx2.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NativeHashEx2.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeHashEx2.java index a7b2415..bb505c6 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/NativeHashEx2.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeHashEx2.java @@ -25,7 +25,7 @@ import java.util.LinkedHashMap; import java.util.Map; import org.apache.freemarker.core.model.ObjectWrapper; -import org.apache.freemarker.core.model.TemplateCollectionModel; +import org.apache.freemarker.core.model.TemplateIterableModel; import org.apache.freemarker.core.model.TemplateHashModelEx2; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.impl.SimpleString; @@ -45,7 +45,7 @@ class NativeHashEx2 implements TemplateHashModelEx2, Serializable { } @Override - public int size() throws TemplateException { + public int getHashSize() throws TemplateException { return map.size(); } @@ -55,7 +55,7 @@ class NativeHashEx2 implements TemplateHashModelEx2, Serializable { } @Override - public boolean isEmpty() throws TemplateException { + public boolean isEmptyHash() throws TemplateException { return map.isEmpty(); } @@ -89,13 +89,13 @@ class NativeHashEx2 implements TemplateHashModelEx2, Serializable { } @Override - public TemplateCollectionModel keys() throws TemplateException { - return new NativeStringCollectionCollectionEx(map.keySet()); + public TemplateIterableModel keys() throws TemplateException { + return new NativeStringCollectionCollection(map.keySet()); } @Override - public TemplateCollectionModel values() throws TemplateException { - return new NativeCollectionEx(map.values()); + public TemplateIterableModel values() throws TemplateException { + return new NativeCollection(map.values()); } public TemplateModel put(String key, TemplateModel value) { http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/NativeSequence.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NativeSequence.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeSequence.java index 5a43ef6..b5febb8 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/NativeSequence.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeSequence.java @@ -25,6 +25,7 @@ import java.util.Collection; import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelIterator; import org.apache.freemarker.core.model.TemplateSequenceModel; /** @@ -49,25 +50,53 @@ class NativeSequence implements TemplateSequenceModel, Serializable { this.items.addAll(items); } - public void add(TemplateModel tm) { + void add(TemplateModel tm) { items.add(tm); } - public void addAll(Collection<TemplateModel> items) { + void addAll(Collection<TemplateModel> items) { this.items.addAll(items); } - public void clear() { + void clear() { items.clear(); } @Override public TemplateModel get(int index) throws TemplateException { - return items.get(index); + return index < items.size() && index >= 0 ? items.get(index) : null; } @Override - public int size() throws TemplateException { + public int getCollectionSize() throws TemplateException { return items.size(); } + + @Override + public boolean isEmptyCollection() throws TemplateException { + return items.isEmpty(); + } + + /** + * Do not call when you will still add items to the sequence! + * + * {@inheritDoc} + */ + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new TemplateModelIterator() { + private int nextIndex = 0; + private final int size = items.size(); + + @Override + public TemplateModel next() throws TemplateException { + return items.get(nextIndex++); + } + + @Override + public boolean hasNext() throws TemplateException { + return nextIndex < size; + } + }; + } } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/NativeStringArraySequence.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NativeStringArraySequence.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeStringArraySequence.java index a74a63e..5aaf62b 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/NativeStringArraySequence.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeStringArraySequence.java @@ -21,6 +21,7 @@ package org.apache.freemarker.core; import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelIterator; import org.apache.freemarker.core.model.TemplateSequenceModel; import org.apache.freemarker.core.model.impl.DefaultArrayAdapter; import org.apache.freemarker.core.model.impl.SimpleString; @@ -41,12 +42,34 @@ class NativeStringArraySequence implements TemplateSequenceModel { @Override public TemplateModel get(int index) throws TemplateException { - return index < items.length ? new SimpleString(items[index]) : null; + return index < items.length && index >= 0 ? new SimpleString(items[index]) : null; } @Override - public int size() throws TemplateException { + public int getCollectionSize() throws TemplateException { return items.length; } + @Override + public boolean isEmptyCollection() throws TemplateException { + return items.length == 0; + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new TemplateModelIterator() { + private int nextIndex; + + @Override + public TemplateModel next() throws TemplateException { + return new SimpleString(items[nextIndex++]); + } + + @Override + public boolean hasNext() throws TemplateException { + return nextIndex < items.length; + } + }; + } + } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/NativeStringCollectionCollection.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NativeStringCollectionCollection.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeStringCollectionCollection.java new file mode 100644 index 0000000..7793789 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeStringCollectionCollection.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 java.util.Collection; +import java.util.Iterator; + +import org.apache.freemarker.core.model.ObjectWrapper; +import org.apache.freemarker.core.model.TemplateCollectionModel; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelIterator; +import org.apache.freemarker.core.model.impl.DefaultNonListCollectionAdapter; +import org.apache.freemarker.core.model.impl.SimpleString; + +/** + * Adapts (not copies) a {@link Collection} of {@link String}-s with on-the-fly wrapping of the items to {@link + * SimpleString}-s. The important difference to {@link DefaultNonListCollectionAdapter} is that it doesn't depend on an + * {@link ObjectWrapper}, which is needed to guarantee the behavior of some template language constructs. The important + * difference to {@link NativeCollection} is that it doesn't need upfront conversion to {@link TemplateModel}-s + * (performance). + */ +class NativeStringCollectionCollection implements TemplateCollectionModel { + + private final Collection<String> collection; + + public NativeStringCollectionCollection(Collection<String> collection) { + this.collection = collection; + } + + @Override + public int getCollectionSize() { + return collection.size(); + } + + @Override + public boolean isEmptyCollection() { + return collection.isEmpty(); + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new TemplateModelIterator() { + + private final Iterator<String> iterator = collection.iterator(); + + @Override + public TemplateModel next() throws TemplateException { + return new SimpleString(iterator.next()); + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + }; + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/NativeStringCollectionCollectionEx.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NativeStringCollectionCollectionEx.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeStringCollectionCollectionEx.java deleted file mode 100644 index 78b2099..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/NativeStringCollectionCollectionEx.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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 java.util.Iterator; - -import org.apache.freemarker.core.model.ObjectWrapper; -import org.apache.freemarker.core.model.TemplateCollectionModelEx; -import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.model.TemplateModelIterator; -import org.apache.freemarker.core.model.impl.DefaultNonListCollectionAdapter; -import org.apache.freemarker.core.model.impl.SimpleString; - -/** - * Adapts (not copies) a {@link Collection} of {@link String}-s with on-the-fly wrapping of the items to {@link - * SimpleString}-s. The important difference to {@link DefaultNonListCollectionAdapter} is that it doesn't depend on an - * {@link ObjectWrapper}, which is needed to guarantee the behavior of some template language constructs. The important - * difference to {@link NativeCollectionEx} is that it doesn't need upfront conversion to {@link TemplateModel}-s - * (performance). - */ -class NativeStringCollectionCollectionEx implements TemplateCollectionModelEx { - - private final Collection<String> collection; - - public NativeStringCollectionCollectionEx(Collection<String> collection) { - this.collection = collection; - } - - @Override - public int size() { - return collection.size(); - } - - @Override - public boolean isEmpty() { - return collection.isEmpty(); - } - - @Override - public TemplateModelIterator iterator() throws TemplateException { - return new TemplateModelIterator() { - - private final Iterator<String> iterator = collection.iterator(); - - @Override - public TemplateModel next() throws TemplateException { - if (!iterator.hasNext()) { - throw new TemplateException("The collection has no more items."); - } - - return new SimpleString(iterator.next()); - } - - @Override - public boolean hasNext() { - return iterator.hasNext(); - } - }; - } -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/NativeStringListSequence.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NativeStringListSequence.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeStringListSequence.java index 8ab167e..4ddfe0d 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/NativeStringListSequence.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeStringListSequence.java @@ -23,6 +23,7 @@ import java.util.List; import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelIterator; import org.apache.freemarker.core.model.TemplateSequenceModel; import org.apache.freemarker.core.model.impl.DefaultListAdapter; import org.apache.freemarker.core.model.impl.SimpleString; @@ -44,12 +45,34 @@ class NativeStringListSequence implements TemplateSequenceModel { @Override public TemplateModel get(int index) throws TemplateException { - return index < items.size() ? new SimpleString(items.get(index)) : null; + return index < items.size() && index >= 0 ? new SimpleString(items.get(index)) : null; } @Override - public int size() throws TemplateException { + public int getCollectionSize() throws TemplateException { return items.size(); } + @Override + public boolean isEmptyCollection() throws TemplateException { + return items.isEmpty(); + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new TemplateModelIterator() { + private int nextIndex; + + @Override + public TemplateModel next() throws TemplateException { + return new SimpleString(items.get(nextIndex++)); + } + + @Override + public boolean hasNext() throws TemplateException { + return nextIndex < items.size(); + } + }; + } + } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/NativeTemplateModelIterator.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/NativeTemplateModelIterator.java b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeTemplateModelIterator.java new file mode 100644 index 0000000..c1c51a3 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/NativeTemplateModelIterator.java @@ -0,0 +1,44 @@ +/* + * 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.Iterator; + +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelIterator; + +class NativeTemplateModelIterator implements TemplateModelIterator { + + private final Iterator<TemplateModel> iterator; + + NativeTemplateModelIterator(Iterator<TemplateModel> iterator) { + this.iterator = iterator; + } + + @Override + public TemplateModel next() throws TemplateException { + return iterator.next(); + } + + @Override + public boolean hasNext() { + return iterator.hasNext(); + } +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/RangeModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/RangeModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/RangeModel.java index 512102a..00be690 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/RangeModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/RangeModel.java @@ -31,19 +31,20 @@ abstract class RangeModel implements TemplateSequenceModel, java.io.Serializable this.begin = begin; } - final int getBegining() { + final int getBeginning() { return begin; } @Override - final public TemplateModel get(int index) throws TemplateException { - if (index < 0 || index >= size()) { - throw new TemplateException("Range item index ", Integer.valueOf(index), " is out of bounds."); - } - long value = begin + getStep() * (long) index; + public final TemplateModel get(int index) throws TemplateException { + return index < getCollectionSize() && index >= 0 ? uncheckedGet(index) : null; + } + + protected final TemplateModel uncheckedGet(long index) { + long value = begin + getStep() * index; return value <= Integer.MAX_VALUE ? new SimpleNumber((int) value) : new SimpleNumber(value); } - + /** * @return {@code 1} or {@code -1}; other return values need not be properly handled until FTL supports other steps. */ @@ -52,7 +53,5 @@ abstract class RangeModel implements TemplateSequenceModel, java.io.Serializable abstract boolean isRightUnbounded(); abstract boolean isRightAdaptive(); - - abstract boolean isAffactedByStringSlicingBug(); } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/RightUnboundedRangeModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/RightUnboundedRangeModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/RightUnboundedRangeModel.java index e135d18..04ab6bc 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/RightUnboundedRangeModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/RightUnboundedRangeModel.java @@ -19,8 +19,18 @@ package org.apache.freemarker.core; -abstract class RightUnboundedRangeModel extends RangeModel { - +import java.math.BigInteger; + +import org.apache.freemarker.core.model.TemplateIterableModel; +import org.apache.freemarker.core.model.TemplateModel; +import org.apache.freemarker.core.model.TemplateModelIterator; +import org.apache.freemarker.core.model.impl.SimpleNumber; + +/** + * This is the model used for right-unbounded ranges + */ +final class RightUnboundedRangeModel extends RangeModel implements TemplateIterableModel { + RightUnboundedRangeModel(int begin) { super(begin); } @@ -34,15 +44,71 @@ abstract class RightUnboundedRangeModel extends RangeModel { final boolean isRightUnbounded() { return true; } - + @Override final boolean isRightAdaptive() { return true; } @Override - final boolean isAffactedByStringSlicingBug() { + public int getCollectionSize() throws TemplateException { + return Integer.MAX_VALUE; + } + + @Override + public boolean isEmptyCollection() throws TemplateException { return false; } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new TemplateModelIterator() { + boolean needInc; + int nextType = 1; + int nextInt = getBeginning(); + long nextLong; + BigInteger nextBigInteger; + + @Override + public TemplateModel next() throws TemplateException { + 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 TemplateException { + return true; + } + + }; + + } } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java b/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java index a8e2ed1..30b869c 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/_EvalUtils.java @@ -26,13 +26,12 @@ import java.util.Date; import org.apache.freemarker.core.arithmetic.ArithmeticEngine; import org.apache.freemarker.core.arithmetic.impl.BigDecimalArithmeticEngine; import org.apache.freemarker.core.model.TemplateBooleanModel; -import org.apache.freemarker.core.model.TemplateCollectionModel; import org.apache.freemarker.core.model.TemplateDateModel; +import org.apache.freemarker.core.model.TemplateIterableModel; import org.apache.freemarker.core.model.TemplateMarkupOutputModel; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.TemplateNumberModel; import org.apache.freemarker.core.model.TemplateStringModel; -import org.apache.freemarker.core.model.TemplateSequenceModel; import org.apache.freemarker.core.outputformat.MarkupOutputFormat; import org.apache.freemarker.core.util.BugException; import org.apache.freemarker.core.util._ClassUtils; @@ -338,7 +337,7 @@ public class _EvalUtils { * {@link TemplateValueFormat} involved produces. * * @param seqTip - * Tip to display if the value type is not coercable, but it's sequence or collection. + * Tip to display if the value type is not coercable, but it's iterable. * * @return Never {@code null} */ @@ -384,7 +383,7 @@ public class _EvalUtils { * if the result is markup. This is what you normally use where markup results can't be used. * * @param seqTip - * Tip to display if the value type is not coercable, but it's sequence or collection. + * Tip to display if the value type is not coercable, but it's iterable. * * @return Never {@code null} */ @@ -417,7 +416,7 @@ public class _EvalUtils { * markup. This should be used rarely, where the user clearly intend to use the plain text variant of the format. * * @param seqTip - * Tip to display if the value type is not coercable, but it's sequence or collection. + * Tip to display if the value type is not coercable, but it's iterable. * * @return Never {@code null} */ @@ -469,7 +468,7 @@ public class _EvalUtils { exp, tm, supportsTOM ? STRING_COERCABLE_TYPES_OR_TOM_DESC : STRING_COERCABLE_TYPES_DESC, supportsTOM ? EXPECTED_TYPES_STRING_COERCABLE_TYPES_AND_TOM : EXPECTED_TYPES_STRING_COERCABLE, - seqHint != null && (tm instanceof TemplateSequenceModel || tm instanceof TemplateCollectionModel) + seqHint != null && tm instanceof TemplateIterableModel ? new Object[] { seqHint } : null, env);
