You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by cb...@apache.org on 2016/07/26 16:11:29 UTC
svn commit: r1754151 [1/3] - in /velocity/engine/trunk: src/changes/
velocity-engine-core/src/main/java/org/apache/velocity/app/
velocity-engine-core/src/main/java/org/apache/velocity/runtime/
velocity-engine-core/src/main/java/org/apache/velocity/runt...
Author: cbrisson
Date: Tue Jul 26 16:11:28 2016
New Revision: 1754151
URL: http://svn.apache.org/viewvc?rev=1754151&view=rev
Log:
New datatypes conversion framework:
- added the new interface o.a.v.util.introspection.ConversionHandler,
and the new 'runtime.conversion.handler' configuration property
which let the configuration handler be pluggable.
- added a default implementation class o.a.v.util.introspection.ConversionHandlerImpl
that will implicitly convert method arguments between all basic Java data types
(boolean, numbers, string).
- added a new Converter<T> interface to represent converters towards type T
- added a public VelMethod.getMethod() getter to ease the work of people
customizing introspection
- added test case ConversionHandlerTestCase
- removed half-finished IntrospectionCache 'pluggability' feature:
since IntrospectionCache now needs a ConversionHandler a c'tor argument,
made it a concrete class and removed IntrospectionCacheImpl.
Added:
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/Pair.java
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ConversionHandler.java
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ConversionHandlerImpl.java
velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/util/introspection/ConversionHandlerTestCase.java
- copied, changed from r1753736, velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/ParseWithMacroLibsTestCase.java
velocity/engine/trunk/velocity-engine-core/src/test/resources/conversion/
velocity/engine/trunk/velocity-engine-core/src/test/resources/conversion/compare/
velocity/engine/trunk/velocity-engine-core/src/test/resources/conversion/compare/matrix.cmp
velocity/engine/trunk/velocity-engine-core/src/test/resources/conversion/compare/test_conv_with_handler.cmp
velocity/engine/trunk/velocity-engine-core/src/test/resources/conversion/compare/test_conv_without_handler.cmp
velocity/engine/trunk/velocity-engine-core/src/test/resources/conversion/matrix.vhtml
velocity/engine/trunk/velocity-engine-core/src/test/resources/conversion/test_conv.vtl
Removed:
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCacheImpl.java
Modified:
velocity/engine/trunk/src/changes/changes.xml
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/app/VelocityEngine.java
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeConstants.java
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeInstance.java
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeServices.java
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeSingleton.java
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTIdentifier.java
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/GetExecutor.java
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassMap.java
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/Converter.java
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectionUtils.java
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/Introspector.java
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorBase.java
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCache.java
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/UberspectImpl.java
velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/VelMethod.java
velocity/engine/trunk/velocity-engine-core/src/main/resources/org/apache/velocity/runtime/defaults/velocity.properties
velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/BaseTestCase.java
velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/ClassloaderChangeTestCase.java
velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/Introspector2TestCase.java
velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/Introspector3TestCase.java
velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/IntrospectorTestCase.java
velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/StrictReferenceTestCase.java
velocity/engine/trunk/velocity-engine-core/src/test/java/org/apache/velocity/test/issues/VelTools66TestCase.java
Modified: velocity/engine/trunk/src/changes/changes.xml
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/changes/changes.xml?rev=1754151&r1=1754150&r2=1754151&view=diff
==============================================================================
--- velocity/engine/trunk/src/changes/changes.xml (original)
+++ velocity/engine/trunk/src/changes/changes.xml Tue Jul 26 16:11:28 2016
@@ -28,6 +28,10 @@
<release version="2.0" date="In Subversion">
<action type="add" dev="cbrisson">
+ added a new pluggable ConversionHandler class which, by default, converts method arguments as needed between main basic Java data types (boolean, numbers and strings)
+ </action>
+
+ <action type="add" dev="cbrisson">
added a runtime.string.interning option to trigger Java String interning on or off
</action>
Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/app/VelocityEngine.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/app/VelocityEngine.java?rev=1754151&r1=1754150&r2=1754151&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/app/VelocityEngine.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/app/VelocityEngine.java Tue Jul 26 16:11:28 2016
@@ -161,7 +161,7 @@ public class VelocityEngine implements R
* is a subset of the parent application's configuration.
*
* @param configuration
- * @deprecated use {@link setExtendedProperties(ExtProperties)}
+ * @deprecated use {@link #setExtendedProperties(ExtProperties)}
*/
public @Deprecated void setExtendedProperties( ExtendedProperties configuration)
{
Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeConstants.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeConstants.java?rev=1754151&r1=1754150&r2=1754151&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeConstants.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeConstants.java Tue Jul 26 16:11:28 2016
@@ -240,6 +240,9 @@ public interface RuntimeConstants
/** key name for uberspector. Multiple classnames can be specified,in which case uberspectors will be chained. */
String UBERSPECT_CLASSNAME = "runtime.introspector.uberspect";
+ /** key for Conversion Manager class */
+ String CONVERSION_HANDLER_CLASS = "runtime.conversion.handler.class";
+
/** A comma separated list of packages to restrict access to in the SecureIntrospector. */
String INTROSPECTOR_RESTRICT_PACKAGES = "introspector.restrict.packages";
Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeInstance.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeInstance.java?rev=1754151&r1=1754150&r2=1754151&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeInstance.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeInstance.java Tue Jul 26 16:11:28 2016
@@ -49,7 +49,6 @@ import org.apache.velocity.util.ExtPrope
import org.apache.velocity.util.RuntimeServicesAware;
import org.apache.velocity.util.StringUtils;
import org.apache.velocity.util.introspection.ChainableUberspector;
-import org.apache.velocity.util.introspection.Introspector;
import org.apache.velocity.util.introspection.LinkingUberspector;
import org.apache.velocity.util.introspection.Uberspect;
import org.apache.velocity.util.introspection.UberspectLoggable;
@@ -175,12 +174,6 @@ public class RuntimeInstance implements
private EventCartridge eventCartridge = null;
/*
- * Each runtime instance has it's own introspector
- * to ensure that each instance is completely separate.
- */
- private Introspector introspector = null;
-
- /*
* Whether to use string interning
*/
private boolean stringInterning = false;
@@ -302,11 +295,6 @@ public class RuntimeInstance implements
vmFactory = new VelocimacroFactory( this );
/*
- * make a new introspector and initialize it
- */
- introspector = new Introspector(getLog());
-
- /*
* and a store for the application attributes
*/
applicationAttributes = new HashMap();
@@ -1807,15 +1795,6 @@ public class RuntimeInstance implements
}
/**
- * Return the Introspector for this instance
- * @return The Introspector for this instance
- */
- public Introspector getIntrospector()
- {
- return introspector;
- }
-
- /**
* Returns the event handlers for the application.
* @return The event handlers for the application.
* @since 1.5
Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeServices.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeServices.java?rev=1754151&r1=1754150&r2=1754151&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeServices.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeServices.java Tue Jul 26 16:11:28 2016
@@ -445,15 +445,6 @@ public interface RuntimeServices
*/
public EventCartridge getApplicationEventCartridge();
-
- /**
- * Returns the configured method introspection/reflection
- * implementation.
- * @return The configured method introspection/reflection
- * implementation.
- */
- public Introspector getIntrospector();
-
/**
* Returns true if the RuntimeInstance has been successfully initialized.
* @return True if the RuntimeInstance has been successfully initialized.
Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeSingleton.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeSingleton.java?rev=1754151&r1=1754150&r2=1754151&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeSingleton.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/RuntimeSingleton.java Tue Jul 26 16:11:28 2016
@@ -524,17 +524,6 @@ public class RuntimeSingleton implements
}
/**
- * Return the Introspector for this RuntimeInstance
- *
- * @return Introspector object for this runtime instance
- * @see RuntimeInstance#getIntrospector()
- */
- public static Introspector getIntrospector()
- {
- return ri.getIntrospector();
- }
-
- /**
* Returns the event handlers for the application.
* @return The event handlers for the application.
* @see RuntimeInstance#getApplicationEventCartridge()
Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTIdentifier.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTIdentifier.java?rev=1754151&r1=1754150&r2=1754151&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTIdentifier.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/ASTIdentifier.java Tue Jul 26 16:11:28 2016
@@ -148,7 +148,7 @@ public class ASTIdentifier extends Simpl
* uberspector
*/
- vg = rsvc.getUberspect().getPropertyGet(o,identifier, uberInfo);
+ vg = rsvc.getUberspect().getPropertyGet(o, identifier, uberInfo);
if (vg != null && vg.isCacheable() && (o != null))
{
Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/GetExecutor.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/GetExecutor.java?rev=1754151&r1=1754150&r2=1754151&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/GetExecutor.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/runtime/parser/node/GetExecutor.java Tue Jul 26 16:11:28 2016
@@ -78,6 +78,17 @@ public class GetExecutor extends Abstrac
try
{
setMethod(introspector.getMethod(clazz, "get", params));
+ /* get(Number) or get(integral) are NOT admissible,
+ * as the key is a string
+ */
+ if (getMethod() != null)
+ {
+ Class<?> [] args = getMethod().getParameterTypes();
+ if (args.length == 1 && (args[0].isPrimitive() || Number.class.isAssignableFrom(args[0])))
+ {
+ setMethod(null);
+ }
+ }
}
/**
* pass through application level runtime exceptions
Added: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/Pair.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/Pair.java?rev=1754151&view=auto
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/Pair.java (added)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/Pair.java Tue Jul 26 16:11:28 2016
@@ -0,0 +1,79 @@
+package org.apache.velocity.util;
+
+/*
+ * 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.
+ */
+
+/**
+ * Combine two objects in a hashable pair
+ *
+ * @version $Id: Pair.java $
+ * @since 2.0
+ */
+
+public class Pair<A, B>
+{
+ private final A first;
+ private final B second;
+
+ public Pair(final A first, final B second)
+ {
+ this.first = first;
+ this.second = second;
+ }
+
+ public int hashCode()
+ {
+ int hashFirst = first != null ? first.hashCode() : 0;
+ int hashSecond = second != null ? second.hashCode() : 0;
+
+ return (hashFirst + hashSecond) * hashSecond + hashFirst;
+ }
+
+ public boolean equals(Object other)
+ {
+ if (other instanceof Pair)
+ {
+ Pair otherPair = (Pair) other;
+ return
+ (( this.first == otherPair.first ||
+ ( this.first != null && otherPair.first != null &&
+ this.first.equals(otherPair.first))) &&
+ ( this.second == otherPair.second ||
+ ( this.second != null && otherPair.second != null &&
+ this.second.equals(otherPair.second))) );
+ }
+
+ return false;
+ }
+
+ public String toString()
+ {
+ return "(" + first + ", " + second + ")";
+ }
+
+ public A getFirst()
+ {
+ return first;
+ }
+
+ public B getSecond()
+ {
+ return second;
+ }
+}
Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassMap.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassMap.java?rev=1754151&r1=1754150&r2=1754151&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassMap.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ClassMap.java Tue Jul 26 16:11:28 2016
@@ -38,6 +38,7 @@ import java.util.concurrent.ConcurrentHa
* @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
* @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
* @author Nathan Bubna
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
* @version $Id$
*/
public class ClassMap
@@ -62,6 +63,18 @@ public class ClassMap
*/
public ClassMap(final Class clazz, final Logger log)
{
+ this(clazz, log, null);
+ }
+
+ /**
+ * Standard constructor
+ * @param clazz The class for which this ClassMap gets constructed.
+ * @param log logger
+ * @param conversionHandler conversion handler
+ * @since 2.0
+ */
+ public ClassMap(final Class clazz, final Logger log, final ConversionHandler conversionHandler)
+ {
this.clazz = clazz;
this.log = log;
@@ -71,7 +84,7 @@ public class ClassMap
log.debug("== Class: {}", clazz);
}
- methodCache = createMethodCache();
+ methodCache = createMethodCache(conversionHandler);
if (debugReflection && log.isDebugEnabled())
{
@@ -108,9 +121,9 @@ public class ClassMap
* are taken from all the public methods
* that our class, its parents and their implemented interfaces provide.
*/
- private MethodCache createMethodCache()
+ private MethodCache createMethodCache(ConversionHandler conversionHandler)
{
- MethodCache methodCache = new MethodCache(log);
+ MethodCache methodCache = new MethodCache(log, conversionHandler);
//
// Looks through all elements in the class hierarchy. This one is bottom-first (i.e. we start
// with the actual declaring class and its interfaces and then move up (superclass etc.) until we
@@ -219,11 +232,12 @@ public class ClassMap
private final Map cache = new ConcurrentHashMap();
/** Map of methods that are searchable according to method parameters to find a match */
- private final MethodMap methodMap = new MethodMap();
+ private final MethodMap methodMap;
- private MethodCache(Logger log)
+ private MethodCache(Logger log, ConversionHandler conversionHandler)
{
this.log = log;
+ methodMap = new MethodMap(conversionHandler);
}
/**
Added: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ConversionHandler.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ConversionHandler.java?rev=1754151&view=auto
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ConversionHandler.java (added)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ConversionHandler.java Tue Jul 26 16:11:28 2016
@@ -0,0 +1,64 @@
+package org.apache.velocity.util.introspection;
+
+/*
+ * 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.
+ */
+
+/**
+ * A conversion handler adds admissible conversions between Java types whenever Velocity introspection has to map
+ * VTL methods and property accessors to Java methods.
+ * Both methods must be consistent: <code>getNeededConverter</code> must not return <code>null</code> whenever
+ * <code>isExplicitelyConvertible</code> returned true with the same arguments.
+ *
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ * @version $Id: ConversionHandler.java $
+ * @since 2.0
+ */
+
+public interface ConversionHandler
+{
+ /**
+ * Check to see if the conversion can be done using an explicit conversion
+ * @param formal expected formal type
+ * @param actual provided argument type
+ * @return null if no conversion is needed, or the appropriate Converter object
+ * @since 2.0
+ */
+ public boolean isExplicitlyConvertible(Class formal, Class actual, boolean possibleVarArg);
+
+ /**
+ * Returns the appropriate Converter object needed for an explicit conversion
+ * Returns null if no conversion is needed.
+ *
+ * @param formal expected formal type
+ * @param actual provided argument type
+ * @return null if no conversion is needed, or the appropriate Converter object
+ * @since 2.0
+ */
+ public Converter getNeededConverter(final Class formal, final Class actual);
+
+ /**
+ * Add the given converter to the handler. Implementation should be thread-safe.
+ *
+ * @param formal expected formal type
+ * @param actual provided argument type
+ * @param converter converter
+ * @since 2.0
+ */
+ public void addConverter(Class formal, Class actual, Converter converter);
+}
Added: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ConversionHandlerImpl.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ConversionHandlerImpl.java?rev=1754151&view=auto
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ConversionHandlerImpl.java (added)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/ConversionHandlerImpl.java Tue Jul 26 16:11:28 2016
@@ -0,0 +1,485 @@
+package org.apache.velocity.util.introspection;
+
+/*
+ * 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.
+ */
+
+import org.apache.velocity.util.Pair;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * A conversion handler adds admissible conversions between Java types whenever Velocity introspection has to map
+ * VTL methods and property accessors to Java methods. This implementation is the default Conversion Handler
+ * for Velocity.
+ *
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
+ * @version $Id: ConversionHandlerImpl.java $
+ * @since 2.0
+ */
+
+public class ConversionHandlerImpl implements ConversionHandler
+{
+ /**
+ * standard narrowing and string parsing conversions.
+ */
+ static Map<Pair<? extends Class, ? extends Class>, Converter> standardConverterMap;
+
+ /**
+ * basic toString converter
+ */
+ static Converter toString;
+
+ /**
+ * cache miss converter
+ */
+ static Converter cacheMiss;
+
+ /**
+ * min/max byte/short/int values as long
+ */
+ static final long minByte = Byte.MIN_VALUE, maxByte = Byte.MAX_VALUE,
+ minShort = Short.MIN_VALUE, maxShort = Short.MAX_VALUE,
+ minInt = Integer.MIN_VALUE, maxInt = Integer.MAX_VALUE;
+
+ /**
+ * min/max long values as double
+ */
+ static final double minLong = Long.MIN_VALUE, maxLong = Long.MAX_VALUE;
+
+ /**
+ * a converters cache map, initialized with the standard narrowing and string parsing conversions.
+ */
+ Map<Pair<? extends Class, ? extends Class>, Converter> converterCacheMap;
+
+ static
+ {
+ standardConverterMap = new HashMap<Pair<? extends Class, ? extends Class>, Converter>();
+
+ cacheMiss = new Converter<Object>()
+ {
+ @Override
+ public Object convert(Object o)
+ {
+ return o;
+ }
+ };
+
+ /* number -> boolean */
+ Converter<Boolean> numberToBool = new Converter<Boolean>()
+ {
+ @Override
+ public Boolean convert(Object o)
+ {
+ return o == null ? null : ((Number) o).intValue() != 0;
+ }
+ };
+ standardConverterMap.put(new Pair<>(Boolean.class, Byte.class), numberToBool);
+ standardConverterMap.put(new Pair<>(Boolean.class, Short.class), numberToBool);
+ standardConverterMap.put(new Pair<>(Boolean.class, Integer.class), numberToBool);
+ standardConverterMap.put(new Pair<>(Boolean.class, Long.class), numberToBool);
+ standardConverterMap.put(new Pair<>(Boolean.class, Float.class), numberToBool);
+ standardConverterMap.put(new Pair<>(Boolean.class, Double.class), numberToBool);
+ standardConverterMap.put(new Pair<>(Boolean.TYPE, Byte.class), numberToBool);
+ standardConverterMap.put(new Pair<>(Boolean.TYPE, Short.class), numberToBool);
+ standardConverterMap.put(new Pair<>(Boolean.TYPE, Integer.class), numberToBool);
+ standardConverterMap.put(new Pair<>(Boolean.TYPE, Long.class), numberToBool);
+ standardConverterMap.put(new Pair<>(Boolean.TYPE, Float.class), numberToBool);
+ standardConverterMap.put(new Pair<>(Boolean.TYPE, Double.class), numberToBool);
+
+ /* character -> boolean */
+ Converter<Boolean> charToBoolean = new Converter<Boolean>()
+ {
+ @Override
+ public Boolean convert(Object o)
+ {
+ return o == null ? null : ((Character) o).charValue() != 0;
+ }
+ };
+ standardConverterMap.put(new Pair<>(Boolean.class, Character.class), charToBoolean);
+ standardConverterMap.put(new Pair<>(Boolean.TYPE, Character.class), charToBoolean);
+
+ /* string -> boolean */
+ Converter<Boolean> stringToBoolean = new Converter<Boolean>()
+ {
+ @Override
+ public Boolean convert(Object o)
+ {
+ return Boolean.valueOf(String.valueOf(o));
+ }
+ };
+ standardConverterMap.put(new Pair<>(Boolean.class, String.class), stringToBoolean);
+ standardConverterMap.put(new Pair<>(Boolean.TYPE, String.class), stringToBoolean);
+
+ /* narrowing towards byte */
+ Converter<Byte> narrowingToByte = new Converter<Byte>()
+ {
+ @Override
+ public Byte convert(Object o)
+ {
+ if (o == null) return null;
+ long l = ((Number)o).longValue();
+ if (l < minByte || l > maxByte)
+ {
+ throw new NumberFormatException("value out of range for byte type: " + l);
+ }
+ return ((Number) o).byteValue();
+ }
+ };
+ standardConverterMap.put(new Pair<>(Byte.class, Short.class), narrowingToByte);
+ standardConverterMap.put(new Pair<>(Byte.class, Integer.class), narrowingToByte);
+ standardConverterMap.put(new Pair<>(Byte.class, Long.class), narrowingToByte);
+ standardConverterMap.put(new Pair<>(Byte.class, Float.class), narrowingToByte);
+ standardConverterMap.put(new Pair<>(Byte.class, Double.class), narrowingToByte);
+ standardConverterMap.put(new Pair<>(Byte.TYPE, Short.class), narrowingToByte);
+ standardConverterMap.put(new Pair<>(Byte.TYPE, Integer.class), narrowingToByte);
+ standardConverterMap.put(new Pair<>(Byte.TYPE, Long.class), narrowingToByte);
+ standardConverterMap.put(new Pair<>(Byte.TYPE, Float.class), narrowingToByte);
+ standardConverterMap.put(new Pair<>(Byte.TYPE, Double.class), narrowingToByte);
+
+ /* string to byte */
+ Converter<Byte> stringToByte = new Converter<Byte>()
+ {
+ @Override
+ public Byte convert(Object o)
+ {
+ return Byte.valueOf(String.valueOf(o));
+ }
+ };
+ standardConverterMap.put(new Pair<>(Byte.class, String.class), stringToByte);
+ standardConverterMap.put(new Pair<>(Byte.TYPE, String.class), stringToByte);
+
+ /* narrowing towards short */
+ Converter<Short> narrowingToShort = new Converter<Short>()
+ {
+ @Override
+ public Short convert(Object o)
+ {
+ if (o == null) return null;
+ long l = ((Number)o).longValue();
+ if (l < minShort || l > maxShort)
+ {
+ throw new NumberFormatException("value out of range for short type: " + l);
+ }
+ return ((Number) o).shortValue();
+ }
+ };
+ standardConverterMap.put(new Pair<>(Short.class, Integer.class), narrowingToShort);
+ standardConverterMap.put(new Pair<>(Short.class, Long.class), narrowingToShort);
+ standardConverterMap.put(new Pair<>(Short.class, Float.class), narrowingToShort);
+ standardConverterMap.put(new Pair<>(Short.class, Double.class), narrowingToShort);
+ standardConverterMap.put(new Pair<>(Short.TYPE, Integer.class), narrowingToShort);
+ standardConverterMap.put(new Pair<>(Short.TYPE, Long.class), narrowingToShort);
+ standardConverterMap.put(new Pair<>(Short.TYPE, Float.class), narrowingToShort);
+ standardConverterMap.put(new Pair<>(Short.TYPE, Double.class), narrowingToShort);
+
+ /* string to short */
+ Converter<Short> stringToShort = new Converter<Short>()
+ {
+ @Override
+ public Short convert(Object o)
+ {
+ return Short.valueOf(String.valueOf(o));
+ }
+ };
+ standardConverterMap.put(new Pair<>(Short.class, String.class), stringToShort);
+ standardConverterMap.put(new Pair<>(Short.TYPE, String.class), stringToShort);
+
+ /* narrowing towards int */
+ Converter<Integer> narrowingToInteger = new Converter<Integer>()
+ {
+ @Override
+ public Integer convert(Object o)
+ {
+ if (o == null) return null;
+ long l = ((Number)o).longValue();
+ if (l < minInt || l > maxInt)
+ {
+ throw new NumberFormatException("value out of range for integer type: " + l);
+ }
+ return ((Number) o).intValue();
+ }
+ };
+ standardConverterMap.put(new Pair<>(Integer.class, Long.class), narrowingToInteger);
+ standardConverterMap.put(new Pair<>(Integer.class, Float.class), narrowingToInteger);
+ standardConverterMap.put(new Pair<>(Integer.class, Double.class), narrowingToInteger);
+ standardConverterMap.put(new Pair<>(Integer.TYPE, Long.class), narrowingToInteger);
+ standardConverterMap.put(new Pair<>(Integer.TYPE, Float.class), narrowingToInteger);
+ standardConverterMap.put(new Pair<>(Integer.TYPE, Double.class), narrowingToInteger);
+
+ /* string to int */
+ Converter<Integer> stringToInteger = new Converter<Integer>()
+ {
+ @Override
+ public Integer convert(Object o)
+ {
+ return Integer.valueOf(String.valueOf(o));
+ }
+ };
+ standardConverterMap.put(new Pair<>(Integer.class, String.class), stringToInteger);
+ standardConverterMap.put(new Pair<>(Integer.TYPE, String.class), stringToInteger);
+
+ /* narrowing towards long */
+ Converter<Long> narrowingToLong = new Converter<Long>()
+ {
+ @Override
+ public Long convert(Object o)
+ {
+ if (o == null) return null;
+ double d = ((Number)o).doubleValue();
+ if (d < minLong || d > maxLong)
+ {
+ throw new NumberFormatException("value out of range for long type: " + d);
+ }
+ return ((Number) o).longValue();
+ }
+ };
+ standardConverterMap.put(new Pair<>(Long.class, Float.class), narrowingToLong);
+ standardConverterMap.put(new Pair<>(Long.class, Double.class), narrowingToLong);
+ standardConverterMap.put(new Pair<>(Long.TYPE, Float.class), narrowingToLong);
+ standardConverterMap.put(new Pair<>(Long.TYPE, Double.class), narrowingToLong);
+
+ /* string to long */
+ Converter<Long> stringToLong = new Converter<Long>()
+ {
+ @Override
+ public Long convert(Object o)
+ {
+ return Long.valueOf(String.valueOf(o));
+ }
+ };
+ standardConverterMap.put(new Pair<>(Long.class, String.class), stringToLong);
+ standardConverterMap.put(new Pair<>(Long.TYPE, String.class), stringToLong);
+
+ /* narrowing towards float */
+ Converter<Float> narrowingToFloat = new Converter<Float>()
+ {
+ @Override
+ public Float convert(Object o)
+ {
+ return o == null ? null : ((Number) o).floatValue();
+ }
+ };
+ standardConverterMap.put(new Pair<>(Float.class, Double.class), narrowingToFloat);
+ standardConverterMap.put(new Pair<>(Float.TYPE, Double.class), narrowingToFloat);
+
+ /* string to float */
+ Converter<Float> stringToFloat = new Converter<Float>()
+ {
+ @Override
+ public Float convert(Object o)
+ {
+ return Float.valueOf(String.valueOf(o));
+ }
+ };
+ standardConverterMap.put(new Pair<>(Float.class, String.class), stringToFloat);
+ standardConverterMap.put(new Pair<>(Float.TYPE, String.class), stringToFloat);
+
+ /* string to double */
+ Converter<Double> stringToDouble = new Converter<Double>()
+ {
+ @Override
+ public Double convert(Object o)
+ {
+ return Double.valueOf(String.valueOf(o));
+ }
+ };
+ standardConverterMap.put(new Pair<>(Double.class, String.class), stringToDouble);
+ standardConverterMap.put(new Pair<>(Double.TYPE, String.class), stringToDouble);
+
+ /* boolean to byte */
+ Converter<Byte> booleanToByte = new Converter<Byte>()
+ {
+ @Override
+ public Byte convert(Object o)
+ {
+ return o == null ? null : ((Boolean)o).booleanValue() ? (byte)1 : (byte)0;
+ }
+ };
+ standardConverterMap.put(new Pair<>(Byte.class, Boolean.class), booleanToByte);
+ standardConverterMap.put(new Pair<>(Byte.TYPE, Boolean.class), booleanToByte);
+
+ /* boolean to short */
+ Converter<Short> booleanToShort = new Converter<Short>()
+ {
+ @Override
+ public Short convert(Object o)
+ {
+ return o == null ? null : ((Boolean)o).booleanValue() ? (short)1 : (short)0;
+ }
+ };
+ standardConverterMap.put(new Pair<>(Short.class, Boolean.class), booleanToShort);
+ standardConverterMap.put(new Pair<>(Short.TYPE, Boolean.class), booleanToShort);
+
+ /* boolean to integer */
+ Converter<Integer> booleanToInteger = new Converter<Integer>()
+ {
+ @Override
+ public Integer convert(Object o)
+ {
+ return o == null ? null : ((Boolean)o).booleanValue() ? (Integer)1 : (Integer)0;
+ }
+ };
+ standardConverterMap.put(new Pair<>(Integer.class, Boolean.class), booleanToInteger);
+ standardConverterMap.put(new Pair<>(Integer.TYPE, Boolean.class), booleanToInteger);
+
+ /* boolean to lonf */
+ Converter<Long> booleanToLong = new Converter<Long>()
+ {
+ @Override
+ public Long convert(Object o)
+ {
+ return o == null ? null : ((Boolean)o).booleanValue() ? 1L : 0L;
+ }
+ };
+ standardConverterMap.put(new Pair<>(Long.class, Boolean.class), booleanToLong);
+ standardConverterMap.put(new Pair<>(Long.TYPE, Boolean.class), booleanToLong);
+
+ /* to string */
+ toString = new Converter<String>()
+ {
+ @Override
+ public String convert(Object o)
+ {
+ return String.valueOf(o);
+ }
+ };
+ standardConverterMap.put(new Pair<>(String.class, Boolean.class), toString);
+ standardConverterMap.put(new Pair<>(String.class, Byte.class), toString);
+ standardConverterMap.put(new Pair<>(String.class, Short.class), toString);
+ standardConverterMap.put(new Pair<>(String.class, Integer.class), toString);
+ standardConverterMap.put(new Pair<>(String.class, Long.class), toString);
+ standardConverterMap.put(new Pair<>(String.class, Float.class), toString);
+ standardConverterMap.put(new Pair<>(String.class, Double.class), toString);
+ standardConverterMap.put(new Pair<>(String.class, Character.class), toString);
+ standardConverterMap.put(new Pair<>(String.class, Object.class), toString);
+ }
+
+ /**
+ * Constructor
+ */
+ public ConversionHandlerImpl()
+ {
+ converterCacheMap = new ConcurrentHashMap<Pair<? extends Class, ? extends Class>, Converter>();
+ }
+
+ /**
+ * Check to see if the conversion can be done using an explicit conversion
+ * @param actual found argument type
+ * @param formal expected formal type
+ * @return null if no conversion is needed, or the appropriate Converter object
+ * @since 2.0
+ */
+ @Override
+ public boolean isExplicitlyConvertible(Class formal, Class actual, boolean possibleVarArg)
+ {
+ if (getNeededConverter(formal, actual) != null)
+ {
+ return true;
+ }
+
+ /* Check var arg */
+ if (possibleVarArg && formal.isArray())
+ {
+ if (actual.isArray())
+ {
+ actual = actual.getComponentType();
+ }
+ return isExplicitlyConvertible(formal.getComponentType(), actual, false);
+ }
+ return false;
+ }
+
+
+ /**
+ * Returns the appropriate Converter object needed for an explicit conversion
+ * Returns null if no conversion is needed.
+ *
+ * @param actual found argument type
+ * @param formal expected formal type
+ * @return null if no conversion is needed, or the appropriate Converter object
+ * @since 2.0
+ */
+ @Override
+ public Converter getNeededConverter(final Class formal, final Class actual)
+ {
+ Pair<Class, Class> key = new Pair<>(formal, actual);
+
+ /* first check for a standard conversion */
+ Converter converter = standardConverterMap.get(key);
+ if (converter == null)
+ {
+ /* then the converters cache map */
+ converter = converterCacheMap.get(key);
+ if (converter == null)
+ {
+ /* check for conversion towards string */
+ if (formal == String.class)
+ {
+ converter = toString;
+ }
+ /* check for String -> Enum constant conversion */
+ else if (formal.isEnum() && actual == String.class)
+ {
+ converter = new Converter()
+ {
+ @Override
+ public Object convert(Object o)
+ {
+ return Enum.valueOf((Class<Enum>) formal, (String) o);
+ }
+ };
+ }
+
+ converterCacheMap.put(key, converter == null ? cacheMiss : converter);
+ }
+ }
+ return converter == cacheMiss ? null : converter;
+ }
+
+ /**
+ * Add the given converter to the handler.
+ *
+ * @param formal expected formal type
+ * @param actual provided argument type
+ * @param converter converter
+ * @since 2.0
+ */
+ @Override
+ public void addConverter(Class formal, Class actual, Converter converter)
+ {
+ Pair<Class, Class> key = new Pair<>(formal, actual);
+ converterCacheMap.put(key, converter);
+ if (formal.isPrimitive())
+ {
+ key = new Pair<>(IntrospectionUtils.getBoxedClass(formal), actual);
+ converterCacheMap.put(key, converter);
+ }
+ else
+ {
+ Class unboxedFormal = IntrospectionUtils.getUnboxedClass(formal);
+ if (unboxedFormal != formal)
+ {
+ key = new Pair<>(unboxedFormal, actual);
+ converterCacheMap.put(key, converter);
+ }
+ }
+ }
+}
Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/Converter.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/Converter.java?rev=1754151&r1=1754150&r2=1754151&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/Converter.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/Converter.java Tue Jul 26 16:11:28 2016
@@ -19,17 +19,20 @@ package org.apache.velocity.util.introsp
* under the License.
*/
-import org.apache.velocity.runtime.parser.node.Node;
-import org.apache.velocity.util.StringUtils;
-
/**
* Converts a value to type T
*
+ * @since 2.0
* @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
* @version $Id$
*/
public interface Converter<T>
{
+ /**
+ * convert object to type T
+ * @param o input object
+ * @result converted object
+ */
T convert(Object o);
}
Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectionUtils.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectionUtils.java?rev=1754151&r1=1754150&r2=1754151&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectionUtils.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectionUtils.java Tue Jul 26 16:11:28 2016
@@ -19,6 +19,10 @@ package org.apache.velocity.util.introsp
* under the License.
*/
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
/**
*
* @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
@@ -27,11 +31,62 @@ package org.apache.velocity.util.introsp
* @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
* @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
* @author Nathan Bubna
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
* @version $Id: IntrospectionUtils.java 476785 2006-11-19 10:06:21Z henning $
* @since 1.6
*/
public class IntrospectionUtils
{
+ /**
+ * boxing helper maps for standard types
+ */
+ static Map<Class, Class> boxingMap, unboxingMap;
+
+ static
+ {
+ boxingMap = new HashMap<Class, Class>();
+ boxingMap.put(Boolean.TYPE, Boolean.class);
+ boxingMap.put(Character.TYPE, Character.class);
+ boxingMap.put(Byte.TYPE, Byte.class);
+ boxingMap.put(Short.TYPE, Short.class);
+ boxingMap.put(Integer.TYPE, Integer.class);
+ boxingMap.put(Long.TYPE, Long.class);
+ boxingMap.put(Float.TYPE, Float.class);
+ boxingMap.put(Double.TYPE, Double.class);
+
+ unboxingMap = new HashMap<Class, Class>();
+ for (Map.Entry<Class,Class> entry : (Set<Map.Entry<Class,Class>>)boxingMap.entrySet())
+ {
+ unboxingMap.put(entry.getValue(), entry.getKey());
+ }
+ }
+
+ /**
+ * returns boxed type (or input type if not a primitive type)
+ * @param clazz input class
+ * @return boxed class
+ */
+ static Class getBoxedClass(Class clazz)
+ {
+ Class boxed = boxingMap.get(clazz);
+ return boxed == null ? clazz : boxed;
+ }
+
+ /**
+ * returns unboxed type (or input type if not successful)
+ * @param clazz input class
+ * @return unboxed class
+ */
+ static Class getUnboxedClass(Class clazz)
+ {
+ Class unboxed = unboxingMap.get(clazz);
+ return unboxed == null ? clazz : unboxed;
+ }
+
+ /**
+ *
+ */
+
/**
* Determines whether a type represented by a class object is
@@ -58,48 +113,74 @@ public class IntrospectionUtils
boolean possibleVarArg)
{
/* if it's a null, it means the arg was null */
- if (actual == null && !formal.isPrimitive())
+ if (actual == null)
{
- return true;
+ return !formal.isPrimitive();
}
/* Check for identity or widening reference conversion */
- if (actual != null && formal.isAssignableFrom(actual))
+ if (formal.isAssignableFrom(actual))
{
return true;
}
- /* Check for boxing with widening primitive conversion. Note that
- * actual parameters are never primitives. */
+ /* 2.0: Since MethodMap's comparison functions now use this method with potentially reversed arguments order,
+ * actual can be a primitive type. */
+
+ /* Check for boxing */
+ if (!formal.isPrimitive() && actual.isPrimitive())
+ {
+ Class boxed = boxingMap.get(actual);
+ if (boxed != null && boxed == formal) return true;
+ }
+
if (formal.isPrimitive())
{
- if(formal == Boolean.TYPE && actual == Boolean.class)
- return true;
- if(formal == Character.TYPE && actual == Character.class)
- return true;
- if(formal == Byte.TYPE && actual == Byte.class)
- return true;
- if(formal == Short.TYPE &&
- (actual == Short.class || actual == Byte.class))
- return true;
- if(formal == Integer.TYPE &&
- (actual == Integer.class || actual == Short.class ||
- actual == Byte.class))
- return true;
- if(formal == Long.TYPE &&
- (actual == Long.class || actual == Integer.class ||
- actual == Short.class || actual == Byte.class))
- return true;
- if(formal == Float.TYPE &&
- (actual == Float.class || actual == Long.class ||
- actual == Integer.class || actual == Short.class ||
- actual == Byte.class))
- return true;
- if(formal == Double.TYPE &&
- (actual == Double.class || actual == Float.class ||
- actual == Long.class || actual == Integer.class ||
- actual == Short.class || actual == Byte.class))
- return true;
+ if (actual.isPrimitive())
+ {
+ /* check for widening primitive conversion */
+ if (formal == Short.TYPE && actual == Byte.TYPE)
+ return true;
+ if (formal == Integer.TYPE && (
+ actual == Byte.TYPE || actual == Short.TYPE))
+ return true;
+ if (formal == Long.TYPE && (
+ actual == Byte.TYPE || actual == Short.TYPE || actual == Integer.TYPE))
+ return true;
+ if (formal == Float.TYPE && (
+ actual == Byte.TYPE || actual == Short.TYPE || actual == Integer.TYPE ||
+ actual == Long.TYPE))
+ return true;
+ if (formal == Double.TYPE && (
+ actual == Byte.TYPE || actual == Short.TYPE || actual == Integer.TYPE ||
+ actual == Long.TYPE || actual == Float.TYPE))
+ return true;
+ }
+ else
+ {
+ /* Check for unboxing with widening primitive conversion. */
+ if (formal == Boolean.TYPE && actual == Boolean.class)
+ return true;
+ if (formal == Character.TYPE && actual == Character.class)
+ return true;
+ if (formal == Byte.TYPE && actual == Byte.class)
+ return true;
+ if (formal == Short.TYPE && (actual == Short.class || actual == Byte.class))
+ return true;
+ if (formal == Integer.TYPE && (actual == Integer.class || actual == Short.class ||
+ actual == Byte.class))
+ return true;
+ if (formal == Long.TYPE && (actual == Long.class || actual == Integer.class ||
+ actual == Short.class || actual == Byte.class))
+ return true;
+ if (formal == Float.TYPE && (actual == Float.class || actual == Long.class ||
+ actual == Integer.class || actual == Short.class || actual == Byte.class))
+ return true;
+ if (formal == Double.TYPE && (actual == Double.class || actual == Float.class ||
+ actual == Long.class || actual == Integer.class || actual == Short.class ||
+ actual == Byte.class))
+ return true;
+ }
}
/* Check for vararg conversion. */
@@ -112,13 +193,6 @@ public class IntrospectionUtils
return isMethodInvocationConvertible(formal.getComponentType(),
actual, false);
}
-
- /* Check for manual conversions */
- if (isExplicitlyConvertible(formal, actual))
- {
- return true;
- }
-
return false;
}
@@ -189,45 +263,4 @@ public class IntrospectionUtils
}
return false;
}
-
- /**
- * Check to see if the conversion can be done using an explicit conversion
- */
- public static boolean isExplicitlyConvertible(Class formal, Class actual)
- {
- /* Check for String to Enum constant conversion */
- if (formal.isEnum() && actual == String.class)
- {
- return true;
- }
- return false;
- }
-
-
- /**
- * Returns the appropriate Converter object needed for an explicit conversion
- * Returns null if no conversion is needed.
- *
- * @param actual found argument type
- * @param formal expected formal type
- * @return null if no conversion is needed, or the appropriate Converter object
- * @since 2.0
- */
- public static Converter getNeededConverter(final Class formal, final Class actual)
- {
- /* the only current use case is the String -> Enum constant conversion. */
- if (formal.isEnum() && actual == String.class)
- {
- Converter converter = new Converter()
- {
- @Override
- public Object convert(Object o)
- {
- return Enum.valueOf((Class<Enum>)formal, (String)o);
- }
- };
- return converter;
- }
- return null;
- }
}
Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/Introspector.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/Introspector.java?rev=1754151&r1=1754150&r2=1754151&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/Introspector.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/Introspector.java Tue Jul 26 16:11:28 2016
@@ -60,7 +60,17 @@ public class Introspector extends Intros
*/
public Introspector(final Logger log)
{
- super(log);
+ this(log, null);
+ }
+
+ /**
+ * @param log A Logger object to use for the introspector.
+ * @param conversionHandler conversion handler
+ * @since 2.0
+ */
+ public Introspector(final Logger log, ConversionHandler conversionHandler)
+ {
+ super(log, conversionHandler);
}
/**
Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorBase.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorBase.java?rev=1754151&r1=1754150&r2=1754151&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorBase.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorBase.java Tue Jul 26 16:11:28 2016
@@ -60,14 +60,18 @@ public abstract class IntrospectorBase
/** The Introspector Cache */
private final IntrospectorCache introspectorCache;
-
+
+ /** The Conversion handler */
+ private final ConversionHandler conversionHandler;
+
/**
* C'tor.
*/
- protected IntrospectorBase(final Logger log)
+ protected IntrospectorBase(final Logger log, final ConversionHandler conversionHandler)
{
this.log = log;
- introspectorCache = new IntrospectorCacheImpl(log); // TODO: Load that from properties.
+ introspectorCache = new IntrospectorCache(log, conversionHandler);
+ this.conversionHandler = conversionHandler;
}
/**
Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCache.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCache.java?rev=1754151&r1=1754150&r2=1754151&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCache.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/IntrospectorCache.java Tue Jul 26 16:11:28 2016
@@ -19,30 +19,113 @@ package org.apache.velocity.util.introsp
* under the License.
*/
+import org.apache.commons.lang3.Conversion;
+import org.slf4j.Logger;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
/**
- * The introspector cache API definition.
+ * This is the internal introspector cache implementation.
*
* @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
* @author <a href="mailto:cdauth@cdauth.eu">Candid Dauth</a>
* @version $Id$
* @since 1.5
*/
-public interface IntrospectorCache {
+public final class IntrospectorCache
+{
+ /**
+ * define a public string so that it can be looked for if interested
+ */
+ public final static String CACHEDUMP_MSG =
+ "IntrospectorCache detected classloader change. Dumping cache.";
+
+ /** Class logger */
+ private final Logger log;
+
+ /**
+ * Holds the method maps for the classes we know about. Map: Class --> ClassMap object.
+ */
+ private final Map classMapCache = new HashMap();
+
+ /**
+ * Holds the field maps for the classes we know about. Map: Class --> ClassFieldMap object.
+ */
+ private final Map classFieldMapCache = new HashMap();
+
+ /**
+ * Keep the names of the classes in another map. This is needed for a multi-classloader environment where it is possible
+ * to have Class 'Foo' loaded by a classloader and then get asked to introspect on 'Foo' from another class loader. While these
+ * two Class objects have the same name, a <code>classMethodMaps.get(Foo.class)</code> will return null. For that case, we
+ * keep a set of class names to recognize this case.
+ */
+ private final Set classNameCache = new HashSet();
+
+ /**
+ * Conversion handler
+ */
+ private final ConversionHandler conversionHandler;
+
+ /**
+ * C'tor
+ */
+ public IntrospectorCache(final Logger log, final ConversionHandler conversionHandler)
+ {
+ this.log = log;
+ this.conversionHandler = conversionHandler;
+ }
/**
* Clears the internal cache.
*/
- void clear();
+ public void clear()
+ {
+ synchronized (classMapCache)
+ {
+ classMapCache.clear();
+ classFieldMapCache.clear();
+ classNameCache.clear();
+ log.debug(CACHEDUMP_MSG);
+ }
+ }
/**
- * Lookup a given Class object in the cache. If it does not exist,
+ * Lookup a given Class object in the cache. If it does not exist,
* check whether this is due to a class change and purge the caches
* eventually.
*
* @param c The class to look up.
* @return A ClassMap object or null if it does not exist in the cache.
*/
- ClassMap get(Class c);
+ public ClassMap get(final Class c)
+ {
+ if (c == null)
+ {
+ throw new IllegalArgumentException("class is null!");
+ }
+
+ ClassMap classMap = (ClassMap)classMapCache.get(c);
+ if (classMap == null)
+ {
+ /*
+ * check to see if we have it by name.
+ * if so, then we have an object with the same
+ * name but loaded through a different class loader.
+ * In that case, we will just dump the cache to be sure.
+ */
+ synchronized (classMapCache)
+ {
+ if (classNameCache.contains(c.getName()))
+ {
+ clear();
+ }
+ }
+ }
+ return classMap;
+ }
/**
* Lookup a given Class object in the cache. If it does not exist,
@@ -52,7 +135,32 @@ public interface IntrospectorCache {
* @param c The class to look up.
* @return A ClassFieldMap object or null if it does not exist in the cache.
*/
- ClassFieldMap getFieldMap(final Class c);
+ public ClassFieldMap getFieldMap(final Class c)
+ {
+ if (c == null)
+ {
+ throw new IllegalArgumentException("class is null!");
+ }
+
+ ClassFieldMap classFieldMap = (ClassFieldMap)classFieldMapCache.get(c);
+ if (classFieldMap == null)
+ {
+ /*
+ * check to see if we have it by name.
+ * if so, then we have an object with the same
+ * name but loaded through a different class loader.
+ * In that case, we will just dump the cache to be sure.
+ */
+ synchronized (classMapCache)
+ {
+ if (classNameCache.contains(c.getName()))
+ {
+ clear();
+ }
+ }
+ }
+ return classFieldMap;
+ }
/**
* Creates a class map for specific class and registers it in the
@@ -62,6 +170,17 @@ public interface IntrospectorCache {
* @param c The class for which the class map gets generated.
* @return A ClassMap object.
*/
- ClassMap put(Class c);
+ public ClassMap put(final Class c)
+ {
+ final ClassMap classMap = new ClassMap(c, log, conversionHandler);
+ final ClassFieldMap classFieldMap = new ClassFieldMap(c, log);
+ synchronized (classMapCache)
+ {
+ classMapCache.put(c, classMap);
+ classFieldMapCache.put(c, classFieldMap);
+ classNameCache.add(c.getName());
+ }
+ return classMap;
+ }
}
Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java?rev=1754151&r1=1754150&r2=1754151&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/MethodMap.java Tue Jul 26 16:11:28 2016
@@ -19,6 +19,8 @@ package org.apache.velocity.util.introsp
* under the License.
*/
+import org.apache.commons.lang3.Conversion;
+
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Iterator;
@@ -33,6 +35,7 @@ import java.util.concurrent.ConcurrentHa
* @author <a href="mailto:Christoph.Reck@dlr.de">Christoph Reck</a>
* @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
* @author <a href="mailto:szegedia@freemail.hu">Attila Szegedi</a>
+ * @author <a href="mailto:claude.brisson@gmail.com">Claude Brisson</a>
* @version $Id$
*/
public class MethodMap
@@ -41,6 +44,26 @@ public class MethodMap
private static final int LESS_SPECIFIC = 1;
private static final int INCOMPARABLE = 2;
+ ConversionHandler conversionHandler;
+
+ /**
+ * Default constructor
+ */
+ public MethodMap()
+ {
+ this(null);
+ }
+
+ /**
+ * Constructor with provided conversion handler
+ * @param conversionHandler conversion handler
+ * @since 2.0
+ */
+ public MethodMap(ConversionHandler conversionHandler)
+ {
+ this.conversionHandler = conversionHandler;
+ }
+
/**
* Keep track of all methods with the same name.
*/
@@ -134,11 +157,17 @@ public class MethodMap
return getBestMatch(methodList, classes);
}
- private static Method getBestMatch(List methods, Class[] args)
+ private Method getBestMatch(List methods, Class[] args)
{
List equivalentMatches = null;
Method bestMatch = null;
Class[] bestMatchTypes = null;
+ int bestMatchComp = INCOMPARABLE; /* how does the best match compare to provided type */
+ Class[] unboxedArgs = new Class[args.length];
+ for (int i = 0; i < args.length; ++i)
+ {
+ unboxedArgs[i] = IntrospectionUtils.getUnboxedClass(args[i]);
+ }
for (Iterator i = methods.iterator(); i.hasNext(); )
{
Method method = (Method)i.next();
@@ -148,6 +177,7 @@ public class MethodMap
{
bestMatch = method;
bestMatchTypes = method.getParameterTypes();
+ bestMatchComp = compare(bestMatchTypes, unboxedArgs);
}
else
{
@@ -155,35 +185,60 @@ public class MethodMap
switch (compare(methodTypes, bestMatchTypes))
{
case MORE_SPECIFIC:
+ /* do not retain method if it's more specific than (or incomparable to) provided (unboxed) arguments
+ * while best batch is less specific
+ */
+ if (bestMatchComp == LESS_SPECIFIC && compare(methodTypes, unboxedArgs) != LESS_SPECIFIC)
+ {
+ break;
+ }
if (equivalentMatches == null)
{
bestMatch = method;
bestMatchTypes = methodTypes;
+ bestMatchComp = compare(bestMatchTypes, unboxedArgs);
}
else
{
- // have to beat all other ambiguous ones...
+ /* have to beat all other ambiguous ones... */
int ambiguities = equivalentMatches.size();
- for (int a=0; a < ambiguities; a++)
+ for (int a = 0; a < ambiguities; a++)
{
Method other = (Method)equivalentMatches.get(a);
switch (compare(methodTypes, other.getParameterTypes()))
{
case MORE_SPECIFIC:
- // ...and thus replace them all...
+ /* ...and thus replace them all...
+ * but do not retain method if it's more specific than (or incomparable to) provided (unboxed) arguments
+ * while best batch is less specific
+ */
+ if (bestMatchComp == LESS_SPECIFIC && compare(methodTypes, unboxedArgs) != LESS_SPECIFIC)
+ {
+ break;
+ }
bestMatch = method;
bestMatchTypes = methodTypes;
+ bestMatchComp = compare(bestMatchTypes, unboxedArgs);
equivalentMatches = null;
ambiguities = 0;
break;
case INCOMPARABLE:
- // ...join them...
+ /* ...join them...*/
equivalentMatches.add(method);
break;
case LESS_SPECIFIC:
- // ...or just go away.
+ /* retain it anyway if less specific than (unboxed) provided args while
+ * bestmatch is more specific
+ */
+ if (bestMatchComp == MORE_SPECIFIC && compare(methodTypes, unboxedArgs) == LESS_SPECIFIC)
+ {
+ bestMatch = method;
+ bestMatchTypes = methodTypes;
+ bestMatchComp = compare(bestMatchTypes, unboxedArgs);
+ equivalentMatches = null;
+ }
break;
}
}
@@ -199,7 +254,16 @@ public class MethodMap
break;
case LESS_SPECIFIC:
- // do nothing
+ /* retain it anyway if less specific than (unboxed) provided args while
+ * bestmatch is more specific
+ */
+ if (bestMatchComp == MORE_SPECIFIC && compare(methodTypes, unboxedArgs) == LESS_SPECIFIC)
+ {
+ bestMatch = method;
+ bestMatchTypes = methodTypes;
+ bestMatchComp = compare(bestMatchTypes, unboxedArgs);
+ equivalentMatches = null;
+ }
break;
}
}
@@ -235,7 +299,7 @@ public class MethodMap
* @return MORE_SPECIFIC if c1 is more specific than c2, LESS_SPECIFIC if
* c1 is less specific than c2, INCOMPARABLE if they are incomparable.
*/
- private static int compare(Class[] c1, Class[] c2)
+ private int compare(Class[] c1, Class[] c2)
{
boolean c1MoreSpecific = false;
boolean c2MoreSpecific = false;
@@ -255,7 +319,7 @@ public class MethodMap
// ok, move on and compare those of equal lengths
for(int i = 0; i < c1.length; ++i)
{
- if(c1[i] != c2[i])
+ if(c1[i] != c2[i] && c1[i] != null && c2[i] != null)
{
boolean last = (i == c1.length - 1);
c1MoreSpecific =
@@ -269,6 +333,20 @@ public class MethodMap
}
}
+ /* check for conversions */
+ if (!c1MoreSpecific && !c2MoreSpecific)
+ {
+ for(int i = 0; i < c1.length; ++i)
+ {
+ boolean last = (i == c1.length - 1);
+ if (c1[i] != c2[i] && c1[i] != null && c2[i] != null)
+ {
+ c1MoreSpecific = c1MoreSpecific || isConvertible(c2[i], c1[i], last);
+ c2MoreSpecific = c2MoreSpecific || isConvertible(c1[i], c2[i], last);
+ }
+ }
+ }
+
if(c1MoreSpecific)
{
if(c2MoreSpecific)
@@ -319,7 +397,7 @@ public class MethodMap
* @param classes arguments to method
* @return true if method is applicable to arguments
*/
- private static boolean isApplicable(Method method, Class[] classes)
+ private boolean isApplicable(Method method, Class[] classes)
{
Class[] methodArgs = method.getParameterTypes();
@@ -333,7 +411,8 @@ public class MethodMap
// all the args preceding the vararg must match
for (int i = 0; i < classes.length; i++)
{
- if (!isConvertible(methodArgs[i], classes[i], false))
+ if (!isConvertible(methodArgs[i], classes[i], false) &&
+ !isExplicitlyConvertible(methodArgs[i], classes[i], false))
{
return false;
}
@@ -352,14 +431,16 @@ public class MethodMap
// (e.g. String when the method is expecting String...)
for(int i = 0; i < classes.length; ++i)
{
- if(!isConvertible(methodArgs[i], classes[i], false))
+ if(!isConvertible(methodArgs[i], classes[i], false) &&
+ !isExplicitlyConvertible(methodArgs[i], classes[i], false))
{
// if we're on the last arg and the method expects an array
if (i == classes.length - 1 && methodArgs[i].isArray())
{
// check to see if the last arg is convertible
// to the array's component type
- return isConvertible(methodArgs[i], classes[i], true);
+ return isConvertible(methodArgs[i], classes[i], true) ||
+ isExplicitlyConvertible(methodArgs[i], classes[i], true);
}
return false;
}
@@ -397,17 +478,45 @@ public class MethodMap
return true;
}
- private static boolean isConvertible(Class formal, Class actual,
- boolean possibleVarArg)
+ /**
+ * Returns true if <code>actual</code> is convertible to <code>formal</code> by implicit Java method call conversions
+ *
+ * @param formal
+ * @param actual
+ * @param possibleVarArg
+ * @return convertible
+ */
+ private boolean isConvertible(Class formal, Class actual, boolean possibleVarArg)
{
return IntrospectionUtils.
isMethodInvocationConvertible(formal, actual, possibleVarArg);
}
- private static boolean isStrictConvertible(Class formal, Class actual,
- boolean possibleVarArg)
+ /**
+ * Returns true if <code>actual</code> is strictly convertible to <code>formal</code> (aka without implicit
+ * boxing/unboxing)
+ *
+ * @param formal
+ * @param actual
+ * @param possibleVarArg
+ * @return convertible
+ */
+ private static boolean isStrictConvertible(Class formal, Class actual, boolean possibleVarArg)
{
return IntrospectionUtils.
isStrictMethodInvocationConvertible(formal, actual, possibleVarArg);
}
+
+ /**
+ * Returns true if <code>actual</code> is convertible to <code>formal</code> using an explicit converter
+ *
+ * @param formal
+ * @param actual
+ * @param possibleVarArg
+ * @return
+ */
+ private boolean isExplicitlyConvertible(Class formal, Class actual, boolean possibleVarArg)
+ {
+ return conversionHandler != null && conversionHandler.isExplicitlyConvertible(formal, actual, possibleVarArg);
+ }
}
Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/UberspectImpl.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/UberspectImpl.java?rev=1754151&r1=1754150&r2=1754151&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/UberspectImpl.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/UberspectImpl.java Tue Jul 26 16:11:28 2016
@@ -20,6 +20,8 @@ package org.apache.velocity.util.introsp
*/
import org.apache.velocity.exception.VelocityException;
+import org.apache.velocity.runtime.RuntimeConstants;
+import org.apache.velocity.runtime.RuntimeServices;
import org.apache.velocity.runtime.parser.node.AbstractExecutor;
import org.apache.velocity.runtime.parser.node.BooleanPropertyExecutor;
import org.apache.velocity.runtime.parser.node.GetExecutor;
@@ -29,9 +31,12 @@ import org.apache.velocity.runtime.parse
import org.apache.velocity.runtime.parser.node.PutExecutor;
import org.apache.velocity.runtime.parser.node.SetExecutor;
import org.apache.velocity.runtime.parser.node.SetPropertyExecutor;
+import org.apache.velocity.runtime.resource.ResourceManager;
import org.apache.velocity.util.ArrayIterator;
import org.apache.velocity.util.ArrayListWrapper;
+import org.apache.velocity.util.ClassUtils;
import org.apache.velocity.util.EnumerationIterator;
+import org.apache.velocity.util.RuntimeServicesAware;
import org.slf4j.Logger;
import java.lang.reflect.Array;
@@ -49,7 +54,7 @@ import java.util.Map;
* @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
* @version $Id$
*/
-public class UberspectImpl implements Uberspect, UberspectLoggable
+public class UberspectImpl implements Uberspect, UberspectLoggable, RuntimeServicesAware
{
/**
* Our runtime logger.
@@ -62,13 +67,72 @@ public class UberspectImpl implements Ub
protected Introspector introspector;
/**
+ * the conversion handler
+ */
+ protected ConversionHandler conversionHandler;
+
+ /**
* init - generates the Introspector. As the setup code
* makes sure that the log gets set before this is called,
* we can initialize the Introspector using the log object.
*/
public void init()
{
- introspector = new Introspector(log);
+ introspector = new Introspector(log, conversionHandler);
+ }
+
+ public ConversionHandler getConversionHandler()
+ {
+ return conversionHandler;
+ }
+
+ /**
+ * sets the runtime services
+ * @param rs runtime services
+ */
+ public void setRuntimeServices(RuntimeServices rs)
+ {
+ String conversionHandlerClass = rs.getString(RuntimeConstants.CONVERSION_HANDLER_CLASS);
+ if (conversionHandlerClass == null || conversionHandlerClass.equals("none"))
+ {
+ conversionHandler = null;
+ }
+ else
+ {
+ Object o = null;
+
+ try
+ {
+ o = ClassUtils.getNewInstance(conversionHandlerClass);
+ }
+ catch (ClassNotFoundException cnfe )
+ {
+ String err = "The specified class for ConversionHandler (" + conversionHandlerClass
+ + ") does not exist or is not accessible to the current classloader.";
+ log.error(err);
+ throw new VelocityException(err, cnfe);
+ }
+ catch (InstantiationException ie)
+ {
+ throw new VelocityException("Could not instantiate class '" + conversionHandlerClass + "'", ie);
+ }
+ catch (IllegalAccessException ae)
+ {
+ throw new VelocityException("Cannot access class '" + conversionHandlerClass + "'", ae);
+ }
+
+ if (!(o instanceof ConversionHandler))
+ {
+ String err = "The specified class for ResourceManager (" + conversionHandlerClass
+ + ") does not implement " + ConversionHandler.class.getName()
+ + "; Velocity is not initialized correctly.";
+
+ log.error(err);
+ throw new VelocityException(err);
+ }
+
+ conversionHandler = (ConversionHandler) o;
+ }
}
/**
@@ -223,14 +287,15 @@ public class UberspectImpl implements Ub
*/
private Converter[] getNeededConverters(Class[] expected, Object[] provided)
{
- // var args are not handled here
+ if (conversionHandler == null) return null;
+ // var args are not handled here - CB TODO
int n = Math.min(expected.length, provided.length);
Converter[] converters = null;
for (int i = 0; i < n; ++i)
{
Object arg = provided[i];
if (arg == null) continue;
- Converter converter = IntrospectionUtils.getNeededConverter(expected[i], arg.getClass());
+ Converter converter = conversionHandler.getNeededConverter(expected[i], arg.getClass());
if (converter != null)
{
if (converters == null)
@@ -289,7 +354,7 @@ public class UberspectImpl implements Ub
if (!executor.isAlive())
{
executor = new BooleanPropertyExecutor(log, introspector, claz,
- identifier);
+ identifier);
}
/*
@@ -350,7 +415,7 @@ public class UberspectImpl implements Ub
/**
* Implementation of VelMethod
*/
- public static class VelMethodImpl implements VelMethod
+ public class VelMethodImpl implements VelMethod
{
final Method method;
Boolean isVarArg;
@@ -415,7 +480,10 @@ public class UberspectImpl implements Ub
{
for (int i = 0; i < actual.length; ++i)
{
- actual[i] = converters[i].convert(actual[i]);
+ if (converters[i] != null)
+ {
+ actual[i] = converters[i].convert(actual[i]);
+ }
}
}
@@ -488,10 +556,7 @@ public class UberspectImpl implements Ub
{
// make sure the last arg is an array of the expected type
Class argClass = actual[index].getClass();
- if (!argClass.isArray() &&
- IntrospectionUtils.isMethodInvocationConvertible(type,
- argClass,
- false))
+ if (!argClass.isArray() && IntrospectionUtils.isMethodInvocationConvertible(type, argClass, false))
{
// create a 1-length array to hold and replace the last param
Object lastActual = Array.newInstance(type, 1);
@@ -541,6 +606,14 @@ public class UberspectImpl implements Ub
}
/**
+ * @see org.apache.velocity.util.introspection.VelMethod#getMethod()
+ */
+ public Method getMethod()
+ {
+ return method;
+ }
+
+ /**
* @see org.apache.velocity.util.introspection.VelMethod#getReturnType()
*/
public Class getReturnType()
Modified: velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/VelMethod.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/VelMethod.java?rev=1754151&r1=1754150&r2=1754151&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/VelMethod.java (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/java/org/apache/velocity/util/introspection/VelMethod.java Tue Jul 26 16:11:28 2016
@@ -1,7 +1,5 @@
package org.apache.velocity.util.introspection;
-import java.lang.reflect.InvocationTargetException;
-
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
@@ -21,6 +19,9 @@ import java.lang.reflect.InvocationTarge
* under the License.
*/
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
/**
* Method used for regular method invocation
*
@@ -58,6 +59,13 @@ public interface VelMethod
public String getMethodName();
/**
+ * returns the underlying Method
+ * @return the method
+ * @since 2.0
+ */
+ public Method getMethod();
+
+ /**
* returns the return type of the method invoked
* @return The return type of the method invoked
*/
Modified: velocity/engine/trunk/velocity-engine-core/src/main/resources/org/apache/velocity/runtime/defaults/velocity.properties
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/velocity-engine-core/src/main/resources/org/apache/velocity/runtime/defaults/velocity.properties?rev=1754151&r1=1754150&r2=1754151&view=diff
==============================================================================
--- velocity/engine/trunk/velocity-engine-core/src/main/resources/org/apache/velocity/runtime/defaults/velocity.properties (original)
+++ velocity/engine/trunk/velocity-engine-core/src/main/resources/org/apache/velocity/runtime/defaults/velocity.properties Tue Jul 26 16:11:28 2016
@@ -186,6 +186,14 @@ parser.pool.size = 20
runtime.introspector.uberspect = org.apache.velocity.util.introspection.UberspectImpl
+# ----------------------------------------------------------------------------
+# CONVERSION HANDLER
+# ----------------------------------------------------------------------------
+# Sets the data types Conversion Handler used by the default uberspector
+# ----------------------------------------------------------------------------
+
+runtime.conversion.handler.class = org.apache.velocity.util.introspection.ConversionHandlerImpl
+
# ----------------------------------------------------------------------------
# SECURE INTROSPECTOR