Package org.apache.shindig.social.core.util.xstream

Source Code of org.apache.shindig.social.core.util.xstream.PropertyDictionary$OrderRetainingMap

/*
* 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.shindig.social.core.util.xstream;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import com.thoughtworks.xstream.converters.javabean.BeanProperty;
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;
import java.util.concurrent.ConcurrentHashMap;

/**
* Builds the serializable properties maps for each bean and caches them.
*/
public class PropertyDictionary {

  private final Map<String, Map<String, BeanProperty>> keyedByPropertyNameCache = new ConcurrentHashMap<String, Map<String, BeanProperty>>();

  public Iterator<BeanProperty> serializablePropertiesFor(Class<?> cls) {
    return buildMap(cls).values().iterator();
  }

  /**
   * Locates a serializable property
   *
   * @param cls
   * @param name
   * @param definedIn
   * @return
   */
  public BeanProperty property(Class<?> cls, String name) {
    Map<String, BeanProperty> 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
   * @param tupleKeyed
   * @return
   */
  private Map<String, BeanProperty> 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<PropertyKey, BeanProperty> propertyMap = Maps.newHashMap();
          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 = null;
            if ((methodName.startsWith("get") || methodName.startsWith("is"))
                && parameters.length == 0 && returnType != null) {
              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.equals(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<BeanProperty> serializableProperties = Lists.newArrayList();
          for (BeanProperty property : propertyMap.values()) {
            if (property.isReadable() || property.isWritable()) {
              serializableProperties.add(property);
            }
          }
          Collections
              .sort(serializableProperties, new BeanPropertyComparator());

          // build the maps and return
          final Map<String, BeanProperty> keyedByFieldName = new OrderRetainingMap();
          for (BeanProperty property : serializableProperties) {
            keyedByFieldName.put(property.getName(), property);
          }

          keyedByPropertyNameCache.put(clsName, keyedByFieldName);
        }
      }
    }
    return (Map<String, BeanProperty>) keyedByPropertyNameCache.get(clsName);
  }

  private BeanProperty getBeanProperty(
      Map<PropertyKey, BeanProperty> 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 0
   */
  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<BeanProperty> {

    public int compare(BeanProperty o1, BeanProperty o2) {
      return o1.getName().compareTo(o2.getName());
    }

  }

  private static class OrderRetainingMap<K, V> extends HashMap<K, V> {
    private static final long serialVersionUID = 1565370254073638221L;
    private List<V> valueOrder = Lists.newArrayList();

    public V put(K key, V value) {
      valueOrder.add(value);
      return super.put(key, value);
    }

    public Collection<V> values() {
      return Collections.unmodifiableList(valueOrder);
    }
  }
}
TOP

Related Classes of org.apache.shindig.social.core.util.xstream.PropertyDictionary$OrderRetainingMap

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.
d', 'pageview');