1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.eclipse.jetty.start;
20
21 import java.io.BufferedWriter;
22 import java.io.IOException;
23 import java.io.PrintWriter;
24 import java.nio.charset.StandardCharsets;
25 import java.nio.file.Files;
26 import java.nio.file.Path;
27 import java.nio.file.StandardOpenOption;
28 import java.util.Collection;
29 import java.util.List;
30
31
32
33
34 public class ModuleGraphWriter
35 {
36 private String colorModuleBg;
37 private String colorEnabledBg;
38 private String colorTransitiveBg;
39 private String colorCellBg;
40 private String colorHeaderBg;
41 private String colorModuleFont;
42
43 public ModuleGraphWriter()
44 {
45 colorModuleBg = "#B8FFB8";
46 colorEnabledBg = "#66FFCC";
47 colorTransitiveBg = "#66CC66";
48 colorCellBg = "#FFFFFF80";
49 colorHeaderBg = "#00000020";
50 colorModuleFont = "#888888";
51 }
52
53 public void config(Props props)
54 {
55 String prefix = "jetty.graph.";
56 colorModuleBg = getProperty(props,prefix + "color.module.bg",colorModuleBg);
57 colorEnabledBg = getProperty(props,prefix + "color.enabled.bg",colorEnabledBg);
58 colorTransitiveBg = getProperty(props,prefix + "color.transitive.bg",colorTransitiveBg);
59 colorCellBg = getProperty(props,prefix + "color.cell.bg",colorCellBg);
60 colorHeaderBg = getProperty(props,prefix + "color.header.bg",colorHeaderBg);
61 colorModuleFont = getProperty(props,prefix + "color.font",colorModuleFont);
62 }
63
64 private String getProperty(Props props, String key, String defVal)
65 {
66 String val = props.getString(key,defVal);
67 if (val == null)
68 {
69 return defVal;
70 }
71 val = val.trim();
72 if (val.length() <= 0)
73 {
74 return defVal;
75 }
76 return val;
77 }
78
79 public void write(Modules modules, Path outputFile) throws IOException
80 {
81 try (BufferedWriter writer = Files.newBufferedWriter(outputFile,StandardCharsets.UTF_8,StandardOpenOption.CREATE_NEW,StandardOpenOption.WRITE);
82 PrintWriter out = new PrintWriter(writer);)
83 {
84 writeHeaderMessage(out,outputFile);
85
86 out.println();
87 out.println("digraph modules {");
88
89
90 out.println(" node [color=gray, style=filled, shape=rectangle];");
91 out.println(" node [fontname=\"Verdana\", size=\"20,20\"];");
92
93 out.println(" graph [");
94 out.println(" concentrate=false,");
95 out.println(" fontname=\"Verdana\",");
96 out.println(" fontsize = 20,");
97 out.println(" rankdir = LR,");
98 out.println(" ranksep = 1.5,");
99 out.println(" nodesep = .5,");
100 out.println(" style = bold,");
101 out.println(" labeljust = l,");
102 out.println(" label = \"Jetty Modules\",");
103 out.println(" ssize = \"20,40\"");
104 out.println(" ];");
105
106 List<Module> enabled = modules.resolveEnabled();
107
108
109 writeModules(out,modules,enabled);
110
111
112 writeRelationships(out,modules,enabled);
113
114 out.println("}");
115 out.println();
116 }
117 }
118
119 private void writeHeaderMessage(PrintWriter out, Path outputFile)
120 {
121 out.println("/*");
122 out.println(" * GraphViz Graph of Jetty Modules");
123 out.println(" * ");
124 out.println(" * Jetty: http://eclipse.org/jetty/");
125 out.println(" * GraphViz: http://graphviz.org/");
126 out.println(" * ");
127 out.println(" * To Generate Graph image using graphviz:");
128 String filename = outputFile.getFileName().toString();
129 String basename = filename.substring(0,filename.indexOf('.'));
130 out.printf(" * $ dot -Tpng -Goverlap=false -o %s.png %s%n",basename,filename);
131 out.println(" */");
132 }
133
134 private void writeModuleDetailHeader(PrintWriter out, String header)
135 {
136 writeModuleDetailHeader(out,header,1);
137 }
138
139 private void writeModuleDetailHeader(PrintWriter out, String header, int count)
140 {
141 out.printf(" <TR>");
142 out.printf("<TD BGCOLOR=\"%s\" ALIGN=\"LEFT\"><I>",colorHeaderBg);
143 out.printf("%s%s</I></TD>",header,count > 1?"s":"");
144 out.println("</TR>");
145 }
146
147 private void writeModuleDetailLine(PrintWriter out, String line)
148 {
149 out.printf(" <TR>");
150 StringBuilder escape = new StringBuilder();
151 for(char c: line.toCharArray()) {
152 switch(c) {
153 case '<': escape.append("<"); break;
154 case '>': escape.append(">"); break;
155 default:
156 escape.append(c);
157 break;
158 }
159 }
160
161 out.printf("<TD BGCOLOR=\"%s\" ALIGN=\"LEFT\">%s</TD></TR>%n",colorCellBg,escape.toString());
162 }
163
164 private void writeModuleNode(PrintWriter out, Module module, boolean resolved)
165 {
166 String color = colorModuleBg;
167 if (module.isEnabled())
168 {
169
170 color = colorEnabledBg;
171 }
172 else if (resolved)
173 {
174
175 color = colorTransitiveBg;
176 }
177
178 out.printf(" \"%s\" [ color=\"%s\" label=<",module.getName(),color);
179 out.printf("<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"2\">%n");
180 out.printf(" <TR><TD ALIGN=\"LEFT\"><B>%s</B></TD></TR>%n",module.getName());
181
182 if (module.isEnabled())
183 {
184 writeModuleDetailHeader(out,"ENABLED");
185 for (String source : module.getSources())
186 {
187 writeModuleDetailLine(out,"via: " + source);
188 }
189 }
190 else if (resolved)
191 {
192 writeModuleDetailHeader(out,"TRANSITIVE");
193 }
194
195 if (!module.getXmls().isEmpty())
196 {
197 List<String> xmls = module.getXmls();
198 writeModuleDetailHeader(out,"XML",xmls.size());
199 for (String xml : xmls)
200 {
201 writeModuleDetailLine(out,xml);
202 }
203 }
204
205 if (!module.getLibs().isEmpty())
206 {
207 List<String> libs = module.getLibs();
208 writeModuleDetailHeader(out,"LIB",libs.size());
209 for (String lib : libs)
210 {
211 writeModuleDetailLine(out,lib);
212 }
213 }
214
215 if (!module.getDefaultConfig().isEmpty())
216 {
217 List<String> inis = module.getDefaultConfig();
218 writeModuleDetailHeader(out,"INI Template",inis.size());
219 }
220
221 out.printf("</TABLE>>];%n");
222 }
223
224 private void writeModules(PrintWriter out, Modules allmodules, List<Module> enabled)
225 {
226 out.println();
227 out.println(" /* Modules */");
228 out.println();
229
230 out.println(" node [ labeljust = l ];");
231
232 for (int depth = 0; depth <= allmodules.getMaxDepth(); depth++)
233 {
234 out.println();
235 Collection<Module> depthModules = allmodules.getModulesAtDepth(depth);
236 if (depthModules.size() > 0)
237 {
238 out.printf(" /* Level %d */%n",depth);
239 out.println(" { rank = same;");
240 for (Module module : depthModules)
241 {
242 boolean resolved = enabled.contains(module);
243 writeModuleNode(out,module,resolved);
244 }
245 out.println(" }");
246 }
247 }
248 }
249
250 private void writeRelationships(PrintWriter out, Modules modules, List<Module> enabled)
251 {
252 for (Module module : modules)
253 {
254 for (Module parent : module.getParentEdges())
255 {
256 out.printf(" \"%s\" -> \"%s\";%n",module.getName(),parent.getName());
257 }
258 }
259 }
260 }