| 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 | } |