View Javadoc

1   package org.apache.maven.plugin.coreit;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.lang.reflect.Array;
23  import java.lang.reflect.Field;
24  import java.lang.reflect.Method;
25  import java.util.Arrays;
26  import java.util.Collection;
27  import java.util.List;
28  import java.util.Map;
29  
30  /**
31   * Assists in evaluating expressions.
32   * 
33   * @author Benjamin Bentmann
34   * @version $Id: ExpressionUtil.java 705750 2008-10-17 21:07:54Z bentmann $
35   */
36  class ExpressionUtil
37  {
38  
39      private static final Object[] NO_ARGS = {};
40  
41      private static final Class[] NO_PARAMS = {};
42  
43      private static final Class[] OBJECT_PARAM = { Object.class };
44  
45      private static final Class[] STRING_PARAM = { String.class };
46  
47      /**
48       * Evaluates the specified expression. Expressions are composed of segments which are separated by a forward slash
49       * ('/'). Each segment specifies a (public) bean property of the current object and drives the evaluation further
50       * down the object graph. For lists, arrays and maps segments can additionally specify the index/key of an element.
51       * The initial segment denotes the root object and the parameter <code>contexts</code> is used to specify which
52       * root objects are available. For instance, if <code>contexts</code> maps the token "project" to a Maven project
53       * instance, the expression "project/build/resources/0/directory" specifies the first resource directory of the
54       * project.
55       * 
56       * @param expression The expression to evaluate, may be <code>null</code>.
57       * @param contexts The possible root objects for the expression evaluation, indexed by their identifying token, must
58       *            not be <code>null</code>.
59       * @return The value of the expression or <code>null</code> if the expression could not be evaluated.
60       */
61      public static Object evaluate( String expression, Map contexts )
62      {
63          Object value = null;
64  
65          if ( expression != null && expression.length() > 0 )
66          {
67              List segments = Arrays.asList( expression.split( "/", 0 ) );
68              if ( !segments.isEmpty() )
69              {
70                  Object context = contexts.get( segments.get( 0 ) );
71                  if ( context != null )
72                  {
73                      value = evaluate( context, segments.subList( 1, segments.size() ) );
74                  }
75              }
76          }
77  
78          return value;
79      }
80  
81      /**
82       * Evaluates the given expression segments against the specified object.
83       * 
84       * @param context The object to evaluate the segments against, may be <code>null</code>.
85       * @param segments The expression segments to evaluate, must not be <code>null</code>.
86       * @return The value of the evaluation or <code>null</code> if the segments could not be evaluated.
87       */
88      private static Object evaluate( Object context, List segments )
89      {
90          Object value = null;
91  
92          if ( segments.isEmpty() )
93          {
94              value = context;
95          }
96          else if ( context != null )
97          {
98              Object target = null;
99              String segment = (String) segments.get( 0 );
100             if ( segment.length() <= 0 )
101             {
102                 value = context;
103             }
104             else if ( context.getClass().isArray() && Character.isDigit( segment.charAt( 0 ) ) )
105             {
106                 try
107                 {
108                     int index = Integer.parseInt( segment );
109                     target = Array.get( context, index );
110                 }
111                 catch ( RuntimeException e )
112                 {
113                     // invalid index, just ignore
114                 }
115             }
116             else if ( ( context instanceof List ) && Character.isDigit( segment.charAt( 0 ) ) )
117             {
118                 try
119                 {
120                     int index = Integer.parseInt( segment );
121                     target = ( (List) context ).get( index );
122                 }
123                 catch ( RuntimeException e )
124                 {
125                     // invalid index, just ignore
126                 }
127             }
128             else
129             {
130                 target = getProperty( context, segment );
131             }
132             value = evaluate( target, segments.subList( 1, segments.size() ) );
133         }
134 
135         return value;
136     }
137 
138     /**
139      * Gets the value of a (public) bean property from the specified object.
140      * 
141      * @param context The object whose bean property should be retrieved, must not be <code>null</code>.
142      * @param property The name of the bean property, must not be <code>null</code>.
143      * @return The value of the bean property or <code>null</code> if the property does not exist.
144      */
145     static Object getProperty( Object context, String property )
146     {
147         Object value;
148 
149         Class type = context.getClass();
150         if ( context instanceof Collection )
151         {
152             type = Collection.class;
153         }
154         else if ( context instanceof Map )
155         {
156             type = Map.class;
157         }
158 
159         try
160         {
161             try
162             {
163                 Method method = type.getMethod( property, NO_PARAMS );
164                 value = method.invoke( context, NO_ARGS );
165             }
166             catch ( NoSuchMethodException e )
167             {
168                 try
169                 {
170                     String name = "get" + Character.toUpperCase( property.charAt( 0 ) ) + property.substring( 1 );
171                     Method method = type.getMethod( name, NO_PARAMS );
172                     value = method.invoke( context, NO_ARGS );
173                 }
174                 catch ( NoSuchMethodException e1 )
175                 {
176                     try
177                     {
178                         String name = "is" + Character.toUpperCase( property.charAt( 0 ) ) + property.substring( 1 );
179                         Method method = type.getMethod( name, NO_PARAMS );
180                         value = method.invoke( context, NO_ARGS );
181                     }
182                     catch ( NoSuchMethodException e2 )
183                     {
184                         try
185                         {
186                             Method method;
187                             try
188                             {
189                                 method = type.getMethod( "get", STRING_PARAM );
190                             }
191                             catch ( NoSuchMethodException e3 )
192                             {
193                                 method = type.getMethod( "get", OBJECT_PARAM );
194                             }
195                             value = method.invoke( context, new Object[] { property } );
196                         }
197                         catch ( NoSuchMethodException e3 )
198                         {
199                             try
200                             {
201                                 Field field = type.getField( property );
202                                 value = field.get( context );
203                             }
204                             catch ( NoSuchFieldException e4 )
205                             {
206                                 if ( "length".equals( property ) && type.isArray() )
207                                 {
208                                     value = new Integer( Array.getLength( context ) );
209                                 }
210                                 else
211                                 {
212                                     throw e4;
213                                 }
214                             }
215                         }
216                     }
217                 }
218             }
219         }
220         catch ( Exception e )
221         {
222             value = null;
223         }
224         return value;
225     }
226 
227 }