1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package net.sf.statcvs.output;
21
22 import java.io.BufferedWriter;
23 import java.io.File;
24 import java.io.FileWriter;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.Writer;
28 import java.util.Calendar;
29 import java.util.Date;
30 import java.util.Iterator;
31 import java.util.SortedSet;
32
33 import net.sf.statcvs.Messages;
34 import net.sf.statcvs.model.Directory;
35 import net.sf.statcvs.model.Repository;
36 import net.sf.statcvs.model.Revision;
37 import net.sf.statcvs.model.VersionedFile;
38 import net.sf.statcvs.pages.HTML;
39 import net.sf.statcvs.pages.NavigationNode;
40 import net.sf.statcvs.pages.Page;
41 import net.sf.statcvs.util.FileUtils;
42
43 /**
44 * New report that Repo Map, a jtreemap-based report (applet) that shows the
45 * entire source tree in a hierarchical manner, the size of each box is related
46 * to LOC and the colour to the changes over the last 30 days (red -loc, green
47 * +loc).
48 *
49 * @author Benoit Xhenseval (www.objectlab.co.uk)
50 * @see http://jtreemap.sourceforge.net for more about JTreeMap.
51 */
52 public class RepoMapPageMaker {
53 private static final int DAYS_FROM_LAST_DATE = 30;
54
55 private static final String WEB_FILE_PATH = "web-files/";
56
57 private static final String REPO_FILE = "repomap-data.txt";
58
59 private final Date deadline;
60
61 private final Date currentDate;
62
63 private final ReportConfig config;
64
65 private int indent = 0;
66
67 /**
68 * @see net.sf.statcvs.output.HTMLPage#HTMLPage(Repository)
69 */
70 public RepoMapPageMaker(final ReportConfig config) {
71 final Calendar cal = Calendar.getInstance();
72 if (config != null && config.getRepository() != null && config.getRepository().getLastDate() != null) {
73 cal.setTime(config.getRepository().getLastDate());
74 }
75 currentDate = cal.getTime();
76 cal.add(Calendar.DATE, -DAYS_FROM_LAST_DATE);
77 deadline = cal.getTime();
78 this.config = config;
79 }
80
81 public NavigationNode toFile() {
82 final Page page = this.config.createPage("repomap", Messages.getString("REPOMAP_TITLE"), Messages.getString("REPOMAP_TITLE"));
83 page.addRawAttribute(Messages.getString("REPOMAP_START_DATE"), HTML.getDate(deadline));
84 page.addRawAttribute(Messages.getString("REPOMAP_END_DATE"), HTML.getDate(currentDate));
85
86 page.addRawContent("<p>" + Messages.getString("REPOMAP_DESCRIPTION") + "</p>");
87 page.addRawContent("<p>" + getApplet() + "</p>");
88 page.addRawContent("<p><small>This page uses <a href=\"http://jtreemap.sourceforge.net\">JTreeMap</a>.</small></p>");
89 buildXmlForJTreeMap();
90
91 return page;
92 }
93
94 private String getApplet() {
95 return "<applet archive=\"./" + Messages.getString("JTREEMAP_JAR") + "\" code=\"net.sf.jtreemap.swing.example.JTreeMapAppletExample\""
96 + " width=\"940\" height=\"600\"><param name=\"dataFile\" value=\"" + REPO_FILE + "\"/>" + "<param name=\"viewTree\" value=\"true\"/>"
97 + "<param name=\"showWeight\" value=\"true\"/>" + "<param name=\"valuePrefix\" value=\"Change:\"/>"
98 + "<param name=\"weightPrefix\" value=\"LOC:\"/>" + "<param name=\"dataFileType\" value=\"xml\"/>"
99 + "<param name=\"colorProvider\" value=\"HSBLog\"/>" + "</applet>";
100 }
101
102 private void buildXmlForJTreeMap() {
103 BufferedWriter out = null;
104 try {
105 copyJar(Messages.getString("JTREEMAP_JAR"));
106 out = new BufferedWriter(new FileWriter(ConfigurationOptions.getOutputDir() + REPO_FILE));
107 out.write("<?xml version='1.0' encoding='ISO-8859-1'?>\n");
108
109 out.write("<root>\n");
110 final Iterator it = config.getRepository().getDirectories().iterator();
111 if (it.hasNext()) {
112 final Directory dir = (Directory) it.next();
113 doDirectory(out, dir);
114 }
115 out.write("</root>");
116 } catch (final IOException e) {
117 e.printStackTrace();
118 } finally {
119 if (out != null) {
120 try {
121 out.close();
122 } catch (final IOException e) {
123
124 }
125 }
126 }
127 }
128
129 private void copyJar(final String jtreemapJar) throws IOException {
130 InputStream stream = null;
131 try {
132 stream = RepoMapPageMaker.class.getResourceAsStream(WEB_FILE_PATH + jtreemapJar);
133 if (stream != null) {
134 FileUtils.copyFile(stream, new File(ConfigurationOptions.getOutputDir() + jtreemapJar));
135 } else {
136 throw new IOException("The stream to " + (WEB_FILE_PATH + jtreemapJar) + " failed, is it copied in the jar?");
137 }
138 } finally {
139 if (stream != null) {
140 stream.close();
141 }
142 }
143 }
144
145 private void addSpaces(final int count, final BufferedWriter out) throws IOException {
146 out.write(getSpaces(count));
147 }
148
149 private String getSpaces(final int count) {
150 final StringBuffer result = new StringBuffer();
151 for (int i = 0; i < count; i++) {
152 result.append(" ");
153 }
154 return result.toString();
155 }
156
157 private void doDirectory(final BufferedWriter out, final Directory dir) throws IOException {
158 indent++;
159
160
161 if (dir.isEmpty()) {
162 indent--;
163 return;
164 }
165
166 final SortedSet set = dir.getSubdirectories();
167 final SortedSet files = dir.getFiles();
168 final String name = dir.isRoot() ? Messages.getString("NAVIGATION_ROOT") : dir.getName();
169 boolean addedBranch = false;
170 if (indent > 1 && set != null && !set.isEmpty()) {
171 out.write("\n");
172 addSpaces(indent, out);
173 out.write("<branch>\n");
174 addSpaces(indent + 2, out);
175 labelTag(out, name);
176 addedBranch = true;
177 } else if (indent == 1) {
178 addSpaces(indent, out);
179 labelTag(out, name);
180 }
181 if (set != null) {
182 for (final Iterator it2 = set.iterator(); it2.hasNext();) {
183 doDirectory(out, (Directory) it2.next());
184 }
185 }
186 addedBranch = handleEachFileInDir(out, files, name, addedBranch);
187 if (addedBranch) {
188 addSpaces(indent, out);
189 out.write("</branch>\n");
190 }
191 indent--;
192 }
193
194 private boolean handleEachFileInDir(final BufferedWriter out, final SortedSet files, final String name, boolean addedBranch) throws IOException {
195 if (files != null && !files.isEmpty()) {
196 for (final Iterator file = files.iterator(); file.hasNext();) {
197 final VersionedFile vfile = (VersionedFile) file.next();
198
199 int loc = vfile.getCurrentLinesOfCode();
200
201
202
203 final int delta = calculateTotalDelta(vfile);
204 if (loc == 0) {
205 loc = Math.abs(delta);
206 }
207 if (loc == 0) {
208 continue;
209 }
210 if (!addedBranch) {
211 out.write("\n");
212 addSpaces(indent, out);
213 out.write("<branch>\n");
214 addSpaces(indent + 2, out);
215 labelTag(out, name);
216 out.write("\n");
217 addedBranch = true;
218 }
219 addSpaces(indent + 2, out);
220 out.write("<leaf>");
221 labelTag(out, vfile.getFilename());
222 tag(out, "weight", String.valueOf(loc));
223 final double percentage = ((double) delta) / (double) loc * 100.0;
224 tag(out, "value", String.valueOf(percentage));
225 out.write("</leaf>\n");
226
227 }
228 }
229 return addedBranch;
230 }
231
232 private int calculateTotalDelta(final VersionedFile vfile) {
233 int delta = 0;
234 final SortedSet revisions = vfile.getRevisions();
235
236 for (final Iterator rev = revisions.iterator(); rev.hasNext();) {
237 final Revision revision = (Revision) rev.next();
238
239
240
241
242
243 if (deadline.before(revision.getDate())) {
244 delta += revision.getLinesDelta();
245
246
247
248
249 }
250 }
251 return delta;
252 }
253
254 private void labelTag(final Writer result, final String name) throws IOException {
255 if (name == null || name.length() == 0) {
256 tag(result, "label", "[root]");
257 } else {
258 tag(result, "label", name);
259 }
260 }
261
262 private void tag(final Writer result, final String tagName, final String value) throws IOException {
263 result.write("<");
264 result.write(tagName);
265 result.write(">");
266 result.write(value);
267 result.write("</");
268 result.write(tagName);
269 result.write(">");
270 }
271 }