1 package net.sf.jlayercheck.util;
2
3 import java.io.File;
4 import java.io.FileOutputStream;
5 import java.io.IOException;
6 import java.io.InputStream;
7 import java.io.PrintWriter;
8 import java.net.URL;
9 import java.util.Map;
10 import java.util.Set;
11 import java.util.TreeMap;
12 import java.util.logging.Logger;
13
14 import javax.imageio.ImageIO;
15
16 import net.sf.jlayercheck.util.exceptions.CycleFoundException;
17 import net.sf.jlayercheck.util.exceptions.OrphanedSearchException;
18 import net.sf.jlayercheck.util.exceptions.OverlappingModulesDefinitionException;
19 import net.sf.jlayercheck.util.graph.GraphModuleDependencies;
20 import net.sf.jlayercheck.util.model.ClassDependency;
21 import de.java2html.Java2Html;
22 import de.java2html.options.JavaSourceConversionOptions;
23
24
25
26
27
28
29 public class HTMLOutput {
30 protected static Logger logger = Logger.getLogger("JLayerCheck");
31
32
33
34
35 protected String outputDir;
36
37 public HTMLOutput(String outputDir) {
38 this.outputDir = outputDir;
39 }
40
41
42
43
44
45
46
47
48
49
50 public void write(DependencyVisitor dv, XMLConfiguration xcp) throws IOException {
51
52 new File(outputDir).mkdirs();
53
54
55 FileOutputStream fos = new FileOutputStream(outputDir+File.separator+"unspecified.html");
56 PrintWriter pw = new PrintWriter(fos);
57
58
59 Map<String, URL> javaSources = new TreeMap<String, URL>();
60 javaSources.putAll(xcp.getClassSources().get(0).getSourceFiles());
61
62 Set<String> unspecifiedPackages;
63 try {
64 unspecifiedPackages = xcp.getUnspecifiedPackages(dv.getDependencies());
65 Map<String, Map<String, ClassDependency>> unallowedDependencies = xcp.getUnallowedDependencies(dv.getDependencies());
66
67
68 copyImage("/error.png", "images/error.png", outputDir);
69 copyImage("/package.png", "images/package.png", outputDir);
70 copyImage("/class.png", "images/class.png", outputDir);
71 copyImage("/list.png", "images/list.png", outputDir);
72 copyImage("/jlayercheck.css", "jlayercheck.css", outputDir);
73
74
75 pw.println("<html><head><title>Unspecified packages</title></head><body>");
76 for(String classPackageName : unspecifiedPackages) {
77 classPackageName = formatPackageName(classPackageName);
78 pw.println("Warning: Package "+classPackageName+" has no module.<br/>");
79 }
80 pw.println("</body>");
81 pw.close();
82 fos.close();
83
84 Map<String, URL> sourceFiles = xcp.getAllClassSources();
85
86 fos = new FileOutputStream(outputDir+File.separator+"violations.html");
87 pw = new PrintWriter(fos);
88
89 pw.println("<html><head><title>Dependency violations</title>");
90 pw.println("<style type=\"text/css\" media=\"all\">@import \"jlayercheck.css\";</style>");
91 pw.println("</head><body>");
92
93
94 pw.println("<h1>Module dependencies:</h1>");
95 try {
96 GraphModuleDependencies gmd = new GraphModuleDependencies(xcp.getModuleDependencies());
97 ImageIO.write(gmd.getImage(), "png", new File(outputDir+File.separator+"images"+File.separator+"hierarchy.png"));
98 pw.println("<img src=\"images/hierarchy.png\" />");
99 } catch (CycleFoundException e) {
100 pw.println("<b>The module dependency graph contains cycles (which should be removed)!</b>");
101 }
102
103
104 writeDependencyViolations(dv, xcp, pw, unallowedDependencies, sourceFiles);
105
106
107 writeOrphanedClasses(dv, xcp, pw);
108
109 pw.println("</body>");
110 pw.close();
111 fos.close();
112 } catch (OverlappingModulesDefinitionException e1) {
113 pw.println("<html><head></head><body>Configuration error: "+e1.getMessage()+"</body>");
114 pw.close();
115 fos.close();
116 }
117 }
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132 protected void writeDependencyViolations(DependencyVisitor dv, XMLConfiguration xcp, PrintWriter pw, Map<String, Map<String, ClassDependency>> unallowedDependencies, Map<String, URL> sourceFiles) throws IOException, OverlappingModulesDefinitionException {
133 pw.println("<h1>Dependency violations by packages:</h1>");
134 for(String packagename : dv.getPackages().keySet()) {
135 boolean wrotePackageHeader = false;
136 for(String classname : dv.getPackages().get(packagename)) {
137 String classmodule = xcp.getMatchingModule(classname);
138
139 if (unallowedDependencies.get(classname) != null) {
140 if (!wrotePackageHeader) {
141 wrotePackageHeader = true;
142 pw.println("<br/>");
143 pw.println("<h2><img src=\"images/package.png\" /> Package "+formatPackageName(packagename)+"</h2>");
144 }
145
146
147 String link = classname.replaceAll("/", "_")+".html";
148 pw.println("<h3><img src=\"images/class.png\" /> Class <a href=\""+link+"\">"+formatPackageName(classname)+"</a> ("+classmodule+")</h3>");
149 pw.println("<ul>");
150 Map<Integer, String> markedLines = new TreeMap<Integer, String>();
151 for(String dependency : unallowedDependencies.get(classname).keySet()) {
152 String dependencyPackageName = StringUtils.getPackageName(dependency);
153
154 String dependencymodule = xcp.getPackageModules().get(dependencyPackageName);
155
156
157 pw.println("<li>"+formatPackageName(dependency)+" ("+dependencymodule+")</li>");
158
159 logger.finer("class="+classname+" dep="+dependency);
160 for(int line : unallowedDependencies.get(classname).get(dependency).getLineNumbers()) {
161 logger.finest(" "+line);
162 markedLines.put(line, "must not depend on "+formatPackageName(dependency)+" ("+dependencymodule+")");
163 }
164 logger.finest("");
165 }
166 pw.println("</ul>");
167
168
169 URL url = sourceFiles.get(classname);
170 if (url != null) {
171 String content = readURL(url);
172 JavaSourceConversionOptions options = JavaSourceConversionOptions.getDefault();
173 options.setShowLineNumbers(true);
174 content = Java2Html.convertToHtml(content, options);
175
176 writeSourceFile(outputDir+File.separator+link,
177 content,
178 markedLines);
179 }
180 }
181 }
182 }
183 }
184
185 protected void writeOrphanedClasses(DependencyVisitor dv, XMLConfiguration xcp, PrintWriter pw) {
186 Set<String> orphanedClasses;
187 pw.println("<br/>");
188 pw.println("<h1><img src=\"images/class.png\" /> Orphaned classes:</h1>");
189 pw.println("<ul>");
190 try {
191 orphanedClasses = xcp.getOrphanedClasses(dv.getDependencies());
192 for(String classname : orphanedClasses) {
193 pw.println("<li>" + formatPackageName(classname) + "</li>");
194 }
195 } catch (OrphanedSearchException e) {
196 e.printStackTrace();
197
198 pw.println("<b>An error ocurred: "+e.getMessage()+"</b>");
199 }
200 pw.println("</ul>");
201 }
202
203
204
205
206
207
208
209
210
211 protected void copyImage(String resourceName, String filename, String outputDir) throws IOException {
212 new File(outputDir, filename).getParentFile().mkdirs();
213
214 InputStream is = getClass().getResourceAsStream(resourceName);
215 FileOutputStream fos = new FileOutputStream(new File(outputDir, filename));
216 while(is.available() > 0) {
217 byte content[] = new byte[is.available()];
218 is.read(content);
219 fos.write(content);
220 }
221
222 is.close();
223 fos.close();
224 }
225
226
227
228
229
230
231
232
233
234
235 protected void writeSourceFile(String filename, String content, Map<Integer, String> markedLines) throws IOException {
236 FileOutputStream fos = new FileOutputStream(filename);
237 PrintWriter pw = new PrintWriter(fos);
238
239
240
241
242
243 content = content.substring(content.indexOf("<code>")+6);
244 content = content.substring(0, content.lastIndexOf("</code>"));
245
246 int linenumber = 1;
247 pw.println("<html><head><title>"+filename+"</title>");
248 pw.println("<style type=\"text/css\" media=\"all\">@import \"jlayercheck.css\";</style>");
249
250 copyImage("/yahoo-debug.js", "yahoo-debug.js", outputDir);
251 copyImage("/event-debug.js", "event-debug.js", outputDir);
252 copyImage("/dom-debug.js", "dom-debug.js", outputDir);
253 copyImage("/jlayercheck.js", "jlayercheck.js", outputDir);
254
255 pw.println("<script src='yahoo-debug.js'></script>");
256 pw.println("<script src='event-debug.js'></script>");
257 pw.println("<script src='dom-debug.js'></script>");
258 pw.println("<script src='jlayercheck.js'></script>");
259 pw.println("<script>YAHOO.util.Event.addListener(window, 'load', jlayercheckInit);</script>");
260
261 pw.println("</head><body>");
262 pw.println("<div align=\"left\" class=\"java\"><table border=\"0\" cellpadding=\"3\" cellspacing=\"0\" bgcolor=\"#ffffff\">");
263
264 while(content.length()>0) {
265 int lastpos = content.indexOf("<br />");
266 String token = "";
267 if (lastpos>=0) {
268 token = content.substring(0, lastpos);
269 content = content.substring(lastpos + 6);
270 } else {
271 token = content;
272 content = "";
273 }
274 pw.print("<tr><td>");
275 if (markedLines.containsKey(linenumber)) {
276 pw.print("<div class='msgErr'>");
277 pw.print("<img class='msgErrImg' src=\"images/error.png\" title=\""+markedLines.get(linenumber)+"\" />");
278 pw.print("<div class='msgErrText' id='errText" + linenumber + "'>");
279 pw.print(markedLines.get(linenumber));
280 pw.print("</div>");
281 pw.print("</div>");
282 }
283 pw.print("</td><td nowrap=\"nowrap\" valign=\"top\" align=\"left\">");
284 pw.println("<code>");
285 pw.write(token);
286 pw.println("</code>");
287 pw.print("</td></tr>");
288 linenumber++;
289 }
290 pw.println("</table></div>");
291 pw.println("</body>");
292 pw.close();
293 fos.close();
294 }
295
296 private String readURL(URL url) throws IOException {
297 String result = "";
298
299 InputStream is = url.openStream();
300 while(is.available() > 0) {
301 byte content[] = new byte[is.available()];
302 is.read(content);
303
304 result = result + new String(content);
305 }
306
307 return result;
308
309 }
310
311
312
313
314
315
316
317
318 public static String formatPackageName(String classPackageName) {
319 return classPackageName.replaceAll("/", ".");
320 }
321 }