1 19 20 package org.apache.cayenne.util; 21 22 import java.lang.reflect.Method ; 23 import java.util.ArrayList ; 24 import java.util.Collection ; 25 import java.util.HashMap ; 26 import java.util.Map ; 27 28 import org.apache.cayenne.jpa.JpaProviderException; 29 import org.apache.cayenne.project.ProjectPath; 30 31 37 public class TraversalUtil { 38 39 static final ClassTraversalDescriptor noopDescriptor = new ClassTraversalDescriptor(); 40 static final Map <String , ClassTraversalDescriptor> descriptors = new HashMap <String , ClassTraversalDescriptor>(); 41 42 private static Method [] traversableGetters(Class nodeType) { 43 44 Collection <Method > getters = null; 45 46 Method [] methods = nodeType.getMethods(); 47 for (int i = 0; i < methods.length; i++) { 48 if (methods[i].isAnnotationPresent(TreeNodeChild.class) 49 && !Void.TYPE.equals(methods[i].getReturnType())) { 50 51 if (getters == null) { 52 getters = new ArrayList <Method >(5); 53 } 54 55 getters.add(methods[i]); 56 } 57 } 58 59 return getters != null ? getters.toArray(new Method [getters.size()]) : null; 60 } 61 62 static synchronized ClassTraversalDescriptor getDescriptor(Class nodeType) { 63 String typeName = nodeType.getName(); 64 ClassTraversalDescriptor descriptor = descriptors.get(typeName); 65 if (descriptor == null) { 66 Method [] getters = traversableGetters(nodeType); 67 descriptor = getters != null 68 ? new ClassTraversalDescriptor(getters) 69 : noopDescriptor; 70 descriptors.put(typeName, descriptor); 71 } 72 73 return descriptor; 74 } 75 76 79 public static void traverse(Object treeRoot, HierarchicalTreeVisitor visitor) { 80 traverse(treeRoot, visitor, null); 81 } 82 83 static void traverse( 84 Object node, 85 HierarchicalTreeVisitor visitor, 86 ProjectPath parentPath) { 87 88 ProjectPath path = parentPath != null 89 ? parentPath.appendToPath(node) 90 : new ProjectPath(node); 91 92 if (visitor.onStartNode(path)) { 93 94 ClassTraversalDescriptor descriptor = getDescriptor(node.getClass()); 95 Class [] childTypes = descriptor.getTraversableChildTypes(); 96 if (childTypes != null && childTypes.length > 0) { 97 for (int i = 0; i < childTypes.length; i++) { 98 99 HierarchicalTreeVisitor childVisitor = visitor.childVisitor( 100 path, 101 childTypes[i]); 102 if (childVisitor != null) { 103 Object child = descriptor.getTraversableChild(node, i); 104 105 if (child == null) { 106 continue; 107 } 108 else if (child instanceof Collection ) { 109 Collection children = (Collection ) child; 110 111 if (children != null && !children.isEmpty()) { 112 for (Object collectionChild : children) { 113 traverse(collectionChild, childVisitor, path); 114 } 115 } 116 } 117 else { 118 traverse(child, childVisitor, path); 119 } 120 } 121 } 122 } 123 124 visitor.onFinishNode(path); 125 } 126 } 127 128 static class ClassTraversalDescriptor { 129 130 Class [] traversableChildTypes; 131 Method [] traversableGetters; 132 133 ClassTraversalDescriptor() { 134 135 } 136 137 ClassTraversalDescriptor(Method [] traversableChildGetters) { 138 this.traversableGetters = traversableChildGetters; 139 this.traversableChildTypes = new Class [traversableChildGetters.length]; 140 for (int i = 0; i < traversableChildGetters.length; i++) { 141 Class type = traversableChildGetters[i].getReturnType(); 142 if (Collection .class.isAssignableFrom(type)) { 143 type = traversableChildGetters[i] 144 .getAnnotation(TreeNodeChild.class) 145 .type(); 146 147 if (void.class.equals(type)) { 150 throw new JpaProviderException("No type for collection defined: " 151 + traversableChildGetters[i].getName()); 152 } 153 } 154 155 traversableChildTypes[i] = type; 156 } 157 } 158 159 Class [] getTraversableChildTypes() { 160 return traversableChildTypes; 161 } 162 163 Object getTraversableChild(Object object, int childIndex) { 164 165 try { 166 return traversableGetters[childIndex].invoke(object, (Object []) null); 167 } 168 catch (Exception e) { 169 throw new JpaProviderException("Error reading traversible property", e); 170 } 171 } 172 } 173 } 174 | Popular Tags |