View Javadoc

1   /*
2    StatCVS - CVS statistics generation
3    Copyright (C) 2006 Benoit Xhenseval
4   
5    This library is free software; you can redistribute it and/or
6    modify it under the terms of the GNU Lesser General Public
7    License as published by the Free Software Foundation; either
8    version 2.1 of the License, or (at your option) any later version.
9   
10   This library is distributed in the hope that it will be useful,
11   but WITHOUT ANY WARRANTY; without even the implied warranty of
12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13   Lesser General Public License for more details.
14  
15   You should have received a copy of the GNU Lesser General Public
16   License along with this library; if not, write to the Free Software
17   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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             // out.append("<!DOCTYPE root SYSTEM \"TreeMap.dtd\" >\n");
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                     //					SvnConfigurationOptions.getTaskLogger().error(e.toString());
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         //		SvnConfigurationOptions.getTaskLogger().log("Directory:" + getSpaces(indent) + dir.getName());
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                 //				SvnConfigurationOptions.getTaskLogger().log("File:" + vfile.getFilename() + " LOC:" + loc);
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                 //				SvnConfigurationOptions.getTaskLogger().log("===========>>> LOC=" + loc + " totalDelta=" + delta + " Delta%=" + percentage);
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         // take all deltas for the last 30 days.
236         for (final Iterator rev = revisions.iterator(); rev.hasNext();) {
237             final Revision revision = (Revision) rev.next();
238 
239             //			SvnConfigurationOptions.getTaskLogger().log(
240             //			        "Revision " + revision.getDate() + " file:" + vfile.getFilename() + " Dead:" + vfile.isDead() + " LOC:" + revision.getLines() + " delta:"
241             //			                + revision.getLinesDelta());
242 
243             if (deadline.before(revision.getDate())) {
244                 delta += revision.getLinesDelta();
245 
246                 //				SvnConfigurationOptions.getTaskLogger().log(
247                 //				        "Revision " + revision.getRevisionNumber() + " Delta:" + revision.getLinesDelta() + " totalDelta:" + delta + " LOC:"
248                 //				                + revision.getLines() + " Dead:" + revision.isDead());
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 }