You are viewing a plain text version of this content. The canonical link for it is here.
Posted to notifications@freemarker.apache.org by dd...@apache.org on 2017/09/17 10:56:39 UTC
[1/9] incubator-freemarker git commit: (Some internal code cleanup)
Repository: incubator-freemarker
Updated Branches:
refs/heads/2.3 b25782910 -> dd60aef18
(Some internal code cleanup)
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/b185b980
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/b185b980
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/b185b980
Branch: refs/heads/2.3
Commit: b185b98001b10281f6c0d1e8df759cdd265714ef
Parents: a4a406a
Author: ddekany <dd...@apache.org>
Authored: Wed Aug 30 21:40:23 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Wed Aug 30 21:40:23 2017 +0200
----------------------------------------------------------------------
src/main/java/freemarker/core/Macro.java | 3 +-
.../java/freemarker/ext/dom/NodeListModel.java | 5 +-
.../template/DefaultIterableAdapter.java | 2 +-
.../freemarker/template/DefaultListAdapter.java | 2 +-
.../DefaultNonListCollectionAdapter.java | 2 +-
.../DefaultUnassignableIteratorAdapter.java | 52 --------------------
.../IteratorToTemplateModelIteratorAdapter.java | 50 +++++++++++++++++++
7 files changed, 58 insertions(+), 58 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b185b980/src/main/java/freemarker/core/Macro.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Macro.java b/src/main/java/freemarker/core/Macro.java
index ef657ac..fdce23b 100644
--- a/src/main/java/freemarker/core/Macro.java
+++ b/src/main/java/freemarker/core/Macro.java
@@ -29,6 +29,7 @@ import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import freemarker.template.TemplateModelIterator;
+import freemarker.template.TemplateScalarModel;
/**
* An element representing a macro declaration.
@@ -263,7 +264,7 @@ public final class Macro extends TemplateElement implements TemplateModel {
public Collection getLocalVariableNames() throws TemplateModelException {
HashSet result = new HashSet();
for (TemplateModelIterator it = localVars.keys().iterator(); it.hasNext(); ) {
- result.add(it.next().toString());
+ result.add(((TemplateScalarModel) it.next()).getAsString());
}
return result;
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b185b980/src/main/java/freemarker/ext/dom/NodeListModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/ext/dom/NodeListModel.java b/src/main/java/freemarker/ext/dom/NodeListModel.java
index a6a7534..46644d9 100644
--- a/src/main/java/freemarker/ext/dom/NodeListModel.java
+++ b/src/main/java/freemarker/ext/dom/NodeListModel.java
@@ -216,11 +216,12 @@ class NodeListModel extends SimpleSequence implements TemplateHashModel, _Unexpe
}
private Object[] newTypeErrorExplanation(String type) {
+ int size = size();
return new Object[] {
"This XML query result can't be used as ", type, " because for that it had to contain exactly "
- + "1 XML node, but it contains ", Integer.valueOf(size()), " nodes. "
+ + "1 XML node, but it contains ", Integer.valueOf(size), " nodes. "
+ "That is, the constructing XML query has found ",
- isEmpty()
+ size == 0
? "no matches."
: "multiple matches."
};
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b185b980/src/main/java/freemarker/template/DefaultIterableAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/DefaultIterableAdapter.java b/src/main/java/freemarker/template/DefaultIterableAdapter.java
index 14aa6d1..824e2d7 100644
--- a/src/main/java/freemarker/template/DefaultIterableAdapter.java
+++ b/src/main/java/freemarker/template/DefaultIterableAdapter.java
@@ -68,7 +68,7 @@ public class DefaultIterableAdapter extends WrappingTemplateModel implements Tem
}
public TemplateModelIterator iterator() throws TemplateModelException {
- return new DefaultUnassignableIteratorAdapter(iterable.iterator(), getObjectWrapper());
+ return new IteratorToTemplateModelIteratorAdapter(iterable.iterator(), getObjectWrapper());
}
public Object getWrappedObject() {
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b185b980/src/main/java/freemarker/template/DefaultListAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/DefaultListAdapter.java b/src/main/java/freemarker/template/DefaultListAdapter.java
index 62e7f18..3585ed8 100644
--- a/src/main/java/freemarker/template/DefaultListAdapter.java
+++ b/src/main/java/freemarker/template/DefaultListAdapter.java
@@ -96,7 +96,7 @@ public class DefaultListAdapter extends WrappingTemplateModel implements Templat
}
public TemplateModelIterator iterator() throws TemplateModelException {
- return new DefaultUnassignableIteratorAdapter(list.iterator(), getObjectWrapper());
+ return new IteratorToTemplateModelIteratorAdapter(list.iterator(), getObjectWrapper());
}
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b185b980/src/main/java/freemarker/template/DefaultNonListCollectionAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/DefaultNonListCollectionAdapter.java b/src/main/java/freemarker/template/DefaultNonListCollectionAdapter.java
index 2ee9ca2..ab87cc6 100644
--- a/src/main/java/freemarker/template/DefaultNonListCollectionAdapter.java
+++ b/src/main/java/freemarker/template/DefaultNonListCollectionAdapter.java
@@ -70,7 +70,7 @@ public class DefaultNonListCollectionAdapter extends WrappingTemplateModel imple
}
public TemplateModelIterator iterator() throws TemplateModelException {
- return new DefaultUnassignableIteratorAdapter(collection.iterator(), getObjectWrapper());
+ return new IteratorToTemplateModelIteratorAdapter(collection.iterator(), getObjectWrapper());
}
public int size() {
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b185b980/src/main/java/freemarker/template/DefaultUnassignableIteratorAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/DefaultUnassignableIteratorAdapter.java b/src/main/java/freemarker/template/DefaultUnassignableIteratorAdapter.java
deleted file mode 100644
index 80c4bc5..0000000
--- a/src/main/java/freemarker/template/DefaultUnassignableIteratorAdapter.java
+++ /dev/null
@@ -1,52 +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 freemarker.template;
-
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
-/**
- * As opposed to {@link DefaultIteratorAdapter}, this simpler {@link Iterator} adapter is used in situations where the
- * {@link TemplateModelIterator} won't be assigned to FreeMarker template variables, only used internally by
- * {@code #list} or custom Java code. Because of that, it doesn't have to handle the situation where the user tries to
- * iterate over the same value twice.
- */
-class DefaultUnassignableIteratorAdapter implements TemplateModelIterator {
-
- private final Iterator<?> it;
- private final ObjectWrapper wrapper;
-
- DefaultUnassignableIteratorAdapter(Iterator<?> it, ObjectWrapper wrapper) {
- this.it = it;
- this.wrapper = wrapper;
- }
-
- public TemplateModel next() throws TemplateModelException {
- try {
- return wrapper.wrap(it.next());
- } catch (NoSuchElementException e) {
- throw new TemplateModelException("The collection has no more items.", e);
- }
- }
-
- public boolean hasNext() throws TemplateModelException {
- return it.hasNext();
- }
-
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/b185b980/src/main/java/freemarker/template/IteratorToTemplateModelIteratorAdapter.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/IteratorToTemplateModelIteratorAdapter.java b/src/main/java/freemarker/template/IteratorToTemplateModelIteratorAdapter.java
new file mode 100644
index 0000000..6b49237
--- /dev/null
+++ b/src/main/java/freemarker/template/IteratorToTemplateModelIteratorAdapter.java
@@ -0,0 +1,50 @@
+/*
+ * 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 freemarker.template;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+/**
+ * Unlike {@link DefaultIteratorAdapter}, this doesn't adapt to some {@link TemplateModel}, but to {@link
+ * TemplateModelIterator}.
+ */
+class IteratorToTemplateModelIteratorAdapter implements TemplateModelIterator {
+
+ private final Iterator<?> it;
+ private final ObjectWrapper wrapper;
+
+ IteratorToTemplateModelIteratorAdapter(Iterator<?> it, ObjectWrapper wrapper) {
+ this.it = it;
+ this.wrapper = wrapper;
+ }
+
+ public TemplateModel next() throws TemplateModelException {
+ try {
+ return wrapper.wrap(it.next());
+ } catch (NoSuchElementException e) {
+ throw new TemplateModelException("The collection has no more items.", e);
+ }
+ }
+
+ public boolean hasNext() throws TemplateModelException {
+ return it.hasNext();
+ }
+
+}
\ No newline at end of file
[9/9] incubator-freemarker git commit: Merge remote-tracking branch
'origin/2.3-gae' into 2.3
Posted by dd...@apache.org.
Merge remote-tracking branch 'origin/2.3-gae' into 2.3
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/dd60aef1
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/dd60aef1
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/dd60aef1
Branch: refs/heads/2.3
Commit: dd60aef18fbe643298edf64e9eca999d0b7ca945
Parents: b257829 a671110
Author: ddekany <dd...@apache.org>
Authored: Sun Sep 17 12:56:03 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Sun Sep 17 12:56:03 2017 +0200
----------------------------------------------------------------------
README.md | 4 +-
.../java/freemarker/core/BreakInstruction.java | 8 +-
.../core/BreakOrContinueException.java | 12 ++
src/main/java/freemarker/core/BuiltIn.java | 4 +-
.../freemarker/core/BuiltInsForSequences.java | 62 ++++----
.../freemarker/core/BuiltInsForStringsMisc.java | 2 +-
.../freemarker/core/ContinueInstruction.java | 64 ++++++++
.../freemarker/core/DefaultToExpression.java | 16 +-
src/main/java/freemarker/core/Environment.java | 10 +-
.../java/freemarker/core/IteratorBlock.java | 102 +++++++------
src/main/java/freemarker/core/Macro.java | 3 +-
src/main/java/freemarker/core/SwitchBlock.java | 2 +-
src/main/java/freemarker/core/_CoreAPI.java | 1 +
.../java/freemarker/ext/beans/BeanModel.java | 4 +-
.../java/freemarker/ext/beans/BeansWrapper.java | 69 +++++----
.../ext/beans/BeansWrapperConfiguration.java | 15 ++
.../java/freemarker/ext/dom/ElementModel.java | 3 +-
.../java/freemarker/ext/dom/NodeListModel.java | 22 +--
.../template/DefaultIterableAdapter.java | 2 +-
.../freemarker/template/DefaultListAdapter.java | 2 +-
.../DefaultNonListCollectionAdapter.java | 2 +-
.../DefaultUnassignableIteratorAdapter.java | 52 -------
.../IteratorToTemplateModelIteratorAdapter.java | 50 ++++++
.../freemarker/template/utility/DeepUnwrap.java | 5 +-
src/main/javacc/FTL.jj | 41 +++++
src/manual/en_US/book.xml | 153 ++++++++++++++++---
.../core/BreakAndContinuePlacementTest.java | 75 +++++++++
.../freemarker/core/BreakPlacementTest.java | 68 ---------
.../freemarker/core/ListBreakContinueTest.java | 93 +++++++++++
.../freemarker/template/ConfigurationTest.java | 4 +
.../template/DefaultObjectWrapperTest.java | 11 +-
31 files changed, 665 insertions(+), 296 deletions(-)
----------------------------------------------------------------------
[4/9] incubator-freemarker git commit: FREEMARKER-71: When using
exp?eval, if the expression inside evaluated string throws an exception,
the cause exception of that exception was lost.
Posted by dd...@apache.org.
FREEMARKER-71: When using exp?eval, if the expression inside evaluated string throws an exception, the cause exception of that exception was lost.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/3aa6c509
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/3aa6c509
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/3aa6c509
Branch: refs/heads/2.3
Commit: 3aa6c5098f9d6a1ac14da45edc6b7e6676700847
Parents: 3cf4223
Author: ddekany <dd...@apache.org>
Authored: Sat Sep 16 15:26:52 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Sat Sep 16 15:26:52 2017 +0200
----------------------------------------------------------------------
src/main/java/freemarker/core/BuiltInsForStringsMisc.java | 2 +-
src/manual/en_US/book.xml | 9 +++++++++
2 files changed, 10 insertions(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3aa6c509/src/main/java/freemarker/core/BuiltInsForStringsMisc.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BuiltInsForStringsMisc.java b/src/main/java/freemarker/core/BuiltInsForStringsMisc.java
index eeeb270..4073235 100644
--- a/src/main/java/freemarker/core/BuiltInsForStringsMisc.java
+++ b/src/main/java/freemarker/core/BuiltInsForStringsMisc.java
@@ -97,7 +97,7 @@ class BuiltInsForStringsMisc {
try {
return exp.eval(env);
} catch (TemplateException e) {
- throw new _MiscTemplateException(this, env,
+ throw new _MiscTemplateException(e, this, env,
"Failed to \"?", key, "\" string with this error:\n\n",
MessageUtil.EMBEDDED_MESSAGE_BEGIN,
new _DelayedGetMessageWithoutStackTop(e),
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3aa6c509/src/manual/en_US/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index 809822b..e127a79 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -26943,6 +26943,15 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
the <literal>switch</literal> tag and the first
<literal>case</literal> tag.</para>
</listitem>
+
+ <listitem>
+ <para>Bug fixed (<link
+ xlink:href="https://issues.apache.org/jira/browse/FREEMARKER-71">FREEMARKER-71</link>):
+ When using
+ <literal><replaceable>exp</replaceable>?eval</literal>, if the
+ expression inside evaluated string throws an exception, the
+ cause exception of that exception was lost.</para>
+ </listitem>
</itemizedlist>
</section>
[8/9] incubator-freemarker git commit: (README typo)
Posted by dd...@apache.org.
(README typo)
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/a6711106
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/a6711106
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/a6711106
Branch: refs/heads/2.3
Commit: a671110653a7428a7e041925f8668d5954063dd7
Parents: da570ca
Author: ddekany <dd...@apache.org>
Authored: Sun Sep 17 12:55:05 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Sun Sep 17 12:55:05 2017 +0200
----------------------------------------------------------------------
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/a6711106/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 779df07..edd56b7 100644
--- a/README.md
+++ b/README.md
@@ -161,7 +161,7 @@ apply it to your development environment:
- Java -> Compiler -> Javadoc:
"Malformed Javadoc comments": Error
"Only consider members as visible": Private
- "Validate tag argunebts": true
+ "Validate tag arguments": true
"Missing tag descriptions": Validate @return tags
"Missing Javadoc tags": Ignore
"Missing Javadoc comments": Ignore
[2/9] incubator-freemarker git commit: (Some internal code cleanup)
Posted by dd...@apache.org.
(Some internal code cleanup)
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/78d4a6e2
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/78d4a6e2
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/78d4a6e2
Branch: refs/heads/2.3
Commit: 78d4a6e2690bcd476629f9e0d0045da752f27593
Parents: b185b98
Author: ddekany <dd...@apache.org>
Authored: Fri Sep 1 22:18:29 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Fri Sep 1 22:18:29 2017 +0200
----------------------------------------------------------------------
.../freemarker/core/BuiltInsForSequences.java | 62 ++++++++++----------
.../freemarker/core/DefaultToExpression.java | 16 +++--
src/main/java/freemarker/core/Environment.java | 10 +++-
.../java/freemarker/ext/beans/BeansWrapper.java | 4 +-
.../java/freemarker/ext/dom/ElementModel.java | 3 +-
.../java/freemarker/ext/dom/NodeListModel.java | 17 +++---
.../freemarker/template/utility/DeepUnwrap.java | 5 +-
7 files changed, 64 insertions(+), 53 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/78d4a6e2/src/main/java/freemarker/core/BuiltInsForSequences.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BuiltInsForSequences.java b/src/main/java/freemarker/core/BuiltInsForSequences.java
index eca5fec..8430133 100644
--- a/src/main/java/freemarker/core/BuiltInsForSequences.java
+++ b/src/main/java/freemarker/core/BuiltInsForSequences.java
@@ -251,10 +251,11 @@ class BuiltInsForSequences {
@Override
TemplateModel calculateResult(TemplateSequenceModel tsm)
throws TemplateModelException {
- if (tsm.size() == 0) {
+ int size = tsm.size();
+ if (size == 0) {
return null;
}
- return tsm.get(tsm.size() - 1);
+ return tsm.get(size - 1);
}
}
@@ -388,39 +389,36 @@ class BuiltInsForSequences {
int argCnt = args.size();
checkMethodArgCount(argCnt, 1, 2);
- TemplateModel target = (TemplateModel) args.get(0);
+ TemplateModel searched = (TemplateModel) args.get(0);
int foundAtIdx;
if (argCnt > 1) {
int startIndex = getNumberMethodArg(args, 1).intValue();
- // In 2.3.x only, we prefer TemplateSequenceModel for
- // backward compatibility:
+ // In 2.3, we prefer TemplateSequenceModel for backward compatibility, even if startIndex is 0:
foundAtIdx = m_seq != null
- ? findInSeq(target, startIndex)
- : findInCol(target, startIndex);
+ ? findInSeq(searched, startIndex)
+ : findInCol(searched, startIndex);
} else {
- // In 2.3.x only, we prefer TemplateSequenceModel for
- // backward compatibility:
foundAtIdx = m_seq != null
- ? findInSeq(target)
- : findInCol(target);
+ ? findInSeq(searched)
+ : findInCol(searched);
}
return foundAtIdx == -1 ? Constants.MINUS_ONE : new SimpleNumber(foundAtIdx);
}
- int findInCol(TemplateModel target) throws TemplateModelException {
- return findInCol(target, 0, Integer.MAX_VALUE);
+ int findInCol(TemplateModel searched) throws TemplateModelException {
+ return findInCol(searched, 0, Integer.MAX_VALUE);
}
- protected int findInCol(TemplateModel target, int startIndex)
+ protected int findInCol(TemplateModel searched, int startIndex)
throws TemplateModelException {
- if (m_dir == 1) {
- return findInCol(target, startIndex, Integer.MAX_VALUE);
+ if (findFirst) {
+ return findInCol(searched, startIndex, Integer.MAX_VALUE);
} else {
- return findInCol(target, 0, startIndex);
+ return findInCol(searched, 0, startIndex);
}
}
- protected int findInCol(TemplateModel target,
+ protected int findInCol(TemplateModel searched,
final int allowedRangeStart, final int allowedRangeEnd)
throws TemplateModelException {
if (allowedRangeEnd < 0) return -1;
@@ -434,10 +432,12 @@ class BuiltInsForSequences {
TemplateModel current = it.next();
if (idx >= allowedRangeStart) {
- if (modelsEqual(idx, current, target, m_env)) {
+ if (modelsEqual(idx, current, searched, m_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++;
@@ -445,25 +445,25 @@ class BuiltInsForSequences {
return foundAtIdx;
}
- int findInSeq(TemplateModel target)
+ int findInSeq(TemplateModel searched)
throws TemplateModelException {
final int seqSize = m_seq.size();
final int actualStartIndex;
- if (m_dir == 1) {
+ if (findFirst) {
actualStartIndex = 0;
} else {
actualStartIndex = seqSize - 1;
}
- return findInSeq(target, actualStartIndex, seqSize);
+ return findInSeq(searched, actualStartIndex, seqSize);
}
- private int findInSeq(TemplateModel target, int startIndex)
+ private int findInSeq(TemplateModel searched, int startIndex)
throws TemplateModelException {
int seqSize = m_seq.size();
- if (m_dir == 1) {
+ if (findFirst) {
if (startIndex >= seqSize) {
return -1;
}
@@ -479,13 +479,13 @@ class BuiltInsForSequences {
}
}
- return findInSeq(target, startIndex, seqSize);
+ return findInSeq(searched, startIndex, seqSize);
}
private int findInSeq(
TemplateModel target, int scanStartIndex, int seqSize)
throws TemplateModelException {
- 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;
}
@@ -499,10 +499,10 @@ 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
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/78d4a6e2/src/main/java/freemarker/core/DefaultToExpression.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/DefaultToExpression.java b/src/main/java/freemarker/core/DefaultToExpression.java
index 1406fa6..245525d 100755
--- a/src/main/java/freemarker/core/DefaultToExpression.java
+++ b/src/main/java/freemarker/core/DefaultToExpression.java
@@ -23,18 +23,20 @@ package freemarker.core;
import freemarker.template.SimpleCollection;
import freemarker.template.TemplateCollectionModel;
import freemarker.template.TemplateException;
-import freemarker.template.TemplateHashModelEx;
+import freemarker.template.TemplateHashModelEx2;
import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
import freemarker.template.TemplateScalarModel;
import freemarker.template.TemplateSequenceModel;
+import freemarker.template.utility.Constants;
/** {@code exp!defExp}, {@code (exp)!defExp} and the same two with {@code (exp)!}. */
class DefaultToExpression extends Expression {
private static final TemplateCollectionModel EMPTY_COLLECTION = new SimpleCollection(new java.util.ArrayList(0));
- static private class EmptyStringAndSequence
- implements TemplateScalarModel, TemplateSequenceModel, TemplateHashModelEx {
+ static private class EmptyStringAndSequenceAndHash implements TemplateScalarModel, TemplateSequenceModel,
+ TemplateHashModelEx2 {
public String getAsString() {
return "";
}
@@ -56,10 +58,12 @@ class DefaultToExpression extends Expression {
public TemplateCollectionModel values() {
return EMPTY_COLLECTION;
}
-
+ public KeyValuePairIterator keyValuePairIterator() throws TemplateModelException {
+ return Constants.EMPTY_KEY_VALUE_PAIR_ITERATOR;
+ }
}
- static final TemplateModel EMPTY_STRING_AND_SEQUENCE = new EmptyStringAndSequence();
+ static final TemplateModel EMPTY_STRING_AND_SEQUENCE_AND_HASH = new EmptyStringAndSequenceAndHash();
private final Expression lho, rho;
@@ -85,7 +89,7 @@ class DefaultToExpression extends Expression {
}
if (left != null) return left;
- else if (rho == null) return EMPTY_STRING_AND_SEQUENCE;
+ else if (rho == null) return EMPTY_STRING_AND_SEQUENCE_AND_HASH;
else return rho.eval(env);
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/78d4a6e2/src/main/java/freemarker/core/Environment.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/Environment.java b/src/main/java/freemarker/core/Environment.java
index 565dcd4..8b03afd 100644
--- a/src/main/java/freemarker/core/Environment.java
+++ b/src/main/java/freemarker/core/Environment.java
@@ -831,8 +831,11 @@ public final class Environment extends Configurable {
}
}
TemplateSequenceModel children = node.getChildNodes();
- if (children == null) return;
- for (int i = 0; i < children.size(); i++) {
+ if (children == null) {
+ return;
+ }
+ int size = children.size();
+ for (int i = 0; i < size; i++) {
TemplateNodeModel child = (TemplateNodeModel) children.get(i);
if (child != null) {
invokeNodeHandlerFor(child, namespaces);
@@ -2359,7 +2362,8 @@ public final class Environment extends Configurable {
throws TemplateException {
TemplateModel result = null;
int i;
- for (i = startIndex; i < nodeNamespaces.size(); i++) {
+ int size = nodeNamespaces.size();
+ for (i = startIndex; i < size; i++) {
Namespace ns = null;
try {
ns = (Namespace) nodeNamespaces.get(i);
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/78d4a6e2/src/main/java/freemarker/ext/beans/BeansWrapper.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/ext/beans/BeansWrapper.java b/src/main/java/freemarker/ext/beans/BeansWrapper.java
index ea9158d..0195234 100644
--- a/src/main/java/freemarker/ext/beans/BeansWrapper.java
+++ b/src/main/java/freemarker/ext/beans/BeansWrapper.java
@@ -1301,10 +1301,10 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
recursionStops = new IdentityHashMap<Object, Object>();
}
Class<?> componentType = arrayClass.getComponentType();
- Object array = Array.newInstance(componentType, seq.size());
+ final int size = seq.size();
+ Object array = Array.newInstance(componentType, size);
recursionStops.put(seq, array);
try {
- final int size = seq.size();
for (int i = 0; i < size; i++) {
final TemplateModel seqItem = seq.get(i);
Object val = tryUnwrapTo(seqItem, componentType, 0, recursionStops);
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/78d4a6e2/src/main/java/freemarker/ext/dom/ElementModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/ext/dom/ElementModel.java b/src/main/java/freemarker/ext/dom/ElementModel.java
index 20481a8..3f140c1 100644
--- a/src/main/java/freemarker/ext/dom/ElementModel.java
+++ b/src/main/java/freemarker/ext/dom/ElementModel.java
@@ -60,7 +60,8 @@ class ElementModel extends NodeModel implements TemplateScalarModel {
if (key.equals("*")) {
NodeListModel ns = new NodeListModel(this);
TemplateSequenceModel children = getChildNodes();
- for (int i = 0; i < children.size(); i++) {
+ int size = children.size();
+ for (int i = 0; i < size; i++) {
NodeModel child = (NodeModel) children.get(i);
if (child.node.getNodeType() == Node.ELEMENT_NODE) {
ns.add(child);
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/78d4a6e2/src/main/java/freemarker/ext/dom/NodeListModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/ext/dom/NodeListModel.java b/src/main/java/freemarker/ext/dom/NodeListModel.java
index 46644d9..ce83d8c 100644
--- a/src/main/java/freemarker/ext/dom/NodeListModel.java
+++ b/src/main/java/freemarker/ext/dom/NodeListModel.java
@@ -119,7 +119,8 @@ class NodeListModel extends SimpleSequence implements TemplateHashModel, _Unexpe
}
public TemplateModel get(String key) throws TemplateModelException {
- if (size() == 1) {
+ int size = size();
+ if (size == 1) {
NodeModel nm = (NodeModel) get(0);
return nm.get(key);
}
@@ -128,7 +129,7 @@ class NodeListModel extends SimpleSequence implements TemplateHashModel, _Unexpe
|| key.equals(AtAtKey.NESTED_MARKUP.getKey())
|| key.equals(AtAtKey.TEXT.getKey())) {
StringBuilder result = new StringBuilder();
- for (int i = 0; i < size(); i++) {
+ for (int i = 0; i < size; i++) {
NodeModel nm = (NodeModel) get(i);
TemplateScalarModel textModel = (TemplateScalarModel) nm.get(key);
result.append(textModel.getAsString());
@@ -139,8 +140,8 @@ class NodeListModel extends SimpleSequence implements TemplateHashModel, _Unexpe
if (AtAtKey.containsKey(key)) {
throw new TemplateModelException(
"\"" + key + "\" is only applicable to a single XML node, but it was applied on "
- + (size() != 0
- ? size() + " XML nodes (multiple matches)."
+ + (size != 0
+ ? size + " XML nodes (multiple matches)."
: "an empty list of XML nodes (no matches)."));
} else {
throw new TemplateModelException("Unsupported @@ key: " + key);
@@ -152,13 +153,13 @@ class NodeListModel extends SimpleSequence implements TemplateHashModel, _Unexpe
&& (DomStringUtil.isXMLNameLike(key, 1) || key.equals("@@") || key.equals("@*"))))
|| key.equals("*") || key.equals("**")) {
NodeListModel result = new NodeListModel(contextNode);
- for (int i = 0; i < size(); i++) {
+ for (int i = 0; i < size; i++) {
NodeModel nm = (NodeModel) get(i);
if (nm instanceof ElementModel) {
TemplateSequenceModel tsm = (TemplateSequenceModel) ((ElementModel) nm).get(key);
if (tsm != null) {
- int size = tsm.size();
- for (int j = 0; j < size; j++) {
+ int tsmSize = tsm.size();
+ for (int j = 0; j < tsmSize; j++) {
result.add(tsm.get(j));
}
}
@@ -171,7 +172,7 @@ class NodeListModel extends SimpleSequence implements TemplateHashModel, _Unexpe
}
XPathSupport xps = getXPathSupport();
if (xps != null) {
- Object context = (size() == 0) ? null : rawNodeList();
+ Object context = (size == 0) ? null : rawNodeList();
return xps.executeQuery(context, key);
} else {
throw new TemplateModelException(
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/78d4a6e2/src/main/java/freemarker/template/utility/DeepUnwrap.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/template/utility/DeepUnwrap.java b/src/main/java/freemarker/template/utility/DeepUnwrap.java
index 586787f..85b032c 100644
--- a/src/main/java/freemarker/template/utility/DeepUnwrap.java
+++ b/src/main/java/freemarker/template/utility/DeepUnwrap.java
@@ -129,8 +129,9 @@ public class DeepUnwrap {
}
if (model instanceof TemplateSequenceModel) {
TemplateSequenceModel seq = (TemplateSequenceModel) model;
- ArrayList list = new ArrayList(seq.size());
- for (int i = 0; i < seq.size(); ++i) {
+ int size = seq.size();
+ ArrayList list = new ArrayList(size);
+ for (int i = 0; i < size; ++i) {
list.add(unwrap(seq.get(i), nullModel, permissive));
}
return list;
[3/9] incubator-freemarker git commit: Previously accidentally
uncommitted lines.
Posted by dd...@apache.org.
Previously accidentally uncommitted lines.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/3cf42234
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/3cf42234
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/3cf42234
Branch: refs/heads/2.3
Commit: 3cf4223470b9ed9c4219f395b1ad78c99c6d1309
Parents: 78d4a6e
Author: ddekany <dd...@apache.org>
Authored: Fri Sep 1 23:43:05 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Fri Sep 1 23:43:05 2017 +0200
----------------------------------------------------------------------
src/main/java/freemarker/core/BuiltIn.java | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3cf42234/src/main/java/freemarker/core/BuiltIn.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BuiltIn.java b/src/main/java/freemarker/core/BuiltIn.java
index 70a94bc..b4e3239 100644
--- a/src/main/java/freemarker/core/BuiltIn.java
+++ b/src/main/java/freemarker/core/BuiltIn.java
@@ -260,8 +260,8 @@ abstract class BuiltIn extends Expression implements Cloneable {
putBI("remove_beginning", "removeBeginning", new BuiltInsForStringsBasic.remove_beginningBI());
putBI("rtf", new BuiltInsForStringsEncoding.rtfBI());
putBI("seq_contains", "seqContains", new seq_containsBI());
- putBI("seq_index_of", "seqIndexOf", new seq_index_ofBI(1));
- putBI("seq_last_index_of", "seqLastIndexOf", new seq_index_ofBI(-1));
+ putBI("seq_index_of", "seqIndexOf", new seq_index_ofBI(true));
+ putBI("seq_last_index_of", "seqLastIndexOf", new seq_index_ofBI(false));
putBI("short", new shortBI());
putBI("size", new BuiltInsForMultipleTypes.sizeBI());
putBI("sort_by", "sortBy", new sort_byBI());
[7/9] incubator-freemarker git commit: Added the continue directive,
which can be used inside a list to skip to the next iteration
(similarly as in Java).
Posted by dd...@apache.org.
Added the continue directive, which can be used inside a list to skip to the next iteration (similarly as in Java).
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/da570caa
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/da570caa
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/da570caa
Branch: refs/heads/2.3
Commit: da570caa85c9b17ece822fe72fcce22502f57b54
Parents: 3dfa8ce
Author: ddekany <dd...@apache.org>
Authored: Sun Sep 17 12:46:02 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Sun Sep 17 12:46:02 2017 +0200
----------------------------------------------------------------------
.../java/freemarker/core/BreakInstruction.java | 8 +-
.../core/BreakOrContinueException.java | 12 ++
.../freemarker/core/ContinueInstruction.java | 64 ++++++++++
.../java/freemarker/core/IteratorBlock.java | 102 ++++++++--------
src/main/java/freemarker/core/SwitchBlock.java | 2 +-
src/main/java/freemarker/core/_CoreAPI.java | 1 +
src/main/javacc/FTL.jj | 41 +++++++
src/manual/en_US/book.xml | 116 ++++++++++++++++---
.../core/BreakAndContinuePlacementTest.java | 75 ++++++++++++
.../freemarker/core/BreakPlacementTest.java | 68 -----------
.../freemarker/core/ListBreakContinueTest.java | 93 +++++++++++++++
11 files changed, 443 insertions(+), 139 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/da570caa/src/main/java/freemarker/core/BreakInstruction.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BreakInstruction.java b/src/main/java/freemarker/core/BreakInstruction.java
index b41f05b..e18f6d0 100644
--- a/src/main/java/freemarker/core/BreakInstruction.java
+++ b/src/main/java/freemarker/core/BreakInstruction.java
@@ -26,7 +26,7 @@ final class BreakInstruction extends TemplateElement {
@Override
TemplateElement[] accept(Environment env) {
- throw Break.INSTANCE;
+ throw BreakOrContinueException.BREAK_INSTANCE;
}
@Override
@@ -54,12 +54,6 @@ final class BreakInstruction extends TemplateElement {
throw new IndexOutOfBoundsException();
}
- static class Break extends RuntimeException {
- static final Break INSTANCE = new Break();
- private Break() {
- }
- }
-
@Override
boolean isNestedBlockRepeater() {
return false;
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/da570caa/src/main/java/freemarker/core/BreakOrContinueException.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/BreakOrContinueException.java b/src/main/java/freemarker/core/BreakOrContinueException.java
new file mode 100644
index 0000000..86bfbd8
--- /dev/null
+++ b/src/main/java/freemarker/core/BreakOrContinueException.java
@@ -0,0 +1,12 @@
+package freemarker.core;
+
+/**
+ * Used for implementing #break and #continue.
+ */
+class BreakOrContinueException extends RuntimeException {
+
+ static final BreakOrContinueException BREAK_INSTANCE = new BreakOrContinueException();
+ static final BreakOrContinueException CONTINUE_INSTANCE = new BreakOrContinueException();
+
+ private BreakOrContinueException() { }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/da570caa/src/main/java/freemarker/core/ContinueInstruction.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/ContinueInstruction.java b/src/main/java/freemarker/core/ContinueInstruction.java
new file mode 100644
index 0000000..21ff84f
--- /dev/null
+++ b/src/main/java/freemarker/core/ContinueInstruction.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 freemarker.core;
+
+/**
+ * Represents a <break> instruction to break out of a loop.
+ */
+final class ContinueInstruction extends TemplateElement {
+
+ @Override
+ TemplateElement[] accept(Environment env) {
+ throw BreakOrContinueException.CONTINUE_INSTANCE;
+ }
+
+ @Override
+ protected String dump(boolean canonical) {
+ return canonical ? "<" + getNodeTypeSymbol() + "/>" : getNodeTypeSymbol();
+ }
+
+ @Override
+ String getNodeTypeSymbol() {
+ return "#continue";
+ }
+
+ @Override
+ int getParameterCount() {
+ return 0;
+ }
+
+ @Override
+ Object getParameterValue(int idx) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ @Override
+ ParameterRole getParameterRole(int idx) {
+ throw new IndexOutOfBoundsException();
+ }
+
+ @Override
+ boolean isNestedBlockRepeater() {
+ return false;
+ }
+
+}
+
+
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/da570caa/src/main/java/freemarker/core/IteratorBlock.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/IteratorBlock.java b/src/main/java/freemarker/core/IteratorBlock.java
index 0d9fcec..83c0b22 100644
--- a/src/main/java/freemarker/core/IteratorBlock.java
+++ b/src/main/java/freemarker/core/IteratorBlock.java
@@ -284,16 +284,18 @@ final class IteratorBlock extends TemplateElement {
listNotEmpty = iterModel.hasNext();
if (listNotEmpty) {
if (loopVarName != null) {
- try {
- do {
+ listLoop: do {
loopVar = iterModel.next();
hasNext = iterModel.hasNext();
- env.visit(childBuffer);
+ try {
+ env.visit(childBuffer);
+ } catch (BreakOrContinueException br) {
+ if (br == BreakOrContinueException.BREAK_INSTANCE) {
+ break listLoop;
+ }
+ }
index++;
} while (hasNext);
- } catch (BreakInstruction.Break br) {
- // Silently exit loop
- }
openedIterator = null;
} else {
// We must reuse this later, because TemplateCollectionModel-s that wrap an Iterator only
@@ -308,15 +310,17 @@ final class IteratorBlock extends TemplateElement {
listNotEmpty = size != 0;
if (listNotEmpty) {
if (loopVarName != null) {
- try {
- for (index = 0; index < size; index++) {
+ listLoop: for (index = 0; index < size; index++) {
loopVar = seqModel.get(index);
hasNext = (size > index + 1);
- env.visit(childBuffer);
+ try {
+ env.visit(childBuffer);
+ } catch (BreakOrContinueException br) {
+ if (br == BreakOrContinueException.BREAK_INSTANCE) {
+ break listLoop;
+ }
+ }
}
- } catch (BreakInstruction.Break br) {
- // Silently exit loop
- }
} else {
env.visit(childBuffer);
}
@@ -329,7 +333,7 @@ final class IteratorBlock extends TemplateElement {
}
try {
env.visit(childBuffer);
- } catch (BreakInstruction.Break br) {
+ } catch (BreakOrContinueException br) {
// Silently exit "loop"
}
} else if (listedValue instanceof TemplateHashModelEx
@@ -359,18 +363,20 @@ final class IteratorBlock extends TemplateElement {
hashNotEmpty = kvpIter.hasNext();
if (hashNotEmpty) {
if (loopVarName != null) {
- try {
- do {
- KeyValuePair kvp = kvpIter.next();
- loopVar = kvp.getKey();
- loopVar2 = kvp.getValue();
- hasNext = kvpIter.hasNext();
+ listLoop: do {
+ KeyValuePair kvp = kvpIter.next();
+ loopVar = kvp.getKey();
+ loopVar2 = kvp.getValue();
+ hasNext = kvpIter.hasNext();
+ try {
env.visit(childBuffer);
- index++;
- } while (hasNext);
- } catch (BreakInstruction.Break br) {
- // Silently exit loop
- }
+ } catch (BreakOrContinueException br) {
+ if (br == BreakOrContinueException.BREAK_INSTANCE) {
+ break listLoop;
+ }
+ }
+ index++;
+ } while (hasNext);
openedIterator = null;
} else {
// We will reuse this at the #iterms
@@ -383,30 +389,32 @@ final class IteratorBlock extends TemplateElement {
hashNotEmpty = keysIter.hasNext();
if (hashNotEmpty) {
if (loopVarName != null) {
- try {
- do {
- loopVar = keysIter.next();
- if (!(loopVar instanceof TemplateScalarModel)) {
- throw new NonStringException(env,
- new _ErrorDescriptionBuilder(
- "When listing key-value pairs of traditional hash "
- + "implementations, all keys must be strings, but one of them "
- + "was ",
- new _DelayedAOrAn(new _DelayedFTLTypeDescription(loopVar)), "."
- ).tip("The listed value's TemplateModel class was ",
- new _DelayedShortClassName(listedValue.getClass()),
- ", which doesn't implement ",
- new _DelayedShortClassName(TemplateHashModelEx2.class),
- ", which leads to this restriction."));
- }
- loopVar2 = listedHash.get(((TemplateScalarModel) loopVar).getAsString());
- hasNext = keysIter.hasNext();
+ listLoop: do {
+ loopVar = keysIter.next();
+ if (!(loopVar instanceof TemplateScalarModel)) {
+ throw new NonStringException(env,
+ new _ErrorDescriptionBuilder(
+ "When listing key-value pairs of traditional hash "
+ + "implementations, all keys must be strings, but one of them "
+ + "was ",
+ new _DelayedAOrAn(new _DelayedFTLTypeDescription(loopVar)), "."
+ ).tip("The listed value's TemplateModel class was ",
+ new _DelayedShortClassName(listedValue.getClass()),
+ ", which doesn't implement ",
+ new _DelayedShortClassName(TemplateHashModelEx2.class),
+ ", which leads to this restriction."));
+ }
+ loopVar2 = listedHash.get(((TemplateScalarModel) loopVar).getAsString());
+ hasNext = keysIter.hasNext();
+ try {
env.visit(childBuffer);
- index++;
- } while (hasNext);
- } catch (BreakInstruction.Break br) {
- // Silently exit loop
- }
+ } catch (BreakOrContinueException br) {
+ if (br == BreakOrContinueException.BREAK_INSTANCE) {
+ break listLoop;
+ }
+ }
+ index++;
+ } while (hasNext);
} else {
env.visit(childBuffer);
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/da570caa/src/main/java/freemarker/core/SwitchBlock.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/SwitchBlock.java b/src/main/java/freemarker/core/SwitchBlock.java
index 3f8a320..19c6a3e 100644
--- a/src/main/java/freemarker/core/SwitchBlock.java
+++ b/src/main/java/freemarker/core/SwitchBlock.java
@@ -86,7 +86,7 @@ final class SwitchBlock extends TemplateElement {
if (!processedCase && defaultCase != null) {
env.visit(defaultCase);
}
- } catch (BreakInstruction.Break br) {}
+ } catch (BreakOrContinueException br) {}
return null;
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/da570caa/src/main/java/freemarker/core/_CoreAPI.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/core/_CoreAPI.java b/src/main/java/freemarker/core/_CoreAPI.java
index 1f10f81..3491568 100644
--- a/src/main/java/freemarker/core/_CoreAPI.java
+++ b/src/main/java/freemarker/core/_CoreAPI.java
@@ -78,6 +78,7 @@ public class _CoreAPI {
addName(allNames, lcNames, ccNames, "case");
addName(allNames, lcNames, ccNames, "comment");
addName(allNames, lcNames, ccNames, "compress");
+ addName(allNames, lcNames, ccNames, "continue");
addName(allNames, lcNames, ccNames, "default");
addName(allNames, lcNames, ccNames, "else");
addName(allNames, lcNames, ccNames, "elseif", "elseIf");
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/da570caa/src/main/javacc/FTL.jj
----------------------------------------------------------------------
diff --git a/src/main/javacc/FTL.jj b/src/main/javacc/FTL.jj
index b7bdd48..ddbf78f 100644
--- a/src/main/javacc/FTL.jj
+++ b/src/main/javacc/FTL.jj
@@ -85,6 +85,11 @@ public class FMParser {
*/
private int breakableDirectiveNesting;
+ /**
+ * Keeps track of the nesting depth of directives that support #continue.
+ */
+ private int continuableDirectiveNesting;
+
private boolean inMacro, inFunction;
private LinkedList escapes = new LinkedList();
private int mixedContentNesting; // for stripText
@@ -1019,6 +1024,8 @@ TOKEN:
|
<BREAK : <START_TAG> "break" <CLOSE_TAG2>> { strictSyntaxCheck(matchedToken, DEFAULT); }
|
+ <CONTINUE : <START_TAG> "continue" <CLOSE_TAG2>> { strictSyntaxCheck(matchedToken, DEFAULT); }
+ |
<SIMPLE_RETURN : <START_TAG> "return" <CLOSE_TAG2>> { strictSyntaxCheck(matchedToken, DEFAULT); }
|
<HALT : <START_TAG> "stop" <CLOSE_TAG2>> { strictSyntaxCheck(matchedToken, DEFAULT); }
@@ -2580,6 +2587,7 @@ TemplateElement List() :
if (loopVar != null) {
iterCtx.loopVarName = loopVar.image;
breakableDirectiveNesting++;
+ continuableDirectiveNesting++;
if (loopVar2 != null) {
iterCtx.loopVar2Name = loopVar2.image;
iterCtx.hashListing = true;
@@ -2596,6 +2604,7 @@ TemplateElement List() :
{
if (loopVar != null) {
breakableDirectiveNesting--;
+ continuableDirectiveNesting--;
} else if (iterCtx.kind != ITERATOR_BLOCK_KIND_ITEMS) {
throw new ParseException(
"#list must have either \"as loopVar\" parameter or nested #items that belongs to it.",
@@ -2660,6 +2669,7 @@ IteratorBlock ForEach() :
iterCtx.loopVarName = loopVar.image;
iterCtx.kind = ITERATOR_BLOCK_KIND_FOREACH;
breakableDirectiveNesting++;
+ continuableDirectiveNesting++;
}
children = MixedContentElements()
@@ -2667,6 +2677,7 @@ IteratorBlock ForEach() :
end = <END_FOREACH>
{
breakableDirectiveNesting--;
+ continuableDirectiveNesting--;
popIteratorBlockContext();
IteratorBlock result = new IteratorBlock(exp, loopVar.image, null, children, false, true);
@@ -2718,6 +2729,7 @@ Items Items() :
}
breakableDirectiveNesting++;
+ continuableDirectiveNesting++;
}
children = MixedContentElements()
@@ -2725,6 +2737,7 @@ Items Items() :
end = <END_ITEMS>
{
breakableDirectiveNesting--;
+ continuableDirectiveNesting--;
iterCtx.loopVarName = null;
iterCtx.loopVar2Name = null;
@@ -2851,6 +2864,27 @@ BreakInstruction Break() :
}
/**
+ * Production used to skip an iteration in a loop.
+ */
+ContinueInstruction Continue() :
+{
+ Token start;
+}
+{
+ start = <CONTINUE>
+ {
+ if (continuableDirectiveNesting < 1) {
+ throw new ParseException(start.image + " must be nested inside a directive that supports it: "
+ + " #list with \"as\", #items (or the deprecated " + forEachDirectiveSymbol() + ")",
+ template, start);
+ }
+ ContinueInstruction result = new ContinueInstruction();
+ result.setLocation(template, start, start);
+ return result;
+ }
+}
+
+/**
* Production used to jump out of a macro.
* The stop instruction terminates the rendering of the template.
*/
@@ -3221,6 +3255,7 @@ Macro Macro() :
Expression defValue = null;
List lastIteratorBlockContexts;
int lastBreakableDirectiveNesting;
+ int lastContiunableDirectiveNesting;
TemplateElements children;
boolean isFunction = false, hasDefaults = false;
boolean isCatchAll = false;
@@ -3292,9 +3327,12 @@ Macro Macro() :
iteratorBlockContexts = null;
if (incompatibleImprovements >= _TemplateAPI.VERSION_INT_2_3_23) {
lastBreakableDirectiveNesting = breakableDirectiveNesting;
+ lastContiunableDirectiveNesting = continuableDirectiveNesting;
breakableDirectiveNesting = 0;
+ continuableDirectiveNesting = 0;
} else {
lastBreakableDirectiveNesting = 0; // Just to prevent uninitialized local variable error later
+ lastContiunableDirectiveNesting = 0;
}
}
children = MixedContentElements()
@@ -3313,6 +3351,7 @@ Macro Macro() :
iteratorBlockContexts = lastIteratorBlockContexts;
if (incompatibleImprovements >= _TemplateAPI.VERSION_INT_2_3_23) {
breakableDirectiveNesting = lastBreakableDirectiveNesting;
+ continuableDirectiveNesting = lastContiunableDirectiveNesting;
}
inMacro = inFunction = false;
@@ -3937,6 +3976,8 @@ TemplateElement FreemarkerDirective() :
|
tp = Break()
|
+ tp = Continue()
+ |
tp = Return()
|
tp = Stop()
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/da570caa/src/manual/en_US/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index f1dc57a..21c3479 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -20428,7 +20428,7 @@ All rights reserved.</emphasis></programlisting>
</section>
<section xml:id="ref_directive_list">
- <title>list, else, items, sep, break</title>
+ <title>list, else, items, sep, break, continue</title>
<anchor xml:id="ref.directive.list"/>
@@ -20522,7 +20522,7 @@ All rights reserved.</emphasis></programlisting>
<section>
<title>Description</title>
- <section>
+ <section xml:id="ref_list_simple">
<title>Simplest form</title>
<para>Assuming <literal>users</literal> contains the
@@ -20569,7 +20569,7 @@ All rights reserved.</emphasis></programlisting>
<literal>Map</literal> objects can be listed.</para>
</section>
- <section>
+ <section xml:id="ref_list_else">
<title>else directive</title>
<anchor xml:id="ref.directive.list.else"/>
@@ -20609,7 +20609,7 @@ All rights reserved.</emphasis></programlisting>
included template.</para>
</section>
- <section>
+ <section xml:id="ref_list_items">
<title>items directive</title>
<anchor xml:id="ref.directive.items"/>
@@ -20712,7 +20712,7 @@ All rights reserved.</emphasis></programlisting>
</itemizedlist>
</section>
- <section>
+ <section xml:id="ref_list_sep">
<title>sep directive</title>
<anchor xml:id="ref.directive.sep"/>
@@ -20765,7 +20765,7 @@ All rights reserved.</emphasis></programlisting>
will be called from).</para>
</section>
- <section>
+ <section xml:id="ref_list_break">
<title>break directive</title>
<anchor xml:id="ref.directive.list.break"/>
@@ -20792,24 +20792,100 @@ All rights reserved.</emphasis></programlisting>
anywhere inside <literal>list</literal> as far as it has
<literal>as <replaceable>item</replaceable></literal> parameter,
otherwise it can be placed anywhere inside the
- <literal>items</literal> directive. If the
- <literal>break</literal> is inside <literal>items</literal>, it
- will only exit from <literal>items</literal>, not from
- <literal>list</literal>. In general, <literal>break</literal> will
- only exit from the directive whose body is called for each item,
- and can only be placed inside such directive. So for example can't
- use <literal>break</literal> inside <literal>list</literal>'s
+ <literal>items</literal> directive. However, it's strongly
+ recommended to place it either before or after all the other
+ things that you do inside the iteration. Otherwise it's easy to
+ end up with unclosed elements in the output, or otherwise make the
+ template harder to understand. Especially, avoid breaking out from
+ the nested content of custom directives (like <literal><#list
+ ...>...<@foo>...<#break>...</@foo>...</#list></literal>),
+ as the author of the directive may not expect that the closing tag
+ (<literal></@foo></literal>) is never executed.</para>
+
+ <para>If the <literal>break</literal> is inside
+ <literal>items</literal>, it will only exit from
+ <literal>items</literal>, not from <literal>list</literal>. In
+ general, <literal>break</literal> will only exit from the
+ directive whose body is called for each item, and can only be
+ placed inside such directive. So for example can't use
+ <literal>break</literal> inside <literal>list</literal>'s
<literal>else</literal> section, unless there's the
<literal>list</literal> is nested into another
<literal>break</literal>-able directive.</para>
+ <para>Using <literal>break</literal> together with
+ <literal>sep</literal> is generally a bad idea, as
+ <literal>sep</literal> can't know if you will skip the rest of
+ items with <literal>break</literal>, and then you end up with a
+ separator after the item printed last.</para>
+
<para>Just like <literal>else</literal> and
<literal>items</literal>, <literal>break</literal> must be
literally inside body of the directive to break out from, and
can't be moved out into a macro or included template.</para>
</section>
- <section>
+ <section xml:id="ref_list_continue">
+ <title>continue directive</title>
+
+ <anchor xml:id="ref.directive.list.continue"/>
+
+ <indexterm>
+ <primary>continue directive</primary>
+ </indexterm>
+
+ <note>
+ <para>The <literal>continue</literal> directive exists since
+ FreeMarker 2.3.27</para>
+ </note>
+
+ <para>You can skip the rest of the iteration body (the section
+ until the <literal></#list></literal> or
+ <literal></#items></literal> tag) with the
+ <literal>continue</literal> directive, then FreeMarker will
+ continue with the next item. For example:</para>
+
+ <programlisting role="template"><#list 1..5 as x>
+ <#if x == 3>
+ <#continue>
+ </#if>
+ ${x}
+</#list></programlisting>
+
+ <programlisting role="output"> 1
+ 2
+ 4
+ 5</programlisting>
+
+ <para>The <literal>continue</literal> directives can be placed
+ anywhere inside <literal>list</literal> as far as it has
+ <literal>as <replaceable>item</replaceable></literal> parameter,
+ otherwise it can be placed anywhere inside the
+ <literal>items</literal> directive. However, it's strongly
+ recommended to place it before all the other things you do inside
+ the iteration. Otherwise it's easy to end up with unclosed
+ elements in the output, or otherwise make the template harder to
+ understand. Especially, avoid breaking out from the nested content
+ of custom directives (like <literal><#list
+ ...>...<@foo>...<#continue>...</@foo>...</#list></literal>),
+ as the author of the directive may not expect that the closing tag
+ (<literal></@foo></literal>) is never executed.</para>
+
+ <para>When you call <literal>continue</literal>, the
+ <literal>sep</literal> directive will not be executed for that
+ iteration. Using <literal>continue</literal> together with
+ <literal>sep</literal> is generally a bad idea, as
+ <literal>sep</literal> can't know if you will skip the rest of the
+ items, and then you end up with a separator after the item printed
+ last.</para>
+
+ <para>Just like <literal>break</literal>,
+ <literal>continue</literal> must be literally inside body of the
+ directive whose iteration need to be <quote>continued</quote>, and
+ can't be moved out into a macro or included template.</para>
+ </section>
+
+ <section xml:id="ref_list_accessing_state">
<title>Accessing iteration state</title>
<indexterm>
@@ -20876,7 +20952,7 @@ All rights reserved.</emphasis></programlisting>
1}</literal>.</para>
</section>
- <section>
+ <section xml:id="ref_list_nesting">
<title>Nesting loops into each other</title>
<para>Naturally, <literal>list</literal> or
@@ -20919,7 +20995,7 @@ All rights reserved.</emphasis></programlisting>
Outer again: 2</programlisting>
</section>
- <section>
+ <section xml:id="ref_list_java_notes">
<title>Notes for Java programmers</title>
<para><phrase role="forProgrammers">If classic compatible mode
@@ -26913,6 +26989,14 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
<itemizedlist>
<listitem>
+ <para>Added the <link
+ linkend="ref.directive.list.continue"><literal>continue</literal>
+ directive</link>, which can be used inside a
+ <literal>list</literal> to skip to the next iteration (similarly
+ as in Java).</para>
+ </listitem>
+
+ <listitem>
<para>Added alternative syntaxes for the
<literal>&&</literal> (logical <quote>and</quote>)
operator: <literal>\and</literal> and
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/da570caa/src/test/java/freemarker/core/BreakAndContinuePlacementTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/BreakAndContinuePlacementTest.java b/src/test/java/freemarker/core/BreakAndContinuePlacementTest.java
new file mode 100644
index 0000000..e4ef01c
--- /dev/null
+++ b/src/test/java/freemarker/core/BreakAndContinuePlacementTest.java
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package freemarker.core;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import freemarker.template.Configuration;
+import freemarker.template.TemplateException;
+import freemarker.test.TemplateTest;
+
+public class BreakAndContinuePlacementTest extends TemplateTest {
+
+ private static final String BREAK_NESTING_ERROR_MESSAGE_PART = "<#break> must be nested";
+ private static final String CONTINUE_NESTING_ERROR_MESSAGE_PART = "<#continue> must be nested";
+
+ @Test
+ public void testValidPlacements() throws IOException, TemplateException {
+ assertOutput("<#assign x = 1><#switch x><#case 1>one<#break><#case 2>two</#switch>", "one");
+ assertOutput("<#list 1..2 as x>${x}<#break></#list>", "1");
+ assertOutput("<#list 1..2 as x>${x}<#continue></#list>", "12");
+ assertOutput("<#list 1..2>[<#items as x>${x}<#break></#items>]</#list>", "[1]");
+ assertOutput("<#list 1..2 as x>${x}<#list 1..3>B<#break>E<#items as y></#items></#list>E</#list>.", "1B.");
+ assertOutput("<#list 1..2 as x>${x}<#list 3..4 as x>${x}<#break></#list>;</#list>", "13;23;");
+ assertOutput("<#list [1..2, 3..4, [], 5..6] as xs>[<#list xs as x>${x}<#else><#break></#list>]</#list>.",
+ "[12][34][.");
+ assertOutput("<#list [1..2, 3..4, [], 5..6] as xs>"
+ + "<#list xs>[<#items as x>${x}</#items>]<#else><#break></#list>"
+ + "</#list>.",
+ "[12][34].");
+ assertOutput("<#forEach x in 1..2>${x}<#break></#forEach>", "1");
+ assertOutput("<#forEach x in 1..2>${x}<#continue></#forEach>", "12");
+ assertOutput("<#switch 1><#case 1>1<#break></#switch>", "1");
+ }
+
+ @Test
+ public void testInvalidPlacements() throws IOException, TemplateException {
+ assertErrorContains("<#break>", BREAK_NESTING_ERROR_MESSAGE_PART);
+ assertErrorContains("<#continue>", CONTINUE_NESTING_ERROR_MESSAGE_PART);
+ assertErrorContains("<#switch 1><#case 1>1<#continue></#switch>", CONTINUE_NESTING_ERROR_MESSAGE_PART);
+ assertErrorContains("<#list 1..2 as x>${x}</#list><#break>", BREAK_NESTING_ERROR_MESSAGE_PART);
+ assertErrorContains("<#if false><#break></#if>", BREAK_NESTING_ERROR_MESSAGE_PART);
+ assertErrorContains("<#list xs><#break></#list>", BREAK_NESTING_ERROR_MESSAGE_PART);
+ assertErrorContains("<#list 1..2 as x>${x}<#else><#break></#list>", BREAK_NESTING_ERROR_MESSAGE_PART);
+ }
+
+ @Test
+ public void testInvalidPlacementMacroLoophole() throws IOException, TemplateException {
+ final String ftl = "<#list 1..2 as x>${x}<#macro m><#break></#macro></#list>";
+ getConfiguration().setIncompatibleImprovements(Configuration.VERSION_2_3_22);
+ assertOutput(ftl, "12");
+ getConfiguration().setIncompatibleImprovements(Configuration.VERSION_2_3_23);
+ assertErrorContains(ftl, BREAK_NESTING_ERROR_MESSAGE_PART);
+ assertErrorContains(ftl.replaceAll("#break", "#continue"), CONTINUE_NESTING_ERROR_MESSAGE_PART);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/da570caa/src/test/java/freemarker/core/BreakPlacementTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/BreakPlacementTest.java b/src/test/java/freemarker/core/BreakPlacementTest.java
deleted file mode 100644
index 8844956..0000000
--- a/src/test/java/freemarker/core/BreakPlacementTest.java
+++ /dev/null
@@ -1,68 +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 freemarker.core;
-
-import java.io.IOException;
-
-import org.junit.Test;
-
-import freemarker.template.Configuration;
-import freemarker.template.TemplateException;
-import freemarker.test.TemplateTest;
-
-public class BreakPlacementTest extends TemplateTest {
-
- private static final String BREAK_NESTING_ERROR_MESSAGE_PART = "<#break> must be nested";
-
- @Test
- public void testValidPlacements() throws IOException, TemplateException {
- assertOutput("<#assign x = 1><#switch x><#case 1>one<#break><#case 2>two</#switch>", "one");
- assertOutput("<#list 1..2 as x>${x}<#break></#list>", "1");
- assertOutput("<#list 1..2>[<#items as x>${x}<#break></#items>]</#list>", "[1]");
- assertOutput("<#list 1..2 as x>${x}<#list 1..3>B<#break>E<#items as y></#items></#list>E</#list>.", "1B.");
- assertOutput("<#list 1..2 as x>${x}<#list 3..4 as x>${x}<#break></#list>;</#list>", "13;23;");
- assertOutput("<#list [1..2, 3..4, [], 5..6] as xs>[<#list xs as x>${x}<#else><#break></#list>]</#list>.",
- "[12][34][.");
- assertOutput("<#list [1..2, 3..4, [], 5..6] as xs>"
- + "<#list xs>[<#items as x>${x}</#items>]<#else><#break></#list>"
- + "</#list>.",
- "[12][34].");
- assertOutput("<#forEach x in 1..2>${x}<#break></#forEach>", "1");
- }
-
- @Test
- public void testInvalidPlacements() throws IOException, TemplateException {
- assertErrorContains("<#break>", BREAK_NESTING_ERROR_MESSAGE_PART);
- assertErrorContains("<#list 1..2 as x>${x}</#list><#break>", BREAK_NESTING_ERROR_MESSAGE_PART);
- assertErrorContains("<#if false><#break></#if>", BREAK_NESTING_ERROR_MESSAGE_PART);
- assertErrorContains("<#list xs><#break></#list>", BREAK_NESTING_ERROR_MESSAGE_PART);
- assertErrorContains("<#list 1..2 as x>${x}<#else><#break></#list>", BREAK_NESTING_ERROR_MESSAGE_PART);
- }
-
- @Test
- public void testInvalidPlacementMacroLoophole() throws IOException, TemplateException {
- final String ftl = "<#list 1..2 as x>${x}<#macro m><#break></#macro></#list>";
- getConfiguration().setIncompatibleImprovements(Configuration.VERSION_2_3_22);
- assertOutput(ftl, "12");
- getConfiguration().setIncompatibleImprovements(Configuration.VERSION_2_3_23);
- assertErrorContains(ftl, BREAK_NESTING_ERROR_MESSAGE_PART);
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/da570caa/src/test/java/freemarker/core/ListBreakContinueTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/core/ListBreakContinueTest.java b/src/test/java/freemarker/core/ListBreakContinueTest.java
new file mode 100644
index 0000000..8ee4af8
--- /dev/null
+++ b/src/test/java/freemarker/core/ListBreakContinueTest.java
@@ -0,0 +1,93 @@
+package freemarker.core;
+
+import java.io.IOException;
+
+import org.junit.Test;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+
+import freemarker.template.Configuration;
+import freemarker.template.DefaultObjectWrapperBuilder;
+import freemarker.template.TemplateCollectionModel;
+import freemarker.template.TemplateException;
+import freemarker.template.TemplateHashModelEx;
+import freemarker.template.TemplateModel;
+import freemarker.template.TemplateModelException;
+import freemarker.test.TemplateTest;
+
+public class ListBreakContinueTest extends TemplateTest {
+
+ @Test
+ public void testNonHash() throws IOException, TemplateException {
+ testNonHash(ImmutableList.of(1, 2, 3, 4, 5)); // Listing a TemplateSequenceModel
+ testNonHash(ImmutableSet.of(1, 2, 3, 4, 5)); // Listing a TemplateCollectionModel
+ }
+
+ private void testNonHash(Object listed) throws IOException, TemplateException {
+ addToDataModel("listed", listed);
+ assertOutput(
+ "<#list listed as i>B(${i}) <#if i == 3>Break!<#break></#if>A(${i})<#sep>, </#list>",
+ "B(1) A(1), B(2) A(2), B(3) Break!");
+ assertOutput(
+ "<#list listed as i>B(${i}) <#if i == 3>Continue! <#continue></#if>A(${i})<#sep>, </#list>",
+ "B(1) A(1), B(2) A(2), B(3) Continue! B(4) A(4), B(5) A(5)");
+ }
+
+ @Test
+ public void testHash() throws IOException, TemplateException {
+ testHash(ImmutableMap.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5)); // Listing a TemplateHashModelEx2
+ testHash(new NonEx2Hash((TemplateHashModelEx) getConfiguration().getObjectWrapper().wrap(
+ ImmutableMap.of("a", 1, "b", 2, "c", 3, "d", 4, "e", 5)))); // Listing a TemplateHashModelEx (non-Ex2)
+ }
+
+ private void testHash(Object listed) throws IOException, TemplateException {
+ addToDataModel("listed", listed);
+ assertOutput(
+ "<#list listed as k, v>B(${k}=${v}) <#if k == 'c'>Break!<#break></#if>A(${k}=${v})<#sep>, </#list>",
+ "B(a=1) A(a=1), B(b=2) A(b=2), B(c=3) Break!");
+ assertOutput(
+ "<#list listed as k, v>B(${k}=${v}) <#if k == 'c'>Continue! <#continue></#if>A(${k}=${v})<#sep>, </#list>",
+ "B(a=1) A(a=1), B(b=2) A(b=2), B(c=3) Continue! B(d=4) A(d=4), B(e=5) A(e=5)");
+ }
+
+ @Override
+ protected Configuration createConfiguration() throws Exception {
+ Configuration conf = super.createConfiguration();
+ DefaultObjectWrapperBuilder owb = new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_27);
+ owb.setForceLegacyNonListCollections(false);
+ conf.setObjectWrapper(owb.build());
+ return conf;
+ }
+
+ /** Hides the Ex2 features of another hash */
+ static class NonEx2Hash implements TemplateHashModelEx {
+ private final TemplateHashModelEx delegate;
+
+ public NonEx2Hash(TemplateHashModelEx delegate) {
+ this.delegate = delegate;
+ }
+
+ public TemplateModel get(String key) throws TemplateModelException {
+ return delegate.get(key);
+ }
+
+ public int size() throws TemplateModelException {
+ return delegate.size();
+ }
+
+ public TemplateCollectionModel keys() throws TemplateModelException {
+ return delegate.keys();
+ }
+
+ public boolean isEmpty() throws TemplateModelException {
+ return delegate.isEmpty();
+ }
+
+ public TemplateCollectionModel values() throws TemplateModelException {
+ return delegate.values();
+ }
+ }
+
+}
[5/9] incubator-freemarker git commit: (README typo)
Posted by dd...@apache.org.
(README typo)
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/19501e41
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/19501e41
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/19501e41
Branch: refs/heads/2.3
Commit: 19501e419ee02d2552a20b6a80182a940a41ab59
Parents: 3aa6c50
Author: ddekany <dd...@apache.org>
Authored: Sat Sep 16 15:27:31 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Sat Sep 16 15:27:31 2017 +0200
----------------------------------------------------------------------
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/19501e41/README.md
----------------------------------------------------------------------
diff --git a/README.md b/README.md
index 9ed8a0d..779df07 100644
--- a/README.md
+++ b/README.md
@@ -139,7 +139,7 @@ apply it to your development environment:
- Window -> Preferences
- General -> Workspace, set the text file encoding
to "UTF-8". (Or, you can set the same later on project level instead.)
- - General -> Editors, set:
+ - General -> Editors -> Text Editors, set:
- Insert space for tabs
- Show print margin, 120 columns
- Java -> Code Style -> Formatter -> Import...
[6/9] incubator-freemarker git commit: Added a new BeansWrapper
setting,
preferIndexedReadMethod. With this one can address the Java 8 compatibility
problem with indexed property read methods without changing the
incompatibleImprovements of the object wr
Posted by dd...@apache.org.
Added a new BeansWrapper setting, preferIndexedReadMethod. With this one can address the Java 8 compatibility problem with indexed property read methods without changing the incompatibleImprovements of the object wrapper.
Project: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/commit/3dfa8ce8
Tree: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/tree/3dfa8ce8
Diff: http://git-wip-us.apache.org/repos/asf/incubator-freemarker/diff/3dfa8ce8
Branch: refs/heads/2.3
Commit: 3dfa8ce8a74a09de9e0830a364d5371802472f4f
Parents: 19501e4
Author: ddekany <dd...@apache.org>
Authored: Sat Sep 16 16:52:37 2017 +0200
Committer: ddekany <dd...@apache.org>
Committed: Sat Sep 16 16:52:37 2017 +0200
----------------------------------------------------------------------
.../java/freemarker/ext/beans/BeanModel.java | 4 +-
.../java/freemarker/ext/beans/BeansWrapper.java | 65 +++++++++++---------
.../ext/beans/BeansWrapperConfiguration.java | 15 +++++
src/manual/en_US/book.xml | 28 ++++++---
.../freemarker/template/ConfigurationTest.java | 4 ++
.../template/DefaultObjectWrapperTest.java | 11 +++-
6 files changed, 86 insertions(+), 41 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3dfa8ce8/src/main/java/freemarker/ext/beans/BeanModel.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/ext/beans/BeanModel.java b/src/main/java/freemarker/ext/beans/BeanModel.java
index 312ec98..4139b09 100644
--- a/src/main/java/freemarker/ext/beans/BeanModel.java
+++ b/src/main/java/freemarker/ext/beans/BeanModel.java
@@ -50,7 +50,6 @@ import freemarker.template.TemplateModelException;
import freemarker.template.TemplateModelIterator;
import freemarker.template.TemplateModelWithAPISupport;
import freemarker.template.TemplateScalarModel;
-import freemarker.template._TemplateAPI;
import freemarker.template.utility.StringUtil;
/**
@@ -221,8 +220,7 @@ implements
TemplateModel resultModel = UNKNOWN;
if (desc instanceof IndexedPropertyDescriptor) {
IndexedPropertyDescriptor pd = (IndexedPropertyDescriptor) desc;
- if (wrapper.getIncompatibleImprovements().intValue() >= _TemplateAPI.VERSION_INT_2_3_27
- && pd.getReadMethod() != null) {
+ if (!wrapper.getPreferIndexedReadMethod() && pd.getReadMethod() != null) {
resultModel = wrapper.invokeMethod(object, pd.getReadMethod(), null);
// cachedModel remains null, as we don't cache these
} else {
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3dfa8ce8/src/main/java/freemarker/ext/beans/BeansWrapper.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/ext/beans/BeansWrapper.java b/src/main/java/freemarker/ext/beans/BeansWrapper.java
index 0195234..3dbb3e1 100644
--- a/src/main/java/freemarker/ext/beans/BeansWrapper.java
+++ b/src/main/java/freemarker/ext/beans/BeansWrapper.java
@@ -179,11 +179,12 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
private volatile boolean writeProtected;
private TemplateModel nullModel = null;
- private int defaultDateType; // initialized by PropertyAssignments.apply
+ private int defaultDateType; // initialized from the BeansWrapperConfiguration
private ObjectWrapper outerIdentity = this;
private boolean methodsShadowItems = true;
- private boolean simpleMapWrapper; // initialized by PropertyAssignments.apply
- private boolean strict; // initialized by PropertyAssignments.apply
+ private boolean simpleMapWrapper; // initialized from the BeansWrapperConfiguration
+ private boolean strict; // initialized from the BeansWrapperConfiguration
+ private boolean preferIndexedReadMethod; // initialized from the BeansWrapperConfiguration
private final Version incompatibleImprovements;
@@ -250,13 +251,8 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
* </li>
* <li>
* <p>2.3.27 (or higher):
- * If the same JavaBean property has both an indexed property reader (like {@code String getFoo(int)}) and
- * a non-indexed property reader (like {@code String[] getFoo()}), and {@link Introspector} exposes both
- * (which apparently only happens since Java 8), we will use the non-indexed property reader method, while
- * before this improvement we have used the indexed property method. When using the indexed property reader,
- * FreeMarker doesn't know the size of the array, so the value becomes unlistable. Before Java 8 this problem
- * haven't surfaced, as {@link Introspector} has only exposed the non-indexed property reader method when both
- * kind of read method was present. So this can be seen as a Java 8 compatibility fix.
+ * The default of the {@link #setPreferIndexedReadMethod(boolean) preferIndexedReadMethod} setting changes
+ * from {@code true} to {@code false}.
* </li>
* </ul>
*
@@ -344,6 +340,7 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
this.incompatibleImprovements = bwConf.getIncompatibleImprovements(); // normalized
simpleMapWrapper = bwConf.isSimpleMapWrapper();
+ preferIndexedReadMethod = bwConf.getPreferIndexedReadMethod();
defaultDateType = bwConf.getDefaultDateType();
outerIdentity = bwConf.getOuterIdentity() != null ? bwConf.getOuterIdentity() : this;
strict = bwConf.isStrict();
@@ -523,31 +520,40 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
return simpleMapWrapper;
}
- // I have commented this out, as it won't be in 2.3.20 yet.
- /*
/**
- * Tells which non-backward-compatible overloaded method selection fixes to apply;
- * see {@link #setOverloadedMethodSelection(Version)}.
- * /
- public Version getOverloadedMethodSelection() {
- return overloadedMethodSelection;
+ * Getter pair of {@link #setPreferIndexedReadMethod(boolean)}
+ *
+ * @since 2.3.27
+ */
+ public boolean getPreferIndexedReadMethod() {
+ return preferIndexedReadMethod;
}
/**
- * Sets which non-backward-compatible overloaded method selection fixes to apply.
- * This has similar logic as {@link Configuration#setIncompatibleImprovements(Version)},
- * but only applies to this aspect.
+ * Sets if when a JavaBean property has both a normal read method (like {@code String[] getFoos()}) and an indexed
+ * read method (like {@code String getFoos(int index)}), and the Java {@link Introspector} exposes both (which only
+ * happens since Java 8, apparently), which read method will be used when the property is accessed with the
+ * shorthand syntax (like {@code myObj.foos}). Before {@link #getIncompatibleImprovements() incompatibleImprovements}
+ * 2.3.27 it defaults to {@code true} for backward compatibility (although it's actually less backward compatible if
+ * you are just switching to Java 8; see later), but the recommended value and the default starting with
+ * {@link #getIncompatibleImprovements() incompatibleImprovements} 2.3.27 is {@code false}. This setting has no
+ * effect on properties that only has normal read method, or only has indexed read method. In case a property has
+ * both, using the indexed reader method is disadvantageous, as then FreeMarker can't tell what the highest allowed
+ * index is, and so the property will be unlistable ({@code <#list foo as myObj.foos>} will fail).
*
- * Currently significant values:
- * <ul>
- * <li>2.3.21: Completetlly rewritten overloaded method selection, fixes several issues with the old one.</li>
- * </ul>
- * /
- public void setOverloadedMethodSelection(Version version) {
- overloadedMethodSelection = version;
+ * <p>
+ * Apparently, this setting only matters since Java 8, as before that {@link Introspector} did not expose the
+ * indexed reader method if there was also a normal reader method. As with Java 8 the behavior of
+ * {@link Introspector} has changed, some old templates started to break, as the property has suddenly become
+ * unlistable (see earlier why). So setting this to {@code false} can be seen as a Java 8 compatibility fix.
+ *
+ * @since 2.3.27
+ */
+ public void setPreferIndexedReadMethod(boolean preferIndexedReadMethod) {
+ checkModifiable();
+ this.preferIndexedReadMethod = preferIndexedReadMethod;
}
- */
-
+
/**
* Sets the method exposure level. By default, set to <code>EXPOSE_SAFE</code>.
* @param exposureLevel can be any of the <code>EXPOSE_xxx</code>
@@ -1746,6 +1752,7 @@ public class BeansWrapper implements RichObjectWrapper, WriteProtectable {
return "simpleMapWrapper=" + simpleMapWrapper + ", "
+ "exposureLevel=" + classIntrospector.getExposureLevel() + ", "
+ "exposeFields=" + classIntrospector.getExposeFields() + ", "
+ + "preferIndexedReadMethod=" + preferIndexedReadMethod + ", "
+ "treatDefaultMethodsAsBeanMembers="
+ classIntrospector.getTreatDefaultMethodsAsBeanMembers() + ", "
+ "sharedClassIntrospCache="
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3dfa8ce8/src/main/java/freemarker/ext/beans/BeansWrapperConfiguration.java
----------------------------------------------------------------------
diff --git a/src/main/java/freemarker/ext/beans/BeansWrapperConfiguration.java b/src/main/java/freemarker/ext/beans/BeansWrapperConfiguration.java
index ba19633..905bde9 100644
--- a/src/main/java/freemarker/ext/beans/BeansWrapperConfiguration.java
+++ b/src/main/java/freemarker/ext/beans/BeansWrapperConfiguration.java
@@ -46,6 +46,7 @@ public abstract class BeansWrapperConfiguration implements Cloneable {
// Properties and their *defaults*:
private boolean simpleMapWrapper = false;
+ private boolean preferIndexedReadMethod;
private int defaultDateType = TemplateDateModel.UNKNOWN;
private ObjectWrapper outerIdentity = null;
private boolean strict = false;
@@ -81,6 +82,8 @@ public abstract class BeansWrapperConfiguration implements Cloneable {
: BeansWrapper.normalizeIncompatibleImprovementsVersion(incompatibleImprovements);
this.incompatibleImprovements = incompatibleImprovements;
+ preferIndexedReadMethod = incompatibleImprovements.intValue() < _TemplateAPI.VERSION_INT_2_3_27;
+
classIntrospectorBuilder = new ClassIntrospectorBuilder(incompatibleImprovements);
}
@@ -97,6 +100,7 @@ public abstract class BeansWrapperConfiguration implements Cloneable {
int result = 1;
result = prime * result + incompatibleImprovements.hashCode();
result = prime * result + (simpleMapWrapper ? 1231 : 1237);
+ result = prime * result + (preferIndexedReadMethod ? 1231 : 1237);
result = prime * result + defaultDateType;
result = prime * result + (outerIdentity != null ? outerIdentity.hashCode() : 0);
result = prime * result + (strict ? 1231 : 1237);
@@ -118,6 +122,7 @@ public abstract class BeansWrapperConfiguration implements Cloneable {
if (!incompatibleImprovements.equals(other.incompatibleImprovements)) return false;
if (simpleMapWrapper != other.simpleMapWrapper) return false;
+ if (preferIndexedReadMethod != other.preferIndexedReadMethod) return false;
if (defaultDateType != other.defaultDateType) return false;
if (outerIdentity != other.outerIdentity) return false;
if (strict != other.strict) return false;
@@ -148,6 +153,16 @@ public abstract class BeansWrapperConfiguration implements Cloneable {
public void setSimpleMapWrapper(boolean simpleMapWrapper) {
this.simpleMapWrapper = simpleMapWrapper;
}
+
+ /** @since 2.3.27 */
+ public boolean getPreferIndexedReadMethod() {
+ return preferIndexedReadMethod;
+ }
+
+ /** See {@link BeansWrapper#setPreferIndexedReadMethod(boolean)}. @since 2.3.27 */
+ public void setPreferIndexedReadMethod(boolean preferIndexedReadMethod) {
+ this.preferIndexedReadMethod = preferIndexedReadMethod;
+ }
public int getDefaultDateType() {
return defaultDateType;
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3dfa8ce8/src/manual/en_US/book.xml
----------------------------------------------------------------------
diff --git a/src/manual/en_US/book.xml b/src/manual/en_US/book.xml
index e127a79..f1dc57a 100644
--- a/src/manual/en_US/book.xml
+++ b/src/manual/en_US/book.xml
@@ -26974,18 +26974,26 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
</listitem>
<listitem>
+ <para>Added new <literal>BeansWrapper</literal> setting,
+ <literal>preferIndexedReadMethod</literal>. This was added to
+ address a Java 8 compatibility problem; see the bug fix entry
+ below for more information.</para>
+ </listitem>
+
+ <listitem>
<para>Bug fixed: <literal>BeansWrapper</literal> and
<literal>DefaultObjectWrapper</literal>, starting from Java 8,
when the same JavaBeans property has both non-indexed read
- method (like <literal>String[] getFoo()</literal>) and indexed
- read method (like <literal>String getFoo(int index)</literal>),
+ method (like <literal>String[] getFoos()</literal>) and indexed
+ read method (like <literal>String getFoos(int index)</literal>),
has mistakenly used the indexed read method to access the
property. This is a problem because then the array size was
unknown, and thus the property has suddenly become unlistable on
- Java 8. To enable the fix (where it will use the non-indexed
- read method), you have to increase the value of the
- <literal>incompatibleImprovements</literal> constructor argument
- of the used <literal>DefaultObjectWrapper</literal> or
+ Java 8 (that is, <literal><#list myObject.foos as
+ foo></literal> fails). To enable the fix (where it will use
+ the non-indexed read method), you should to increase the value
+ of the <literal>incompatibleImprovements</literal> constructor
+ argument of the used <literal>DefaultObjectWrapper</literal> or
<literal>BeansWrapper</literal> to 2.3.27. Note that if you
leave the <literal>object_wrapper</literal> setting of the
<literal>Configuration</literal> on its default, it's enough to
@@ -26993,8 +27001,12 @@ TemplateModel x = env.getVariable("x"); // get variable x</programlisting>
linkend="pgui_config_incompatible_improvements_how_to_set"><literal>incompatibleImprovements</literal>
setting</link> of the <literal>Configuration</literal> to
2.3.27, as that's inherited by the default
- <literal>object_wrapper</literal>. Note that this bug haven't
- surfaced before Java 8, as then
+ <literal>object_wrapper</literal>. In case increasing the
+ <literal>incompatibleImprovements</literal> is not an option
+ (because of the other changes it brings), you can instead set
+ the <literal>preferIndexedReadMethod</literal> property of the
+ object wrapper to <literal>false</literal>. Note that this bug
+ haven't surfaced before Java 8, as then
<literal>java.beans.Inrospector</literal> has only exposed the
non-indexed method when both kind of read method was
present.</para>
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3dfa8ce8/src/test/java/freemarker/template/ConfigurationTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/template/ConfigurationTest.java b/src/test/java/freemarker/template/ConfigurationTest.java
index 2beeff0..226306f 100644
--- a/src/test/java/freemarker/template/ConfigurationTest.java
+++ b/src/test/java/freemarker/template/ConfigurationTest.java
@@ -167,6 +167,10 @@ public class ConfigurationTest extends TestCase {
assertFalse(((DefaultObjectWrapper) cfg.getObjectWrapper()).getTreatDefaultMethodsAsBeanMembers());
cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_26);
assertTrue(((DefaultObjectWrapper) cfg.getObjectWrapper()).getTreatDefaultMethodsAsBeanMembers());
+ assertTrue(((DefaultObjectWrapper) cfg.getObjectWrapper()).getPreferIndexedReadMethod());
+ cfg.setIncompatibleImprovements(Configuration.VERSION_2_3_27);
+ assertTrue(((DefaultObjectWrapper) cfg.getObjectWrapper()).getTreatDefaultMethodsAsBeanMembers());
+ assertFalse(((DefaultObjectWrapper) cfg.getObjectWrapper()).getPreferIndexedReadMethod());
}
private void assertUses2322ObjectWrapper(Configuration cfg) {
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3dfa8ce8/src/test/java/freemarker/template/DefaultObjectWrapperTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/freemarker/template/DefaultObjectWrapperTest.java b/src/test/java/freemarker/template/DefaultObjectWrapperTest.java
index 26bcf9a..833f509 100644
--- a/src/test/java/freemarker/template/DefaultObjectWrapperTest.java
+++ b/src/test/java/freemarker/template/DefaultObjectWrapperTest.java
@@ -1001,7 +1001,7 @@ public class DefaultObjectWrapperTest {
}
@Test
- public void assertCanWrapDOM() throws SAXException, IOException, ParserConfigurationException,
+ public void testCanWrapDOM() throws SAXException, IOException, ParserConfigurationException,
TemplateModelException {
DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
InputSource is = new InputSource();
@@ -1009,6 +1009,15 @@ public class DefaultObjectWrapperTest {
Document doc = db.parse(is);
assertTrue(OW22.wrap(doc) instanceof TemplateNodeModel);
}
+
+ @Test
+ public void testPreferIndexedReadMethodAndIcI() {
+ assertTrue(new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_26).build().getPreferIndexedReadMethod());
+ assertTrue(new DefaultObjectWrapper(Configuration.VERSION_2_3_26).getPreferIndexedReadMethod());
+
+ assertFalse(new DefaultObjectWrapperBuilder(Configuration.VERSION_2_3_27).build().getPreferIndexedReadMethod());
+ assertFalse(new DefaultObjectWrapper(Configuration.VERSION_2_3_27).getPreferIndexedReadMethod());
+ }
private void assertSizeThroughAPIModel(int expectedSize, TemplateModel normalModel) throws TemplateModelException {
if (!(normalModel instanceof TemplateModelWithAPISupport)) {