1/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. 2 * 3 * This program and the accompanying materials are made available under 4 * the terms of the Common Public License v1.0 which accompanies this distribution, 5 * and is available at http://www.eclipse.org/legal/cpl-v10.html 6 * 7 * $Id: ReportProcessor.java,v 1.1.1.1.2.2 2004/07/16 23:32:29 vlad_r Exp $ 8 */ 9package com.vladium.emma.report; 10 11import java.io.File; 12import java.io.IOException; 13 14import com.vladium.logging.Logger; 15import com.vladium.util.Files; 16import com.vladium.util.IConstants; 17import com.vladium.util.IProperties; 18import com.vladium.util.Strings; 19import com.vladium.util.asserts.$assert; 20import com.vladium.util.exception.Exceptions; 21import com.vladium.emma.IAppConstants; 22import com.vladium.emma.IAppErrorCodes; 23import com.vladium.emma.EMMARuntimeException; 24import com.vladium.emma.Processor; 25import com.vladium.emma.data.DataFactory; 26import com.vladium.emma.data.ICoverageData; 27import com.vladium.emma.data.IMergeable; 28import com.vladium.emma.data.IMetaData; 29 30// ---------------------------------------------------------------------------- 31/* 32 * This class was not meant to be public by design. It is made to to work around 33 * access bugs in reflective invocations. 34 */ 35/** 36 * @author Vlad Roubtsov, (C) 2003 37 */ 38public 39final class ReportProcessor extends Processor 40 implements IAppErrorCodes 41{ 42 // public: ................................................................ 43 44 public static ReportProcessor create () 45 { 46 return new ReportProcessor (); 47 } 48 49 /** 50 * 51 * @param path [null is equivalent to an empty array] 52 */ 53 public synchronized final void setDataPath (final String [] path) 54 { 55 if ((path == null) || (path.length == 0)) 56 m_dataPath = IConstants.EMPTY_FILE_ARRAY; 57 else 58 m_dataPath = Files.pathToFiles (path, true); 59 } 60 61 /** 62 * @param path [null is equivalent to no source path] 63 */ 64 public synchronized void setSourcePath (final String [] path) 65 { 66 if (path == null) 67 m_sourcePath = null; 68 else 69 m_sourcePath = Files.pathToFiles (path, true); // always canonicalize source path 70 } 71 72 /** 73 * 74 * @param types [may not be null] 75 */ 76 public synchronized void setReportTypes (final String [] types) 77 { 78 if (types == null) throw new IllegalArgumentException ("null input: types"); 79 80 final String [] reportTypes = Strings.removeDuplicates (types, true); 81 if (reportTypes.length == 0) throw new IllegalArgumentException ("empty input: types"); 82 83 if ($assert.ENABLED) $assert.ASSERT (reportTypes != null && reportTypes.length > 0); 84 85 86 final IReportGenerator [] reportGenerators = new IReportGenerator [reportTypes.length]; 87 for (int t = 0; t < reportTypes.length; ++ t) 88 { 89 reportGenerators [t] = AbstractReportGenerator.create (reportTypes [t]); 90 } 91 92 m_reportGenerators = reportGenerators; 93 } 94 95 // protected: ............................................................. 96 97 98 protected void validateState () 99 { 100 super.validateState (); 101 102 if (m_dataPath == null) 103 throw new IllegalStateException ("data path not set"); 104 105 // [m_sourcePath can be null] 106 107 if ((m_reportGenerators == null) || (m_reportGenerators.length == 0)) 108 throw new IllegalStateException ("report types not set"); 109 110 // [m_propertyOverrides can be null] 111 } 112 113 114 protected void _run (final IProperties toolProperties) 115 { 116 final Logger log = m_log; 117 118 final boolean verbose = m_log.atVERBOSE (); 119 if (verbose) 120 { 121 log.verbose (IAppConstants.APP_VERBOSE_BUILD_ID); 122 123 // [assertion: m_dataPath != null] 124 log.verbose ("input data path:"); 125 log.verbose ("{"); 126 for (int p = 0; p < m_dataPath.length; ++ p) 127 { 128 final File f = m_dataPath [p]; 129 final String nonexistent = f.exists () ? "" : "{nonexistent} "; 130 131 log.verbose (" " + nonexistent + f.getAbsolutePath ()); 132 } 133 log.verbose ("}"); 134 135 136 if ((m_sourcePath == null) || (m_sourcePath.length == 0)) 137 { 138 log.verbose ("source path not set"); 139 } 140 else 141 { 142 log.verbose ("source path:"); 143 log.verbose ("{"); 144 for (int p = 0; p < m_sourcePath.length; ++ p) 145 { 146 final File f = m_sourcePath [p]; 147 final String nonexistent = f.exists () ? "" : "{nonexistent} "; 148 149 log.verbose (" " + nonexistent + f.getAbsolutePath ()); 150 } 151 log.verbose ("}"); 152 } 153 } 154 else 155 { 156 log.info ("processing input files ..."); 157 } 158 159 RuntimeException failure = null; 160 try 161 { 162 final long start = log.atINFO () ? System.currentTimeMillis () : 0; 163 164 IMetaData mdata = null; 165 ICoverageData cdata = null; 166 167 // merge all data files: 168 try 169 { 170 for (int f = 0; f < m_dataPath.length; ++ f) 171 { 172 final File dataFile = m_dataPath [f]; 173 if (verbose) log.verbose ("processing input file [" + dataFile.getAbsolutePath () + "] ..."); 174 175 final IMergeable [] fileData = DataFactory.load (dataFile); 176 177 final IMetaData _mdata = (IMetaData) fileData [DataFactory.TYPE_METADATA]; 178 if (_mdata != null) 179 { 180 if (verbose) log.verbose (" loaded " + _mdata.size () + " metadata entries"); 181 182 if (mdata == null) 183 mdata = _mdata; 184 else 185 mdata = (IMetaData) mdata.merge (_mdata); // note: later datapath entries override earlier ones 186 } 187 188 final ICoverageData _cdata = (ICoverageData) fileData [DataFactory.TYPE_COVERAGEDATA]; 189 if (_cdata != null) 190 { 191 if (verbose) log.verbose (" loaded " + _cdata.size () + " coverage data entries"); 192 193 if (cdata == null) 194 cdata = _cdata; 195 else 196 cdata = (ICoverageData) cdata.merge (_cdata); // note: later datapath entries override earlier ones 197 } 198 199 ++ m_dataFileCount; 200 } 201 202 if (log.atINFO ()) 203 { 204 final long end = System.currentTimeMillis (); 205 206 log.info (m_dataFileCount + " file(s) read and merged in " + (end - start) + " ms"); 207 } 208 209 if ((mdata == null) || mdata.isEmpty ()) 210 { 211 log.warning ("nothing to do: no metadata found in any of the data files"); 212 213 return; 214 } 215 216 if (cdata == null) 217 { 218 log.warning ("nothing to do: no runtime coverage data found in any of the data files"); 219 220 return; 221 } 222 223 if (cdata.isEmpty ()) 224 { 225 log.warning ("no collected coverage data found in any of the data files [all reports will be empty]"); 226 } 227 228 229 if (verbose) 230 { 231 if (mdata != null) 232 { 233 log.verbose (" merged metadata contains " + mdata.size () + " entries"); 234 } 235 236 if (cdata != null) 237 { 238 log.verbose (" merged coverage data contains " + cdata.size () + " entries"); 239 } 240 } 241 242 SourcePathCache srcpathCache = null; 243 if (m_sourcePath != null) srcpathCache = new SourcePathCache (m_sourcePath, true); // ignore non-existent source dirs 244 245 for (int g = 0; g < m_reportGenerators.length; ++ g) 246 { 247 final IReportGenerator generator = m_reportGenerators [g]; 248 249 try 250 { 251 // no shallow copies of 'mdata' or 'cdata' are needed here 252 // because this command never runs in a concurrent situation 253 254 generator.process (mdata, cdata, srcpathCache, toolProperties); 255 } 256 catch (Throwable t) 257 { 258 // TODO: handle and continue 259 t.printStackTrace (System.out); 260 261 // TODO: continue here 262 break; 263 } 264 finally 265 { 266 try { generator.cleanup (); } catch (Throwable ignore) {} 267 } 268 } 269 } 270 catch (IOException ioe) 271 { 272 // TODO: handle 273 ioe.printStackTrace (System.out); 274 } 275 } 276 catch (SecurityException se) 277 { 278 failure = new EMMARuntimeException (SECURITY_RESTRICTION, new String [] {IAppConstants.APP_NAME}, se); 279 } 280 catch (RuntimeException re) 281 { 282 failure = re; 283 } 284 finally 285 { 286 reset (); 287 } 288 289 if (failure != null) 290 { 291 if (Exceptions.unexpectedFailure (failure, EXPECTED_FAILURES)) 292 { 293 throw new EMMARuntimeException (UNEXPECTED_FAILURE, 294 new Object [] {failure.toString (), IAppConstants.APP_BUG_REPORT_LINK}, 295 failure); 296 } 297 else 298 throw failure; 299 } 300 } 301 302 // package: ............................................................... 303 304 // private: ............................................................... 305 306 307 private ReportProcessor () 308 { 309 m_dataPath = IConstants.EMPTY_FILE_ARRAY; 310 } 311 312 private void reset () 313 { 314 m_dataFileCount = 0; 315 } 316 317 318 // caller-settable state [scoped to this runner instance]: 319 320 private File [] m_dataPath; // required to be non-null for run() 321 private File [] m_sourcePath; // can be null/empty for run() 322 private IReportGenerator [] m_reportGenerators; // required to be non-null for run() 323 324 // internal run()-scoped state: 325 326 private int m_dataFileCount; 327 328 private static final Class [] EXPECTED_FAILURES; // set in <clinit> 329 330 static 331 { 332 EXPECTED_FAILURES = new Class [] 333 { 334 EMMARuntimeException.class, 335 IllegalArgumentException.class, 336 IllegalStateException.class, 337 }; 338 } 339 340} // end of class 341// ----------------------------------------------------------------------------