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: ReportProperties.java,v 1.1.1.1 2004/05/09 16:57:38 vlad_r Exp $ 8 */ 9 package com.vladium.emma.report; 10 11 import java.io.File; 12 import java.util.HashSet; 13 import java.util.Set; 14 import java.util.StringTokenizer; 15 16 import com.vladium.util.Files; 17 import com.vladium.util.IProperties; 18 import com.vladium.util.IntIntMap; 19 import com.vladium.util.IntVector; 20 import com.vladium.util.ObjectIntMap; 21 import com.vladium.util.Property; 22 import com.vladium.util.asserts.$assert; 23 import com.vladium.emma.IAppErrorCodes; 24 import com.vladium.emma.EMMARuntimeException; 25 26 // ---------------------------------------------------------------------------- 27 /** 28 * @author Vlad Roubtsov, (C) 2003 29 */ 30 public 31 abstract class ReportProperties implements IAppErrorCodes 32 { 33 // public: ................................................................ 34 35 36 public static final IProperties.IMapper REPORT_PROPERTY_MAPPER; // set in <clinit> 37 38 39 public static final class ParsedProperties 40 { setOutEncoding(final String outEncoding)41 public void setOutEncoding (final String outEncoding) 42 { 43 if ($assert.ENABLED) $assert.ASSERT (outEncoding != null, "null input: outEncoding"); 44 45 m_outEncoding = outEncoding; 46 } 47 getOutEncoding()48 public String getOutEncoding () 49 { 50 return m_outEncoding; 51 } 52 setOutDir(final File outDir)53 public void setOutDir (final File outDir) 54 { 55 if ($assert.ENABLED) $assert.ASSERT (outDir != null, "null input: outDir"); 56 57 m_outDir = outDir; 58 } 59 getOutDir()60 public File getOutDir () 61 { 62 return m_outDir; 63 } 64 setOutFile(final File outFile)65 public void setOutFile (final File outFile) 66 { 67 if ($assert.ENABLED) $assert.ASSERT (outFile != null, "null input: outFile"); 68 69 m_outFile = outFile; 70 } 71 getOutFile()72 public File getOutFile () 73 { 74 return m_outFile; 75 } 76 setUnitsType(final int unitsType)77 public void setUnitsType (final int unitsType) 78 { 79 if ($assert.ENABLED) $assert.ASSERT (unitsType >= IItemAttribute.UNITS_COUNT && unitsType <= IItemAttribute.UNITS_INSTR, "invalid units type: " + unitsType); 80 81 m_unitsType = unitsType; 82 } 83 getUnitsType()84 public int getUnitsType () 85 { 86 return m_unitsType; 87 } 88 setViewType(final int viewType)89 public void setViewType (final int viewType) 90 { 91 if ($assert.ENABLED) $assert.ASSERT (viewType >= IReportDataView.HIER_CLS_VIEW && viewType <= IReportDataView.HIER_SRC_VIEW, "invalid view type: " + viewType); 92 93 m_viewType = viewType; 94 } 95 getViewType()96 public int getViewType () 97 { 98 return m_viewType; 99 } 100 setDepth(final int depth)101 public void setDepth (final int depth) 102 { 103 if ($assert.ENABLED) $assert.ASSERT (depth >= IItemMetadata.TYPE_ID_ALL && depth <= IItemMetadata.TYPE_ID_METHOD, "invalid depth: " + depth); 104 105 m_depth = depth; 106 } 107 getDepth()108 public int getDepth() 109 { 110 return m_depth; 111 } 112 setHideClasses(final boolean hideClasses)113 public void setHideClasses (final boolean hideClasses) 114 { 115 m_hideClasses = hideClasses; 116 } 117 getHideClasses()118 public boolean getHideClasses () 119 { 120 return m_hideClasses; 121 } 122 setColumnOrder(final int [] columnOrder)123 public void setColumnOrder (final int [] columnOrder) 124 { 125 if ($assert.ENABLED) $assert.ASSERT (columnOrder != null && columnOrder.length != 0, "null/empty input: outEncoding"); 126 127 m_columnOrder = columnOrder; 128 } 129 getColumnOrder()130 public int [] getColumnOrder () 131 { 132 return m_columnOrder; 133 } 134 setSortOrder(final int [] sortOrder)135 public void setSortOrder (final int [] sortOrder) 136 { 137 if ($assert.ENABLED) $assert.ASSERT (sortOrder != null, "null input: sortOrder"); 138 139 m_sortOrder = sortOrder; 140 } 141 getSortOrder()142 public int [] getSortOrder () 143 { 144 return m_sortOrder; 145 } 146 setMetrics(final IntIntMap metrics)147 public void setMetrics (final IntIntMap metrics) 148 { 149 if ($assert.ENABLED) $assert.ASSERT (metrics != null, "null input: metrics"); 150 151 m_metrics = metrics; 152 } 153 getMetrics()154 public IntIntMap getMetrics () 155 { 156 return m_metrics; 157 } 158 159 // TODO: toString/logging 160 validate()161 void validate () throws IllegalArgumentException 162 { 163 if ($assert.ENABLED) 164 { 165 $assert.ASSERT (m_outEncoding != null, "m_outEncoding not set"); 166 $assert.ASSERT (m_outDir != null || m_outFile != null, "either m_outDir or m_outFile must be set"); 167 $assert.ASSERT (m_columnOrder != null, "m_columnOrder not set"); 168 $assert.ASSERT (m_sortOrder != null, "m_sortOrder not set"); 169 $assert.ASSERT (m_metrics != null, "m_metrics not set"); 170 } 171 } 172 173 174 private String m_outEncoding; 175 private File m_outDir; 176 private File m_outFile; 177 178 private int m_unitsType; 179 private int m_viewType; 180 181 private boolean m_hideClasses; 182 private int m_depth; 183 184 // TODO: fraction/number format strings... 185 186 private int [] m_columnOrder; // attribute IDs [order indicates column order] 187 private int [] m_sortOrder; // if m_sortOrder[i+1]>0 , sort m_columnOrder[m_sortOrder[i]] in ascending order 188 private IntIntMap m_metrics; // pass criteria (column attribute ID -> metric) 189 190 } // end of nested class 191 192 193 // /** 194 // * Creates a property view specific to 'reportType' report type. 195 // * 196 // * @param appProperties 197 // * @param reportType 198 // * @return 199 // */ 200 // public static Properties getReportProperties (final Properties appProperties, final String reportType) 201 // { 202 // if ((reportType == null) || (reportType.length () == 0)) 203 // throw new IllegalArgumentException ("null/empty input: reportType"); 204 // 205 // if (appProperties == null) return new XProperties (); 206 // 207 // return new ReportPropertyLookup (appProperties, reportType); 208 // } 209 210 211 // /** 212 // * @param type [null/empty indicates type-neutral property] 213 // */ 214 // public static String getReportProperty (final String type, final Map properties, final String key) 215 // { 216 // if (properties == null) throw new IllegalArgumentException ("null input: properties"); 217 // if (key == null) throw new IllegalArgumentException ("null input: key"); 218 // 219 // String fullKey; 220 // 221 // if ((type == null) || (type.length () == 0)) 222 // fullKey = IReportParameters.PREFIX.concat (key); 223 // else 224 // { 225 // fullKey = IReportParameters.PREFIX.concat (type).concat (".").concat (key); 226 // 227 // if (! properties.containsKey (fullKey)) // default to type-neutral lookup 228 // fullKey = IReportParameters.PREFIX.concat (key); 229 // } 230 // 231 // return (String) properties.get (fullKey); 232 // } 233 // 234 // public static String getReportParameter (final String type, final Map properties, final String key, final String def) 235 // { 236 // final String value = getReportProperty (type, properties, key); 237 // 238 // return (value == null) ? def : value; 239 // } 240 241 parseProperties(final IProperties properties, final String type)242 public static ParsedProperties parseProperties (final IProperties properties, final String type) 243 { 244 if ($assert.ENABLED) $assert.ASSERT (properties != null, "properties = null"); 245 246 final ParsedProperties result = new ParsedProperties (); 247 { 248 result.setOutEncoding (getReportProperty (properties, type, IReportProperties.OUT_ENCODING, false)); 249 } 250 251 // TODO: outDirName is no longer supported 252 253 { 254 final String outDirName = getReportProperty (properties, type, IReportProperties.OUT_DIR, true); 255 final String outFileName = getReportProperty (properties, type, IReportProperties.OUT_FILE, false); 256 257 // renormalize the out dir and file combination: 258 259 if (outFileName != null) 260 { 261 final File fullOutFile = Files.newFile (outDirName, outFileName); 262 263 final File dir = fullOutFile.getParentFile (); 264 if (dir != null) result.setOutDir (dir); 265 266 result.setOutFile (new File (fullOutFile.getName ())); 267 } 268 else if (outDirName != null) 269 { 270 result.setOutDir (new File (outDirName)); 271 } 272 } 273 274 { 275 final String unitsType = getReportProperty (properties, type, IReportProperties.UNITS_TYPE, true, IReportProperties.DEFAULT_UNITS_TYPE); 276 result.setUnitsType (IReportProperties.COUNT_UNITS.equals (unitsType) ? IItemAttribute.UNITS_COUNT : IItemAttribute.UNITS_INSTR); 277 278 // TODO: invalid setting not checked 279 } 280 { 281 /* view type is no longer a user-overridable property [it is driven by SourceFile attribute presence] 282 283 final String viewType = getReportProperty (properties, type, IReportProperties.VIEW_TYPE, IReportProperties.DEFAULT_VIEW_TYPE); 284 result.setViewType (IReportProperties.SRC_VIEW.equals (viewType) ? IReportDataView.HIER_SRC_VIEW : IReportDataView.HIER_CLS_VIEW); 285 */ 286 287 result.setViewType (IReportDataView.HIER_SRC_VIEW); 288 } 289 290 { 291 final String hideClasses = getReportProperty (properties, type, IReportProperties.HIDE_CLASSES, true, IReportProperties.DEFAULT_HIDE_CLASSES); 292 result.setHideClasses (Property.toBoolean (hideClasses)); 293 294 // TODO: log this 295 if (result.getViewType () == IReportDataView.HIER_CLS_VIEW) 296 result.setHideClasses (false); 297 } 298 { 299 final String depth = getReportProperty (properties, type, IReportProperties.DEPTH, false, IReportProperties.DEFAULT_DEPTH); 300 301 if (IReportProperties.DEPTH_ALL.equals (depth)) 302 result.setDepth (AllItem.getTypeMetadata ().getTypeID ()); 303 else if (IReportProperties.DEPTH_PACKAGE.equals (depth)) 304 result.setDepth (PackageItem.getTypeMetadata ().getTypeID ()); 305 else if (IReportProperties.DEPTH_SRCFILE.equals (depth)) 306 result.setDepth (SrcFileItem.getTypeMetadata ().getTypeID ()); 307 else if (IReportProperties.DEPTH_CLASS.equals (depth)) 308 result.setDepth (ClassItem.getTypeMetadata ().getTypeID ()); 309 else if (IReportProperties.DEPTH_METHOD.equals (depth)) 310 result.setDepth (MethodItem.getTypeMetadata ().getTypeID ()); 311 else 312 // TODO: properly prefixes prop name 313 throw new EMMARuntimeException (INVALID_PARAMETER_VALUE, new Object [] {IReportProperties.DEPTH, depth}); 314 } 315 316 if (result.getHideClasses () && 317 (result.getViewType () == IReportDataView.HIER_SRC_VIEW) && 318 (result.getDepth () == IItemMetadata.TYPE_ID_CLASS)) 319 { 320 result.setDepth (IItemMetadata.TYPE_ID_SRCFILE); 321 } 322 323 final Set /* String */ columnNames = new HashSet (); 324 { 325 final String columnList = getReportProperty (properties, type, IReportProperties.COLUMNS, false, IReportProperties.DEFAULT_COLUMNS); 326 final IntVector _columns = new IntVector (); 327 328 final int [] out = new int [1]; 329 330 for (StringTokenizer tokenizer = new StringTokenizer (columnList, ","); tokenizer.hasMoreTokens (); ) 331 { 332 final String columnName = tokenizer.nextToken ().trim (); 333 if (! COLUMNS.get (columnName, out)) 334 { 335 // TODO: generate the entire enum list in the err msg 336 throw new EMMARuntimeException (INVALID_COLUMN_NAME, new Object [] {columnName}); 337 } 338 339 if (! REMOVE_DUPLICATE_COLUMNS || ! columnNames.contains (columnName)) 340 { 341 columnNames.add (columnName); 342 _columns.add (out [0]); 343 } 344 } 345 346 result.setColumnOrder (_columns.values ()); 347 } 348 // [assertion: columnNames contains all columns for the report (some 349 // may get removed later by individual report generators if some debug info 350 // is missing)] 351 352 { 353 final String sortList = getReportProperty (properties, type, IReportProperties.SORT, false, IReportProperties.DEFAULT_SORT); 354 final IntVector _sort = new IntVector (); 355 356 final int [] out = new int [1]; 357 358 for (StringTokenizer tokenizer = new StringTokenizer (sortList, ","); tokenizer.hasMoreTokens (); ) 359 { 360 final String sortSpec = tokenizer.nextToken ().trim (); 361 final String columnName; 362 final int dir; 363 364 switch (sortSpec.charAt (0)) 365 { 366 case IReportProperties.ASC: 367 { 368 dir = +1; 369 columnName = sortSpec.substring (1); 370 } 371 break; 372 373 case IReportProperties.DESC: 374 { 375 dir = -1; 376 columnName = sortSpec.substring (1); 377 } 378 break; 379 380 default: 381 { 382 dir = +1; 383 columnName = sortSpec; 384 } 385 break; 386 387 } // end of switch 388 389 // silently ignore columns not in the column list: 390 if (columnNames.contains (columnName)) 391 { 392 COLUMNS.get (columnName, out); 393 394 _sort.add (out [0]); // sort attribute ID 395 _sort.add (dir); // sort direction 396 } 397 398 result.setSortOrder (_sort.values ()); 399 } 400 } 401 { 402 final String metricList = getReportProperty (properties, type, IReportProperties.METRICS, true, IReportProperties.DEFAULT_METRICS); 403 final IntIntMap _metrics = new IntIntMap (); 404 405 final int [] out = new int [1]; 406 407 // TODO: perhaps should throw on invalid input here 408 for (StringTokenizer tokenizer = new StringTokenizer (metricList, ","); tokenizer.hasMoreTokens (); ) 409 { 410 final String metricSpec = tokenizer.nextToken ().trim (); 411 final String columnName; 412 final double criterion; 413 414 final int separator = metricSpec.indexOf (IReportProperties.MSEPARATOR); 415 if (separator > 0) // silently ignore invalid entries 416 { 417 // silently ignore invalid cutoff values: 418 try 419 { 420 criterion = Double.parseDouble (metricSpec.substring (separator + 1)); 421 if ((criterion < 0.0) || (criterion > 101.0)) continue; 422 } 423 catch (NumberFormatException nfe) 424 { 425 nfe.printStackTrace (System.out); 426 continue; 427 } 428 429 columnName = metricSpec.substring (0, separator); 430 431 // silently ignore columns not in the column list: 432 if (columnNames.contains (columnName)) 433 { 434 COLUMNS.get (columnName, out); 435 436 _metrics.put (out [0], (int) Math.round (((criterion * IItem.PRECISION) / 100.0))); 437 } 438 } 439 } 440 441 result.setMetrics (_metrics); 442 } 443 444 result.validate (); 445 446 return result; 447 } 448 449 450 // protected: ............................................................. 451 452 // package: ............................................................... 453 454 // private: ............................................................... 455 456 457 private static final class ReportPropertyMapper implements IProperties.IMapper 458 { getMappedKey(final String key)459 public String getMappedKey (final String key) 460 { 461 if (key != null) 462 { 463 if (key.startsWith (IReportProperties.PREFIX)) 464 { 465 final int secondDot = key.indexOf ('.', IReportProperties.PREFIX.length ()); 466 if (secondDot > 0) 467 { 468 // TODO: make this more precise (actually check the report type value string) 469 470 return IReportProperties.PREFIX.concat (key.substring (secondDot + 1)); 471 } 472 } 473 } 474 475 return null; 476 } 477 478 } // end of nested class 479 480 481 // private static final class ReportPropertyLookup extends XProperties 482 // { 483 // // note: due to incredibly stupid coding in java.util.Properties 484 // // (getProperty() uses a non-virtual call to super.get(), while propertyNames() 485 // // uses a virtual call to the same method instead of delegating to getProperty()) 486 // // I must override both methods below 487 // 488 // public String getProperty (String key) 489 // { 490 // return (String) get (key); 491 // } 492 // 493 // // TODO: this kind of lookup makes the property listing confusing 494 // 495 // public Object get (final Object _key) 496 // { 497 // if (! (_key instanceof String)) return null; 498 // 499 // String key = (String) _key; 500 // 501 // if (key.startsWith (IReportProperties.PREFIX)) 502 // key = key.substring (IReportProperties.PREFIX.length ()); 503 // 504 // if (key.startsWith (m_reportType)) 505 // key = key.substring (m_reportType.length () + 1); 506 // 507 // String fullKey = IReportProperties.PREFIX.concat (m_reportType).concat (".").concat (key); 508 // 509 // String result = defaults.getProperty (fullKey, null); 510 // if (result != null) return result; 511 // 512 // // fall back to report type-neutral namespace: 513 // fullKey = IReportProperties.PREFIX.concat (key); 514 // 515 // result = defaults.getProperty (fullKey, null); 516 // if (result != null) return result; 517 // 518 // return null; 519 // } 520 // 521 // 522 // ReportPropertyLookup (final Properties appProperties, final String reportType) 523 // { 524 // super (appProperties); 525 // 526 // m_reportType = reportType; 527 // } 528 // 529 // 530 // private final String m_reportType; // never null or empty [factory-ensured] 531 // 532 // } // end of nested class 533 534 ReportProperties()535 private ReportProperties () {} // prevent subclassing 536 537 getReportProperty(final IProperties properties, final String type, final String key, final boolean allowBlank)538 private static String getReportProperty (final IProperties properties, final String type, final String key, final boolean allowBlank) 539 { 540 return getReportProperty (properties, type, key, allowBlank, null); 541 } 542 getReportProperty(final IProperties properties, final String type, final String key, final boolean allowBlank, final String dflt)543 private static String getReportProperty (final IProperties properties, final String type, final String key, final boolean allowBlank, final String dflt) 544 { 545 if ($assert.ENABLED) $assert.ASSERT (properties != null, "null input: properties"); 546 if ($assert.ENABLED) $assert.ASSERT (key != null, "null input: key"); 547 548 final String result = properties.getProperty (IReportProperties.PREFIX.concat (type).concat (".").concat (key), dflt); 549 550 if (! allowBlank && (result != null) && (result.trim ().length () == 0)) 551 return dflt; 552 else 553 return result; 554 } 555 556 557 private static final boolean REMOVE_DUPLICATE_COLUMNS = true; 558 private static final ObjectIntMap /* col name:String -> metadata:IItemMetadata */ COLUMNS; // set in <clinit> 559 560 static 561 { 562 REPORT_PROPERTY_MAPPER = new ReportPropertyMapper (); 563 564 final ObjectIntMap columns = new ObjectIntMap (); 565 columns.put(IReportProperties.ITEM_NAME_COLUMN, IItemAttribute.ATTRIBUTE_NAME_ID)566 columns.put (IReportProperties.ITEM_NAME_COLUMN, IItemAttribute.ATTRIBUTE_NAME_ID); columns.put(IReportProperties.CLASS_COVERAGE_COLUMN, IItemAttribute.ATTRIBUTE_CLASS_COVERAGE_ID)567 columns.put (IReportProperties.CLASS_COVERAGE_COLUMN, IItemAttribute.ATTRIBUTE_CLASS_COVERAGE_ID); columns.put(IReportProperties.METHOD_COVERAGE_COLUMN, IItemAttribute.ATTRIBUTE_METHOD_COVERAGE_ID)568 columns.put (IReportProperties.METHOD_COVERAGE_COLUMN, IItemAttribute.ATTRIBUTE_METHOD_COVERAGE_ID); columns.put(IReportProperties.BLOCK_COVERAGE_COLUMN, IItemAttribute.ATTRIBUTE_BLOCK_COVERAGE_ID)569 columns.put (IReportProperties.BLOCK_COVERAGE_COLUMN, IItemAttribute.ATTRIBUTE_BLOCK_COVERAGE_ID); columns.put(IReportProperties.LINE_COVERAGE_COLUMN, IItemAttribute.ATTRIBUTE_LINE_COVERAGE_ID)570 columns.put (IReportProperties.LINE_COVERAGE_COLUMN, IItemAttribute.ATTRIBUTE_LINE_COVERAGE_ID); 571 572 COLUMNS = columns; 573 } 574 575 } // end of class 576 // ----------------------------------------------------------------------------