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; + } + } + +}
