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