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   *
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,
58       *                   must
59       *                   not be <code>null</code>.
60       * @return The value of the expression or <code>null</code> if the expression could not be evaluated.
61       */
62      public static Object evaluate( String expression, Map contexts )
63      {
64          Object value = null;
65  
66          if ( expression != null && expression.length() > 0 )
67          {
68              List segments = Arrays.asList( expression.split( "/", 0 ) );
69              if ( !segments.isEmpty() )
70              {
71                  Object context = contexts.get( segments.get( 0 ) );
72                  if ( context != null )
73                  {
74                      value = evaluate( context, segments.subList( 1, segments.size() ) );
75                  }
76              }
77          }
78  
79          return value;
80      }
81  
82      /**
83       * Evaluates the given expression segments against the specified object.
84       *
85       * @param context  The object to evaluate the segments against, may be <code>null</code>.
86       * @param segments The expression segments to evaluate, must not be <code>null</code>.
87       * @return The value of the evaluation or <code>null</code> if the segments could not be evaluated.
88       */
89      private static Object evaluate( Object context, List segments )
90      {
91          Object value = null;
92  
93          if ( segments.isEmpty() )
94          {
95              value = context;
96          }
97          else if ( context != null )
98          {
99              Object target = null;
100             String segment = (String) segments.get( 0 );
101             if ( segment.length() <= 0 )
102             {
103                 value = context;
104             }
105             else if ( context.getClass().isArray() && Character.isDigit( segment.charAt( 0 ) ) )
106             {
107                 try
108                 {
109                     int index = Integer.parseInt( segment );
110                     target = Array.get( context, index );
111                 }
112                 catch ( RuntimeException e )
113                 {
114                     // invalid index, just ignore
115                 }
116             }
117             else if ( ( context instanceof List ) && Character.isDigit( segment.charAt( 0 ) ) )
118             {
119                 try
120                 {
121                     int index = Integer.parseInt( segment );
122                     target = ( (List) context ).get( index );
123                 }
124                 catch ( RuntimeException e )
125                 {
126                     // invalid index, just ignore
127                 }
128             }
129             else
130             {
131                 target = getProperty( context, segment );
132             }
133             value = evaluate( target, segments.subList( 1, segments.size() ) );
134         }
135 
136         return value;
137     }
138 
139     /**
140      * Gets the value of a (public) bean property from the specified object.
141      *
142      * @param context  The object whose bean property should be retrieved, must not be <code>null</code>.
143      * @param property The name of the bean property, must not be <code>null</code>.
144      * @return The value of the bean property or <code>null</code> if the property does not exist.
145      */
146     static Object getProperty( Object context, String property )
147     {
148         Object value;
149 
150         Class type = context.getClass();
151         if ( context instanceof Collection )
152         {
153             type = Collection.class;
154         }
155         else if ( context instanceof Map )
156         {
157             type = Map.class;
158         }
159 
160         try
161         {
162             try
163             {
164                 Method method = type.getMethod( property, NO_PARAMS );
165                 value = method.invoke( context, NO_ARGS );
166             }
167             catch ( NoSuchMethodException e )
168             {
169                 try
170                 {
171                     String name = "get" + Character.toUpperCase( property.charAt( 0 ) ) + property.substring( 1 );
172                     Method method = type.getMethod( name, NO_PARAMS );
173                     value = method.invoke( context, NO_ARGS );
174                 }
175                 catch ( NoSuchMethodException e1 )
176                 {
177                     try
178                     {
179                         String name = "is" + Character.toUpperCase( property.charAt( 0 ) ) + property.substring( 1 );
180                         Method method = type.getMethod( name, NO_PARAMS );
181                         value = method.invoke( context, NO_ARGS );
182                     }
183                     catch ( NoSuchMethodException e2 )
184                     {
185                         try
186                         {
187                             Method method;
188                             try
189                             {
190                                 method = type.getMethod( "get", STRING_PARAM );
191                             }
192                             catch ( NoSuchMethodException e3 )
193                             {
194                                 method = type.getMethod( "get", OBJECT_PARAM );
195                             }
196                             value = method.invoke( context, new Object[]{ property } );
197                         }
198                         catch ( NoSuchMethodException e3 )
199                         {
200                             try
201                             {
202                                 Field field = type.getField( property );
203                                 value = field.get( context );
204                             }
205                             catch ( NoSuchFieldException e4 )
206                             {
207                                 if ( "length".equals( property ) && type.isArray() )
208                                 {
209                                     value = Array.getLength( context );
210                                 }
211                                 else
212                                 {
213                                     throw e4;
214                                 }
215                             }
216                         }
217                     }
218                 }
219             }
220         }
221         catch ( Exception e )
222         {
223             value = null;
224         }
225         return value;
226     }
227 
228 }