package org.apache.maven.plugin.coreit; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Array; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Properties; /** * Assists in serializing primitives and beans into properties for later inspection/verification. * * @author Benjamin Bentmann * @version $Id$ */ class PropertyUtil { private static final Object[] NO_ARGS = {}; private static final Class[] NO_PARAMS = {}; /** * Serializes the specified object into the given properties, using the provided key. The object may be a scalar * value like a string or some array/collection/map or a bean. * * @param props The properties to serialize into, must not be <code>null</code>. * @param key The key to use for serialization of the object data, must not be <code>null</code>. * @param obj The object to serialize, may be <code>null</code>. */ public static void store( Properties props, String key, Object obj ) { store( props, key, obj, new HashSet() ); } /** * Serializes the specified object into the given properties, using the provided key. The object may be a scalar * value like a string or some array/collection/map or a bean. * * @param props The properties to serialize into, must not be <code>null</code>. * @param key The key to use for serialization of the object data, must not be <code>null</code>. * @param obj The object to serialize, may be <code>null</code>. * @param visited The set/stack of already visited objects, used to detect back references in the object graph, must * not be <code>null</code>. */ private static void store( Properties props, String key, Object obj, Collection visited ) { if ( obj != null && !visited.contains( obj ) ) { visited.add( obj ); if ( ( obj instanceof String ) || ( obj instanceof Number ) || ( obj instanceof Boolean ) || ( obj instanceof File ) ) { props.put( key, obj.toString() ); } else if ( obj instanceof Collection ) { Collection coll = (Collection) obj; props.put( key, Integer.toString( coll.size() ) ); int index = 0; for ( Iterator it = coll.iterator(); it.hasNext(); index++ ) { Object elem = it.next(); store( props, key + "." + index, elem, visited ); } } else if ( obj instanceof Map ) { Map map = (Map) obj; props.put( key, Integer.toString( map.size() ) ); int index = 0; for ( Iterator it = map.entrySet().iterator(); it.hasNext(); index++ ) { Map.Entry entry = (Map.Entry) it.next(); store( props, key + "." + entry.getKey(), entry.getValue(), visited ); } } else if ( obj.getClass().isArray() ) { int length = Array.getLength( obj ); props.put( key, Integer.toString( length ) ); for ( int index = 0; index < length; index++ ) { Object elem = Array.get( obj, index ); store( props, key + "." + index, elem, visited ); } } else if ( obj.getClass().getName().endsWith( "Xpp3Dom" ) ) { Class type = obj.getClass(); try { Method getValue = type.getMethod( "getValue", NO_PARAMS ); String value = (String) getValue.invoke( obj, NO_ARGS ); if ( value != null ) { props.put( key + ".value", value ); } Method getName = type.getMethod( "getName", NO_PARAMS ); Method getChildren = type.getMethod( "getChildren", NO_PARAMS ); Object[] children = (Object[]) getChildren.invoke( obj, NO_ARGS ); props.put( key + ".children", Integer.toString( children.length ) ); Map indices = new HashMap(); for ( int i = 0; i < children.length; i++ ) { Object child = children[i]; String name = (String) getName.invoke( child, NO_ARGS ); Integer index = (Integer) indices.get( name ); if ( index == null ) { index = new Integer( 0 ); } store( props, key + ".children." + name + "." + index, child, visited ); indices.put( name, new Integer( index.intValue() + 1 ) ); } } catch ( Exception e ) { // can't happen } } else { Class type = obj.getClass(); Method[] methods = type.getMethods(); for ( int i = 0; i < methods.length; i++ ) { Method method = methods[i]; if ( Modifier.isStatic( method.getModifiers() ) || method.getParameterTypes().length > 0 || !method.getName().matches( "(get|is)\\p{Lu}.*" ) || method.getName().endsWith( "AsMap" ) || Class.class.isAssignableFrom( method.getReturnType() ) || Object.class.equals( method.getReturnType() ) ) { continue; } try { Object value = method.invoke( obj, NO_ARGS ); store( props, key + "." + getPropertyName( method.getName() ), value, visited ); } catch ( Exception e ) { // just ignore } } } visited.remove( obj ); } } /** * Derives the bean property name from the specified method for its getter. * * @param methodName The method name of the property's getter, must not be <code>null</code>. * @return The property name, never <code>null</code>. */ static String getPropertyName( String methodName ) { String propertyName = methodName; if ( methodName.startsWith( "get" ) && methodName.length() > 3 ) { propertyName = Character.toLowerCase( methodName.charAt( 3 ) ) + methodName.substring( 4 ); } else if ( methodName.startsWith( "is" ) && methodName.length() > 2 ) { propertyName = Character.toLowerCase( methodName.charAt( 2 ) ) + methodName.substring( 3 ); } return propertyName; } /** * Writes the specified properties to the given file. * * @param props The properties to write, must not be <code>null</code>. * @param file The output file for the properties, must not be <code>null</code>. * @throws IOException If the properties could not be written to the file. */ public static void write( Properties props, File file ) throws IOException { OutputStream out = null; try { file.getParentFile().mkdirs(); out = new FileOutputStream( file ); props.store( out, "MAVEN-CORE-IT-LOG" ); } finally { if ( out != null ) { try { out.close(); } catch ( IOException e ) { // just ignore } } } } }