/* * Copyright (c) 2013, OpenCloudDB/MyCAT and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software;Designed and Developed mainly by many Chinese * opensource volunteers. you can redistribute it and/or modify it under the * terms of the GNU General Public License version 2 only, as published by the * Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Any questions about this component can be directed to it's project Web address * https://code.google.com/p/opencloudb/. * */ package org.opencloudb.config.util; import java.lang.reflect.Field; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; /** * @author mycat */ public class FieldDictionary { private final Map<String, Map<String, Field>> nameCache = Collections .synchronizedMap(new HashMap<String, Map<String, Field>>()); private final Map<String, Map<FieldKey, Field>> keyCache = Collections .synchronizedMap(new HashMap<String, Map<FieldKey, Field>>()); /** * Returns an iterator for all serializable fields for some class * * @param cls * the class you are interested on * @return an iterator for its serializable fields */ public Iterator<Field> serializableFieldsFor(Class<?> cls) { return buildMap(cls, true).values().iterator(); } /** * Returns an specific field of some class. If definedIn is null, it searchs * for the field named 'name' inside the class cls. If definedIn is * different than null, tries to find the specified field name in the * specified class cls which should be defined in class definedIn (either * equals cls or a one of it's superclasses) * * @param cls * the class where the field is to be searched * @param name * the field name * @param definedIn * the superclass (or the class itself) of cls where the field * was defined * @return the field itself */ public Field field(Class<?> cls, String name, Class<?> definedIn) { Map<?, Field> fields = buildMap(cls, definedIn != null); Field field = fields.get(definedIn != null ? new FieldKey(name, definedIn, 0) : name); if (field == null) { throw new ObjectAccessException("No such field " + cls.getName() + "." + name); } else { return field; } } private Map<?, Field> buildMap(Class<?> cls, boolean tupleKeyed) { final String clsName = cls.getName(); if (!nameCache.containsKey(clsName)) { synchronized (keyCache) { if (!nameCache.containsKey(clsName)) { // double check final Map<String, Field> keyedByFieldName = new HashMap<String, Field>(); final Map<FieldKey, Field> keyedByFieldKey = new OrderRetainingMap<FieldKey, Field>(); while (!Object.class.equals(cls)) { Field[] fields = cls.getDeclaredFields(); if (JVMInfo.reverseFieldDefinition()) { for (int i = fields.length >> 1; i-- > 0;) { final int idx = fields.length - i - 1; final Field field = fields[i]; fields[i] = fields[idx]; fields[idx] = field; } } for (int i = 0; i < fields.length; i++) { Field field = fields[i]; field.setAccessible(true); if (!keyedByFieldName.containsKey(field.getName())) { keyedByFieldName.put(field.getName(), field); } keyedByFieldKey.put(new FieldKey(field.getName(), field.getDeclaringClass(), i), field); } cls = cls.getSuperclass(); } nameCache.put(clsName, keyedByFieldName); keyCache.put(clsName, keyedByFieldKey); } } } return tupleKeyed ? keyCache.get(clsName) : nameCache.get(clsName); } /** * @author mycat */ private static class FieldKey { private String fieldName; private Class<?> declaringClass; private Integer depth; private int order; public FieldKey(String fieldName, Class<?> declaringClass, int order) { this.fieldName = fieldName; this.declaringClass = declaringClass; this.order = order; Class<?> c = declaringClass; int i = 0; while (c.getSuperclass() != null) { i++; c = c.getSuperclass(); } depth = new Integer(i); } @Override public boolean equals(Object o) { if (this == o) { return true; } if (!(o instanceof FieldKey)) { return false; } final FieldKey fieldKey = (FieldKey) o; if (declaringClass != null ? !declaringClass.equals(fieldKey.declaringClass) : fieldKey.declaringClass != null) { return false; } if (fieldName != null ? !fieldName.equals(fieldKey.fieldName) : fieldKey.fieldName != null) { return false; } return true; } @Override public int hashCode() { int result; result = (fieldName != null ? fieldName.hashCode() : 0); result = 29 * result + (declaringClass != null ? declaringClass.hashCode() : 0); return result; } @Override public String toString() { return "FieldKey{" + "order=" + order + ", writer=" + depth + ", declaringClass=" + declaringClass + ", fieldName='" + fieldName + "'" + "}"; } } }