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 * @version $Id$
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 ( int i = 0; i < children.length; i++ )
136 {
137 Object child = children[i];
138
139 String name = (String) getName.invoke( child, NO_ARGS );
140
141 Integer index = (Integer) indices.get( name );
142 if ( index == null )
143 {
144 index = new Integer( 0 );
145 }
146
147 store( props, key + ".children." + name + "." + index, child, visited );
148
149 indices.put( name, new Integer( index.intValue() + 1 ) );
150 }
151 }
152 catch ( Exception e )
153 {
154 // can't happen
155 }
156 }
157 else
158 {
159 Class type = obj.getClass();
160 Method[] methods = type.getMethods();
161 for ( int i = 0; i < methods.length; i++ )
162 {
163 Method method = methods[i];
164 if ( Modifier.isStatic( method.getModifiers() ) || method.getParameterTypes().length > 0
165 || !method.getName().matches( "(get|is)\\p{Lu}.*" ) || method.getName().endsWith( "AsMap" )
166 || Class.class.isAssignableFrom( method.getReturnType() )
167 || Object.class.equals( method.getReturnType() ) )
168 {
169 continue;
170 }
171
172 try
173 {
174 Object value = method.invoke( obj, NO_ARGS );
175 store( props, key + "." + getPropertyName( method.getName() ), value, visited );
176 }
177 catch ( Exception e )
178 {
179 // just ignore
180 }
181 }
182 }
183 visited.remove( obj );
184 }
185 }
186
187 /**
188 * Derives the bean property name from the specified method for its getter.
189 *
190 * @param methodName The method name of the property's getter, must not be <code>null</code>.
191 * @return The property name, never <code>null</code>.
192 */
193 static String getPropertyName( String methodName )
194 {
195 String propertyName = methodName;
196 if ( methodName.startsWith( "get" ) && methodName.length() > 3 )
197 {
198 propertyName = Character.toLowerCase( methodName.charAt( 3 ) ) + methodName.substring( 4 );
199 }
200 else if ( methodName.startsWith( "is" ) && methodName.length() > 2 )
201 {
202 propertyName = Character.toLowerCase( methodName.charAt( 2 ) ) + methodName.substring( 3 );
203 }
204 return propertyName;
205 }
206
207 /**
208 * Writes the specified properties to the given file.
209 *
210 * @param props The properties to write, must not be <code>null</code>.
211 * @param file The output file for the properties, must not be <code>null</code>.
212 * @throws IOException If the properties could not be written to the file.
213 */
214 public static void write( Properties props, File file )
215 throws IOException
216 {
217 OutputStream out = null;
218 try
219 {
220 file.getParentFile().mkdirs();
221 out = new FileOutputStream( file );
222 props.store( out, "MAVEN-CORE-IT-LOG" );
223 }
224 finally
225 {
226 if ( out != null )
227 {
228 try
229 {
230 out.close();
231 }
232 catch ( IOException e )
233 {
234 // just ignore
235 }
236 }
237 }
238 }
239
240 }