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