1 package org.opendaylight.controller.northbound.commons.query;
3 import java.beans.BeanInfo;
4 import java.beans.IntrospectionException;
5 import java.beans.Introspector;
6 import java.beans.PropertyDescriptor;
7 import java.lang.annotation.Annotation;
8 import java.lang.reflect.Field;
9 import java.lang.reflect.Method;
10 import java.lang.reflect.Modifier;
11 import java.lang.reflect.ParameterizedType;
12 import java.lang.reflect.Type;
13 import java.util.Collection;
14 import java.util.HashMap;
17 import javax.xml.bind.annotation.XmlAccessType;
18 import javax.xml.bind.annotation.XmlAccessorType;
19 import javax.xml.bind.annotation.XmlAttribute;
20 import javax.xml.bind.annotation.XmlElement;
21 import javax.xml.bind.annotation.XmlElementRef;
22 import javax.xml.bind.annotation.XmlElementWrapper;
23 import javax.xml.bind.annotation.XmlRootElement;
24 import javax.xml.bind.annotation.XmlTransient;
25 import javax.xml.bind.annotation.XmlType;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
31 * A wrapper over a JAXB type to allow traversal of the object graph and
32 * search for specific values in the object tree.
34 /*package*/ class TypeInfo {
36 public static final Logger LOGGER = LoggerFactory.getLogger(TypeInfo.class);
37 public static final String DEFAULT_NAME = "##default";
39 protected final String _name; // the jaxb name
40 protected Class<?> _class; // jaxb type class
41 protected final XmlAccessType _accessType; // jaxb access type
42 protected final Accessor _accessor; // accessor to access object value
43 protected Map<String,TypeInfo> _types = new HashMap<String,TypeInfo>();
44 protected volatile boolean _explored = false;
46 * Create a TypeInfo with a name and a class type. The accessor will be null
49 protected TypeInfo(String name, Class<?> clz, Accessor accessor) {
53 XmlAccessorType accessorType = null;
55 throw new NullPointerException("Type class can not be null");
57 accessorType = clz.getAnnotation(XmlAccessorType.class);
58 _accessType = (accessorType == null ?
59 XmlAccessType.PUBLIC_MEMBER : accessorType.value());
60 if (LOGGER.isDebugEnabled()) {
61 LOGGER.debug("Created type info name:{} type:{}", _name, _class);
66 * @return the Accessor to access the value
68 public final Accessor getAccessor() {
73 * @return get the child by name
75 public final TypeInfo getChild(String name) {
76 return _types.get(name);
79 public TypeInfo getCollectionChild(Class<?> childType) {
81 for (TypeInfo ti : _types.values()) {
82 if (Collection.class.isAssignableFrom(ti.getType())) {
83 ParameterizedType p = (ParameterizedType)
84 ti.getAccessor().getGenericType();
85 Type[] pts = p.getActualTypeArguments();
86 if (pts.length == 1 && pts[0].equals(childType)) {
94 public Class getType() {
98 public String getName() {
103 * @return the object value by a selector query
105 public Object retrieve(Object target, String[] query, int index)
106 throws QueryException {
107 if (LOGGER.isDebugEnabled()) {
108 LOGGER.debug("retrieve: {}/{} type:{}", index, query.length, target.getClass());
110 if (index >= query.length) {
114 if (!target.getClass().equals(_class)) {
115 if (_class.isAssignableFrom(target.getClass())) {
116 if (LOGGER.isDebugEnabled()) {
117 LOGGER.debug("Handling subtype {} of {} ", target.getClass(), _class);
119 // explore the subtype
120 TypeInfo subTypeInfo = new TypeInfo(getRootName(target.getClass()),
121 target.getClass(), _accessor);
122 return subTypeInfo.retrieve(target, query, index);
124 // non compatible object; bail out
128 TypeInfo child = getChild(query[index]);
132 target = child.getAccessor().getValue(target);
133 if (index+1 == query.length) {
137 return child.retrieve(target, query, index+1);
141 * Explore the type info for children.
143 public synchronized void explore() {
147 for (Class<?> c = _class; c != null; c = c.getSuperclass()) {
148 if (c.equals(Object.class)) {
151 // Currently only fields and methods annotated with JAXB annotations are
152 // considered as valid for search purposes.
153 //check methods first
154 for (Method m : c.getDeclaredMethods()) {
155 String tn = getTypeName(m, _accessType);
157 if (LOGGER.isDebugEnabled()) {
159 "exploring type: {} name: {} method: {}",
160 _class.getSimpleName(), tn, m);
162 _types.put(tn, createTypeInfo(tn, new Accessor(m)));
165 for (Field f : c.getDeclaredFields()) {
166 String tn = getTypeName(f, _accessType);
168 if (LOGGER.isDebugEnabled()) {
170 "exploring type: {} name: {} field: {}",
171 _class.getSimpleName(), tn, f);
173 _types.put(tn, createTypeInfo(tn, new Accessor(f)));
180 public static final String getTypeName(Field f, XmlAccessType access) {
181 // ignore static, transient and xmltransient fields
182 if (Modifier.isTransient(f.getModifiers()) ||
183 Modifier.isStatic(f.getModifiers()) ||
184 f.getAnnotation(XmlTransient.class) != null ) {
187 // try to read annotation
188 String name = getTypeName(f.getAnnotations(), f.getName());
189 if (name != null) return name;
190 // no annotation present check accesstype
191 else if (access == XmlAccessType.NONE) { // none return name
193 } else if (access == XmlAccessType.FIELD) {
194 // return field name if no annotation present
196 } else if (access == XmlAccessType.PUBLIC_MEMBER
197 && Modifier.isPublic(f.getModifiers())) { // look for public access
200 // return annotated name ( if any )
204 public static final String getTypeName(Method m, XmlAccessType access) {
205 // ignore static, transient and xmltransient fields
206 if (Modifier.isStatic(m.getModifiers()) ||
207 m.getAnnotation(XmlTransient.class) != null ) {
210 // try to read annotation
211 String name = getTypeName(m.getAnnotations(), m.getName());
212 if (name != null) return name;
214 else if (access == XmlAccessType.NONE) { // none return name
216 } else if (access == XmlAccessType.PROPERTY) {
217 // return bean property name if no annotation present
218 return getBeanPropertyName(m);
219 } else if (access == XmlAccessType.PUBLIC_MEMBER
220 && Modifier.isPublic(m.getModifiers())) { // look for public access
221 return getBeanPropertyName(m);
226 private static String getBeanPropertyName(Method m){
229 Class<?> clazz=m.getDeclaringClass();
230 BeanInfo info = Introspector.getBeanInfo(clazz);
231 PropertyDescriptor[] props = info.getPropertyDescriptors();
232 for (PropertyDescriptor pd : props)
234 if (m.equals(pd.getReadMethod())) {
239 catch (IntrospectionException e)
241 LOGGER.error("Could not read bean property name for method = {}",
247 public static TypeInfo createRoot(String name, Class<?> clz) {
248 // root is always a composite type
249 // FIXME assert its a JAXB type
250 XmlRootElement root = clz.getAnnotation(XmlRootElement.class);
252 throw new IllegalArgumentException("Not a JAXB type: " + clz);
255 name = getRootName(clz);
257 return new TypeInfo(name, clz, null);
260 public static TypeInfo createTypeInfo(String name, Accessor accessor) {
261 if (accessor.getAccessibleObject().getAnnotation(XmlElementWrapper.class) != null) {
262 //XmlElementWrapperType
263 return new WrapperTypeInfo(name, accessor);
264 } else if (Collection.class.isAssignableFrom(accessor.getType())) {
266 return new IteratableTypeInfo(name, accessor);
268 return new TypeInfo(name, accessor.getType(), accessor);
271 public static String getRootName(Class<?> cls) {
272 XmlRootElement root = cls.getAnnotation(XmlRootElement.class);
276 String rootName = root.name();
277 if (DEFAULT_NAME.equals(rootName)) {
278 String clsName = cls.getSimpleName();
279 rootName = Character.toLowerCase(clsName.charAt(0)) + clsName.substring(1);
284 protected static String getTypeName(Annotation[] annotations, String dflt) {
286 for (Annotation a : annotations) {
287 if (a.annotationType() == XmlAttribute.class) {
288 name = ((XmlAttribute)a).name();
289 } else if (a.annotationType() == XmlElement.class) {
290 name = ((XmlElement)a).name();
291 } else if (a.annotationType() == XmlElementRef.class) {
292 name = ((XmlElementRef)a).name();
293 } else if (a.annotationType() == XmlElementWrapper.class) {
294 name = ((XmlElementWrapper)a).name();
295 // break the loop as we don't want name to be overwritten by XmlElement
297 } else if (a.annotationType() == XmlType.class) {
298 name = ((XmlType)a).name();
299 } else if (a.annotationType() == XmlTransient.class) {
304 if (DEFAULT_NAME.equals(name)) {
311 public String toString() {
312 return " TypeInfo [_name=" + _name + ", _class=" + _class
313 + ", _accessType=" + _accessType + ", _accessor=" + _accessor
314 + ", _types=" + _types + ", _explored=" + _explored + " ] ";