| /* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. |
| * |
| * This program and the accompanying materials are made available under |
| * the terms of the Common Public License v1.0 which accompanies this distribution, |
| * and is available at http://www.eclipse.org/legal/cpl-v10.html |
| * |
| * $Id: ReportProperties.java,v 1.1.1.1 2004/05/09 16:57:38 vlad_r Exp $ |
| */ |
| package com.vladium.emma.report; |
| |
| import java.io.File; |
| import java.util.HashSet; |
| import java.util.Set; |
| import java.util.StringTokenizer; |
| |
| import com.vladium.util.Files; |
| import com.vladium.util.IProperties; |
| import com.vladium.util.IntIntMap; |
| import com.vladium.util.IntVector; |
| import com.vladium.util.ObjectIntMap; |
| import com.vladium.util.Property; |
| import com.vladium.util.asserts.$assert; |
| import com.vladium.emma.IAppErrorCodes; |
| import com.vladium.emma.EMMARuntimeException; |
| |
| // ---------------------------------------------------------------------------- |
| /** |
| * @author Vlad Roubtsov, (C) 2003 |
| */ |
| public |
| abstract class ReportProperties implements IAppErrorCodes |
| { |
| // public: ................................................................ |
| |
| |
| public static final IProperties.IMapper REPORT_PROPERTY_MAPPER; // set in <clinit> |
| |
| |
| public static final class ParsedProperties |
| { |
| public void setOutEncoding (final String outEncoding) |
| { |
| if ($assert.ENABLED) $assert.ASSERT (outEncoding != null, "null input: outEncoding"); |
| |
| m_outEncoding = outEncoding; |
| } |
| |
| public String getOutEncoding () |
| { |
| return m_outEncoding; |
| } |
| |
| public void setOutDir (final File outDir) |
| { |
| if ($assert.ENABLED) $assert.ASSERT (outDir != null, "null input: outDir"); |
| |
| m_outDir = outDir; |
| } |
| |
| public File getOutDir () |
| { |
| return m_outDir; |
| } |
| |
| public void setOutFile (final File outFile) |
| { |
| if ($assert.ENABLED) $assert.ASSERT (outFile != null, "null input: outFile"); |
| |
| m_outFile = outFile; |
| } |
| |
| public File getOutFile () |
| { |
| return m_outFile; |
| } |
| |
| public void setUnitsType (final int unitsType) |
| { |
| if ($assert.ENABLED) $assert.ASSERT (unitsType >= IItemAttribute.UNITS_COUNT && unitsType <= IItemAttribute.UNITS_INSTR, "invalid units type: " + unitsType); |
| |
| m_unitsType = unitsType; |
| } |
| |
| public int getUnitsType () |
| { |
| return m_unitsType; |
| } |
| |
| public void setViewType (final int viewType) |
| { |
| if ($assert.ENABLED) $assert.ASSERT (viewType >= IReportDataView.HIER_CLS_VIEW && viewType <= IReportDataView.HIER_SRC_VIEW, "invalid view type: " + viewType); |
| |
| m_viewType = viewType; |
| } |
| |
| public int getViewType () |
| { |
| return m_viewType; |
| } |
| |
| public void setDepth (final int depth) |
| { |
| if ($assert.ENABLED) $assert.ASSERT (depth >= IItemMetadata.TYPE_ID_ALL && depth <= IItemMetadata.TYPE_ID_METHOD, "invalid depth: " + depth); |
| |
| m_depth = depth; |
| } |
| |
| public int getDepth() |
| { |
| return m_depth; |
| } |
| |
| public void setHideClasses (final boolean hideClasses) |
| { |
| m_hideClasses = hideClasses; |
| } |
| |
| public boolean getHideClasses () |
| { |
| return m_hideClasses; |
| } |
| |
| public void setColumnOrder (final int [] columnOrder) |
| { |
| if ($assert.ENABLED) $assert.ASSERT (columnOrder != null && columnOrder.length != 0, "null/empty input: outEncoding"); |
| |
| m_columnOrder = columnOrder; |
| } |
| |
| public int [] getColumnOrder () |
| { |
| return m_columnOrder; |
| } |
| |
| public void setSortOrder (final int [] sortOrder) |
| { |
| if ($assert.ENABLED) $assert.ASSERT (sortOrder != null, "null input: sortOrder"); |
| |
| m_sortOrder = sortOrder; |
| } |
| |
| public int [] getSortOrder () |
| { |
| return m_sortOrder; |
| } |
| |
| public void setMetrics (final IntIntMap metrics) |
| { |
| if ($assert.ENABLED) $assert.ASSERT (metrics != null, "null input: metrics"); |
| |
| m_metrics = metrics; |
| } |
| |
| public IntIntMap getMetrics () |
| { |
| return m_metrics; |
| } |
| |
| // TODO: toString/logging |
| |
| void validate () throws IllegalArgumentException |
| { |
| if ($assert.ENABLED) |
| { |
| $assert.ASSERT (m_outEncoding != null, "m_outEncoding not set"); |
| $assert.ASSERT (m_outDir != null || m_outFile != null, "either m_outDir or m_outFile must be set"); |
| $assert.ASSERT (m_columnOrder != null, "m_columnOrder not set"); |
| $assert.ASSERT (m_sortOrder != null, "m_sortOrder not set"); |
| $assert.ASSERT (m_metrics != null, "m_metrics not set"); |
| } |
| } |
| |
| |
| private String m_outEncoding; |
| private File m_outDir; |
| private File m_outFile; |
| |
| private int m_unitsType; |
| private int m_viewType; |
| |
| private boolean m_hideClasses; |
| private int m_depth; |
| |
| // TODO: fraction/number format strings... |
| |
| private int [] m_columnOrder; // attribute IDs [order indicates column order] |
| private int [] m_sortOrder; // if m_sortOrder[i+1]>0 , sort m_columnOrder[m_sortOrder[i]] in ascending order |
| private IntIntMap m_metrics; // pass criteria (column attribute ID -> metric) |
| |
| } // end of nested class |
| |
| |
| // /** |
| // * Creates a property view specific to 'reportType' report type. |
| // * |
| // * @param appProperties |
| // * @param reportType |
| // * @return |
| // */ |
| // public static Properties getReportProperties (final Properties appProperties, final String reportType) |
| // { |
| // if ((reportType == null) || (reportType.length () == 0)) |
| // throw new IllegalArgumentException ("null/empty input: reportType"); |
| // |
| // if (appProperties == null) return new XProperties (); |
| // |
| // return new ReportPropertyLookup (appProperties, reportType); |
| // } |
| |
| |
| // /** |
| // * @param type [null/empty indicates type-neutral property] |
| // */ |
| // public static String getReportProperty (final String type, final Map properties, final String key) |
| // { |
| // if (properties == null) throw new IllegalArgumentException ("null input: properties"); |
| // if (key == null) throw new IllegalArgumentException ("null input: key"); |
| // |
| // String fullKey; |
| // |
| // if ((type == null) || (type.length () == 0)) |
| // fullKey = IReportParameters.PREFIX.concat (key); |
| // else |
| // { |
| // fullKey = IReportParameters.PREFIX.concat (type).concat (".").concat (key); |
| // |
| // if (! properties.containsKey (fullKey)) // default to type-neutral lookup |
| // fullKey = IReportParameters.PREFIX.concat (key); |
| // } |
| // |
| // return (String) properties.get (fullKey); |
| // } |
| // |
| // public static String getReportParameter (final String type, final Map properties, final String key, final String def) |
| // { |
| // final String value = getReportProperty (type, properties, key); |
| // |
| // return (value == null) ? def : value; |
| // } |
| |
| |
| public static ParsedProperties parseProperties (final IProperties properties, final String type) |
| { |
| if ($assert.ENABLED) $assert.ASSERT (properties != null, "properties = null"); |
| |
| final ParsedProperties result = new ParsedProperties (); |
| { |
| result.setOutEncoding (getReportProperty (properties, type, IReportProperties.OUT_ENCODING, false)); |
| } |
| |
| // TODO: outDirName is no longer supported |
| |
| { |
| final String outDirName = getReportProperty (properties, type, IReportProperties.OUT_DIR, true); |
| final String outFileName = getReportProperty (properties, type, IReportProperties.OUT_FILE, false); |
| |
| // renormalize the out dir and file combination: |
| |
| if (outFileName != null) |
| { |
| final File fullOutFile = Files.newFile (outDirName, outFileName); |
| |
| final File dir = fullOutFile.getParentFile (); |
| if (dir != null) result.setOutDir (dir); |
| |
| result.setOutFile (new File (fullOutFile.getName ())); |
| } |
| else if (outDirName != null) |
| { |
| result.setOutDir (new File (outDirName)); |
| } |
| } |
| |
| { |
| final String unitsType = getReportProperty (properties, type, IReportProperties.UNITS_TYPE, true, IReportProperties.DEFAULT_UNITS_TYPE); |
| result.setUnitsType (IReportProperties.COUNT_UNITS.equals (unitsType) ? IItemAttribute.UNITS_COUNT : IItemAttribute.UNITS_INSTR); |
| |
| // TODO: invalid setting not checked |
| } |
| { |
| /* view type is no longer a user-overridable property [it is driven by SourceFile attribute presence] |
| |
| final String viewType = getReportProperty (properties, type, IReportProperties.VIEW_TYPE, IReportProperties.DEFAULT_VIEW_TYPE); |
| result.setViewType (IReportProperties.SRC_VIEW.equals (viewType) ? IReportDataView.HIER_SRC_VIEW : IReportDataView.HIER_CLS_VIEW); |
| */ |
| |
| result.setViewType (IReportDataView.HIER_SRC_VIEW); |
| } |
| |
| { |
| final String hideClasses = getReportProperty (properties, type, IReportProperties.HIDE_CLASSES, true, IReportProperties.DEFAULT_HIDE_CLASSES); |
| result.setHideClasses (Property.toBoolean (hideClasses)); |
| |
| // TODO: log this |
| if (result.getViewType () == IReportDataView.HIER_CLS_VIEW) |
| result.setHideClasses (false); |
| } |
| { |
| final String depth = getReportProperty (properties, type, IReportProperties.DEPTH, false, IReportProperties.DEFAULT_DEPTH); |
| |
| if (IReportProperties.DEPTH_ALL.equals (depth)) |
| result.setDepth (AllItem.getTypeMetadata ().getTypeID ()); |
| else if (IReportProperties.DEPTH_PACKAGE.equals (depth)) |
| result.setDepth (PackageItem.getTypeMetadata ().getTypeID ()); |
| else if (IReportProperties.DEPTH_SRCFILE.equals (depth)) |
| result.setDepth (SrcFileItem.getTypeMetadata ().getTypeID ()); |
| else if (IReportProperties.DEPTH_CLASS.equals (depth)) |
| result.setDepth (ClassItem.getTypeMetadata ().getTypeID ()); |
| else if (IReportProperties.DEPTH_METHOD.equals (depth)) |
| result.setDepth (MethodItem.getTypeMetadata ().getTypeID ()); |
| else |
| // TODO: properly prefixes prop name |
| throw new EMMARuntimeException (INVALID_PARAMETER_VALUE, new Object [] {IReportProperties.DEPTH, depth}); |
| } |
| |
| if (result.getHideClasses () && |
| (result.getViewType () == IReportDataView.HIER_SRC_VIEW) && |
| (result.getDepth () == IItemMetadata.TYPE_ID_CLASS)) |
| { |
| result.setDepth (IItemMetadata.TYPE_ID_SRCFILE); |
| } |
| |
| final Set /* String */ columnNames = new HashSet (); |
| { |
| final String columnList = getReportProperty (properties, type, IReportProperties.COLUMNS, false, IReportProperties.DEFAULT_COLUMNS); |
| final IntVector _columns = new IntVector (); |
| |
| final int [] out = new int [1]; |
| |
| for (StringTokenizer tokenizer = new StringTokenizer (columnList, ","); tokenizer.hasMoreTokens (); ) |
| { |
| final String columnName = tokenizer.nextToken ().trim (); |
| if (! COLUMNS.get (columnName, out)) |
| { |
| // TODO: generate the entire enum list in the err msg |
| throw new EMMARuntimeException (INVALID_COLUMN_NAME, new Object [] {columnName}); |
| } |
| |
| if (! REMOVE_DUPLICATE_COLUMNS || ! columnNames.contains (columnName)) |
| { |
| columnNames.add (columnName); |
| _columns.add (out [0]); |
| } |
| } |
| |
| result.setColumnOrder (_columns.values ()); |
| } |
| // [assertion: columnNames contains all columns for the report (some |
| // may get removed later by individual report generators if some debug info |
| // is missing)] |
| |
| { |
| final String sortList = getReportProperty (properties, type, IReportProperties.SORT, false, IReportProperties.DEFAULT_SORT); |
| final IntVector _sort = new IntVector (); |
| |
| final int [] out = new int [1]; |
| |
| for (StringTokenizer tokenizer = new StringTokenizer (sortList, ","); tokenizer.hasMoreTokens (); ) |
| { |
| final String sortSpec = tokenizer.nextToken ().trim (); |
| final String columnName; |
| final int dir; |
| |
| switch (sortSpec.charAt (0)) |
| { |
| case IReportProperties.ASC: |
| { |
| dir = +1; |
| columnName = sortSpec.substring (1); |
| } |
| break; |
| |
| case IReportProperties.DESC: |
| { |
| dir = -1; |
| columnName = sortSpec.substring (1); |
| } |
| break; |
| |
| default: |
| { |
| dir = +1; |
| columnName = sortSpec; |
| } |
| break; |
| |
| } // end of switch |
| |
| // silently ignore columns not in the column list: |
| if (columnNames.contains (columnName)) |
| { |
| COLUMNS.get (columnName, out); |
| |
| _sort.add (out [0]); // sort attribute ID |
| _sort.add (dir); // sort direction |
| } |
| |
| result.setSortOrder (_sort.values ()); |
| } |
| } |
| { |
| final String metricList = getReportProperty (properties, type, IReportProperties.METRICS, true, IReportProperties.DEFAULT_METRICS); |
| final IntIntMap _metrics = new IntIntMap (); |
| |
| final int [] out = new int [1]; |
| |
| // TODO: perhaps should throw on invalid input here |
| for (StringTokenizer tokenizer = new StringTokenizer (metricList, ","); tokenizer.hasMoreTokens (); ) |
| { |
| final String metricSpec = tokenizer.nextToken ().trim (); |
| final String columnName; |
| final double criterion; |
| |
| final int separator = metricSpec.indexOf (IReportProperties.MSEPARATOR); |
| if (separator > 0) // silently ignore invalid entries |
| { |
| // silently ignore invalid cutoff values: |
| try |
| { |
| criterion = Double.parseDouble (metricSpec.substring (separator + 1)); |
| if ((criterion < 0.0) || (criterion > 101.0)) continue; |
| } |
| catch (NumberFormatException nfe) |
| { |
| nfe.printStackTrace (System.out); |
| continue; |
| } |
| |
| columnName = metricSpec.substring (0, separator); |
| |
| // silently ignore columns not in the column list: |
| if (columnNames.contains (columnName)) |
| { |
| COLUMNS.get (columnName, out); |
| |
| _metrics.put (out [0], (int) Math.round (((criterion * IItem.PRECISION) / 100.0))); |
| } |
| } |
| } |
| |
| result.setMetrics (_metrics); |
| } |
| |
| result.validate (); |
| |
| return result; |
| } |
| |
| |
| // protected: ............................................................. |
| |
| // package: ............................................................... |
| |
| // private: ............................................................... |
| |
| |
| private static final class ReportPropertyMapper implements IProperties.IMapper |
| { |
| public String getMappedKey (final String key) |
| { |
| if (key != null) |
| { |
| if (key.startsWith (IReportProperties.PREFIX)) |
| { |
| final int secondDot = key.indexOf ('.', IReportProperties.PREFIX.length ()); |
| if (secondDot > 0) |
| { |
| // TODO: make this more precise (actually check the report type value string) |
| |
| return IReportProperties.PREFIX.concat (key.substring (secondDot + 1)); |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| } // end of nested class |
| |
| |
| // private static final class ReportPropertyLookup extends XProperties |
| // { |
| // // note: due to incredibly stupid coding in java.util.Properties |
| // // (getProperty() uses a non-virtual call to super.get(), while propertyNames() |
| // // uses a virtual call to the same method instead of delegating to getProperty()) |
| // // I must override both methods below |
| // |
| // public String getProperty (String key) |
| // { |
| // return (String) get (key); |
| // } |
| // |
| // // TODO: this kind of lookup makes the property listing confusing |
| // |
| // public Object get (final Object _key) |
| // { |
| // if (! (_key instanceof String)) return null; |
| // |
| // String key = (String) _key; |
| // |
| // if (key.startsWith (IReportProperties.PREFIX)) |
| // key = key.substring (IReportProperties.PREFIX.length ()); |
| // |
| // if (key.startsWith (m_reportType)) |
| // key = key.substring (m_reportType.length () + 1); |
| // |
| // String fullKey = IReportProperties.PREFIX.concat (m_reportType).concat (".").concat (key); |
| // |
| // String result = defaults.getProperty (fullKey, null); |
| // if (result != null) return result; |
| // |
| // // fall back to report type-neutral namespace: |
| // fullKey = IReportProperties.PREFIX.concat (key); |
| // |
| // result = defaults.getProperty (fullKey, null); |
| // if (result != null) return result; |
| // |
| // return null; |
| // } |
| // |
| // |
| // ReportPropertyLookup (final Properties appProperties, final String reportType) |
| // { |
| // super (appProperties); |
| // |
| // m_reportType = reportType; |
| // } |
| // |
| // |
| // private final String m_reportType; // never null or empty [factory-ensured] |
| // |
| // } // end of nested class |
| |
| |
| private ReportProperties () {} // prevent subclassing |
| |
| |
| private static String getReportProperty (final IProperties properties, final String type, final String key, final boolean allowBlank) |
| { |
| return getReportProperty (properties, type, key, allowBlank, null); |
| } |
| |
| private static String getReportProperty (final IProperties properties, final String type, final String key, final boolean allowBlank, final String dflt) |
| { |
| if ($assert.ENABLED) $assert.ASSERT (properties != null, "null input: properties"); |
| if ($assert.ENABLED) $assert.ASSERT (key != null, "null input: key"); |
| |
| final String result = properties.getProperty (IReportProperties.PREFIX.concat (type).concat (".").concat (key), dflt); |
| |
| if (! allowBlank && (result != null) && (result.trim ().length () == 0)) |
| return dflt; |
| else |
| return result; |
| } |
| |
| |
| private static final boolean REMOVE_DUPLICATE_COLUMNS = true; |
| private static final ObjectIntMap /* col name:String -> metadata:IItemMetadata */ COLUMNS; // set in <clinit> |
| |
| static |
| { |
| REPORT_PROPERTY_MAPPER = new ReportPropertyMapper (); |
| |
| final ObjectIntMap columns = new ObjectIntMap (); |
| |
| columns.put (IReportProperties.ITEM_NAME_COLUMN, IItemAttribute.ATTRIBUTE_NAME_ID); |
| columns.put (IReportProperties.CLASS_COVERAGE_COLUMN, IItemAttribute.ATTRIBUTE_CLASS_COVERAGE_ID); |
| columns.put (IReportProperties.METHOD_COVERAGE_COLUMN, IItemAttribute.ATTRIBUTE_METHOD_COVERAGE_ID); |
| columns.put (IReportProperties.BLOCK_COVERAGE_COLUMN, IItemAttribute.ATTRIBUTE_BLOCK_COVERAGE_ID); |
| columns.put (IReportProperties.LINE_COVERAGE_COLUMN, IItemAttribute.ATTRIBUTE_LINE_COVERAGE_ID); |
| |
| COLUMNS = columns; |
| } |
| |
| } // end of class |
| // ---------------------------------------------------------------------------- |