http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java b/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java index debe879..5334692 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebugModelImpl.java @@ -27,7 +27,7 @@ import java.util.List; import org.apache.freemarker.core.TemplateException; 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.TemplateDirectiveModel; import org.apache.freemarker.core.model.TemplateFunctionModel; @@ -84,9 +84,9 @@ class RmiDebugModelImpl extends UnicastRemoteObject implements DebugModel { @Override public int size() throws TemplateException { if (model instanceof TemplateSequenceModel) { - return ((TemplateSequenceModel) model).size(); + return ((TemplateSequenceModel) model).getCollectionSize(); } - return ((TemplateHashModelEx) model).size(); + return ((TemplateHashModelEx) model).getHashSize(); } @Override @@ -107,7 +107,7 @@ class RmiDebugModelImpl extends UnicastRemoteObject implements DebugModel { @Override public DebugModel[] getCollection() throws TemplateException, RemoteException { List list = new ArrayList(); - TemplateModelIterator i = ((TemplateCollectionModel) model).iterator(); + TemplateModelIterator i = ((TemplateIterableModel) model).iterator(); while (i.hasNext()) { list.add(getDebugModel(i.next())); } @@ -152,7 +152,7 @@ class RmiDebugModelImpl extends UnicastRemoteObject implements DebugModel { if (model instanceof TemplateDateModel) type += TYPE_DATE; if (model instanceof TemplateBooleanModel) type += TYPE_BOOLEAN; if (model instanceof TemplateSequenceModel) type += TYPE_SEQUENCE; - if (model instanceof TemplateCollectionModel) type += TYPE_COLLECTION; + if (model instanceof TemplateIterableModel) type += TYPE_COLLECTION; if (model instanceof TemplateHashModelEx) type += TYPE_HASH_EX; if (model instanceof TemplateHashModel) type += TYPE_HASH; if (model instanceof TemplateFunctionModel) type += TYPE_FUNCTION;
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java b/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java index be2953d..4d9ace6 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/debug/RmiDebuggedEnvironmentImpl.java @@ -38,11 +38,11 @@ import org.apache.freemarker.core.MutableProcessingConfiguration; import org.apache.freemarker.core.ProcessingConfiguration; import org.apache.freemarker.core.Template; 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.TemplateHashModelEx; import org.apache.freemarker.core.model.TemplateModel; import org.apache.freemarker.core.model.impl.DefaultObjectWrapper; -import org.apache.freemarker.core.model.impl.SimpleCollection; +import org.apache.freemarker.core.model.impl.SimpleIterable; import org.apache.freemarker.core.model.impl.SimpleString; import org.apache.freemarker.core.util.UndeclaredThrowableException; @@ -128,29 +128,29 @@ class RmiDebuggedEnvironmentImpl extends RmiDebugModelImpl implements DebuggedEn private abstract static class DebugMapModel implements TemplateHashModelEx { @Override - public int size() { + public int getHashSize() { return keySet().size(); } @Override - public TemplateCollectionModel keys() { - return new SimpleCollection(keySet(), OBJECT_WRAPPER); + public TemplateIterableModel keys() { + return new SimpleIterable(keySet(), OBJECT_WRAPPER); } @Override - public TemplateCollectionModel values() throws TemplateException { + public TemplateIterableModel values() throws TemplateException { Collection keys = keySet(); List list = new ArrayList(keys.size()); for (Iterator it = keys.iterator(); it.hasNext(); ) { list.add(get((String) it.next())); } - return new SimpleCollection(list, OBJECT_WRAPPER); + return new SimpleIterable(list, OBJECT_WRAPPER); } @Override - public boolean isEmpty() { - return size() == 0; + public boolean isEmptyHash() { + return getHashSize() == 0; } abstract Collection keySet(); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyCollectionExModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyCollectionExModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyCollectionExModel.java deleted file mode 100644 index 34f8029..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyCollectionExModel.java +++ /dev/null @@ -1,43 +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; - -import java.io.Serializable; - -import org.apache.freemarker.core.TemplateException; - -class EmptyCollectionExModel implements TemplateCollectionModelEx, Serializable { - - @Override - public int size() throws TemplateException { - return 0; - } - - @Override - public boolean isEmpty() throws TemplateException { - return true; - } - - @Override - public TemplateModelIterator iterator() throws TemplateException { - return TemplateModelIterator.EMPTY_ITERATOR; - } - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyCollectionModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyCollectionModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyCollectionModel.java new file mode 100644 index 0000000..a38574c --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyCollectionModel.java @@ -0,0 +1,43 @@ +/* + * 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; + +import java.io.Serializable; + +import org.apache.freemarker.core.TemplateException; + +class EmptyCollectionModel implements TemplateCollectionModel, Serializable { + + @Override + public int getCollectionSize() throws TemplateException { + return 0; + } + + @Override + public boolean isEmptyCollection() throws TemplateException { + return true; + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return TemplateModelIterator.EMPTY_ITERATOR; + } + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyHashModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyHashModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyHashModel.java index 0b32160..966e0bb 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyHashModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyHashModel.java @@ -26,18 +26,18 @@ import org.apache.freemarker.core.TemplateException; class EmptyHashModel implements TemplateHashModelEx2, Serializable { @Override - public int size() throws TemplateException { + public int getHashSize() throws TemplateException { return 0; } @Override - public TemplateCollectionModel keys() throws TemplateException { - return TemplateCollectionModel.EMPTY_COLLECTION; + public TemplateIterableModel keys() throws TemplateException { + return TemplateIterableModel.EMPTY_ITERABLE; } @Override - public TemplateCollectionModel values() throws TemplateException { - return TemplateCollectionModel.EMPTY_COLLECTION; + public TemplateIterableModel values() throws TemplateException { + return TemplateIterableModel.EMPTY_ITERABLE; } @Override @@ -46,7 +46,7 @@ class EmptyHashModel implements TemplateHashModelEx2, Serializable { } @Override - public boolean isEmpty() throws TemplateException { + public boolean isEmptyHash() throws TemplateException { return true; } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyIteratorModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyIteratorModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyIteratorModel.java index a0b47e7..c2876c0 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyIteratorModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptyIteratorModel.java @@ -27,7 +27,7 @@ class EmptyIteratorModel implements TemplateModelIterator, Serializable { @Override public TemplateModel next() throws TemplateException { - throw new TemplateException("The collection has no more elements."); + throw new TemplateException("It's an empty iterator it has no elements."); } @Override http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptySequenceModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptySequenceModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptySequenceModel.java index b6a176e..bfd7930 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptySequenceModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/EmptySequenceModel.java @@ -31,8 +31,18 @@ class EmptySequenceModel implements TemplateSequenceModel, Serializable { } @Override - public int size() throws TemplateException { + public int getCollectionSize() throws TemplateException { return 0; } + @Override + public boolean isEmptyCollection() throws TemplateException { + return true; + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return TemplateModelIterator.EMPTY_ITERATOR; + } + } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java index e3d5c4a..3eba7a9 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/GeneralPurposeNothing.java @@ -28,12 +28,12 @@ import org.apache.freemarker.core.TemplateException; * It is meant to be interpreted in the most sensible way possible in various contexts. * This can be returned to avoid exceptions. */ - +// TODO [FM3] As `exp!` doesn't use this, are the other use cases necessary and correct? final class GeneralPurposeNothing implements TemplateBooleanModel, TemplateStringModel, TemplateSequenceModel, TemplateHashModelEx2, TemplateFunctionModel { - public static final TemplateModel INSTANCE = new GeneralPurposeNothing(); + static final TemplateModel INSTANCE = new GeneralPurposeNothing(); private static final ArgumentArrayLayout ARGS_LAYOUT = ArgumentArrayLayout.create( 0, true, @@ -53,18 +53,28 @@ implements TemplateBooleanModel, TemplateStringModel, TemplateSequenceModel, Tem } @Override - public boolean isEmpty() { + public int getHashSize() throws TemplateException { + return 0; + } + + @Override + public boolean isEmptyHash() { return true; } @Override - public int size() { + public int getCollectionSize() { return 0; } @Override + public boolean isEmptyCollection() throws TemplateException { + return true; + } + + @Override public TemplateModel get(int i) throws TemplateException { - throw new TemplateException("Can't get item from an empty sequence."); + return null; } @Override @@ -73,6 +83,11 @@ implements TemplateBooleanModel, TemplateStringModel, TemplateSequenceModel, Tem } @Override + public TemplateModelIterator iterator() throws TemplateException { + return TemplateModelIterator.EMPTY_ITERATOR; + } + + @Override public TemplateModel execute(TemplateModel[] args, CallPlace callPlace, Environment env) throws TemplateException { return null; } @@ -83,13 +98,13 @@ implements TemplateBooleanModel, TemplateStringModel, TemplateSequenceModel, Tem } @Override - public TemplateCollectionModel keys() { - return TemplateCollectionModel.EMPTY_COLLECTION; + public TemplateIterableModel keys() { + return TemplateIterableModel.EMPTY_ITERABLE; } @Override - public TemplateCollectionModel values() { - return TemplateCollectionModel.EMPTY_COLLECTION; + public TemplateIterableModel values() { + return TemplateIterableModel.EMPTY_ITERABLE; } @Override http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCollectionModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCollectionModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCollectionModel.java index d1db16c..1562b0a 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCollectionModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCollectionModel.java @@ -19,34 +19,24 @@ package org.apache.freemarker.core.model; -import java.util.Collection; - import org.apache.freemarker.core.TemplateException; /** - * "collection" template language data type: a collection of values that can be enumerated, but can't be or not meant to - * be accessed by index or key. As such, this is not a super-interface of {@link TemplateSequenceModel}, and - * implementations of that interface needn't also implement this interface just because they can. They should though, if - * enumeration with this interface is significantly faster than enumeration by index. The {@code #list} directive will - * enumerate using this interface if it's available. - * - * <p> - * The enumeration should be repeatable if that's possible with reasonable effort, otherwise a second enumeration - * attempt is allowed to throw an {@link TemplateException}. Generally, the interface user Java code need not - * handle that kind of exception, as in practice only the template author can handle it, by not listing such collections - * twice. - * - * <p> - * Note that to wrap Java's {@link Collection}, you should implement {@link TemplateCollectionModelEx}, not just this - * interface. + * "collection" template language data type: Extends {@link TemplateIterableModel} with the ability to get the + * whether there's any element, and the ability to get number of elements in the collection. */ -public interface TemplateCollectionModel extends TemplateModel { +public interface TemplateCollectionModel extends TemplateIterableModel { - TemplateCollectionModelEx EMPTY_COLLECTION = new EmptyCollectionExModel(); + /** + * Returns the number items in this collection, or {@link Integer#MAX_VALUE}, if there are more than + * {@link Integer#MAX_VALUE} items. + */ + int getCollectionSize() throws TemplateException; /** - * Retrieves a template model iterator that is used to iterate over the elements in this collection. + * Returns if the collection contains any elements. This differs from {@code getCollectionSize() != 0} only in that + * the exact number of items need not be calculated. */ - TemplateModelIterator iterator() throws TemplateException; + boolean isEmptyCollection() throws TemplateException; } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCollectionModelEx.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCollectionModelEx.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCollectionModelEx.java deleted file mode 100644 index 730a267..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateCollectionModelEx.java +++ /dev/null @@ -1,45 +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; - -import java.util.Collection; - -import org.apache.freemarker.core.TemplateException; - -/** - * "extended collection" template language data type: Adds size/emptiness querybility and "contains" test to - * {@link TemplateCollectionModel}. The added extra operations is provided by all Java {@link Collection}-s, and - * this interface was added to make that accessible for templates too. - */ -public interface TemplateCollectionModelEx extends TemplateCollectionModel { - - /** - * Returns the number items in this collection, or {@link Integer#MAX_VALUE}, if there are more than - * {@link Integer#MAX_VALUE} items. - */ - int size() throws TemplateException; - - /** - * Returns if the collection contains any elements. This differs from {@code size() != 0} only in that the exact - * number of items need not be calculated. - */ - boolean isEmpty() throws TemplateException; - -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java index bfdc118..3eb140d 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateDirectiveModel.java @@ -47,7 +47,7 @@ public interface TemplateDirectiveModel extends TemplateCallableModel { * call is not from a template, you can use {@link NonTemplateCallPlace#INSTANCE} (or another {@link * NonTemplateCallPlace} instance). Note that {@link CallPlace#executeNestedContent(TemplateModel[], Writer, * Environment)} can be used to execute the nested content (even if there's no nested content; then simply - * nothing happens). + * nothing happens), and to pass nested content parameters to it. * @param out * Print the output here (if there's any) * @param env http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModel.java index 8125233..d15d1fd 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModel.java @@ -41,5 +41,5 @@ public interface TemplateHashModel extends TemplateModel { */ TemplateModel get(String key) throws TemplateException; - boolean isEmpty() throws TemplateException; + boolean isEmptyHash() throws TemplateException; } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModelEx.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModelEx.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModelEx.java index becb3c5..2a968c9 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModelEx.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateHashModelEx.java @@ -35,18 +35,17 @@ public interface TemplateHashModelEx extends TemplateHashModel { /** * @return the number of key/value mappings in the hash. */ - int size() throws TemplateException; + int getHashSize() throws TemplateException; /** - * @return a collection containing the keys in the hash. Every element of - * the returned collection must implement the {@link TemplateStringModel} - * (as the keys of hashes are always strings). + * @return a iterable returning the keys in the hash. Every element of the returned by the iterable must implement + * the {@link TemplateStringModel} (as the keys of hashes are always strings). */ - TemplateCollectionModel keys() throws TemplateException; + TemplateIterableModel keys() throws TemplateException; /** - * @return a collection containing the values in the hash. The elements of the - * returned collection can be any kind of {@link TemplateModel}-s. + * @return An iterable returning the values in the hash. The elements returned by the iterable can be any kind of + * {@link TemplateModel}-s. */ - TemplateCollectionModel values() throws TemplateException; + TemplateIterableModel values() throws TemplateException; } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateIterableModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateIterableModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateIterableModel.java new file mode 100644 index 0000000..7d67f08 --- /dev/null +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateIterableModel.java @@ -0,0 +1,49 @@ +/* + * 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; + +import java.util.Collection; + +import org.apache.freemarker.core.TemplateException; + +/** + * "iterable" template language data type: a collection of values that can be listed; this interface doesn't add + * the ability of getting the number of elements, or to return elements by index. + * + * <p> + * The enumeration should be repeatable if that's possible with reasonable effort, otherwise a second enumeration + * attempt is allowed to throw an {@link TemplateException}. Generally, the interface user Java code need not + * handle that kind of exception, as in practice only the template author can handle it, by not listing such collections + * twice. + * + * <p> + * Note that to wrap Java's {@link Collection}, you should implement {@link TemplateCollectionModel}, not just this + * interface. + */ +public interface TemplateIterableModel extends TemplateModel { + + TemplateCollectionModel EMPTY_ITERABLE = new EmptyCollectionModel(); + + /** + * Retrieves an iterator that is used to iterate over the elements of something. + */ + TemplateModelIterator iterator() throws TemplateException; + +} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModelIterator.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModelIterator.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModelIterator.java index cd24cbb..4f70a5b 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModelIterator.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateModelIterator.java @@ -19,20 +19,33 @@ package org.apache.freemarker.core.model; +import java.util.List; +import java.util.NoSuchElementException; + import org.apache.freemarker.core.TemplateException; +import org.apache.freemarker.core.model.impl.DefaultListAdapter; /** - * Used to iterate over a set of template models <em>once</em>; usually returned from - * {@link TemplateCollectionModel#iterator()}. Note that it's not a {@link TemplateModel}. + * Used to iterate over a set of template models <em>once</em>; usually returned from {@link + * TemplateIterableModel#iterator()}. Note that it's not a {@link TemplateModel}. Note that the implementation of this + * interface may assume that the collection of elements that we iterate through is not changing after the {@link + * TemplateModelIterator} was created, as far as the {@link TemplateModelIterator} is still in use. If they still + * change, the methods of this interface might throw any kind of exception or gives and inconsistent view of the + * elements (like partially old element, partially new elements). Of course, implementations of this interface may + * specify a more specific behavior. Notably, the {@link TemplateModelIterator} return by {@link + * DefaultListAdapter#iterator()} gives the same concurrency guarantees as the wrapped {@link List} object. */ public interface TemplateModelIterator { TemplateModelIterator EMPTY_ITERATOR = new EmptyIteratorModel(); /** - * Returns the next model. - * @throws TemplateException if the next model can not be retrieved - * (i.e. because the iterator is exhausted). + * Returns the next item. It must not be called if there are no more items, as the behavior is undefined then + * (typically, {@link NoSuchElementException} or {@link IndexOutOfBoundsException} will be thrown, or {@code null} + * will be returned). Hence, you should almost always call {@link #hasNext()} before this method, and only call this + * method if that has returned {@code true}. (Note that the implementation still can't assume that {@link + * #hasNext()} is always called before {@link #next()}; the caller might knows that there's a next item for a + * different reason, like already knows the size of the collection.) */ TemplateModel next() throws TemplateException; http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateSequenceModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateSequenceModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateSequenceModel.java index d9dc6c6..6fd1903 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateSequenceModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/TemplateSequenceModel.java @@ -24,29 +24,26 @@ import org.apache.freemarker.core.TemplateException; /** * "sequence" template language data type; an object that contains other objects accessible through an integer 0-based * index. - * * <p> * Used in templates like: {@code mySeq[index]}, {@code <#list mySeq as i>...</#list>}, {@code mySeq?size}, etc. - * - * @see TemplateCollectionModel + * + * @see TemplateIterableModel */ -public interface TemplateSequenceModel extends TemplateModel { +public interface TemplateSequenceModel extends TemplateCollectionModel { TemplateSequenceModel EMPTY_SEQUENCE = new EmptySequenceModel(); /** - * Retrieves the i-th template model in this sequence. - * + * Retrieves the template model at the given index, or {@code null} if the index is out of bounds. If index-based + * access on the backing data structure is not very efficient (say, it's O(N) and you known N can be big), then you + * should consider only implementing {@link TemplateCollectionModel} instead. Also, if you need to list all items, + * you shouldn't use this method, but {@link TemplateIterableModel#iterator()}. + * * @return the item at the specified index, or <code>null</code> if the index is out of bounds. Note that a - * <code>null</code> value is interpreted by FreeMarker as "variable does not exist", and accessing a - * missing variables is usually considered as an error in the FreeMarker Template Language, so the usage of - * a bad index will not remain hidden, unless the default value for that case was also specified in the - * template. + * <code>null</code> value is interpreted by FreeMarker as "variable does not exist", and accessing a missing + * variables is usually considered as an error in the template language, so the usage of a bad index will not remain + * hidden, unless the default value for that case was also specified in the template. */ TemplateModel get(int index) throws TemplateException; - /** - * @return the number of items in the list. - */ - int size() 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/BeanModel.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java index 1b779d5..06dd2c0 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/BeanModel.java @@ -37,7 +37,7 @@ import org.apache.freemarker.core._DelayedJQuote; import org.apache.freemarker.core._DelayedTemplateLanguageTypeDescription; import org.apache.freemarker.core.model.AdapterTemplateModel; import org.apache.freemarker.core.model.ObjectWrappingException; -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; @@ -76,7 +76,7 @@ public class BeanModel /** * Creates a new model that wraps the specified object. Note that there are * specialized subclasses of this class for wrapping arrays, collections, - * enumeration, iterators, and maps. Note also that the superclass can be + * enumeration, iterators, maps, etc. Note also that the superclass can be * used to wrap String objects if only {@link TemplateStringModel} functionality is needed. You * can also choose to delegate the choice over which model class is used for * wrapping to {@link DefaultObjectWrapper#wrap(Object)}. @@ -189,8 +189,8 @@ public class BeanModel // cachedModel remains null, as we don't cache these } else if (desc instanceof IndexedPropertyDescriptor) { // In FreeMarker 2 we have exposed such indexed properties as sequences, but they can't support - // the size() method, so we have discontinued that. People has to call the indexed read method like - // any other method. + // the getCollectionSize() method, so we have discontinued that. People has to call the indexed read + // method like any other method. resultModel = UNKNOWN; } else { throw new IllegalStateException("PropertyDescriptor.readMethod shouldn't be null"); @@ -251,7 +251,7 @@ public class BeanModel * {@link Map}, or an {@link Iterator} that has no more items, or a {@link Boolean#FALSE}, or {@code null}. */ @Override - public boolean isEmpty() { + public boolean isEmptyHash() { if (object instanceof String) { return ((String) object).length() == 0; } @@ -283,24 +283,24 @@ public class BeanModel } @Override - public int size() { + public int getHashSize() { return wrapper.getClassIntrospector().keyCount(object.getClass()); } @Override - public TemplateCollectionModel keys() { - return new CollectionAndSequence(new SimpleSequence(keySet(), wrapper)); + public TemplateIterableModel keys() { + return new IterableAndSequence(DefaultNonListCollectionAdapter.adapt(keySet(), wrapper)); } @Override - public TemplateCollectionModel values() throws TemplateException { - List<Object> values = new ArrayList<>(size()); + public TemplateIterableModel values() throws TemplateException { + List<Object> values = new ArrayList<>(getHashSize()); TemplateModelIterator it = keys().iterator(); while (it.hasNext()) { String key = ((TemplateStringModel) it.next()).getAsString(); values.add(get(key)); } - return new CollectionAndSequence(new SimpleSequence(values, wrapper)); + return new IterableAndSequence(DefaultNonListCollectionAdapter.adapt(values, wrapper)); } @Override @@ -314,7 +314,7 @@ public class BeanModel * interface. Subclasses that override <tt>invokeGenericGet</tt> to * provide additional hash keys should also override this method. */ - protected Set/*<Object>*/ keySet() { + protected Set<String> keySet() { return wrapper.getClassIntrospector().keySet(object.getClass()); } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassBasedModelFactory.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassBasedModelFactory.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassBasedModelFactory.java index ac055b4..31701cb 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassBasedModelFactory.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassBasedModelFactory.java @@ -136,7 +136,7 @@ abstract class ClassBasedModelFactory implements TemplateHashModel { } @Override - public boolean isEmpty() { + public boolean isEmptyHash() { return false; } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java index a1f5935..cc083d4 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ClassIntrospector.java @@ -1007,12 +1007,14 @@ class ClassIntrospector { * Returns the Set of names of introspected methods/properties that should be available via the TemplateHashModel * interface. */ - Set<Object> keySet(Class<?> clazz) { + // TODO [FM3] Can't we instead return an Iterable<String> that filters out the non-String keys? + @SuppressWarnings("rawtypes") + Set<String> keySet(Class<?> clazz) { Set<Object> set = new HashSet<>(get(clazz).keySet()); set.remove(CONSTRUCTORS_KEY); set.remove(GENERIC_GET_KEY); set.remove(ARG_TYPES_BY_METHOD_KEY); - return set; + return (Set) set; } // ----------------------------------------------------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/CollectionAdapter.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/CollectionAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/CollectionAdapter.java deleted file mode 100644 index 10f2091..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/CollectionAdapter.java +++ /dev/null @@ -1,88 +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.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.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 TemplateCollectionModel} to {@link Collection}. - */ -class CollectionAdapter extends AbstractCollection implements TemplateModelAdapter { - private final DefaultObjectWrapper wrapper; - private final TemplateCollectionModel model; - - CollectionAdapter(TemplateCollectionModel model, DefaultObjectWrapper wrapper) { - this.model = model; - this.wrapper = wrapper; - } - - @Override - public TemplateModel getTemplateModel() { - return model; - } - - @Override - public int size() { - throw new UnsupportedOperationException(); - } - - @Override - public Iterator iterator() { - try { - return new Iterator() { - final TemplateModelIterator i = model.iterator(); - - @Override - public boolean hasNext() { - try { - return i.hasNext(); - } catch (TemplateException e) { - throw new UndeclaredThrowableException(e); - } - } - - @Override - public Object next() { - try { - return wrapper.unwrap(i.next()); - } catch (TemplateException e) { - throw new UndeclaredThrowableException(e); - } - } - - @Override - public void remove() { - throw new UnsupportedOperationException(); - } - }; - } 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/CollectionAndSequence.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/CollectionAndSequence.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/CollectionAndSequence.java deleted file mode 100644 index 8a7970a..0000000 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/CollectionAndSequence.java +++ /dev/null @@ -1,111 +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.ArrayList; - -import org.apache.freemarker.core.TemplateException; -import org.apache.freemarker.core.model.TemplateCollectionModel; -import org.apache.freemarker.core.model.TemplateCollectionModelEx; -import org.apache.freemarker.core.model.TemplateModel; -import org.apache.freemarker.core.model.TemplateModelIterator; -import org.apache.freemarker.core.model.TemplateSequenceModel; - -/** - * Add sequence capabilities to an existing collection, or - * vice versa. Used by ?keys and ?values built-ins. - */ -// [FM3] FTL sequence should extend FTL collection, so we shouldn't need that direction, only the other. -final public class CollectionAndSequence implements TemplateCollectionModel, TemplateSequenceModel { - private TemplateCollectionModel collection; - private TemplateSequenceModel sequence; - private ArrayList data; - - public CollectionAndSequence(TemplateCollectionModel collection) { - this.collection = collection; - } - - public CollectionAndSequence(TemplateSequenceModel sequence) { - this.sequence = sequence; - } - - @Override - public TemplateModelIterator iterator() throws TemplateException { - if (collection != null) { - return collection.iterator(); - } else { - return new SequenceIterator(sequence); - } - } - - @Override - public TemplateModel get(int i) throws TemplateException { - if (sequence != null) { - return sequence.get(i); - } else { - initSequence(); - return (TemplateModel) data.get(i); - } - } - - @Override - public int size() throws TemplateException { - if (sequence != null) { - return sequence.size(); - } else if (collection instanceof TemplateCollectionModelEx) { - return ((TemplateCollectionModelEx) collection).size(); - } else { - initSequence(); - return data.size(); - } - } - - private void initSequence() throws TemplateException { - if (data == null) { - data = new ArrayList(); - TemplateModelIterator it = collection.iterator(); - while (it.hasNext()) { - data.add(it.next()); - } - } - } - - private static class SequenceIterator - implements TemplateModelIterator { - private final TemplateSequenceModel sequence; - private final int size; - private int index = 0; - - SequenceIterator(TemplateSequenceModel sequence) throws TemplateException { - this.sequence = sequence; - size = sequence.size(); - - } - @Override - public TemplateModel next() throws TemplateException { - return sequence.get(index++); - } - - @Override - public boolean hasNext() { - return index < size; - } - } -} http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultArrayAdapter.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultArrayAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultArrayAdapter.java index 0e15194..06816fd 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultArrayAdapter.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultArrayAdapter.java @@ -27,6 +27,7 @@ import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper; import org.apache.freemarker.core.model.TemplateHashModelEx; 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.WrapperTemplateModel; import org.apache.freemarker.core.model.WrappingTemplateModel; @@ -122,11 +123,33 @@ public abstract class DefaultArrayAdapter extends WrappingTemplateModel implemen } @Override - public int size() throws TemplateException { + public int getCollectionSize() throws TemplateException { return array.length; } @Override + public boolean isEmptyCollection() throws TemplateException { + return array.length == 0; + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new TemplateModelIterator() { + private int nextIndex; + + @Override + public TemplateModel next() throws TemplateException { + return wrap(array[nextIndex++]); + } + + @Override + public boolean hasNext() throws TemplateException { + return nextIndex < array.length; + } + }; + } + + @Override public Object getWrappedObject() { return array; } @@ -144,14 +167,37 @@ public abstract class DefaultArrayAdapter extends WrappingTemplateModel implemen @Override public TemplateModel get(int index) throws TemplateException { - return index >= 0 && index < array.length ? wrap(Byte.valueOf(array[index])) : null; + return index >= 0 && index < array.length ? wrap(array[index]) : null; } @Override - public int size() throws TemplateException { + public int getCollectionSize() throws TemplateException { return array.length; } + + @Override + public boolean isEmptyCollection() throws TemplateException { + return array.length == 0; + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new TemplateModelIterator() { + private int nextIndex; + + @Override + public TemplateModel next() throws TemplateException { + return wrap(array[nextIndex++]); + } + + @Override + public boolean hasNext() throws TemplateException { + return nextIndex < array.length; + } + }; + } + @Override public Object getWrappedObject() { return array; @@ -170,15 +216,37 @@ public abstract class DefaultArrayAdapter extends WrappingTemplateModel implemen @Override public TemplateModel get(int index) throws TemplateException { - return index >= 0 && index < array.length ? wrap(Short.valueOf(array[index])) : null; + return index >= 0 && index < array.length ? wrap(array[index]) : null; } @Override - public int size() throws TemplateException { + public int getCollectionSize() throws TemplateException { return array.length; } @Override + public boolean isEmptyCollection() throws TemplateException { + return array.length == 0; + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new TemplateModelIterator() { + private int nextIndex; + + @Override + public TemplateModel next() throws TemplateException { + return wrap(array[nextIndex++]); + } + + @Override + public boolean hasNext() throws TemplateException { + return nextIndex < array.length; + } + }; + } + + @Override public Object getWrappedObject() { return array; } @@ -196,15 +264,37 @@ public abstract class DefaultArrayAdapter extends WrappingTemplateModel implemen @Override public TemplateModel get(int index) throws TemplateException { - return index >= 0 && index < array.length ? wrap(Integer.valueOf(array[index])) : null; + return index >= 0 && index < array.length ? wrap(array[index]) : null; } @Override - public int size() throws TemplateException { + public int getCollectionSize() throws TemplateException { return array.length; } @Override + public boolean isEmptyCollection() throws TemplateException { + return array.length == 0; + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new TemplateModelIterator() { + private int nextIndex; + + @Override + public TemplateModel next() throws TemplateException { + return wrap(array[nextIndex++]); + } + + @Override + public boolean hasNext() throws TemplateException { + return nextIndex < array.length; + } + }; + } + + @Override public Object getWrappedObject() { return array; } @@ -222,15 +312,37 @@ public abstract class DefaultArrayAdapter extends WrappingTemplateModel implemen @Override public TemplateModel get(int index) throws TemplateException { - return index >= 0 && index < array.length ? wrap(Long.valueOf(array[index])) : null; + return index >= 0 && index < array.length ? wrap(array[index]) : null; } @Override - public int size() throws TemplateException { + public int getCollectionSize() throws TemplateException { return array.length; } @Override + public boolean isEmptyCollection() throws TemplateException { + return array.length == 0; + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new TemplateModelIterator() { + private int nextIndex; + + @Override + public TemplateModel next() throws TemplateException { + return wrap(array[nextIndex++]); + } + + @Override + public boolean hasNext() throws TemplateException { + return nextIndex < array.length; + } + }; + } + + @Override public Object getWrappedObject() { return array; } @@ -248,15 +360,37 @@ public abstract class DefaultArrayAdapter extends WrappingTemplateModel implemen @Override public TemplateModel get(int index) throws TemplateException { - return index >= 0 && index < array.length ? wrap(Float.valueOf(array[index])) : null; + return index >= 0 && index < array.length ? wrap(array[index]) : null; } @Override - public int size() throws TemplateException { + public int getCollectionSize() throws TemplateException { return array.length; } @Override + public boolean isEmptyCollection() throws TemplateException { + return array.length == 0; + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new TemplateModelIterator() { + private int nextIndex; + + @Override + public TemplateModel next() throws TemplateException { + return wrap(array[nextIndex++]); + } + + @Override + public boolean hasNext() throws TemplateException { + return nextIndex < array.length; + } + }; + } + + @Override public Object getWrappedObject() { return array; } @@ -274,15 +408,37 @@ public abstract class DefaultArrayAdapter extends WrappingTemplateModel implemen @Override public TemplateModel get(int index) throws TemplateException { - return index >= 0 && index < array.length ? wrap(Double.valueOf(array[index])) : null; + return index >= 0 && index < array.length ? wrap(array[index]) : null; } @Override - public int size() throws TemplateException { + public int getCollectionSize() throws TemplateException { return array.length; } @Override + public boolean isEmptyCollection() throws TemplateException { + return array.length == 0; + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new TemplateModelIterator() { + private int nextIndex; + + @Override + public TemplateModel next() throws TemplateException { + return wrap(array[nextIndex++]); + } + + @Override + public boolean hasNext() throws TemplateException { + return nextIndex < array.length; + } + }; + } + + @Override public Object getWrappedObject() { return array; } @@ -300,15 +456,37 @@ public abstract class DefaultArrayAdapter extends WrappingTemplateModel implemen @Override public TemplateModel get(int index) throws TemplateException { - return index >= 0 && index < array.length ? wrap(Character.valueOf(array[index])) : null; + return index >= 0 && index < array.length ? wrap(array[index]) : null; } @Override - public int size() throws TemplateException { + public int getCollectionSize() throws TemplateException { return array.length; } @Override + public boolean isEmptyCollection() throws TemplateException { + return array.length == 0; + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new TemplateModelIterator() { + private int nextIndex; + + @Override + public TemplateModel next() throws TemplateException { + return wrap(array[nextIndex++]); + } + + @Override + public boolean hasNext() throws TemplateException { + return nextIndex < array.length; + } + }; + } + + @Override public Object getWrappedObject() { return array; } @@ -326,15 +504,37 @@ public abstract class DefaultArrayAdapter extends WrappingTemplateModel implemen @Override public TemplateModel get(int index) throws TemplateException { - return index >= 0 && index < array.length ? wrap(Boolean.valueOf(array[index])) : null; + return index >= 0 && index < array.length ? wrap(array[index]) : null; } @Override - public int size() throws TemplateException { + public int getCollectionSize() throws TemplateException { return array.length; } @Override + public boolean isEmptyCollection() throws TemplateException { + return array.length == 0; + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new TemplateModelIterator() { + private int nextIndex; + + @Override + public TemplateModel next() throws TemplateException { + return wrap(array[nextIndex++]); + } + + @Override + public boolean hasNext() throws TemplateException { + return nextIndex < array.length; + } + }; + } + + @Override public Object getWrappedObject() { return array; } @@ -361,11 +561,33 @@ public abstract class DefaultArrayAdapter extends WrappingTemplateModel implemen } @Override - public int size() throws TemplateException { + public int getCollectionSize() throws TemplateException { return length; } @Override + public boolean isEmptyCollection() throws TemplateException { + return length == 0; + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new TemplateModelIterator() { + private int nextIndex; + + @Override + public TemplateModel next() throws TemplateException { + return wrap(Array.get(array, nextIndex++)); + } + + @Override + public boolean hasNext() throws TemplateException { + return nextIndex < length; + } + }; + } + + @Override public Object getWrappedObject() { return array; } http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultEnumerationAdapter.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultEnumerationAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultEnumerationAdapter.java index af61102..428d7f8 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultEnumerationAdapter.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultEnumerationAdapter.java @@ -26,7 +26,7 @@ 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.ObjectWrapperWithAPISupport; -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.TemplateModelIterator; import org.apache.freemarker.core.model.TemplateModelWithAPISupport; @@ -37,11 +37,11 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** * Adapts an {@link Enumeration} to the corresponding {@link TemplateModel} interface(s), most importantly to - * {@link TemplateCollectionModel}. Putting aside that it wraps an {@link Enumeration} instead of an {@link Iterator}, + * {@link TemplateIterableModel}. Putting aside that it wraps an {@link Enumeration} instead of an {@link Iterator}, * this is identical to {@link DefaultIteratorAdapter}, so see further details there. */ @SuppressWarnings("serial") -public class DefaultEnumerationAdapter extends WrappingTemplateModel implements TemplateCollectionModel, +public class DefaultEnumerationAdapter extends WrappingTemplateModel implements TemplateIterableModel, AdapterTemplateModel, WrapperTemplateModel, TemplateModelWithAPISupport { @SuppressFBWarnings(value="SE_BAD_FIELD", justification="We hope it's Seralizable") @@ -98,10 +98,6 @@ public class DefaultEnumerationAdapter extends WrappingTemplateModel implements enumerationOwnedByMe = true; } - if (!enumeration.hasMoreElements()) { - throw new TemplateException("The collection has no more items."); - } - Object value = enumeration.nextElement(); return value instanceof TemplateModel ? (TemplateModel) value : wrap(value); } @@ -119,7 +115,7 @@ public class DefaultEnumerationAdapter extends WrappingTemplateModel implements private void checkNotOwner() throws TemplateException { if (enumerationOwnedBySomeone) { throw new TemplateException( - "This collection value wraps a java.util.Enumeration, thus it can be listed only once."); + "This iterator value wraps a java.util.Enumeration, 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/DefaultIterableAdapter.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultIterableAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultIterableAdapter.java index e60a090..82dd9eb 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultIterableAdapter.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultIterableAdapter.java @@ -27,7 +27,7 @@ import org.apache.freemarker.core.model.AdapterTemplateModel; import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper; import org.apache.freemarker.core.model.ObjectWrapperWithAPISupport; -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.TemplateModelIterator; import org.apache.freemarker.core.model.TemplateModelWithAPISupport; @@ -36,7 +36,7 @@ import org.apache.freemarker.core.model.WrappingTemplateModel; /** * Adapts an {@link Iterable} to the corresponding {@link TemplateModel} interface(s), most importantly to - * {@link TemplateCollectionModel}. This should only be used if {@link Collection} is not implemented by the adapted + * {@link TemplateIterableModel}. This should only be used if {@link Collection} is not implemented by the adapted * object, because then {@link DefaultListAdapter} and {@link DefaultNonListCollectionAdapter} gives more functionality. * * <p> @@ -45,7 +45,7 @@ import org.apache.freemarker.core.model.WrappingTemplateModel; * {@link Iterator} modifier methods (though of course, Java methods called from the template can violate this rule). */ @SuppressWarnings("serial") -public class DefaultIterableAdapter extends WrappingTemplateModel implements TemplateCollectionModel, +public class DefaultIterableAdapter extends WrappingTemplateModel implements TemplateIterableModel, AdapterTemplateModel, WrapperTemplateModel, TemplateModelWithAPISupport { private final Iterable<?> iterable; @@ -54,7 +54,7 @@ public class DefaultIterableAdapter extends WrappingTemplateModel implements Tem * Factory method for creating new adapter instances. * * @param iterable - * The collection to adapt; can't be {@code null}. + * The {@link Iterable} to adapt; can't be {@code null}. * @param wrapper * The {@link ObjectWrapper} used to wrap the items in the array. Has to be * {@link ObjectWrapperAndUnwrapper} because of planned future features. @@ -70,7 +70,7 @@ public class DefaultIterableAdapter extends WrappingTemplateModel implements Tem @Override public TemplateModelIterator iterator() throws TemplateException { - return new DefaultUnassignableIteratorAdapter(iterable.iterator(), getObjectWrapper()); + return new IteratorToTemplateModelIteratorAdapter(iterable.iterator(), 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/DefaultIteratorAdapter.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultIteratorAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultIteratorAdapter.java index ea9af3f..e727fa3 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultIteratorAdapter.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultIteratorAdapter.java @@ -25,7 +25,7 @@ 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.ObjectWrapperWithAPISupport; -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.TemplateModelIterator; import org.apache.freemarker.core.model.TemplateModelWithAPISupport; @@ -36,7 +36,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** * Adapts an {@link Iterator} to the corresponding {@link TemplateModel} interface(s), most importantly to - * {@link TemplateCollectionModel}. The resulting {@link TemplateCollectionModel} can only be listed (iterated) once. + * {@link TemplateIterableModel}. The resulting {@link TemplateIterableModel} can only be listed (iterated) once. * If the user tries list the variable for a second time, an exception will be thrown instead of silently gettig an * empty (or partial) listing. * @@ -49,7 +49,7 @@ import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; * This adapter is used by {@link DefaultObjectWrapper} if its {@code useAdaptersForCollections} property is * {@code true}, which is the default when its {@code incompatibleImprovements} property is 2.3.22 or higher. */ -public class DefaultIteratorAdapter extends WrappingTemplateModel implements TemplateCollectionModel, +public class DefaultIteratorAdapter extends WrappingTemplateModel implements TemplateIterableModel, AdapterTemplateModel, WrapperTemplateModel, TemplateModelWithAPISupport { @SuppressFBWarnings(value="SE_BAD_FIELD", justification="We hope it's Seralizable") @@ -106,10 +106,6 @@ public class DefaultIteratorAdapter extends WrappingTemplateModel implements Tem 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); } @@ -127,7 +123,7 @@ public class DefaultIteratorAdapter extends WrappingTemplateModel implements Tem private void checkNotOwner() throws TemplateException { if (iteratorOwnedBySomeone) { throw new TemplateException( - "This collection value wraps a java.util.Iterator, thus it can be listed only once."); + "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/DefaultListAdapter.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultListAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultListAdapter.java index 80e76d8..32b01ac 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultListAdapter.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultListAdapter.java @@ -19,7 +19,7 @@ package org.apache.freemarker.core.model.impl; -import java.util.AbstractSequentialList; +import java.util.Iterator; import java.util.List; import org.apache.freemarker.core.TemplateException; @@ -27,7 +27,6 @@ import org.apache.freemarker.core.model.AdapterTemplateModel; import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.model.ObjectWrapperWithAPISupport; import org.apache.freemarker.core.model.RichObjectWrapper; -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.TemplateModelWithAPISupport; @@ -41,9 +40,11 @@ import org.apache.freemarker.core.model.WrappingTemplateModel; * specifically to be used from a template, also consider using {@link SimpleSequence} (see comparison there). * * <p> - * Thread safety: A {@link DefaultListAdapter} is as thread-safe as the {@link List} that it wraps is. Normally you only - * have to consider read-only access, as the FreeMarker template language doesn't allow writing these sequences (though - * of course, Java methods called from the template can violate this rule). + * Thread safety: A {@link DefaultListAdapter} is as thread-safe as the {@link List} that it wraps is. Also, + * {@link #iterator()} will return a {@link TemplateModelIterator} that is as thread-safe as the {@link Iterator} + * that the wrapped {@link List} returns. + * Note that normally you only have to consider read-only access, as the FreeMarker template language doesn't allow + * writing these sequences (though of course, Java methods called from the template can violate this rule). * * <p> * This adapter is used by {@link DefaultObjectWrapper} if its {@code useAdaptersForCollections} property is @@ -67,10 +68,7 @@ public class DefaultListAdapter extends WrappingTemplateModel implements Templat * The {@link ObjectWrapper} used to wrap the items in the array. */ public static DefaultListAdapter adapt(List list, RichObjectWrapper wrapper) { - // [2.4] DefaultListAdapter should implement TemplateCollectionModelEx, so this choice becomes unnecessary - return list instanceof AbstractSequentialList - ? new DefaultListAdapterWithCollectionSupport(list, wrapper) - : new DefaultListAdapter(list, wrapper); + return new DefaultListAdapter(list, wrapper); } private DefaultListAdapter(List list, RichObjectWrapper wrapper) { @@ -84,11 +82,21 @@ public class DefaultListAdapter extends WrappingTemplateModel implements Templat } @Override - public int size() throws TemplateException { + public int getCollectionSize() throws TemplateException { return list.size(); } @Override + public boolean isEmptyCollection() throws TemplateException { + return list.isEmpty(); + } + + @Override + public TemplateModelIterator iterator() throws TemplateException { + return new IteratorToTemplateModelIteratorAdapter(list.iterator(), getObjectWrapper()); + } + + @Override public Object getAdaptedObject(Class hint) { return getWrappedObject(); } @@ -98,20 +106,6 @@ public class DefaultListAdapter extends WrappingTemplateModel implements Templat return list; } - private static class DefaultListAdapterWithCollectionSupport extends DefaultListAdapter implements - TemplateCollectionModel { - - private DefaultListAdapterWithCollectionSupport(List list, RichObjectWrapper wrapper) { - super(list, wrapper); - } - - @Override - public TemplateModelIterator iterator() throws TemplateException { - return new DefaultUnassignableIteratorAdapter(list.iterator(), getObjectWrapper()); - } - - } - @Override public TemplateModel getAPI() throws TemplateException { return ((ObjectWrapperWithAPISupport) getObjectWrapper()).wrapAsAPI(list); http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/cae86e18/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultMapAdapter.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultMapAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultMapAdapter.java index 832c675..f663990 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultMapAdapter.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultMapAdapter.java @@ -27,7 +27,7 @@ import org.apache.freemarker.core._DelayedJQuote; import org.apache.freemarker.core.model.AdapterTemplateModel; import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.model.ObjectWrapperWithAPISupport; -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; @@ -124,23 +124,23 @@ public class DefaultMapAdapter extends WrappingTemplateModel } @Override - public boolean isEmpty() { + public boolean isEmptyHash() { return map.isEmpty(); } @Override - public int size() { + public int getHashSize() { return map.size(); } @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/DefaultNonListCollectionAdapter.java ---------------------------------------------------------------------- diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultNonListCollectionAdapter.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultNonListCollectionAdapter.java index 6f8a5f9..74d74d8 100644 --- a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultNonListCollectionAdapter.java +++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/DefaultNonListCollectionAdapter.java @@ -27,7 +27,7 @@ import org.apache.freemarker.core.model.AdapterTemplateModel; import org.apache.freemarker.core.model.ObjectWrapper; import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper; import org.apache.freemarker.core.model.ObjectWrapperWithAPISupport; -import org.apache.freemarker.core.model.TemplateCollectionModelEx; +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.TemplateModelWithAPISupport; @@ -36,7 +36,7 @@ import org.apache.freemarker.core.model.WrappingTemplateModel; /** * Adapts a non-{@link List} Java {@link Collection} to the corresponding {@link TemplateModel} interface(s), most - * importantly to {@link TemplateCollectionModelEx}. For {@link List}-s, use {@link DefaultListAdapter}, or else you + * importantly to {@link TemplateCollectionModel}. For {@link List}-s, use {@link DefaultListAdapter}, or else you * lose indexed element access. * * <p> @@ -44,7 +44,7 @@ import org.apache.freemarker.core.model.WrappingTemplateModel; * is. Normally you only have to consider read-only access, as the FreeMarker template language doesn't allow writing * these collections (though of course, Java methods called from the template can violate this rule). */ -public class DefaultNonListCollectionAdapter extends WrappingTemplateModel implements TemplateCollectionModelEx, +public class DefaultNonListCollectionAdapter extends WrappingTemplateModel implements TemplateCollectionModel, AdapterTemplateModel, WrapperTemplateModel, TemplateModelWithAPISupport { private final Collection collection; @@ -69,16 +69,16 @@ public class DefaultNonListCollectionAdapter extends WrappingTemplateModel imple @Override public TemplateModelIterator iterator() throws TemplateException { - return new DefaultUnassignableIteratorAdapter(collection.iterator(), getObjectWrapper()); + return new IteratorToTemplateModelIteratorAdapter(collection.iterator(), getObjectWrapper()); } @Override - public int size() { + public int getCollectionSize() { return collection.size(); } @Override - public boolean isEmpty() { + public boolean isEmptyCollection() { return collection.isEmpty(); }
