| /* |
| * 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. |
| */ |
| package org.apache.maven.plugin.coreit; |
| |
| 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.nio.file.Path; |
| 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 |
| * |
| */ |
| 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) |
| || (obj instanceof Path)) { |
| 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 (Object child : children) { |
| String name = (String) getName.invoke(child, NO_ARGS); |
| |
| Integer index = (Integer) indices.get(name); |
| if (index == null) { |
| index = 0; |
| } |
| |
| store(props, key + ".children." + name + "." + index, child, visited); |
| |
| indices.put(name, index + 1); |
| } |
| } catch (Exception e) { |
| // can't happen |
| } |
| } else { |
| Class type = obj.getClass(); |
| Method[] methods = type.getMethods(); |
| for (Method method : methods) { |
| 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 |
| } |
| } |
| } |
| } |
| } |