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: ReportGenerator.java,v 1.1.1.1.2.1 2004/07/16 23:32:30 vlad_r Exp $ 8 */ 9 package com.vladium.emma.report.txt; 10 11 import java.io.BufferedWriter; 12 import java.io.File; 13 import java.io.FileOutputStream; 14 import java.io.IOException; 15 import java.io.OutputStreamWriter; 16 import java.io.UnsupportedEncodingException; 17 import java.util.Date; 18 import java.util.Iterator; 19 import java.util.LinkedList; 20 21 import com.vladium.util.Files; 22 import com.vladium.util.IProperties; 23 import com.vladium.util.asserts.$assert; 24 import com.vladium.emma.IAppConstants; 25 import com.vladium.emma.IAppErrorCodes; 26 import com.vladium.emma.EMMAProperties; 27 import com.vladium.emma.EMMARuntimeException; 28 import com.vladium.emma.data.ICoverageData; 29 import com.vladium.emma.data.IMetaData; 30 import com.vladium.emma.report.AbstractReportGenerator; 31 import com.vladium.emma.report.AllItem; 32 import com.vladium.emma.report.ClassItem; 33 import com.vladium.emma.report.IItem; 34 import com.vladium.emma.report.IItemAttribute; 35 import com.vladium.emma.report.ItemComparator; 36 import com.vladium.emma.report.MethodItem; 37 import com.vladium.emma.report.PackageItem; 38 import com.vladium.emma.report.SourcePathCache; 39 import com.vladium.emma.report.SrcFileItem; 40 41 // ---------------------------------------------------------------------------- 42 /** 43 * @author Vlad Roubtsov, (C) 2003 44 */ 45 public 46 final class ReportGenerator extends AbstractReportGenerator 47 implements IAppErrorCodes 48 { 49 // public: ................................................................ 50 51 // TODO: this is prototype quality, needs major cleanup 52 53 // IReportGenerator: 54 getType()55 public String getType () 56 { 57 return TYPE; 58 } 59 process(final IMetaData mdata, final ICoverageData cdata, final SourcePathCache cache, final IProperties properties)60 public void process (final IMetaData mdata, final ICoverageData cdata, 61 final SourcePathCache cache, final IProperties properties) 62 throws EMMARuntimeException 63 { 64 initialize (mdata, cdata, cache, properties); 65 66 long start = 0, end; 67 final boolean trace1 = m_log.atTRACE1 (); 68 69 if (trace1) start = System.currentTimeMillis (); 70 71 { 72 m_queue = new LinkedList (); 73 for (m_queue.add (m_view.getRoot ()); ! m_queue.isEmpty (); ) 74 { 75 final IItem head = (IItem) m_queue.removeFirst (); 76 77 head.accept (this, null); 78 } 79 line (); 80 81 close (); 82 } 83 84 if (trace1) 85 { 86 end = System.currentTimeMillis (); 87 88 m_log.trace1 ("process", "[" + getType () + "] report generated in " + (end - start) + " ms"); 89 } 90 } 91 cleanup()92 public void cleanup () 93 { 94 m_queue = null; 95 close (); 96 97 super.cleanup (); 98 } 99 100 101 // IItemVisitor: 102 visit(final AllItem item, final Object ctx)103 public Object visit (final AllItem item, final Object ctx) 104 { 105 File outFile = m_settings.getOutFile (); 106 if (outFile == null) 107 { 108 outFile = new File ("coverage.txt"); 109 m_settings.setOutFile (outFile); 110 } 111 112 final File fullOutFile = Files.newFile (m_settings.getOutDir (), outFile); 113 114 m_log.info ("writing [" + getType () + "] report to [" + fullOutFile.getAbsolutePath () + "] ..."); 115 116 openOutFile (fullOutFile, m_settings.getOutEncoding (), true); 117 118 // build ID stamp: 119 try 120 { 121 final StringBuffer label = new StringBuffer (101); 122 123 label.append ("["); 124 label.append (IAppConstants.APP_NAME); 125 label.append (" v"); label.append (IAppConstants.APP_VERSION_WITH_BUILD_ID_AND_TAG); 126 label.append (" report, generated "); 127 label.append (new Date (EMMAProperties.getTimeStamp ())); 128 label.append ("]"); 129 130 m_out.write (label.toString ()); 131 m_out.newLine (); 132 133 m_out.flush (); 134 } 135 catch (IOException ioe) 136 { 137 throw new EMMARuntimeException (IAppErrorCodes.REPORT_IO_FAILURE, ioe); 138 } 139 140 final int [] columns = m_settings.getColumnOrder (); 141 142 line (); 143 144 // [all] coverage summary row: 145 addTitleRow ("OVERALL COVERAGE SUMMARY", 0, 1); 146 { 147 // header row: 148 addHeaderRow (item, columns); 149 150 // coverage row: 151 addItemRow (item, columns); 152 } 153 154 // [all] stats summary table ([all] only): 155 addTitleRow ("OVERALL STATS SUMMARY", 1, 1); 156 { 157 row ("total packages:" + m_separator + item.getChildCount ()); 158 row ("total classes:" + m_separator + item.getAggregate (IItem.TOTAL_CLASS_COUNT)); 159 row ("total methods:" + m_separator + item.getAggregate (IItem.TOTAL_METHOD_COUNT)); 160 161 if (m_srcView && m_hasSrcFileInfo) 162 { 163 row ("total executable files:" + m_separator + item.getAggregate (IItem.TOTAL_SRCFILE_COUNT)); 164 165 if (m_hasLineNumberInfo) 166 row ("total executable lines:" + m_separator + item.getAggregate (IItem.TOTAL_LINE_COUNT)); 167 } 168 } 169 170 final boolean deeper = (m_settings.getDepth () > item.getMetadata ().getTypeID ()); 171 172 // render package summary rows: 173 addTitleRow ("COVERAGE BREAKDOWN BY PACKAGE", 1, 1); 174 { 175 boolean headerDone = false; 176 final ItemComparator order = m_typeSortComparators [PackageItem.getTypeMetadata ().getTypeID ()]; 177 for (Iterator packages = item.getChildren (order); packages.hasNext (); ) 178 { 179 final IItem pkg = (IItem) packages.next (); 180 181 if (! headerDone) 182 { 183 // header row: 184 addHeaderRow (pkg, columns); 185 headerDone = true; 186 } 187 188 // coverage row: 189 addItemRow (pkg, columns); 190 191 if (deeper) m_queue.addLast (pkg); 192 } 193 } 194 195 return ctx; 196 } 197 visit(final PackageItem item, final Object ctx)198 public Object visit (final PackageItem item, final Object ctx) 199 { 200 if (m_verbose) m_log.verbose (" report: processing package [" + item.getName () + "] ..."); 201 202 final int [] columns = m_settings.getColumnOrder (); 203 204 line (); 205 206 // coverage summary row: 207 addTitleRow ("COVERAGE SUMMARY FOR PACKAGE [".concat (item.getName ()).concat ("]"), 0, 1); 208 { 209 // header row: 210 addHeaderRow (item, columns); 211 212 // coverage row: 213 addItemRow (item, columns); 214 } 215 216 217 final boolean deeper = (m_settings.getDepth () > item.getMetadata ().getTypeID ()); 218 219 // render child summary rows: 220 221 final String summaryTitle = m_srcView ? "COVERAGE BREAKDOWN BY SOURCE FILE" : "COVERAGE BREAKDOWN BY CLASS"; 222 addTitleRow (summaryTitle, 1, 1); 223 { 224 boolean headerDone = false; 225 final ItemComparator order = m_typeSortComparators [m_srcView ? SrcFileItem.getTypeMetadata ().getTypeID () : ClassItem.getTypeMetadata ().getTypeID ()]; 226 for (Iterator srcORclsFiles = item.getChildren (order); srcORclsFiles.hasNext (); ) 227 { 228 final IItem srcORcls = (IItem) srcORclsFiles.next (); 229 230 if (! headerDone) 231 { 232 // header row: 233 addHeaderRow (srcORcls, columns); 234 headerDone = true; 235 } 236 237 // coverage row: 238 addItemRow (srcORcls, columns); 239 240 if (deeper) m_queue.addLast (srcORcls); 241 } 242 } 243 244 return ctx; 245 } 246 visit(final SrcFileItem item, final Object ctx)247 public Object visit (final SrcFileItem item, final Object ctx) 248 { 249 final int [] columns = m_settings.getColumnOrder (); 250 251 line (); 252 253 // coverage summary row: 254 addTitleRow ("COVERAGE SUMMARY FOR SOURCE FILE [".concat (item.getName ()).concat ("]"), 0, 1); 255 { 256 // header row: 257 addHeaderRow (item, columns); 258 259 // coverage row: 260 addItemRow (item, columns); 261 } 262 263 // render child summary rows: 264 addTitleRow ("COVERAGE BREAKDOWN BY CLASS AND METHOD", 1, 1); 265 { 266 boolean headerDone = false; 267 final ItemComparator order = m_typeSortComparators [ClassItem.getTypeMetadata ().getTypeID ()]; 268 for (Iterator classes = item.getChildren (order); classes.hasNext (); ) 269 { 270 final IItem cls = (IItem) classes.next (); 271 272 if (! headerDone) 273 { 274 // header row: 275 addHeaderRow (cls, columns); 276 headerDone = true; 277 } 278 279 // coverage row: 280 //addItemRow (child, columns); 281 282 addTitleRow ("class [" + cls.getName () + "] methods", 0, 0); 283 284 // TODO: select the right comparator here 285 final ItemComparator order2 = m_typeSortComparators [MethodItem.getTypeMetadata ().getTypeID ()]; 286 for (Iterator methods = cls.getChildren (order2); methods.hasNext (); ) 287 { 288 final MethodItem method = (MethodItem) methods.next (); 289 290 addItemRow (method, columns); 291 } 292 } 293 } 294 295 return ctx; 296 } 297 visit(final ClassItem item, final Object ctx)298 public Object visit (final ClassItem item, final Object ctx) 299 { 300 final int [] columns = m_settings.getColumnOrder (); 301 302 line (); 303 304 // coverage summary row: 305 addTitleRow ("COVERAGE SUMMARY FOR CLASS [".concat (item.getName ()).concat ("]"), 0, 1); 306 { 307 // header row: 308 addHeaderRow (item, columns); 309 310 // coverage row: 311 addItemRow (item, columns); 312 } 313 314 // render child summary rows: 315 addTitleRow ("COVERAGE BREAKDOWN BY METHOD", 1, 1); 316 { 317 final ItemComparator order = m_typeSortComparators [MethodItem.getTypeMetadata ().getTypeID ()]; 318 for (Iterator methods = item.getChildren (order); methods.hasNext (); ) 319 { 320 final IItem method = (IItem) methods.next (); 321 322 // coverage row: 323 addItemRow (method, columns); 324 } 325 } 326 327 return ctx; 328 } 329 330 // protected: ............................................................. 331 332 // package: ............................................................... 333 334 // private: ............................................................... 335 336 addTitleRow(final String text, final int hlines, final int flines)337 private void addTitleRow (final String text, final int hlines, final int flines) 338 { 339 for (int i = 0; i < hlines; ++ i) eol (); 340 row (new StringBuffer (text).append (":")); 341 for (int i = 0; i < flines; ++ i) eol (); 342 } 343 addHeaderRow(final IItem item, final int [] columns)344 private void addHeaderRow (final IItem item, final int [] columns) 345 { 346 if ($assert.ENABLED) 347 { 348 $assert.ASSERT (item != null, "null input: item"); 349 $assert.ASSERT (columns != null, "null input: columns"); 350 } 351 352 // header row: 353 final StringBuffer buf = new StringBuffer (); 354 355 for (int c = 0, cLimit = columns.length; c < cLimit; ++ c) 356 { 357 final int attrID = columns [c]; 358 final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ()); 359 360 if (attr != null) 361 { 362 buf.append ('['); 363 buf.append (attr.getName ()); 364 buf.append (']'); 365 } 366 if (c != cLimit - 1) buf.append (m_separator); 367 } 368 369 row (buf); 370 } 371 372 373 /* 374 * No header row, just data rows. 375 */ addItemRow(final IItem item, final int [] columns)376 private void addItemRow (final IItem item, final int [] columns) 377 { 378 if ($assert.ENABLED) 379 { 380 $assert.ASSERT (item != null, "null input: item"); 381 $assert.ASSERT (columns != null, "null input: columns"); 382 } 383 384 final StringBuffer buf = new StringBuffer (11); // TODO: reuse a buffer 385 386 for (int c = 0, cLimit = columns.length; c < cLimit; ++ c) 387 { 388 final int attrID = columns [c]; 389 final IItemAttribute attr = item.getAttribute (attrID, m_settings.getUnitsType ()); 390 391 if (attr != null) 392 { 393 boolean fail = (m_metrics [attrID] > 0) && ! attr.passes (item, m_metrics [attrID]); 394 395 if (fail) 396 { 397 //buf.append ('('); 398 //buf.append ("! "); 399 attr.format (item, buf); 400 buf.append ('!'); 401 //buf.append (')'); 402 } 403 else 404 { 405 attr.format (item, buf); 406 } 407 } 408 if (c != cLimit - 1) buf.append (m_separator); 409 } 410 411 row (buf); 412 } 413 414 row(final StringBuffer str)415 private void row (final StringBuffer str) 416 { 417 if ($assert.ENABLED) $assert.ASSERT (str != null, "str = null"); 418 419 try 420 { 421 m_out.write (str.toString ()); 422 m_out.newLine (); 423 } 424 catch (IOException ioe) 425 { 426 throw new EMMARuntimeException (IAppErrorCodes.REPORT_IO_FAILURE, ioe); 427 } 428 } 429 row(final String str)430 private void row (final String str) 431 { 432 if ($assert.ENABLED) $assert.ASSERT (str != null, "str = null"); 433 434 try 435 { 436 m_out.write (str); 437 m_out.newLine (); 438 } 439 catch (IOException ioe) 440 { 441 throw new EMMARuntimeException (IAppErrorCodes.REPORT_IO_FAILURE, ioe); 442 } 443 } 444 line()445 private void line () 446 { 447 try 448 { 449 m_out.write (LINE); 450 m_out.newLine (); 451 } 452 catch (IOException ioe) 453 { 454 throw new EMMARuntimeException (IAppErrorCodes.REPORT_IO_FAILURE, ioe); 455 } 456 } 457 eol()458 private void eol () 459 { 460 try 461 { 462 m_out.newLine (); 463 } 464 catch (IOException ioe) 465 { 466 throw new EMMARuntimeException (IAppErrorCodes.REPORT_IO_FAILURE, ioe); 467 } 468 } 469 close()470 private void close () 471 { 472 if (m_out != null) 473 { 474 try 475 { 476 m_out.flush (); 477 m_out.close (); 478 } 479 catch (IOException ioe) 480 { 481 throw new EMMARuntimeException (IAppErrorCodes.REPORT_IO_FAILURE, ioe); 482 } 483 finally 484 { 485 m_out = null; 486 } 487 } 488 } 489 openOutFile(final File file, final String encoding, final boolean mkdirs)490 private void openOutFile (final File file, final String encoding, final boolean mkdirs) 491 { 492 try 493 { 494 if (mkdirs) 495 { 496 final File parent = file.getParentFile (); 497 if (parent != null) parent.mkdirs (); 498 } 499 500 m_out = new BufferedWriter (new OutputStreamWriter (new FileOutputStream (file), encoding), IO_BUF_SIZE); 501 } 502 catch (UnsupportedEncodingException uee) 503 { 504 // TODO: error code 505 throw new EMMARuntimeException (uee); 506 } 507 // note: in J2SDK 1.3 FileOutputStream constructor's throws clause 508 // was narrowed to FileNotFoundException: 509 catch (IOException fnfe) // FileNotFoundException 510 { 511 // TODO: error code 512 throw new EMMARuntimeException (fnfe); 513 } 514 } 515 516 517 private char m_separator = '\t'; // TODO: set this 518 519 private LinkedList /* IITem */ m_queue; 520 private BufferedWriter m_out; 521 522 private static final String TYPE = "txt"; 523 private static final String LINE = "-------------------------------------------------------------------------------"; 524 525 private static final int IO_BUF_SIZE = 32 * 1024; 526 527 } // end of class 528 // ----------------------------------------------------------------------------