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/05/14 10:53:02 UTC
[19/51] [partial] incubator-freemarker git commit: Migrated from Ant
to Gradle, and modularized the project. This is an incomplete migration;
there are some TODO-s in the build scripts, and release related tasks are
still missing. What works: Building th
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/PrimtiveArrayBackedReadOnlyList.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/PrimtiveArrayBackedReadOnlyList.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/PrimtiveArrayBackedReadOnlyList.java
new file mode 100644
index 0000000..0cf7d80
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/PrimtiveArrayBackedReadOnlyList.java
@@ -0,0 +1,47 @@
+/*
+ * 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.lang.reflect.Array;
+import java.util.AbstractList;
+
+/**
+ * Similar to {@link NonPrimitiveArrayBackedReadOnlyList}, but uses reflection so that it works with primitive arrays
+ * too.
+ */
+class PrimtiveArrayBackedReadOnlyList extends AbstractList {
+
+ private final Object array;
+
+ PrimtiveArrayBackedReadOnlyList(Object array) {
+ this.array = array;
+ }
+
+ @Override
+ public Object get(int index) {
+ return Array.get(array, index);
+ }
+
+ @Override
+ public int size() {
+ return Array.getLength(array);
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ReflectionCallableMemberDescriptor.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ReflectionCallableMemberDescriptor.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ReflectionCallableMemberDescriptor.java
new file mode 100644
index 0000000..eb2ade5
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ReflectionCallableMemberDescriptor.java
@@ -0,0 +1,95 @@
+/*
+ * 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.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+/**
+ * The most commonly used {@link CallableMemberDescriptor} implementation.
+ */
+final class ReflectionCallableMemberDescriptor extends CallableMemberDescriptor {
+
+ private final Member/*Method|Constructor*/ member;
+
+ /**
+ * Don't modify this array!
+ */
+ final Class[] paramTypes;
+
+ ReflectionCallableMemberDescriptor(Method member, Class[] paramTypes) {
+ this.member = member;
+ this.paramTypes = paramTypes;
+ }
+
+ ReflectionCallableMemberDescriptor(Constructor member, Class[] paramTypes) {
+ this.member = member;
+ this.paramTypes = paramTypes;
+ }
+
+ @Override
+ TemplateModel invokeMethod(DefaultObjectWrapper ow, Object obj, Object[] args)
+ throws TemplateModelException, InvocationTargetException, IllegalAccessException {
+ return ow.invokeMethod(obj, (Method) member, args);
+ }
+
+ @Override
+ Object invokeConstructor(DefaultObjectWrapper ow, Object[] args)
+ throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
+ return ((Constructor) member).newInstance(args);
+ }
+
+ @Override
+ String getDeclaration() {
+ return _MethodUtil.toString(member);
+ }
+
+ @Override
+ boolean isConstructor() {
+ return member instanceof Constructor;
+ }
+
+ @Override
+ boolean isStatic() {
+ return (member.getModifiers() & Modifier.STATIC) != 0;
+ }
+
+ @Override
+ boolean isVarargs() {
+ return _MethodUtil.isVarargs(member);
+ }
+
+ @Override
+ Class[] getParamTypes() {
+ return paramTypes;
+ }
+
+ @Override
+ String getName() {
+ return member.getName();
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/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
new file mode 100644
index 0000000..31af451
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/ResourceBundleModel.java
@@ -0,0 +1,181 @@
+/*
+ * 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.text.MessageFormat;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.Set;
+
+import org.apache.freemarker.core._DelayedJQuote;
+import org.apache.freemarker.core._TemplateModelException;
+import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+/**
+ * <p>A hash model that wraps a resource bundle. Makes it convenient to store
+ * localized content in the data model. It also acts as a method model that will
+ * take a resource key and arbitrary number of arguments and will apply
+ * {@link MessageFormat} with arguments on the string represented by the key.</p>
+ *
+ * <p>Typical usages:</p>
+ * <ul>
+ * <li><tt>bundle.resourceKey</tt> will retrieve the object from resource bundle
+ * with key <tt>resourceKey</tt></li>
+ * <li><tt>bundle("patternKey", arg1, arg2, arg3)</tt> will retrieve the string
+ * from resource bundle with key <tt>patternKey</tt>, and will use it as a pattern
+ * for MessageFormat with arguments arg1, arg2 and arg3</li>
+ * </ul>
+ */
+public class ResourceBundleModel
+ extends
+ BeanModel
+ implements
+ TemplateMethodModelEx {
+
+ private Hashtable formats = null;
+
+ public ResourceBundleModel(ResourceBundle bundle, DefaultObjectWrapper wrapper) {
+ super(bundle, wrapper);
+ }
+
+ /**
+ * Overridden to invoke the get method of the resource bundle.
+ */
+ @Override
+ protected TemplateModel invokeGenericGet(Map keyMap, Class clazz, String key)
+ throws TemplateModelException {
+ try {
+ return wrap(((ResourceBundle) object).getObject(key));
+ } catch (MissingResourceException e) {
+ throw new _TemplateModelException(e,
+ "No ", new _DelayedJQuote(key), " key in the ResourceBundle. "
+ + "Note that conforming to the ResourceBundle Java API, this is an error and not just "
+ + "a missing sub-variable (a null).");
+ }
+ }
+
+ /**
+ * Returns true if this bundle contains no objects.
+ */
+ @Override
+ public boolean isEmpty() {
+ return !((ResourceBundle) object).getKeys().hasMoreElements() &&
+ super.isEmpty();
+ }
+
+ @Override
+ public int size() {
+ return keySet().size();
+ }
+
+ @Override
+ protected Set keySet() {
+ Set set = super.keySet();
+ Enumeration e = ((ResourceBundle) object).getKeys();
+ while (e.hasMoreElements()) {
+ set.add(e.nextElement());
+ }
+ return set;
+ }
+
+ /**
+ * Takes first argument as a resource key, looks up a string in resource bundle
+ * with this key, then applies a MessageFormat.format on the string with the
+ * rest of the arguments. The created MessageFormats are cached for later reuse.
+ */
+ @Override
+ public Object exec(List arguments)
+ throws TemplateModelException {
+ // Must have at least one argument - the key
+ if (arguments.size() < 1)
+ throw new TemplateModelException("No message key was specified");
+ // Read it
+ Iterator it = arguments.iterator();
+ String key = unwrap((TemplateModel) it.next()).toString();
+ try {
+ if (!it.hasNext()) {
+ return wrap(((ResourceBundle) object).getObject(key));
+ }
+
+ // Copy remaining arguments into an Object[]
+ int args = arguments.size() - 1;
+ Object[] params = new Object[args];
+ for (int i = 0; i < args; ++i)
+ params[i] = unwrap((TemplateModel) it.next());
+
+ // Invoke format
+ return new BeanAndStringModel(format(key, params), wrapper);
+ } catch (MissingResourceException e) {
+ throw new TemplateModelException("No such key: " + key);
+ } catch (Exception e) {
+ throw new TemplateModelException(e.getMessage());
+ }
+ }
+
+ /**
+ * Provides direct access to caching format engine from code (instead of from script).
+ */
+ public String format(String key, Object[] params)
+ throws MissingResourceException {
+ // Check to see if we already have a cache for message formats
+ // and construct it if we don't
+ // NOTE: this block statement should be synchronized. However
+ // concurrent creation of two caches will have no harmful
+ // consequences, and we avoid a performance hit.
+ /* synchronized(this) */
+ {
+ if (formats == null)
+ formats = new Hashtable();
+ }
+
+ MessageFormat format = null;
+ // Check to see if we already have a requested MessageFormat cached
+ // and construct it if we don't
+ // NOTE: this block statement should be synchronized. However
+ // concurrent creation of two formats will have no harmful
+ // consequences, and we avoid a performance hit.
+ /* synchronized(formats) */
+ {
+ format = (MessageFormat) formats.get(key);
+ if (format == null) {
+ format = new MessageFormat(((ResourceBundle) object).getString(key));
+ format.setLocale(getBundle().getLocale());
+ formats.put(key, format);
+ }
+ }
+
+ // Perform the formatting. We synchronize on it in case it
+ // contains date formatting, which is not thread-safe.
+ synchronized (format) {
+ return format.format(params);
+ }
+ }
+
+ public ResourceBundle getBundle() {
+ return (ResourceBundle) object;
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapper.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapper.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapper.java
new file mode 100644
index 0000000..e456dc6
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/RestrictedObjectWrapper.java
@@ -0,0 +1,98 @@
+/*
+ * 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.lang.ref.ReferenceQueue;
+import java.lang.ref.WeakReference;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.apache.freemarker.core.Version;
+import org.apache.freemarker.core.model.TemplateHashModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+/**
+ * A restricted version of {@link DefaultObjectWrapper} that doesn't expose arbitrary object, just those that directly
+ * correspond to the {@link TemplateModel} sub-interfaces ({@code String}, {@code Map} and such). If it had to wrap
+ * other kind of objects, it will throw exception. It will also block {@code ?api} calls on the values it wraps.
+ */
+public class RestrictedObjectWrapper extends DefaultObjectWrapper {
+
+ protected RestrictedObjectWrapper(Builder builder, boolean finalizeConstruction) {
+ super(builder, finalizeConstruction);
+ }
+
+ /**
+ * Called if a type other than the simple ones we know about is passed in.
+ * In this implementation, this just throws an exception.
+ */
+ @Override
+ protected TemplateModel handleNonBasicTypes(Object obj) throws TemplateModelException {
+ throw new TemplateModelException("RestrictedObjectWrapper deliberately won't wrap this type: "
+ + obj.getClass().getName());
+ }
+
+ @Override
+ public TemplateHashModel wrapAsAPI(Object obj) throws TemplateModelException {
+ throw new TemplateModelException("RestrictedObjectWrapper deliberately doesn't allow ?api.");
+ }
+
+ protected static abstract class ExtendableBuilder<
+ ProductT extends RestrictedObjectWrapper, SelfT extends ExtendableBuilder<ProductT,
+ SelfT>> extends DefaultObjectWrapper.ExtendableBuilder<ProductT, SelfT> {
+
+ protected ExtendableBuilder(Version incompatibleImprovements, boolean isIncompImprsAlreadyNormalized) {
+ super(incompatibleImprovements, isIncompImprsAlreadyNormalized);
+ }
+
+ }
+
+ public static final class Builder extends ExtendableBuilder<RestrictedObjectWrapper, Builder> {
+
+ private final static Map<ClassLoader, Map<Builder, WeakReference<RestrictedObjectWrapper>>>
+ INSTANCE_CACHE = new WeakHashMap<>();
+
+ private final static ReferenceQueue<RestrictedObjectWrapper> INSTANCE_CACHE_REF_QUEUE = new ReferenceQueue<>();
+
+ public Builder(Version incompatibleImprovements) {
+ super(incompatibleImprovements, false);
+ }
+
+ @Override
+ public RestrictedObjectWrapper build() {
+ return DefaultObjectWrapperTCCLSingletonUtil.getSingleton(
+ this, INSTANCE_CACHE, INSTANCE_CACHE_REF_QUEUE, ConstructorInvoker.INSTANCE);
+ }
+
+ private static class ConstructorInvoker
+ implements DefaultObjectWrapperTCCLSingletonUtil._ConstructorInvoker<RestrictedObjectWrapper, Builder> {
+
+ private static final ConstructorInvoker INSTANCE = new ConstructorInvoker();
+
+ @Override
+ public RestrictedObjectWrapper invoke(Builder builder) {
+ return new RestrictedObjectWrapper(builder, true);
+ }
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/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
new file mode 100644
index 0000000..e9b6156
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SequenceAdapter.java
@@ -0,0 +1,68 @@
+/*
+ * 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.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelAdapter;
+import org.apache.freemarker.core.model.TemplateModelException;
+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 (TemplateModelException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ @Override
+ public Object get(int index) {
+ try {
+ return wrapper.unwrap(model.get(index));
+ } catch (TemplateModelException e) {
+ throw new UndeclaredThrowableException(e);
+ }
+ }
+
+ public TemplateSequenceModel getTemplateSequenceModel() {
+ return model;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/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
new file mode 100644
index 0000000..0975ac4
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SetAdapter.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;
+
+/**
+ */
+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/3fd56062/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
new file mode 100644
index 0000000..cf399ab
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleCollection.java
@@ -0,0 +1,138 @@
+/*
+ * 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.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateModelIterator;
+import org.apache.freemarker.core.model.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
+ * <tt>TemplateModelException</tt> 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 TemplateModelException {
+ if (!iteratorOwnedByMe) {
+ synchronized (SimpleCollection.this) {
+ checkIteratorNotOwned();
+ iteratorOwned = true;
+ iteratorOwnedByMe = true;
+ }
+ }
+
+ if (!iterator.hasNext()) {
+ throw new TemplateModelException("The collection has no more items.");
+ }
+
+ Object value = iterator.next();
+ return value instanceof TemplateModel ? (TemplateModel) value : wrap(value);
+ }
+
+ @Override
+ public boolean hasNext() throws TemplateModelException {
+ // Calling hasNext may looks safe, but I have met sync. problems.
+ if (!iteratorOwnedByMe) {
+ synchronized (SimpleCollection.this) {
+ checkIteratorNotOwned();
+ }
+ }
+
+ return iterator.hasNext();
+ }
+
+ private void checkIteratorNotOwned() throws TemplateModelException {
+ if (iteratorOwned) {
+ throw new TemplateModelException(
+ "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/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleDate.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleDate.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleDate.java
new file mode 100644
index 0000000..cf6e753
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleDate.java
@@ -0,0 +1,85 @@
+/*
+ * 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 org.apache.freemarker.core.model.TemplateDateModel;
+
+/**
+ * A simple implementation of the <tt>TemplateDateModel</tt>
+ * interface. Note that this class is immutable.
+ * <p>This class is thread-safe.
+ */
+public class SimpleDate implements TemplateDateModel, Serializable {
+ private final java.util.Date date;
+ private final int type;
+
+ /**
+ * Creates a new date model wrapping the specified date object and
+ * having DATE type.
+ */
+ public SimpleDate(java.sql.Date date) {
+ this(date, DATE);
+ }
+
+ /**
+ * Creates a new date model wrapping the specified time object and
+ * having TIME type.
+ */
+ public SimpleDate(java.sql.Time time) {
+ this(time, TIME);
+ }
+
+ /**
+ * Creates a new date model wrapping the specified time object and
+ * having DATETIME type.
+ */
+ public SimpleDate(java.sql.Timestamp datetime) {
+ this(datetime, DATETIME);
+ }
+
+ /**
+ * Creates a new date model wrapping the specified date object and
+ * having the specified type.
+ */
+ public SimpleDate(java.util.Date date, int type) {
+ if (date == null) {
+ throw new IllegalArgumentException("date == null");
+ }
+ this.date = date;
+ this.type = type;
+ }
+
+ @Override
+ public java.util.Date getAsDate() {
+ return date;
+ }
+
+ @Override
+ public int getDateType() {
+ return type;
+ }
+
+ @Override
+ public String toString() {
+ return date.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/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
new file mode 100644
index 0000000..f520c3d
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleHash.java
@@ -0,0 +1,296 @@
+/*
+ * 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.ConcurrentModificationException;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+import org.apache.freemarker.core._DelayedJQuote;
+import org.apache.freemarker.core._TemplateModelException;
+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.TemplateHashModelEx;
+import org.apache.freemarker.core.model.TemplateHashModelEx2;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.WrappingTemplateModel;
+
+/**
+ * A simple implementation of the {@link TemplateHashModelEx} interface, using its own underlying {@link Map} or
+ * {@link SortedMap} for storing the hash entries. If you are wrapping an already existing {@link Map}, you should
+ * certainly use {@link DefaultMapAdapter} instead (see comparison below).
+ *
+ * <p>
+ * This class is thread-safe if you don't call modifying methods (like {@link #put(String, Object)},
+ * {@link #remove(String)}, etc.) after you have made the object available for multiple threads (assuming you have
+ * published it safely to the other threads; see JSR-133 Java Memory Model). These methods aren't called by FreeMarker,
+ * so it's usually not a concern.
+ *
+ * <p>
+ * <b>{@link SimpleHash} VS {@link DefaultMapAdapter} - Which to use when?</b>
+ *
+ * <p>
+ * For a {@link Map} that exists regardless of FreeMarker, only you need to access it from templates,
+ * {@link DefaultMapAdapter} should be the default choice, as it reflects the exact behavior of the underlying
+ * {@link Map} (no surprises), can be unwrapped to the originally wrapped object (important when passing it to Java
+ * methods from the template), and has more predictable performance (no spikes).
+ *
+ * <p>
+ * For a hash that's made specifically to be used from templates, creating an empty {@link SimpleHash} then filling it
+ * with {@link SimpleHash#put(String, Object)} is usually the way to go, as the resulting hash is significantly faster
+ * to read from templates than a {@link DefaultMapAdapter} (though it's somewhat slower to read from a plain Java method
+ * to which it had to be passed adapted to a {@link Map}).
+ *
+ * <p>
+ * It also matters if for how many times will the <em>same</em> {@link Map} entry be read from the template(s) later, on
+ * average. If, on average, you read each entry for more than 4 times, {@link SimpleHash} will be most certainly faster,
+ * but if for 2 times or less (and especially if not at all) then {@link DefaultMapAdapter} will be faster. Before
+ * choosing based on performance though, pay attention to the behavioral differences; {@link SimpleHash} will
+ * shallow-copy the original {@link Map} at construction time, so key order will be lost in some cases, and it won't
+ * reflect {@link Map} content changes after the {@link SimpleHash} construction, also {@link SimpleHash} can't be
+ * unwrapped to the original {@link Map} instance.
+ *
+ * @see DefaultMapAdapter
+ * @see TemplateHashModelEx
+ */
+public class SimpleHash extends WrappingTemplateModel implements TemplateHashModelEx2, Serializable {
+
+ private final Map map;
+ private boolean putFailed;
+ private Map unwrappedMap;
+
+ /**
+ * Creates an empty simple hash using the specified object wrapper.
+ * @param wrapper The object wrapper to use to wrap objects into
+ * {@link TemplateModel} instances. Not {@code null}.
+ */
+ public SimpleHash(ObjectWrapper wrapper) {
+ super(wrapper);
+ map = new HashMap();
+ }
+
+ /**
+ * Creates a new hash by shallow-coping (possibly cloning) the underlying map; in many applications you should use
+ * {@link DefaultMapAdapter} instead.
+ *
+ * @param map
+ * The Map to use for the key/value pairs. It makes a copy for internal use. If the map implements the
+ * {@link SortedMap} interface, the internal copy will be a {@link TreeMap}, otherwise it will be a
+ * @param wrapper
+ * The object wrapper to use to wrap contained objects into {@link TemplateModel} instances. Not
+ * {@code null}.
+ */
+ public SimpleHash(Map map, ObjectWrapper wrapper) {
+ super(wrapper);
+ Map mapCopy;
+ try {
+ mapCopy = copyMap(map);
+ } catch (ConcurrentModificationException cme) {
+ //This will occur extremely rarely.
+ //If it does, we just wait 5 ms and try again. If
+ // the ConcurrentModificationException
+ // is thrown again, we just let it bubble up this time.
+ // TODO: Maybe we should log here.
+ try {
+ Thread.sleep(5);
+ } catch (InterruptedException ie) {
+ // Ignored
+ }
+ synchronized (map) {
+ mapCopy = copyMap(map);
+ }
+ }
+ this.map = mapCopy;
+ }
+
+ protected Map copyMap(Map map) {
+ if (map instanceof HashMap) {
+ return (Map) ((HashMap) map).clone();
+ }
+ if (map instanceof SortedMap) {
+ if (map instanceof TreeMap) {
+ return (Map) ((TreeMap) map).clone();
+ } else {
+ return new TreeMap((SortedMap) map);
+ }
+ }
+ return new HashMap(map);
+ }
+
+ /**
+ * Adds a key-value entry to this hash.
+ *
+ * @param key
+ * The name by which the object is identified in the template.
+ * @param value
+ * The value to which the name will be associated. This will only be wrapped to {@link TemplateModel}
+ * lazily when it's first read.
+ */
+ public void put(String key, Object value) {
+ map.put(key, value);
+ unwrappedMap = null;
+ }
+
+ /**
+ * Puts a boolean in the map
+ *
+ * @param key the name by which the resulting <tt>TemplateModel</tt>
+ * is identified in the template.
+ * @param b the boolean to store.
+ */
+ public void put(String key, boolean b) {
+ put(key, b ? TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE);
+ }
+
+ @Override
+ public TemplateModel get(String key) throws TemplateModelException {
+ Object result;
+ try {
+ result = map.get(key);
+ } catch (ClassCastException e) {
+ throw new _TemplateModelException(e,
+ "ClassCastException while getting Map entry with String key ",
+ new _DelayedJQuote(key));
+ } catch (NullPointerException e) {
+ throw new _TemplateModelException(e,
+ "NullPointerException while getting Map entry with String key ",
+ new _DelayedJQuote(key));
+ }
+ // The key to use for putting -- it's the key that already exists in
+ // the map (either key or charKey below). This way, we'll never put a
+ // new key in the map, avoiding spurious ConcurrentModificationException
+ // from another thread iterating over the map, see bug #1939742 in
+ // SourceForge tracker.
+ Object putKey = null;
+ if (result == null) {
+ // Check for Character key if this is a single-character string.
+ // In SortedMap-s, however, we can't do that safely, as it can cause ClassCastException.
+ if (key.length() == 1 && !(map instanceof SortedMap)) {
+ Character charKey = Character.valueOf(key.charAt(0));
+ try {
+ result = map.get(charKey);
+ if (result != null || map.containsKey(charKey)) {
+ putKey = charKey;
+ }
+ } catch (ClassCastException e) {
+ throw new _TemplateModelException(e,
+ "ClassCastException while getting Map entry with Character key ",
+ new _DelayedJQuote(key));
+ } catch (NullPointerException e) {
+ throw new _TemplateModelException(e,
+ "NullPointerException while getting Map entry with Character key ",
+ new _DelayedJQuote(key));
+ }
+ }
+ if (putKey == null) {
+ if (!map.containsKey(key)) {
+ return null;
+ } else {
+ putKey = key;
+ }
+ }
+ } else {
+ putKey = key;
+ }
+
+ if (result instanceof TemplateModel) {
+ return (TemplateModel) result;
+ }
+
+ TemplateModel tm = wrap(result);
+ if (!putFailed) {
+ try {
+ map.put(putKey, tm);
+ } catch (Exception e) {
+ // If it's immutable or something, we just keep going.
+ putFailed = true;
+ }
+ }
+ return tm;
+ }
+
+ /**
+ * Tells if the map contains a key or not, regardless if the associated value is {@code null} or not.
+ * @since 2.3.20
+ */
+ public boolean containsKey(String key) {
+ return map.containsKey(key);
+ }
+
+ /**
+ * Removes the given key from the underlying map.
+ *
+ * @param key the key to be removed
+ */
+ public void remove(String key) {
+ map.remove(key);
+ }
+
+ /**
+ * Adds all the key/value entries in the map
+ * @param m the map with the entries to add, the keys are assumed to be strings.
+ */
+
+ public void putAll(Map m) {
+ for (Iterator it = m.entrySet().iterator(); it.hasNext(); ) {
+ Map.Entry entry = (Map.Entry) it.next();
+ put((String) entry.getKey(), entry.getValue());
+ }
+ }
+
+ /**
+ * Returns the {@code toString()} of the underlying {@link Map}.
+ */
+ @Override
+ public String toString() {
+ return map.toString();
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public boolean isEmpty() {
+ return map == null || map.isEmpty();
+ }
+
+ @Override
+ public TemplateCollectionModel keys() {
+ return new SimpleCollection(map.keySet(), getObjectWrapper());
+ }
+
+ @Override
+ public TemplateCollectionModel values() {
+ return new SimpleCollection(map.values(), getObjectWrapper());
+ }
+
+ @Override
+ public KeyValuePairIterator keyValuePairIterator() {
+ return new MapKeyValuePairIterator(map, getObjectWrapper());
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java
new file mode 100644
index 0000000..ae5c531
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleMethod.java
@@ -0,0 +1,174 @@
+/*
+ * 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.lang.reflect.Array;
+import java.lang.reflect.Member;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.freemarker.core._DelayedFTLTypeDescription;
+import org.apache.freemarker.core._DelayedOrdinal;
+import org.apache.freemarker.core._ErrorDescriptionBuilder;
+import org.apache.freemarker.core._TemplateModelException;
+import org.apache.freemarker.core.model.ObjectWrapperAndUnwrapper;
+import org.apache.freemarker.core.model.TemplateMarkupOutputModel;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.util._ClassUtil;
+
+/**
+ * This class is used for as a base for non-overloaded method models and for constructors.
+ * (For overloaded methods and constructors see {@link OverloadedMethods}.)
+ */
+class SimpleMethod {
+
+ static final String MARKUP_OUTPUT_TO_STRING_TIP
+ = "A markup output value can be converted to markup string like value?markup_string. "
+ + "But consider if the Java method whose argument it will be can handle markup strings properly.";
+
+ private final Member member;
+ private final Class[] argTypes;
+
+ protected SimpleMethod(Member member, Class[] argTypes) {
+ this.member = member;
+ this.argTypes = argTypes;
+ }
+
+ Object[] unwrapArguments(List arguments, DefaultObjectWrapper wrapper) throws TemplateModelException {
+ if (arguments == null) {
+ arguments = Collections.EMPTY_LIST;
+ }
+ boolean isVarArg = _MethodUtil.isVarargs(member);
+ int typesLen = argTypes.length;
+ if (isVarArg) {
+ if (typesLen - 1 > arguments.size()) {
+ throw new _TemplateModelException(
+ _MethodUtil.invocationErrorMessageStart(member),
+ " takes at least ", Integer.valueOf(typesLen - 1),
+ typesLen - 1 == 1 ? " argument" : " arguments", ", but ",
+ Integer.valueOf(arguments.size()), " was given.");
+ }
+ } else if (typesLen != arguments.size()) {
+ throw new _TemplateModelException(
+ _MethodUtil.invocationErrorMessageStart(member),
+ " takes ", Integer.valueOf(typesLen), typesLen == 1 ? " argument" : " arguments", ", but ",
+ Integer.valueOf(arguments.size()), " was given.");
+ }
+
+ return unwrapArguments(arguments, argTypes, isVarArg, wrapper);
+ }
+
+ private Object[] unwrapArguments(List args, Class[] argTypes, boolean isVarargs,
+ DefaultObjectWrapper w)
+ throws TemplateModelException {
+ if (args == null) return null;
+
+ int typesLen = argTypes.length;
+ int argsLen = args.size();
+
+ Object[] unwrappedArgs = new Object[typesLen];
+
+ // Unwrap arguments:
+ Iterator it = args.iterator();
+ int normalArgCnt = isVarargs ? typesLen - 1 : typesLen;
+ int argIdx = 0;
+ while (argIdx < normalArgCnt) {
+ Class argType = argTypes[argIdx];
+ TemplateModel argVal = (TemplateModel) it.next();
+ Object unwrappedArgVal = w.tryUnwrapTo(argVal, argType);
+ if (unwrappedArgVal == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
+ throw createArgumentTypeMismarchException(argIdx, argVal, argType);
+ }
+ if (unwrappedArgVal == null && argType.isPrimitive()) {
+ throw createNullToPrimitiveArgumentException(argIdx, argType);
+ }
+
+ unwrappedArgs[argIdx++] = unwrappedArgVal;
+ }
+ if (isVarargs) {
+ // The last argType, which is the vararg type, wasn't processed yet.
+
+ Class varargType = argTypes[typesLen - 1];
+ Class varargItemType = varargType.getComponentType();
+ if (!it.hasNext()) {
+ unwrappedArgs[argIdx++] = Array.newInstance(varargItemType, 0);
+ } else {
+ TemplateModel argVal = (TemplateModel) it.next();
+
+ Object unwrappedArgVal;
+ // We first try to treat the last argument as a vararg *array*.
+ // This is consistent to what OverloadedVarArgMethod does.
+ if (argsLen - argIdx == 1
+ && (unwrappedArgVal = w.tryUnwrapTo(argVal, varargType))
+ != ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
+ // It was a vararg array.
+ unwrappedArgs[argIdx++] = unwrappedArgVal;
+ } else {
+ // It wasn't a vararg array, so we assume it's a vararg
+ // array *item*, possibly followed by further ones.
+ int varargArrayLen = argsLen - argIdx;
+ Object varargArray = Array.newInstance(varargItemType, varargArrayLen);
+ for (int varargIdx = 0; varargIdx < varargArrayLen; varargIdx++) {
+ TemplateModel varargVal = (TemplateModel) (varargIdx == 0 ? argVal : it.next());
+ Object unwrappedVarargVal = w.tryUnwrapTo(varargVal, varargItemType);
+ if (unwrappedVarargVal == ObjectWrapperAndUnwrapper.CANT_UNWRAP_TO_TARGET_CLASS) {
+ throw createArgumentTypeMismarchException(
+ argIdx + varargIdx, varargVal, varargItemType);
+ }
+
+ if (unwrappedVarargVal == null && varargItemType.isPrimitive()) {
+ throw createNullToPrimitiveArgumentException(argIdx + varargIdx, varargItemType);
+ }
+ Array.set(varargArray, varargIdx, unwrappedVarargVal);
+ }
+ unwrappedArgs[argIdx++] = varargArray;
+ }
+ }
+ }
+
+ return unwrappedArgs;
+ }
+
+ private TemplateModelException createArgumentTypeMismarchException(
+ int argIdx, TemplateModel argVal, Class targetType) {
+ _ErrorDescriptionBuilder desc = new _ErrorDescriptionBuilder(
+ _MethodUtil.invocationErrorMessageStart(member), " couldn't be called: Can't convert the ",
+ new _DelayedOrdinal(Integer.valueOf(argIdx + 1)),
+ " argument's value to the target Java type, ", _ClassUtil.getShortClassName(targetType),
+ ". The type of the actual value was: ", new _DelayedFTLTypeDescription(argVal));
+ if (argVal instanceof TemplateMarkupOutputModel && (targetType.isAssignableFrom(String.class))) {
+ desc.tip(MARKUP_OUTPUT_TO_STRING_TIP);
+ }
+ return new _TemplateModelException(desc);
+ }
+
+ private TemplateModelException createNullToPrimitiveArgumentException(int argIdx, Class targetType) {
+ return new _TemplateModelException(
+ _MethodUtil.invocationErrorMessageStart(member), " couldn't be called: The value of the ",
+ new _DelayedOrdinal(Integer.valueOf(argIdx + 1)),
+ " argument was null, but the target Java parameter type (", _ClassUtil.getShortClassName(targetType),
+ ") is primitive and so can't store null.");
+ }
+
+ protected Member getMember() {
+ return member;
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleNumber.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleNumber.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleNumber.java
new file mode 100644
index 0000000..6f588b6
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleNumber.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.io.Serializable;
+
+import org.apache.freemarker.core.model.TemplateNumberModel;
+
+
+/**
+ * A simple implementation of the <tt>TemplateNumberModel</tt>
+ * interface. Note that this class is immutable.
+ *
+ * <p>This class is thread-safe.
+ */
+public final class SimpleNumber implements TemplateNumberModel, Serializable {
+
+ /**
+ * @serial the value of this <tt>SimpleNumber</tt>
+ */
+ private final Number value;
+
+ public SimpleNumber(Number value) {
+ this.value = value;
+ }
+
+ public SimpleNumber(byte val) {
+ value = Byte.valueOf(val);
+ }
+
+ public SimpleNumber(short val) {
+ value = Short.valueOf(val);
+ }
+
+ public SimpleNumber(int val) {
+ value = Integer.valueOf(val);
+ }
+
+ public SimpleNumber(long val) {
+ value = Long.valueOf(val);
+ }
+
+ public SimpleNumber(float val) {
+ value = Float.valueOf(val);
+ }
+
+ public SimpleNumber(double val) {
+ value = Double.valueOf(val);
+ }
+
+ @Override
+ public Number getAsNumber() {
+ return value;
+ }
+
+ @Override
+ public String toString() {
+ return value.toString();
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleScalar.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleScalar.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleScalar.java
new file mode 100644
index 0000000..79a8820
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleScalar.java
@@ -0,0 +1,73 @@
+/*
+ * 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 org.apache.freemarker.core.model.TemplateScalarModel;
+
+/**
+ * A simple implementation of the <tt>TemplateScalarModel</tt>
+ * interface, using a <tt>String</tt>.
+ * As of version 2.0 this object is immutable.
+ *
+ * <p>This class is thread-safe.
+ *
+ * @see SimpleSequence
+ * @see SimpleHash
+ */
+public final class SimpleScalar
+implements TemplateScalarModel, Serializable {
+
+ /**
+ * @serial the value of this <tt>SimpleScalar</tt> if it wraps a
+ * <tt>String</tt>.
+ */
+ private final String value;
+
+ /**
+ * Constructs a <tt>SimpleScalar</tt> containing a string value.
+ * @param value the string value. If this is {@code null}, its value in FTL will be {@code ""}.
+ */
+ public SimpleScalar(String value) {
+ this.value = value;
+ }
+
+ @Override
+ public String getAsString() {
+ return (value == null) ? "" : value;
+ }
+
+ @Override
+ public String toString() {
+ // [2.4] Shouldn't return null
+ return value;
+ }
+
+ /**
+ * Same as calling the constructor, except that for a {@code null} parameter it returns null.
+ *
+ * @since 2.3.23
+ */
+ public static SimpleScalar newInstanceOrNull(String s) {
+ return s != null ? new SimpleScalar(s) : null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/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
new file mode 100644
index 0000000..1b949f1
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SimpleSequence.java
@@ -0,0 +1,162 @@
+/*
+ * 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.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.freemarker.core.model.ObjectWrapper;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.apache.freemarker.core.model.TemplateSequenceModel;
+import org.apache.freemarker.core.model.WrappingTemplateModel;
+
+/**
+ * A simple implementation of the {@link TemplateSequenceModel} interface, using its own underlying {@link List} for
+ * storing the list items. If you are wrapping an already existing {@link List} or {@code array}, you should certainly
+ * use {@link DefaultMapAdapter} or {@link DefaultArrayAdapter} (see comparison below).
+ *
+ * <p>
+ * This class is thread-safe if you don't call modifying methods (like {@link #add(Object)}) after you have made the
+ * object available for multiple threads (assuming you have published it safely to the other threads; see JSR-133 Java
+ * Memory Model). These methods aren't called by FreeMarker, so it's usually not a concern.
+ *
+ * <p>
+ * <b>{@link SimpleSequence} VS {@link DefaultListAdapter}/{@link DefaultArrayAdapter} - Which to use when?</b>
+ * </p>
+ *
+ * <p>
+ * For a {@link List} or {@code array} that exists regardless of FreeMarker, only you need to access it from templates,
+ * {@link DefaultMapAdapter} should be the default choice, as it can be unwrapped to the originally wrapped object
+ * (important when passing it to Java methods from the template). It also has more predictable performance (no spikes).
+ *
+ * <p>
+ * For a sequence that's made specifically to be used from templates, creating an empty {@link SimpleSequence} then
+ * filling it with {@link SimpleSequence#add(Object)} is usually the way to go, as the resulting sequence is
+ * significantly faster to read from templates than a {@link DefaultListAdapter} (though it's somewhat slower to read
+ * from a plain Java method to which it had to be passed adapted to a {@link List}).
+ *
+ * <p>
+ * It also matters if for how many times will the <em>same</em> {@link List} entry be read from the template(s) later,
+ * on average. If, on average, you read each entry for more than 4 times, {@link SimpleSequence} will be most
+ * certainly faster, but if for 2 times or less (and especially if not at all) then {@link DefaultMapAdapter} will
+ * be faster. Before choosing based on performance though, pay attention to the behavioral differences;
+ * {@link SimpleSequence} will shallow-copy the original {@link List} at construction time, so it won't reflect
+ * {@link List} content changes after the {@link SimpleSequence} construction, also {@link SimpleSequence} can't be
+ * unwrapped to the original wrapped instance.
+ *
+ * @see DefaultListAdapter
+ * @see DefaultArrayAdapter
+ * @see TemplateSequenceModel
+ */
+public class SimpleSequence extends WrappingTemplateModel implements TemplateSequenceModel, Serializable {
+
+ /**
+ * The {@link List} that stored the elements of this sequence. It might contains both {@link TemplateModel} elements
+ * and non-{@link TemplateModel} elements.
+ */
+ protected final List list;
+
+ /**
+ * Constructs an empty sequence using the specified object wrapper.
+ *
+ * @param wrapper
+ * The object wrapper to use to wrap the list items into {@link TemplateModel} instances. Not
+ * {@code null}.
+ */
+ public SimpleSequence(ObjectWrapper wrapper) {
+ super(wrapper);
+ list = new ArrayList();
+ }
+
+ /**
+ * Constructs an empty simple sequence with preallocated capacity.
+ *
+ * @param wrapper
+ * See the similar parameter of {@link SimpleSequence#SimpleSequence(ObjectWrapper)}.
+ *
+ * @since 2.3.21
+ */
+ public SimpleSequence(int capacity, ObjectWrapper wrapper) {
+ super(wrapper);
+ list = new ArrayList(capacity);
+ }
+
+ /**
+ * Constructs a simple sequence that will contain the elements from the specified {@link Collection}; consider
+ * using {@link DefaultListAdapter} instead.
+ *
+ * @param collection
+ * The collection containing the initial items of the sequence. A shallow copy of this collection is made
+ * immediately for internal use (thus, later modification on the parameter collection won't be visible in
+ * the resulting sequence). The items however, will be only wrapped with the {@link ObjectWrapper}
+ * lazily, when first needed.
+ * @param wrapper
+ * See the similar parameter of {@link SimpleSequence#SimpleSequence(ObjectWrapper)}.
+ */
+ public SimpleSequence(Collection collection, ObjectWrapper wrapper) {
+ super(wrapper);
+ list = new ArrayList(collection);
+ }
+
+ /**
+ * Adds an arbitrary object to the end of this sequence. If the newly added object does not implement the
+ * {@link TemplateModel} interface, it will be wrapped into the appropriate {@link TemplateModel} interface when
+ * it's first read (lazily).
+ *
+ * @param obj
+ * The object to be added.
+ */
+ public void add(Object obj) {
+ list.add(obj);
+ }
+
+ /**
+ * Returns the item at the specified index of the list. If the item isn't yet an {@link TemplateModel}, it will wrap
+ * it to one now, and writes it back into the backing list.
+ */
+ @Override
+ public TemplateModel get(int index) throws TemplateModelException {
+ 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) {
+ return null;
+ }
+ }
+
+ @Override
+ public int size() {
+ return list.size();
+ }
+
+ @Override
+ public String toString() {
+ return list.toString();
+ }
+
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SingletonCustomizer.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SingletonCustomizer.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SingletonCustomizer.java
new file mode 100644
index 0000000..e4a0e5a
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/SingletonCustomizer.java
@@ -0,0 +1,51 @@
+/*
+ * 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.model.ObjectWrapper;
+
+/**
+ * Marker interface useful when used together with {@link MethodAppearanceFineTuner} and such customizer objects, to
+ * indicate that it <b>doesn't contain reference to the {@link ObjectWrapper}</b> (so beware with non-static inner
+ * classes) and can be and should be used in call introspection cache keys. This also implies that you won't
+ * invoke many instances of the class, rather just reuse the same (or same few) instances over and over. Furthermore,
+ * the instances must be thread-safe. The typical pattern in which this instance should be used is like this:
+ *
+ * <pre>static class MyMethodAppearanceFineTuner implements MethodAppearanceFineTuner, SingletonCustomizer {
+ *
+ * // This is the singleton:
+ * static final MyMethodAppearanceFineTuner INSTANCE = new MyMethodAppearanceFineTuner();
+ *
+ * // Private, so it can't be constructed from outside this class.
+ * private MyMethodAppearanceFineTuner() { }
+ *
+ * @Override
+ * public void fineTuneMethodAppearance(...) {
+ * // Do something here, only using the parameters and maybe some other singletons.
+ * ...
+ * }
+ *
+ * }</pre>
+ *
+ * @since 2.3.21
+ */
+public interface SingletonCustomizer {
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/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
new file mode 100644
index 0000000..fbee788
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModel.java
@@ -0,0 +1,177 @@
+/*
+ * 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.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.freemarker.core._CoreLogs;
+import org.apache.freemarker.core.model.TemplateCollectionModel;
+import org.apache.freemarker.core.model.TemplateHashModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+import org.slf4j.Logger;
+
+/**
+ * Wraps the static fields and methods of a class in a
+ * {@link org.apache.freemarker.core.model.TemplateHashModel}.
+ * Fields are wrapped using {@link DefaultObjectWrapper#wrap(Object)}, and
+ * methods are wrapped into an appropriate {@link org.apache.freemarker.core.model.TemplateMethodModelEx} instance.
+ * Unfortunately, there is currently no support for bean property-style
+ * calls of static methods, similar to that in {@link BeanModel}.
+ */
+final class StaticModel implements TemplateHashModelEx {
+
+ private static final Logger LOG = _CoreLogs.OBJECT_WRAPPER;
+
+ private final Class clazz;
+ private final DefaultObjectWrapper wrapper;
+ private final Map map = new HashMap();
+
+ StaticModel(Class clazz, DefaultObjectWrapper wrapper) throws TemplateModelException {
+ this.clazz = clazz;
+ this.wrapper = wrapper;
+ populate();
+ }
+
+ /**
+ * Returns the field or method named by the <tt>key</tt>
+ * parameter.
+ */
+ @Override
+ public TemplateModel get(String key) throws TemplateModelException {
+ Object model = map.get(key);
+ // Simple method, overloaded method or final field -- these have cached
+ // template models
+ if (model instanceof TemplateModel)
+ return (TemplateModel) model;
+ // Non-final field; this must be evaluated on each call.
+ if (model instanceof Field) {
+ try {
+ return wrapper.getOuterIdentity().wrap(((Field) model).get(null));
+ } catch (IllegalAccessException e) {
+ throw new TemplateModelException(
+ "Illegal access for field " + key + " of class " + clazz.getName());
+ }
+ }
+
+ throw new TemplateModelException(
+ "No such key: " + key + " in class " + clazz.getName());
+ }
+
+ /**
+ * Returns true if there is at least one public static
+ * field or method in the underlying class.
+ */
+ @Override
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ @Override
+ public int size() {
+ return map.size();
+ }
+
+ @Override
+ public TemplateCollectionModel keys() throws TemplateModelException {
+ return (TemplateCollectionModel) wrapper.getOuterIdentity().wrap(map.keySet());
+ }
+
+ @Override
+ public TemplateCollectionModel values() throws TemplateModelException {
+ return (TemplateCollectionModel) wrapper.getOuterIdentity().wrap(map.values());
+ }
+
+ private void populate() throws TemplateModelException {
+ if (!Modifier.isPublic(clazz.getModifiers())) {
+ throw new TemplateModelException(
+ "Can't wrap the non-public class " + clazz.getName());
+ }
+
+ if (wrapper.getExposureLevel() == DefaultObjectWrapper.EXPOSE_NOTHING) {
+ return;
+ }
+
+ Field[] fields = clazz.getFields();
+ for (Field field : fields) {
+ int mod = field.getModifiers();
+ if (Modifier.isPublic(mod) && Modifier.isStatic(mod)) {
+ if (Modifier.isFinal(mod))
+ try {
+ // public static final fields are evaluated once and
+ // stored in the map
+ map.put(field.getName(), wrapper.getOuterIdentity().wrap(field.get(null)));
+ } catch (IllegalAccessException e) {
+ // Intentionally ignored
+ }
+ else
+ // This is a special flagging value: Field in the map means
+ // that this is a non-final field, and it must be evaluated
+ // on each get() call.
+ map.put(field.getName(), field);
+ }
+ }
+ if (wrapper.getExposureLevel() < DefaultObjectWrapper.EXPOSE_PROPERTIES_ONLY) {
+ Method[] methods = clazz.getMethods();
+ for (Method method : methods) {
+ int mod = method.getModifiers();
+ if (Modifier.isPublic(mod) && Modifier.isStatic(mod)
+ && wrapper.getClassIntrospector().isAllowedToExpose(method)) {
+ String name = method.getName();
+ Object obj = map.get(name);
+ if (obj instanceof Method) {
+ OverloadedMethods overloadedMethods = new OverloadedMethods();
+ overloadedMethods.addMethod((Method) obj);
+ overloadedMethods.addMethod(method);
+ map.put(name, overloadedMethods);
+ } else if (obj instanceof OverloadedMethods) {
+ OverloadedMethods overloadedMethods = (OverloadedMethods) obj;
+ overloadedMethods.addMethod(method);
+ } else {
+ if (obj != null) {
+ if (LOG.isInfoEnabled()) {
+ LOG.info("Overwriting value [" + obj + "] for " +
+ " key '" + name + "' with [" + method +
+ "] in static model for " + clazz.getName());
+ }
+ }
+ map.put(name, method);
+ }
+ }
+ }
+ for (Iterator entries = map.entrySet().iterator(); entries.hasNext(); ) {
+ Map.Entry entry = (Map.Entry) entries.next();
+ Object value = entry.getValue();
+ if (value instanceof Method) {
+ Method method = (Method) value;
+ entry.setValue(new JavaMethodModel(null, method,
+ method.getParameterTypes(), wrapper));
+ } else if (value instanceof OverloadedMethods) {
+ entry.setValue(new OverloadedMethodsModel(null, (OverloadedMethods) value, wrapper));
+ }
+ }
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModels.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModels.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModels.java
new file mode 100644
index 0000000..15b45bc
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/StaticModels.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.impl;
+
+import org.apache.freemarker.core.model.TemplateModel;
+import org.apache.freemarker.core.model.TemplateModelException;
+
+/**
+ * Utility class for instantiating {@link StaticModel} instances from
+ * templates. If your template's data model contains an instance of
+ * StaticModels (named, say <tt>StaticModels</tt>), then you can
+ * instantiate an arbitrary StaticModel using get syntax (i.e.
+ * <tt>StaticModels["java.lang.System"].currentTimeMillis()</tt>).
+ */
+class StaticModels extends ClassBasedModelFactory {
+
+ StaticModels(DefaultObjectWrapper wrapper) {
+ super(wrapper);
+ }
+
+ @Override
+ protected TemplateModel createModel(Class clazz)
+ throws TemplateModelException {
+ return new StaticModel(clazz, getWrapper());
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/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
new file mode 100644
index 0000000..b049c63
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TemplateModelListSequence.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.freemarker.core.model.impl;
+
+import java.util.List;
+
+import org.apache.freemarker.core.model.TemplateMethodModelEx;
+import org.apache.freemarker.core.model.TemplateModel;
+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 TemplateMethodModelEx}-es that collect items from other
+ * {@link TemplateModel}-s.
+ */
+public class TemplateModelListSequence implements TemplateSequenceModel {
+
+ private List/*<TemplateModel>*/ list;
+
+ public TemplateModelListSequence(List list) {
+ this.list = list;
+ }
+
+ @Override
+ public TemplateModel get(int index) {
+ return (TemplateModel) list.get(index);
+ }
+
+ @Override
+ public int size() {
+ return list.size();
+ }
+
+ /**
+ * Returns the original {@link List} of {@link TemplateModel}-s, so it's not a fully unwrapped value.
+ */
+ public Object getWrappedObject() {
+ return list;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/incubator-freemarker/blob/3fd56062/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TypeFlags.java
----------------------------------------------------------------------
diff --git a/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TypeFlags.java b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TypeFlags.java
new file mode 100644
index 0000000..6a5579b
--- /dev/null
+++ b/freemarker-core/src/main/java/org/apache/freemarker/core/model/impl/TypeFlags.java
@@ -0,0 +1,130 @@
+/*
+ * 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.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Flag values and masks for "type flags". "Type flags" is a set of bits that store information about the possible
+ * destination types at a parameter position of overloaded methods.
+ */
+class TypeFlags {
+
+ /**
+ * Indicates that the unwrapping hint will not be a specific numerical type; it must not be set if there's no
+ * numerical type at the given parameter index.
+ */
+ static final int WIDENED_NUMERICAL_UNWRAPPING_HINT = 1;
+
+ static final int BYTE = 4;
+ static final int SHORT = 8;
+ static final int INTEGER = 16;
+ static final int LONG = 32;
+ static final int FLOAT = 64;
+ static final int DOUBLE = 128;
+ static final int BIG_INTEGER = 256;
+ static final int BIG_DECIMAL = 512;
+ static final int UNKNOWN_NUMERICAL_TYPE = 1024;
+
+ static final int ACCEPTS_NUMBER = 0x800;
+ static final int ACCEPTS_DATE = 0x1000;
+ static final int ACCEPTS_STRING = 0x2000;
+ static final int ACCEPTS_BOOLEAN = 0x4000;
+ static final int ACCEPTS_MAP = 0x8000;
+ static final int ACCEPTS_LIST = 0x10000;
+ static final int ACCEPTS_SET = 0x20000;
+ static final int ACCEPTS_ARRAY = 0x40000;
+
+ /**
+ * Indicates the presence of the char or Character type
+ */
+ static final int CHARACTER = 0x80000;
+
+ static final int ACCEPTS_ANY_OBJECT = ACCEPTS_NUMBER | ACCEPTS_DATE | ACCEPTS_STRING | ACCEPTS_BOOLEAN
+ | ACCEPTS_MAP | ACCEPTS_LIST | ACCEPTS_SET | ACCEPTS_ARRAY;
+
+ static final int MASK_KNOWN_INTEGERS = BYTE | SHORT | INTEGER | LONG | BIG_INTEGER;
+ static final int MASK_KNOWN_NONINTEGERS = FLOAT | DOUBLE | BIG_DECIMAL;
+ static final int MASK_ALL_KNOWN_NUMERICALS = MASK_KNOWN_INTEGERS | MASK_KNOWN_NONINTEGERS;
+ static final int MASK_ALL_NUMERICALS = MASK_ALL_KNOWN_NUMERICALS | UNKNOWN_NUMERICAL_TYPE;
+
+ static int classToTypeFlags(Class pClass) {
+ // We start with the most frequent cases
+ if (pClass == Object.class) {
+ return ACCEPTS_ANY_OBJECT;
+ } else if (pClass == String.class) {
+ return ACCEPTS_STRING;
+ } else if (pClass.isPrimitive()) {
+ if (pClass == Integer.TYPE) return INTEGER | ACCEPTS_NUMBER;
+ else if (pClass == Long.TYPE) return LONG | ACCEPTS_NUMBER;
+ else if (pClass == Double.TYPE) return DOUBLE | ACCEPTS_NUMBER;
+ else if (pClass == Float.TYPE) return FLOAT | ACCEPTS_NUMBER;
+ else if (pClass == Byte.TYPE) return BYTE | ACCEPTS_NUMBER;
+ else if (pClass == Short.TYPE) return SHORT | ACCEPTS_NUMBER;
+ else if (pClass == Character.TYPE) return CHARACTER;
+ else if (pClass == Boolean.TYPE) return ACCEPTS_BOOLEAN;
+ else return 0;
+ } else if (Number.class.isAssignableFrom(pClass)) {
+ if (pClass == Integer.class) return INTEGER | ACCEPTS_NUMBER;
+ else if (pClass == Long.class) return LONG | ACCEPTS_NUMBER;
+ else if (pClass == Double.class) return DOUBLE | ACCEPTS_NUMBER;
+ else if (pClass == Float.class) return FLOAT | ACCEPTS_NUMBER;
+ else if (pClass == Byte.class) return BYTE | ACCEPTS_NUMBER;
+ else if (pClass == Short.class) return SHORT | ACCEPTS_NUMBER;
+ else if (BigDecimal.class.isAssignableFrom(pClass)) return BIG_DECIMAL | ACCEPTS_NUMBER;
+ else if (BigInteger.class.isAssignableFrom(pClass)) return BIG_INTEGER | ACCEPTS_NUMBER;
+ else return UNKNOWN_NUMERICAL_TYPE | ACCEPTS_NUMBER;
+ } else if (pClass.isArray()) {
+ return ACCEPTS_ARRAY;
+ } else {
+ int flags = 0;
+ if (pClass.isAssignableFrom(String.class)) {
+ flags |= ACCEPTS_STRING;
+ }
+ if (pClass.isAssignableFrom(Date.class)) {
+ flags |= ACCEPTS_DATE;
+ }
+ if (pClass.isAssignableFrom(Boolean.class)) {
+ flags |= ACCEPTS_BOOLEAN;
+ }
+ if (pClass.isAssignableFrom(Map.class)) {
+ flags |= ACCEPTS_MAP;
+ }
+ if (pClass.isAssignableFrom(List.class)) {
+ flags |= ACCEPTS_LIST;
+ }
+ if (pClass.isAssignableFrom(Set.class)) {
+ flags |= ACCEPTS_SET;
+ }
+
+ if (pClass == Character.class) {
+ flags |= CHARACTER;
+ }
+
+ return flags;
+ }
+ }
+
+}