Author: radu Date: Fri Aug 25 12:50:08 2017 New Revision: 1806167 URL: http://svn.apache.org/viewvc?rev=1806167&view=rev Log: SLING-7085 - Reduce code duplication
* exported org.apache.sling.scripting.sightly.compiler.util.ObjectModel * delegated calls from org.apache.sling.scripting.sightly.render.AbstractRuntimeObjectModel to ObjectModel Added: sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/util/ObjectModel.java sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/ sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/Person.java - copied, changed from r1806139, sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/impl/utils/CompileTimeObjectModelTest.java sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/internal/ sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/internal/AbstractPerson.java - copied, changed from r1806139, sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/impl/utils/CompileTimeObjectModelTest.java sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/internal/Adult.java - copied, changed from r1806139, sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/impl/utils/CompileTimeObjectModelTest.java sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/internal/AdultFactory.java - copied, changed from r1806139, sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/impl/utils/CompileTimeObjectModelTest.java sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/util/ sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/util/ObjectModelTest.java Removed: sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/CompileTimeObjectModel.java sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/impl/utils/CompileTimeObjectModelTest.java Modified: sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/BinaryOperator.java sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/UnaryOperator.java sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/util/package-info.java sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/optimization/DeadCodeRemoval.java sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/optimization/reduce/ExpressionReducer.java sling/trunk/bundles/scripting/sightly/engine/pom.xml sling/trunk/bundles/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/impl/engine/runtime/SlingRuntimeObjectModel.java sling/trunk/bundles/scripting/sightly/java-compiler/src/main/java/org/apache/sling/scripting/sightly/render/AbstractRuntimeObjectModel.java Modified: sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/BinaryOperator.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/BinaryOperator.java?rev=1806167&r1=1806166&r2=1806167&view=diff ============================================================================== --- sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/BinaryOperator.java (original) +++ sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/BinaryOperator.java Fri Aug 25 12:50:08 2017 @@ -19,7 +19,7 @@ package org.apache.sling.scripting.sightly.compiler.expression.nodes; import org.apache.sling.scripting.sightly.compiler.SightlyCompilerException; -import org.apache.sling.scripting.sightly.impl.compiler.CompileTimeObjectModel; +import org.apache.sling.scripting.sightly.compiler.util.ObjectModel; /** * Binary operators used in expressions. @@ -31,7 +31,7 @@ public enum BinaryOperator { AND { @Override public Object eval(Object left, Object right) { - return (CompileTimeObjectModel.toBoolean(left)) ? right : left; + return (ObjectModel.toBoolean(left)) ? right : left; } }, /** @@ -40,7 +40,7 @@ public enum BinaryOperator { OR { @Override public Object eval(Object left, Object right) { - return (CompileTimeObjectModel.toBoolean(left)) ? left : right; + return (ObjectModel.toBoolean(left)) ? left : right; } }, /** @@ -49,7 +49,7 @@ public enum BinaryOperator { CONCATENATE { @Override public Object eval(Object left, Object right) { - return CompileTimeObjectModel.toString(left).concat(CompileTimeObjectModel.toString(right)); + return ObjectModel.toString(left).concat(ObjectModel.toString(right)); } }, /** @@ -131,7 +131,7 @@ public enum BinaryOperator { ADD { @Override public Object eval(Object left, Object right) { - return adjust(CompileTimeObjectModel.toNumber(left).doubleValue() + CompileTimeObjectModel.toNumber(right).doubleValue()); + return adjust(ObjectModel.toNumber(left).doubleValue() + ObjectModel.toNumber(right).doubleValue()); } }, @@ -141,7 +141,7 @@ public enum BinaryOperator { SUB { @Override public Object eval(Object left, Object right) { - return adjust(CompileTimeObjectModel.toNumber(left).doubleValue() - CompileTimeObjectModel.toNumber(right).doubleValue()); + return adjust(ObjectModel.toNumber(left).doubleValue() - ObjectModel.toNumber(right).doubleValue()); } }, /** @@ -150,7 +150,7 @@ public enum BinaryOperator { MUL { @Override public Object eval(Object left, Object right) { - return adjust(CompileTimeObjectModel.toNumber(left).doubleValue() * CompileTimeObjectModel.toNumber(right).doubleValue()); + return adjust(ObjectModel.toNumber(left).doubleValue() * ObjectModel.toNumber(right).doubleValue()); } }, /** @@ -159,7 +159,7 @@ public enum BinaryOperator { DIV { @Override public Object eval(Object left, Object right) { - return adjust(CompileTimeObjectModel.toNumber(left).doubleValue() / CompileTimeObjectModel.toNumber(right).doubleValue()); + return adjust(ObjectModel.toNumber(left).doubleValue() / ObjectModel.toNumber(right).doubleValue()); } }, /** @@ -168,7 +168,7 @@ public enum BinaryOperator { I_DIV { @Override public Object eval(Object left, Object right) { - return CompileTimeObjectModel.toNumber(left).intValue() / CompileTimeObjectModel.toNumber(right).intValue(); + return ObjectModel.toNumber(left).intValue() / ObjectModel.toNumber(right).intValue(); } }, @@ -178,8 +178,8 @@ public enum BinaryOperator { REM { @Override public Object eval(Object left, Object right) { - return adjust(CompileTimeObjectModel.toNumber(left).intValue() - % CompileTimeObjectModel.toNumber(right).intValue()); + return adjust(ObjectModel.toNumber(left).intValue() + % ObjectModel.toNumber(right).intValue()); } }; Modified: sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/UnaryOperator.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/UnaryOperator.java?rev=1806167&r1=1806166&r2=1806167&view=diff ============================================================================== --- sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/UnaryOperator.java (original) +++ sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/expression/nodes/UnaryOperator.java Fri Aug 25 12:50:08 2017 @@ -19,7 +19,7 @@ package org.apache.sling.scripting.sightly.compiler.expression.nodes; import org.apache.commons.lang3.StringUtils; -import org.apache.sling.scripting.sightly.impl.compiler.CompileTimeObjectModel; +import org.apache.sling.scripting.sightly.compiler.util.ObjectModel; /** * Unary operators used in expressions. @@ -30,7 +30,7 @@ public enum UnaryOperator { NOT { @Override public Object eval(Object operand) { - return !CompileTimeObjectModel.toBoolean(operand); + return !ObjectModel.toBoolean(operand); } }, @@ -38,7 +38,7 @@ public enum UnaryOperator { IS_WHITESPACE { @Override public Object eval(Object operand) { - return StringUtils.isWhitespace(CompileTimeObjectModel.toString(operand)); + return StringUtils.isWhitespace(ObjectModel.toString(operand)); } }, @@ -48,7 +48,7 @@ public enum UnaryOperator { LENGTH { @Override public Object eval(Object operand) { - return CompileTimeObjectModel.toCollection(operand).size(); + return ObjectModel.toCollection(operand).size(); } }; Added: sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/util/ObjectModel.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/util/ObjectModel.java?rev=1806167&view=auto ============================================================================== --- sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/util/ObjectModel.java (added) +++ sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/util/ObjectModel.java Fri Aug 25 12:50:08 2017 @@ -0,0 +1,443 @@ +/******************************************************************************* + * 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.sling.scripting.sightly.compiler.util; + +import java.lang.reflect.Array; +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.math.NumberUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * The {@code ObjectModel} class provides various static models for object conversion and object property resolution. + */ +public final class ObjectModel { + + private static final Logger LOGGER = LoggerFactory.getLogger(ObjectModel.class); + + /** + * A {@link Set} that stores all the supported primitive classes. + */ + public static final Set<Class<?>> PRIMITIVE_CLASSES; + + static { + Set<Class<?>> primitivesBuilder = new HashSet<>(); + primitivesBuilder.add(Boolean.class); + primitivesBuilder.add(Boolean.class); + primitivesBuilder.add(Character.class); + primitivesBuilder.add(Byte.class); + primitivesBuilder.add(Short.class); + primitivesBuilder.add(Integer.class); + primitivesBuilder.add(Long.class); + primitivesBuilder.add(Float.class); + primitivesBuilder.add(Double.class); + primitivesBuilder.add(Void.class); + PRIMITIVE_CLASSES = Collections.unmodifiableSet(primitivesBuilder); + } + + + private static final String TO_STRING_METHOD = "toString"; + + private ObjectModel() {} + + /** + * Checks if the provided {@code object} is an instance of a primitive class. + * + * @param object the {@code Object} to check + * @return {@code true} if the {@code object} is a primitive, {@code false} otherwise + */ + public static boolean isPrimitive(Object object) { + return PRIMITIVE_CLASSES.contains(object.getClass()); + } + + /** + * Given the {@code target} object, this method attempts to resolve and return the value of the passed {@code property}. + * + * <p> + * The property can be either an index or a name: + * <ul> + * <li>index: the property is considered an index if its value is an integer number and in this case the {@code target} + * will be assumed to be either an array or it will be converted to a {@link Collection}; a fallback to {@link Map} will be + * made in case the previous two attempts failed + * </li> + * <li>name: the {@code property} will be converted to a {@link String} (see {@link #toString(Object)}); the {@code target} + * will be assumed to be either a {@link Map} or an object; if the {@link Map} attempt fails, the {@code property} will be + * used to check if the {@code target} has a publicly accessible field with this name or a publicly accessible method with no + * parameters with this name or a combination of the "get" or "is" prefixes plus the capitalised name (see + * {@link #invokeBeanMethod(Object, String)})</li> + * </ul> + * </p> + * @param target the target object + * @param property the property to be resolved + * @return the value of the property or {@code null} + */ + public static Object resolveProperty(Object target, Object property) { + if (target == null || property == null) { + return null; + } + Object resolved = null; + if (property instanceof Number) { + resolved = getIndex(target, ((Number) property).intValue()); + } + if (resolved == null) { + String propertyName = toString(property); + if (StringUtils.isNotEmpty(propertyName)) { + if (target instanceof Map) { + resolved = ((Map) target).get(propertyName); + } + if (resolved == null) { + resolved = getField(target, propertyName); + } + if (resolved == null) { + resolved = invokeBeanMethod(target, propertyName); + } + } + } + return resolved; + } + + /** + * Converts the given {@code object} to a boolean value, applying the following rules: + * + * <ul> + * <li>if the {@code object} is {@code null} the returned value is {@code false}</li> + * <li>if the {@code object} is a {@link Number} the method will return {@code false} only if the number's value is 0</li> + * <li>if the {@link String} representation of the {@code object} is equal irrespective of its casing to "true", the method will + * return {@code true}</li> + * <li>if the {@code object} is a {@link Collection} or a {@link Map}, the method will return {@code true} only if the collection / + * map is not empty</li> + * <li>if the object is an array, the method will return {@code true} only if the array is not empty</li> + * </ul> + * + * @param object the target object + * @return the boolean representation of the {@code object} according to the conversion rules + */ + public static boolean toBoolean(Object object) { + if (object == null) { + return false; + } + + if (object instanceof Number) { + Number number = (Number) object; + return !(number.doubleValue() == 0.0); + } + + String s = object.toString().trim(); + if ("".equals(s)) { + return false; + } else if ("true".equalsIgnoreCase(s) || "false".equalsIgnoreCase(s)) { + return Boolean.parseBoolean(s); + } + + if (object instanceof Collection) { + return ((Collection) object).size() > 0; + } + + if (object instanceof Map) { + return ((Map) object).size() > 0; + } + + if (object instanceof Iterable<?>) { + return ((Iterable<?>) object).iterator().hasNext(); + } + + if (object instanceof Iterator<?>) { + return ((Iterator<?>) object).hasNext(); + } + + return !(object instanceof Object[]) || ((Object[]) object).length > 0; + } + + /** + * Coerces the passed {@code object} to a numeric value. If the passed value is a {@link String} the conversion rules are those of + * {@link NumberUtils#createNumber(String)}. + * + * @param object the target object + * @return the numeric representation if one can be determined or {@code null} + * @see NumberUtils#createNumber(String) + */ + public static Number toNumber(Object object) { + if (object == null) { + return null; + } + if (object instanceof Number) { + return (Number) object; + } + String stringValue = toString(object); + try { + return NumberUtils.createNumber(stringValue); + } catch (NumberFormatException e) { + return null; + } + } + + /** + * Converts the passed {@code object} to a {@link String}. The following rules apply: + * + * <ul> + * <li>if the {@code object} is {@code null} an empty string will be returned</li> + * <li>if the {@code object} is an instance of a {@link String} the object itself will be returned</li> + * <li>if the object is a primitive (see {@link #isPrimitive(Object)}), its {@link String} representation will be returned</li> + * <li>if the object is an {@link Enum} its name will be returned (see {@link Enum#name()})</li> + * <li>otherwise an attempt to convert the object to a {@link Collection} will be made and then the output of + * {@link #collectionToString(Collection)} will be returned</li> + * </ul> + * + * @param object the target object + * @return the string representation of the object or an empty string + */ + public static String toString(Object object) { + String output = ""; + if (object != null) { + if (object instanceof String) { + output = (String) object; + } else if (isPrimitive(object)) { + output = object.toString(); + } else if (object instanceof Enum) { + return ((Enum) object).name(); + } else { + Collection<?> col = toCollection(object); + output = collectionToString(col); + } + } + return output; + } + + /** + * Forces the conversion of the passed {@code object} to a collection, according to the following rules: + * + * <ul> + * <li>if the {@code object} is {@code null} an empty collection will be returned</li> + * <li>if the {@code object} is an array a list transformation of the array will be returned</li> + * <li>if the {@code object} is a {@link Collection} the object itself will be returned</li> + * <li>if the {@code object} is an instance of a {@link Map} the map's key set will be returned (see {@link Map#keySet()})</li> + * <li>if the {@code object} is an instance of an {@link Enumeration} a list transformation will be returned</li> + * <li>if the {@code object} is an instance of an {@link Iterator} or {@link Iterable} the result of {@link #fromIterator(Iterator)} + * will be returned</li> + * <li>if the {@code object} is an instance of a {@link String} or {@link Number} a {@link Collection} containing only this + * object will be returned</li> + * <li>any other case not covered by the previous rules will result in an empty {@link Collection}</li> + * </ul> + * + * @param object the target object + * @return the collection representation of the object + */ + public static Collection<Object> toCollection(Object object) { + if (object == null) { + return Collections.emptyList(); + } + if (object instanceof Object[]) { + return Arrays.asList((Object[]) object); + } + if (object instanceof Collection) { + return (Collection<Object>) object; + } + if (object instanceof Map) { + return ((Map) object).keySet(); + } + if (object instanceof Enumeration) { + return Collections.list((Enumeration<Object>) object); + } + if (object instanceof Iterator) { + return fromIterator((Iterator<Object>) object); + } + if (object instanceof Iterable) { + Iterable<Object> iterable = (Iterable<Object>) object; + return fromIterator(iterable.iterator()); + } + if (object instanceof String || object instanceof Number) { + Collection<Object> list = new ArrayList<>(); + list.add(object); + return list; + } + return Collections.emptyList(); + } + + /** + * Converts the passed {@code collection} to a comma separated values {@link String} representation. + * + * @param collection the collection to be converted to CSV + * @return the CSV; if the {@code collection} is empty then an empty string will be returned + */ + public static String collectionToString(Collection<?> collection) { + StringBuilder builder = new StringBuilder(); + String prefix = ""; + for (Object o : collection) { + builder.append(prefix).append(toString(o)); + prefix = ","; + } + return builder.toString(); + } + + /** + * Given an {@code iterator}, this method will return a {@link Collection}. + * + * @param iterator the iterator to be transformed into a {@code collection} + * @return a collection with the iterator's elements + */ + public static Collection<Object> fromIterator(Iterator<Object> iterator) { + ArrayList<Object> result = new ArrayList<>(); + while (iterator.hasNext()) { + result.add(iterator.next()); + } + return result; + } + + /** + * Given an indexable {@code object} (i.e. an array or a collection), this method will return the value available at the {@code + * index} position. + * + * @param object the indexable object + * @param index the index + * @return the value stored at the {@code index} or {@code null} + */ + public static Object getIndex(Object object, int index) { + Class<?> cls = object.getClass(); + if (cls.isArray() && index >= 0 && index < Array.getLength(object)) { + return Array.get(object, index); + } + Collection collection = toCollection(object); + if (collection instanceof List && index >= 0 && index < collection.size()) { + return ((List) collection).get(index); + } + return null; + } + + /** + * Given an {@code object}, this method will return the value of the public field identified by {@code fieldName}. + * + * @param object the target object + * @param fieldName the name of the field + * @return the value of the field or {@code null} if the field was not found + */ + public static Object getField(Object object, String fieldName) { + Class<?> cls = object.getClass(); + if (cls.isArray() && "length".equals(fieldName)) { + return Array.getLength(object); + } + try { + Field field = cls.getField(fieldName); + return field.get(object); + } catch (Exception e) { + return null; + } + } + + /** + * Given a bean {@code object}, this method will invoke the public method without parameters identified by {@code methodName} and + * return the invocation's result. + * + * @param object the target object + * @param methodName the name of the public method without parameters to invoke + * @return the invocation's result or {@code null} if such a method cannot be found + */ + public static Object invokeBeanMethod(Object object, String methodName) { + Class<?> cls = object.getClass(); + Method method = findBeanMethod(cls, methodName); + if (method != null) { + try { + method = extractMethodInheritanceChain(cls, method); + return method.invoke(object); + } catch (Exception e) { + LOGGER.error("Cannot access method " + methodName + " on object " + object.toString(), e); + } + } + return null; + } + + /** + * Given a bean class and a base method name, this method will try to find a public method without parameters that is named: + * <ol> + * <li>{@code baseName}</li> + * <li>get + {@code BaseName}</li> + * <li>is + {@code BaseName}</li> + * </ol> + * + * @param cls the class into which to search for the method + * @param baseName the base method name + * @return a method that matches the criteria or {@code null} + */ + public static Method findBeanMethod(Class<?> cls, String baseName) { + Method[] publicMethods = cls.getMethods(); + String capitalized = StringUtils.capitalize(baseName); + for (Method method : publicMethods) { + if (method.getParameterTypes().length == 0) { + String methodName = method.getName(); + if (baseName.equals(methodName) || ("get" + capitalized).equals(methodName) || ("is" + capitalized).equals(methodName)) { + if (isMethodAllowed(method)) { + return method; + } + break; + } + } + } + return null; + } + + /** + * Returns {@code true} if the method is not one of the {@link Object}'s class declared methods, with the exception of + * {@link Object#toString()}. + * + * @param method the method to check + * @return {@code true} if the method is not one of the {@link Object}'s class declared methods, with the exception of + * {@link Object#toString()}, {@code false} otherwise + */ + public static boolean isMethodAllowed(Method method) { + Class<?> declaringClass = method.getDeclaringClass(); + return declaringClass != Object.class || TO_STRING_METHOD.equals(method.getName()); + } + + private static Method extractMethodInheritanceChain(Class type, Method method) throws NoSuchMethodException { + if (method == null || Modifier.isPublic(type.getModifiers())) { + return method; + } + Class[] interfaces = type.getInterfaces(); + Method parentMethod; + for (Class<?> iface : interfaces) { + parentMethod = getClassMethod(iface, method); + if (parentMethod != null) { + return parentMethod; + } + } + return getClassMethod(type.getSuperclass(), method); + } + + private static Method getClassMethod(Class<?> type, Method method) throws NoSuchMethodException { + Method parentMethod = type.getMethod(method.getName(), method.getParameterTypes()); + parentMethod = extractMethodInheritanceChain(parentMethod.getDeclaringClass(), parentMethod); + if (parentMethod != null) { + return parentMethod; + } + return null; + } + + +} Modified: sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/util/package-info.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/util/package-info.java?rev=1806167&r1=1806166&r2=1806167&view=diff ============================================================================== --- sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/util/package-info.java (original) +++ sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/compiler/util/package-info.java Fri Aug 25 12:50:08 2017 @@ -15,7 +15,7 @@ * limitations under the License. ******************************************************************************/ -@Version("1.0.0") +@Version("1.1.0") package org.apache.sling.scripting.sightly.compiler.util; import org.osgi.annotation.versioning.Version; Modified: sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/optimization/DeadCodeRemoval.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/optimization/DeadCodeRemoval.java?rev=1806167&r1=1806166&r2=1806167&view=diff ============================================================================== --- sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/optimization/DeadCodeRemoval.java (original) +++ sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/optimization/DeadCodeRemoval.java Fri Aug 25 12:50:08 2017 @@ -35,7 +35,7 @@ import org.apache.sling.scripting.sightl import org.apache.sling.scripting.sightly.impl.compiler.util.stream.Streams; import org.apache.sling.scripting.sightly.impl.compiler.visitor.StatefulRangeIgnore; import org.apache.sling.scripting.sightly.impl.compiler.visitor.TrackingVisitor; -import org.apache.sling.scripting.sightly.impl.compiler.CompileTimeObjectModel; +import org.apache.sling.scripting.sightly.compiler.util.ObjectModel; /** * Removes code under conditionals which are proven to fail. It is probably a good idea to run this optimization after running @@ -70,16 +70,16 @@ public class DeadCodeRemoval extends Tra Boolean truthValue = null; ExpressionNode node = variableBindingStart.getExpression(); if (node instanceof StringConstant) { - truthValue = CompileTimeObjectModel.toBoolean(((StringConstant) node).getText()); + truthValue = ObjectModel.toBoolean(((StringConstant) node).getText()); } if (node instanceof BooleanConstant) { truthValue = ((BooleanConstant) node).getValue(); } if (node instanceof NumericConstant) { - truthValue = CompileTimeObjectModel.toBoolean(((NumericConstant) node).getValue()); + truthValue = ObjectModel.toBoolean(((NumericConstant) node).getValue()); } if (node instanceof NullLiteral) { - truthValue = CompileTimeObjectModel.toBoolean(null); + truthValue = ObjectModel.toBoolean(null); } tracker.pushVariable(variableBindingStart.getVariableName(), truthValue); outStream.write(variableBindingStart); Modified: sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/optimization/reduce/ExpressionReducer.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/optimization/reduce/ExpressionReducer.java?rev=1806167&r1=1806166&r2=1806167&view=diff ============================================================================== --- sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/optimization/reduce/ExpressionReducer.java (original) +++ sling/trunk/bundles/scripting/sightly/compiler/src/main/java/org/apache/sling/scripting/sightly/impl/compiler/optimization/reduce/ExpressionReducer.java Fri Aug 25 12:50:08 2017 @@ -40,7 +40,7 @@ import org.apache.sling.scripting.sightl import org.apache.sling.scripting.sightly.compiler.expression.nodes.TernaryOperator; import org.apache.sling.scripting.sightly.compiler.expression.nodes.UnaryOperation; import org.apache.sling.scripting.sightly.compiler.util.VariableTracker; -import org.apache.sling.scripting.sightly.impl.compiler.CompileTimeObjectModel; +import org.apache.sling.scripting.sightly.compiler.util.ObjectModel; /** * Try to evaluate constant parts in expressions @@ -87,7 +87,7 @@ public class ExpressionReducer implement property.getNode())); } - return EvalResult.constant(CompileTimeObjectModel.resolveProperty( + return EvalResult.constant(ObjectModel.resolveProperty( target.getValue(), property.getValue())); } @@ -147,7 +147,7 @@ public class ExpressionReducer implement ternaryOperator.getThenBranch(), ternaryOperator.getElseBranch())); } - return (CompileTimeObjectModel.toBoolean(condition.getValue())) + return (ObjectModel.toBoolean(condition.getValue())) ? eval(ternaryOperator.getThenBranch()) : eval(ternaryOperator.getElseBranch()); } Copied: sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/Person.java (from r1806139, sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/impl/utils/CompileTimeObjectModelTest.java) URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/Person.java?p2=sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/Person.java&p1=sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/impl/utils/CompileTimeObjectModelTest.java&r1=1806139&r2=1806167&rev=1806167&view=diff ============================================================================== --- sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/impl/utils/CompileTimeObjectModelTest.java (original) +++ sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/Person.java Fri Aug 25 12:50:08 2017 @@ -16,25 +16,14 @@ * specific language governing permissions and limitations * under the License. ******************************************************************************/ -package org.apache.sling.scripting.sightly.impl.utils; +package org.apache.sling.scripting.sightly.testobjects; -import java.util.Collection; +public interface Person { -import org.apache.sling.scripting.sightly.impl.compiler.CompileTimeObjectModel; -import org.junit.Test; + long CONSTANT = 1; -import static org.junit.Assert.*; - -public class CompileTimeObjectModelTest { - - @Test - public void testGetCollectionWithOneElement() { - String stringObject = "test"; - Integer numberObject = 1; - Collection stringCollection = CompileTimeObjectModel.toCollection(stringObject); - assertTrue(stringCollection.size() == 1 && stringCollection.contains(stringObject)); - Collection numberCollection = CompileTimeObjectModel.toCollection(numberObject); - assertTrue(numberCollection.size() == 1 && numberCollection.contains(numberObject)); - } + String getFirstName(); + String getLastName(); + } Copied: sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/internal/AbstractPerson.java (from r1806139, sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/impl/utils/CompileTimeObjectModelTest.java) URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/internal/AbstractPerson.java?p2=sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/internal/AbstractPerson.java&p1=sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/impl/utils/CompileTimeObjectModelTest.java&r1=1806139&r2=1806167&rev=1806167&view=diff ============================================================================== --- sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/impl/utils/CompileTimeObjectModelTest.java (original) +++ sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/internal/AbstractPerson.java Fri Aug 25 12:50:08 2017 @@ -16,25 +16,27 @@ * specific language governing permissions and limitations * under the License. ******************************************************************************/ -package org.apache.sling.scripting.sightly.impl.utils; +package org.apache.sling.scripting.sightly.testobjects.internal; -import java.util.Collection; +import org.apache.sling.scripting.sightly.testobjects.Person; -import org.apache.sling.scripting.sightly.impl.compiler.CompileTimeObjectModel; -import org.junit.Test; +abstract class AbstractPerson implements Person { -import static org.junit.Assert.*; + private String firstName; + private String lastName; -public class CompileTimeObjectModelTest { + public AbstractPerson(String firstName, String lastName) { + this.firstName = firstName; + this.lastName = lastName; + } - @Test - public void testGetCollectionWithOneElement() { - String stringObject = "test"; - Integer numberObject = 1; - Collection stringCollection = CompileTimeObjectModel.toCollection(stringObject); - assertTrue(stringCollection.size() == 1 && stringCollection.contains(stringObject)); - Collection numberCollection = CompileTimeObjectModel.toCollection(numberObject); - assertTrue(numberCollection.size() == 1 && numberCollection.contains(numberObject)); + @Override + public String getFirstName() { + return firstName; } + @Override + public String getLastName() { + return lastName; + } } Copied: sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/internal/Adult.java (from r1806139, sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/impl/utils/CompileTimeObjectModelTest.java) URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/internal/Adult.java?p2=sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/internal/Adult.java&p1=sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/impl/utils/CompileTimeObjectModelTest.java&r1=1806139&r2=1806167&rev=1806167&view=diff ============================================================================== --- sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/impl/utils/CompileTimeObjectModelTest.java (original) +++ sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/internal/Adult.java Fri Aug 25 12:50:08 2017 @@ -16,25 +16,17 @@ * specific language governing permissions and limitations * under the License. ******************************************************************************/ -package org.apache.sling.scripting.sightly.impl.utils; +package org.apache.sling.scripting.sightly.testobjects.internal; -import java.util.Collection; +class Adult extends AbstractPerson { -import org.apache.sling.scripting.sightly.impl.compiler.CompileTimeObjectModel; -import org.junit.Test; + public static final long TODAY = System.currentTimeMillis(); -import static org.junit.Assert.*; - -public class CompileTimeObjectModelTest { - - @Test - public void testGetCollectionWithOneElement() { - String stringObject = "test"; - Integer numberObject = 1; - Collection stringCollection = CompileTimeObjectModel.toCollection(stringObject); - assertTrue(stringCollection.size() == 1 && stringCollection.contains(stringObject)); - Collection numberCollection = CompileTimeObjectModel.toCollection(numberObject); - assertTrue(numberCollection.size() == 1 && numberCollection.contains(numberObject)); + Adult(String firstName, String lastName) { + super(firstName, lastName); } + public String getFullName() { + return getFirstName() + ", " + getLastName(); + } } Copied: sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/internal/AdultFactory.java (from r1806139, sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/impl/utils/CompileTimeObjectModelTest.java) URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/internal/AdultFactory.java?p2=sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/internal/AdultFactory.java&p1=sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/impl/utils/CompileTimeObjectModelTest.java&r1=1806139&r2=1806167&rev=1806167&view=diff ============================================================================== --- sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/impl/utils/CompileTimeObjectModelTest.java (original) +++ sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/testobjects/internal/AdultFactory.java Fri Aug 25 12:50:08 2017 @@ -16,25 +16,11 @@ * specific language governing permissions and limitations * under the License. ******************************************************************************/ -package org.apache.sling.scripting.sightly.impl.utils; +package org.apache.sling.scripting.sightly.testobjects.internal; -import java.util.Collection; +public class AdultFactory { -import org.apache.sling.scripting.sightly.impl.compiler.CompileTimeObjectModel; -import org.junit.Test; - -import static org.junit.Assert.*; - -public class CompileTimeObjectModelTest { - - @Test - public void testGetCollectionWithOneElement() { - String stringObject = "test"; - Integer numberObject = 1; - Collection stringCollection = CompileTimeObjectModel.toCollection(stringObject); - assertTrue(stringCollection.size() == 1 && stringCollection.contains(stringObject)); - Collection numberCollection = CompileTimeObjectModel.toCollection(numberObject); - assertTrue(numberCollection.size() == 1 && numberCollection.contains(numberObject)); + public static Adult createAdult(String firstName, String lastName) { + return new Adult(firstName, lastName); } - } Added: sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/util/ObjectModelTest.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/util/ObjectModelTest.java?rev=1806167&view=auto ============================================================================== --- sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/util/ObjectModelTest.java (added) +++ sling/trunk/bundles/scripting/sightly/compiler/src/test/java/org/apache/sling/scripting/sightly/util/ObjectModelTest.java Fri Aug 25 12:50:08 2017 @@ -0,0 +1,193 @@ +/******************************************************************************* + * 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.sling.scripting.sightly.util; + +import java.util.Arrays; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Vector; + +import org.apache.sling.scripting.sightly.compiler.expression.nodes.BinaryOperator; +import org.apache.sling.scripting.sightly.compiler.util.ObjectModel; +import org.apache.sling.scripting.sightly.testobjects.Person; +import org.apache.sling.scripting.sightly.testobjects.internal.AdultFactory; +import org.junit.Test; + +import static org.junit.Assert.*; + +public class ObjectModelTest { + + @Test + public void testToBoolean() { + assertFalse(ObjectModel.toBoolean(null)); + assertFalse(ObjectModel.toBoolean(0)); + assertTrue(ObjectModel.toBoolean(123456)); + assertFalse(ObjectModel.toBoolean("")); + assertFalse(ObjectModel.toBoolean("FalSe")); + assertFalse(ObjectModel.toBoolean("false")); + assertFalse(ObjectModel.toBoolean("FALSE")); + assertTrue(ObjectModel.toBoolean("true")); + assertTrue(ObjectModel.toBoolean("TRUE")); + assertTrue(ObjectModel.toBoolean("TrUE")); + Integer[] testArray = new Integer[] {1, 2, 3}; + List testList = Arrays.asList(testArray); + assertTrue(ObjectModel.toBoolean(testArray)); + assertFalse(ObjectModel.toBoolean(new Integer[]{})); + assertTrue(ObjectModel.toBoolean(testList)); + assertFalse(ObjectModel.toBoolean(Collections.emptyList())); + Map<String, Integer> map = new HashMap<String, Integer>() {{ + put("one", 1); + put("two", 2); + }}; + assertTrue(ObjectModel.toBoolean(map)); + assertFalse(ObjectModel.toBoolean(Collections.EMPTY_MAP)); + assertTrue(ObjectModel.toBoolean(testList.iterator())); + assertFalse(ObjectModel.toBoolean(Collections.EMPTY_LIST.iterator())); + assertTrue(ObjectModel.toBoolean(new Bag<>(testArray))); + assertFalse(ObjectModel.toBoolean(new Bag<>(new Integer[]{}))); + assertTrue(ObjectModel.toBoolean(new Date())); + } + + @Test + public void testToNumber() { + assertEquals(1, ObjectModel.toNumber(1)); + assertEquals(1, ObjectModel.toNumber("1")); + assertNull(ObjectModel.toNumber(null)); + assertNull(ObjectModel.toNumber("1-2")); + } + + @Test + public void testToString() { + assertEquals("", ObjectModel.toString(null)); + assertEquals("1", ObjectModel.toString("1")); + assertEquals("1", ObjectModel.toString(1)); + assertEquals("ADD", ObjectModel.toString(BinaryOperator.ADD)); + Integer[] testArray = new Integer[] {1, 2, 3}; + List testList = Arrays.asList(testArray); + assertEquals("1,2,3", ObjectModel.toString(testList)); + } + + @Test + public void testToCollection() { + assertTrue(ObjectModel.toCollection(null).isEmpty()); + assertTrue(ObjectModel.toCollection(new StringBuilder()).isEmpty()); + Integer[] testArray = new Integer[] {1, 2, 3}; + List testList = Arrays.asList(testArray); + Map<String, Integer> map = new HashMap<String, Integer>() {{ + put("one", 1); + put("two", 2); + }}; + assertEquals(testList, ObjectModel.toCollection(testArray)); + assertEquals(testList, ObjectModel.toCollection(testList)); + assertEquals(map.keySet(), ObjectModel.toCollection(map)); + Vector vector = new Vector(testList); + assertEquals(testList, ObjectModel.toCollection(vector.elements())); + assertEquals(testList, ObjectModel.toCollection(testList.iterator())); + assertEquals(testList, ObjectModel.toCollection(new Bag<>(testArray))); + String stringObject = "test"; + Integer numberObject = 1; + Collection stringCollection = ObjectModel.toCollection(stringObject); + assertTrue(stringCollection.size() == 1 && stringCollection.contains(stringObject)); + Collection numberCollection = ObjectModel.toCollection(numberObject); + assertTrue(numberCollection.size() == 1 && numberCollection.contains(numberObject)); + } + + @Test + public void testCollectionToString() { + Integer[] testArray = new Integer[] {1, 2, 3}; + List testList = Arrays.asList(testArray); + assertEquals("1,2,3", ObjectModel.collectionToString(testList)); + } + + @Test + public void testFromIterator() { + Integer[] testArray = new Integer[] {1, 2, 3}; + List testList = Arrays.asList(testArray); + assertEquals(testList, ObjectModel.fromIterator(testList.iterator())); + } + + @Test + public void testResolveProperty() { + assertNull(ObjectModel.resolveProperty(null, null)); + Integer[] testArray = new Integer[] {1, 2, 3}; + assertEquals(2, ObjectModel.resolveProperty(testArray, 1)); + assertNull(ObjectModel.resolveProperty(testArray, 3)); + assertNull(ObjectModel.resolveProperty(testArray, -1)); + List<Integer> testList = Arrays.asList(testArray); + assertEquals(2, ObjectModel.resolveProperty(testList, 1)); + assertNull(ObjectModel.resolveProperty(testList, 3)); + assertNull(ObjectModel.resolveProperty(testList, -1)); + Map<String, Integer> map = new HashMap<String, Integer>() {{ + put("one", 1); + put("two", 2); + }}; + assertEquals(1, ObjectModel.resolveProperty(map, "one")); + assertNull(ObjectModel.resolveProperty(map, null)); + assertNull(ObjectModel.resolveProperty(map, "")); + Person johnDoe = AdultFactory.createAdult("John", "Doe"); + assertEquals("Expected to be able to access public static final constants.", 1l, ObjectModel.resolveProperty(johnDoe, "CONSTANT")); + assertNull("Did not expect to be able to access public fields from package protected classes.", ObjectModel.resolveProperty(johnDoe, + "TODAY")); + assertEquals("Expected to be able to access an array's length property.", 3, ObjectModel.resolveProperty(testArray, "length")); + assertNotNull("Expected not null result for invocation of interface method on implementation class.", + ObjectModel.resolveProperty(johnDoe, "lastName")); + assertNull("Expected null result for public method available on implementation but not exposed by interface.", ObjectModel + .resolveProperty(johnDoe, "fullName")); + assertNull("Expected null result for inexistent method.", ObjectModel.resolveProperty(johnDoe, "nomethod")); + } + + + private class Bag<T> implements Iterable<T> { + + private T[] backingArray; + + public Bag(T[] array) { + this.backingArray = array; + } + + @Override + public Iterator<T> iterator() { + return new Iterator<T>() { + + int index = 0; + + @Override + public boolean hasNext() { + return index < backingArray.length; + } + + @Override + public T next() { + return backingArray[index++]; + } + + @Override + public void remove() { + + } + }; + } + } +} Modified: sling/trunk/bundles/scripting/sightly/engine/pom.xml URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/sightly/engine/pom.xml?rev=1806167&r1=1806166&r2=1806167&view=diff ============================================================================== --- sling/trunk/bundles/scripting/sightly/engine/pom.xml (original) +++ sling/trunk/bundles/scripting/sightly/engine/pom.xml Fri Aug 25 12:50:08 2017 @@ -172,8 +172,7 @@ <dependency> <groupId>org.apache.sling</groupId> <artifactId>org.apache.sling.scripting.sightly.compiler.java</artifactId> - <!-- reset to 1.0.10 after the release is public --> - <version>1.0.10</version> + <version>1.0.11-SNAPSHOT</version> <scope>provided</scope> </dependency> Modified: sling/trunk/bundles/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/impl/engine/runtime/SlingRuntimeObjectModel.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/impl/engine/runtime/SlingRuntimeObjectModel.java?rev=1806167&r1=1806166&r2=1806167&view=diff ============================================================================== --- sling/trunk/bundles/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/impl/engine/runtime/SlingRuntimeObjectModel.java (original) +++ sling/trunk/bundles/scripting/sightly/engine/src/main/java/org/apache/sling/scripting/sightly/impl/engine/runtime/SlingRuntimeObjectModel.java Fri Aug 25 12:50:08 2017 @@ -18,41 +18,25 @@ ******************************************************************************/ package org.apache.sling.scripting.sightly.impl.engine.runtime; -import java.util.Map; - -import org.apache.commons.lang3.StringUtils; import org.apache.sling.api.adapter.Adaptable; import org.apache.sling.api.resource.ValueMap; -import org.apache.sling.scripting.sightly.Record; import org.apache.sling.scripting.sightly.render.AbstractRuntimeObjectModel; public class SlingRuntimeObjectModel extends AbstractRuntimeObjectModel { @Override protected Object getProperty(Object target, Object propertyObj) { - String property = toString(propertyObj); - if (StringUtils.isEmpty(property)) { - throw new IllegalArgumentException("Invalid property name"); - } - if (target == null) { - return null; - } Object result = null; - if (target instanceof Map) { - result = getMapProperty((Map) target, property); - } - if (result == null && target instanceof Record) { - result = ((Record) target).getProperty(property); - } - if (result == null) { - result = getObjectProperty(target, property); - } - if (result == null && target instanceof Adaptable) { + if (target instanceof Adaptable) { ValueMap valueMap = ((Adaptable) target).adaptTo(ValueMap.class); if (valueMap != null) { + String property = toString(propertyObj); result = valueMap.get(property); } } + if (result == null) { + result = super.getProperty(target, propertyObj); + } return result; } Modified: sling/trunk/bundles/scripting/sightly/java-compiler/src/main/java/org/apache/sling/scripting/sightly/render/AbstractRuntimeObjectModel.java URL: http://svn.apache.org/viewvc/sling/trunk/bundles/scripting/sightly/java-compiler/src/main/java/org/apache/sling/scripting/sightly/render/AbstractRuntimeObjectModel.java?rev=1806167&r1=1806166&r2=1806167&view=diff ============================================================================== --- sling/trunk/bundles/scripting/sightly/java-compiler/src/main/java/org/apache/sling/scripting/sightly/render/AbstractRuntimeObjectModel.java (original) +++ sling/trunk/bundles/scripting/sightly/java-compiler/src/main/java/org/apache/sling/scripting/sightly/render/AbstractRuntimeObjectModel.java Fri Aug 25 12:50:08 2017 @@ -16,57 +16,36 @@ ******************************************************************************/ package org.apache.sling.scripting.sightly.render; -import java.lang.reflect.Array; -import java.lang.reflect.Field; import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.Date; -import java.util.Enumeration; import java.util.HashMap; -import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.apache.sling.scripting.sightly.Record; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.sling.scripting.sightly.compiler.util.ObjectModel; /** * Default abstract implementation of {@link RuntimeObjectModel}. */ public abstract class AbstractRuntimeObjectModel implements RuntimeObjectModel { - private static final Logger LOGGER = LoggerFactory.getLogger(AbstractRuntimeObjectModel.class); - /** * A {@link Set} that stores all the supported primitive classes. */ - public static final Set<Class<?>> PRIMITIVE_CLASSES = Collections.unmodifiableSet(new HashSet<Class<?>>() {{ - add(Boolean.class); - add(Character.class); - add(Byte.class); - add(Short.class); - add(Integer.class); - add(Long.class); - add(Float.class); - add(Double.class); - add(Void.class); - }}); + public static final Set<Class<?>> PRIMITIVE_CLASSES = ObjectModel.PRIMITIVE_CLASSES; public static final String TO_STRING_METHOD = "toString"; @Override public boolean isPrimitive(Object obj) { - return PRIMITIVE_CLASSES.contains(obj.getClass()); + return ObjectModel.isPrimitive(obj); } @Override @@ -83,7 +62,7 @@ public abstract class AbstractRuntimeObj return true; } String value = toString(target); - return NumberUtils.isNumber(value); + return NumberUtils.isCreatable(value); } @Override @@ -96,7 +75,7 @@ public abstract class AbstractRuntimeObj public Object resolveProperty(Object target, Object property) { Object resolved; if (property instanceof Number) { - resolved = getIndex(target, ((Number) property).intValue()); + resolved = ObjectModel.getIndex(target, ((Number) property).intValue()); } else { resolved = getProperty(target, property); } @@ -105,25 +84,15 @@ public abstract class AbstractRuntimeObj @Override public boolean toBoolean(Object object) { - return toBooleanInternal(object); + return ObjectModel.toBoolean(object); } @Override public Number toNumber(Object object) { - if (object == null) { - return null; - } - if (object instanceof Number) { - return (Number) object; - } - String stringValue = toString(object); - try { - return NumberUtils.createNumber(stringValue); - } catch (NumberFormatException e) { - return null; - } + return ObjectModel.toNumber(object); } + @Override public Date toDate(Object object) { if (object instanceof Date) { return (Date)object; @@ -135,12 +104,15 @@ public abstract class AbstractRuntimeObj @Override public String toString(Object target) { - return objectToString(target); + return ObjectModel.toString(target); } @Override public Collection<Object> toCollection(Object object) { - return obtainCollection(object); + if (object instanceof Record) { + return ((Record) object).getPropertyNames(); + } + return ObjectModel.toCollection(object); } @Override @@ -150,7 +122,6 @@ public abstract class AbstractRuntimeObj } else if (object instanceof Record) { Map<String, Object> map = new HashMap<>(); Record record = (Record) object; - @SuppressWarnings("unchecked") Set<String> properties = record.getPropertyNames(); for (String property : properties) { map.put(property, record.getProperty(property)); @@ -160,247 +131,136 @@ public abstract class AbstractRuntimeObj return Collections.emptyMap(); } - protected String objectToString(Object obj) { - String output = ""; - if (obj != null) { - if (obj instanceof String) { - output = (String) obj; - } else if (isPrimitive(obj)) { - output = obj.toString(); - } else if (obj instanceof Enum) { - return ((Enum) obj).name(); - } else { - Collection<?> col = obtainCollection(obj); - if (col != null) { - output = collectionToString(col); - } - } - } - return output; - } - protected Object getProperty(Object target, Object propertyObj) { - String property = toString(propertyObj); - if (StringUtils.isEmpty(property)) { - throw new IllegalArgumentException("Invalid property name"); - } - if (target == null) { - return null; - } + String property = ObjectModel.toString(propertyObj); Object result = null; - if (target instanceof Map) { - result = getMapProperty((Map) target, property); - } - if (result == null && target instanceof Record) { + if (target instanceof Record) { result = ((Record) target).getProperty(property); } if (result == null) { - result = getObjectProperty(target, property); + result = ObjectModel.resolveProperty(target, property); } return result; } - @SuppressWarnings("unchecked") + /** + * @deprecated see {@link ObjectModel#toCollection(Object)} + */ + @Deprecated protected Collection<Object> obtainCollection(Object obj) { - if (obj == null) { - return Collections.emptyList(); - } - if (obj instanceof Object[]) { - return Arrays.asList((Object[]) obj); - } - if (obj instanceof Collection) { - return (Collection<Object>) obj; - } - if (obj instanceof Map) { - return ((Map) obj).keySet(); - } - if (obj instanceof Record) { - return ((Record) obj).getPropertyNames(); - } - if (obj instanceof Enumeration) { - return Collections.list((Enumeration<Object>) obj); - } - if (obj instanceof Iterator) { - return fromIterator((Iterator<Object>) obj); - } - if (obj instanceof Iterable) { - Iterable iterable = (Iterable) obj; - return fromIterator(iterable.iterator()); - } - if (obj instanceof String || obj instanceof Number) { - Collection list = new ArrayList(); - list.add(obj); - return list; - } - return Collections.emptyList(); + return ObjectModel.toCollection(obj); + } + + /** + * @deprecated see {@link ObjectModel#toString(Object)} + */ + @Deprecated + protected String objectToString(Object obj) { + return ObjectModel.toString(obj); } + /** + * @deprecated see {@link ObjectModel#collectionToString(Collection)} + */ + @Deprecated protected String collectionToString(Collection<?> col) { - StringBuilder builder = new StringBuilder(); - String prefix = ""; - for (Object o : col) { - builder.append(prefix).append(objectToString(o)); - prefix = ","; - } - return builder.toString(); + return ObjectModel.collectionToString(col); } + /** + * @deprecated see {@link ObjectModel#fromIterator(Iterator)} + */ + @Deprecated protected Collection<Object> fromIterator(Iterator<Object> iterator) { - ArrayList<Object> result = new ArrayList<>(); - while (iterator.hasNext()) { - result.add(iterator.next()); - } - return result; + return ObjectModel.fromIterator(iterator); } + /** + * @deprecated see {@link ObjectModel#toBoolean(Object)} + */ + @Deprecated protected boolean toBooleanInternal(Object obj) { - if (obj == null) { - return false; - } - - if (obj instanceof Number) { - Number number = (Number) obj; - return !(number.doubleValue() == 0.0); - } - - String s = obj.toString().trim(); - if ("".equals(s)) { - return false; - } else if ("true".equalsIgnoreCase(s) || "false".equalsIgnoreCase(s)) { - return Boolean.parseBoolean(s); - } - - if (obj instanceof Collection) { - return ((Collection) obj).size() > 0; - } - - if (obj instanceof Map) { - return ((Map) obj).size() > 0; - } - - if (obj instanceof Iterable<?>) { - return ((Iterable<?>) obj).iterator().hasNext(); - } - - if (obj instanceof Iterator<?>) { - return ((Iterator<?>) obj).hasNext(); - } - - return !(obj instanceof Object[]) || ((Object[]) obj).length > 0; + return ObjectModel.toBoolean(obj); } + /** + * @deprecated see {@link ObjectModel#getIndex(Object, int)} + */ + @Deprecated protected Object getIndex(Object obj, int index) { - if (obj instanceof Map) { - Map map = (Map) obj; - if (map.containsKey(index)) { - return map.get(index); - } - } - Collection collection = toCollection(obj); - if (collection instanceof List) { - return getIndexSafe((List) collection, index); - } - return null; + return ObjectModel.getIndex(obj, index); } + /** + * @deprecated see {@link ObjectModel#getIndex(Object, int)} + */ + @Deprecated protected Object getIndexSafe(List list, int index) { - if (index < 0 || index >= list.size()) { - return null; - } - return list.get(index); + return ObjectModel.getIndex(list, index); } + /** + * @deprecated use {@link Map#get(Object)} + */ + @Deprecated protected Object getMapProperty(Map map, String property) { return map.get(property); } + /** + * @deprecated see {@link ObjectModel#resolveProperty(Object, Object)} + */ + @Deprecated protected Object getObjectProperty(Object obj, String property) { - Object result = getObjectNoArgMethod(obj, property); - if (result == null) { - result = getField(obj, property); - } - return result; + return ObjectModel.resolveProperty(obj, property); } + /** + * @deprecated see {@link ObjectModel#getField(Object, String)} + */ + @Deprecated protected static Object getField(Object obj, String property) { - Class<?> cls = obj.getClass(); - if (cls.isArray() && "length".equals(property)) { - return Array.getLength(obj); - } - try { - Field field = cls.getDeclaredField(property); - return field.get(obj); - } catch (Exception e) { - return null; - } + return ObjectModel.getField(obj, property); } + /** + * @deprecated see {@link ObjectModel#invokeBeanMethod(Object, String)} + */ + @Deprecated protected Object getObjectNoArgMethod(Object obj, String property) { - Class<?> cls = obj.getClass(); - Method method = findMethod(cls, property); - if (method != null) { - method = extractMethodInheritanceChain(cls, method); - try { - return method.invoke(obj); - } catch (Exception e) { - LOGGER.error("Cannot access method " + property + " on object " + obj.toString(), e); - } - } - return null; + return ObjectModel.invokeBeanMethod(obj, property); } + /** + * @deprecated see {@link ObjectModel#findBeanMethod(Class, String)} + */ + @Deprecated protected static Method findMethod(Class<?> cls, String baseName) { - Method[] publicMethods = cls.getMethods(); - String capitalized = StringUtils.capitalize(baseName); - for (Method m : publicMethods) { - if (m.getParameterTypes().length == 0) { - String methodName = m.getName(); - if (baseName.equals(methodName) || ("get" + capitalized).equals(methodName) || ("is" + capitalized).equals(methodName)) { - // this method is good, check whether allowed - if (isMethodAllowed(m)) { - return m; - } - // method would match but is not allowed, abort - break; - } - } - } - return null; + return ObjectModel.findBeanMethod(cls, baseName); } + /** + * @deprecated see {@link ObjectModel#isMethodAllowed(Method)} + */ + @Deprecated protected static boolean isMethodAllowed(Method method) { - Class<?> declaringClass = method.getDeclaringClass(); - //methods of the Object.class are forbidden (except toString, which is allowed) - return declaringClass != Object.class || TO_STRING_METHOD.equals(method.getName()); + return ObjectModel.isMethodAllowed(method); } + /** + * @deprecated see {@link ObjectModel#findBeanMethod(Class, String)} (Class, Method)} + */ + @Deprecated protected Method extractMethodInheritanceChain(Class type, Method m) { - if (m == null || Modifier.isPublic(type.getModifiers())) { - return m; - } - Class[] iFaces = type.getInterfaces(); - Method mp; - for (Class<?> iFace : iFaces) { - mp = getClassMethod(iFace, m); - if (mp != null) { - return mp; - } - } - return getClassMethod(type.getSuperclass(), m); + return ObjectModel.findBeanMethod(type, m.getName()); } + /** + * @deprecated see {@link ObjectModel#findBeanMethod(Class, String)} (Class, Method)} + */ + @Deprecated protected Method getClassMethod(Class<?> clazz, Method m) { - Method mp; - try { - mp = clazz.getMethod(m.getName(), m.getParameterTypes()); - mp = extractMethodInheritanceChain(mp.getDeclaringClass(), mp); - if (mp != null) { - return mp; - } - } catch (NoSuchMethodException e) { - // do nothing - } - return null; + return ObjectModel.findBeanMethod(clazz, m.getName()); } }