added possibility to whitelist or blacklist classes of packages by wildcard pattern
[javainspect.git] / src / main / java / eu / svjatoslav / inspector / java / structure / ClassDescriptor.java
1 /*
2  * JavaInspect - Utility to visualize java software
3  * Copyright (C) 2013, Svjatoslav Agejenko, svjatoslav@svjatoslav.eu
4  * 
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of version 2 of the GNU General Public License
7  * as published by the Free Software Foundation.
8  */
9
10 package eu.svjatoslav.inspector.java.structure;
11
12 import java.lang.reflect.Field;
13 import java.lang.reflect.Method;
14 import java.util.ArrayList;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.Map;
18
19 /**
20  * Describes single class instance
21  */
22 public class ClassDescriptor implements GraphElement {
23
24         private static final int MAX_REFERECNES_COUNT = 10;
25
26         public final String fullyQualifiedName;
27
28         Map<String, FieldDescriptor> nameToFieldMap = new HashMap<String, FieldDescriptor>();
29
30         public List<MethodDescriptor> methods = new ArrayList<MethodDescriptor>();
31
32         /**
33          * Incoming arrows will have this color.
34          */
35         private String distinctiveReferenceColor;
36
37         private String interfaceColor;
38
39         private String superClassColor;
40
41         boolean isEnum;
42
43         boolean isInterface;
44
45         boolean isArray;
46
47         private boolean isShown = true;
48
49         private final ClassGraph classGraph;
50
51         List<ClassDescriptor> interfaces = new ArrayList<ClassDescriptor>();
52
53         ClassDescriptor superClass;
54
55         /**
56          * Amount of field and method references pointing to this class.
57          */
58         private int referenceCount = 0;
59
60         public ClassDescriptor(final Class<? extends Object> clazz,
61                         final ClassGraph dump) {
62                 classGraph = dump;
63
64                 fullyQualifiedName = clazz.getName();
65                 dump.nameToClassMap.put(fullyQualifiedName, this);
66
67                 isArray = clazz.isArray();
68
69                 if (isArray) {
70                         final Class<?> componentType = clazz.getComponentType();
71                         dump.addClass(componentType);
72                 }
73
74                 // System.out.println("class: " + fullyQualifiedName);
75
76                 isEnum = clazz.isEnum();
77
78                 isInterface = clazz.isInterface();
79
80                 if (!isVisible())
81                         return;
82
83                 indexFields(clazz.getDeclaredFields());
84                 indexFields(clazz.getFields());
85
86                 indexMethods(clazz);
87
88                 for (final Class interfaceClass : clazz.getInterfaces())
89                         interfaces.add(dump.addClass(interfaceClass));
90
91                 superClass = dump.addClass(clazz.getSuperclass());
92         }
93
94         public boolean areReferencesShown() {
95                 return referenceCount <= MAX_REFERECNES_COUNT;
96         }
97
98         public void enlistFieldReferences(final StringBuffer result) {
99                 if (nameToFieldMap.isEmpty())
100                         return;
101
102                 result.append("\n");
103                 result.append("    // field references to other classes\n");
104                 for (final Map.Entry<String, FieldDescriptor> entry : nameToFieldMap
105                                 .entrySet())
106                         result.append(entry.getValue().getDot());
107         }
108
109         public void enlistFields(final StringBuffer result) {
110                 if (nameToFieldMap.isEmpty())
111                         return;
112
113                 result.append("\n");
114                 result.append("    // fields:\n");
115
116                 // enlist fields
117                 for (final Map.Entry<String, FieldDescriptor> entry : nameToFieldMap
118                                 .entrySet())
119                         result.append(entry.getValue().getEmbeddedDot());
120         }
121
122         public void enlistImplementedInterfaces(final StringBuffer result) {
123                 if (interfaces.isEmpty())
124                         return;
125
126                 result.append("\n");
127                 result.append("    // interfaces implemented by class: "
128                                 + fullyQualifiedName + "\n");
129
130                 for (final ClassDescriptor interfaceDescriptor : interfaces) {
131                         if (!interfaceDescriptor.isVisible())
132                                 continue;
133
134                         result.append("    " + interfaceDescriptor.getGraphId() + " -> "
135                                         + getGraphId() + "[style=\"dotted, tapered\", color=\""
136                                         + interfaceDescriptor.getInterfaceColor()
137                                         + "\", penwidth=20, dir=\"forward\"];\n");
138                 }
139         }
140
141         public void enlistMethodReferences(final StringBuffer result) {
142                 if (methods.isEmpty())
143                         return;
144
145                 result.append("\n");
146                 result.append("    // method references to other classes\n");
147                 for (final MethodDescriptor methodDescriptor : methods)
148                         result.append(methodDescriptor.getDot());
149         }
150
151         public void enlistMethods(final StringBuffer result) {
152                 if (methods.isEmpty())
153                         return;
154
155                 result.append("\n");
156                 result.append("    // methods:\n");
157
158                 // enlist methods
159                 for (final MethodDescriptor methodDescriptor : methods)
160                         result.append(methodDescriptor.getEmbeddedDot());
161         }
162
163         public void enlistSuperClass(final StringBuffer result) {
164                 if (superClass == null)
165                         return;
166
167                 if (!superClass.isVisible())
168                         return;
169
170                 result.append("\n");
171                 result.append("    // super class for: " + fullyQualifiedName + "\n");
172
173                 result.append("    " + superClass.getGraphId() + " -> " + getGraphId()
174                                 + "[style=\"tapered\", color=\""
175                                 + superClass.getSuperClassColor()
176                                 + "\", penwidth=10, dir=\"forward\"];\n");
177         }
178
179         public void generateDotHeader(final StringBuffer result) {
180                 result.append("\n");
181                 result.append("// Class: " + fullyQualifiedName + "\n");
182
183                 result.append("    " + getGraphId() + "[label=<<TABLE "
184                                 + getBackgroundColor() + " BORDER=\"" + getBorderWidth()
185                                 + "\" CELLBORDER=\"1\" CELLSPACING=\"0\">\n");
186
187                 result.append("\n");
188                 result.append("    // class descriptor header\n");
189                 result.append("    <TR><TD colspan=\"2\" PORT=\"f0\">"
190                                 + "<FONT POINT-SIZE=\"8.0\" >" + getPackageName()
191                                 + "</FONT><br/>");
192
193                 final String parentClassesName = getParentClassesName();
194                 if (parentClassesName.length() > 0)
195                         result.append("<FONT POINT-SIZE=\"12.0\"><B>" + parentClassesName
196                                         + "</B></FONT><br/>\n");
197
198                 result.append("<FONT POINT-SIZE=\"25.0\"><B>" + getClassName(false)
199                                 + "</B></FONT>" + "</TD></TR>\n");
200         }
201
202         public List<FieldDescriptor> getAllFields() {
203                 final List<FieldDescriptor> result = new ArrayList<FieldDescriptor>();
204
205                 for (final Map.Entry<String, FieldDescriptor> entry : nameToFieldMap
206                                 .entrySet())
207                         result.add(entry.getValue());
208
209                 return result;
210         }
211
212         public String getBackgroundColor() {
213                 String bgColor = "";
214
215                 if (isEnum)
216                         bgColor = "bgcolor=\"navajowhite2\"";
217
218                 if (isInterface)
219                         bgColor = "bgcolor=\"darkslategray1\"";
220
221                 return bgColor;
222         }
223
224         public String getBorderWidth() {
225
226                 if (!areReferencesShown())
227                         return "4";
228                 return "1";
229         }
230
231         public String getClassName(final boolean differentiateArray) {
232                 // this is needed for nested classes
233                 final String actualClassName = fullyQualifiedName.replace('$', '.');
234
235                 final int i = actualClassName.lastIndexOf('.');
236
237                 String result = actualClassName.substring(i + 1);
238
239                 if (isArray)
240                         result = result.substring(0, result.length() - 1);
241
242                 if (differentiateArray)
243                         if (isArray)
244                                 result += " []";
245
246                 // this is needed for nested classes
247                 // result = result.replace('$', '.');
248                 return result;
249         }
250
251         public String getColor() {
252                 if (getDistinctiveColor() == null)
253                         setDistinctiveColor(Utils.getNextDarkColor());
254
255                 return getDistinctiveColor();
256         }
257
258         public String getDistinctiveColor() {
259                 return distinctiveReferenceColor;
260         }
261
262         @Override
263         public String getDot() {
264                 if (!isVisible())
265                         return "";
266
267                 if (isArray)
268                         return "";
269
270                 final StringBuffer result = new StringBuffer();
271
272                 generateDotHeader(result);
273
274                 enlistFields(result);
275
276                 enlistMethods(result);
277
278                 result.append("    </TABLE>>, shape=\"none\"];\n");
279
280                 enlistFieldReferences(result);
281
282                 enlistMethodReferences(result);
283
284                 enlistImplementedInterfaces(result);
285
286                 enlistSuperClass(result);
287
288                 return result.toString();
289         }
290
291         @Override
292         public String getEmbeddedDot() {
293                 return null;
294         }
295
296         @Override
297         public String getGraphId() {
298                 final String result = "class_"
299                                 + fullyQualifiedName.replace('.', '_').replace(";", "")
300                                                 .replace("[L", "").replace('$', '_');
301                 return result;
302         }
303
304         public String getInterfaceColor() {
305                 if (interfaceColor == null)
306                         interfaceColor = Utils.getNextLightColor();
307
308                 return interfaceColor;
309         }
310
311         public String getPackageName() {
312
313                 final int i = fullyQualifiedName.lastIndexOf('.');
314
315                 if (i == -1)
316                         return "";
317
318                 return fullyQualifiedName.substring(0, i).replace("[L", "");
319         }
320
321         public String getParentClassesName() {
322                 int i = fullyQualifiedName.lastIndexOf('.');
323                 final String fullClassName = fullyQualifiedName.substring(i + 1);
324
325                 i = fullClassName.lastIndexOf('$');
326                 if (i == -1)
327                         return "";
328                 final String parentClassesName = fullClassName.substring(0, i);
329                 return parentClassesName.replace('$', '.');
330         }
331
332         // public String getReadableName() {
333         //
334         // // do not print full class name for well known system classes
335         // final String packageName = getPackageName();
336         //
337         // if (packageName.equals("java.util"))
338         // return getClassName();
339         //
340         // if (packageName.equals("java.lang"))
341         // return getClassName();
342         //
343         // return fullyQualifiedName;
344         // }
345
346         public String getSuperClassColor() {
347                 if (superClassColor == null)
348                         superClassColor = Utils.getNextLightColor();
349
350                 return superClassColor;
351         }
352
353         public void hide() {
354                 isShown = false;
355         }
356
357         public void indexFields(final Field[] fields) {
358                 for (final Field field : fields) {
359                         if (nameToFieldMap.containsKey(field.getName()))
360                                 continue;
361
362                         final FieldDescriptor fieldDescriptor = new FieldDescriptor(field,
363                                         this, classGraph);
364
365                 }
366         }
367
368         private void indexMethods(final Class<? extends Object> clazz) {
369                 final Method[] methods = clazz.getMethods();
370
371                 for (final Method method : methods)
372                         new MethodDescriptor(method, this, classGraph);
373
374         }
375
376         @Override
377         public boolean isVisible() {
378
379                 if (Utils.isSystemDataType(fullyQualifiedName))
380                         return false;
381
382                 if (Utils.isSystemPackage(fullyQualifiedName))
383                         return false;
384
385                 if (!classGraph.filter.isClassShown(fullyQualifiedName))
386                         return false;
387
388                 return isShown;
389         }
390
391         public void registerReference() {
392                 referenceCount++;
393         }
394
395         public void setDistinctiveColor(final String distinctiveColor) {
396                 distinctiveReferenceColor = distinctiveColor;
397         }
398 }