/** * */ package com.francetelecom.rd.stubs.engine; /* * #%L * Matos * $Id:$ * $HeadURL:$ * %% * Copyright (C) 2008 - 2014 Orange SA * %% * Licensed 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. * #L% */ import java.awt.Color; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.Vector; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.tree.TreeNode; class Node implements TreeNode, Comparable<Node> { public final static int NO_ANNOTATION = 0; public final static int ANNOTATED = 1; public final static int CLASS_DONE = 2; public final static int IS_CLASS_DONE = 3; public final static int IS_ANNOTATED = 4; /** * The path in the midlet where the folder with all the icons is. */ private static final String ICON_PATH = "icons/"; /** * Icon for the root. */ public final static ImageIcon rootIcon = createIcon("root"); /** * Icon for a class */ public final static ImageIcon classIcon = createIcon("class"); /** * Icon for an interface */ public final static ImageIcon interfaceIcon = createIcon("interface"); /** * Icon for a method */ public final static ImageIcon methodIcon = createIcon("method"); /** * Icon for a field */ public final static ImageIcon fieldIcon = createIcon("field"); /** * Icon for a constructor */ public final static ImageIcon constructorIcon = createIcon("constructor"); /** * Icon for a package */ public final static ImageIcon packageIcon = createIcon("package"); /** * Icon for a annotation */ public final static ImageIcon annotationIcon = createIcon("annotation"); /** * Icon for an argument */ public final static ImageIcon argIcon = createIcon("arg"); public final static Object [][] IconMap = { { EngineConstant.ARG_RULE_ANNOT, createIcon("annot-rule-args"), 2 }, { EngineConstant.CALLBACK_ANNOT, createIcon("annot-callback"), 1 }, { EngineConstant.CALLBACK_REGISTER_ANNOT, createIcon("annot-callback-register"), 1 }, { EngineConstant.FIELD_ANNOT, createIcon("annot-field"), 3 }, { EngineConstant.FIELD_ARRAY_ANNOT, createIcon("annot-field"), 3 }, { EngineConstant.FIELD_GET_ANNOT,createIcon("annot-getfield"), 3 }, { EngineConstant.FIELD_SET_ANNOT, createIcon("annot-setfield"), 3 }, { EngineConstant.FIELD_RULE_ANNOT, createIcon("annot-rule-field"), 3 }, { EngineConstant.RETURN_RULE_ANNOT, createIcon("annot-rule-return"), 3 }, { EngineConstant.SUPER_ANNOT, createIcon("annot-use-super"), 0 }, { EngineConstant.USE_RULE_ANNOT, createIcon("annot-rule-use"), 3 }, { EngineConstant.CODE_ANNOT, createIcon("annot-code"), 0 }, { EngineConstant.ACCUMULATOR_ANNOT, createIcon("annot-accumulator"), 0 }, { EngineConstant.DUMP_HIERARCHY_ANNOT, createIcon("annot-dump-hierarchy"), 0 }, { EngineConstant.REAL_ANNOT, createIcon("annot-real"), 3 }, { EngineConstant.MAY_BE_ANNOT, createIcon("annot-maybe"), 1 }, { EngineConstant.FIELD_NO_VALUE_ANNOT, createIcon("annot-no-value"), 0 }, { EngineConstant.FIELD_VALUE_IMPL_ANNOT, createIcon("annot-value-impl"), 3 } }; private static final Color CLASS_ANNOTATED_COLOR = Color.BLUE; private static final Color CLASS_DONE_COLOR = new Color(20,180,20); public static class ELEMENT_VERSION_COLORS { public static final Color notPresent = Color.white; public static final Color hidden = new Color(0x00d51818); public static final Color visible = new Color(0x0044cd1f); } public static class PACKAGE_VERSION_COLORS { public static final Color allPresentandVisible = new Color(0x0044cd1f); // green public static final Color allPresentAndHidden = new Color(0x00d51818); // red public static final Color allPresentSomeHidden = new Color(0x00ff9e13); // orange public static final Color someElementsNotPresentButAllVisible = new Color(0x00c7f3bb); // pale green public static final Color someElementsNotPresentButAllHidden = new Color(0x00f89898); // pale red public static final Color someElementsNotPresent = new Color(0x00ffddac); // pale orange public static final Color noElementsPresent = Color.white; } private static Color DEPTH_COLORS[] = { Color.white, new Color(0x00f4f6f9), new Color(0x00e7ebf1), new Color(0x00d9dfe8), new Color(0x00c9cfd7), new Color(0x00b7bcc3) }; /** * The parent node in the tree */ private Node parent = null; /** * All the children. */ private Vector<Node> children = new Vector<Node>(); /** * Arbitrary content (in fact one of string, reflexive element or integer.) */ private Object content; private Vector<Node> subChilds = new Vector<Node>(); /** * For sorting */ private String shortName = null; /** * For printing */ private String prettyName; private HashSet<Icon> icons = new HashSet<Icon>(); final int annotationLevel; /** * Information about in which versions this element/package is. * If the Node is a package: a Map<String,Integer> with totals of occurences in each version. * If the Node is an element: a List<String> with the versions in which the element is missing. */ private VersionInfo versionsInfo; /** * Depth of a Node in the tree (used to change background color depending on depth) */ private final int depth; /** * Constructor for an annotation. * @param jcv * @param a * @param d depth of the node in the tree */ Node(JClassView jcv, Annotation a, int d) { content = a; depth = d; children = null; String shortName = a.annotationType().getSimpleName(); for (int i = 0; i < IconMap.length; i ++) { if (shortName.equals(IconMap[i][0])) { icons.add((Icon) IconMap[i][1]); switch ((Integer) IconMap[i][2]) { case 0: prettyName = shortName; break; case 1: prettyName = shortName + "(" + Arrays.toString(ReflexUtil.getStringValues(a)) + ")"; break; case 2: prettyName = shortName + "(" + ReflexUtil.getStringValue(a) + "," + Arrays.toString((int []) ReflexUtil.getAnnotationField(a, EngineConstant.ARGS_FIELD)) + ")"; break; case 3: prettyName = shortName + "(" + ReflexUtil.getStringValue(a) + ")"; break; default: prettyName = shortName; } break; } } if (prettyName == null) { prettyName = shortName; } annotationLevel = (shortName.equals(EngineConstant.CLASSDONE_ANNOT)) ? IS_CLASS_DONE : IS_ANNOTATED; } /** * Constructor for a field * @param jcv * @param f * @param d depth of the node in the tree */ Node(JClassView jcv, Field f, int d) { content = f; depth = d; Annotation[] as = f.getAnnotations(); setAnnotations(jcv, children, as, d); setChildrenParent(); prettyName = name(jcv.rf, f); annotationLevel = levelFromChildren(children, true); setIconsFromChildren(); versionsInfo = new VersionInfo.ElementVersionInfo(f); } /** * Constructor for a constructor * @param jcv * @param co * @param d depth of the node in the tree */ Node(JClassView jcv, Constructor<?> co, int d) { content = co; depth = d; Annotation[] as = co.getAnnotations(); Annotation[][] argAs = co.getParameterAnnotations(); setAnnotations(jcv, children, as, d); setArgAnnotation(jcv, children, argAs, d); setChildrenParent(); setIconsFromChildren(); prettyName = name(jcv.rf, co); annotationLevel = levelFromChildren(children, true); versionsInfo = new VersionInfo.ElementVersionInfo(co); } /** * Constructor for a method * @param jcv * @param me * @param d depth of the node in the tree */ Node(JClassView jcv, Method me, int d) { content = me; depth = d; Annotation[] as = me.getAnnotations(); Annotation[][] argAs = me.getParameterAnnotations(); setAnnotations(jcv, children, as, d); setArgAnnotation(jcv, children, argAs, d); setChildrenParent(); setIconsFromChildren(); prettyName = name(jcv.rf, me); annotationLevel = levelFromChildren(children, true); versionsInfo = new VersionInfo.ElementVersionInfo(me); } /** * Constructor for a class * @param jcv * @param c * @param d depth of the node in the tree */ Node(JClassView jcv, Class<?> c, int d) { content = c; depth = d; Annotation[] as = c.getAnnotations(); setAnnotations(jcv, children, as, d); for (Constructor<?> co : c.getDeclaredConstructors()) { children.add(new Node(jcv, co, d+1)); } for (Class<?> cl : c.getDeclaredClasses()) { children.add(new Node(jcv, cl, d+1)); } for (Field f : c.getDeclaredFields()) { children.add(new Node(jcv, f, d+1)); } for (Method m : c.getDeclaredMethods()) { children.add(new Node(jcv, m, d+1)); } setChildrenParent(); setIconsFromChildren(); prettyName = c.getSimpleName(); annotationLevel = levelFromChildren(children, true); versionsInfo = new VersionInfo.ElementVersionInfo(c); } /** * Constructor from a method/constructor argument * @param jcv * @param pos position of the argument. * @param children * @param d depth of the node in the tree */ Node(JClassView jcv, Integer pos, Vector<Node> children, int d) { content = pos; depth = d; this.children = children; setChildrenParent(); prettyName = Integer.toString(pos); annotationLevel = levelFromChildren(children, false); setIconsFromChildren(); } /** * Constructor from a package * @param jcv * @param pkg * @param d depth of the node in the tree */ Node(JClassView jcv, String pkg, int d) { VersionInfo.PackageVersionInfo pkgVersionInfo = new VersionInfo.PackageVersionInfo(); content = pkg; depth = d; HashSet<String> subPkgs = jcv.subPackages.get(pkg); if (subPkgs != null) { for (String subPackage : subPkgs) { Node subNode = new Node(jcv, subPackage, d+1); children.add(subNode); pkgVersionInfo.add(subNode.versionsInfo.getPackageVersionsInfo()); } } HashSet<Class<?>> classes = jcv.packageContents.get(pkg); if (classes != null) { for (Class<?> clazz : classes) { children.add(new Node(jcv, clazz, d+1)); } } setChildrenParent(); prettyName = pkg.substring(pkg.lastIndexOf('.') + 1); annotationLevel = levelFromChildren(children, false); pkgVersionInfo.add(JClassView.database.getVersionStats(pkg)); versionsInfo = pkgVersionInfo; } private void setIconsFromChildren() { for(Node n : children) icons.addAll(n.icons); } private void setChildrenParent() { for (Node n : children) { n.parent = this; } Collections.sort(children); } private int levelFromChildren(Vector<Node> children2, boolean isAnnotated) { int[] counts = new int[5]; for (Node node : children) { counts[node.annotationLevel]++; } if (counts[IS_CLASS_DONE] > 0) return CLASS_DONE; if (isAnnotated) { if (counts[IS_ANNOTATED] > 0) counts[ANNOTATED]++; else counts[NO_ANNOTATION]++; } if (counts[NO_ANNOTATION] > 0) { return (counts[ANNOTATED] > 0 || counts[CLASS_DONE] > 0) ? ANNOTATED : NO_ANNOTATION; } else { return (counts[CLASS_DONE] > 0 || counts[ANNOTATED] == 0) ? CLASS_DONE : ANNOTATED; } } private void setAnnotations(JClassView jcv, Vector<Node> children, Annotation[] as, int d) { for (Annotation a : as) { if (jcv.rf.isStubAnnotation(a)) children.add(new Node(jcv, a, d+1)); } } private void setArgAnnotation(JClassView jcv, Vector<Node> children, Annotation[][] argAs, int d) { for (int i = 0; i < argAs.length; i++) { setAnnotations(jcv, subChilds, argAs[i], d); if (subChilds.size() > 0) { children.add(new Node(jcv, i, subChilds, d+1)); subChilds = new Vector<Node>(); } } } private String getShortName() { if (shortName == null) { if (getContent() instanceof String) shortName = "P" + getContent(); else if (getContent() instanceof Annotation) { shortName = "A" + ((Annotation) getContent()).annotationType() .getSimpleName(); } else if (getContent() instanceof Constructor<?>) { shortName = "D" + ((Constructor<?>) getContent()).getName(); } else if (getContent() instanceof Class<?>) { shortName = "D" + ((Class<?>) getContent()).getSimpleName(); } else if (getContent() instanceof Field) { shortName = "F" + ((Field) getContent()).getName(); } else if (getContent() instanceof Method) { shortName = "M" + ((Method) getContent()).getName(); } else shortName = "Z" + getContent(); } return shortName; } @Override public Enumeration<?> children() { return children.elements(); } @Override public boolean getAllowsChildren() { return true; } @Override public TreeNode getChildAt(int i) { return children.elementAt(i); } @Override public int getChildCount() { return children.size(); } @Override public int getIndex(TreeNode arg0) { return children.indexOf(arg0); } @Override public TreeNode getParent() { return parent; } @Override public boolean isLeaf() { return children == null || children.size() == 0; } @Override public String toString() { return prettyName; } @Override public int compareTo(Node arg0) { return getShortName().compareTo(arg0.getShortName()); } @Override public boolean equals(Object arg0) { return arg0 instanceof Node && getShortName().equals(((Node) arg0).getShortName()); } @Override public int hashCode() { return getShortName().hashCode(); } /** * Gives back the color of the node in the tree. * @return */ public Color getColor() { switch (annotationLevel) { case CLASS_DONE: return CLASS_DONE_COLOR; case ANNOTATED: return CLASS_ANNOTATED_COLOR; default: return Color.BLACK; } } /** * Gives back the icon * @return */ public Icon getIcon() { if (getContent() instanceof String) { if (getContent().equals("")) return rootIcon; return packageIcon; } if (getContent() instanceof Class<?>) { if (((Class<?>) getContent()).isInterface()) return interfaceIcon; return classIcon; } if (getContent() instanceof Constructor<?>) return constructorIcon; if (getContent() instanceof Method) return methodIcon; if (getContent() instanceof Field) return fieldIcon; if (getContent() instanceof Annotation) return annotationIcon; if (getContent() instanceof Integer) return argIcon; return null; } /** * Creates an icon from a path of a resource in the JAR. * * @param s * the local path excluding the prefix . * @return */ private static ImageIcon createIcon(String s) { String pathname = ICON_PATH + s + ".png"; java.net.URL imgURL = Node.class.getResource(pathname); if (imgURL != null) return new ImageIcon(imgURL); else { return null; } } /** * The node content * @return */ public Object getContent() { return content; } public HashSet<Icon> getIcons() { return icons; } /** * Computes a readable description for a method * @param rf * @param m * @return */ static String name(ReflexUtil rf, Method m) { Class <?> here = m.getDeclaringClass(); StringBuilder result = new StringBuilder(); result.append(rf.prettyName(m.getReturnType(), here)); result.append(" "); result.append(m.getName()); result.append("("); boolean first = true; for(Class<?> arg : m.getParameterTypes()) { if(first) first = false; else result.append(","); result.append(rf.prettyName(arg,here)); } result.append(")"); return result.toString(); } /** * A readable description for a constructor * @param rf * @param co * @return */ static String name(ReflexUtil rf, Constructor<?> co) { Class <?> here = co.getDeclaringClass(); StringBuilder result = new StringBuilder(); result.append(here.getSimpleName()); result.append("("); boolean first = true; for(Class<?> arg : co.getParameterTypes()) { if(first) first = false; else result.append(","); result.append(rf.prettyName(arg,here)); } result.append(")"); return result.toString(); } /** * A readable description for a field. * @param rf * @param f * @return */ static String name(ReflexUtil rf, Field f) { Class <?> here = f.getDeclaringClass(); StringBuilder result = new StringBuilder(); result.append(rf.prettyName(f.getType(), here)); result.append(" "); result.append(f.getName()); return result.toString(); } /** * Gets the color to display for a version presence * @param v * @return a Color */ public Color getColorForVersion(String v) { return versionsInfo==null ? Color.BLACK : versionsInfo.getColor(v); } /** * Gets the depth of the Node in the tree * @return */ public int getDepth() { return depth; } public Color getDepthColor() { if(depth>=DEPTH_COLORS.length) { return DEPTH_COLORS[DEPTH_COLORS.length-1]; } else if (depth<0) { return DEPTH_COLORS[0]; } return DEPTH_COLORS[depth]; } }