View Javadoc
1   
2   package com.simpligility.maven.plugins.android.config;
3   
4   import org.apache.maven.execution.MavenSession;
5   import org.apache.maven.plugin.MojoExecution;
6   import org.apache.maven.plugin.PluginParameterExpressionEvaluator;
7   
8   import java.lang.annotation.Annotation;
9   import java.lang.reflect.Field;
10  import java.lang.reflect.Method;
11  import java.util.ArrayList;
12  import java.util.Collection;
13  
14  /**
15   * ConfigHandler is able to parse the configuration of a Mojo based on the Maven injected parameters as well as a config
16   * pojo and annontations for default values on properties named parsed*. See the ProguardMojo for a working
17   * implementation.
18   * 
19   * @author Adrian Stabiszewski https://github.com/grundid/
20   * @author Manfred Moser - manfred@simpligility.com
21   * @see ConfigPojo
22   * @see PullParameter
23   */
24  public class ConfigHandler
25  {
26  
27      private Object mojo;
28      private Object configPojoInstance;
29      private String configPojoName;
30      private String configPojoPrefix;
31      private PluginParameterExpressionEvaluator evaluator;
32  
33      public ConfigHandler( Object mojo, MavenSession session, MojoExecution execution )
34      {
35          this.mojo = mojo;
36  
37          if ( session == null )
38          {
39              throw new IllegalArgumentException( "The argument session is required" );
40          }
41          if ( execution == null )
42          {
43              throw new IllegalArgumentException( "The argument execution is required" );
44          }
45  
46          this.evaluator = new PluginParameterExpressionEvaluator( session, execution );
47  
48          initConfigPojo();
49      }
50  
51      private Collection< Field > findPropertiesByAnnotation( Class< ? extends Annotation > annotation )
52      {
53          Collection< Field > result = new ArrayList< Field >();
54          for ( Class< ? extends Object > cls = mojo.getClass(); cls != Object.class; cls = cls.getSuperclass() )
55          {
56              for ( Field field : cls.getDeclaredFields() )
57              {
58                  if ( field.isAnnotationPresent( annotation ) )
59                  {
60                      field.setAccessible( true );
61                      result.add( field );
62                  }
63              }
64          }
65  
66          return result;
67      }
68  
69      public void parseConfiguration()
70      {
71          Collection< Field > parsedFields = findPropertiesByAnnotation( PullParameter.class );
72  
73          for ( Field field : parsedFields )
74          {
75              Object value = null;
76              String fieldBaseName = getFieldNameWithoutParsedPrefix( field );
77              // first take the setting from the config pojo (e.g. nested config in plugin configuration)
78              if ( configPojoInstance != null )
79              {
80                  value = getValueFromPojo( fieldBaseName );
81              }
82              // then override with value from properties supplied in pom, settings or command line
83              // unless it is null or an empty array
84              Object propertyValue = getValueFromMojo( fieldBaseName );
85              if ( propertyValue == null || propertyValue instanceof Object[]//
86                  && ( (Object[]) propertyValue ).length == 0 )
87              {
88                  // no useful value
89              }
90              else
91              {
92                  value = propertyValue;
93              }
94              // and only if we still have no value, get the default as declared in the annotation
95              if ( value == null )
96              {
97                  value = getValueFromAnnotation( field );
98              }
99  
100             try
101             {
102                 field.set( mojo, value );
103             }
104             catch ( Exception e )
105             {
106                 e.printStackTrace();
107             }
108         }
109     }
110 
111     private Object getValueFromAnnotation( Field field )
112     {
113         PullParameter annotation = field.getAnnotation( PullParameter.class );
114         String[] defaultValue = annotation.defaultValue();
115         boolean required = annotation.required();
116         String currentParameterName = "android." + configPojoName + "." + getFieldNameWithoutParsedPrefix( field );
117 
118         if ( defaultValue.length > 0 )
119         {
120             if ( defaultValue.length > 1 )
121             {
122                 throw new RuntimeException( String.format( "Too many default values for field %s", field.getName() ) );
123             }
124 
125             final Class< ? > fieldType = field.getType();
126 
127             try
128             {
129                 final Object defValue = evaluator.evaluate( defaultValue[0], fieldType );
130 
131                 if ( defValue == null || fieldType.isInstance( defValue ) )
132                 {
133                     return defValue;
134                 }
135 
136                 return convertTo( fieldType, defValue );
137             }
138             catch ( RuntimeException e )
139             {
140                 throw e;
141             }
142             catch ( Exception e )
143             {
144                 throw new RuntimeException( String.format(
145                     "Problem encountered converting default value for %s parameter to %s",
146                     currentParameterName, fieldType ), e );
147             }
148         }
149         else
150         {
151             if ( !required )
152             {
153                 // if no default value method, simply return null
154                 if ( annotation.defaultValueGetterMethod().isEmpty() )
155                 {
156                     return null;
157                 }
158 
159                 try
160                 {
161                     Method method = mojo.getClass().getDeclaredMethod( annotation.defaultValueGetterMethod() );
162                     // even access it if the method is private
163                     method.setAccessible( true );
164                     return method.invoke( mojo );
165                 }
166                 catch ( Exception e )
167                 {
168                     throw new RuntimeException( String.format(
169                         "Problem encountered accessing default value for %s parameter",
170                         currentParameterName ), e );
171                 }
172             }
173             else
174             {
175                 throw new RuntimeException( String.format(
176                     "Required parameter %1$ has no value. Please "
177                         + "supply with -D%1$=value on the command line or as property or "
178                         + "plugin configuration in your pom or settings file.",
179                     currentParameterName ) );
180             }
181         }
182     }
183 
184     private Object convertTo( Class< ? > javaType, Object defValue )
185     throws Exception
186     {
187         // try valueOf
188         try
189         {
190             return javaType.getMethod( "valueOf", String.class ).invoke( null, defValue );
191         }
192         catch ( NoSuchMethodException e )
193         {
194             return javaType.getConstructor( String.class ).newInstance( defValue );
195         }
196     }
197 
198     private Object getValueFromMojo( String fieldBaseName )
199     {
200         return getValueFromObject( mojo, configPojoName + toFirstLetterUppercase( fieldBaseName ) );
201     }
202 
203     private Object getValueFromPojo( String fieldBaseName )
204     {
205         return getValueFromObject( configPojoInstance, fieldBaseName );
206     }
207 
208     private Object getValueFromObject( Object object, String fieldBaseName )
209     {
210         Object value = null;
211         try
212         {
213             Field pojoField = findFieldByName( object, fieldBaseName );
214             if ( pojoField != null )
215             {
216                 value = pojoField.get( object );
217             }
218         }
219         catch ( Exception e )
220         {
221             // swallow
222         }
223         return value;
224     }
225 
226     private Field findFieldByName( Object object, String name )
227     {
228         for ( Field field : object.getClass().getDeclaredFields() )
229         {
230             if ( field.getName().equals( name ) )
231             {
232                 field.setAccessible( true );
233                 return field;
234             }
235         }
236         return null;
237     }
238 
239     private String getFieldNameWithoutPrefix( Field field, String prefix )
240     {
241         if ( field.getName().startsWith( prefix ) )
242         {
243             String fieldName = field.getName().substring( prefix.length() );
244             return fieldName.substring( 0, 1 ).toLowerCase() + fieldName.substring( 1 );
245         }
246         else
247         {
248             return field.getName();
249         }
250     }
251 
252     private String toFirstLetterUppercase( String s )
253     {
254         return s.substring( 0, 1 ).toUpperCase() + s.substring( 1 );
255     }
256 
257     private String getFieldNameWithoutParsedPrefix( Field field )
258     {
259         return getFieldNameWithoutPrefix( field, configPojoPrefix );
260     }
261 
262     private void initConfigPojo()
263     {
264         try
265         {
266             Field configPojo = findPropertiesByAnnotation( ConfigPojo.class ).iterator().next();
267             configPojoName = configPojo.getName();
268             configPojoInstance = configPojo.get( mojo );
269             configPojoPrefix = configPojo.getAnnotation( ConfigPojo.class ).prefix();
270         }
271         catch ( Exception e )
272         {
273             // ignore, we can live without a config pojo
274         }
275     }
276 }