Coverage Report - org.apache.shindig.social.core.util.xstream.PropertyDictionary
 
Classes in this File Line Coverage Branch Coverage Complexity
PropertyDictionary
0%
0/51
0%
0/38
0
PropertyDictionary$1
N/A
N/A
0
PropertyDictionary$BeanPropertyComparator
0%
0/2
N/A
0
PropertyDictionary$OrderRetainingMap
0%
0/5
N/A
0
PropertyDictionary$PropertyKey
0%
0/18
0%
0/20
0
 
 1  
 /*
 2  
  * Licensed to the Apache Software Foundation (ASF) under one
 3  
  * or more contributor license agreements. See the NOTICE file
 4  
  * distributed with this work for additional information
 5  
  * regarding copyright ownership. The ASF licenses this file
 6  
  * to you under the Apache License, Version 2.0 (the
 7  
  * "License"); you may not use this file except in compliance
 8  
  * with the License. You may obtain a copy of the License at
 9  
  *
 10  
  *     http://www.apache.org/licenses/LICENSE-2.0
 11  
  *
 12  
  * Unless required by applicable law or agreed to in writing,
 13  
  * software distributed under the License is distributed on an
 14  
  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 15  
  * KIND, either express or implied. See the License for the
 16  
  * specific language governing permissions and limitations under the License.
 17  
  */
 18  
 package org.apache.shindig.social.core.util.xstream;
 19  
 
 20  
 import com.google.common.collect.Lists;
 21  
 import com.google.common.collect.Maps;
 22  
 
 23  
 import com.thoughtworks.xstream.converters.javabean.BeanProperty;
 24  
 import com.thoughtworks.xstream.converters.reflection.ObjectAccessException;
 25  
 
 26  
 import java.beans.Introspector;
 27  
 import java.lang.reflect.Method;
 28  
 import java.lang.reflect.Modifier;
 29  
 import java.util.ArrayList;
 30  
 import java.util.Collection;
 31  
 import java.util.Collections;
 32  
 import java.util.Comparator;
 33  
 import java.util.HashMap;
 34  
 import java.util.Iterator;
 35  
 import java.util.List;
 36  
 import java.util.Map;
 37  
 import java.util.concurrent.ConcurrentHashMap;
 38  
 
 39  
 /**
 40  
  * Builds the serializable properties maps for each bean and caches them.
 41  
  */
 42  0
 public class PropertyDictionary {
 43  
 
 44  0
   private final Map<String, Map<String, BeanProperty>> keyedByPropertyNameCache = new ConcurrentHashMap<String, Map<String, BeanProperty>>();
 45  
 
 46  
   public Iterator<BeanProperty> serializablePropertiesFor(Class<?> cls) {
 47  0
     return buildMap(cls).values().iterator();
 48  
   }
 49  
 
 50  
   /**
 51  
    * Locates a serializable property
 52  
    * 
 53  
    * @param cls
 54  
    * @param name
 55  
    * @param definedIn
 56  
    * @return
 57  
    */
 58  
   public BeanProperty property(Class<?> cls, String name) {
 59  0
     Map<String, BeanProperty> properties = buildMap(cls);
 60  0
     BeanProperty property = (BeanProperty) properties.get(name);
 61  0
     if (property == null) {
 62  0
       throw new ObjectAccessException("No such property " + cls.getName() + "."
 63  
           + name);
 64  
     } else {
 65  0
       return property;
 66  
     }
 67  
   }
 68  
 
 69  
   /**
 70  
    * Builds the map of all serializable properties for the the provided bean
 71  
    * 
 72  
    * @param cls
 73  
    * @param tupleKeyed
 74  
    * @return
 75  
    */
 76  
   private Map<String, BeanProperty> buildMap(Class<?> cls) {
 77  0
     final String clsName = cls.getName();
 78  0
     if (!keyedByPropertyNameCache.containsKey(clsName)) {
 79  0
       synchronized (keyedByPropertyNameCache) {
 80  0
         if (!keyedByPropertyNameCache.containsKey(clsName)) { // double check
 81  
           // Gather all the properties, using only the keyed map. It
 82  
           // is possible that a class have two writable only
 83  
           // properties that have the same name
 84  
           // but different types
 85  0
           final Map<PropertyKey, BeanProperty> propertyMap = Maps.newHashMap();
 86  0
           Method[] methods = cls.getMethods();
 87  
 
 88  0
           for (int i = 0; i < methods.length; i++) {
 89  0
             if (!Modifier.isPublic(methods[i].getModifiers())
 90  
                 || Modifier.isStatic(methods[i].getModifiers())) {
 91  0
               continue;
 92  
             }
 93  
 
 94  0
             String methodName = methods[i].getName();
 95  0
             Class<?>[] parameters = methods[i].getParameterTypes();
 96  0
             Class<?> returnType = methods[i].getReturnType();
 97  0
             String propertyName = null;
 98  0
             if ((methodName.startsWith("get") || methodName.startsWith("is"))
 99  
                 && parameters.length == 0 && returnType != null) {
 100  0
               if (methodName.startsWith("get"))
 101  0
                 propertyName = Introspector.decapitalize(methodName
 102  
                     .substring(3));
 103  
               else
 104  0
                 propertyName = Introspector.decapitalize(methodName
 105  
                     .substring(2));
 106  0
               BeanProperty property = getBeanProperty(propertyMap, cls,
 107  
                   propertyName, returnType);
 108  0
               property.setGetterMethod(methods[i]);
 109  0
             } else if (methodName.startsWith("set") && parameters.length == 1
 110  
                 && returnType.equals(void.class)) {
 111  0
               propertyName = Introspector.decapitalize(methodName.substring(3));
 112  0
               BeanProperty property = getBeanProperty(propertyMap, cls,
 113  
                   propertyName, parameters[0]);
 114  0
               property.setSetterMethod(methods[i]);
 115  
             }
 116  
           }
 117  
 
 118  
           // retain only those that can be both read and written and
 119  
           // sort them by name
 120  0
           List<BeanProperty> serializableProperties = Lists.newArrayList();
 121  0
           for (BeanProperty property : propertyMap.values()) {
 122  0
             if (property.isReadable() || property.isWritable()) {
 123  0
               serializableProperties.add(property);
 124  
             }
 125  0
           }
 126  0
           Collections
 127  
               .sort(serializableProperties, new BeanPropertyComparator());
 128  
 
 129  
           // build the maps and return
 130  0
           final Map<String, BeanProperty> keyedByFieldName = new OrderRetainingMap();
 131  0
           for (BeanProperty property : serializableProperties) {
 132  0
             keyedByFieldName.put(property.getName(), property);
 133  0
           }
 134  
 
 135  0
           keyedByPropertyNameCache.put(clsName, keyedByFieldName);
 136  
         }
 137  0
       }
 138  
     }
 139  0
     return (Map<String, BeanProperty>) keyedByPropertyNameCache.get(clsName);
 140  
   }
 141  
 
 142  
   private BeanProperty getBeanProperty(
 143  
       Map<PropertyKey, BeanProperty> propertyMap, Class<?> cls,
 144  
       String propertyName, Class<?> type) {
 145  0
     PropertyKey key = new PropertyKey(propertyName, type);
 146  0
     BeanProperty property = (BeanProperty) propertyMap.get(key);
 147  0
     if (property == null) {
 148  0
       property = new BeanProperty(cls, propertyName, type);
 149  0
       propertyMap.put(key, property);
 150  
     }
 151  0
     return property;
 152  
   }
 153  
 
 154  
   /**
 155  
    * Needed to avoid problems with multiple setters with the same name, but
 156  
    * referred to different types 0
 157  
    */
 158  
   private static class PropertyKey {
 159  
     private String propertyName;
 160  
 
 161  
     private Class<?> propertyType;
 162  
 
 163  0
     public PropertyKey(String propertyName, Class<?> propertyType) {
 164  0
       this.propertyName = propertyName;
 165  0
       this.propertyType = propertyType;
 166  0
     }
 167  
 
 168  
     public boolean equals(Object o) {
 169  0
       if (this == o)
 170  0
         return true;
 171  0
       if (!(o instanceof PropertyKey))
 172  0
         return false;
 173  
 
 174  0
       final PropertyKey propertyKey = (PropertyKey) o;
 175  
 
 176  0
       if (propertyName != null ? !propertyName.equals(propertyKey.propertyName)
 177  
           : propertyKey.propertyName != null)
 178  0
         return false;
 179  0
       if (propertyType != null ? !propertyType.equals(propertyKey.propertyType)
 180  
           : propertyKey.propertyType != null)
 181  0
         return false;
 182  
 
 183  0
       return true;
 184  
     }
 185  
 
 186  
     public int hashCode() {
 187  
       int result;
 188  0
       result = (propertyName != null ? propertyName.hashCode() : 0);
 189  0
       result = 29 * result
 190  
           + (propertyType != null ? propertyType.hashCode() : 0);
 191  0
       return result;
 192  
     }
 193  
 
 194  
     public String toString() {
 195  0
       return "PropertyKey{propertyName='" + propertyName + "'"
 196  
           + ", propertyType=" + propertyType + "}";
 197  
     }
 198  
 
 199  
   }
 200  
 
 201  
   /**
 202  
    * Compares properties by name
 203  
    */
 204  0
   private static class BeanPropertyComparator implements
 205  
       Comparator<BeanProperty> {
 206  
 
 207  
     public int compare(BeanProperty o1, BeanProperty o2) {
 208  0
       return o1.getName().compareTo(o2.getName());
 209  
     }
 210  
 
 211  
   }
 212  
 
 213  0
   private static class OrderRetainingMap<K, V> extends HashMap<K, V> {
 214  
     private static final long serialVersionUID = 1565370254073638221L;
 215  0
     private List<V> valueOrder = Lists.newArrayList();
 216  
 
 217  
     public V put(K key, V value) {
 218  0
       valueOrder.add(value);
 219  0
       return super.put(key, value);
 220  
     }
 221  
 
 222  
     public Collection<V> values() {
 223  0
       return Collections.unmodifiableList(valueOrder);
 224  
     }
 225  
   }
 226  
 }