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.io.File;
23  import java.io.FileOutputStream;
24  import java.io.IOException;
25  import java.io.OutputStream;
26  import java.lang.reflect.Array;
27  import java.lang.reflect.Method;
28  import java.lang.reflect.Modifier;
29  import java.util.Collection;
30  import java.util.HashMap;
31  import java.util.HashSet;
32  import java.util.Iterator;
33  import java.util.Map;
34  import java.util.Properties;
35  
36  /**
37   * Assists in serializing primitives and beans into properties for later inspection/verification.
38   *
39   * @author Benjamin Bentmann
40   *
41   */
42  class PropertyUtil
43  {
44  
45      private static final Object[] NO_ARGS = {};
46  
47      private static final Class[] NO_PARAMS = {};
48  
49      /**
50       * Serializes the specified object into the given properties, using the provided key. The object may be a scalar
51       * value like a string or some array/collection/map or a bean.
52       *
53       * @param props The properties to serialize into, must not be <code>null</code>.
54       * @param key   The key to use for serialization of the object data, must not be <code>null</code>.
55       * @param obj   The object to serialize, may be <code>null</code>.
56       */
57      public static void store( Properties props, String key, Object obj )
58      {
59          store( props, key, obj, new HashSet() );
60      }
61  
62      /**
63       * Serializes the specified object into the given properties, using the provided key. The object may be a scalar
64       * value like a string or some array/collection/map or a bean.
65       *
66       * @param props   The properties to serialize into, must not be <code>null</code>.
67       * @param key     The key to use for serialization of the object data, must not be <code>null</code>.
68       * @param obj     The object to serialize, may be <code>null</code>.
69       * @param visited The set/stack of already visited objects, used to detect back references in the object graph, must
70       *                not be <code>null</code>.
71       */
72      private static void store( Properties props, String key, Object obj, Collection visited )
73      {
74          if ( obj != null && !visited.contains( obj ) )
75          {
76              visited.add( obj );
77              if ( ( obj instanceof String ) || ( obj instanceof Number ) || ( obj instanceof Boolean )
78                  || ( obj instanceof File ) )
79              {
80                  props.put( key, obj.toString() );
81              }
82              else if ( obj instanceof Collection )
83              {
84                  Collection coll = (Collection) obj;
85                  props.put( key, Integer.toString( coll.size() ) );
86                  int index = 0;
87                  for ( Iterator it = coll.iterator(); it.hasNext(); index++ )
88                  {
89                      Object elem = it.next();
90                      store( props, key + "." + index, elem, visited );
91                  }
92              }
93              else if ( obj instanceof Map )
94              {
95                  Map map = (Map) obj;
96                  props.put( key, Integer.toString( map.size() ) );
97                  int index = 0;
98                  for ( Iterator it = map.entrySet().iterator(); it.hasNext(); index++ )
99                  {
100                     Map.Entry entry = (Map.Entry) it.next();
101                     store( props, key + "." + entry.getKey(), entry.getValue(), visited );
102                 }
103             }
104             else if ( obj.getClass().isArray() )
105             {
106                 int length = Array.getLength( obj );
107                 props.put( key, Integer.toString( length ) );
108                 for ( int index = 0; index < length; index++ )
109                 {
110                     Object elem = Array.get( obj, index );
111                     store( props, key + "." + index, elem, visited );
112                 }
113             }
114             else if ( obj.getClass().getName().endsWith( "Xpp3Dom" ) )
115             {
116                 Class type = obj.getClass();
117                 try
118                 {
119                     Method getValue = type.getMethod( "getValue", NO_PARAMS );
120                     String value = (String) getValue.invoke( obj, NO_ARGS );
121 
122                     if ( value != null )
123                     {
124                         props.put( key + ".value", value );
125                     }
126 
127                     Method getName = type.getMethod( "getName", NO_PARAMS );
128 
129                     Method getChildren = type.getMethod( "getChildren", NO_PARAMS );
130                     Object[] children = (Object[]) getChildren.invoke( obj, NO_ARGS );
131 
132                     props.put( key + ".children", Integer.toString( children.length ) );
133 
134                     Map indices = new HashMap();
135                     for ( Object child : children )
136                     {
137                         String name = (String) getName.invoke( child, NO_ARGS );
138 
139                         Integer index = (Integer) indices.get( name );
140                         if ( index == null )
141                         {
142                             index = 0;
143                         }
144 
145                         store( props, key + ".children." + name + "." + index, child, visited );
146 
147                         indices.put( name, index + 1 );
148                     }
149                 }
150                 catch ( Exception e )
151                 {
152                     // can't happen
153                 }
154             }
155             else
156             {
157                 Class type = obj.getClass();
158                 Method[] methods = type.getMethods();
159                 for ( Method method : methods )
160                 {
161                     if ( Modifier.isStatic( method.getModifiers() ) || method.getParameterTypes().length > 0
162                         || !method.getName().matches( "(get|is)\\p{Lu}.*" ) || method.getName().endsWith( "AsMap" )
163                         || Class.class.isAssignableFrom( method.getReturnType() ) || Object.class.equals(
164                         method.getReturnType() ) )
165                     {
166                         continue;
167                     }
168 
169                     try
170                     {
171                         Object value = method.invoke( obj, NO_ARGS );
172                         store( props, key + "." + getPropertyName( method.getName() ), value, visited );
173                     }
174                     catch ( Exception e )
175                     {
176                         // just ignore
177                     }
178                 }
179             }
180             visited.remove( obj );
181         }
182     }
183 
184     /**
185      * Derives the bean property name from the specified method for its getter.
186      *
187      * @param methodName The method name of the property's getter, must not be <code>null</code>.
188      * @return The property name, never <code>null</code>.
189      */
190     static String getPropertyName( String methodName )
191     {
192         String propertyName = methodName;
193         if ( methodName.startsWith( "get" ) && methodName.length() > 3 )
194         {
195             propertyName = Character.toLowerCase( methodName.charAt( 3 ) ) + methodName.substring( 4 );
196         }
197         else if ( methodName.startsWith( "is" ) && methodName.length() > 2 )
198         {
199             propertyName = Character.toLowerCase( methodName.charAt( 2 ) ) + methodName.substring( 3 );
200         }
201         return propertyName;
202     }
203 
204     /**
205      * Writes the specified properties to the given file.
206      *
207      * @param props The properties to write, must not be <code>null</code>.
208      * @param file  The output file for the properties, must not be <code>null</code>.
209      * @throws IOException If the properties could not be written to the file.
210      */
211     public static void write( Properties props, File file )
212         throws IOException
213     {
214         OutputStream out = null;
215         try
216         {
217             file.getParentFile().mkdirs();
218             out = new FileOutputStream( file );
219             props.store( out, "MAVEN-CORE-IT-LOG" );
220         }
221         finally
222         {
223             if ( out != null )
224             {
225                 try
226                 {
227                     out.close();
228                 }
229                 catch ( IOException e )
230                 {
231                     // just ignore
232                 }
233             }
234         }
235     }
236 
237 }