1 | /** |
2 | * BSD-style license; for more info see http://xradar.sourceforge.net/license.html |
3 | */ |
4 | package org.sourceforge.xradar.statics; |
5 | |
6 | import java.io.BufferedInputStream; |
7 | import java.io.File; |
8 | import java.io.StringReader; |
9 | import java.io.StringWriter; |
10 | import java.net.MalformedURLException; |
11 | import java.net.URL; |
12 | import java.util.ArrayList; |
13 | import java.util.List; |
14 | import java.util.SortedSet; |
15 | import java.util.TreeSet; |
16 | import java.util.logging.Level; |
17 | import java.util.logging.Logger; |
18 | |
19 | import org.sourceforge.xradar.AbstractProcess; |
20 | import org.sourceforge.xradar.XRadarException; |
21 | import org.sourceforge.xradar.logging.LogUtils; |
22 | import org.sourceforge.xradar.util.FileUtils; |
23 | import org.sourceforge.xradar.util.StreamUtils; |
24 | import org.sourceforge.xradar.util.URLUtils; |
25 | import org.xml.sax.InputSource; |
26 | |
27 | import ccl.util.FileUtil; |
28 | |
29 | /** |
30 | * |
31 | * @author Romain PELISSE <belaran@gmail.com> |
32 | * |
33 | */ |
34 | public class MergeReportEngine extends AbstractProcess { |
35 | |
36 | private static final Logger logger = Logger.getLogger(MergeReportEngine.class.getSimpleName()); |
37 | |
38 | public static final String LOG = "log"; |
39 | protected static final String INPUT_FILE_FIELD = "input-report-file"; |
40 | |
41 | private boolean unitTestAvailaible = false; |
42 | private SortedSet<Report> reportsToMerge = new TreeSet<Report>(); |
43 | private List<String> supportedTools; |
44 | |
45 | protected int nbToolsSupported; |
46 | |
47 | /** |
48 | * @return the unitTestAvailaible |
49 | */ |
50 | public boolean isUnitTestAvailaible() { |
51 | return unitTestAvailaible; |
52 | } |
53 | |
54 | /** |
55 | * @param unitTestAvailaible the unitTestAvailaible to set |
56 | */ |
57 | public void setUnitTestAvailaible(boolean unitTestAvailaible) { |
58 | this.unitTestAvailaible = unitTestAvailaible; |
59 | } |
60 | |
61 | /** |
62 | * @return the reportsToMerge |
63 | */ |
64 | public SortedSet<Report> getReportsToMerge() { |
65 | return reportsToMerge; |
66 | } |
67 | |
68 | /** |
69 | * @param reportsToMerge the reportsToMerge to set |
70 | */ |
71 | public void setReportsToMerge(SortedSet<Report> reportsToMerge) { |
72 | this.reportsToMerge = reportsToMerge; |
73 | } |
74 | |
75 | /** |
76 | * @return the supportedTools |
77 | */ |
78 | public List<String> getSupportedTools() { |
79 | return supportedTools; |
80 | } |
81 | |
82 | /** |
83 | * @param supportedTools the supportedTools to set |
84 | */ |
85 | public void setSupportedTools(List<String> supportedTools) { |
86 | this.supportedTools = supportedTools; |
87 | } |
88 | |
89 | /** |
90 | * @return the nbToolsSupported |
91 | */ |
92 | public int getNbToolsSupported() { |
93 | return nbToolsSupported; |
94 | } |
95 | |
96 | /** |
97 | * @param nbToolsSupported the nbToolsSupported to set |
98 | */ |
99 | public void setNbToolsSupported(int nbToolsSupported) { |
100 | this.nbToolsSupported = nbToolsSupported; |
101 | } |
102 | |
103 | /** |
104 | * @return the classesDirectory |
105 | */ |
106 | public String getClassesDirectory() { |
107 | return classesDirectory; |
108 | } |
109 | |
110 | /** |
111 | * @param classesDirectory the classesDirectory to set |
112 | */ |
113 | public void setClassesDirectory(String classesDirectory) { |
114 | this.classesDirectory = classesDirectory; |
115 | } |
116 | |
117 | private String classesDirectory; // Only required if there is no 'jdepend' report to build the backbone file on top of it |
118 | |
119 | /** |
120 | * <p>Load from properties file the metadata on each tools XRadar can agregate.</p> |
121 | */ |
122 | protected void loadSupportedToolsList() { |
123 | this.nbToolsSupported = getDefaultIntValue("Statics.tools.nbTools"); |
124 | this.supportedTools = new ArrayList<String>(this.nbToolsSupported); |
125 | for ( int toolId = 1; toolId <= this.nbToolsSupported ; toolId++ ) |
126 | this.supportedTools.add(getDefault("Statics.tools." + toolId)); |
127 | } |
128 | |
129 | /* |
130 | * Check if filename is not null or equals to "", then try to use it |
131 | * as an XRadar URL. |
132 | * |
133 | * @param filename |
134 | * @param report |
135 | * @throws BuildException |
136 | */ |
137 | //TODO: refactor this to have the "File handling" part of the code |
138 | // relying more on FileUtils. |
139 | private void checkFile(String filename,Report report) throws XRadarException { |
140 | // weirdly enough, a 'null' value for filename is not an issue... |
141 | // FIXME: Add parameters to message |
142 | if ( "".equals(filename)) |
143 | throw new XRadarException(getDefault("Statics.message.missingFilename") + report.toString()); |
144 | else if ( filename != null ) |
145 | { |
146 | if ( ! filename.contains(XRADAR_PROTOCOL) ) { |
147 | try { |
148 | filename = new File(filename).toURL().toString(); |
149 | report.setFile(filename); |
150 | } catch (MalformedURLException e) { |
151 | throw new XRadarException(e); |
152 | } |
153 | } |
154 | if ( URLUtils.openConnection(filename) == null ) |
155 | throw new XRadarException("Statics.message.malformedFilename" + filename); // FIXME: Add parameters to message |
156 | } |
157 | } |
158 | |
159 | |
160 | /** |
161 | * <p>Add a new report prior to merge.</p> |
162 | * |
163 | * @param report |
164 | * @return |
165 | * @throws XRadarException |
166 | */ |
167 | public boolean addReport(Report report) throws XRadarException { |
168 | boolean status = false; |
169 | // TYPE Checking if the field type has been properly set |
170 | if ( "".equals(report.getType()) ) |
171 | throw new XRadarException(getDefault("Statics.message.missingReportType")); |
172 | else if ( ! this.supportedTools.contains(report.getType())) |
173 | throw new XRadarException(getDefault("Statics.message.unsupportedTool")); // FIXME: Add arguments to message |
174 | |
175 | // FILENAME: Checking if the file exists and is readable |
176 | checkFile(report.getFile(), report); |
177 | if ( "".equals(report.getFile())) |
178 | throw new XRadarException(getDefault("Statics.message.missingFilename")); |
179 | File input = new File(getFile(report.getFile())); |
180 | if ( input.exists() && input.canRead() ) |
181 | { |
182 | // MERGING: Checking if mergingFile is setted |
183 | checkFile(report.getMergingFile(), report); |
184 | if ( ! "".equals(report.getMergingFile() ) ) |
185 | report.setMergingFile(getDefault("Statics.defaultXslt." + report.getType())); |
186 | |
187 | // ORDERID: Order consistency checking |
188 | Integer orderId = Integer.valueOf(report.getOrderId()); |
189 | if ( report.getOrderId() == 0 ) |
190 | report.setOrderId(getDefaultIntValue("Statics.defaultOrderId." + report.getType())); |
191 | if ( ! this.reportsToMerge.add(report) ) |
192 | throw new XRadarException(getDefault("Statics.message.inconsistentOrderId") |
193 | + orderId + getDefault("Statics.message.OrderIdAlreadyUsed")); |
194 | // Setting the flag if we add the junit report |
195 | if ( getDefault("Statics.tools.9").equals(report.getType()) ) |
196 | this.unitTestAvailaible = true; |
197 | status = true; |
198 | } |
199 | return status; |
200 | } |
201 | /* |
202 | * tmp hack |
203 | */ |
204 | private String getFile(String fileAsAnURL) throws XRadarException { |
205 | try { |
206 | return new URL(fileAsAnURL).getFile(); |
207 | } catch (MalformedURLException e) { |
208 | throw new XRadarException(e); |
209 | } |
210 | } |
211 | |
212 | /** |
213 | * Executes the merge of each report and then return the product as a String. |
214 | * |
215 | * @return the final result, returned as a String. |
216 | * @throws XRadarException |
217 | */ |
218 | public String executeMerge() throws XRadarException { |
219 | |
220 | // Setting the debug level |
221 | if ( this.isDebug() ) |
222 | logger.setLevel(Level.FINEST); |
223 | // First merge all the report |
224 | StringWriter currentResult = null; |
225 | // Do we have a JDepent report ? |
226 | createBackboneReportIfNeeded(); |
227 | // Now, let merge all the tools reports into one |
228 | for ( Report report : this.reportsToMerge ) { |
229 | if ( this.isDebug() ) { |
230 | logger.fine("Processing report:" + report.getFile() + "[type:" + report.getType() + "]"); |
231 | logger.fine("\tXSLT:" + report.getMergingFile()); |
232 | logger.fine("\tOrderId:" + report.getOrderId()); |
233 | } |
234 | // TODO: Still a StringWriter here, maybe still source of memory consumption issue ? |
235 | StringWriter writer = new StringWriter(); |
236 | merger.setXmlResult(StreamUtils.createOuput(writer)); //new StreamResult(System.out) |
237 | InputSource inputSource; |
238 | // Only on the first merge the report file is the input |
239 | if ( report.getOrderId() == 1 ) |
240 | inputSource = new InputSource (URLUtils.openStream(report.getFile())); |
241 | else { |
242 | merger.setPreambule(getPreambule(report)); |
243 | String str = currentResult.getBuffer().toString(); |
244 | inputSource = new InputSource(new StringReader(str)); |
245 | } |
246 | // TODO: Add success/failure checking, semantic-based (Look for specific xml part that |
247 | // should have been added by the merge ... Maybe using the Analyser API ? |
248 | merger.merge( inputSource, |
249 | new BufferedInputStream(URLUtils.openStream(report.getMergingFile()))); |
250 | currentResult = writer; |
251 | } |
252 | LogUtils.logBuffer(logger,currentResult,"Report produce from merge"); |
253 | return currentResult.getBuffer().toString(); |
254 | } |
255 | |
256 | private void createBackboneReportIfNeeded() throws XRadarException { |
257 | int backboneReportOrderId = getDefaultIntValue("Statics.defaultOrderId.jdepend"); |
258 | if ( this.getClassesDirectory() != null && |
259 | ! "".equals(this.getClassesDirectory()) && // if we have no classes directory, no way around JDepend ! |
260 | backboneReportOrderId > 0 && |
261 | this.reportsToMerge.contains( new Report( backboneReportOrderId) ) ) { |
262 | String reportFilename = this.getDocsHome() + "backbonefile.xml"; |
263 | File backboneReport = new File(this.getDocsHome() + "backbonefile.xml"); |
264 | if ( ! MergeFileBackboneCreator.createBackBoneFile( new File(this.getClassesDirectory()), backboneReport) ) //NOPMD (maybe even a PMD Bug) |
265 | throw new XRadarException("No jdepend report provided to build on and the classes directory provided " + this.getClassesDirectory() + " does not seems to exist"); |
266 | else |
267 | this.reportsToMerge.add(new Report("jdepend",reportFilename)); |
268 | } |
269 | } |
270 | |
271 | /** |
272 | * <p>Return preambule used for each report merge.</p> |
273 | * @param report |
274 | * @return |
275 | */ |
276 | public Preambule getPreambule(Report report) { |
277 | //TODO: To some kind of singleton for this ? |
278 | Preambule preambule = new Preambule(); |
279 | preambule.setParams(report.getParams()); |
280 | preambule.getParams().put(INPUT_FILE_FIELD,new Param(INPUT_FILE_FIELD,report.getFile())); |
281 | preambule.getParams().put(LOG,new Param(LOG,String.valueOf(this.isDebug()))); |
282 | preambule.getParams().put(XRADAR_CONFIG_FILE, new Param(XRADAR_CONFIG_FILE,this.getConfig())); |
283 | return preambule; |
284 | } |
285 | } |