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 }