All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.plexus.interpolation.reflection.ReflectionValueExtractor Maven / Gradle / Ivy

There is a newer version: 3.0.0-alpha-3
Show newest version
package org.codehaus.plexus.interpolation.reflection;

/*
 * Copyright 2001-2006 Codehaus Foundation.
 *
 * Licensed 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 java.lang.ref.WeakReference;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;

import org.codehaus.plexus.interpolation.util.StringUtils;

/**
 * NOTE: This class was copied from plexus-utils, to allow this library to stand completely self-contained.
 * 

Using simple dotted expressions extract the values from a MavenProject instance, For example we might want to extract * a value like: project.build.sourceDirectory

* * @author Jason van Zyl */ public class ReflectionValueExtractor { private static final Class[] CLASS_ARGS = new Class[0]; private static final Object[] OBJECT_ARGS = new Object[0]; /** * Use a WeakHashMap here, so the keys (Class objects) can be garbage collected. This approach prevents permgen * space overflows due to retention of discarded classloaders. */ private static final Map, WeakReference> classMaps = new WeakHashMap, WeakReference>(); static final int EOF = -1; static final char PROPERTY_START = '.'; static final char INDEXED_START = '['; static final char INDEXED_END = ']'; static final char MAPPED_START = '('; static final char MAPPED_END = ')'; static class Tokenizer { final String expression; int idx; public Tokenizer(String expression) { this.expression = expression; } public int peekChar() { return idx < expression.length() ? expression.charAt(idx) : EOF; } public int skipChar() { return idx < expression.length() ? expression.charAt(idx++) : EOF; } public String nextToken(char delimiter) { int start = idx; while (idx < expression.length() && delimiter != expression.charAt(idx)) { idx++; } // delimiter MUST be present if (idx <= start || idx >= expression.length()) { return null; } return expression.substring(start, idx++); } public String nextPropertyName() { final int start = idx; while (idx < expression.length() && Character.isJavaIdentifierPart(expression.charAt(idx))) { idx++; } // property name does not require delimiter if (idx <= start || idx > expression.length()) { return null; } return expression.substring(start, idx); } public int getPosition() { return idx < expression.length() ? idx : EOF; } // to make tokenizer look pretty in debugger @Override public String toString() { return idx < expression.length() ? expression.substring(idx) : ""; } } private ReflectionValueExtractor() {} /** *

* The implementation supports indexed, nested and mapped properties. *

*
    *
  • nested properties should be defined by a dot, i.e. "user.address.street"
  • *
  • indexed properties (java.util.List or array instance) should be contains (\\w+)\\[(\\d+)\\] * pattern, i.e. "user.addresses[1].street"
  • *
  • mapped properties should be contains (\\w+)\\((.+)\\) pattern, i.e. * "user.addresses(myAddress).street"
  • *
* * @param expression not null expression * @param root not null object * @return the object defined by the expression * @throws Exception if any */ public static Object evaluate(String expression, Object root) throws Exception { return evaluate(expression, root, true); } /** *

* The implementation supports indexed, nested and mapped properties. *

*
    *
  • nested properties should be defined by a dot, i.e. "user.address.street"
  • *
  • indexed properties (java.util.List or array instance) should be contains (\\w+)\\[(\\d+)\\] * pattern, i.e. "user.addresses[1].street"
  • *
  • mapped properties should be contains (\\w+)\\((.+)\\) pattern, i.e. * "user.addresses(myAddress).street"
  • *
* * @param expression not null expression * @param root not null object * @param trimRootToken trim the token or not. * @return the object defined by the expression * @throws Exception if any */ // TODO: don't throw Exception public static Object evaluate(String expression, final Object root, final boolean trimRootToken) throws Exception { Object value = root; // ---------------------------------------------------------------------- // Walk the dots and retrieve the ultimate value desired from the // MavenProject instance. // ---------------------------------------------------------------------- if (expression == null || "".equals(expression.trim()) || !Character.isJavaIdentifierStart(expression.charAt(0))) { return null; } boolean hasDots = expression.indexOf(PROPERTY_START) >= 0; final Tokenizer tokenizer; if (trimRootToken && hasDots) { tokenizer = new Tokenizer(expression); tokenizer.nextPropertyName(); if (tokenizer.getPosition() == EOF) { return null; } } else { tokenizer = new Tokenizer("." + expression); } int propertyPosition = tokenizer.getPosition(); while (value != null && tokenizer.peekChar() != EOF) { switch (tokenizer.skipChar()) { case INDEXED_START: value = getIndexedValue( expression, propertyPosition, tokenizer.getPosition(), value, tokenizer.nextToken(INDEXED_END)); break; case MAPPED_START: value = getMappedValue( expression, propertyPosition, tokenizer.getPosition(), value, tokenizer.nextToken(MAPPED_END)); break; case PROPERTY_START: propertyPosition = tokenizer.getPosition(); value = getPropertyValue(value, tokenizer.nextPropertyName()); break; default: // could not parse expression return null; } } return value; } private static Object getMappedValue( final String expression, final int from, final int to, final Object value, final String key) throws Exception { if (value == null || key == null) { return null; } if (value instanceof Map) { Object[] localParams = new Object[] {key}; ClassMap classMap = getClassMap(value.getClass()); Method method = classMap.findMethod("get", localParams); return method.invoke(value, localParams); } final String message = String.format( "The token '%s' at position '%d' refers to a java.util.Map, but the value seems is an instance of '%s'", expression.subSequence(from, to), from, value.getClass()); throw new Exception(message); } private static Object getIndexedValue( final String expression, final int from, final int to, final Object value, final String indexStr) throws Exception { try { int index = Integer.parseInt(indexStr); if (value.getClass().isArray()) { return Array.get(value, index); } if (value instanceof List) { ClassMap classMap = getClassMap(value.getClass()); // use get method on List interface Object[] localParams = new Object[] {index}; Method method = classMap.findMethod("get", localParams); return method.invoke(value, localParams); } } catch (NumberFormatException e) { return null; } catch (InvocationTargetException e) { // catch array index issues gracefully, otherwise release if (e.getCause() instanceof IndexOutOfBoundsException) { return null; } throw e; } final String message = String.format( "The token '%s' at position '%d' refers to a java.util.List or an array, but the value seems is an instance of '%s'", expression.subSequence(from, to), from, value.getClass()); throw new Exception(message); } private static Object getPropertyValue(Object value, String property) throws Exception { if (value == null || property == null) { return null; } ClassMap classMap = getClassMap(value.getClass()); String methodBase = StringUtils.capitalizeFirstLetter(property); String methodName = "get" + methodBase; Method method = classMap.findMethod(methodName, CLASS_ARGS); if (method == null) { // perhaps this is a boolean property?? methodName = "is" + methodBase; method = classMap.findMethod(methodName, CLASS_ARGS); } if (method == null) { return null; } try { return method.invoke(value, OBJECT_ARGS); } catch (InvocationTargetException e) { throw e; } } private static ClassMap getClassMap(Class clazz) { WeakReference softRef = classMaps.get(clazz); ClassMap classMap; if (softRef == null || (classMap = softRef.get()) == null) { classMap = new ClassMap(clazz); classMaps.put(clazz, new WeakReference(classMap)); } return classMap; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy