/* * Copyright (C) 2005 Joe Walnes. * Copyright (C) 2006, 2007, 2008 XStream Committers. * All rights reserved. * * The software in this package is published under the terms of the BSD * style license a copy of which has been included with this distribution in * the LICENSE.txt file. * * Created on 12. April 2005 by Joe Walnes */ package com.thoughtworks.xstream.converters.javabean; import com.thoughtworks.xstream.converters.reflection.ObjectAccessException; //import java.beans.Introspector; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; /** * Builds the serializable properties maps for each bean and caches them. * @deprecated since 1.3.1, no longer in use */ public class PropertyDictionary { private final Map keyedByPropertyNameCache = Collections.synchronizedMap(new HashMap()); public Iterator serializablePropertiesFor(Class cls) { return buildMap(cls).values().iterator(); } /** * Locates a serializable property * * @param cls * @param name */ public BeanProperty property(Class cls, String name) { Map properties = buildMap(cls); BeanProperty property = (BeanProperty) properties.get(name); if (property == null) { throw new ObjectAccessException("No such property " + cls.getName() + "." + name); } else { return property; } } /** * Builds the map of all serializable properties for the the provided bean * * @param cls */ private Map buildMap(Class cls) { final String clsName = cls.getName(); if (!keyedByPropertyNameCache.containsKey(clsName)) { synchronized (keyedByPropertyNameCache) { if (!keyedByPropertyNameCache.containsKey(clsName)) { // double check // Gather all the properties, using only the keyed map. It // is possible that a class have two writable only // properties that have the same name // but different types final Map propertyMap = new HashMap(); Method[] methods = cls.getMethods(); for (int i = 0; i < methods.length; i++) { if (!Modifier.isPublic(methods[i].getModifiers()) || Modifier.isStatic(methods[i].getModifiers())) continue; String methodName = methods[i].getName(); Class[] parameters = methods[i].getParameterTypes(); Class returnType = methods[i].getReturnType(); String propertyName; if ((methodName.startsWith("get") || methodName.startsWith("is")) && parameters.length == 0 && returnType != void.class) { // if (methodName.startsWith("get")) { // propertyName = Introspector.decapitalize(methodName.substring(3)); // } else { // propertyName = Introspector.decapitalize(methodName.substring(2)); // } // BeanProperty property = getBeanProperty(propertyMap, cls, propertyName, // returnType); // property.setGetterMethod(methods[i]); } else if (methodName.startsWith("set") && parameters.length == 1 && returnType == void.class) { // propertyName = Introspector.decapitalize(methodName.substring(3)); // BeanProperty property = getBeanProperty(propertyMap, cls, propertyName, // parameters[0]); // property.setSetterMethod(methods[i]); } } // retain only those that can be both read and written and // sort them by name List serializableProperties = new ArrayList(); for (Iterator it = propertyMap.values().iterator(); it.hasNext();) { BeanProperty property = (BeanProperty) it.next(); if (property.isReadable() && property.isWritable()) { serializableProperties.add(property); } } Collections.sort(serializableProperties, new BeanPropertyComparator()); // build the maps and return final Map keyedByFieldName = new OrderRetainingMap(); for (Iterator it = serializableProperties.iterator(); it.hasNext();) { BeanProperty property = (BeanProperty) it.next(); keyedByFieldName.put(property.getName(), property); } keyedByPropertyNameCache.put(clsName, keyedByFieldName); } } } return (Map) keyedByPropertyNameCache.get(clsName); } private BeanProperty getBeanProperty(Map propertyMap, Class cls, String propertyName, Class type) { PropertyKey key = new PropertyKey(propertyName, type); BeanProperty property = (BeanProperty) propertyMap.get(key); if (property == null) { property = new BeanProperty(cls, propertyName, type); propertyMap.put(key, property); } return property; } /** * Needed to avoid problems with multiple setters with the same name, but * referred to different types */ private static class PropertyKey { private String propertyName; private Class propertyType; public PropertyKey(String propertyName, Class propertyType) { this.propertyName = propertyName; this.propertyType = propertyType; } public boolean equals(Object o) { if (this == o) return true; if (!(o instanceof PropertyKey)) return false; final PropertyKey propertyKey = (PropertyKey) o; if (propertyName != null ? !propertyName.equals(propertyKey.propertyName) : propertyKey.propertyName != null) return false; if (propertyType != null ? !propertyType.equals(propertyKey.propertyType) : propertyKey.propertyType != null) return false; return true; } public int hashCode() { int result; result = (propertyName != null ? propertyName.hashCode() : 0); result = 29 * result + (propertyType != null ? propertyType.hashCode() : 0); return result; } public String toString() { return "PropertyKey{propertyName='" + propertyName + "'" + ", propertyType=" + propertyType + "}"; } } /** * Compares properties by name */ private static class BeanPropertyComparator implements Comparator { public int compare(Object o1, Object o2) { return ((BeanProperty) o1).getName().compareTo(((BeanProperty) o2).getName()); } } private static class OrderRetainingMap extends HashMap { private List valueOrder = new ArrayList(); public Object put(Object key, Object value) { valueOrder.add(value); return super.put(key, value); } public Collection values() { return Collections.unmodifiableList(valueOrder); } } }