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/01 18:58:21 UTC
[2/6] incubator-freemarker git commit: Reworked list/iterable-like
TemplateModel interfaced. Now we have TemplateIterableModel (which is like
TemplateCollectionModel in FM2),
TemplateCollectionModel (which similar to TemplateCollectionModelEx in FM2)
tha
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
index ca06017..2d78119 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultObjectWrapper.java
@@ -57,11 +57,13 @@ import org.apache.freemarker.core.model.ObjectWrappingException;
import org.apache.freemarker.core.model.RichObjectWrapper;
import org.apache.freemarker.core.model.TemplateBooleanModel;
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.TemplateFunctionModel;
import org.apache.freemarker.core.model.TemplateHashModel;
import org.apache.freemarker.core.model.TemplateModel;
import org.apache.freemarker.core.model.TemplateModelAdapter;
+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;
@@ -511,7 +513,7 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
* {@link TemplateBooleanModel} instances into a Boolean, arbitrary
* {@link TemplateHashModel} instances into a Map, arbitrary
* {@link TemplateSequenceModel} into a List, and arbitrary
- * {@link TemplateCollectionModel} into a Set. All other objects are
+ * {@link TemplateIterableModel} into a Set. All other objects are
* returned unchanged.
* @throws TemplateException if an attempted unwrapping fails.
*/
@@ -649,29 +651,41 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
if (Map.class == targetClass) {
if (model instanceof TemplateHashModel) {
- return new HashAdapter((TemplateHashModel) model, this);
+ return new TemplateHashModelAdapter((TemplateHashModel) model, this);
}
}
if (List.class == targetClass) {
if (model instanceof TemplateSequenceModel) {
- return new SequenceAdapter((TemplateSequenceModel) model, this);
+ return new TemplateSequenceModelAdapter((TemplateSequenceModel) model, this);
}
}
if (Set.class == targetClass) {
if (model instanceof TemplateCollectionModel) {
- return new SetAdapter((TemplateCollectionModel) model, this);
+ return new TemplateSetModelAdapter((TemplateCollectionModel) model, this);
}
}
- if (Collection.class == targetClass || Iterable.class == targetClass) {
+ if (Collection.class == targetClass) {
+ if (model instanceof TemplateSequenceModel) {
+ return new TemplateSequenceModelAdapter((TemplateSequenceModel) model, this);
+ }
if (model instanceof TemplateCollectionModel) {
- return new CollectionAdapter((TemplateCollectionModel) model,
- this);
+ return new TemplateCollectionModelAdapter((TemplateCollectionModel) model, this);
}
+ }
+
+ if (Iterable.class == targetClass) {
if (model instanceof TemplateSequenceModel) {
- return new SequenceAdapter((TemplateSequenceModel) model, this);
+ return new TemplateSequenceModelAdapter((TemplateSequenceModel) model, this);
+ }
+ if (model instanceof TemplateCollectionModel) {
+ return new TemplateCollectionModelAdapter((TemplateCollectionModel) model, this);
+ }
+ if (model instanceof TemplateIterableModel) {
+ return new TemplateIterableModelAdapter((TemplateIterableModel) model,
+ this);
}
}
@@ -753,23 +767,23 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
}
if ((itf == 0 || (itf & TypeFlags.ACCEPTS_MAP) != 0)
&& model instanceof TemplateHashModel
- && (itf != 0 || targetClass.isAssignableFrom(HashAdapter.class))) {
- return new HashAdapter((TemplateHashModel) model, this);
+ && (itf != 0 || targetClass.isAssignableFrom(TemplateHashModelAdapter.class))) {
+ return new TemplateHashModelAdapter((TemplateHashModel) model, this);
}
if ((itf == 0 || (itf & TypeFlags.ACCEPTS_LIST) != 0)
&& model instanceof TemplateSequenceModel
- && (itf != 0 || targetClass.isAssignableFrom(SequenceAdapter.class))) {
- return new SequenceAdapter((TemplateSequenceModel) model, this);
+ && (itf != 0 || targetClass.isAssignableFrom(TemplateSequenceModelAdapter.class))) {
+ return new TemplateSequenceModelAdapter((TemplateSequenceModel) model, this);
}
if ((itf == 0 || (itf & TypeFlags.ACCEPTS_SET) != 0)
&& model instanceof TemplateCollectionModel
- && (itf != 0 || targetClass.isAssignableFrom(SetAdapter.class))) {
- return new SetAdapter((TemplateCollectionModel) model, this);
+ && (itf != 0 || targetClass.isAssignableFrom(TemplateSetModelAdapter.class))) {
+ return new TemplateSetModelAdapter((TemplateCollectionModel) model, this);
}
if ((itf & TypeFlags.ACCEPTS_ARRAY) != 0
&& model instanceof TemplateSequenceModel) {
- return new SequenceAdapter((TemplateSequenceModel) model, this);
+ return new TemplateSequenceModelAdapter((TemplateSequenceModel) model, this);
}
if (itf == 0) {
@@ -805,12 +819,13 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
recursionStops = new IdentityHashMap<>();
}
Class<?> componentType = arrayClass.getComponentType();
- Object array = Array.newInstance(componentType, seq.size());
+ final int size = seq.getCollectionSize();
+ 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);
+ TemplateModelIterator iter = seq.iterator();
+ for (int idx = 0; idx < size; idx++) {
+ final TemplateModel seqItem = iter.next();
Object val = tryUnwrapTo(seqItem, componentType, 0, recursionStops);
if (val == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
if (tryOnly) {
@@ -819,12 +834,12 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
throw new TemplateException(
"Failed to convert ", new _DelayedTemplateLanguageTypeDescription(seq),
" object to ", new _DelayedShortClassName(array.getClass()),
- ": Problematic sequence item at index ", Integer.valueOf(i) ," with value type: ",
+ ": Problematic sequence item at index ", Integer.valueOf(idx) ," with value type: ",
new _DelayedTemplateLanguageTypeDescription(seqItem));
}
}
- Array.set(array, i, val);
+ Array.set(array, idx, val);
}
} finally {
recursionStops.remove(seq);
@@ -834,9 +849,9 @@ public class DefaultObjectWrapper implements RichObjectWrapper {
Object listToArray(List<?> list, Class<?> arrayClass, Map<Object, Object> recursionStops)
throws TemplateException {
- if (list instanceof SequenceAdapter) {
+ if (list instanceof TemplateSequenceModelAdapter) {
return unwrapSequenceToArray(
- ((SequenceAdapter) list).getTemplateSequenceModel(),
+ ((TemplateSequenceModelAdapter) list).getTemplateSequenceModel(),
arrayClass, false,
recursionStops);
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultUnassignableIteratorAdapter.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultUnassignableIteratorAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultUnassignableIteratorAdapter.java
deleted file mode 100644
index c87f3f7..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultUnassignableIteratorAdapter.java
+++ /dev/null
@@ -1,59 +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.model.impl;
-
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-
-import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.core.model.ObjectWrapper;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelIterator;
-
-/**
- * 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;
- }
-
- @Override
- public TemplateModel next() throws TemplateException {
- try {
- return wrapper.wrap(it.next());
- } catch (NoSuchElementException e) {
- throw new TemplateException("The collection has no more items.", e);
- }
- }
-
- @Override
- public boolean hasNext() throws TemplateException {
- return it.hasNext();
- }
-
-}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/HashAdapter.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/HashAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/HashAdapter.java
deleted file mode 100644
index d829f6b..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/HashAdapter.java
+++ /dev/null
@@ -1,181 +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.model.impl;
-
-import java.util.AbstractMap;
-import java.util.AbstractSet;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.core.model.TemplateHashModel;
-import org.apache.freemarker.core.model.TemplateHashModelEx;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelAdapter;
-import org.apache.freemarker.core.model.TemplateModelIterator;
-import org.apache.freemarker.core.util.UndeclaredThrowableException;
-
-/**
- * Adapts a {@link TemplateHashModel} to a {@link Map}.
- */
-class HashAdapter extends AbstractMap implements TemplateModelAdapter {
- private final DefaultObjectWrapper wrapper;
- private final TemplateHashModel model;
- private Set entrySet;
-
- HashAdapter(TemplateHashModel model, DefaultObjectWrapper wrapper) {
- this.model = model;
- this.wrapper = wrapper;
- }
-
- @Override
- public TemplateModel getTemplateModel() {
- return model;
- }
-
- @Override
- public boolean isEmpty() {
- try {
- return model.isEmpty();
- } catch (TemplateException e) {
- throw new UndeclaredThrowableException(e);
- }
- }
-
- @Override
- public Object get(Object key) {
- try {
- return wrapper.unwrap(model.get(String.valueOf(key)));
- } catch (TemplateException e) {
- throw new UndeclaredThrowableException(e);
- }
- }
-
- @Override
- public boolean containsKey(Object key) {
- // A quick check that doesn't require TemplateHashModelEx
- if (get(key) != null) {
- return true;
- }
- return super.containsKey(key);
- }
-
- @Override
- public Set entrySet() {
- if (entrySet != null) {
- return entrySet;
- }
- return entrySet = new AbstractSet() {
- @Override
- public Iterator iterator() {
- final TemplateModelIterator i;
- try {
- i = getModelEx().keys().iterator();
- } catch (TemplateException e) {
- throw new UndeclaredThrowableException(e);
- }
- return new Iterator() {
- @Override
- public boolean hasNext() {
- try {
- return i.hasNext();
- } catch (TemplateException e) {
- throw new UndeclaredThrowableException(e);
- }
- }
-
- @Override
- public Object next() {
- final Object key;
- try {
- key = wrapper.unwrap(i.next());
- } catch (TemplateException e) {
- throw new UndeclaredThrowableException(e);
- }
- return new Map.Entry() {
- @Override
- public Object getKey() {
- return key;
- }
-
- @Override
- public Object getValue() {
- return get(key);
- }
-
- @Override
- public Object setValue(Object value) {
- throw new UnsupportedOperationException();
- }
-
- @Override
- public boolean equals(Object o) {
- if (!(o instanceof Map.Entry))
- return false;
- Map.Entry e = (Map.Entry) o;
- Object k1 = getKey();
- Object k2 = e.getKey();
- if (k1 == k2 || (k1 != null && k1.equals(k2))) {
- Object v1 = getValue();
- Object v2 = e.getValue();
- if (v1 == v2 || (v1 != null && v1.equals(v2)))
- return true;
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- Object value = getValue();
- return (key == null ? 0 : key.hashCode()) ^
- (value == null ? 0 : value.hashCode());
- }
- };
- }
-
- @Override
- public void remove() {
- throw new UnsupportedOperationException();
- }
- };
- }
-
- @Override
- public int size() {
- try {
- return getModelEx().size();
- } catch (TemplateException e) {
- throw new UndeclaredThrowableException(e);
- }
- }
- };
- }
-
- private TemplateHashModelEx getModelEx() {
- if (model instanceof TemplateHashModelEx) {
- return ((TemplateHashModelEx) model);
- }
- throw new UnsupportedOperationException(
- "Operation supported only on TemplateHashModelEx. " +
- model.getClass().getName() + " does not implement it though.");
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/IterableAndSequence.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/IterableAndSequence.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/IterableAndSequence.java
new file mode 100644
index 0000000..eb51098
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/IterableAndSequence.java
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.util.ArrayList;
+
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.TemplateIterableModel;
+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.TemplateSequenceModel;
+
+/**
+ * Add sequence capabilities to an existing iterable. Used by ?keys and ?values built-ins.
+ */
+// TODO [FM3] Maybe ?keys/?values should just return a TemplateCollectionModel
+final public class IterableAndSequence implements TemplateSequenceModel {
+ private TemplateIterableModel iterable;
+ private ArrayList<TemplateModel> data;
+
+ public IterableAndSequence(TemplateIterableModel iterable) {
+ this.iterable = iterable;
+ }
+
+ @Override
+ public TemplateModelIterator iterator() throws TemplateException {
+ return iterable.iterator();
+ }
+
+ @Override
+ public TemplateModel get(int i) throws TemplateException {
+ initSequence();
+ return i < data.size() && i >= 0 ? data.get(i) : null;
+ }
+
+ @Override
+ public int getCollectionSize() throws TemplateException {
+ if (iterable instanceof TemplateCollectionModel) {
+ return ((TemplateCollectionModel) iterable).getCollectionSize();
+ } else {
+ initSequence();
+ return data.size();
+ }
+ }
+
+ @Override
+ public boolean isEmptyCollection() throws TemplateException {
+ if (iterable instanceof TemplateCollectionModel) {
+ return ((TemplateCollectionModel) iterable).isEmptyCollection();
+ } else {
+ return iterable.iterator().hasNext();
+ }
+ }
+
+ private void initSequence() throws TemplateException {
+ if (data == null) {
+ data = new ArrayList<>();
+ TemplateModelIterator it = iterable.iterator();
+ while (it.hasNext()) {
+ data.add(it.next());
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/IteratorToTemplateModelIteratorAdapter.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/IteratorToTemplateModelIteratorAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/IteratorToTemplateModelIteratorAdapter.java
new file mode 100644
index 0000000..df31393
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/IteratorToTemplateModelIteratorAdapter.java
@@ -0,0 +1,52 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.freemarker.core.model.impl;
+
+import java.util.Iterator;
+
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelIterator;
+
+/**
+ * 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;
+ }
+
+ @Override
+ public TemplateModel next() throws TemplateException {
+ return wrapper.wrap(it.next());
+ }
+
+ @Override
+ public boolean hasNext() throws TemplateException {
+ return it.hasNext();
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
index 037ff15..9f12c05 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
@@ -79,13 +79,13 @@ public class ResourceBundleModel extends BeanModel implements TemplateFunctionMo
* Returns true if this bundle contains no objects.
*/
@Override
- public boolean isEmpty() {
+ public boolean isEmptyHash() {
return !((ResourceBundle) object).getKeys().hasMoreElements() &&
- super.isEmpty();
+ super.isEmptyHash();
}
@Override
- public int size() {
+ public int getHashSize() {
return keySet().size();
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SequenceAdapter.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SequenceAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SequenceAdapter.java
deleted file mode 100644
index b7a9136..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SequenceAdapter.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 org.apache.freemarker.core.model.impl;
-
-import java.util.AbstractList;
-
-import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.core.model.TemplateModel;
-import org.apache.freemarker.core.model.TemplateModelAdapter;
-import org.apache.freemarker.core.model.TemplateSequenceModel;
-import org.apache.freemarker.core.util.UndeclaredThrowableException;
-
-/**
- */
-class SequenceAdapter extends AbstractList implements TemplateModelAdapter {
- private final DefaultObjectWrapper wrapper;
- private final TemplateSequenceModel model;
-
- SequenceAdapter(TemplateSequenceModel model, DefaultObjectWrapper wrapper) {
- this.model = model;
- this.wrapper = wrapper;
- }
-
- @Override
- public TemplateModel getTemplateModel() {
- return model;
- }
-
- @Override
- public int size() {
- try {
- return model.size();
- } catch (TemplateException e) {
- throw new UndeclaredThrowableException(e);
- }
- }
-
- @Override
- public Object get(int index) {
- try {
- return wrapper.unwrap(model.get(index));
- } catch (TemplateException e) {
- throw new UndeclaredThrowableException(e);
- }
- }
-
- public TemplateSequenceModel getTemplateSequenceModel() {
- return model;
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SequenceTemplateModelIterator.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SequenceTemplateModelIterator.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SequenceTemplateModelIterator.java
new file mode 100644
index 0000000..ede0bac
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SequenceTemplateModelIterator.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.util.List;
+
+import org.apache.freemarker.core.TemplateException;
+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.TemplateSequenceModel;
+
+/**
+ * {@link TemplateIterableModel} implementation that can iterate through a {@link TemplateSequenceModel} be going
+ * through the valid indexes and calling {@link TemplateSequenceModel#get(int)} for each. This should only be used if
+ * you can be sure that {@link TemplateSequenceModel#get(int)} and {@link TemplateSequenceModel#getCollectionSize()} is efficient.
+ * ({@link TemplateSequenceModel} doesn't require that to be efficient, as listing the contents should be done with with
+ * {@link TemplateIterableModel#iterator()}).
+ * <p>
+ * This class assumes that the sequence doesn't change during iteration (as it's technically impossible to detect if a
+ * generic {@link TemplateSequenceModel} was changed, similarly as it's impossible for a generic {@link List}). The
+ * index range is decided when the instance is created, and it simply iterates through the predefined index range. Thus
+ * for example, if the sequence length decreases after that, it will return {@code null}-s (means missing element) at
+ * the end, for the indexes that are now out of bounds.
+ */
+public class SequenceTemplateModelIterator implements TemplateModelIterator {
+
+ private final TemplateSequenceModel sequence;
+ private final int size;
+ private int nextIndex = 0;
+
+ public SequenceTemplateModelIterator(TemplateSequenceModel sequence) throws TemplateException {
+ this.sequence = sequence;
+ this.size = sequence.getCollectionSize();
+ }
+
+ @Override
+ public TemplateModel next() throws TemplateException {
+ return sequence.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/model/impl/SetAdapter.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SetAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SetAdapter.java
deleted file mode 100644
index 0975ac4..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SetAdapter.java
+++ /dev/null
@@ -1,32 +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.model.impl;
-
-import java.util.Set;
-
-import org.apache.freemarker.core.model.TemplateCollectionModel;
-
-/**
- */
-class SetAdapter extends CollectionAdapter implements Set {
- SetAdapter(TemplateCollectionModel model, DefaultObjectWrapper wrapper) {
- super(model, wrapper);
- }
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleCollection.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleCollection.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleCollection.java
deleted file mode 100644
index 012beb6..0000000
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleCollection.java
+++ /dev/null
@@ -1,138 +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.model.impl;
-
-import java.io.Serializable;
-import java.util.Collection;
-import java.util.Iterator;
-
-import org.apache.freemarker.core.TemplateException;
-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.WrappingTemplateModel;
-
-/**
- * A simple implementation of {@link TemplateCollectionModel}.
- * It's able to wrap <tt>java.util.Iterator</tt>-s and <tt>java.util.Collection</tt>-s.
- * If you wrap an <tt>Iterator</tt>, the variable can be <#list>-ed only once!
- *
- * <p>Consider using {@link SimpleSequence} instead of this class if you want to wrap <tt>Iterator</tt>s.
- * <tt>SimpleSequence</tt> will read all elements of the <tt>Iterator</tt>, and store them in a <tt>List</tt>
- * (this may cause too high resource consumption in some applications), so you can list the variable
- * for unlimited times. Also, if you want to wrap <tt>Collection</tt>s, and then list the resulting
- * variable for many times, <tt>SimpleSequence</tt> may gives better performance, as the
- * wrapping of non-<tt>TemplateModel</tt> objects happens only once.
- *
- * <p>This class is thread-safe. The returned {@link TemplateModelIterator}-s
- * are <em>not</em> thread-safe.
- */
-public class SimpleCollection extends WrappingTemplateModel
-implements TemplateCollectionModel, Serializable {
-
- private boolean iteratorOwned;
- private final Iterator iterator;
- private final Collection collection;
-
- public SimpleCollection(Iterator iterator, ObjectWrapper wrapper) {
- super(wrapper);
- this.iterator = iterator;
- collection = null;
- }
-
- public SimpleCollection(Collection collection, ObjectWrapper wrapper) {
- super(wrapper);
- this.collection = collection;
- iterator = null;
- }
-
- /**
- * Retrieves a template model iterator that is used to iterate over the elements in this collection.
- *
- * <p>When you wrap an <tt>Iterator</tt> and you get <tt>TemplateModelIterator</tt> for multiple times,
- * only one of the returned <tt>TemplateModelIterator</tt> instances can be really used. When you have called a
- * method of a <tt>TemplateModelIterator</tt> instance, all other instance will throw a
- * {@link TemplateException} when you try to call their methods, since the wrapped <tt>Iterator</tt>
- * can't return the first element anymore.
- */
- @Override
- public TemplateModelIterator iterator() {
- return iterator != null
- ? new SimpleTemplateModelIterator(iterator, false)
- : new SimpleTemplateModelIterator(collection.iterator(), true);
- }
-
- /**
- * Wraps an {@link Iterator}; not thread-safe. The encapsulated {@link Iterator} may be accessible from multiple
- * threads (as multiple {@link SimpleTemplateModelIterator} instance can wrap the same {@link Iterator} instance),
- * but if the {@link Iterator} was marked in the constructor as shared, the first thread which uses the
- * {@link Iterator} will monopolize that.
- */
- private class SimpleTemplateModelIterator implements TemplateModelIterator {
-
- private final Iterator iterator;
- private boolean iteratorOwnedByMe;
-
- SimpleTemplateModelIterator(Iterator iterator, boolean iteratorOwnedByMe) {
- this.iterator = iterator;
- this.iteratorOwnedByMe = iteratorOwnedByMe;
- }
-
- @Override
- public TemplateModel next() throws TemplateException {
- if (!iteratorOwnedByMe) {
- synchronized (SimpleCollection.this) {
- checkIteratorNotOwned();
- iteratorOwned = true;
- iteratorOwnedByMe = true;
- }
- }
-
- if (!iterator.hasNext()) {
- throw new TemplateException("The collection has no more items.");
- }
-
- Object value = iterator.next();
- return value instanceof TemplateModel ? (TemplateModel) value : wrap(value);
- }
-
- @Override
- public boolean hasNext() throws TemplateException {
- // Calling hasNext may looks safe, but I have met sync. problems.
- if (!iteratorOwnedByMe) {
- synchronized (SimpleCollection.this) {
- checkIteratorNotOwned();
- }
- }
-
- return iterator.hasNext();
- }
-
- private void checkIteratorNotOwned() throws TemplateException {
- if (iteratorOwned) {
- throw new TemplateException(
- "This collection value wraps a java.util.Iterator, thus it can be listed only once.");
- }
- }
-
- }
-
-}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
index df2f6ae..e2c867e 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
@@ -30,7 +30,7 @@ import org.apache.freemarker.core.TemplateException;
import org.apache.freemarker.core._DelayedJQuote;
import org.apache.freemarker.core.model.ObjectWrapper;
import org.apache.freemarker.core.model.TemplateBooleanModel;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateIterableModel;
import org.apache.freemarker.core.model.TemplateHashModelEx;
import org.apache.freemarker.core.model.TemplateHashModelEx2;
import org.apache.freemarker.core.model.TemplateModel;
@@ -267,23 +267,23 @@ public class SimpleHash extends WrappingTemplateModel implements TemplateHashMod
}
@Override
- public int size() {
+ public int getHashSize() {
return map.size();
}
@Override
- public boolean isEmpty() {
+ public boolean isEmptyHash() {
return map == null || map.isEmpty();
}
@Override
- public TemplateCollectionModel keys() {
- return new SimpleCollection(map.keySet(), getObjectWrapper());
+ public TemplateIterableModel keys() {
+ return new SimpleIterable(map.keySet(), getObjectWrapper());
}
@Override
- public TemplateCollectionModel values() {
- return new SimpleCollection(map.values(), getObjectWrapper());
+ public TemplateIterableModel values() {
+ return new SimpleIterable(map.values(), getObjectWrapper());
}
@Override
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleIterable.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleIterable.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleIterable.java
new file mode 100644
index 0000000..1856535
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleIterable.java
@@ -0,0 +1,133 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.io.Serializable;
+import java.util.Iterator;
+
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.ObjectWrapper;
+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.WrappingTemplateModel;
+
+/**
+ * A simple implementation of {@link TemplateIterableModel}.
+ * It's able to wrap <tt>java.util.Iterator</tt>-s and <tt>java.util.Collection</tt>-s.
+ * If you wrap an <tt>Iterator</tt>, the variable can be <#list>-ed only once!
+ *
+ * <p>Consider using {@link SimpleSequence} instead of this class if you want to wrap <tt>Iterator</tt>s.
+ * <tt>SimpleSequence</tt> will read all elements of the <tt>Iterator</tt>, and store them in a <tt>List</tt>
+ * (this may cause too high resource consumption in some applications), so you can list the variable
+ * for unlimited times. Also, if you want to wrap <tt>Collection</tt>s, and then list the resulting
+ * variable for many times, <tt>SimpleSequence</tt> may gives better performance, as the
+ * wrapping of non-<tt>TemplateModel</tt> objects happens only once.
+ *
+ * <p>This class is thread-safe. The returned {@link TemplateModelIterator}-s
+ * are <em>not</em> thread-safe.
+ */
+public class SimpleIterable extends WrappingTemplateModel
+implements TemplateIterableModel, Serializable {
+
+ private boolean iteratorOwned;
+ private final Iterator iterator;
+ private final Iterable iterable;
+
+ public SimpleIterable(Iterator iterator, ObjectWrapper wrapper) {
+ super(wrapper);
+ this.iterator = iterator;
+ iterable = null;
+ }
+
+ public SimpleIterable(Iterable iterable, ObjectWrapper wrapper) {
+ super(wrapper);
+ this.iterable = iterable;
+ iterator = null;
+ }
+
+ /**
+ * Retrieves a template model iterator that is used to iterate over the elements in this iterable.
+ *
+ * <p>When you wrap an <tt>Iterator</tt> and you get <tt>TemplateModelIterator</tt> for multiple times,
+ * only one of the returned <tt>TemplateModelIterator</tt> instances can be really used. When you have called a
+ * method of a <tt>TemplateModelIterator</tt> instance, all other instance will throw a
+ * {@link TemplateException} when you try to call their methods, since the wrapped <tt>Iterator</tt>
+ * can't return the first element anymore.
+ */
+ @Override
+ public TemplateModelIterator iterator() {
+ return iterator != null
+ ? new SimpleTemplateModelIterator(iterator, false)
+ : new SimpleTemplateModelIterator(iterable.iterator(), true);
+ }
+
+ /**
+ * Wraps an {@link Iterator}; not thread-safe. The encapsulated {@link Iterator} may be accessible from multiple
+ * threads (as multiple {@link SimpleTemplateModelIterator} instance can wrap the same {@link Iterator} instance),
+ * but if the {@link Iterator} was marked in the constructor as shared, the first thread which uses the
+ * {@link Iterator} will monopolize that.
+ */
+ private class SimpleTemplateModelIterator implements TemplateModelIterator {
+
+ private final Iterator iterator;
+ private boolean iteratorOwnedByMe;
+
+ SimpleTemplateModelIterator(Iterator iterator, boolean iteratorOwnedByMe) {
+ this.iterator = iterator;
+ this.iteratorOwnedByMe = iteratorOwnedByMe;
+ }
+
+ @Override
+ public TemplateModel next() throws TemplateException {
+ if (!iteratorOwnedByMe) {
+ synchronized (SimpleIterable.this) {
+ checkIteratorNotOwned();
+ iteratorOwned = true;
+ iteratorOwnedByMe = true;
+ }
+ }
+
+ Object value = iterator.next();
+ return value instanceof TemplateModel ? (TemplateModel) value : wrap(value);
+ }
+
+ @Override
+ public boolean hasNext() throws TemplateException {
+ // Calling hasNext may looks safe, but I have met sync. problems.
+ if (!iteratorOwnedByMe) {
+ synchronized (SimpleIterable.this) {
+ checkIteratorNotOwned();
+ }
+ }
+
+ return iterator.hasNext();
+ }
+
+ private void checkIteratorNotOwned() throws TemplateException {
+ if (iteratorOwned) {
+ throw new TemplateException(
+ "This value wraps a java.util.Iterator, thus it can be listed only once.");
+ }
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java
index e0e0397..851147a 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java
@@ -26,6 +26,7 @@ import java.util.List;
import org.apache.freemarker.core.TemplateException;
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.WrappingTemplateModel;
@@ -133,25 +134,35 @@ public class SimpleSequence extends WrappingTemplateModel implements TemplateSeq
*/
@Override
public TemplateModel get(int index) throws TemplateException {
- try {
- Object value = list.get(index);
- if (value instanceof TemplateModel) {
- return (TemplateModel) value;
- }
- TemplateModel tm = wrap(value);
- list.set(index, tm);
- return tm;
- } catch (IndexOutOfBoundsException e) {
+ if (index >= list.size() || index < 0) {
return null;
}
+
+ Object value = list.get(index);
+ if (value instanceof TemplateModel) {
+ return (TemplateModel) value;
+ }
+ TemplateModel tm = wrap(value);
+ list.set(index, tm);
+ return tm;
}
@Override
- public int size() {
+ public int getCollectionSize() {
return list.size();
}
@Override
+ public boolean isEmptyCollection() throws TemplateException {
+ return list.isEmpty();
+ }
+
+ @Override
+ public TemplateModelIterator iterator() throws TemplateException {
+ return new SequenceTemplateModelIterator(this);
+ }
+
+ @Override
public String toString() {
return list.toString();
}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SingleItemTemplateModelIterator.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SingleItemTemplateModelIterator.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SingleItemTemplateModelIterator.java
new file mode 100644
index 0000000..7b8461b
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SingleItemTemplateModelIterator.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelIterator;
+
+/**
+ * {@link TemplateModelIterator} for the special case when you have exactly one element.
+ */
+public class SingleItemTemplateModelIterator implements TemplateModelIterator {
+
+ private final TemplateModel element;
+ private boolean hasNext;
+
+ public SingleItemTemplateModelIterator(TemplateModel element) {
+ this.element = element;
+ }
+
+ @Override
+ public TemplateModel next() throws TemplateException {
+ hasNext = false;
+ return element;
+ }
+
+ @Override
+ public boolean hasNext() throws TemplateException {
+ return hasNext;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java
index 12ecbc3..1ea6027 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java
@@ -27,7 +27,7 @@ import java.util.Iterator;
import java.util.Map;
import org.apache.freemarker.core.TemplateException;
-import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateIterableModel;
import org.apache.freemarker.core.model.TemplateFunctionModel;
import org.apache.freemarker.core.model.TemplateHashModelEx;
import org.apache.freemarker.core.model.TemplateModel;
@@ -84,23 +84,23 @@ final class StaticModel implements TemplateHashModelEx {
* field or method in the underlying class.
*/
@Override
- public boolean isEmpty() {
+ public boolean isEmptyHash() {
return map.isEmpty();
}
@Override
- public int size() {
+ public int getHashSize() {
return map.size();
}
@Override
- public TemplateCollectionModel keys() throws TemplateException {
- return (TemplateCollectionModel) wrapper.getOuterIdentity().wrap(map.keySet());
+ public TemplateIterableModel keys() throws TemplateException {
+ return (TemplateIterableModel) wrapper.getOuterIdentity().wrap(map.keySet());
}
@Override
- public TemplateCollectionModel values() throws TemplateException {
- return (TemplateCollectionModel) wrapper.getOuterIdentity().wrap(map.values());
+ public TemplateIterableModel values() throws TemplateException {
+ return (TemplateIterableModel) wrapper.getOuterIdentity().wrap(map.values());
}
private void populate() throws TemplateException {
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateCollectionModelAdapter.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateCollectionModelAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateCollectionModelAdapter.java
new file mode 100644
index 0000000..91a38ed
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateCollectionModelAdapter.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.util.AbstractCollection;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateIterableModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelAdapter;
+import org.apache.freemarker.core.util.UndeclaredThrowableException;
+
+/**
+ * Adapts a {@link TemplateIterableModel} to {@link Collection}.
+ */
+class TemplateCollectionModelAdapter<T> extends AbstractCollection<T> implements TemplateModelAdapter {
+ private final DefaultObjectWrapper wrapper;
+ private final TemplateCollectionModel model;
+
+ TemplateCollectionModelAdapter(TemplateCollectionModel model, DefaultObjectWrapper wrapper) {
+ this.model = model;
+ this.wrapper = wrapper;
+ }
+
+ @Override
+ public TemplateModel getTemplateModel() {
+ return model;
+ }
+
+ @Override
+ public int size() {
+ try {
+ return model.getCollectionSize();
+ } catch (TemplateException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ @Override
+ public boolean isEmpty() {
+ try {
+ return model.isEmptyCollection();
+ } catch (TemplateException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ try {
+ return new TemplateModelIteratorAdapter<T>(model.iterator(), wrapper);
+ } catch (TemplateException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateHashModelAdapter.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateHashModelAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateHashModelAdapter.java
new file mode 100644
index 0000000..b9ca888
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateHashModelAdapter.java
@@ -0,0 +1,185 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.util.AbstractMap;
+import java.util.AbstractSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import java.util.Set;
+
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateHashModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelAdapter;
+import org.apache.freemarker.core.model.TemplateModelIterator;
+import org.apache.freemarker.core.util.UndeclaredThrowableException;
+
+/**
+ * Adapts a {@link TemplateHashModel} to a {@link Map}.
+ */
+class TemplateHashModelAdapter extends AbstractMap implements TemplateModelAdapter {
+ private final DefaultObjectWrapper wrapper;
+ private final TemplateHashModel model;
+ private Set entrySet;
+
+ TemplateHashModelAdapter(TemplateHashModel model, DefaultObjectWrapper wrapper) {
+ this.model = model;
+ this.wrapper = wrapper;
+ }
+
+ @Override
+ public TemplateModel getTemplateModel() {
+ return model;
+ }
+
+ @Override
+ public boolean isEmpty() {
+ try {
+ return model.isEmptyHash();
+ } catch (TemplateException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ @Override
+ public Object get(Object key) {
+ try {
+ return wrapper.unwrap(model.get(String.valueOf(key)));
+ } catch (TemplateException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ @Override
+ public boolean containsKey(Object key) {
+ // A quick check that doesn't require TemplateHashModelEx
+ if (get(key) != null) {
+ return true;
+ }
+ return super.containsKey(key);
+ }
+
+ @Override
+ public Set entrySet() {
+ if (entrySet != null) {
+ return entrySet;
+ }
+ return entrySet = new AbstractSet() {
+ @Override
+ public Iterator iterator() {
+ final TemplateModelIterator iterator;
+ try {
+ iterator = getModelEx().keys().iterator();
+ } catch (TemplateException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ return new Iterator() {
+ @Override
+ public boolean hasNext() {
+ try {
+ return iterator.hasNext();
+ } catch (TemplateException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ @Override
+ public Object next() {
+ final Object key;
+ try {
+ if (!iterator.hasNext()) {
+ throw new NoSuchElementException();
+ }
+ key = wrapper.unwrap(iterator.next());
+ } catch (TemplateException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ return new Map.Entry() {
+ @Override
+ public Object getKey() {
+ return key;
+ }
+
+ @Override
+ public Object getValue() {
+ return get(key);
+ }
+
+ @Override
+ public Object setValue(Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof Map.Entry))
+ return false;
+ Map.Entry e = (Map.Entry) o;
+ Object k1 = getKey();
+ Object k2 = e.getKey();
+ if (k1 == k2 || (k1 != null && k1.equals(k2))) {
+ Object v1 = getValue();
+ Object v2 = e.getValue();
+ if (v1 == v2 || (v1 != null && v1.equals(v2)))
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ Object value = getValue();
+ return (key == null ? 0 : key.hashCode()) ^
+ (value == null ? 0 : value.hashCode());
+ }
+ };
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ };
+ }
+
+ @Override
+ public int size() {
+ try {
+ return getModelEx().getHashSize();
+ } catch (TemplateException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+ };
+ }
+
+ private TemplateHashModelEx getModelEx() {
+ if (model instanceof TemplateHashModelEx) {
+ return ((TemplateHashModelEx) model);
+ }
+ throw new UnsupportedOperationException(
+ "Operation supported only on TemplateHashModelEx. " +
+ model.getClass().getName() + " does not implement it though.");
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateIterableModelAdapter.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateIterableModelAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateIterableModelAdapter.java
new file mode 100644
index 0000000..58e1835
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateIterableModelAdapter.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.TemplateIterableModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelAdapter;
+import org.apache.freemarker.core.util.UndeclaredThrowableException;
+
+/**
+ * Adapts a {@link TemplateIterableModel} to {@link Collection}.
+ */
+class TemplateIterableModelAdapter<T> implements TemplateModelAdapter, Iterable<T> {
+ private final DefaultObjectWrapper wrapper;
+ private final TemplateIterableModel model;
+
+ TemplateIterableModelAdapter(TemplateIterableModel model, DefaultObjectWrapper wrapper) {
+ this.model = model;
+ this.wrapper = wrapper;
+ }
+
+ @Override
+ public TemplateModel getTemplateModel() {
+ return model;
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ try {
+ return new TemplateModelIteratorAdapter<T>(model.iterator(), wrapper);
+ } catch (TemplateException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelIteratorAdapter.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelIteratorAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelIteratorAdapter.java
new file mode 100644
index 0000000..263d6e0
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelIteratorAdapter.java
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.TemplateModelIterator;
+import org.apache.freemarker.core.util.UndeclaredThrowableException;
+
+class TemplateModelIteratorAdapter<T> implements Iterator<T> {
+ private final TemplateModelIterator iterator;
+ private final DefaultObjectWrapper wrapper;
+
+ public TemplateModelIteratorAdapter(TemplateModelIterator iterator,
+ DefaultObjectWrapper wrapper) {
+ this.iterator = iterator;
+ this.wrapper = wrapper;
+ }
+
+ @Override
+ public boolean hasNext() {
+ try {
+ return iterator.hasNext();
+ } catch (TemplateException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T next() {
+ try {
+ if (!iterator.hasNext()) {
+ throw new NoSuchElementException();
+ }
+ return (T) wrapper.unwrap(iterator.next());
+ } catch (TemplateException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelListSequence.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelListSequence.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelListSequence.java
index a93df33..db4dde1 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelListSequence.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelListSequence.java
@@ -19,16 +19,17 @@
package org.apache.freemarker.core.model.impl;
+import java.util.Iterator;
import java.util.List;
-import org.apache.freemarker.core.model.TemplateFunctionModel;
+import org.apache.freemarker.core.TemplateException;
import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelIterator;
import org.apache.freemarker.core.model.TemplateSequenceModel;
/**
* A sequence that wraps a {@link List} of {@link TemplateModel}-s. It does not copy the original
- * list. It's mostly useful when implementing {@link TemplateFunctionModel}-es that collect items from other
- * {@link TemplateModel}-s.
+ * list. The thread safety (and other behavior in face on concurrency) remains the same as of the wrapped list.
*/
public class TemplateModelListSequence implements TemplateSequenceModel {
@@ -43,14 +44,36 @@ public class TemplateModelListSequence implements TemplateSequenceModel {
@Override
public TemplateModel get(int index) {
- return list.get(index);
+ return index < list.size() && index >= 0 ? list.get(index) : null;
}
@Override
- public int size() {
+ public int getCollectionSize() {
return list.size();
}
+ @Override
+ public boolean isEmptyCollection() throws TemplateException {
+ return list.isEmpty();
+ }
+
+ @Override
+ public TemplateModelIterator iterator() throws TemplateException {
+ return new TemplateModelIterator() {
+ private final Iterator<? extends TemplateModel> iterator = list.iterator();
+
+ @Override
+ public TemplateModel next() throws TemplateException {
+ return iterator.next();
+ }
+
+ @Override
+ public boolean hasNext() throws TemplateException {
+ return iterator.hasNext();
+ }
+ };
+ }
+
/**
* Returns the original {@link List} of {@link TemplateModel}-s, so it's not a fully unwrapped value.
*/
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateSequenceModelAdapter.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateSequenceModelAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateSequenceModelAdapter.java
new file mode 100644
index 0000000..49da53a
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateSequenceModelAdapter.java
@@ -0,0 +1,88 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.util.AbstractList;
+import java.util.Iterator;
+
+import org.apache.freemarker.core.TemplateException;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelAdapter;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+import org.apache.freemarker.core.util.UndeclaredThrowableException;
+
+/**
+ */
+class TemplateSequenceModelAdapter<T> extends AbstractList<T> implements TemplateModelAdapter {
+ private final DefaultObjectWrapper wrapper;
+ private final TemplateSequenceModel model;
+
+ TemplateSequenceModelAdapter(TemplateSequenceModel model, DefaultObjectWrapper wrapper) {
+ this.model = model;
+ this.wrapper = wrapper;
+ }
+
+ @Override
+ public TemplateModel getTemplateModel() {
+ return model;
+ }
+
+ @Override
+ public int size() {
+ try {
+ return model.getCollectionSize();
+ } catch (TemplateException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ @Override
+ public boolean isEmpty() {
+ try {
+ return model.isEmptyCollection();
+ } catch (TemplateException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public T get(int index) {
+ try {
+ return (T) wrapper.unwrap(model.get(index));
+ } catch (TemplateException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ @Override
+ public Iterator<T> iterator() {
+ try {
+ return new TemplateModelIteratorAdapter<T>(model.iterator(), wrapper);
+ } catch (TemplateException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ public TemplateSequenceModel getTemplateSequenceModel() {
+ return model;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateSetModelAdapter.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateSetModelAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateSetModelAdapter.java
new file mode 100644
index 0000000..aa98bca
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateSetModelAdapter.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.util.Set;
+
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+
+// TODO [FM3] There's no TemplateSetModel, but we need to be able to do something like obj.needsASet([1, 2, 3]). But,
+// TemplateCollectionModel doesn't guarantee that the items are unique.
+class TemplateSetModelAdapter<T> extends TemplateCollectionModelAdapter<T> implements Set<T> {
+ TemplateSetModelAdapter(TemplateCollectionModel model, DefaultObjectWrapper wrapper) {
+ super(model, wrapper);
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/util/CallableUtils.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/util/CallableUtils.java b/freemarker-core/src/main/java/org/apache/freemarker/core/util/CallableUtils.java
index b3442d9..f305d3c 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/util/CallableUtils.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/util/CallableUtils.java
@@ -289,7 +289,7 @@ public final class CallableUtils {
_ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder(
getMessageArgumentProblem(
callable, argIdx,
- new Object[]{ " should be ", new _DelayedAOrAn(expectedTypesDesc), ", but was ",
+ new Object[]{ "should be ", new _DelayedAOrAn(expectedTypesDesc), ", but was ",
new _DelayedAOrAn(new _DelayedTemplateLanguageTypeDescription(argValue)),
"." },
calledAsFunction));
@@ -543,25 +543,33 @@ public final class CallableUtils {
}
/**
- * Convenience method to call
+ * Convenience method to return {@code null} if the argument is missing, otherwise call
* {@link #castArgumentValueToInt(TemplateModel, int, boolean, int, TemplateCallableModel, boolean)
- * castArgumentValueToInt(args[argIndex], argIndex, true, null, callable, true)}.
+ * castArgumentValueToInt(args[argIndex], argIndex, false, 0, callable, false)}.
*/
- public static int getOptionalIntArgument(
+ public static Integer getOptionalIntArgument(
TemplateModel[] args, int argIndex, TemplateFunctionModel callable)
throws TemplateException {
- return castArgumentValueToInt(args[argIndex], argIndex, true, 0, callable, true);
+ TemplateModel argValue = args[argIndex];
+ if (argValue == null) {
+ return null;
+ }
+ return castArgumentValueToInt(argValue, argIndex, false, 0, callable, true);
}
/**
- * Convenience method to call
+ * Convenience method to return {@code null} if the argument is missing, otherwise call
* {@link #castArgumentValueToInt(TemplateModel, int, boolean, int, TemplateCallableModel, boolean)
- * castArgumentValueToInt(args[argIndex], argIndex, true, null, callable, false)}.
+ * castArgumentValueToInt(args[argIndex], argIndex, false, 0, callable, false)}.
*/
- public static int getOptionalIntArgument(
+ public static Integer getOptionalIntArgument(
TemplateModel[] args, int argIndex, TemplateDirectiveModel callable)
throws TemplateException {
- return castArgumentValueToInt(args[argIndex], argIndex, true, 0, callable, false);
+ TemplateModel argValue = args[argIndex];
+ if (argValue == null) {
+ return null;
+ }
+ return castArgumentValueToInt(argValue, argIndex, false, 0, callable, false);
}
/**
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/util/DeepUnwrap.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/util/DeepUnwrap.java b/freemarker-core/src/main/java/org/apache/freemarker/core/util/DeepUnwrap.java
index 62bc25d..eb674cb 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/util/DeepUnwrap.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/util/DeepUnwrap.java
@@ -27,14 +27,14 @@ import org.apache.freemarker.core.TemplateException;
import org.apache.freemarker.core.model.AdapterTemplateModel;
import org.apache.freemarker.core.model.ObjectWrapper;
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.TemplateHashModelEx;
+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.TemplateStringModel;
import org.apache.freemarker.core.model.WrapperTemplateModel;
/**
@@ -61,7 +61,7 @@ public class DeepUnwrap {
* <li>If the object implements {@link TemplateBooleanModel}, then the result
* of {@link TemplateBooleanModel#getAsBoolean()} is returned.
* <li>If the object implements {@link TemplateSequenceModel} or
- * {@link TemplateCollectionModel}, then a <code>java.util.ArrayList</code> is
+ * {@link TemplateIterableModel}, then a <code>java.util.ArrayList</code> is
* constructed from the subvariables, and each subvariable is unwrapped with
* the rules described here (recursive unwrapping).
* <li>If the object implements {@link TemplateHashModelEx}, then a
@@ -119,14 +119,16 @@ 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) {
- list.add(unwrap(seq.get(i), nullModel, permissive));
+ int size = seq.getCollectionSize();
+ ArrayList list = new ArrayList(size);
+ TemplateModelIterator iter = seq.iterator();
+ for (int i = 0; i < size; ++i) {
+ list.add(unwrap(iter.next(), nullModel, permissive));
}
return list;
}
- if (model instanceof TemplateCollectionModel) {
- TemplateCollectionModel coll = (TemplateCollectionModel) model;
+ if (model instanceof TemplateIterableModel) {
+ TemplateIterableModel coll = (TemplateIterableModel) model;
ArrayList list = new ArrayList();
TemplateModelIterator it = coll.iterator();
while (it.hasNext()) {
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/util/StringToIndexMap.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/util/StringToIndexMap.java b/freemarker-core/src/main/java/org/apache/freemarker/core/util/StringToIndexMap.java
index feb9586..830a41f 100644
--- a/freemarker-core/src/main/java/org/apache/freemarker/core/util/StringToIndexMap.java
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/util/StringToIndexMap.java
@@ -247,9 +247,11 @@ public final class StringToIndexMap {
}
/**
- * Checks if all entries are in the {@code start} - {@code start}+{@code size()} (exclusive) index range.
+ * Checks if all entries are in the {@code start} - {@code start}+{@code getCollectionSize()} (exclusive) index
+ * range.
*
- * @throws IllegalArgumentException If some entry is not in the specified index range.
+ * @throws IllegalArgumentException
+ * If some entry is not in the specified index range.
*/
public void checkIndexRange(int start) {
if (buckets == null) {