• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 // ----------------------------------------------------------------------------