GMetrics-0.7/0000775000175000017500000000000012623566446012445 5ustar ebourgebourgGMetrics-0.7/docs/0000775000175000017500000000000012623566446013375 5ustar ebourgebourgGMetrics-0.7/docs/gmetrics-AfferentCouplingMetric.html0000644000175000017500000002450412463254277022476 0ustar ebourgebourg GMetrics - GMetrics - Afferent Coupling Metric

Afferent Coupling Metric

Calculates the Afferent Coupling for a package. This is a count of the number of other packages that depend on the classes within this package. It is an indicator of the package's responsibility ([1]). This is a package-level metric.

Implemented by the org.gmetrics.metric.coupling.AfferentCouplingMetric class.

Metric Properties

The following properties can be configured for this metric within a MetricSet. See Creating a MetricSet for information on the syntax of setting a metric property.

PropertyDescriptionDefault Value
enabledThis boolean property controls whether the metric is enabled. If set to false, then the metric is not included as part of the results or the output reports.true
functionsThis List<String> property contains the names of the functions to be calculated at the package level. and (potentially) included within the report(s). Valid values are: 
  - "value" - the value for the current package 
  - "total" - the total value for the current package and its descendant packages 
  - "average" - the average value for the current package and its descendant packages 
  - "referencedFromPackages" - the list of packages that reference classes within the current package
["value","average"]
ignorePackageNamesThe names of packages to ignore when calculating afferent coupling. This pattern string may contain wildcard characters ('*' or '?'); it may also contain more than one pattern, separated by commas.null

References


GMetrics-0.7/docs/gmetrics-ClassLineCountMetric.html0000644000175000017500000002213412463254300022111 0ustar ebourgebourg GMetrics - GMetrics - ClassLineCount Metric

ClassLineCount Metric

Metric for counting the number of lines for classes. Note that this metric measures the number of lines from the first line of the class to the last line of the class. The count includes all program statements, comment lines and whitespace.

Implemented by the org.gmetrics.metric.linecount.ClassLineCountMetric class.

Metric Properties

The following properties can be configured for this metric within a MetricSet. See Creating a MetricSet for information on the syntax of setting a metric property.

PropertyDescriptionDefault Value
enabledThis boolean property controls whether the metric is enabled. If set to false, then the metric is not included as part of the results or the output reports.true
functionsThis List<String> property contains the names of the functions to be calculated at the class and package levels and (potentially) included within the report(s). Valid values are: - "total" - "average" - "minimum" - "maximum"["total","average"]

References

  • The The C2 Wiki page for Lines of Code has a good discussion of the perils of using (abusing) lines of code as a measure of just about anything. Note: That page specifically refers to measuring non-comment lines. This metric only measures total lines, including statements, comments and whitespace.

GMetrics-0.7/docs/SampleGMetricsReport.html0000644000175000017500000044753012463254373020344 0ustar ebourgebourgGMetrics Report: Sample

GMetrics Report: Sample

Report timestamp: Jan 31, 2015 3:48:36 PM

Metric Results

Package/Class/MethodComplexity (total)Complexity (average)Method Lines (total)Method Lines (average)
[p] All packages7371.727866.3
[p] org7371.727866.3
[p] org/gmetrics7371.727866.3
[c] org.gmetrics.GMetricsRunner11.01919.0
[m] execute111919
[p] org/gmetrics/analyzer00.000.0
[c] org.gmetrics.analyzer.AnalysisContextN/AN/AN/AN/A
[c] org.gmetrics.analyzer.SourceAnalyzerN/AN/AN/AN/A
[p] org/gmetrics/ant381.71607.0
[c] org.gmetrics.ant.AntFileSetSourceAnalyzer281.81217.6
[m] analyze1188
[m] getSourceDirectories1177
[m] calculatePackageLevelMetricResults1144
[m] calculatePackageLevelMetricResults1155
[m] processFileSet221313
[m] processFile442020
[m] applyMetricsToClass1188
[m] findResultsNodeForPath1133
[m] findPackageResultsNodeForPath2277
[m] findPackageResultsNodeForPath1144
[m] resultFromFirstMatchOrElseNull2277
[m] findOrAddResultsNodeForPath331212
[m] removeBaseDirectoryPrefix2277
[m] removeLeadingSlash3333
[m] afterAllSourceCodeProcessed2277
[m] <init>1166
[c] org.gmetrics.ant.GMetricsTask91.5366.0
[m] execute221515
[m] addFileset1144
[m] addConfiguredReport221010
[m] createMetricSet2233
[m] createSourceAnalyzer1133
[m] createGMetricsRunner1111
[c] org.gmetrics.ant.Report11.033.0
[m] addConfiguredOption1133
[c] org.gmetrics.ant.ReportOptionN/AN/AN/AN/A
[p] org/gmetrics/formatter41.3196.3
[c] org.gmetrics.formatter.FormatterN/AN/AN/AN/A
[c] org.gmetrics.formatter.FormatterFactory11.066.0
[m] getFormatter1166
[c] org.gmetrics.formatter.PercentageFormatter22.01010.0
[m] format221010
[c] org.gmetrics.formatter.ToStringFormatter11.033.0
[m] format1133
[p] org/gmetrics/metric3051.611206.1
[c] org.gmetrics.metric.AbstractAstVisitor61.5164.0
[m] isFirstVisit2277
[m] sourceLine1133
[m] getSourceUnit1133
[m] isSyntheticNonRunMethod2233
[c] org.gmetrics.metric.AbstractMethodMetric122.45511.0
[m] applyToMethod2277
[m] applyToClosure2277
[m] calculateForClass331616
[m] addClosureFieldsToMetricResults331414
[m] addMethodsToMetricResults221111
[c] org.gmetrics.metric.AbstractMetric81.6234.6
[m] applyToPackage2266
[m] calculateForPackage1144
[m] applyToClass2266
[m] isNotAnInterface1133
[m] createAggregateMetricResult2244
[c] org.gmetrics.metric.AstVisitorN/AN/AN/AN/A
[c] org.gmetrics.metric.MethodMetricN/AN/AN/AN/A
[c] org.gmetrics.metric.MetricN/AN/AN/AN/A
[c] org.gmetrics.metric.MetricLevel51.0183.6
[m] parse1133
[m] parseCommaSeparatedList1166
[m] getNames1133
[m] toString1133
[m] <init>1133
[c] org.gmetrics.metric.PostProcessingMetricN/AN/AN/AN/A
[p] org/gmetrics/metric/abc671.62315.5
[c] org.gmetrics.metric.abc.AbcAstVisitor331.71045.5
[m] visitMethod3366
[m] visitBinaryExpression1144
[m] visitPrefixExpression1144
[m] visitPostfixExpression1144
[m] visitMethodCallExpression1144
[m] visitPropertyExpression1155
[m] visitConstructorCallExpression1144
[m] visitIfElse2266
[m] visitSwitch2277
[m] visitTryCatchFinally1155
[m] visitTernaryExpression1144
[m] visitBooleanExpression2266
[m] visitNotExpression2266
[m] handleExpressionContainingOperation551212
[m] countUnaryConditionals441313
[m] countUnaryConditionals1144
[m] isSingleVariable1133
[m] isFinalVariableDeclaration2244
[m] isNotEmptyStatement1133
[c] org.gmetrics.metric.abc.AbcMetric51.7196.3
[m] calculate2299
[m] calculate1166
[m] createAggregateMetricResult2244
[c] org.gmetrics.metric.abc.AbcVector41.0194.8
[m] getMagnitude1155
[m] toString1133
[m] squared1133
[m] <init>1188
[p] org/gmetrics/metric/abc/result251.6895.6
[c] org.gmetrics.metric.abc.result.AbcMetricResult41.3155.0
[m] getAt2233
[m] toString1133
[m] <init>1199
[c] org.gmetrics.metric.abc.result.AggregateAbcMetricResult211.6745.7
[m] calculateFunctions551717
[m] getCount1133
[m] getTotalAbcVector1133
[m] getAbcVector1133
[m] getAverageAbcVector1166
[m] getAt1133
[m] toString1133
[m] addChildrenToAbcVector1188
[m] calculateMinimum2244
[m] calculateMaximum2244
[m] includesFunction1133
[m] average3388
[m] <init>1199
[p] org/gmetrics/metric/classcount31.5105.0
[c] org.gmetrics.metric.classcount.ClassCountMetric31.5105.0
[m] calculateForClass1155
[m] calculateForPackage2255
[p] org/gmetrics/metric/coupling611.42425.6
[c] org.gmetrics.metric.coupling.AbstractCouplingReferenceManager161.8475.2
[m] addReferencesFromPackage1166
[m] getPackageMetricResult1144
[m] isSourcePackageOrAncestor3388
[m] sortPackagesWithReferencesWithParentFirst1133
[m] updateStatisticsForAncestorPackage441212
[m] parentPackageName2233
[m] getReferencesFromPackage1144
[m] normalizePackageName2233
[m] <init>1144
[c] org.gmetrics.metric.coupling.AbstractPackageCouplingMetric21.0136.5
[m] calculateForClass111010
[m] <init>1133
[c] org.gmetrics.metric.coupling.AfferentCouplingMetric41.3165.3
[m] calculateForPackage2299
[m] afterAllSourceCodeProcessed1144
[m] getMetricResult1133
[c] org.gmetrics.metric.coupling.AfferentCouplingReferenceManager61.2336.6
[m] updateStatisticsForAllPackages1166
[m] createEmptyMetricResult1144
[m] applyReverseReferencesForPackage221313
[m] updateStatisticsForAllAncestorPackages1177
[m] <init>1133
[c] org.gmetrics.metric.coupling.EfferentCouplingMetric41.3165.3
[m] calculateForPackage2299
[m] getMetricResult1133
[m] afterAllSourceCodeProcessed1144
[c] org.gmetrics.metric.coupling.EfferentCouplingReferenceManager61.0335.5
[m] updateStatisticsForAllPackages1166
[m] createEmptyMetricResult1144
[m] applyReferencesForPackage111010
[m] updateStatisticsForAllAncestorPackages1177
[m] isSourcePackage1133
[m] <init>1133
[c] org.gmetrics.metric.coupling.PackageReferenceAstVisitor231.5845.6
[m] visitClass111010
[m] visitField1144
[m] visitConstructorCallExpression1155
[m] visitVariableExpression1155
[m] visitConstructorOrMethod1188
[m] visitClosureExpression1177
[m] visitCastExpression1155
[m] visitClassExpression1155
[m] visitPropertyExpression2277
[m] visitImports2299
[m] checkType1133
[m] checkTypeName1144
[m] checkPackageName5566
[m] isValidPackageReference3333
[m] <init>1133
[p] org/gmetrics/metric/coverage821.63486.8
[c] org.gmetrics.metric.coverage.AbstractCoberturaCoverageMetric271.91208.6
[m] applyToMethod2277
[m] applyToClosure1144
[m] calculateForClass442121
[m] calculateForPackage551616
[m] calculate1155
[m] getCoverageRatioForClass111010
[m] calculateCoverageForClassAndInnerClasses1144
[m] getOverallPackageMetricValue1144
[m] buildMethodResults331616
[m] calculateMethodResult2299
[m] logMissingMethodCoverageInformation2266
[m] findMethodElement1166
[m] containsClasses1133
[m] getCoberturaCoverageFile2299
[c] org.gmetrics.metric.coverage.CoberturaBranchCoverageMetric41.3268.7
[m] getCoverageRatioForSingleClass221010
[m] findLineElementsWithBranches1133
[m] getBranchCoverageRatio111313
[c] org.gmetrics.metric.coverage.CoberturaCoverageFile141.3696.3
[m] getOverallCoverageRate1144
[m] parseCoverageRate1155
[m] findPackageElement1144
[m] findClassElement1144
[m] findInnerClasses1144
[m] hasInnerClasses1133
[m] findMethodElement221111
[m] findAllMethodElements2277
[m] getCoberturaXml221212
[m] createNonValidatingXmlSlurper111111
[m] <init>1144
[c] org.gmetrics.metric.coverage.CoberturaLineCoverageMetric31.5157.5
[m] getCoverageRatioForSingleClass221010
[m] getLinesCoverageRatio1155
[c] org.gmetrics.metric.coverage.CoberturaSignatureParser202.0757.5
[m] matchesCoberturaMethod2288
[m] numberOfParameters1133
[m] parseSignatureParameterTypes331616
[m] extractParameters1155
[m] parseCoberturaSignatureParameterTypes2277
[m] parseParameterTypes221212
[m] processStandaloneCharacter441111
[m] processCharacterWithinFullyQualifiedTypeName2288
[m] classNameNoPackage2244
[m] <init>1111
[c] org.gmetrics.metric.coverage.CoberturaSignatureParser$ParseContext81.3233.8
[m] startFullyQualifiedTypeName1133
[m] withinFullyQualifiedTypeName1133
[m] appendToFullyQualifiedTypeName1133
[m] terminateFullyQualifiedTypeName2266
[m] startNewArrayType1133
[m] processPrimitiveTypeCode2255
[c] org.gmetrics.metric.coverage.Ratio61.2204.0
[m] plus1144
[m] toBigDecimal2244
[m] asType1144
[m] toString1144
[m] <init>1144
[p] org/gmetrics/metric/crap103.33612.0
[c] org.gmetrics.metric.crap.CrapMetric103.33612.0
[m] calculate1144
[m] calculate662424
[m] calculateCrapScore3388
[p] org/gmetrics/metric/cyclomatic201.5634.8
[c] org.gmetrics.metric.cyclomatic.CyclomaticComplexityAstVisitor161.5514.6
[m] visitMethod3388
[m] visitIfElse1144
[m] visitWhileLoop1144
[m] visitForLoop1144
[m] visitSwitch1144
[m] visitCatchStatement1144
[m] visitBinaryExpression1144
[m] visitTernaryExpression1144
[m] visitMethodCallExpression2255
[m] visitPropertyExpression2244
[m] handleExpressionContainingOperation2266
[c] org.gmetrics.metric.cyclomatic.CyclomaticComplexityMetric42.0126.0
[m] calculate2266
[m] calculate2266
[p] org/gmetrics/metric/fieldcount82.7196.3
[c] org.gmetrics.metric.fieldcount.FieldCountAstVisitor63.0105.0
[m] getNumberOfFields1133
[m] visitClass5577
[c] org.gmetrics.metric.fieldcount.FieldCountMetric22.099.0
[m] calculateForClass2299
[p] org/gmetrics/metric/linecount122.0386.3
[c] org.gmetrics.metric.linecount.ClassLineCountMetric22.099.0
[m] calculateForClass2299
[c] org.gmetrics.metric.linecount.ClassLineCountAstVisitor22.066.0
[m] visitClass2266
[c] org.gmetrics.metric.linecount.MethodLineCountAstVisitor52.5126.0
[m] visitMethod3366
[m] visitClosureExpression2266
[c] org.gmetrics.metric.linecount.MethodLineCountMetric31.5115.5
[m] calculate2266
[m] calculate1155
[p] org/gmetrics/metric/methodcount113.7217.0
[c] org.gmetrics.metric.methodcount.MethodCountAstVisitor94.5126.0
[m] getNumberOfMethods1133
[m] visitClass8899
[c] org.gmetrics.metric.methodcount.MethodCountMetric22.099.0
[m] calculateForClass2299
[p] org/gmetrics/metricregistry31.0165.3
[c] org.gmetrics.metricregistry.DefaultMetricRegistry31.0165.3
[m] getMetricClass1144
[m] getAllMetricNames1144
[m] buildMetricClassMap1188
[c] org.gmetrics.metricregistry.MetricRegistryN/AN/AN/AN/A
[c] org.gmetrics.metricregistry.MetricRegistryHolderN/AN/AN/AN/A
[p] org/gmetrics/metricset301.21435.7
[c] org.gmetrics.metricset.CompositeMetricSet31.0113.7
[m] addMetric1144
[m] addMetricSet1144
[m] getMetrics1133
[c] org.gmetrics.metricset.DefaultMetricSet11.033.0
[m] getMetrics1133
[c] org.gmetrics.metricset.GroovyDslMetricSet21.02110.5
[m] getMetrics1133
[m] <init>111818
[c] org.gmetrics.metricset.ListMetricSet21.0105.0
[m] getMetrics1133
[m] <init>1177
[c] org.gmetrics.metricset.MetricSetN/AN/AN/AN/A
[c] org.gmetrics.metricset.MetricSetBuilder21.073.5
[m] metricset1144
[m] getMetricSet1133
[c] org.gmetrics.metricset.TopLevelDelegate161.3705.8
[m] metricset1144
[m] metricset111010
[m] metric1155
[m] metric1166
[m] metric111010
[m] propertyMissing2255
[m] methodMissing331010
[m] description1144
[m] getMetricSet1133
[m] assertClassImplementsMetricInterface1144
[m] addMetric2266
[m] isNotWithinAnotherMetricDefinition1133
[c] org.gmetrics.metricset.MetricSetDelegate41.3217.0
[m] methodMissing221515
[m] findMetric1133
[m] <init>1133
[p] org/gmetrics/report1381.76587.9
[c] org.gmetrics.report.AbstractMetricCriteriaFilterN/AN/AN/AN/A
[c] org.gmetrics.report.AbstractReportWriter201.7766.3
[m] writeReport221414
[m] writeReportToStandardOut1144
[m] writeReportToFile2288
[m] initializeDefaultResourceBundle221212
[m] getResourceBundleString2299
[m] getResourceBundleStringOrNull2299
[m] initializeFormatters2277
[m] formatMetricResultValue2244
[m] getFormattedTimestamp1144
[m] isWriteToStandardOut2233
[m] initializeResourceBundle1111
[m] getTimestamp1111
[c] org.gmetrics.report.BasicHtmlReportWriter321.917710.4
[m] writeReport111717
[m] buildMetricResultColumns221111
[m] buildCSS1188
[m] buildHeaderSection1188
[m] buildBodySection111111
[m] buildReportTimestamp1166
[m] buildResultsTable221717
[m] getMetricResultColumnHeading1144
[m] setReportLevels1133
[m] includesReportLevel2233
[m] buildResultsTableRowRecursively10103333
[m] buildResultsRowsForChildren2299
[m] prefixForResultsNodeLevel1188
[m] buildMetricDescriptions222424
[m] getDescriptionForMetricName1144
[m] buildVersionFooter1188
[m] buildTitle2233
[c] org.gmetrics.report.FunctionsCriteriaFilter21.063.0
[m] setFunctions1133
[m] includesFunction1133
[c] org.gmetrics.report.LevelsCriteriaFilter21.063.0
[m] setLevels1133
[m] includesLevel1133
[c] org.gmetrics.report.MetricCriteriaFilterHelper61.5235.8
[m] includesName3377
[m] parseCriteria1177
[m] parseCriteriaForSingleMetric1166
[m] parseCommaSeparatedList1133
[c] org.gmetrics.report.MetricsCriteriaFilter31.563.0
[m] setMetrics1133
[m] includesMetric2233
[c] org.gmetrics.report.ReportWriterN/AN/AN/AN/A
[c] org.gmetrics.report.SeriesValue21.073.5
[m] toString1133
[m] <init>1144
[c] org.gmetrics.report.SingleSeriesCriteriaFilter331.91317.7
[m] buildSeriesData112020
[m] findMatchingValuesForChildren1166
[m] getResultsNodeFullName331414
[m] findMatchingValues441515
[m] sortValuesIfApplicable3399
[m] limitToGreaterThanIfApplicable2277
[m] limitToLessThanIfApplicable2277
[m] limitToMaxResultsIfApplicable3399
[m] assertMetricExists1144
[m] assertLevelExists1133
[m] assertFunctionExists1144
[m] assertValidSortValue2233
[m] assertValidMaxResultsValue331111
[m] assertValidGreaterThanValue1133
[m] assertValidLessThanValue1133
[m] assertValidNumberValue331010
[m] findMetric1133
[c] org.gmetrics.report.SingleSeriesHtmlReportWriter111.1969.6
[m] writeReport111818
[m] buildHeaderSection1188
[m] buildCSS1188
[m] buildBodySection221313
[m] buildReportTimestamp1166
[m] buildResultsTable111313
[m] getSeriesValueNameHeading1199
[m] buildSeriesValueRow1199
[m] getMetricResultColumnHeading1144
[m] buildVersionFooter1188
[c] org.gmetrics.report.XmlReportWriter271.81308.7
[m] writeReport111717
[m] buildReportElement1155
[m] buildProjectElement1199
[m] buildPackageElements1133
[m] buildElement4477
[m] buildPackageElement552020
[m] buildClassElement111111
[m] buildMethodElement111010
[m] buildMetricElements1177
[m] buildMetricElement661414
[m] isRoot1133
[m] buildMetricsElement111414
[m] getDescriptionForMetric1144
[m] isPackage1133
[m] cdata1133
[p] org/gmetrics/result491.51604.8
[c] org.gmetrics.result.ClassMetricResult21.073.5
[m] toString1133
[m] <init>1144
[c] org.gmetrics.result.FunctionNamesN/AN/AN/AN/A
[c] org.gmetrics.result.MapMetricResult31.0175.7
[m] getAt1144
[m] toString1144
[m] <init>1199
[c] org.gmetrics.result.MethodKey61.2234.6
[m] equals2244
[m] hashCode1144
[m] toString1144
[m] <init>1155
[m] <init>1166
[c] org.gmetrics.result.MetricResultN/AN/AN/AN/A
[c] org.gmetrics.result.MetricResultBuilder282.2614.7
[m] createAggregateMetricResult111313
[m] toString1133
[m] calculateFunctions111010
[m] calculateCount1133
[m] total3333
[m] calculateTotal2255
[m] minimum3333
[m] calculateMinimum2244
[m] maximum3333
[m] calculateMaximum2244
[m] average3333
[m] shouldCalculateFunction3333
[m] isFunctionSpecifiedOrImplied3344
[c] org.gmetrics.result.MutableMapMetricResult41.0194.8
[m] getAt1144
[m] putAt1133
[m] toString1144
[m] <init>1188
[c] org.gmetrics.result.NumberMetricResult31.0165.3
[m] getAt1133
[m] toString1133
[m] <init>111010
[c] org.gmetrics.result.SingleNumberMetricResult31.0175.7
[m] getAt1144
[m] toString1144
[m] <init>1199
[p] org/gmetrics/resultsnode271.21024.6
[c] org.gmetrics.resultsnode.ClassResultsNode91.3344.9
[m] containsClassResults1133
[m] getMetricResult1144
[m] addClassMetricResult221010
[m] toString1133
[m] addMethodMetricResult2266
[m] <init>1133
[m] <init>1155
[c] org.gmetrics.resultsnode.MethodResultsNode71.0243.4
[m] containsClassResults1133
[m] getMetricResult1144
[m] addMetricResult1144
[m] getChildren1133
[m] toString1133
[m] <init>1133
[m] <init>1144
[c] org.gmetrics.resultsnode.PackageResultsNode111.4445.5
[m] getChildren1133
[m] containsClassResults1133
[m] getMetricResult1144
[m] addChildIfNotEmpty2277
[m] addChild1155
[m] applyMetric331414
[m] toString1133
[m] <init>1155
[c] org.gmetrics.resultsnode.ResultsNodeN/AN/AN/AN/A
[p] org/gmetrics/source372.11116.2
[c] org.gmetrics.source.AbstractSourceCode162.7518.5
[m] getLines2266
[m] line3355
[m] getAst331616
[m] getLineNumberForCharacterIndex551616
[m] isValid1133
[m] normalizePath2255
[c] org.gmetrics.source.SourceCodeN/AN/AN/AN/A
[c] org.gmetrics.source.SourceCodeCriteria88.01717.0
[m] matches881717
[c] org.gmetrics.source.SourceFile71.2254.2
[m] getName1133
[m] getPath1133
[m] getText2266
[m] toString1133
[m] createSourceUnit1155
[m] <init>1155
[c] org.gmetrics.source.SourceString61.2183.6
[m] getText1133
[m] setPath2233
[m] toString1133
[m] createSourceUnit1133
[m] <init>1166
[p] org/gmetrics/util1052.42786.5
[c] org.gmetrics.util.AstUtil402.7946.3
[m] isEmptyMethod1133
[m] isClosureField2233
[m] isBlock1133
[m] isEmptyBlock4455
[m] getMethodArguments331010
[m] isMethodCall331010
[m] isMethodCall2244
[m] isMethodCall3399
[m] isMethodNamed1144
[m] getAnnotation2266
[m] getVariableExpressions991616
[m] isFinalVariable441414
[m] isFromGeneratedSourceCode3333
[m] respondsTo1133
[m] <init>1111
[c] org.gmetrics.util.Calculator42.084.0
[m] calculateAverage3377
[m] <init>1111
[c] org.gmetrics.util.ClassNameUtil142.3264.3
[m] parentPackageName3377
[m] isPackageName2244
[m] isClassName2244
[m] getNameOnly4477
[m] isCapitalized2233
[m] <init>1111
[c] org.gmetrics.util.GMetricsVersion11.033.0
[m] getVersion1133
[c] org.gmetrics.util.ImportUtil44.01111.0
[m] packageNameForImport441111
[c] org.gmetrics.util.PathUtil132.6336.6
[m] getName3377
[m] getParent3399
[m] normalize2233
[m] toPackageName441313
[m] <init>1111
[c] org.gmetrics.util.PropertyUtil63.02311.5
[m] setPropertyFromString552222
[m] <init>1111
[c] org.gmetrics.util.WildcardPattern133.34611.5
[m] matches4477
[m] containsWildcards1133
[m] convertStringWithWildcardsToRegex552424
[m] <init>331212
[p] org/gmetrics/util/io101.4344.9
[c] org.gmetrics.util.io.ClassPathResource41.3144.7
[m] getInputStream1133
[m] getInputStream2277
[m] <init>1144
[c] org.gmetrics.util.io.DefaultResourceFactory42.0126.0
[m] getResource3399
[m] isUrl1133
[c] org.gmetrics.util.io.ResourceN/AN/AN/AN/A
[c] org.gmetrics.util.io.ResourceFactoryN/AN/AN/AN/A
[c] org.gmetrics.util.io.UrlResource21.084.0
[m] getInputStream1144
[m] <init>1144

Metric Descriptions

#Metric NameDescription
1CyclomaticComplexityMeasures the (McCabe) Cyclomatic Complexity of source code. See the Wikipedia entry for Cyclomatic Complexity.
2MethodLineCountCounts the number of lines in each method.

GMetrics 0.7

GMetrics-0.7/docs/gmetrics-CrapMetric.html0000644000175000017500000003071112463254301020111 0ustar ebourgebourg GMetrics - GMetrics - CRAP Metric

CRAP Metric

Measures the C.R.A.P. (Change Risk Anti-Patterns) score. It is designed to analyze and predict the amount of effort, pain, and time required to maintain an existing body of code.

A method with a CRAP score over 30 is considered CRAPpy (i.e., unacceptable, offensive, etc.). [2]

Implemented by the org.gmetrics.metric.crap.CrapMetric class.

CRAP Formula

Given a Groovy method m, C.R.A.P. for m is calculated as follows:

  C.R.A.P.(m) = comp(m)^2 * (1 – cov(m)/100)^3 + comp(m)

Where comp(m) is the cyclomatic complexity of method m, and cov(m) is the test code coverage provided by automated tests.

Metric Properties

The following properties can be configured for this metric within a MetricSet. See Creating a MetricSet for information on the syntax of setting a metric property.

PropertyDescriptionDefault Value
enabledThis boolean property controls whether the metric is enabled. If set to false, then the metric is not included as part of the results or the output reports.true
functionsThis List<String> property contains the names of the functions to be calculated at the class and package levels and (potentially) included within the report(s). Valid values are: - "total" - "average" - "minimum" - "maximum"["total","average"]
coverageMetricThe GMetrics Metric instance to provide code coverage data. GMetrics includes a Cobertura-based coverage metric. See CoberturaLineCoverageMetric This property is REQUIRED.N/A
complexityMetricThe GMetrics Metric instance to provide cyclomatic complexity data. GMetrics includes a CyclomaticComplexityMetric, which is used as the default provider.An instance of CyclomaticComplexityMetric

Sample MetricSet Definition

Here is a sample MetricSet definition that includes a CrapMetric.

  final COBERTURA_FILE = 'coverage/GMetrics/coverage.xml'

  metricset {

      def coberturaMetric = CoberturaLineCoverage {
          coberturaFile = COBERTURA_FILE
          functions = ['total']
      }

      CRAP {
          functions = ['total']
          coverageMetric = coberturaMetric
    }
  }

Note setting the coverageMetric field is required.

The CRAP metric is a special composite metric -- it uses other metrics to calculate complexity and code coverage. In the example above, the CoberturaLineCoverage metric is also included within the MetricSet being defined. Alternatively, if you define the CoberturaLineCoverage metric within the CRAP metric definition, then the CoberturaLineCoverage metric is not included within the MetricSet, as in the example below.

  final COBERTURA_FILE = 'coverage/GMetrics/coverage.xml'

  metricset {

      CRAP {
          // CoberturaLineCoverage is not included in the MetricSet
          coverageMetric = CoberturaLineCoverage {
              coberturaFile = COBERTURA_FILE
          }
    }
  }

There is one annoying known limitation with that behavior. If you use the Map syntax, rather than the Closure syntax, to define the outer metric, then inner metric is also included within the MetricSet, as in the example below.

  metricset {
      // Both CRAP and CoberturaLineCoverage metrics are included in the MetricSet
      CRAP(coverageMetric:CoberturaLineCoverage(coberturaFile:'coverage/GMetrics/coverage.xml'))
  }

References

  • [1] The original 2007 blog post that defined the CRAP metric.
  • [2] A 2011 blog post from Alberto Savoia (the co-creator of the CRAP metric with Bob Evans), describing the formula, the motivation, and the CRAP4J tool for calculating CRAP score for Java code.

GMetrics-0.7/docs/gmetrics-ClassCountMetric.html0000644000175000017500000002101512463254300021276 0ustar ebourgebourg GMetrics - GMetrics - ClassCount Metric

ClassCount Metric

Metric for counting the number of classes in each package. This metric counts the number of classes, interfaces and enums within a package.

Implemented by the org.gmetrics.metric.classcount.ClassCountMetric class.

Metric Properties

The following properties can be configured for this metric within a MetricSet. See Creating a MetricSet for information on the syntax of setting a metric property.

PropertyDescriptionDefault Value
enabledThis boolean property controls whether the metric is enabled. If set to false, then the metric is not included as part of the results or the output reports.true
functionsThis List<String> property contains the names of the functions to be calculated at the class and package levels and (potentially) included within the report(s). Valid values are: - "total" - "average" - "minimum" - "maximum"["total","average"]

GMetrics-0.7/docs/gmetrics-AbcMetric.html0000644000175000017500000003001312463254277017720 0ustar ebourgebourg GMetrics - GMetrics - ABC Metric

ABC Metric

Calculates the ABC Metric for a class or method. ABC is a metric of size/complexity that counts the number of Assignments (A), Branches (B) and Conditions (C) and assigns a single numerical score calculated as:

 |ABC| = sqrt((A*A)+(B*B)+(C*C))

Implemented by the org.gmetrics.metric.abc.AbcMetric class.

The ABC Metric calculation rules for Groovy:

  • Add one to the assignment count for each occurrence of an assignment operator, excluding constant declarations: = *= /= %= += <<= >>= &= |= ^= >>>=
  • Add one to the assignment count for each occurrence of an increment or decrement operator (prefix or postfix): ++ --
  • Add one to the branch count for each function call or class method call.
  • Add one to the branch count for each occurrence of the new operator.
  • Add one to the condition count for each use of a conditional operator: == != <= >= < > <=> =~ ==~
  • Add one to the condition count for each use of the following keywords: else case default try catch ?
  • Add one to the condition count for each unary conditional expression. These are cases where a single variable/field/value is treated as a boolean value. Examples include if (x) and return !ready.

Additional notes

  • A property access is treated like a method call (and thus increments the branch count).
  • If a class field is initialized to a Closure (ClosureExpression), then that Closure is analyzed just like a method.

Guidelines for Interpreting ABC Metric Values

A frequently-referenced blog post by Jake Scruggs ([4]) offers the following guidelines for interpreting an ABC score. Note that these values refer to the score (magnitude) calculated for a single method:

  • 0-10 = Awesome
  • 11-20 = Good enough
  • 21-40 = Might need refactoring
  • 41-60 = Possible to justify
  • 61-100 = Danger
  • 100-200 = Whoop, whoop, whoop
  • 200 + = Someone please think of the children

Metric Properties

The following properties can be configured for this metric within a MetricSet. See Creating a MetricSet for information on the syntax of setting a metric property.

PropertyDescriptionDefault Value
enabledThis boolean property controls whether the metric is enabled. If set to false, then the metric is not included as part of the results or the output reports.true
functionsThis List<String> property contains the names of the functions to be calculated at the method, class and package levels and (potentially) included within the report(s). Valid values are: - "total" - "average" - "minimum" - "maximum"["total","average"]
includeClosureFieldsThis boolean property controls whether metric values are calculated for Closure Fields and treated as methods. A Closure Field is a field that is initialized to a Closure Expression, e.g., def myField = { println 12 }.true

References

  • [1] The ABC Metric specification.
  • [2] The The C2 Wiki page for the ABC Metric.
  • [3] Flog is the popular Ruby tool that uses ABC.
  • [4] This blog post describes some guidelines for interpreting the ABC score. The post refers to the Flog tool, but the ABC score is calculated similarly (though adapted somewhat to account for language specifics) and the guidelines should be transferable.

GMetrics-0.7/docs/images/0000755000175000017500000000000012623566446014640 5ustar ebourgebourgGMetrics-0.7/docs/images/expanded.gif0000644000175000017500000000006412463254373017112 0ustar ebourgebourgGIF89a! , j ;GMetrics-0.7/docs/images/icon_info_sml.gif0000644000175000017500000000113612463254373020141 0ustar ebourgebourgGIF89ar 'SG]}]yVpOgLcJ`EYxCWtI^~FZyehgjlmpXltvdyw|~wunzw…ǘƂ˦ΩЭҬџ԰ӵָؼݲ@TpAUqy}ŞɧŹ!r,r`fg`l\d LG^qqopnQmbj.MObhNgr\oP6 [ig%dmE4k_^eL PRah YcHBjfV F`\- 5Z=97D`] I' 130@KICA@,"c $XA ?tr兊!8h2† @Pab ;GMetrics-0.7/docs/images/icon_success_sml.gif0000644000175000017500000000173612463254373020664 0ustar ebourgebourgGIF89aΧμc~w˗éSq*/%(#8='*!37,/(CH'AF&>B/2*,)FI"!PrkjtŲFcO7G;ȝȣ֩۴߲2B.ᥳ⦷㫸䬸䭺导屏€㩲ࢲࣜΈޘܙٗۗ]B΅ԋۑ̈́՗]>Ԅሻfrmjb7Jw*U0W2im;Ag$r?o=e8f8b7Z2Y1S.Jt)Qy1WYuQeFcE{{hGv@u?k:h7`3Jr(E^3Q|,S~-Dh%}DEi&QŢIm&Qx+Ot)Mo(Ll&Ig%Gc$!, HA!,XE aCF5pAƏ *01#"NT0A=Y& NRI1bJ':o8b3-e˓у>|¥ (qŋ4hԱ(ЋmT"J;*d("4lp ,P( ;GMetrics-0.7/docs/images/newwindow.png0000644000175000017500000000033412463254373017362 0ustar ebourgebourgPNG  IHDR Ӻ&gAMA7tEXtSoftwareAdobe ImageReadyqe< PLTEuuu8tRNS@*FIDATxb`ffffb@  8@! @`6L& ^IENDB`GMetrics-0.7/docs/images/collapsed.gif0000644000175000017500000000006512463254373017271 0ustar ebourgebourgGIF89a! , D`c5 ;GMetrics-0.7/docs/images/icon_warning_sml.gif0000644000175000017500000000110012463254373020642 0ustar ebourgebourgGIF89ab`awr}~j|fsη⣴[M@K? kTN G J$wsrpiffecb_[ZTQOJ E D E ? i2 nm]XWVH A = h2 ]-bz{z&Zi&i(j&127m-D2%p4BI|Du@Ya\}R׈[v[mI:2̓`vّs=.()+*砅.!ӗ<,&餍5&!:*%y*vp묜*,-~zǟ!v,v oaed][sv iT u^PW\ZMIgXUGADSRQ?4VONK)#CnJHFE0=7'6*v B@%+8, l !5"9.:/Lt"$&(-3:c Yf3cđ# ;;GMetrics-0.7/docs/images/icon_error_sml.gif0000644000175000017500000000176212463254373020344 0ustar ebourgebourgGIF89a  q x v m;@GJf46_     i [        ~ } s q p n X V L     v l ~ "",9?6;{6:df島$')OWͱ-/*"4"3s}%8&:(=*@+A*?5H=MM\+B.F/G0I2M3O2N%9J.FprN3Ovx{@-EL6RO8V08G3O1:bGm3=4>7 C5AgOx?2ME7TmW|d}gu^V!,; H AJgN޴Yc* FrM/]HIh $5iΌ %BT 0P8$2,ㅋ,Z(DL@;Gk$<Hd >`'!\2?vhp  u@ .X = q0 )f$Tp"FQGr̉C;GMetrics-0.7/docs/images/external.png0000644000175000017500000000034612463254373017166 0ustar ebourgebourgPNG  IHDR Ӻ&gAMA7tEXtSoftwareAdobe ImageReadyqe< PLTEuuuPtRNS@*PIDATxb`&& @ P6#@ `XĆ2 d@A3 ( *tIENDB`GMetrics-0.7/docs/images/logos/0000755000175000017500000000000012623566446015763 5ustar ebourgebourgGMetrics-0.7/docs/images/logos/maven-feather.png0000644000175000017500000000640212463254373021210 0ustar ebourgebourgPNG  IHDRZ#/ pHYs.#.#x?vgAMA|Q cHRMz%u0`:o_F xIDATxb`H  FFblnڳg!߿?x8ߟ? XQtƍ.[LL,##CAAM'O\WW7eooog o߾1cFuu5ABCC&=qߟ7j%7<Ճ Jϟ?yfZZZJJ ''gpp0666mmm c߾}o޼ٰaCoo/\۷oh'.33 366^zW<{sb['>>׌}'|vJzdt9&^=bpE@@*;;ɓ'@ٳg_~ȸz… 1y]>ucǎMO}}ŋդI._ sUU߿֭[@`&F˖-Ѽ}OgU:̪M͟"oo3.?Ú)Jz֝U ݽ?  XTwΝV8P^^2555sa`dxyyZtuu&>}:77䘘ٵk׳gϘ /ԁ&&& 7lxɞ[1K3+J2>eggaA3|~9gǚ37Va㖕e$)8QWW:9s$&&Q T#** 2p XX T`vww[...( ,b`\|1g]*˟ y|?';#?FAvVN=\JzK_`r|y9 y0*r Krp&@!!!'= ~a4)))`2??s;y>/~œO}]_EҜ;q֏, _~:ZGpYGLL"R&X "68~ X@=y`Vd@a?~@9w܃T_\\ ,**rttQUU&+XX@V%=ygO}{;vo/q?Blߘ$lY[{ET8u/NW*pGQ9X*))S{zzXX nnnSS &ܹRkߜ`1  SBl?~rr2<8]>5[V:rn_bz΢U1w?`M=%C.?2~3o=cS@J%+* l71~ ` L u}ߏot?#+'3'&.&L,18 .9}$,f.qfIvvF~t@ X#}݅ÃT~߽~ϯ9}9Y/A2L?M}a]X!DQp Q`'<% ?."T/|ݛ?~{˯nN%A/lls1?l\\<4t3@AM$]VM/` X.B`kXpk޼yR {:"!8Ab 6sssC+u31~QtH#0W ,Ae bh0Zʅ r6"„NSTOr?]PXm;8pKm6{0XbDغ FLKK &R`T4 XkC2`O08 t1#+}@.0^|  c",M7Bv "2<܁l`O!|`[˗/ `Kn.ЕGkY`2`ߙ1> eC`]N3,]UH:t*)$Kxǧ(@ĉᝫOB[l[80??{Ύǖ6$0Ctmذȝ9s& A Ağ AjRRDWcc#XHKx= b9`At+&&owK `3¾r ydx).`xoG€'Q @Cl?{,d0YjT i' ϹV@,D>'OB FV¿ _AVQΐ[) ~102@ԫ[&MD ةb `ρU;$tAvҒO۷oGͭϣ ""$0!xW2002x~"E^Q'+**Acٲeh-1HR0D@5L ρ5Ç ϯ_G `8FED tk.`3 #I… !UV eҥKAQXtApg!9L,l < _߃s-4!##4dD5d= oBB TWbqqq`:@dgF< J@Cх  @q 0*DF̬L> lNΧϮ3Z32rqt1;& H &-THsF0cyyypM^w" (hkkKL } 'ƒ]I@G':`={`Tpr3|~, | HKH`|\v H= ,V^V+*HA< `vY8 P@4P@Z':vrIENDB`GMetrics-0.7/docs/images/logos/build-by-maven-white.png0000644000175000017500000000432412463254373022420 0ustar ebourgebourgPNG  IHDRZ#/ pHYs.#.#x?vtIME5BasIDATXY}LS[{_b6(_i _ڠA+ D+*5وp dQyFC$XkX\*R>  VDOj G>|a?Ι3̙sJ!`&^`/&)--{hW^1 ;2j0~!pwtt>|СC"hݳu|~`` |8::ZRR%ɖ-[WXA,hܚJY֯^ R߰aåKFg\]]RD"!իW%&&VWWڵݻ[n%$$cǔJׅ~zr<66uuuslll.B!fx<EQnnn*؁Di***fS5>>>]mX***֭[ ))))FoD0L\\\\\ٳg ERRRaa.]G,TVVV=عsg{{}Ε+WH2jl6fQe9! }}}aaa3t.zqUUU``{ω/mccc|>Ņ(,Z(Lfo8>s,A֖޽{-˗hi:44 }S?L!55uƍ$F02á 3{l/n&'S˲ 1CBCC7Z aFquwwJD1e{6wO+l.@ !͛hJc6KU*J*..BVg\.%R$z||\VT覦'Nxzz:99)ʊyd23&&wRh4Vupp ;UvT6Noooݍc^^^& !t9wtt$9rOimmihdWeddd~ਫ#v˱ O:Eؾ}dpqĽ~޾^z }2VY|9U__9(({^RR~:v5** CVSS#UPP@z߭[>}:22DN8rss?m4ׇoB^Ȍ 1M+v-8nŃj*++C]x?n޼8j}'8p:s 6`4?\\}}=f/^Ln5BBB0SҞ!{BOQ fHtL&|]$ ϟ?OQTcc#YVIx28Ÿ!sDzlcc#WZEǏcӓ`D+Woad( ܃)_+lQ+fz}^^^OO*VݝGHPH.3s5/^* ( JBZ@%?h?"jbQd~(d\A $&21 "R`UJ-Z8m4ɴNǩs}~sɚTWWzxxQ(bYU&⥣f_غu+!6<<|8˲NNNΝKMM~~~1118`rrRT@IID"$www???t޶m[JJJ^^_^__q܅ eǏ ϟ_J5gϞ---(++;;;@TTTxxx@@={ݍKjOOOooﴴ4%0 # vh၁'Or3&`0LOO>իWҥKo czz:;;;>>|ܽ{Í  :;;~_ߘZ9q@#iiike@~~~___ppma̯svvvssC]B<==V<2 R&''Yp|y܀jݻwgv;!2<<'J^j2h2nll}ZVHG-3~;ueH/A:3{ CK7lk׮eC,ˢ?G[lXʹ%>5MSSӇnh4>}ZL&H$'ms<-_f˗Ye^z{{ZVMNNx"lCCCtJQQNvӧjo޼V???Á+q^.{{{7n܈)DX8*CthnnYGAKPP~6fq``˷Z~։zXx,rTʾ:X<33P(oeH$r122;vѣh["=..[#""CWWҋ,5&r1}@.f7BE`II :ujkkcF d0322@R2zu twwF1ߍ=p rwc3{ƶeKKK1͉D"@)(((((XBΝ;tvv RPT*2##̃{.3ckM|ĸccceY\{n<ϗ/_D"]Y,'OB-U*أ$4\R@oݺFVukFͿ'9`3cAaUuV!݋M@, 0;;[TTD<_VVQbtuuc,p344DW{{; <Ͽ{hFׯi GMetrics - GMetrics - DefaultMetricSet

The Default MetricSet

If no MetricSet is configured for the GMetrics Ant Task then it uses the set of Metrics defined by the org.gmetrics.metricset.DefaultMetricSet class. That includes the following Metrics:


GMetrics-0.7/docs/gmetrics-other-tools-frameworks.html0000644000175000017500000002014312463254302022514 0ustar ebourgebourg GMetrics - GMetrics - Other Tools / Frameworks

GMetrics - Integration with Other Tools / Frameworks

Application Frameworks

Build and Code Quality Tools


GMetrics-0.7/docs/gmetrics-FieldCountMetric.html0000644000175000017500000002104312463254302021257 0ustar ebourgebourg GMetrics - GMetrics - FieldCount Metric

FieldCount Metric

Metric for counting the number of fields within each class. Note that static final fields (i.e., constants) are included as well.

Implemented by the org.gmetrics.metric.fieldcount.FieldCountMetric class.

Metric Properties

The following properties can be configured for this metric within a MetricSet. See Creating a MetricSet for information on the syntax of setting a metric property.

PropertyDescriptionDefault Value
enabledThis boolean property controls whether the metric is enabled. If set to false, then the metric is not included as part of the results or the output reports.true
functionsThis List<String> property contains the names of the functions to be calculated at the method, class and package levels and (potentially) included within the report(s). Valid values are: - "total" - "average" - "minimum" - "maximum"["total","average"]

GMetrics-0.7/docs/gmetrics-CoberturaBranchCoverageMetric.html0000644000175000017500000002357512463254300023755 0ustar ebourgebourg GMetrics - GMetrics - CoberturaBranchCoverage Metric

CoberturaBranchCoverage Metric

Metric that measures the code coverage of branches (conditionals) based on a Cobertura coverage XML file.

Implemented by the org.gmetrics.metric.coverage.CoberturaBranchCoverageMetric class.

Metric Properties

The following properties can be configured for this metric within a MetricSet. See Creating a MetricSet for information on the syntax of setting a metric property.

PropertyDescriptionDefault Value
enabledThis boolean property controls whether the metric is enabled. If set to false, then the metric is not included as part of the results or the output reports.true
functionsThis List<String> property contains the names of the functions to be calculated at the class and package levels and (potentially) included within the report(s). Valid values are: - "total" - "average" - "minimum" - "maximum"["total","average"]
coberturaFileThe path to the Cobertura XML file. By default, the path is relative to the classpath. But the path may be optionally prefixed by any of the valid java.net.URL prefixes, such as "file:" (to load from a relative or absolute path on the filesystem), or "http:". This property is REQUIRED.N/A

Known Limitations

This metric does not calculate coverage for Closure Fields (fields initialized to a Closure Expression), unlike some other method-level metrics.

References


GMetrics-0.7/docs/SampleGMetricsSingleSeriesReport.html0000644000175000017500000001002512463254373022642 0ustar ebourgebourgMethods With Highest Line Count

Methods With Highest Line Count

Report timestamp: Jan 31, 2015 3:48:42 PM

MethodMethod Lines (total)
org.gmetrics.report.BasicHtmlReportWriter#buildResultsTableRowRecursively33
org.gmetrics.metric.crap.CrapMetric#calculate24
org.gmetrics.report.BasicHtmlReportWriter#buildMetricDescriptions24
org.gmetrics.util.WildcardPattern#convertStringWithWildcardsToRegex24
org.gmetrics.util.PropertyUtil#setPropertyFromString22
org.gmetrics.metric.coverage.AbstractCoberturaCoverageMetric#calculateForClass21
org.gmetrics.ant.AntFileSetSourceAnalyzer#processFile20
org.gmetrics.report.SingleSeriesCriteriaFilter#buildSeriesData20
org.gmetrics.report.XmlReportWriter#buildPackageElement20
org.gmetrics.GMetricsRunner#execute19
org.gmetrics.metricset.GroovyDslMetricSet#<init>18
org.gmetrics.report.SingleSeriesHtmlReportWriter#writeReport18
org.gmetrics.metric.abc.result.AggregateAbcMetricResult#calculateFunctions17
org.gmetrics.report.BasicHtmlReportWriter#writeReport17
org.gmetrics.report.BasicHtmlReportWriter#buildResultsTable17
org.gmetrics.report.XmlReportWriter#writeReport17
org.gmetrics.source.SourceCodeCriteria#matches17
org.gmetrics.metric.AbstractMethodMetric#calculateForClass16
org.gmetrics.metric.coverage.AbstractCoberturaCoverageMetric#calculateForPackage16
org.gmetrics.metric.coverage.AbstractCoberturaCoverageMetric#buildMethodResults16

GMetrics 0.7

GMetrics-0.7/docs/gmetrics-SingleSeriesHtmlReportWriter.html0000644000175000017500000002741112463254303023677 0ustar ebourgebourg GMetrics - GMetrics SingleSeriesHtmlReportWriter

SingleSeriesHtmlReportWriter

Description

The org.gmetrics.report.SingleSeriesHtmlReportWriter class produces an HTML report of metric results based on a single metric, single level and single function to provide a single series of data.

The metric, level and function properties are required (must be non-null and non-empty). These three properties uniquely identify a single series of metric values.

See a Sample Report.

Option Nested Elements

The option element is a child of the report element and defines a report-specific option for a report.

org.gmetrics.report.SingleSeriesHtmlReportWriter supports the following options:

AttributeDescriptionRequired
metricThe name (case-sensitive) of a single Metric included in the analysis results (e.g. "CyclomaticComplexity").Yes
levelThe single level at which results are included in the report. Valid level values are "package", "class" and "method".Yes
functionThe function for which results are included in the report. Valid function values are metric-specific, but are typically "total", "average", "minimum" or "maximum".Yes
outputFileThe path and filename for the output report file.No
titleThe title for the output report.No
writeToStandardOutSet to "true" or true to write out the report to stdout (System.out) instead of writing to a file.No
sortControls whether the report results are sorted numerically. A value of null or empty means no sorting is performed; otherwise, the value must be either "ascending" or "descending".No
maxResultsSpecifies the limit on the number of results included in the report. A value of null, zero or empty means no limit is applied; otherwise the value must be a positive integer.No
greaterThanSpecifies a lower-bound threshold -- only results with a larger value are included within the report. A value of null or empty means no lower-bound threshold is applied.No
lessThanSpecifies an upper-bound threshold -- only results with a smaller value are included within the report. A value of null or empty means no upper-bound threshold is applied.No

Example

Here is an example Ant XML build file illustrating configuration of org.gmetrics.report.SingleSeriesHtmlReportWriter.

<taskdef name="gmetrics" classname="org.gmetrics.ant.GMetricsTask">
<target name="runGMetrics">

    <gmetrics>
        <report type="org.gmetrics.report.SingleSeriesHtmlReportWriter">
            <option name="outputFile" value="SampleGMetricsReport.html" />
            <option name="title" value="Sample" />
            <option name="metric" value="CyclomaticComplexity" />
            <option name="level" value="class" />
            <option name="function" value="average" />
            <option name="maxResults" value="50" />
            <option name="greaterThan" value="100.0" />
        </report>
        <fileset dir="src">
            <include name="**/*.groovy"/>
        </fileset>
    </gmetrics>

</target>

GMetrics-0.7/docs/gmetrics-XmlReportWriter.html0000644000175000017500000003131512463254303021214 0ustar ebourgebourg GMetrics - GMetrics XmlReportWriter

XmlReportWriter

Description

The org.gmetrics.report.XmlReportWriter class produces an XML report of GMetrics results.

See a Sample Report.

Option Nested Elements

The option element is a child of the report element and defines a report-specific option for a report.

org.gmetrics.report.BasicHtmlReportWriter supports the following options:

AttributeDescriptionRequired
functionsThe (comma-separated) list of functions for which results are included in the report for a particular metric. Multiple metrics are configured by separating them with semicolons (;). By default, all functions provided by a metric are included within the report. Any metrics not explicitly configured include all applicable functions as well. ---- Valid function values are metric-specific, but typically include "total", "average", "minimum" and "maximum". ---- For example, a value of "MethodLineCount=minimum,average" for functions includes only the minimum and average function results for the MethodLineCount metric. Likewise, a value of "ABC=average; CyclomaticComplexity=total,maximum" includes the average function results for the ABC metric and the total and maximum function results for the CyclomaticComplexity metric. Note that all functions provided by any other metrics (not configured) are included.No
levelsThe (comma-separated) list of levels at which results are included in the report for a particular metric. Multiple metrics are configured by separating them with semicolons (;). By default, all levels are included for all metrics within the report. Any metrics not explicitly configured include all applicable levels as well. ---- Valid level values are "package", "class" and "method". ---- For example, a value of "MethodLineCount=class,method" for levels includes only the class and method level results for the MethodLineCount metric. Likewise, a value of "ABC=method; CyclomaticComplexity=package,class" includes the method level results for the ABC metric and the package and class level results for the CyclomaticComplexity metric. Note that all levels are included for any other metrics not explicitly configured.No
metricsThe (comma-separated) list of names of the metrics included in the report. By default, all metrics within the results are included in the report. ---- For example, a metrics value of "ABC, MethodLineCount" includes only the ABC and MethodLineCount metrics within the report.No
outputFileThe path and filename for the output report file.No
titleThe title for the output report.No
writeToStandardOutSet to "true" or true to write out the report to stdout (System.out) instead of writing to a file.No

Example

Here is an example Ant XML build file illustrating configuration of org.gmetrics.report.XmlReportWriter.

<taskdef name="gmetrics" classname="org.gmetrics.ant.GMetricsTask">
<target name="runGMetrics">

    <gmetrics>
        <report type="org.gmetrics.report.XmlReportWriter">
            <option name="outputFile" value="SampleGMetricsXmlReport.html" />
        </report>
        <fileset dir="src">
            <include name="**/*.groovy"/>
        </fileset>
    </gmetrics>

</target>

And here is an example illustrating explicit configuration of the metrics, levels and functions to be included within the report:

<taskdef name="gmetrics" classname="org.gmetrics.ant.GMetricsTask">
<target name="runGMetrics">

    <gmetrics>
        <report type="org.gmetrics.report.XmlReportWriter">
            <option name="outputFile" value="SampleGMetricsXmlReport.html" />
            <option name="metrics" value="CyclomaticComplexity, ABC" />
            <option name="levels" value="CyclomaticComplexity=class,method; ABC=method" />
            <option name="functions" value="ABC=average; CyclomaticComplexity=total,maximum" />
        </report>
        <fileset dir="src">
            <include name="**/*.groovy"/>
        </fileset>
    </gmetrics>

</target>

GMetrics-0.7/docs/gmetrics-SampleReports.html0000644000175000017500000002141612463254302020663 0ustar ebourgebourg GMetrics - GMetrics Sample Reports

Sample Reports

Sample HTML Report (BasicHtmlReportWriter)

See a Sample Report from the org.gmetrics.report.BasicHtmlReportWriter ReportWriter.

Sample Single-Series HTML Report (SingleSeriesHtmlReportWriter)

See a Sample Report from the org.gmetrics.report.SingleSeriesHtmlReportWriter ReportWriter.

Sample XML Report (SingleSeriesHtmlReportWriter)

See a See a Sample Report from the org.gmetrics.report.XmlReportWriter ReportWriter.

Use XSLT to Generate a Custom HTML Report

MrHaki has written a detailed blog post describing How to Create a GMetrics Report with XSLT


GMetrics-0.7/docs/gmetrics-roadmap.html0000644000175000017500000001671512463254302017514 0ustar ebourgebourg GMetrics - GMetrics Road Map

GMetrics - Road Map

Suggestions or preferences? Create a Feature Request.

On the Horizon

  • More metrics

GMetrics-0.7/docs/gmetrics-CoberturaLineCoverageMetric.html0000644000175000017500000002355612463254301023447 0ustar ebourgebourg GMetrics - GMetrics - CoberturaLineCoverage Metric

CoberturaLineCoverage Metric

Metric that measures the code coverage of source lines based on a Cobertura coverage XML file.

Implemented by the org.gmetrics.metric.coverage.CoberturaLineCoverageMetric class.

Metric Properties

The following properties can be configured for this metric within a MetricSet. See Creating a MetricSet for information on the syntax of setting a metric property.

PropertyDescriptionDefault Value
enabledThis boolean property controls whether the metric is enabled. If set to false, then the metric is not included as part of the results or the output reports.true
functionsThis List<String> property contains the names of the functions to be calculated at the class and package levels and (potentially) included within the report(s). Valid values are: - "total" - "average" - "minimum" - "maximum"["total","average"]
coberturaFileThe path to the Cobertura XML file. By default, the path is relative to the classpath. But the path may be optionally prefixed by any of the valid java.net.URL prefixes, such as "file:" (to load from a relative or absolute path on the filesystem), or "http:". This property is REQUIRED.N/A

Known Limitations

This metric does not calculate coverage for Closure Fields (fields initialized to a Closure Expression), unlike some other method-level metrics.

References


GMetrics-0.7/docs/gmetrics-BasicHtmlReportWriter.html0000644000175000017500000003276312463254300022327 0ustar ebourgebourg GMetrics - GMetrics BasicHtmlReportWriter

BasicHtmlReportWriter

Description

The org.gmetrics.report.BasicHtmlReportWriter class produces an HTML report of metric results in a single HTML table, with one row for each package, class or method (or closure field) and one column for each metric value (function) being calculated.

See a Sample Report.

Option Nested Elements

The option element is a child of the report element and defines a report-specific option for a report.

org.gmetrics.report.BasicHtmlReportWriter supports the following options:

AttributeDescriptionRequired
functionsThe (comma-separated) list of functions for which results are included in the report for a particular metric. Multiple metrics are configured by separating them with semicolons (;). By default, all functions provided by a metric are included within the report. Any metrics not explicitly configured include all applicable functions as well. ---- Valid function values are metric-specific, but typically include "total", "average", "minimum" and "maximum". ---- For example, a value of "MethodLineCount=minimum,average" for functions includes only the minimum and average function results for the MethodLineCount metric. Likewise, a value of "ABC=average; CyclomaticComplexity=total,maximum" includes the average function results for the ABC metric and the total and maximum function results for the CyclomaticComplexity metric. Note that all functions provided by any other metrics (not configured) are included.No
levelsThe (comma-separated) list of levels at which results are included in the report for a particular metric. Multiple metrics are configured by separating them with semicolons (;). By default, all levels are included for all metrics within the report. Any metrics not explicitly configured include all applicable levels as well. ---- Valid level values are "package", "class" and "method". ---- For example, a value of "MethodLineCount=class,method" for levels includes only the class and method level results for the MethodLineCount metric. Likewise, a value of "ABC=method; CyclomaticComplexity=package,class" includes the method level results for the ABC metric and the package and class level results for the CyclomaticComplexity metric. Note that all levels are included for any other metrics not explicitly configured.No
reportLevelsThe (comma-separated) list of levels included in the report for all metrics. By default, all levels are included in the report. ---- Valid level values are "package", "class" and "method". ---- For example, a value of "package,method" for reportLevels includes only rows for the package and method.No
metricsThe (comma-separated) list of names of the metrics included in the report. By default, all metrics within the results are included in the report. ---- For example, a metrics value of "ABC, MethodLineCount" includes only the ABC and MethodLineCount metrics within the report.No
outputFileThe path and filename for the output report file.No
titleThe title for the output report.No
writeToStandardOutSet to "true" or true to write out the report to stdout (System.out) instead of writing to a file.No

Example

Here is an example Ant XML build file illustrating configuration of org.gmetrics.report.BasicHtmlReportWriter.

<taskdef name="gmetrics" classname="org.gmetrics.ant.GMetricsTask">
<target name="runGMetrics">

    <gmetrics>
        <report type="org.gmetrics.report.BasicHtmlReportWriter">
            <option name="outputFile" value="SampleGMetricsReport.html" />
            <option name="title" value="Sample" />
        </report>
        <fileset dir="src">
            <include name="**/*.groovy"/>
        </fileset>
    </gmetrics>

</target>

And here is an example illustrating explicit configuration of the metrics, levels and functions to be included within the report:

<taskdef name="gmetrics" classname="org.gmetrics.ant.GMetricsTask">
<target name="runGMetrics">

    <gmetrics>
        <report type="org.gmetrics.report.BasicHtmlReportWriter">
            <option name="outputFile" value="SampleGMetricsReport.html" />
            <option name="title" value="Sample" />
            <option name="metrics" value="CyclomaticComplexity, ABC" />
            <option name="levels" value="CyclomaticComplexity=class,method; ABC=method" />
            <option name="functions" value="ABC=average; CyclomaticComplexity=total,maximum" />
        </report>
        <fileset dir="src">
            <include name="**/*.groovy"/>
        </fileset>
    </gmetrics>

</target>

GMetrics-0.7/docs/gmetrics-CyclomaticComplexityMetric.html0000644000175000017500000003053512463254302023376 0ustar ebourgebourg GMetrics - GMetrics - Cyclomatic Complexity Metric

Cyclomatic Complexity (McCabe) Metric

Calculates the Cyclomatic Complexity Metric for a class or method. Cyclomatic Complexity is a metric of complexity that counts the number of independent paths through the source code, and assigns a single numerical score for each method. The Cyclomatic Complexity score can also serve as an upper bound for the number of test cases that are necessary to achieve a complete branch coverage for a particular method ([1]).

Implemented by the org.gmetrics.metric.cyclomatic.CyclomaticComplexityMetric class.

Metric Calculation Rules

The Cyclomatic Complexity Metric calculates a value for each method. Furthermore, if a class field is initialized to a Closure (ClosureExpression), then that Closure is analyzed just like a method.

  • Start with a initial (default) value of one (1). Add one (1) for each occurrence of each of the following:
    • if statement
    • while statement
    • for statement
    • case statement
    • catch statement
    • && and || boolean operations
    • ?: ternary operator and ?: Elvis operator.
    • ?. null-check operator

Guidelines for Interpreting Cyclomatic Complexity Values

The value of 10 is often considered as the threshold between acceptable (low risk) code and too complex (higher risk). See [1] and [2], for instance. As McCabe ([1]) says, "The particular upper bound that has been used for cyclomatic complexity is 10 which seems like a reasonable, but not magical, upper limit."

Other sources cite 15 as a useful threshold, and/or draw further delineations between low/medium/high/unacceptable complexity values. NDepend ([4]), for instance, recommends that methods with a score of "15 are hard to understand and maintain. Methods where CC is higher than 30 are extremely complex and should be split." On the other hand, [5] categorizes cyclomatic complexity scores into the following levels:

  • 1-10 = Low risk program
  • 11-20 = Moderate risk
  • 21-50 = High risk
  • >50 = Most complex and highly unstable method

Metric Properties

The following properties can be configured for this metric within a MetricSet. See Creating a MetricSet for information on the syntax of setting a metric property.

PropertyDescriptionDefault Value
enabledThis boolean property controls whether the metric is enabled. If set to false, then the metric is not included as part of the results or the output reports.true
functionsThis List<String> property contains the names of the functions to be calculated at the method, class and package levels and (potentially) included within the report(s). Valid values are: - "total" - "average" - "minimum" - "maximum"["total","average"]
includeClosureFieldsThis boolean property controls whether metric values are calculated for Closure Fields and treated as methods. A Closure Field is a field that is initialized to a Closure Expression, e.g., def myField = { println 12 }.true

References


GMetrics-0.7/docs/index.html0000644000175000017500000002275612463254303015370 0ustar ebourgebourg GMetrics - GMetrics - Home

GMetrics

The GMetrics project provides calculation and reporting of size and complexity metrics for Groovy source code. GMetrics scans Groovy source code, applying a set of metrics, and generates an HTML or XML report of the results.

You can run GMetrics using the supplied Ant Task.

Total and average values for the following metrics are provided:

See the site navigation menu for a list of the metrics and reports provided out of the box by GMetrics.

Take a look at a Sample GMetrics Report.

Requirements

The GMetrics project requires:

  • Groovy version 1.7 or later
  • Java 1.5 or later
  • The Log4J jar, version 1.2.13 or later, accessible on the classpath.

Getting GMetrics from the Maven2 Central Repository

For projects built using Maven, GMetrics is now available from the Maven Central Repository.

  • groupId = org.gmetrics
  • artifactId = GMetrics

GMetrics-0.7/docs/gmetrics-EfferentCouplingMetric.html0000644000175000017500000002442312463254302022467 0ustar ebourgebourg GMetrics - GMetrics - Efferent Coupling Metric

Efferent Coupling Metric

Calculates the Efferent Coupling for a package. This is a count of the number of other packages that the classes in a package depend upon, and is an indicator of the package's independence ([1]). This is a package-level metric.

Implemented by the org.gmetrics.metric.coupling.EfferentCouplingMetric class.

Metric Properties

The following properties can be configured for this metric within a MetricSet. See Creating a MetricSet for information on the syntax of setting a metric property.

PropertyDescriptionDefault Value
enabledThis boolean property controls whether the metric is enabled. If set to false, then the metric is not included as part of the results or the output reports.true
functionsThis List<String> property contains the names of the functions to be calculated at the package level. and (potentially) included within the report(s). Valid values are: 
  - "value" - the value for the current package 
  - "total" - the total value for the current package and its descendant packages 
  - "average" - the average value for the current package and its descendant packages 
  - "referencedPackages" - the list of referenced packages
["value","average"]
ignorePackageNamesThe names of packages to ignore when calculating efferent coupling. This pattern string may contain wildcard characters ('*' or '?'); it may also contain more than one pattern, separated by commas.null

References


GMetrics-0.7/docs/gmetrics-MethodLineCountMetric.html0000644000175000017500000002336212463254302022272 0ustar ebourgebourg GMetrics - GMetrics - MethodLineCount Metric

MethodLineCount Metric

Metric for counting the number of lines for methods and closure fields. Note that this metric measures the number of lines from the first line of the method to the last line of the metric. The count includes all program statements, comment lines and whitespace.

Implemented by the org.gmetrics.metric.linecount.MethodLineCountMetric class.

Additional notes

  • If a class field is initialized to a Closure (ClosureExpression), then that Closure is analyzed just like a method.

Metric Properties

The following properties can be configured for this metric within a MetricSet. See Creating a MetricSet for information on the syntax of setting a metric property.

PropertyDescriptionDefault Value
enabledThis boolean property controls whether the metric is enabled. If set to false, then the metric is not included as part of the results or the output reports.true
functionsThis List<String> property contains the names of the functions to be calculated at the method, class and package levels and (potentially) included within the report(s). Valid values are: - "total" - "average" - "minimum" - "maximum"["total","average"]
includeClosureFieldsThis boolean property controls whether metric values are calculated for Closure Fields and treated as methods. A Closure Field is a field that is initialized to a Closure Expression, e.g., def myField = { println 12 }.true

References

  • The The C2 Wiki page for Lines of Code has a good discussion of the perils of using (abusing) lines of code as a measure of just about anything. Note: That page specifically refers to measuring non-comment lines. This metric only measures total lines, including statements, comments and whitespace.

GMetrics-0.7/docs/gmetrics-ant-task.html0000644000175000017500000002534012463254300017603 0ustar ebourgebourg GMetrics - GMetrics Ant Task

GMetrics - Ant Task

Description

The GMetrics Ant Task is implemented by the org.gmetrics.ant.GMetricsTask class.

Attributes

AttributeDescriptionRequired
metricSetFileThe paths to a Groovy MetricSet DSL file. By default, the path specified is relative to the classpath, but it may be optionally prefixed by any of the valid java.net.URL prefixes, such as "file:" (to load from a relative or absolute filesystem path), or "http:". If not specified, it uses the set of Metrics defined by the Default MetricSet.NO

Report Nested Element

The <report> nested element defines the type and options for a report. It includes a type attribute and contains optional nested <option> elements.

NOTE: Currently, org.gmetrics.report.BasicHtmlReportWriter is the only report provided with GMetrics.

AttributeDescriptionRequired
typeThe fully-qualified class name for the report class. This class must implement the org.gmetrics.report.ReportWriter interface.Yes

Option Nested Element

The <option> element is a child of the <report> element and defines a report-specific option for a report. See the Example below.

Fileset Nested Element

At least one fileset nested element is required, and is used to specify the source files that GMetrics should analyze. This is the standard Ant FileSet, and is quite powerful and flexible. See the Apache Ant Manual for more information on FileSets.

Example

Here is an example Ant XML build file.

<taskdef name="gmetrics" classname="org.gmetrics.ant.GMetricsTask">
<target name="runGMetrics">

    <gmetrics metricSetFile="config/metricset.groovy">
        <report type="org.gmetrics.report.BasicHtmlReportWriter">
            <option name="outputFile" value="SampleGMetricsReport.html" />
            <option name="title" value="Sample" />
        </report>
        <fileset dir="src">
            <include name="**/*.groovy"/>
        </fileset>
    </gmetrics>

</target>

Things to note:

  • The fileset specifies that all ".groovy" files are analyzed.
  • Remember that you need the log4j jar (and possibly a "log4j.properties" file) on the classpath.

GMetrics-0.7/docs/gmetrics-MethodCountMetric.html0000644000175000017500000002141112463254302021453 0ustar ebourgebourg GMetrics - GMetrics - MethodCount Metric

MethodCount Metric

Metric for counting the number methods and closure fields within each class. Note that this metric counts methods and constructors.

Implemented by the org.gmetrics.metric.methodcount.MethodCountMetric class.

Additional notes

  • If a class field is initialized to a Closure (ClosureExpression), then that Closure is analyzed just like a method.

Metric Properties

The following properties can be configured for this metric within a MetricSet. See Creating a MetricSet for information on the syntax of setting a metric property.

PropertyDescriptionDefault Value
enabledThis boolean property controls whether the metric is enabled. If set to false, then the metric is not included as part of the results or the output reports.true
functionsThis List<String> property contains the names of the functions to be calculated at the method, class and package levels and (potentially) included within the report(s). Valid values are: - "total" - "average" - "minimum" - "maximum"["total","average"]

GMetrics-0.7/docs/css/0000755000175000017500000000000012623566446014163 5ustar ebourgebourgGMetrics-0.7/docs/css/maven-theme.css0000644000175000017500000000536112463254373017103 0ustar ebourgebourgbody { padding: 0px 0px 10px 0px; } body, td, select, input, li{ font-family: Verdana, Helvetica, Arial, sans-serif; font-size: 13px; } code{ font-family: Courier, monospace; font-size: 13px; } a { text-decoration: none; } a:link { color:#36a; } a:visited { color:#47a; } a:active, a:hover { color:#69c; } #legend li.externalLink { background: url(../images/external.png) left top no-repeat; padding-left: 18px; } a.externalLink, a.externalLink:link, a.externalLink:visited, a.externalLink:active, a.externalLink:hover { background: url(../images/external.png) right center no-repeat; padding-right: 18px; } #legend li.newWindow { background: url(../images/newwindow.png) left top no-repeat; padding-left: 18px; } a.newWindow, a.newWindow:link, a.newWindow:visited, a.newWindow:active, a.newWindow:hover { background: url(../images/newwindow.png) right center no-repeat; padding-right: 18px; } h2 { padding: 4px 4px 4px 6px; border: 1px solid #999; color: #900; background-color: #ddd; font-weight:900; font-size: x-large; } h3 { padding: 4px 4px 4px 6px; border: 1px solid #aaa; color: #900; background-color: #eee; font-weight: normal; font-size: large; } h4 { padding: 4px 4px 4px 6px; border: 1px solid #bbb; color: #900; background-color: #fff; font-weight: normal; font-size: large; } h5 { padding: 4px 4px 4px 6px; color: #900; font-size: normal; } p { line-height: 1.3em; font-size: small; } #breadcrumbs { border-top: 1px solid #aaa; border-bottom: 1px solid #aaa; background-color: #ccc; } #leftColumn { margin: 10px 0 0 5px; border: 1px solid #999; background-color: #eee; } #navcolumn h5 { font-size: smaller; border-bottom: 1px solid #aaaaaa; padding-top: 2px; color: #000; } table.bodyTable th { color: white; background-color: #bbb; text-align: left; font-weight: bold; } table.bodyTable th, table.bodyTable td { font-size: 1em; } table.bodyTable tr.a { background-color: #ddd; } table.bodyTable tr.b { background-color: #eee; } .source { border: 1px solid #999; } dl { padding: 4px 4px 4px 6px; border: 1px solid #aaa; background-color: #ffc; } dt { color: #900; } #organizationLogo img, #projectLogo img, #projectLogo span{ margin: 8px; } #banner { border-bottom: 1px solid #fff; } .errormark, .warningmark, .donemark, .infomark { background: url(../images/icon_error_sml.gif) no-repeat; } .warningmark { background-image: url(../images/icon_warning_sml.gif); } .donemark { background-image: url(../images/icon_success_sml.gif); } .infomark { background-image: url(../images/icon_info_sml.gif); } GMetrics-0.7/docs/css/print.css0000644000175000017500000000033612463254373016026 0ustar ebourgebourg#banner, #footer, #leftcol, #breadcrumbs, .docs #toc, .docs .courtesylinks, #leftColumn, #navColumn { display: none !important; } #bodyColumn, body.docs div.docs { margin: 0 !important; border: none !important } GMetrics-0.7/docs/css/maven-base.css0000644000175000017500000000463612463254373016717 0ustar ebourgebourgbody { margin: 0px; padding: 0px; } img { border:none; } table { padding:0px; width: 100%; margin-left: -2px; margin-right: -2px; } acronym { cursor: help; border-bottom: 1px dotted #feb; } table.bodyTable th, table.bodyTable td { padding: 2px 4px 2px 4px; vertical-align: top; } div.clear{ clear:both; visibility: hidden; } div.clear hr{ display: none; } #bannerLeft, #bannerRight { font-size: xx-large; font-weight: bold; } #bannerLeft img, #bannerRight img { margin: 0px; } .xleft, #bannerLeft img { float:left; } .xright, #bannerRight { float:right; } #banner { padding: 0px; } #banner img { border: none; } #breadcrumbs { padding: 3px 10px 3px 10px; } #leftColumn { width: 170px; float:left; overflow: auto; } #bodyColumn { margin-right: 1.5em; margin-left: 197px; } #legend { padding: 8px 0 8px 0; } #navcolumn { padding: 8px 4px 0 8px; } #navcolumn h5 { margin: 0; padding: 0; font-size: small; } #navcolumn ul { margin: 0; padding: 0; font-size: small; } #navcolumn li { list-style-type: none; background-image: none; background-repeat: no-repeat; background-position: 0 0.4em; padding-left: 16px; list-style-position: outside; line-height: 1.2em; font-size: smaller; } #navcolumn li.expanded { background-image: url(../images/expanded.gif); } #navcolumn li.collapsed { background-image: url(../images/collapsed.gif); } #poweredBy { text-align: center; } #navcolumn img { margin-top: 10px; margin-bottom: 3px; } #poweredBy img { display:block; margin: 20px 0 20px 17px; } #search img { margin: 0px; display: block; } #search #q, #search #btnG { border: 1px solid #999; margin-bottom:10px; } #search form { margin: 0px; } #lastPublished { font-size: x-small; } .navSection { margin-bottom: 2px; padding: 8px; } .navSectionHead { font-weight: bold; font-size: x-small; } .section { padding: 4px; } #footer { padding: 3px 10px 3px 10px; font-size: x-small; } #breadcrumbs { font-size: x-small; margin: 0pt; } .source { padding: 12px; margin: 1em 7px 1em 7px; } .source pre { margin: 0px; padding: 0px; } #navcolumn img.imageLink, .imageLink { padding-left: 0px; padding-bottom: 0px; padding-top: 0px; padding-right: 2px; border: 0px; margin: 0px; } GMetrics-0.7/docs/css/site.css0000644000175000017500000000006712463254373015637 0ustar ebourgebourgtt { font-size: 110%; font-weight: bolder; }GMetrics-0.7/docs/gmetrics-creating-metricset.html0000644000175000017500000003140612463254301021653 0ustar ebourgebourg GMetrics - GMetrics - Creating a MetricSet

GMetrics - Creating a MetricSet

Creating a Groovy MetricSet File

GMetrics provides a Groovy DSL (domain-specific language) for defining MetricSets.

A Sample Groovy MetricSet

The preferred syntax for defining a MetricSet is to specify the list of Metrics using the Metric names. Here is simple example:

metricset {

    description 'A simple MetricSet'

    ClassCount          // specify metric name only (note no "Metric" suffix on metric name)

    FieldCount {        // configure the Metric using a Closure
        functions = ['total']
    }

    MethodCount([functions:['total'])    // configure the Metric using a Map of properties
}

Things to note:

  • The Groovy MetricSet file itself must be accessible on the classpath.
  • This MetricSet includes the ClassCount, FieldCount and MethodCount Metrics.
  • Note that the Metric names do not include the "Metric" suffix (of the Metric implementation class name).
  • Metric properties can be configured either within a Closure (as assignments) or through a Map passed in within the parentheses (after the Metric name).
  • You can specify the Metric names for all Metrics provided with GMetrics. If you provide your own custom Metrics, then you must use the metric(Class) syntax instead -- see below.

A More Comprehensive Groovy MetricSet

Here is an example of a more comprehensive Groovy MetricSet file, illustrating specifying Metrics by Metric class or Metric name, and also nested MetricSet files:

import org.gmetrics.metric.cyclomatic.CyclomaticComplexityMetric

metricset {

    description 'An example MetricSet'

    metricset("MyMetricSet.groovy")

    metric(org.gmetrics.metric.abc.AbcMetric) {
        functions = ['average', 'maximum']
    }

    metric(CyclomaticComplexityMetric)      // specify metric class (see import)

    ClassCount          // specify metric name only (note no "Metric" suffix on metric name)

    FieldCount {          // specify metric name only
        functions = ['total']
    }

    metricset("somepath/MyOtherMetricSet.groovy") {
        ClassLineCount {
            enabled = false
        }

        MethodLineCount name:'CustomMethodLineCount'
    }
}

Things to note:

  • The Groovy MetricSet file itself must be accessible on the classpath.
  • The "outer" metricset defines the contents of the MetricSet (within a closure). It can include an optional description and any combination of metricset (other MetricSets) and metric statements (individual Metrics).

About the "inner" metricset statements:

  • Each metricset statement loads a MetricSet file. The path specifies a Groovy file. By default, the paths specified are relative to the classpath. But these paths may be optionally prefixed by any of the valid java.net.URL prefixes, such as "file:" (to load from a relative or absolute path on the filesystem), or "http:".
  • The MetricSet can be customized by following the MetricSet path with an (optional) closure, containing configuration of the properties for individual metrics within the MetricSet. Specify the name of the metric (as a String), followed by its configuration. (Remember to specify the metric name, not the class name. In most cases, the metric name is the class name without the "Metric" suffix). The name of the Metric is followed by:
    1. A Map of property names and values. See 'MethodLineCount' within the example.
    2. A closure containing property assignments statements. See 'ClassLineCount' within the example.

About the metric statements:

  • Each metric statements loads a single Metric.
  • The metric statement must specify the class name for a Metric. The Metric class must be available on the classpath.
  • A metric can optionally provide configuration of the Metric properties by specifying a closure containing property assignment statements. See AbcMetric within the example. As within MetricSet statements, properties set this way can be of type String, int, long or boolean.
  • The ClassCount and FieldCount Metrics are included by specifying only the Metric name (not the class)
  • Setting the enabled property of a Metric turns it off so that it will not show up in the output report(s).

Turning off a Metric

You can turn off a Metric by setting its enabled boolean property to false. It defaults to true. This applies to all descendants of org.gmetrics.metric.AbstractMetric (i.e., all Metrics supplied with GMetrics).


GMetrics-0.7/README.txt0000644000175000017500000000107512463254744014141 0ustar ebourgebourgGMetrics version 0.7 ------------------------------------------------------------------------------- http://gmetrics.sourceforge.net/ The GMetrics project provides calculation and reporting of size and complexity metrics for Groovy source code. GMetrics scans Groovy source code, applying a set of metrics, and generates an HTML report of the results. DEPENDENCIES CodeNarc requires - Groovy version 1.6 or later - Java 1.5 or later - The Log4J jar, version 1.2.13 or later, accessible on the CLASSPATH (http://logging.apache.org/log4j/index.html). GMetrics-0.7/src/0000755000175000017500000000000012301147514013212 5ustar ebourgebourgGMetrics-0.7/src/main/0000755000175000017500000000000012301147512014134 5ustar ebourgebourgGMetrics-0.7/src/main/resources/0000755000175000017500000000000012623566446016170 5ustar ebourgebourgGMetrics-0.7/src/main/resources/gmetrics-version.txt0000644000175000017500000000000312463237205022210 0ustar ebourgebourg0.7GMetrics-0.7/src/main/resources/gmetrics-basic-html-report.css0000644000175000017500000000274712107724364024053 0ustar ebourgebourgGMetrics-0.7/src/main/resources/gmetrics-base-messages.properties0000644000175000017500000001327112107724364024634 0ustar ebourgebourg# Resource Bundle for GMetrics # Base HTML Report basicHtmlReport.titlePrefix=GMetrics Report basicHtmlReport.reportTimestamp.label=Report timestamp: basicHtmlReport.metricResults.title=Metric Results basicHtmlReport.metricResults.notApplicable=N/A basicHtmlReport.metricDescriptions.title=Metric Descriptions basicHtmlReport.metricResults.nameHeading=Package/Class/Method basicHtmlReport.metricDescriptions.nameHeading=Metric Name basicHtmlReport.metricDescriptions.descriptionHeading=Description # Single-Series HTML Report singleSeriesHtmlReport.reportTimestamp.label = Report timestamp: singleSeriesHtmlReport.packageHeading = Package singleSeriesHtmlReport.classHeading = Class singleSeriesHtmlReport.methodHeading = Method #----------------------------------------------------------------------------------------- # Metrics #----------------------------------------------------------------------------------------- ABC.description.html=Measures size and complexity of source code. See the Wikipedia entry for the ABC metric. ABC.description=Measures size and complexity of source code. See http://c2.com/cgi/wiki?AbcMetric ABC.total=ABC (total) ABC.average=ABC (average) AfferentCoupling.description.html=Counts the number of other packages that depend on the classes within a package. (Package-level). AfferentCoupling.description=Counts the number of other packages that depend on the classes within a package. (Package-level). AfferentCoupling.total=Afferent Coupling (total) AfferentCoupling.value=Afferent Coupling AfferentCoupling.average=Afferent Coupling (average) AfferentCoupling.referencedFromPackages=Afferent Coupling (from packages) ClassCount.description.html=Counts the number classes within each package. ClassCount.description=Counts the number classes within each package. ClassCount.total=Classes (total) ClassCount.average=Classes (average) ClassLineCount.description.html=Counts the number of lines in each class or interface. ClassLineCount.description=Counts the number of lines in each class or interface. ClassLineCount.total=Class Lines (total) ClassLineCount.average=Class Lines (average) CoberturaBranchCoverage.description.html=Measures code coverage of branches (conditionals) from a Cobertura XML file. CoberturaBranchCoverage.description=Measures code coverage of branches (conditionals) from a Cobertura XML file. CoberturaBranchCoverage.total=Branch Coverage (total) CoberturaBranchCoverage.average=Branch Coverage (average) CoberturaBranchCoverage.formatter=org.gmetrics.formatter.PercentageFormatter CoberturaLineCoverage.description.html=Measures code coverage by line from a Cobertura XML file. CoberturaLineCoverage.description=Measures code coverage by line from a Cobertura XML file. CoberturaLineCoverage.total=Line Coverage (total) CoberturaLineCoverage.average=Line Coverage (average) CoberturaLineCoverage.formatter=org.gmetrics.formatter.PercentageFormatter CRAP.description.html=Measures the C.R.A.P. (Change Risk Anti-Patterns) score. It is designed to analyze and predict the amount of effort, pain, and time required to maintain an existing body of code. Given a Java method m, C.R.A.P. for m is calculated as follows: C.R.A.P.(m) = comp(m)^2 * (1 cov(m)/100)^3 + comp(m). Where comp(m) is the cyclomatic complexity of method m, and cov(m) is the test code coverage provided by automated tests. See Blog post describing the CRAP metric. CRAP.description=Measures the C.R.A.P. (Change Risk Anti-Patterns) score. It is designed to analyze and predict the amount of effort, pain, and time required to maintain an existing body of code. Given a Java method m, C.R.A.P. for m is calculated as follows: C.R.A.P.(m) = comp(m)^2 * (1 cov(m)/100)^3 + comp(m). Where comp(m) is the cyclomatic complexity of method m, and cov(m) is the test code coverage provided by automated tests. See http://www.artima.com/weblogs/viewpost.jsp?thread=210575. CRAP.total=CRAP (total) CRAP.average=CRAP (average) CyclomaticComplexity.description.html=Measures the (McCabe) Cyclomatic Complexity of source code. See the Wikipedia entry for Cyclomatic Complexity. CyclomaticComplexity.description=Measures the (McCabe) Cyclomatic Complexity of source code. See http://en.wikipedia.org/wiki/Cyclomatic_complexity. CyclomaticComplexity.total=Complexity (total) CyclomaticComplexity.average=Complexity (average) EfferentCoupling.description.html=Counts the number of other packages that the classes in a package depend upon. (Package-level). EfferentCoupling.description=Counts the number of other packages that the classes in a package depend upon. (Package-level). EfferentCoupling.total=Efferent Coupling (total) EfferentCoupling.value=Efferent Coupling EfferentCoupling.average=Efferent Coupling (average) EfferentCoupling.referencedPackages=Efferent Coupling (to packages) FieldCount.description.html=Counts the number of fields within each class. FieldCount.description=Counts the number of fields within each class. FieldCount.total=Fields (total) FieldCount.average=Fields (average) MethodCount.description.html=Counts the number of methods within each class. MethodCount.description=Counts the number of methods within each class. MethodCount.total=Methods (total) MethodCount.average=Methods (average) MethodLineCount.description.html=Counts the number of lines in each method. MethodLineCount.description=Counts the number of lines in each method. MethodLineCount.total=Method Lines (total) MethodLineCount.average=Method Lines (average) GMetrics-0.7/src/main/resources/gmetrics-single-series-html-report.css0000644000175000017500000000203212107724364025526 0ustar ebourgebourgGMetrics-0.7/src/main/groovy/0000755000175000017500000000000012301147475015471 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/0000755000175000017500000000000012301147476016261 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/0000755000175000017500000000000012623566446020107 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/util/0000755000175000017500000000000012623566446021064 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/util/PathUtil.groovy0000644000175000017500000000414212107724363024055 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.util /** * Contains static utility methods related to file and directory paths. *

* This is an internal class and its API is subject to change. * * @author Chris Mair */ class PathUtil { private static final SEP = '/' static String getName(String filePath) { if (filePath == null) { return null } def separator = normalize(filePath).lastIndexOf('/') return (separator == -1) ? filePath : filePath[separator+1..-1] } static String getParent(String filePath) { def normalizedPath = normalize(filePath) def partList = normalizedPath ? normalizedPath.tokenize(SEP) : [] if (partList.size() < 2) { return null } def parentList = partList[0..-2] return parentList.join(SEP) } static String normalize(String path) { return path ? path.replaceAll('\\\\', SEP) : path } static String toPackageName(String filePath) { if (!filePath) { return null } def normalizedPath = normalize(filePath).trim() if (normalizedPath[0] == SEP) { normalizedPath = normalizedPath[1..-1] } if (normalizedPath[-1] == SEP) { normalizedPath = normalizedPath[0..-2] } return normalizedPath.replace(SEP, '.') } /** * Private constructor. All methods are static. */ private PathUtil() { } }GMetrics-0.7/src/main/groovy/org/gmetrics/util/AstUtil.groovy0000644000175000017500000002457412461576656023740 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.util import org.codehaus.groovy.ast.ClassNode import org.codehaus.groovy.ast.stmt.Statement import org.codehaus.groovy.ast.stmt.ExpressionStatement import org.codehaus.groovy.ast.expr.MethodCallExpression import org.codehaus.groovy.ast.expr.VariableExpression import org.codehaus.groovy.ast.stmt.BlockStatement import org.codehaus.groovy.ast.AnnotatedNode import org.codehaus.groovy.ast.AnnotationNode import org.codehaus.groovy.ast.expr.DeclarationExpression import org.gmetrics.source.SourceCode import org.codehaus.groovy.ast.ASTNode import org.codehaus.groovy.ast.expr.TupleExpression import org.codehaus.groovy.ast.expr.ListExpression import org.codehaus.groovy.ast.expr.ArrayExpression import org.codehaus.groovy.ast.FieldNode import org.codehaus.groovy.ast.expr.ClosureExpression import org.codehaus.groovy.ast.MethodNode /** * Contains static utility methods related to Groovy AST. *

* This is an internal class and its API is subject to change. * * @author Chris Mair */ class AstUtil { static boolean isEmptyMethod(MethodNode methodNode) { isEmptyBlock(methodNode.code) } /** * Return true only if the specified FieldNode has an initial expression that is a Closure * @param fieldNode - the FieldNode * @return true if the field is a Closure field; otherwise return false */ static boolean isClosureField(FieldNode fieldNode) { !isFromGeneratedSourceCode(fieldNode) && fieldNode.initialExpression instanceof ClosureExpression } /** * Return true if the Statement is a block * @param statement - the Statement to check * @return true if the Statement is a block */ static boolean isBlock(Statement statement) { return statement instanceof BlockStatement } /** * Return true if the Statement is a block and it is empty (contains no "meaningful" statements). * This implementation also addresses some "weirdness" around some statement types (specifically finally) * where the BlockStatement answered false to isEmpty() even if it was. * @param statement - the Statement to check * @return true if the BlockStatement is empty */ static boolean isEmptyBlock(Statement statement) { return statement instanceof BlockStatement && (statement.empty || (statement.statements.size() == 1 && statement.statements[0].empty)) } /** * Return the List of Arguments for the specified MethodCallExpression. The returned List contains * either ConstantExpression or MapEntryExpression objects. * @param methodCall - the AST MethodCallExpression * @return the List of argument objects */ static List getMethodArguments(MethodCallExpression methodCall) { def argumentsExpression = methodCall.arguments if (respondsTo(argumentsExpression, 'getExpressions')) { return argumentsExpression.expressions } if (respondsTo(argumentsExpression, 'getMapEntryExpressions')) { return argumentsExpression.mapEntryExpressions } return [] } /** * Return true only if the Statement represents a method call for the specified method object (receiver), * method name, and with the specified number of arguments. * @param stmt - the AST Statement * @param methodObject - the name of the method object (receiver) * @param methodName - the name of the method being called * @param numArguments - the number of arguments passed into the method * @return true only if the Statement is a method call matching the specified criteria */ static boolean isMethodCall(Statement stmt, String methodObject, String methodName, int numArguments) { def match = false if (stmt instanceof ExpressionStatement) { def expression = stmt.expression if (expression instanceof MethodCallExpression) { match = isMethodCall(expression, methodObject, methodName, numArguments) } } return match } /** * Return true only if the MethodCallExpression represents a method call for the specified method object (receiver), * method name, and with the specified number of arguments. * @param methodCall - the AST MethodCallExpression * @param methodObject - the name of the method object (receiver) * @param methodName - the name of the method being called * @param numArguments - the number of arguments passed into the method * @return true only if the method call matches the specified criteria */ static boolean isMethodCall(MethodCallExpression methodCall, String methodObject, String methodName, int numArguments) { def match = isMethodCall(methodCall, methodObject, methodName) return match && getMethodArguments(methodCall).size() == numArguments } /** * Return true only if the MethodCallExpression represents a method call for the specified method * object (receiver) and method name. * @param methodCall - the AST MethodCallExpression * @param methodObject - the name of the method object (receiver) * @param methodName - the name of the method being called * @return true only if the method call matches the specified criteria */ static boolean isMethodCall(MethodCallExpression methodCall, String methodObject, String methodName) { def match = false def objectExpression = methodCall.objectExpression if (objectExpression instanceof VariableExpression) { def objectName = objectExpression.name match = (objectName == methodObject) } return match && isMethodNamed(methodCall, methodName) } /** * Return true only if the MethodCallExpression represents a method call for the specified method name * @param methodCall - the AST MethodCallExpression * @param methodName - the expected name of the method being called * @return true only if the method call name matches */ static boolean isMethodNamed(MethodCallExpression methodCall, String methodName) { def method = methodCall.method return method.properties['value'] == methodName } /** * Return the AnnotationNode for the named annotation, or else null. * Supports Groovy 1.5 and Groovy 1.6. * @param node - the AnnotatedNode * @param name - the name of the annotation * @return the AnnotationNode or else null */ static AnnotationNode getAnnotation(AnnotatedNode node, String name) { def annotations = node.annotations return annotations instanceof Map ? annotations[name] : // Groovy 1.5 annotations.find { annot -> annot.classNode.name == name } // Groovy 1.6 } /** * Return the List of VariableExpression objects referenced by the specified DeclarationExpression. * @param declarationExpression - the DeclarationExpression * @return the List of VariableExpression objects */ static List getVariableExpressions(DeclarationExpression declarationExpression) { def leftExpression = declarationExpression.leftExpression // !important: performance enhancement if (leftExpression instanceof ArrayExpression) { leftExpression.expressions ?: [leftExpression] } else if (leftExpression instanceof ListExpression) { leftExpression.expressions ?: [leftExpression] } else if (leftExpression instanceof TupleExpression) { leftExpression.expressions ?: [leftExpression] } else if (leftExpression instanceof VariableExpression) { [leftExpression] } else { leftExpression.properties['expressions'] ?: [leftExpression] } } /** * Return true if the DeclarationExpression represents a 'final' variable declaration. * * NOTE: THIS IS A WORKAROUND. * * There does not seem to be an easy way to determine whether the 'final' modifier has been * specified for a variable declaration. Return true if the 'final' is present before the variable name. */ static boolean isFinalVariable(DeclarationExpression declarationExpression, SourceCode sourceCode) { if (isFromGeneratedSourceCode(declarationExpression)) { return false } def variableExpressions = AstUtil.getVariableExpressions(declarationExpression) def variableExpression = variableExpressions[0] def startOfDeclaration = declarationExpression.columnNumber def startOfVariableName = variableExpression.columnNumber def sourceLine = sourceCode.lines[declarationExpression.lineNumber-1] def modifiers = (startOfDeclaration >= 0 && startOfVariableName >= 0) ? sourceLine[startOfDeclaration-1..startOfVariableName-2] : '' return modifiers.contains('final') } /** * @return true if the ASTNode was generated (synthetic) rather than from the "real" input source code. */ static boolean isFromGeneratedSourceCode(ASTNode node) { return node.lineNumber < 0 || (node instanceof ClassNode && node.script) } /** * Return true only if the specified object responds to the named method * @param object - the object to check * @param methodName - the name of the method * @return true if the object responds to the named method */ private static boolean respondsTo(Object object, String methodName) { return object.metaClass.respondsTo(object, methodName) } /** * Private constructor. All methods are static. */ private AstUtil() { } }GMetrics-0.7/src/main/groovy/org/gmetrics/util/PropertyUtil.groovy0000644000175000017500000000407612107724363025013 0ustar ebourgebourg/* * Copyright 2008 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.util /** * Contains property-related static utility methods * * @author Chris Mair */ class PropertyUtil { /** * Set the value of the named property on the specified Object from a String value. * If the name specifies an int, long or boolean value then parse the provided String value * and convert to the appropriate type. * @param object - the Object whose field should be set * @param name - the property name to set * @param value - the property value as a String * @throws NoSuchFieldException - if the object does not contain the named field */ static void setPropertyFromString(Object object, String propertyName, String propertyValue) { def property = object.metaClass.getMetaProperty(propertyName) if (property == null) { throw new NoSuchFieldException(propertyName) } Object newPropertyValue = propertyValue if (property.type == int) { newPropertyValue = Integer.parseInt(propertyValue) } if (property.type == long) { newPropertyValue = Long.parseLong(propertyValue) } if (property.type == boolean) { newPropertyValue = Boolean.parseBoolean(propertyValue) } object[propertyName] = newPropertyValue } /** * Private constructor. All methods are static. */ private PropertyUtil() { } }GMetrics-0.7/src/main/groovy/org/gmetrics/util/ClassNameUtil.groovy0000644000175000017500000000361512107724363025033 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.util /** * Static utility methods for dealing with class and package names * * @author Chris Mair */ class ClassNameUtil { private static final UPPER_CASE = ~/[A-Z].*/ static String parentPackageName(String typeName) { if (typeName?.contains('.')) { def lastPeriod = typeName.lastIndexOf('.') return typeName[0..lastPeriod-1] } null } static boolean isPackageName(String name) { def lastPart = getNameOnly(name) return lastPart ? !isCapitalized(lastPart) : false } static boolean isClassName(String fullName) { def name = getNameOnly(fullName) return isCapitalized(name) && isPackageName(parentPackageName(fullName)) } private static String getNameOnly(String packageName) { if (packageName?.contains('.')) { def lastPeriod = packageName.lastIndexOf('.') return packageName.substring(lastPeriod+1) } return packageName ?: null } private static boolean isCapitalized(String name) { return name ? UPPER_CASE.matcher(name).matches() : false } /** * Private constructor. All Methods are static. */ private ClassNameUtil() { } } GMetrics-0.7/src/main/groovy/org/gmetrics/util/ImportUtil.groovy0000644000175000017500000000325712107724363024441 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.util import org.codehaus.groovy.ast.ImportNode /** * Contains static utility methods and constants related to Import statements. *

* This is an internal class and its API is subject to change. * * @author Chris Mair */ class ImportUtil { /** * Return the package name for the specified import statement or else an empty String * @param importNode - the ImportNode for the import * @return the name package being imported (i.e., the import minus the class name/spec) * or an empty String if the import contains no package component */ static String packageNameForImport(ImportNode importNode) { if (importNode.className) { def importClassName = importNode.className def index = importClassName.lastIndexOf('.') (index == -1) ? '' : importClassName[0..index - 1] } else { def packageName = importNode.packageName packageName.endsWith('.') ? packageName[0..-2] : packageName } } } GMetrics-0.7/src/main/groovy/org/gmetrics/util/GMetricsVersion.groovy0000644000175000017500000000175712107724363025417 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.util import org.gmetrics.util.io.ClassPathResource /** * Utility class to return the current GMetrics version number. * * @author Chris Mair */ class GMetricsVersion { private static final VERSION = ClassPathResource.getInputStream('gmetrics-version.txt').text static String getVersion() { return VERSION } } GMetrics-0.7/src/main/groovy/org/gmetrics/util/io/0000755000175000017500000000000012623566446021473 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/util/io/UrlResource.groovy0000644000175000017500000000267012107724363025210 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.util.io /** * A Resource implementation based on java.net.URL. *

* This is an internal class and its API is subject to change. * * @author Chris Mair * @version $Revision: 24 $ - $Date: 2009-12-10 21:17:05 -0500 (Thu, 10 Dec 2009) $ */ class UrlResource implements Resource { final String path /** * Construct a new FileResource * @path - the filesystem path to the file. May be absolute or relative. */ UrlResource(String path) { assert path this.path = path } /** * Open a FileInputStream on the file * @throws IOException - if an error occurs opening the InputStream */ InputStream getInputStream() throws IOException { def url = new URL(path) return url.openStream() } }GMetrics-0.7/src/main/groovy/org/gmetrics/util/io/Resource.groovy0000644000175000017500000000246612107724363024530 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.util.io /** * Defines the interface for objects that represent a resource (e.g. a file) and * provide access to its InputStream. *

* The design of this (resource) framework is heavily influenced by the Resource classes * within The Spring Framework. *

* This is an internal class and its API is subject to change. * * @author Chris Mair * @version $Revision: 24 $ - $Date: 2009-12-10 21:17:05 -0500 (Thu, 10 Dec 2009) $ */ interface Resource { /** * Return the InputStream for this resource. * @throws IOException - if an error occurs opening the InputStream */ InputStream getInputStream() throws IOException }GMetrics-0.7/src/main/groovy/org/gmetrics/util/io/ClassPathResource.groovy0000644000175000017500000000527512107724363026334 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.util.io /** * A Resource implementation for resources available on the classpath. *

* This class also provides a static InputStream getInputStream(String path) convenience method. *

* This is an internal class and its API is subject to change. * * @author Chris Mair * @version $Revision: 24 $ - $Date: 2009-12-10 21:17:05 -0500 (Thu, 10 Dec 2009) $ */ class ClassPathResource implements Resource { final String path /** * Convenience method to open an InputStream on the specified resource path relative the classpath * @path - the path to the resource (file). The path is relative to the classpath, * by default, but may be optionally prefixed by any of the valid java.net.URL prefixes, such * as "file:" (to load from a relative or absolute path on the filesystem), or "http:". The * path must not be empty or null. * @throws IOException - if an error occurs opening the InputStream */ static InputStream getInputStream(String path) throws IOException { return new ClassPathResource(path).getInputStream() } /** * Construct a new ClassPathResource * @path - the path to the resource (file). The path is relative to the classpath, * by default, but may be optionally prefixed by any of the valid java.net.URL prefixes, such * as "file:" (to load from a relative or absolute path on the filesystem), or "http:". The * path must not be empty or null. */ ClassPathResource(String path) { assert path this.path = path } /** * Open an InputStream on the classpath resource path * @throws IOException - if an error occurs opening the InputStream */ InputStream getInputStream() throws IOException { def inputStream = getClass().classLoader.getResourceAsStream(path) if (!inputStream) { throw new FileNotFoundException("File [$path] does not exist or is not accessible") } return inputStream } }GMetrics-0.7/src/main/groovy/org/gmetrics/util/io/DefaultResourceFactory.groovy0000644000175000017500000000330012107724363027351 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.util.io /** * Default implementation of ResourceFactory. *

* This is an internal class and its API is subject to change. * * @author Chris Mair * @version $Revision: 24 $ - $Date: 2009-12-10 21:17:05 -0500 (Thu, 10 Dec 2009) $ */ class DefaultResourceFactory implements ResourceFactory { /** * Return a Resource instance suitable for the specified path. * @param path - the path to the resource. Must not be null or empty. This may be * optionally prefixed by "classpath:" or any of the valid java.net.URL prefixes * (e.g., "file:", "http:") * @throws IOException - if an error occurs opening the InputStream */ Resource getResource(String path) throws IOException { assert path if (path.startsWith('classpath:')) { return new ClassPathResource(path - 'classpath:') } return isUrl(path) ? new UrlResource(path) : new ClassPathResource(path) } private isUrl(String path) { return path =~ /.*\:.*/ } }GMetrics-0.7/src/main/groovy/org/gmetrics/util/io/ResourceFactory.groovy0000644000175000017500000000237712107724363026061 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.util.io /** * Defines the interface for factory objects that create/return Resource instances. *

* This is an internal class and its API is subject to change. * * @author Chris Mair * @version $Revision: 24 $ - $Date: 2009-12-10 21:17:05 -0500 (Thu, 10 Dec 2009) $ */ interface ResourceFactory { /** * Return a Resource instance suitable for the specified path. * @param path - the path to the resource. This may be optionally prefixed by * @throws IOException - if an error occurs opening the InputStream */ Resource getResource(String path) throws IOException }GMetrics-0.7/src/main/groovy/org/gmetrics/util/WildcardPattern.groovy0000644000175000017500000001216312107724363025414 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.util /** * Represents a string pattern that may optionally include wildcards ('*', '**' or '?'), and * provides an API to determine whether that pattern matches a specified input string. *

* The wildcard character '*' within the pattern matches a sequence of zero or more characters within a * single file or directory name in the input string. It does not match a sequence of two or more * dir/file names. For instance, 'a*b' matches 'a12345b' and 'ab', but does NOT match 'a/b' or 'a123/b'. *

* The '**' wildcard matches any sequence of zero or more characters in the input string, including * directory names and separators . It matches any part of the directory tree. For instance, 'a**b' * matches 'a12345b', 'ab', 'a/b' and 'a1/a2/a3b'. *

* The wildcard character '?' within the pattern matches exactly one character in the input string, * excluding the normalized file separator character ('/'). *

* This is an internal class and its API is subject to change. * * @author Chris Mair * @version $Revision: 98 $ - $Date: 2010-03-09 21:45:10 -0500 (Tue, 09 Mar 2010) $ */ class WildcardPattern { private List regexes = [] private List strings = [] private defaultMatches /** * Construct a new WildcardPattern instance on a single pattern or a comma-separated list of patterns. * @param patternString - the pattern string, optionally including wildcard characters ('*' or '?'); * may optionally contain more than one pattern, separated by commas; may be null or empty to always match * @param defaultMatches - a boolean indicating whether matches() should * return true if the pattern string is either empty or null. This parameter is * optional and defaults to true. */ WildcardPattern(String patternString, boolean defaultMatches=true) { this.defaultMatches = defaultMatches def patterns = patternString ? patternString.tokenize(',') : [] patterns.each { pattern -> if (containsWildcards(pattern)) { regexes << convertStringWithWildcardsToRegex(pattern) } else { strings << pattern } } } /** * Return true if the specified String matches the pattern or if the original * patternString (specified in the constructor) was null or empty and the * value for defaultMatches (also specified in the constructor) was true. * @param string - the String to check * @return true if the String matches the pattern */ boolean matches(String string) { if (regexes.empty && strings.empty) { return defaultMatches } return regexes.find { regex -> string ==~ regex } || strings.contains(string) } /** * Return true if the specified String contains one or more wildcard characters ('?' or '*') * @param string - the String to check * @return true if the String contains wildcards */ private static boolean containsWildcards(String string) { return string =~ /\*|\?/ } /** * Convert the specified String, optionally containing wildcards (? or *), to a regular expression String * * @param stringWithWildcards - the String to convert, optionally containing wildcards (? or *) * @return an equivalent regex String * * @throws AssertionError - if the stringWithWildcards is null */ private static String convertStringWithWildcardsToRegex(String stringWithWildcards) { assert stringWithWildcards != null def result = new StringBuffer() def prevCharWasStar = false stringWithWildcards.each {ch -> switch (ch) { case '*': // Single '*' matches single dir/file; Double '*' matches sequence of zero or more dirs/files result << (prevCharWasStar ? /.*/ : /[^\/]*/) prevCharWasStar = !prevCharWasStar break; case '?': // Any character except the normalized file separator ('/') result << /[^\/]/ break; case ['$', '|', '[', ']', '(', ')', '.', ':', '{', '}', '\\', '^', '+']: result << '\\' + ch break; default: result << ch } } return result } }GMetrics-0.7/src/main/groovy/org/gmetrics/util/Calculator.groovy0000644000175000017500000000223012107724363024410 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.util /** * Contains static utility methods related to mathematical calculations * * @author Chris Mair */ class Calculator { static BigDecimal calculateAverage(BigDecimal sum, int count, int scale) { if(sum && count) { def result = sum / count return result.setScale(scale, BigDecimal.ROUND_HALF_UP) } return 0.0.setScale(scale, BigDecimal.ROUND_HALF_UP) } // Private constructor. All methods are static. private Calculator() { } } GMetrics-0.7/src/main/groovy/org/gmetrics/result/0000755000175000017500000000000012623566446021425 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/result/MutableMapMetricResult.groovy0000644000175000017500000000347612463030155027261 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.result import org.gmetrics.metric.Metric import org.gmetrics.metric.MetricLevel /** * A MetricResult for arbitrary values, backed by a mutable Map. * * @author Chris Mair */ class MutableMapMetricResult implements MetricResult { final Metric metric final MetricLevel metricLevel final Integer lineNumber = null final map int count = 0 /** * Construct a new instance * @param metric - the Metric to which this result applies * @param metricLevel - the metric level for this result * @param map - the Map of metric result values */ MutableMapMetricResult(Metric metric, MetricLevel metricLevel, Map map) { assert metric assert metricLevel assert map != null this.metric = metric this.metricLevel = metricLevel this.map = new HashMap(map) } @Override Object getAt(String name) { return map[name] } void putAt(String name, Object newValue) { map[name] = newValue } @Override String toString() { "MutableMapMetricResult[metric=${metric.name}, count=$count, $map]" } }GMetrics-0.7/src/main/groovy/org/gmetrics/result/MetricResultBuilder.groovy0000644000175000017500000001206112107724364026615 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.result import static FunctionNames.* import org.gmetrics.metric.Metric import org.gmetrics.metric.MetricLevel import org.gmetrics.util.Calculator /** * A Builder for MetricResult objects. * * @author Chris Mair */ class MetricResultBuilder { Metric metric MetricLevel metricLevel int scale = 1 /** * Calculate the aggregate metric results for the specified child metric results. The metric and * metricLevel properties of this object must be set before calling this method. * * @param children - the optional collection of results from children * @param lineNumber - the line number for the source element (AST) that triggered this metric result; may be null * @param overrideValues - the optional Map of functionName:resultValue to specify override values * for the specified functions; may be null or empty to specify no predefined values * @return a MetricResult for the calculated values */ MetricResult createAggregateMetricResult( Collection children, Integer lineNumber, Map overrideValues=null) { assert metric assert metricLevel assert children != null def count = calculateCount(children) Map functionValues = calculateFunctions(children, count, overrideValues) return new NumberMetricResult(metric, metricLevel, functionValues, lineNumber, count) } String toString() { "MetricResultBuilder[metric=$metric, level=$metricLevel]" } //------------------------------------------------------------------------------------ // Helper Methods //------------------------------------------------------------------------------------ private Map calculateFunctions(Collection children, int count, Map overrideValues) { Map functionValues = [:] def sum = calculateTotal(children) functionValues[TOTAL] = total(sum, overrideValues) functionValues[AVERAGE] = average(sum, count, overrideValues) functionValues[MINIMUM] = minimum(children, overrideValues) functionValues[MAXIMUM] = maximum(children, overrideValues) return functionValues } private int calculateCount(Collection children) { children.inject(0) { value, child -> value + child.count } } private Object total(sum, Map overrideValues) { return shouldCalculateFunction(TOTAL, overrideValues) ? sum : overrideValues?.get(TOTAL) } private Object calculateTotal(Collection children) { return children.inject(0) { value, child -> return child[TOTAL] ? value + child[TOTAL] : value } } private Object minimum(Collection children, Map overrideValues) { return shouldCalculateFunction(MINIMUM, overrideValues) ? calculateMinimum(children) : overrideValues?.get(MINIMUM) } private Object calculateMinimum(Collection children) { def minChild = children.min { child -> child[MINIMUM] } return minChild != null ? minChild[MINIMUM] : 0 } private Object maximum(Collection children, Map overrideValues) { return shouldCalculateFunction(MAXIMUM, overrideValues) ? calculateMaximum(children) : overrideValues?.get(MAXIMUM) } private Object calculateMaximum(Collection children) { def maxChild = children.max { child -> child[MAXIMUM] } return maxChild != null ? maxChild[MAXIMUM] : 0 } private Object average(sum, int count, Map overrideValues) { return shouldCalculateFunction(AVERAGE, overrideValues) ? Calculator.calculateAverage(sum, count, scale) : overrideValues?.get(AVERAGE) } private boolean shouldCalculateFunction(String functionName, Map functionValues) { return isFunctionSpecifiedOrImplied(functionName) && !functionValues?.containsKey(functionName) } private boolean isFunctionSpecifiedOrImplied(String functionName) { // Ensure TOTAL is included if AVERAGE is included return functionName in metric.functions || (functionName == FunctionNames.TOTAL && metric.functions.contains(FunctionNames.AVERAGE)) } }GMetrics-0.7/src/main/groovy/org/gmetrics/result/MetricResult.groovy0000644000175000017500000000371012107724364025307 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.result import org.gmetrics.metric.Metric import org.gmetrics.metric.MetricLevel /** * Represents the result from applying a single metric (to a package, class or method) * * @author Chris Mair * @version $Revision: 163 $ - $Date: 2011-10-02 21:55:46 -0400 (Sun, 02 Oct 2011) $ */ interface MetricResult { /** * @return the Metric for which this object represents results. */ Metric getMetric() /** * @return the MetricLevel for this metric result */ MetricLevel getMetricLevel() /** * Return the count of the nodes/results that are descendants. For instance, if this result * represents a class, then the count includes all of the method-level result children * (if applicable for the metric). * * @return the count of metric result children */ int getCount() /** * Return the metric result value for the named function (e.g. "average", "total") * @param propertyName - the function name * @return the named function value or null if that function is not supported */ Object getAt(String propertyName) /** * Return the line number associated with this metric result (i.e., method or class) * @return an Integer; may be null */ Integer getLineNumber() }GMetrics-0.7/src/main/groovy/org/gmetrics/result/ClassMetricResult.groovy0000644000175000017500000000250112107724364026272 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.result /** * Represents the results for a single metric for a single class * * @author Chris Mair * @version $Revision: 180 $ - $Date: 2011-11-27 21:56:48 -0500 (Sun, 27 Nov 2011) $ */ class ClassMetricResult { MetricResult classMetricResult Map methodMetricResults = [:] ClassMetricResult(MetricResult metricResult, Map methodMetricResults=null) { this.classMetricResult = metricResult this.methodMetricResults = methodMetricResults } String toString() { return "ClassMetricResult[classMetricResult=$classMetricResult, methodMetricResults=$methodMetricResults]" } }GMetrics-0.7/src/main/groovy/org/gmetrics/result/NumberMetricResult.groovy0000644000175000017500000000415612107724363026464 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.result import org.gmetrics.metric.Metric import org.gmetrics.metric.MetricLevel /** * A MetricResult for numbers (integers, BigDecimals, etc.), that has separate values for * total, average, minimum and maximum. An instance of this class is immutable. * * @author Chris Mair */ class NumberMetricResult implements MetricResult { final Metric metric final MetricLevel metricLevel final number final Integer lineNumber final int count final Map values /** * Construct a new instance * @param metric - the Metric to which this result applies * @param metricLevel - the metric level for this result * @param values - the Map of values by function name; should include entries for total,average,minimum,maximum as appropriate * @param lineNumber - the line number for the source element (AST) that triggered this metric result; may be null */ NumberMetricResult(Metric metric, MetricLevel metricLevel, Map values, Integer lineNumber=null, int count=1) { assert metric assert metricLevel assert values != null this.metric = metric this.metricLevel = metricLevel this.values = values this.lineNumber = lineNumber this.count = count } Object getAt(String name) { return values[name] } String toString() { "NumberMetricResult[metric=${metric.name}, $values]" } }GMetrics-0.7/src/main/groovy/org/gmetrics/result/MapMetricResult.groovy0000644000175000017500000000352512107724364025751 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.result import org.gmetrics.metric.Metric import org.gmetrics.metric.MetricLevel /** * A MetricResult for arbitrary values, backed by a Map. An instance of this class is immutable. * * @author Chris Mair */ class MapMetricResult implements MetricResult { final Metric metric final MetricLevel metricLevel final Integer lineNumber = null final int count private final map /** * Construct a new instance * @param metric - the Metric to which this result applies * @param metricLevel - the metric level for this result * @param map - the Map of metric result values * @param count - the count value; optional, defaults to 1 */ MapMetricResult(Metric metric, MetricLevel metricLevel, Map map, int count=1) { assert metric assert metricLevel assert map != null this.metric = metric this.metricLevel = metricLevel this.map = new HashMap(map) this.count = count } @Override Object getAt(String name) { return map[name] } @Override String toString() { "MapMetricResult[metric=${metric.name}, $map]" } }GMetrics-0.7/src/main/groovy/org/gmetrics/result/SingleNumberMetricResult.groovy0000644000175000017500000000403312107724363027620 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.result import org.gmetrics.metric.Metric import org.gmetrics.metric.MetricLevel /** * A MetricResult for numbers (integers, BigDecimals, etc.), that returns the same, single value for * total, average, minimum and maximum. An instance of this class is immutable. * * @author Chris Mair */ class SingleNumberMetricResult implements MetricResult { final Metric metric final MetricLevel metricLevel final number final Integer lineNumber final int count = 1 /** * Construct a new instance * @param metric - the Metric to which this result applies * @param metricLevel - the metric level for this result * @param number - the single value to use for total, average, minimum, and maximum * @param lineNumber - the line number for the source element (AST) that triggered this metric result; may be null */ SingleNumberMetricResult(Metric metric, MetricLevel metricLevel, number, Integer lineNumber=null) { assert metric assert metricLevel assert number != null this.metric = metric this.metricLevel = metricLevel this.number = number this.lineNumber = lineNumber } @Override Object getAt(String name) { return number } @Override String toString() { "SingleNumberMetricResult[metric=${metric.name}, $number]" } }GMetrics-0.7/src/main/groovy/org/gmetrics/result/FunctionNames.groovy0000644000175000017500000000204312107724364025434 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.result /** * Contains constants for function names * * @author Chris Mair */ class FunctionNames { // Consider making this into a first-class object and/or Enum. public static final VALUE = 'value' public static final TOTAL = 'total' public static final AVERAGE = 'average' public static final MINIMUM = 'minimum' public static final MAXIMUM = 'maximum' } GMetrics-0.7/src/main/groovy/org/gmetrics/result/MethodKey.groovy0000644000175000017500000000336612107724364024565 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.result import org.codehaus.groovy.ast.MethodNode /** * Serves as a key in the Map of method --> MetricResult. This encapsulates the unique method information * that enables distinguishing between multiple overridden methods (same name, different parameters). * * @author Chris Mair */ class MethodKey { final methodName final signature private final comparableString MethodKey(String methodName) { assert methodName this.methodName = methodName this.comparableString = methodName } MethodKey(MethodNode methodNode) { assert methodNode this.methodName = methodNode.name this.signature = methodNode.typeDescriptor this.comparableString = methodNode.typeDescriptor } @Override boolean equals(Object object) { return object instanceof MethodKey && object.comparableString == comparableString } @Override int hashCode() { return comparableString.hashCode() } @Override String toString() { return '{' + comparableString + '}' } } GMetrics-0.7/src/main/groovy/org/gmetrics/analyzer/0000755000175000017500000000000012623566446021734 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/analyzer/SourceAnalyzer.groovy0000644000175000017500000000300112107724360026127 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.analyzer import org.gmetrics.metricset.MetricSet import org.gmetrics.resultsnode.ResultsNode /** * The interface for objects that can analyze the source files within one or more directory * trees using a specified MetricSet and produce report results. * * @author Chris Mair * @version $Revision: 69 $ - $Date: 2010-02-12 22:30:12 -0500 (Fri, 12 Feb 2010) $ */ interface SourceAnalyzer { /** * Analyze all source code using the specified set of Metrics and return the results. * @param metricSet - the MetricSet to apply to each source component; must not be null. * @return the results from applying the metrics to all of the source */ ResultsNode analyze(MetricSet metricSet) /** * Return the List of source directories to be analyzed. May be empty; may not be null. */ List getSourceDirectories() }GMetrics-0.7/src/main/groovy/org/gmetrics/analyzer/AnalysisContext.groovy0000644000175000017500000000223712107724360026323 0ustar ebourgebourg/* * Copyright 2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.analyzer import org.gmetrics.metricset.MetricSet /** * Holds information related to the configuration and context for the source code analysis. * * @author Chris Mair * @version $Revision: 92 $ - $Date: 2010-03-05 20:50:48 -0500 (Fri, 05 Mar 2010) $ */ class AnalysisContext { /** * The List of source directories being analyzed. May be null or empty. */ List sourceDirectories /** * The MetricSet containing the metrics being applied. */ MetricSet metricSet } GMetrics-0.7/src/main/groovy/org/gmetrics/report/0000755000175000017500000000000012623566446021422 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/report/MetricCriteriaFilterHelper.groovy0000644000175000017500000000453412107724361030100 0ustar ebourgebourg/* * Copyright 2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.report import org.gmetrics.metric.Metric /** * Provides common static helper methods for classes that provides data and behavior for enabling reports * to filter the results included within a report based a filter map keyed on the metric name. * * @author Chris Mair * @version $Revision: 118 $ - $Date: 2010-07-10 21:55:10 -0400 (Sat, 10 Jul 2010) $ */ class MetricCriteriaFilterHelper { protected static boolean includesName(Map criteriaMap, Metric metric, String name) { if (criteriaMap == null) { return true } def matchingNames = criteriaMap[metric.name] return matchingNames == null || name in matchingNames } /** * Parse the criteria string * @param criteria - the String of the form =,; =, */ protected static Map parseCriteria(String criteria) { assert criteria def criteriaMap = [:] def metricCriteriaStrings = criteria.tokenize(';') metricCriteriaStrings.each { metricCriteria -> parseCriteriaForSingleMetric(criteriaMap, metricCriteria) } return criteriaMap } protected static void parseCriteriaForSingleMetric(Map criteriaMap, String metricCriteria) { def tokens = metricCriteria.tokenize('=') assert tokens.size() == 2, "Each metric criteria must be of the form: =, but was [$metricCriteria]" def name = tokens[0].trim() criteriaMap[name] = parseCommaSeparatedList(tokens[1]) } private static List parseCommaSeparatedList(String values) { values.tokenize(',').collect { it.trim().toLowerCase() } } } GMetrics-0.7/src/main/groovy/org/gmetrics/report/LevelsCriteriaFilter.groovy0000644000175000017500000000261012107724361026740 0ustar ebourgebourg/* * Copyright 2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.report import org.gmetrics.metric.Metric import org.gmetrics.metric.MetricLevel /** * Provides data and behavior for enabling reports to filter the set of metrics included in a report. * This class is intended to be used as a Groovy @Mixin for ReportWriter classes. * * @author Chris Mair * @version $Revision: 118 $ - $Date: 2010-07-10 21:55:10 -0400 (Sat, 10 Jul 2010) $ */ class LevelsCriteriaFilter { private Map levelsCriteriaMap void setLevels(String criteria) { levelsCriteriaMap = MetricCriteriaFilterHelper.parseCriteria(criteria) } boolean includesLevel(Metric metric, MetricLevel level) { return MetricCriteriaFilterHelper.includesName(levelsCriteriaMap, metric, level.name) } }GMetrics-0.7/src/main/groovy/org/gmetrics/report/AbstractReportWriter.groovy0000644000175000017500000001241112107724361027011 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.report import org.apache.log4j.Logger import org.gmetrics.resultsnode.ResultsNode import org.gmetrics.analyzer.AnalysisContext import org.gmetrics.metric.Metric import org.gmetrics.formatter.FormatterFactory import org.gmetrics.formatter.Formatter import org.gmetrics.metricset.MetricSet /** * Abstract superclass for ReportWriter implementation classes. *

* Subclasses must implement the writeReport(Writer, ResultsNode, AnalysisContext) method * and define a defaultOutputFile property. * * @author Chris Mair */ abstract class AbstractReportWriter implements ReportWriter { protected static final BASE_MESSAGES_BUNDLE = "gmetrics-base-messages" protected static final CUSTOM_MESSAGES_BUNDLE = "gmetrics-messages" protected static final GMETRICS_URL = "http://www.gmetrics.org" String outputFile Object writeToStandardOut @SuppressWarnings(['LoggerWithWrongModifiers', 'FieldName']) protected final LOG = Logger.getLogger(getClass()) protected customMessagesBundleName = CUSTOM_MESSAGES_BUNDLE protected resourceBundle protected Map formatters = [:] protected formatterFactory = new FormatterFactory() // Allow tests to override these protected initializeResourceBundle = { initializeDefaultResourceBundle() } protected getTimestamp = { new Date() } protected abstract void writeReport(Writer writer, ResultsNode resultsNode, AnalysisContext analysisContext) void writeReport(ResultsNode resultsNode, AnalysisContext analysisContext) { assert analysisContext assert analysisContext.metricSet initializeResourceBundle() initializeFormatters(analysisContext.metricSet) if (isWriteToStandardOut()) { writeReportToStandardOut(resultsNode, analysisContext) } else { writeReportToFile(resultsNode, analysisContext) } } private void writeReportToStandardOut(ResultsNode resultsNode, AnalysisContext analysisContext) { def writer = new OutputStreamWriter(System.out) writeReport(writer, resultsNode, analysisContext) } private void writeReportToFile(ResultsNode resultsNode, AnalysisContext analysisContext) { def outputFilename = outputFile ?: getProperty('defaultOutputFile') def file = new File(outputFilename) file.withWriter { writer -> writeReport(writer, resultsNode, analysisContext) } LOG.info("Report file [$outputFilename] created.") } protected void initializeDefaultResourceBundle() { def baseBundle = ResourceBundle.getBundle(BASE_MESSAGES_BUNDLE) resourceBundle = baseBundle try { resourceBundle = ResourceBundle.getBundle(customMessagesBundleName) LOG.info("Using custom message bundle [$customMessagesBundleName]") resourceBundle.setParent(baseBundle) } catch(MissingResourceException) { LOG.info("No custom message bundle found for [$customMessagesBundleName]. Using default messages.") } } protected String getResourceBundleString(String resourceKey, String defaultString='?') { def string = defaultString try { string = resourceBundle.getString(resourceKey) } catch (MissingResourceException e) { LOG.warn("No string found for resourceKey=[$resourceKey]") } return string } @SuppressWarnings('ReturnNullFromCatchBlock') protected String getResourceBundleStringOrNull(String resourceKey) { try { return resourceBundle.getString(resourceKey) } catch (MissingResourceException e) { return null } } protected void initializeFormatters(MetricSet metricSet) { metricSet.metrics.each { metric -> def lookupKey = metric.name + '.formatter' def formatterSpec = getResourceBundleStringOrNull(lookupKey) formatters[metric.name] = formatterSpec ? formatterFactory.getFormatter(formatterSpec) : null } } protected String formatMetricResultValue(String metricName, Object value) { def formatter = formatters[metricName] return formatter ? formatter.format(value) : value } protected String getFormattedTimestamp() { def dateFormat = java.text.DateFormat.getDateTimeInstance() return dateFormat.format(getTimestamp()) } private boolean isWriteToStandardOut() { writeToStandardOut == true || writeToStandardOut == 'true' } }GMetrics-0.7/src/main/groovy/org/gmetrics/report/XmlReportWriter.groovy0000644000175000017500000001555712463030012026010 0ustar ebourgebourg/* * Copyright 2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.report import org.gmetrics.metricset.MetricSet import org.gmetrics.resultsnode.ResultsNode import groovy.xml.StreamingMarkupBuilder import org.gmetrics.metric.Metric import org.gmetrics.result.MetricResult import org.gmetrics.metric.MetricLevel import org.gmetrics.analyzer.AnalysisContext import org.gmetrics.util.GMetricsVersion /** * ReportWriter that generates an XML report. The XML includes * and the metric descriptions for each Metric within the configured MetricSet. * * @author Chris Mair */ @Mixin([MetricsCriteriaFilter, LevelsCriteriaFilter, FunctionsCriteriaFilter]) class XmlReportWriter extends AbstractReportWriter { public static final DEFAULT_OUTPUT_FILE = 'GMetricsXmlReport.xml' static defaultOutputFile = DEFAULT_OUTPUT_FILE String title @Override protected void writeReport(Writer writer, ResultsNode resultsNode, AnalysisContext analysisContext) { assert resultsNode assert writer def builder = new StreamingMarkupBuilder() def xml = builder.bind { mkp.xmlDeclaration() GMetrics(url:GMETRICS_URL, version:GMetricsVersion.getVersion()) { out << buildReportElement() out << buildProjectElement(analysisContext) out << buildPackageElements(resultsNode) out << buildMetricsElement(analysisContext.metricSet) } } writer << xml } //-------------------------------------------------------------------------- // Internal Helper Methods //-------------------------------------------------------------------------- private buildReportElement() { return { Report(timestamp:getFormattedTimestamp()) } } private buildProjectElement(AnalysisContext analysisContext) { return { Project(title:title) { analysisContext.sourceDirectories.each { sourceDirectory -> SourceDirectory(sourceDirectory) } } } } private buildPackageElements(resultsNode) { return buildElement(resultsNode) } private buildElement(ResultsNode resultsNode) { switch(resultsNode.level) { case MetricLevel.PACKAGE: return buildPackageElement(resultsNode) case MetricLevel.CLASS: return buildClassElement(resultsNode) case MetricLevel.METHOD: return buildMethodElement(resultsNode) } } private buildPackageElement(resultsNode) { def elementName = isRoot(resultsNode) ? 'PackageSummary' : 'Package' def attributeMap = isRoot(resultsNode) ? [:] : [path:resultsNode.path, name:resultsNode.packageName] return { "$elementName"(attributeMap) { out << buildMetricElements(resultsNode.metricResults, resultsNode.level) resultsNode.children.each { childName, childResultsNode -> if (!isPackage(childResultsNode)) { out << buildElement(childResultsNode) } } } resultsNode.children.each { childName, childResultsNode -> if (isPackage(childResultsNode)) { out << buildElement(childResultsNode) } } } } private buildClassElement(resultsNode) { return { Class([name:resultsNode.name, fileName:resultsNode.fileName, filePath:resultsNode.filePath]) { // Class([name:resultsNode.name]) { out << buildMetricElements(resultsNode.metricResults, resultsNode.level) resultsNode.children.each { childName, childResultsNode -> out << buildElement(childResultsNode) } } } } private buildMethodElement(resultsNode) { return { Method([name:resultsNode.name, signature:resultsNode.signature]) { out << buildMetricElements(resultsNode.metricResults, resultsNode.level) resultsNode.children.each { childName, childResultsNode -> out << buildElement(childResultsNode) } } } } private buildMetricElements(metricResults, MetricLevel level) { return { metricResults.each { metricResult -> out << buildMetricElement(metricResult, level) } } } private buildMetricElement(MetricResult metricResult, MetricLevel level) { def metric = metricResult.getMetric() return { if (includesMetric(metric) && includesLevel(metric, level) && level >= metric.getBaseLevel()) { def attributes = [name: metric.name] metric.functions.each { functionName -> if (includesFunction(metric, functionName) && metricResult[functionName] != null) { attributes[functionName] = metricResult[functionName].toString() } } MetricResult(attributes) } } } private boolean isRoot(results) { results.path == null } private buildMetricsElement(MetricSet metricSet) { def metrics = metricSet.metrics.findAll { metric -> includesMetric(metric) } def sortedMetrics = metrics.toList().sort { metric -> metric.name } return { Metrics { sortedMetrics.each { Metric metric -> def description = getDescriptionForMetric(metric) Metric(name:metric.name) { Description(cdata(description)) } } } } } protected String getDescriptionForMetric(Metric metric) { def resourceKey = metric.name + '.description' return getResourceBundleString(resourceKey, "No description provided for metric named [$metric.name]") } private boolean isPackage(resultsNode) { return resultsNode.level == MetricLevel.PACKAGE } private cdata(String text) { return { unescaped << "" } } } GMetrics-0.7/src/main/groovy/org/gmetrics/report/ReportWriter.groovy0000644000175000017500000000202412107724362025325 0ustar ebourgebourg/* * Copyright 2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.report import org.gmetrics.resultsnode.ResultsNode import org.gmetrics.analyzer.AnalysisContext /** * Common interface for report writer implementations * * @author Chris Mair * @version $Revision: 91 $ - $Date: 2010-03-05 20:21:49 -0500 (Fri, 05 Mar 2010) $ */ interface ReportWriter { void writeReport(ResultsNode resultsNode, AnalysisContext analysisContext) }GMetrics-0.7/src/main/groovy/org/gmetrics/report/SeriesValue.groovy0000644000175000017500000000207712107724361025113 0ustar ebourgebourg/* * Copyright 2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.report /** * Holder for a single data item within a series * * @author Chris Mair * @version $Revision: 119 $ - $Date: 2010-07-11 21:56:11 -0400 (Sun, 11 Jul 2010) $ */ class SeriesValue { final String name final Object value SeriesValue(String name, Object value) { this.name = name this.value = value } String toString() { return "{$name:$value}" } } GMetrics-0.7/src/main/groovy/org/gmetrics/report/SingleSeriesHtmlReportWriter.groovy0000644000175000017500000001454512107724361030501 0ustar ebourgebourg/* * Copyright 2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.report import org.gmetrics.resultsnode.ResultsNode import org.gmetrics.analyzer.AnalysisContext import groovy.xml.StreamingMarkupBuilder import org.gmetrics.util.io.ClassPathResource import org.gmetrics.util.GMetricsVersion /** * ReportWriter that generates a HTML report for a single series of metric values. This single * series is specified by a single Metric, a level (package, class or method) and a single * function (total, average, minimum, maximum). *

* The metric, level and function properties are required (must * be non-null and non-empty). These three properties uniquely identify a single series of metric values. *

* The metric property must specify the name (case-sensitive) of a single Metric (for example * "CyclomaticComplexity") included in the analysis results. *

* The level property must be set to one of: "package", "class" or "method". *

* The function property must be set to the name of a function supported by the * metric, typically one of: "total", "average", "minimum" or "maximum". *

* The sort property is optional, and if not null or empty, must either have * the value of "ascending" or "descending", and causes the results to be sorted numerically in either ascending * or descending order. *

* The maxResults property is optional. A value of null, empty or 0 * means no limit. Otherwise, the value must be positive, and limits the number of results returned. *

* The greaterThan property is optional. The value specifies a threshold -- only results * with a larger value are returned. A value of null or empty means no threshold. *

* The lessThan property is optional. The value specifies a threshold -- only results * with a smaller value are returned. A value of null or empty means no threshold. * * @author Chris Mair */ @Mixin(SingleSeriesCriteriaFilter) class SingleSeriesHtmlReportWriter extends AbstractReportWriter { public static final DEFAULT_OUTPUT_FILE = 'GMetricsSingleSeriesReport.html' protected static final DEFAULT_CSS_FILE = 'gmetrics-single-series-html-report.css' protected static final DEFAULT_TITLE = "GMetrics Report" static defaultOutputFile = DEFAULT_OUTPUT_FILE String title = DEFAULT_TITLE String subtitle @Override protected void writeReport(Writer writer, ResultsNode resultsNode, AnalysisContext analysisContext) { assert resultsNode assert writer def seriesData = buildSeriesData(resultsNode, analysisContext.metricSet) def builder = new StreamingMarkupBuilder() def html = builder.bind { html { out << buildHeaderSection() out << buildBodySection(seriesData) } } writer << html LOG.info("Report created") } private buildHeaderSection() { return { head { title(title) out << buildCSS() } } } private buildCSS() { return { def cssInputStream = ClassPathResource.getInputStream(DEFAULT_CSS_FILE) assert cssInputStream, "CSS File [$DEFAULT_CSS_FILE] not found" def css = cssInputStream.text unescaped << css } } private buildBodySection(List seriesData) { return { body { h1(title) if (subtitle) { h2(subtitle) } out << buildReportTimestamp() out << buildResultsTable(seriesData) out << buildVersionFooter() } } } private buildReportTimestamp() { return { def timestamp = getFormattedTimestamp() p(getResourceBundleString('singleSeriesHtmlReport.reportTimestamp.label') + " $timestamp", class:'timestamp') } } private buildResultsTable(List seriesData) { return { table { tr(class:'tableHeader') { th(getSeriesValueNameHeading()) th(getMetricResultColumnHeading(metric, function)) } seriesData.each { seriesValue -> out << buildSeriesValueRow(seriesValue) } } } } private String getSeriesValueNameHeading() { final KEYS = [ 'package':'singleSeriesHtmlReport.packageHeading', 'class':'singleSeriesHtmlReport.classHeading', 'method':'singleSeriesHtmlReport.methodHeading' ] def key = KEYS[level.toLowerCase()] return getResourceBundleString(key) } private buildSeriesValueRow(SeriesValue seriesValue) { def formattedValue = formatMetricResultValue(metric, seriesValue.value) return { tr { td(seriesValue.name, class:'seriesDataName') td(formattedValue, class:'seriesDataValue') } } } private String getMetricResultColumnHeading(String metricName, String functionName) { def resourceKey = metricName + '.' + functionName return getResourceBundleString(resourceKey, "$metricName ($functionName)") } private buildVersionFooter() { def versionText = 'GMetrics ' + GMetricsVersion.getVersion() return { p(class:'version') { a(versionText, href:GMETRICS_URL) } } } } GMetrics-0.7/src/main/groovy/org/gmetrics/report/AbstractMetricCriteriaFilter.groovy0000644000175000017500000000137212107724361030421 0ustar ebourgebourgpackage org.gmetrics.report /* * Copyright 2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ class AbstractMetricCriteriaFilter { // Placeholder -- workaround for SVN / Maven issue } GMetrics-0.7/src/main/groovy/org/gmetrics/report/BasicHtmlReportWriter.groovy0000644000175000017500000002352112463027764027130 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.report import org.gmetrics.resultsnode.ResultsNode import org.gmetrics.util.io.ClassPathResource import groovy.xml.StreamingMarkupBuilder import org.gmetrics.metricset.MetricSet import org.gmetrics.metric.MetricLevel import org.gmetrics.analyzer.AnalysisContext import org.gmetrics.metric.Metric import org.gmetrics.util.GMetricsVersion /** * ReportWriter that generates a basic HTML report. The HTML includes a table containing * a row for each package, class and method, and the metric values for each Metric * within the passed-in MetricSet. * * @author Chris Mair */ @Mixin([MetricsCriteriaFilter, LevelsCriteriaFilter, FunctionsCriteriaFilter]) class BasicHtmlReportWriter extends AbstractReportWriter { public static final DEFAULT_OUTPUT_FILE = 'GMetricsReport.html' private static final CSS_FILE = 'gmetrics-basic-html-report.css' private static final ROOT_PACKAGE_NAME = 'All packages' static defaultOutputFile = DEFAULT_OUTPUT_FILE String title private List reportMetricLevels private String notApplicable @Override protected void writeReport(Writer writer, ResultsNode resultsNode, AnalysisContext analysisContext) { assert resultsNode assert writer notApplicable = getResourceBundleString('basicHtmlReport.metricResults.notApplicable') def metricResultColumns = buildMetricResultColumns(analysisContext.metricSet) def builder = new StreamingMarkupBuilder() def html = builder.bind { html { out << buildHeaderSection() out << buildBodySection(resultsNode, metricResultColumns, analysisContext) } } writer << html } //-------------------------------------------------------------------------- // Internal Helper Methods //-------------------------------------------------------------------------- private buildMetricResultColumns(MetricSet metricSet) { def metricResultColumns = [] metricSet.getMetrics().each {metric -> metric.functions.each { functionName -> if (includesFunction(metric, functionName)) { metricResultColumns << [metric: metric, property: functionName] } } } return metricResultColumns } private buildCSS() { return { def cssInputStream = ClassPathResource.getInputStream(CSS_FILE) assert cssInputStream, "CSS File [$CSS_FILE] not found" def css = cssInputStream.text unescaped << css } } private buildHeaderSection() { return { head { title(buildTitle()) out << buildCSS() } } } private buildBodySection(ResultsNode resultsNode, List metricResultColumns, AnalysisContext analysisContext) { return { body { h1(buildTitle()) out << buildReportTimestamp() out << buildResultsTable(resultsNode, metricResultColumns) out << buildMetricDescriptions(analysisContext.metricSet) out << buildVersionFooter() } } } private buildReportTimestamp() { return { def timestamp = getFormattedTimestamp() p(getResourceBundleString('basicHtmlReport.reportTimestamp.label') + " $timestamp", class:'reportInfo') } } private buildResultsTable(ResultsNode resultsNode, List metricResultColumns) { return { h2(getResourceBundleString('basicHtmlReport.metricResults.title')) table { tr(class:'tableHeader') { th(getResourceBundleString('basicHtmlReport.metricResults.nameHeading')) metricResultColumns.each { columnDef -> if (includesMetric(columnDef.metric)) { def columnHeading = getMetricResultColumnHeading(columnDef.metric.name, columnDef.property) th(columnHeading, class:'metricColumnHeader') } } } out << buildResultsTableRowRecursively(resultsNode, metricResultColumns) } } } private String getMetricResultColumnHeading(String metricName, String metricProperty) { def resourceKey = metricName + '.' + metricProperty return getResourceBundleString(resourceKey, "$metricName ($metricProperty)") } void setReportLevels(String levels) { reportMetricLevels = MetricLevel.parseCommaSeparatedList(levels) } private boolean includesReportLevel(MetricLevel level) { reportMetricLevels == null || level in reportMetricLevels } private buildResultsTableRowRecursively(ResultsNode resultsNode, List metricResultColumns) { return { def level = resultsNode.level if (includesReportLevel(level)) { def rowCssClass = level.name tr(class:rowCssClass) { def prefix = prefixForResultsNodeLevel(resultsNode) def nodeName = level == MetricLevel.PACKAGE ? resultsNode.path : resultsNode.name def pathName = nodeName ?: ROOT_PACKAGE_NAME def cssClass = resultsNode.name ? 'name' : 'allPackages' td(class:"${level.name}Indent") { span(prefix, class:'rowTypePrefix') span(pathName, class:cssClass) } metricResultColumns.each { columnDef -> Metric metric = columnDef.metric if (includesMetric(metric)) { def includeMetricResults = includesLevel(metric, level) && level >= metric.getBaseLevel() def metricResult = includeMetricResults ? resultsNode.getMetricResult(metric) : null boolean hasNonNullValue = metricResult && metricResult[columnDef.property] != null def formattedValue = hasNonNullValue ? formatMetricResultValue(metric.name, metricResult[columnDef.property]) : notApplicable td(formattedValue, class:'metricValue') } } } } out << buildResultsRowsForChildren(resultsNode, metricResultColumns, MetricLevel.METHOD) out << buildResultsRowsForChildren(resultsNode, metricResultColumns, MetricLevel.CLASS) out << buildResultsRowsForChildren(resultsNode, metricResultColumns, MetricLevel.PACKAGE) } } private buildResultsRowsForChildren(ResultsNode resultsNode, List metricResultColumns, MetricLevel metricLevel) { return { resultsNode.children.each { childName, childNode -> if (childNode.level == metricLevel) { out << buildResultsTableRowRecursively(childNode, metricResultColumns) } } } } private String prefixForResultsNodeLevel(ResultsNode resultsNode) { def prefixes = [ (MetricLevel.PACKAGE):'[p] ', (MetricLevel.CLASS):'[c] ', (MetricLevel.METHOD):'[m] ' ] return prefixes[resultsNode.level] } private buildMetricDescriptions(MetricSet metricSet) { def metrics = new ArrayList(metricSet.metrics).findAll { metric -> metric.enabled && includesMetric(metric)} metrics.sort { metric -> metric.name } return { h2(getResourceBundleString('basicHtmlReport.metricDescriptions.title')) table(border:'1') { tr(class:'tableHeader') { th('#', class:'metricDescriptions') th(getResourceBundleString('basicHtmlReport.metricDescriptions.nameHeading'), class:'metricDescriptions') th(getResourceBundleString('basicHtmlReport.metricDescriptions.descriptionHeading'), class:'metricDescriptions') } metrics.eachWithIndex { metric, index -> tr(class:'metricDescriptions') { a((metric.name):metric.name) td(index+1) td(metric.name, class:'metricName') td { unescaped << getDescriptionForMetricName(metric.name) } } } } } } protected String getDescriptionForMetricName(String metricName) { def resourceKey = metricName + '.description.html' return getResourceBundleString(resourceKey, "No description provided for metric named [$metricName]") } private buildVersionFooter() { def versionText = 'GMetrics ' + GMetricsVersion.getVersion() return { p(class:'version') { a(versionText, href:GMETRICS_URL) } } } private String buildTitle() { return getResourceBundleString('basicHtmlReport.titlePrefix') + (title ? ": $title": '') } }GMetrics-0.7/src/main/groovy/org/gmetrics/report/FunctionsCriteriaFilter.groovy0000644000175000017500000000257212107724361027465 0ustar ebourgebourg/* * Copyright 2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.report import org.gmetrics.metric.Metric /** * Provides data and behavior for enabling reports to filter the set of functions included in a report. * This class is intended to be used as a Groovy @Mixin for ReportWriter classes. * * @author Chris Mair * @version $Revision: 118 $ - $Date: 2010-07-10 21:55:10 -0400 (Sat, 10 Jul 2010) $ */ class FunctionsCriteriaFilter { private Map functionsCriteriaMap void setFunctions(String criteria) { functionsCriteriaMap = MetricCriteriaFilterHelper.parseCriteria(criteria) } boolean includesFunction(Metric metric, String functionName) { return MetricCriteriaFilterHelper.includesName(functionsCriteriaMap, metric, functionName) } }GMetrics-0.7/src/main/groovy/org/gmetrics/report/SingleSeriesCriteriaFilter.groovy0000644000175000017500000002063412107724361030110 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.report import org.gmetrics.metricset.MetricSet import org.gmetrics.resultsnode.ResultsNode import org.gmetrics.metric.MetricLevel import org.gmetrics.metric.Metric /** * Provides data and behavior for enabling reports to filter the results based on a single * metric, single level and single function to provide a single series of data. * This class is intended to be used as a Groovy @Mixin for ReportWriter classes. *

* The metric, level and function properties are required (must * be non-null and non-empty). These three properties uniquely identify a single series of metric values. *

* The metric property must specify the name (case-sensitive) of a single Metric (for example * "CyclomaticComplexity") included in the analysis results. *

* The level property must be set to one of: "package", "class" or "method". *

* The function property must be set to the name of a function supported by the * metric, typically one of: "total", "average", "minimum" or "maximum". *

* The sort property is optional, and if not null or empty, must either have * the value of "ascending" or "descending", and causes the results to be sorted numerically in either ascending * or descending order. *

* The maxResults property is optional. A value of null, empty or 0 * means no limit. Otherwise, the value must be positive, and limits the number of results returned. *

* The greaterThan property is optional. The value specifies a threshold -- only results * with a larger value are returned. A value of null or empty means no threshold. *

* The lessThan property is optional. The value specifies a threshold -- only results * with a smaller value are returned. A value of null or empty means no threshold. * * @author Chris Mair */ class SingleSeriesCriteriaFilter { private static final VALID_SORT_VALUES = ['ascending', 'descending'] String metric String level String function String sort String maxResults String greaterThan String lessThan List buildSeriesData(ResultsNode resultsNode, MetricSet metricSet) { assert metric assert level assert function assertMetricExists(metricSet) assertLevelExists() assertFunctionExists(metricSet) assertValidSortValue() assertValidMaxResultsValue() assertValidGreaterThanValue() assertValidLessThanValue() List matchingValues = [] findMatchingValuesForChildren(resultsNode, null, matchingValues) def seriesValues = sortValuesIfApplicable(matchingValues) seriesValues = limitToGreaterThanIfApplicable(seriesValues) seriesValues = limitToLessThanIfApplicable(seriesValues) return limitToMaxResultsIfApplicable(seriesValues) } private void findMatchingValuesForChildren(ResultsNode resultsNode, String parentName, List matchingValues) { resultsNode.children.each { childName, childResultsNode -> String fullChildName = getResultsNodeFullName(childResultsNode, childName, parentName) findMatchingValues(childResultsNode, fullChildName, matchingValues) } } private String getResultsNodeFullName(ResultsNode childResultsNode, childName, String parentName) { String fullChildName switch (childResultsNode.level) { case MetricLevel.METHOD: fullChildName = "${parentName}#${childResultsNode.name}" break case MetricLevel.PACKAGE: fullChildName = childResultsNode.path break default: fullChildName = childName } return fullChildName } private void findMatchingValues(ResultsNode resultsNode, String name, List matchingValues) { def metricResults = resultsNode.getMetricResults() boolean matchesLevel = resultsNode.getLevel().getName() == level if (matchesLevel) { def metricResult = metricResults.find { metricResult -> boolean matchesMetric = metricResult.getMetric().getName() == metric boolean hasMatchingFunction = metricResult[function] return matchesMetric && hasMatchingFunction } if (metricResult) { matchingValues << new SeriesValue(name, metricResult[function]) } } findMatchingValuesForChildren(resultsNode, name, matchingValues) } private List sortValuesIfApplicable(List seriesValues) { if (sort == 'ascending') { return seriesValues.sort { v1, v2 -> v1.value <=> v2.value } } if (sort == 'descending') { return seriesValues.sort { v1, v2 -> v2.value <=> v1.value } } return seriesValues } private List limitToGreaterThanIfApplicable(List seriesValues) { if (greaterThan) { def greaterThanValue = greaterThan as BigDecimal return seriesValues.findAll { seriesValue -> seriesValue.value > greaterThanValue } } return seriesValues } private List limitToLessThanIfApplicable(List seriesValues) { if (lessThan) { def lessThanValue = lessThan as BigDecimal return seriesValues.findAll { seriesValue -> seriesValue.value < lessThanValue } } return seriesValues } private List limitToMaxResultsIfApplicable(List seriesValues) { if (maxResults) { def maxResultsInt = maxResults as int if (maxResultsInt < seriesValues.size()) { return seriesValues[0..maxResultsInt-1] } } return seriesValues } private void assertMetricExists(MetricSet metricSet) { Metric m = findMetric(metricSet) assert m != null, "The metric named [$metric] does not exist" } private void assertLevelExists() { assert level in MetricLevel.names, "The level named [$level] does not exist" } private void assertFunctionExists(MetricSet metricSet) { Metric m = findMetric(metricSet) assert function in m.getFunctions(), "The function named [$function] does not exist for metric [$metric]" } private void assertValidSortValue() { assert !sort || sort in VALID_SORT_VALUES, "The sort value named [$sort] is not one of $VALID_SORT_VALUES" } private void assertValidMaxResultsValue() { if (maxResults) { try { def value = Integer.parseInt(maxResults) assert value >= 0, "The maxResults value [$maxResults] must be null or greater than or equal to zero" } catch(NumberFormatException e) { throw new AssertionError("The maxResults value [$maxResults] must be null or greater than or equal to zero") } } } private void assertValidGreaterThanValue() { assertValidNumberValue(greaterThan, 'greaterThan') } private void assertValidLessThanValue() { assertValidNumberValue(lessThan, 'lessThan') } private void assertValidNumberValue(value, String name) { if (value) { try { new BigDecimal(value) } catch(NumberFormatException e) { throw new AssertionError("The $name value [$value] must be a valid number") } } } private Metric findMetric(MetricSet metricSet) { metricSet.metrics.find { m -> m.name == metric } } }GMetrics-0.7/src/main/groovy/org/gmetrics/report/MetricsCriteriaFilter.groovy0000644000175000017500000000247112107724361027121 0ustar ebourgebourg/* * Copyright 2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.report import org.gmetrics.metric.Metric /** * Provides data and behavior for enabling reports to filter the set of metrics included in a report. * This class is intended to be used as a Groovy @Mixin for ReportWriter classes. * * @author Chris Mair * @version $Revision: 118 $ - $Date: 2010-07-10 21:55:10 -0400 (Sat, 10 Jul 2010) $ */ class MetricsCriteriaFilter { private Collection metricNames void setMetrics(String metricNames) { this.metricNames = metricNames.tokenize(',').collect { it.trim() } } boolean includesMetric(Metric metric) { return metricNames == null ? true : metric.name in metricNames } }GMetrics-0.7/src/main/groovy/org/gmetrics/metricset/0000755000175000017500000000000012623566446022106 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/metricset/MetricSetBuilder.groovy0000644000175000017500000001233412107724353026554 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metricset import org.gmetrics.metric.Metric import org.gmetrics.metricregistry.MetricRegistryHolder /** * A Builder for MetricSets. Create a MetricSet by calling the metricset * method, passing in a Closure defining the contents of the MetricSet. * The Closure can contain any combination of the following (as well as * arbitrary Groovy code): *

    *
  • metricset - to load a MetricSet file. The path specifies a Groovy file.
  • *
  • metric - to load a single Metric; specify the Metric class
  • *
  • The name of a predefined Metric - to load a single Metric
  • *
  • description - description of the MetricSet (optional)
  • *
* * @author Chris Mair */ class MetricSetBuilder { private topLevelDelegate = new TopLevelDelegate() void metricset(Closure closure) { closure.delegate = topLevelDelegate closure.call() } MetricSet getMetricSet() { topLevelDelegate.metricSet } } class TopLevelDelegate { private allMetricSet = new CompositeMetricSet() private nestedLevel = 0 void metricset(String path) { def metricSet = new GroovyDslMetricSet(path) allMetricSet.addMetricSet(metricSet) } void metricset(String path, Closure closure) { def metricSet = new GroovyDslMetricSet(path) def metricSetConfigurer = new MetricSetDelegate(metricSet) closure.delegate = metricSetConfigurer closure.setResolveStrategy(Closure.DELEGATE_FIRST) nestedLevel++ closure.call() nestedLevel-- allMetricSet.addMetricSet(metricSetConfigurer.metricSet) } Metric metric(Class metricClass) { assertClassImplementsMetricInterface(metricClass) Metric metric = metricClass.newInstance() return addMetric(metric) } Metric metric(Class metricClass, Map properties) { assertClassImplementsMetricInterface(metricClass) Metric metric = metricClass.newInstance() properties.each { key, value -> metric[key] = value } return addMetric(metric) } Metric metric(Class metricClass, Closure closure) { assertClassImplementsMetricInterface(metricClass) Metric metric = metricClass.newInstance() closure.delegate = metric closure.resolveStrategy = Closure.DELEGATE_FIRST nestedLevel++ closure.call() nestedLevel-- return addMetric(metric) } Metric propertyMissing(String name) { def metricClass = MetricRegistryHolder.metricRegistry?.getMetricClass(name) assert metricClass, "No such metric named [$name]" metric(metricClass) } Metric methodMissing(String name, args) { def metricClass = MetricRegistryHolder.metricRegistry?.getMetricClass(name) assert metricClass, "No such metric named [$name]" if (args.size() > 0) { metric(metricClass, args[0]) } else { metric(metricClass) } } @SuppressWarnings(['EmptyMethod', 'UnusedMethodParameter']) void description(String description) { // Do nothing } protected MetricSet getMetricSet() { return allMetricSet } private void assertClassImplementsMetricInterface(Class metricClass) { assert metricClass assert Metric.isAssignableFrom(metricClass), "The metric class [${metricClass.name}] does not implement the org.gmetrics.metric.Metric interface" } private Metric addMetric(Metric metric) { if (isNotWithinAnotherMetricDefinition()) { allMetricSet.addMetric(metric) } return metric } private boolean isNotWithinAnotherMetricDefinition() { return nestedLevel == 0 } } class MetricSetDelegate { MetricSet metricSet MetricSetDelegate(MetricSet metricSet) { this.metricSet = metricSet } def methodMissing(String name, args) { def metric = findMetric(name) assert metric, "No such metric named [$name]" def arg = args[0] if (arg instanceof Closure) { arg.delegate = metric arg.setResolveStrategy(Closure.DELEGATE_FIRST) arg.call() } else { // Assume it is a Map arg.each { key, value -> metric[key] = value } } } private Metric findMetric(String name) { metricSet.metrics.find { metric -> metric.name == name } } }GMetrics-0.7/src/main/groovy/org/gmetrics/metricset/GroovyDslMetricSet.groovy0000644000175000017500000000461512462777012027125 0ustar ebourgebourg/* * Copyright 2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metricset import org.gmetrics.util.io.ResourceFactory import org.gmetrics.util.io.DefaultResourceFactory import org.apache.log4j.Logger /** * A MetricSet implementation that parses a Groovy DSL of Metric definitions. * The filename passed into the constructor is interpreted relative to the classpath. * Note that this class attempts to read the file and parse the Groovy from within the constructor. * * @author Chris Mair * @version $Revision: 298 $ - $Date: 2015-01-30 16:56:55 -0500 (Fri, 30 Jan 2015) $ */ class GroovyDslMetricSet implements MetricSet { private static final LOG = Logger.getLogger(GroovyDslMetricSet) private ResourceFactory resourceFactory = new DefaultResourceFactory() private metrics /** * Construct a new instance on the specified Groovy DSL MetricSet file path * @param path - the path to the Groovy DSL MetricSet definition file, relative to the classpath; must not be empty or null */ GroovyDslMetricSet(String path) { assert path LOG.info("Loading metrics from [$path]") def inputStream = resourceFactory.getResource(path).inputStream def metricSetBuilder = new MetricSetBuilder() def callMetricSet = { closure -> closure.resolveStrategy = Closure.DELEGATE_ONLY // fail if access non-existent properties metricSetBuilder.metricset(closure) } Binding binding = new Binding(metricset:callMetricSet) GroovyShell shell = new GroovyShell(binding); shell.evaluate(inputStream.text); metrics = metricSetBuilder.metricSet.metrics } /** * @return a List of Metric objects */ List getMetrics() { return metrics } } GMetrics-0.7/src/main/groovy/org/gmetrics/metricset/ListMetricSet.groovy0000644000175000017500000000302512107724353026076 0ustar ebourgebourg/* * Copyright 2008 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metricset import org.gmetrics.metric.Metric /** * A MetricSet implementation that returns a static List of Metrics passed into its constructor. * * @author Chris Mair * @version $Revision: 107 $ - $Date: 2010-06-05 07:23:27 -0400 (Sat, 05 Jun 2010) $ */ class ListMetricSet implements MetricSet { private metrics /** * Construct a new instance from the specified List of metrics. * @param metrics - the List of List of Metric objects; must not be null, but may be empty. */ ListMetricSet(List metrics) { assert metrics != null assert metrics.every { it instanceof Metric } def copy = [] copy.addAll(metrics) this.metrics = Collections.unmodifiableList(copy) } /** * @return a List of Metric objects */ List getMetrics() { metrics } }GMetrics-0.7/src/main/groovy/org/gmetrics/metricset/CompositeMetricSet.groovy0000644000175000017500000000322112107724353027123 0ustar ebourgebourg/* * Copyright 2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metricset import org.gmetrics.metric.Metric /** * A MetricSet implementation that aggregates a set of MetricSets and Metrics. * * @author Chris Mair * @version $Revision: 85 $ - $Date: 2010-02-26 21:51:52 -0500 (Fri, 26 Feb 2010) $ */ class CompositeMetricSet implements MetricSet { private metrics = [] /** * Add a single Metric to this MetricSet * @param metric - the Metric to add */ void addMetric(Metric metric) { assert metric != null metrics << metric } /** * Add all of the Metrics within the specified MetricSet to this MetricSet * @param metricSet - the MetricSet whose Metrics are to be included */ void addMetricSet(MetricSet metricSet) { assert metricSet != null metrics.addAll(metricSet.getMetrics()) } /** * @return a List of Metric objects. The returned List is immutable. */ List getMetrics() { return metrics.asImmutable() } } GMetrics-0.7/src/main/groovy/org/gmetrics/metricset/DefaultMetricSet.groovy0000644000175000017500000000246112107724353026552 0ustar ebourgebourg/* * Copyright 2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metricset import org.gmetrics.metric.linecount.MethodLineCountMetric import org.gmetrics.metric.linecount.ClassLineCountMetric import org.gmetrics.metric.cyclomatic.CyclomaticComplexityMetric /** * A MetricSet implementation that returns the default static List of Metrics. * * @author Chris Mair * @version $Revision: 79 $ - $Date: 2010-02-20 21:51:05 -0500 (Sat, 20 Feb 2010) $ */ class DefaultMetricSet implements MetricSet { private metricSet = new ListMetricSet([new CyclomaticComplexityMetric(), new ClassLineCountMetric(), new MethodLineCountMetric()]) List getMetrics() { return metricSet.getMetrics() } }GMetrics-0.7/src/main/groovy/org/gmetrics/metricset/MetricSet.groovy0000644000175000017500000000157312107724353025250 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metricset /** * Represents a set of (configured) Metric objects * * @author Chris Mair * @version $Revision: 24 $ - $Date: 2009-12-10 21:17:05 -0500 (Thu, 10 Dec 2009) $ */ interface MetricSet { List getMetrics() }GMetrics-0.7/src/main/groovy/org/gmetrics/GMetricsRunner.groovy0000644000175000017500000000460512107724361024257 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics import org.gmetrics.resultsnode.ResultsNode import org.gmetrics.analyzer.SourceAnalyzer import org.apache.log4j.Logger import org.gmetrics.metricset.MetricSet import org.gmetrics.analyzer.AnalysisContext /** * Helper class to run GMetrics. *

* The following properties must be configured before invoking the execute() method: *

    *
  • sourceAnalyzer - An instance of a org.gmetrics.analyzer.SourceAnalyzer implementation.
  • *
  • reportWriters - The list of ReportWriter instances. A report is generated * for each element in this list. This list can be empty, but cannot be null.
  • *
* * NOTE: This is an internal class. Its API is subject to change. * * @author Chris Mair * @version $Revision: 95 $ - $Date: 2010-03-08 22:00:10 -0500 (Mon, 08 Mar 2010) $ */ class GMetricsRunner { private static final LOG = Logger.getLogger(GMetricsRunner) MetricSet metricSet SourceAnalyzer sourceAnalyzer List reportWriters = [] ResultsNode execute() { assert metricSet assert sourceAnalyzer assert reportWriters != null def startTime = System.currentTimeMillis() def resultsNode = sourceAnalyzer.analyze(metricSet) def elapsedTime = System.currentTimeMillis() - startTime LOG.debug("resultsNode=$resultsNode") def analysisContext = new AnalysisContext(metricSet:metricSet, sourceDirectories:sourceAnalyzer.sourceDirectories) reportWriters.each { reportWriter -> reportWriter.writeReport(resultsNode, analysisContext) } LOG.info("GMetrics completed: ${elapsedTime}ms") return resultsNode } }GMetrics-0.7/src/main/groovy/org/gmetrics/metricregistry/0000755000175000017500000000000012623566446023163 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/metricregistry/MetricRegistryHolder.groovy0000644000175000017500000000155412107724362030537 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metricregistry /** * Holds a reference to the MetricRegistry static singleton * * @author Chris Mair */ class MetricRegistryHolder { static MetricRegistry metricRegistry = new DefaultMetricRegistry() } GMetrics-0.7/src/main/groovy/org/gmetrics/metricregistry/DefaultMetricRegistry.groovy0000644000175000017500000000474312107724362030711 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metricregistry import org.gmetrics.metric.Metric import org.gmetrics.metric.abc.AbcMetric import org.gmetrics.metric.methodcount.MethodCountMetric import org.gmetrics.metric.linecount.MethodLineCountMetric import org.gmetrics.metric.linecount.ClassLineCountMetric import org.gmetrics.metric.fieldcount.FieldCountMetric import org.gmetrics.metric.classcount.ClassCountMetric import org.gmetrics.metric.cyclomatic.CyclomaticComplexityMetric import org.gmetrics.metric.coverage.CoberturaBranchCoverageMetric import org.gmetrics.metric.coverage.CoberturaLineCoverageMetric import org.gmetrics.metric.crap.CrapMetric import org.gmetrics.metric.coupling.EfferentCouplingMetric import org.gmetrics.metric.coupling.AfferentCouplingMetric /** * Default implementation of MetricRegistry * * @author Chris Mair */ class DefaultMetricRegistry implements MetricRegistry { public static final METRIC_CLASSES = [ AbcMetric, AfferentCouplingMetric, CoberturaBranchCoverageMetric, CoberturaLineCoverageMetric, ClassCountMetric, ClassLineCountMetric, CrapMetric, CyclomaticComplexityMetric, EfferentCouplingMetric, FieldCountMetric, MethodCountMetric, MethodLineCountMetric ] private static final METRIC_MAP = buildMetricClassMap() @Override Class getMetricClass(String metricName) { return METRIC_MAP[metricName] } @Override Collection getAllMetricNames() { return METRIC_MAP.keySet() } private static Map> buildMetricClassMap() { def map = [:] METRIC_CLASSES.each { metricClass -> def instance = metricClass.newInstance() map[instance.getName()] = metricClass } return map } }GMetrics-0.7/src/main/groovy/org/gmetrics/metricregistry/MetricRegistry.groovy0000644000175000017500000000166412107724362027403 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metricregistry import org.gmetrics.metric.Metric /** * Represents a registry of metric classes, accessible by metric name * * @author Chris Mair */ interface MetricRegistry { Class getMetricClass(String metricName) Collection getAllMetricNames() } GMetrics-0.7/src/main/groovy/org/gmetrics/source/0000755000175000017500000000000012623566446021407 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/source/SourceCodeCriteria.groovy0000644000175000017500000000635612107724353026374 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.source import org.gmetrics.util.WildcardPattern /** * Represents the set of criteria used to filter source code (files). Provides an API * to determine whether a particular source code file matches the criteria. *

* This is an internal class and its API is subject to change. * * @author Chris Mair * @version $Revision: 24 $ - $Date: 2009-12-10 21:17:05 -0500 (Thu, 10 Dec 2009) $ */ class SourceCodeCriteria { /** * Apply only to source code (file) pathnames matching this regular expression. * If null, then all SourceCode instances match this part of the criteria (i.e., this property is ignored). */ String applyToFilesMatching /** * Do NOT apply to source code (file) pathnames matching this regular expression. * If null, then all SourceCode instances match this part of the criteria (i.e., this property is ignored). */ String doNotApplyToFilesMatching /** * Only apply to source code (file) names matching this value. * The value may optionally be a comma-separated list of names, in which case one of the names must match. * The name(s) may optionally include wildcard characters ('*' or '?'). * If null, then all SourceCode instances match this part of the criteria (i.e., this property is ignored). */ String applyToFileNames /** * Do NOT apply to source code (file) names matching this value. * The value may optionally be a comma-separated list of names, in which case any one of the names can match. * The name(s) may optionally include wildcard characters ('*' or '?'). * If null, then all SourceCode instances match this part of the criteria (i.e., this property is ignored). */ String doNotApplyToFileNames /** * Return true if all of the criteria specified in this object apply to thw SourceCode. * @param sourceCode - the SourceCode * @return true only if all of the (specified, i.e. non-null) criteria match the SourceCode */ boolean matches(SourceCode sourceCode) { boolean apply = (applyToFilesMatching) ? sourceCode.path ==~ applyToFilesMatching : true if (apply && doNotApplyToFilesMatching) { apply = !(sourceCode.path ==~ doNotApplyToFilesMatching) } if (apply && applyToFileNames) { apply = new WildcardPattern(applyToFileNames).matches(sourceCode.name) } if (apply && doNotApplyToFileNames) { apply = !new WildcardPattern(doNotApplyToFileNames).matches(sourceCode.name) } return apply } }GMetrics-0.7/src/main/groovy/org/gmetrics/source/SourceString.groovy0000644000175000017500000000372412107724353025301 0ustar ebourgebourg/* * Copyright 2008 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.source import org.codehaus.groovy.control.SourceUnit /** * SourceCode implementation that uses source from a pre-defined String. * Note that the path is normalized: file separator chars are normalized to standard '/'. * * @author Chris Mair * @version $Revision: 29 $ - $Date: 2009-12-13 15:50:29 -0500 (Sun, 13 Dec 2009) $ */ class SourceString extends AbstractSourceCode { String path String name private String source /** * Construct a new instance for the file at the specified path * @param source - the source; must not be null or empty * @param path - the path for the source code; may be null; defaults to null * @param name - the name for the source code; may be null; defaults to null */ SourceString(String source, String path=null, String name=null) { assert source this.source = source setPath(path) this.name = name } /** * @return the full text of the source code */ String getText() { return source } void setPath(String path) { this.path = path ? normalizePath(path) : path } String toString() { return "SourceString[$source]" } protected createSourceUnit() { return SourceUnit.create("Script", getText()) } }GMetrics-0.7/src/main/groovy/org/gmetrics/source/SourceCode.groovy0000644000175000017500000000516112107724353024702 0ustar ebourgebourg/* * Copyright 2008 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.source import org.codehaus.groovy.ast.ModuleNode /** * Represents a unit of source code to be analyzed * * @author Chris Mair * @version $Revision: 155 $ - $Date: 2011-09-27 20:33:08 -0400 (Tue, 27 Sep 2011) $ */ interface SourceCode { /** * Get the logical name for this source code. If this object is a file, then the name * is the filename, without a path. * @return the name for this source; may be null */ String getName() /** * Get the logical path for this source code. If this object is a file, then the name * is the full path in the filesystem. File separators are normalized to forward slash (/). * @return the name for this source; may be null */ String getPath() /** * @return the full text of the source code */ String getText() /** * @return the List of lines of the source code (with line terminators removed) */ List getLines() /** * Get the trimmed line at the specified index * @param lineNumber - the line number; may be negative * @return the trimmed line at the specified index, or null if lineNumber is not valid */ String line(int lineNumber) /** * Return the Groovy AST (Abstract Syntax Tree) for this source file * @return the ModuleNode representing the AST for this source file */ ModuleNode getAst() /** * Return true if and only if the source code can be successfully compiled * @return true only if the source code is valid */ boolean isValid() /** * Return the line index for the line containing the character at the specified index within the source code. * @param charIndex - the index of the character within the source code (zero-based) * @return the line number (one-based) containing the specified character; Return -1 if charIndex is not valid. */ int getLineNumberForCharacterIndex(int charIndex) }GMetrics-0.7/src/main/groovy/org/gmetrics/source/SourceFile.groovy0000644000175000017500000000437512107724353024715 0ustar ebourgebourg/* * Copyright 2008 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.source import org.codehaus.groovy.control.SourceUnit import org.codehaus.groovy.control.CompilerConfiguration import org.codehaus.groovy.control.ErrorCollector /** * The SourceCode implementation for a single file. * Note that the path is normalized: file separator chars are normalized to standard '/'. * * @author Chris Mair * @version $Revision: 29 $ - $Date: 2009-12-13 15:50:29 -0500 (Sun, 13 Dec 2009) $ */ class SourceFile extends AbstractSourceCode { private File file private String text private path /** * Construct a new instance for the file at the specified path * @param path - the path of the file; must not be null or empty */ SourceFile(File file) { assert file this.file = file this.path = normalizePath(file.path) } /** * @return the filename for this source file, excluding path */ String getName() { return file.name } /** * @return the normalized path for this source file, including filename */ String getPath() { return path } /** * @return the full text of the source code */ String getText() { if (text == null) { text = file.text } return text } String toString() { return "SourceFile[$file.absolutePath]" } protected createSourceUnit() { def configuration = new CompilerConfiguration(); def errorCollector = new ErrorCollector(configuration) return new SourceUnit(file, configuration, null, errorCollector) } }GMetrics-0.7/src/main/groovy/org/gmetrics/source/AbstractSourceCode.groovy0000644000175000017500000001031712107724353026365 0ustar ebourgebourg/* * Copyright 2008 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.source import org.codehaus.groovy.ast.ModuleNode import org.codehaus.groovy.control.CompilationUnit import org.codehaus.groovy.control.Phases import org.codehaus.groovy.control.SourceUnit import org.codehaus.groovy.control.CompilationFailedException import org.apache.log4j.Logger /** * Abstract superclass for SourceCode implementations * * @author Chris Mair * @version $Revision: 155 $ - $Date: 2011-09-27 20:33:08 -0400 (Tue, 27 Sep 2011) $ */ abstract class AbstractSourceCode implements SourceCode { static final LOG = Logger.getLogger(AbstractSourceCode) static final FILE_SEPARATOR = System.getProperty("file.separator") private ModuleNode ast private List lines private astParsed = false abstract protected createSourceUnit() /** * @return the List of lines of the source code (with line terminators removed) */ List getLines() { if (lines == null) { lines = new StringReader(getText()).readLines() } return lines } /** * Get the trimmed line at the specified index * @param lineNumber - the line number; may be negative * @return the trimmed line at the specified index, or null if lineNumber is not valid */ String line(int lineNumber) { def allLines = getLines() return (lineNumber >= 0) && lineNumber < allLines.size() ? allLines[lineNumber].trim() : null } /** * Return the Groovy AST (Abstract Syntax Tree) for this source file * @return the ModuleNode representing the AST for this source file */ ModuleNode getAst() { if (!astParsed) { SourceUnit unit = createSourceUnit() CompilationUnit compUnit = new CompilationUnit() compUnit.addSource(unit) try { compUnit.compile(Phases.CONVERSION) ast = unit.getAST() } catch(CompilationFailedException e) { LOG.warn("Compilation failed for [${toString()}]") } astParsed = true } return ast } /** * Return the line index for the line containing the character at the specified index within the source code. * @param charIndex - the index of the character within the source code (zero-based) * @return the line number (one-based) containing the specified character; Return -1 if charIndex is not valid. */ int getLineNumberForCharacterIndex(int charIndex) { int lineCount = 0 def source = getText() if (charIndex >= source.size() || charIndex < 0) { return -1 } if (charIndex > 0) { (charIndex-1..0).each { index -> def ch = source[index] if (ch == '\n') { lineCount++ } } } return lineCount } /** * Return true if and only if the source code can be successfully compiled * @return true only if the source code is valid */ boolean isValid() { return getAst() } /** * Return the normalized value of the specified path. Convert file separator chars to standard '/'. * @param path - the path to normalize * @return the normalized value */ protected String normalizePath(String path) { final SEP = '/' as char char separatorChar = System.getProperty("file.separator").charAt(0) return (separatorChar == SEP) ? path : path.replace(separatorChar, SEP) } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/0000755000175000017500000000000012623566446021372 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/metric/cyclomatic/0000755000175000017500000000000012623566446023521 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/metric/cyclomatic/CyclomaticComplexityAstVisitor.groovy0000644000175000017500000000652612107724356033167 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.cyclomatic import org.codehaus.groovy.ast.MethodNode import org.gmetrics.metric.AbstractAstVisitor import org.codehaus.groovy.ast.stmt.IfStatement import org.codehaus.groovy.ast.stmt.WhileStatement import org.codehaus.groovy.ast.stmt.ForStatement import org.codehaus.groovy.ast.stmt.SwitchStatement import org.codehaus.groovy.ast.stmt.CatchStatement import org.codehaus.groovy.ast.expr.BinaryExpression import org.codehaus.groovy.ast.expr.Expression import org.codehaus.groovy.ast.expr.TernaryExpression import org.codehaus.groovy.ast.expr.PropertyExpression import org.codehaus.groovy.ast.expr.MethodCallExpression /** * AST Visitor for calculating the Cyclomatic Complexity for a method or closure field. * * @see CyclomaticComplexityMetric * * @author Chris Mair */ class CyclomaticComplexityAstVisitor extends AbstractAstVisitor { private static final BOOLEAN_LOGIC_OPERATIONS = ['&&', '||'] Integer complexity = 1 void visitMethod(MethodNode methodNode) { if (methodNode.isAbstract() || isSyntheticNonRunMethod(methodNode) ) { complexity = null } else { super.visitMethod(methodNode) } } void visitIfElse(IfStatement ifElse) { complexity++ super.visitIfElse(ifElse) } void visitWhileLoop(WhileStatement loop) { complexity++ super.visitWhileLoop(loop) } void visitForLoop(ForStatement forLoop) { complexity++ super.visitForLoop(forLoop) } void visitSwitch(SwitchStatement statement) { complexity += statement.caseStatements.size() super.visitSwitch(statement) } void visitCatchStatement(CatchStatement statement) { complexity++ super.visitCatchStatement(statement) } void visitBinaryExpression(BinaryExpression expression) { handleExpressionContainingOperation(expression) super.visitBinaryExpression(expression) } void visitTernaryExpression(TernaryExpression expression) { complexity++ super.visitTernaryExpression(expression) } @Override void visitMethodCallExpression(MethodCallExpression call) { complexity += call.safe ? 1 : 0 super.visitMethodCallExpression(call) } void visitPropertyExpression(PropertyExpression expression) { complexity += expression.safe ? 1 : 0 super.visitPropertyExpression(expression) } private void handleExpressionContainingOperation(Expression expression) { def operationName = expression.operation.text if (operationName in BOOLEAN_LOGIC_OPERATIONS) { complexity++ } } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/cyclomatic/CyclomaticComplexityMetric.groovy0000644000175000017500000000502312107724356032272 0ustar ebourgebourg/* * Copyright 2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.cyclomatic import org.codehaus.groovy.ast.MethodNode import org.gmetrics.source.SourceCode import org.gmetrics.metric.AbstractMethodMetric import org.codehaus.groovy.ast.expr.ClosureExpression import org.gmetrics.result.MetricResult import org.gmetrics.metric.MetricLevel import org.gmetrics.result.SingleNumberMetricResult /** * Metric for counting the (McCabe) Cyclomatic Complexity for methods and closure fields. * * The counting rules for Groovy: *

 *   1. Each method starts with a complexity count of one.
 *   2. Add one to the complexity count for each occurrence of:
 *      if  while  for  case  catch  &&  ||  ?: (ternary-operator)  ?: (elvis-operator)  ?. (null-check)
 * 
* * Additional notes: *
    *
  • If a class field is initialized to a Closure (ClosureExpression), then that Closure is * analyzed just like a method.
  • *
* * See http://en.wikipedia.org/wiki/Cyclomatic_complexity * * @author Chris Mair */ class CyclomaticComplexityMetric extends AbstractMethodMetric { final String name = 'CyclomaticComplexity' MetricResult calculate(MethodNode methodNode, SourceCode sourceCode) { def visitor = new CyclomaticComplexityAstVisitor(sourceCode:sourceCode) visitor.visitMethod(methodNode) def complexity = visitor.complexity return complexity ? new SingleNumberMetricResult(this, MetricLevel.METHOD, complexity, methodNode.lineNumber) : null } MetricResult calculate(ClosureExpression closureExpression, SourceCode sourceCode) { def visitor = new CyclomaticComplexityAstVisitor(sourceCode:sourceCode) visitor.visitClosureExpression(closureExpression) def complexity = visitor.complexity return complexity ? new SingleNumberMetricResult(this, MetricLevel.METHOD, complexity, closureExpression.lineNumber) : null } } GMetrics-0.7/src/main/groovy/org/gmetrics/metric/methodcount/0000755000175000017500000000000012623566446023723 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/metric/methodcount/MethodCountAstVisitor.groovy0000644000175000017500000000266012107724356031450 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.methodcount import org.codehaus.groovy.ast.ClassNode import org.gmetrics.metric.AbstractAstVisitor import org.gmetrics.util.AstUtil /** * AstVisitor for the MethodCountMetric. * * @author Chris Mair */ class MethodCountAstVisitor extends AbstractAstVisitor { private int numberOfMethods int getNumberOfMethods() { return numberOfMethods } void visitClass(ClassNode classNode) { numberOfMethods = 0 if (classNode.lineNumber >= 0) { numberOfMethods += classNode.methods?.size() ?: 0 numberOfMethods += classNode.declaredConstructors?.size() ?: 0 numberOfMethods += classNode.fields.findAll { AstUtil.isClosureField(it) }.size() } super.visitClass(classNode) } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/methodcount/MethodCountMetric.groovy0000644000175000017500000000331212107724356030557 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.methodcount import org.codehaus.groovy.ast.ClassNode import org.gmetrics.metric.AbstractMetric import org.gmetrics.metric.MetricLevel import org.gmetrics.result.ClassMetricResult import org.gmetrics.source.SourceCode import org.gmetrics.util.AstUtil import org.gmetrics.result.SingleNumberMetricResult /** * Metric for counting the number of methods within classes and interfaces. * * @author Chris Mair */ class MethodCountMetric extends AbstractMetric { final String name = 'MethodCount' final MetricLevel baseLevel = MetricLevel.CLASS protected ClassMetricResult calculateForClass(ClassNode classNode, SourceCode sourceCode) { def visitor = new MethodCountAstVisitor(sourceCode:sourceCode) if (AstUtil.isFromGeneratedSourceCode(classNode)) { return null } visitor.visitClass(classNode) def metricResult = new SingleNumberMetricResult(this, MetricLevel.CLASS, visitor.numberOfMethods, classNode.lineNumber) return new ClassMetricResult(metricResult) } } GMetrics-0.7/src/main/groovy/org/gmetrics/metric/linecount/0000755000175000017500000000000012623566446023372 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/metric/linecount/MethodLineCountMetric.groovy0000644000175000017500000000363212107724357031044 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.linecount import org.codehaus.groovy.ast.MethodNode import org.codehaus.groovy.ast.expr.ClosureExpression import org.gmetrics.metric.AbstractMethodMetric import org.gmetrics.source.SourceCode import org.gmetrics.result.MetricResult import org.gmetrics.metric.MetricLevel import org.gmetrics.result.SingleNumberMetricResult /** * Metric for counting the lines of code for methods and closure fields. * * @author Chris Mair */ class MethodLineCountMetric extends AbstractMethodMetric { final String name = 'MethodLineCount' MetricResult calculate(MethodNode methodNode, SourceCode sourceCode) { def visitor = new MethodLineCountAstVisitor(sourceCode:sourceCode) visitor.visitMethod(methodNode) def numLines = visitor.numberOfLinesInMethod return numLines ? new SingleNumberMetricResult(this, MetricLevel.METHOD, numLines, methodNode.lineNumber) : null } MetricResult calculate(ClosureExpression closureExpression, SourceCode sourceCode) { def visitor = new MethodLineCountAstVisitor(sourceCode:sourceCode) visitor.visitClosureExpression(closureExpression) return new SingleNumberMetricResult(this, MetricLevel.METHOD, visitor.numberOfLinesInClosure, closureExpression.lineNumber) } } GMetrics-0.7/src/main/groovy/org/gmetrics/metric/linecount/ClassLineCountMetric.groovy0000644000175000017500000000401212461576207030664 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.linecount import org.gmetrics.metric.MetricLevel import org.gmetrics.result.ClassMetricResult import org.codehaus.groovy.ast.ClassNode import org.gmetrics.source.SourceCode import org.gmetrics.metric.AbstractAstVisitor import org.gmetrics.result.SingleNumberMetricResult import org.gmetrics.metric.AbstractMetric /** * Metric for counting the lines of code for classes and interfaces. * * @author Chris Mair */ class ClassLineCountMetric extends AbstractMetric { final String name = 'ClassLineCount' final MetricLevel baseLevel = MetricLevel.CLASS protected ClassMetricResult calculateForClass(ClassNode classNode, SourceCode sourceCode) { def visitor = new ClassLineCountAstVisitor(sourceCode:sourceCode) visitor.visitClass(classNode) if (visitor.numberOfLinesInClass == 0) { return null } def metricResult = new SingleNumberMetricResult(this, MetricLevel.CLASS, visitor.numberOfLinesInClass, classNode.lineNumber) return new ClassMetricResult(metricResult) } } class ClassLineCountAstVisitor extends AbstractAstVisitor { int numberOfLinesInClass void visitClass(ClassNode classNode) { if (!classNode.script) { numberOfLinesInClass = classNode.lastLineNumber - classNode.lineNumber + 1 } super.visitClass(classNode) } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/linecount/MethodLineCountAstVisitor.groovy0000644000175000017500000000323312107724357031725 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.linecount import org.codehaus.groovy.ast.MethodNode import org.codehaus.groovy.ast.expr.ClosureExpression import org.gmetrics.metric.AbstractAstVisitor /** * AST Visitor for calculating the lines of code for a method or closure field. * * @author Chris Mair * @version $Revision: 31 $ - $Date: 2009-12-15 20:50:40 -0500 (Tue, 15 Dec 2009) $ */ class MethodLineCountAstVisitor extends AbstractAstVisitor { int numberOfLinesInMethod int numberOfLinesInClosure void visitMethod(MethodNode methodNode) { if (methodNode.lineNumber >= 0 && !methodNode.isAbstract()) { numberOfLinesInMethod = methodNode.lastLineNumber - methodNode.lineNumber + 1 } super.visitMethod(methodNode) } void visitClosureExpression(ClosureExpression expression) { if (expression.lineNumber >= 0) { numberOfLinesInClosure = expression.lastLineNumber - expression.lineNumber + 1 } super.visitClosureExpression(expression) } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/AstVisitor.groovy0000644000175000017500000000203412107724356024740 0ustar ebourgebourg/* * Copyright 2008 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric import org.codehaus.groovy.ast.GroovyClassVisitor import org.gmetrics.source.SourceCode /** * Interface for Groovy AST Visitors * * @author Chris Mair */ interface AstVisitor extends GroovyClassVisitor { /** * Set the SourceCode associated with this visitor * @param sourceCode - the SourceCode */ void setSourceCode(SourceCode sourceCode) }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/Metric.groovy0000644000175000017500000000232412107724360024051 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric import org.codehaus.groovy.ast.ClassNode import org.gmetrics.result.ClassMetricResult import org.gmetrics.source.SourceCode import org.gmetrics.result.MetricResult /** * Represents a metric * * @author Chris Mair */ interface Metric { String getName() MetricLevel getBaseLevel() ClassMetricResult applyToClass(ClassNode classNode, SourceCode sourceCode) MetricResult applyToPackage(String path, String packageName, Collection childMetricResults) List getFunctions() boolean isEnabled() }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/MetricLevel.groovy0000644000175000017500000000264012107724357025050 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric /** * Enum representing the level at which a metric is applied - either method, class or package * * @author Chris Mair */ enum MetricLevel { METHOD('method'), CLASS('class'), PACKAGE('package') static MetricLevel parse(String name) { return valueOf(name.toUpperCase()) } static List parseCommaSeparatedList(String names) { def tokens = names.tokenize(',') return tokens.collect { name -> parse(name.trim()) } } static List getNames() { return MetricLevel.values().collect { it.name } } final String name String toString() { return name } private MetricLevel(String name) { this.name = name } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/fieldcount/0000755000175000017500000000000012623566446023526 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/metric/fieldcount/FieldCountMetric.groovy0000644000175000017500000000330512107724357030170 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.fieldcount import org.codehaus.groovy.ast.ClassNode import org.gmetrics.metric.AbstractMetric import org.gmetrics.metric.MetricLevel import org.gmetrics.result.ClassMetricResult import org.gmetrics.source.SourceCode import org.gmetrics.util.AstUtil import org.gmetrics.result.SingleNumberMetricResult /** * Metric for counting the number of methods within classes and interfaces. * * @author Chris Mair */ class FieldCountMetric extends AbstractMetric { final String name = 'FieldCount' final MetricLevel baseLevel = MetricLevel.CLASS protected ClassMetricResult calculateForClass(ClassNode classNode, SourceCode sourceCode) { def visitor = new FieldCountAstVisitor(sourceCode:sourceCode) if (AstUtil.isFromGeneratedSourceCode(classNode)) { return null } visitor.visitClass(classNode) def metricResult = new SingleNumberMetricResult(this, MetricLevel.CLASS, visitor.numberOfFields, classNode.lineNumber) return new ClassMetricResult(metricResult) } } GMetrics-0.7/src/main/groovy/org/gmetrics/metric/fieldcount/FieldCountAstVisitor.groovy0000644000175000017500000000233212107724357031053 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.fieldcount import org.codehaus.groovy.ast.ClassNode import org.gmetrics.metric.AbstractAstVisitor /** * AstVisitor for the FieldCountMetric. * * @author Chris Mair */ class FieldCountAstVisitor extends AbstractAstVisitor { private int numberOfFields int getNumberOfFields() { return numberOfFields } void visitClass(ClassNode classNode) { numberOfFields = 0 if (classNode.lineNumber >= 0) { numberOfFields += classNode.fields?.size() ?: 0 } super.visitClass(classNode) } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/PostProcessingMetric.groovy0000644000175000017500000000165712107724360026764 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric /** * Interface for metrics that need to perform processing after all source files have been processed (analyzed). * * NOTE: This interface is subject to change. * * @author Chris Mair */ interface PostProcessingMetric { void afterAllSourceCodeProcessed() } GMetrics-0.7/src/main/groovy/org/gmetrics/metric/MethodMetric.groovy0000644000175000017500000000216112107724360025211 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric import org.codehaus.groovy.ast.MethodNode import org.gmetrics.result.MetricResult import org.gmetrics.source.SourceCode import org.codehaus.groovy.ast.expr.ClosureExpression /** * Represents a method-level metric * * @author Chris Mair */ interface MethodMetric extends Metric { MetricResult applyToMethod(MethodNode methodNode, SourceCode sourceCode) MetricResult applyToClosure(ClosureExpression closureExpression, SourceCode sourceCode) }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/AbstractMethodMetric.groovy0000644000175000017500000000720712107724357026711 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric import org.codehaus.groovy.ast.ClassNode import org.codehaus.groovy.ast.MethodNode import org.codehaus.groovy.ast.expr.ClosureExpression import org.gmetrics.source.SourceCode import org.gmetrics.util.AstUtil import org.gmetrics.result.ClassMetricResult import org.gmetrics.result.MetricResult import org.gmetrics.result.MethodKey /** * Abstract superclass for method-based metrics. * * @author Chris Mair */ abstract class AbstractMethodMetric extends AbstractMetric implements MethodMetric { final MetricLevel baseLevel = MetricLevel.METHOD boolean includeClosureFields = true abstract MetricResult calculate(MethodNode methodNode, SourceCode sourceCode) abstract MetricResult calculate(ClosureExpression closureExpression, SourceCode sourceCode) @Override MetricResult applyToMethod(MethodNode methodNode, SourceCode sourceCode) { if (!enabled) { return null } return calculate(methodNode, sourceCode) } @Override MetricResult applyToClosure(ClosureExpression closureExpression, SourceCode sourceCode) { if (!enabled) { return null } return calculate(closureExpression, sourceCode) } protected ClassMetricResult calculateForClass(ClassNode classNode, SourceCode sourceCode) { Map childMetricResults = [:] if (isNotAnInterface(classNode)) { addMethodsToMetricResults(sourceCode, classNode, childMetricResults) addClosureFieldsToMetricResults(sourceCode, classNode, childMetricResults) } if (childMetricResults.isEmpty()) { return null } def aggregateMetricResults = createAggregateMetricResult(MetricLevel.CLASS, childMetricResults.values(), classNode) return new ClassMetricResult(aggregateMetricResults, childMetricResults) } private void addClosureFieldsToMetricResults(SourceCode sourceCode, ClassNode classNode, Map childMetricResults) { if (!includeClosureFields) { return } def closureFields = classNode.fields.findAll {fieldNode -> AstUtil.isClosureField(fieldNode) } closureFields.each {fieldNode -> def fieldResult = calculate(fieldNode.initialExpression, sourceCode) if (fieldResult) { def methodKey = new MethodKey(fieldNode.name) childMetricResults[methodKey] = fieldResult } } } private void addMethodsToMetricResults(SourceCode sourceCode, ClassNode classNode, Map childMetricResults) { def methodsPlusConstructors = classNode.getMethods() + classNode.getDeclaredConstructors() methodsPlusConstructors.each {methodNode -> def methodResult = calculate(methodNode, sourceCode) if (methodResult) { def methodKey = new MethodKey(methodNode) childMetricResults[methodKey] = methodResult } } } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/AbstractAstVisitor.groovy0000644000175000017500000000425312107724355026430 0ustar ebourgebourg/* * Copyright 2008 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric import org.codehaus.groovy.ast.ASTNode import org.codehaus.groovy.ast.ClassCodeVisitorSupport import org.codehaus.groovy.control.SourceUnit import org.gmetrics.source.SourceCode import org.codehaus.groovy.ast.MethodNode /** * Abstract superclass for Groovy AST Visitors * * @author Chris Mair */ abstract class AbstractAstVisitor extends ClassCodeVisitorSupport implements AstVisitor { public static final MAX_SOURCE_LINE_LENGTH = 60 public static final SOURCE_LINE_LAST_SEGMENT_LENGTH = 12 SourceCode sourceCode private Set visited = [] as Set /** * Return true if the AST expression has not already been visited. If it is * the first visit, register the expression so that the next visit will return false. * @param expression - the AST expression to check * @return true if the AST expression has NOT already been visited */ protected isFirstVisit(expression) { if(visited.contains(expression)) { return false } visited << expression return true } /** * Return the source line corresponding to the specified AST node * @param node - the Groovy AST node */ protected String sourceLine(ASTNode node) { return sourceCode.line(node.lineNumber-1) } protected SourceUnit getSourceUnit() { return source } protected boolean isSyntheticNonRunMethod(MethodNode methodNode) { return methodNode.lineNumber < 0 && methodNode.name != 'run' } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/AbstractMetric.groovy0000644000175000017500000000456712107724356025555 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric import org.codehaus.groovy.ast.ClassNode import org.gmetrics.source.SourceCode import org.gmetrics.result.* import org.codehaus.groovy.ast.ASTNode /** * Abstract superclass for metrics. * * Subclasses must implement the calculateForClass(ClassNode, SourceCode) method. * * @author Chris Mair */ abstract class AbstractMetric implements Metric { boolean enabled = true List functions = ['total', 'average'] protected abstract ClassMetricResult calculateForClass(ClassNode classNode, SourceCode sourceCode) MetricResult applyToPackage(String path, String packageName, Collection childMetricResults) { if (!enabled) { return null } return calculateForPackage(path, packageName, childMetricResults) } @SuppressWarnings('UnusedMethodParameter') protected MetricResult calculateForPackage(String path, String packageName, Collection childMetricResults) { return createAggregateMetricResult(MetricLevel.PACKAGE, childMetricResults) } ClassMetricResult applyToClass(ClassNode classNode, SourceCode sourceCode) { if (!enabled) { return null } return calculateForClass(classNode, sourceCode) } protected boolean isNotAnInterface(ClassNode classNode) { return !classNode.isInterface() } protected MetricResult createAggregateMetricResult(MetricLevel metricLevel, Collection childMetricResults, ASTNode node=null) { def metricResultBuilder = new MetricResultBuilder(metric:this, metricLevel:metricLevel) return metricResultBuilder.createAggregateMetricResult(childMetricResults, node?.lineNumber) } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/crap/0000755000175000017500000000000012623566446022317 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/metric/crap/CrapMetric.groovy0000644000175000017500000000673112107724356025617 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.crap import org.codehaus.groovy.ast.MethodNode import org.codehaus.groovy.ast.expr.ClosureExpression import org.gmetrics.metric.AbstractMethodMetric import org.gmetrics.metric.MetricLevel import org.gmetrics.metric.cyclomatic.CyclomaticComplexityMetric import org.gmetrics.result.MetricResult import org.gmetrics.result.SingleNumberMetricResult import org.gmetrics.source.SourceCode import org.gmetrics.metric.MethodMetric /** * Metric to calculate the CRAP metric * * See http://www.artima.com/weblogs/viewpost.jsp?thread=210575 * * @author Chris Mair */ class CrapMetric extends AbstractMethodMetric { private static final int ROUNDING_MODE = BigDecimal.ROUND_HALF_UP final String name = 'CRAP' MethodMetric coverageMetric MethodMetric complexityMetric = new CyclomaticComplexityMetric() @Override MetricResult calculate(ClosureExpression closureExpression, SourceCode sourceCode) { return null } @Override MetricResult calculate(MethodNode methodNode, SourceCode sourceCode) { assert coverageMetric assert complexityMetric if (methodNode.isAbstract() || methodNode.lineNumber < 0) { return null } def complexityResult = complexityMetric.applyToMethod(methodNode, sourceCode) if (complexityResult == null) { return null } def complexityValue = complexityResult['total'] def coverageResult = coverageMetric.applyToMethod(methodNode, sourceCode) if (coverageResult == null) { return null } def coverageValue = coverageResult['total'] def crap = calculateCrapScore(complexityValue, coverageValue) return crap == null ? null : new SingleNumberMetricResult(this, MetricLevel.METHOD, crap, methodNode.lineNumber) } //------------------------------------------------------------------------------------ // Helper Methods //------------------------------------------------------------------------------------ /** * Calculate the CRAP metric score * @param complexity - the cyclomatic complexity of a method * @param coverage - the code coverage (by line) of a method * @return the CRAP Metric score * * Given a Java method m, C.R.A.P. for m is calculated as follows: * * C.R.A.P.(m) = comp(m)^2 * (1 – cov(m)/100)^3 + comp(m) * * See http://www.artima.com/weblogs/viewpost.jsp?thread=210575 */ protected BigDecimal calculateCrapScore(BigDecimal complexity, BigDecimal coverage) { if (complexity == null || coverage == null) { return null } def result = (complexity * complexity) * ((1.0 - coverage) ** 3) + complexity return result.setScale(1, ROUNDING_MODE) } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/coupling/0000755000175000017500000000000012623566446023212 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/metric/coupling/AfferentCouplingReferenceManager.groovy0000644000175000017500000000635412107724355033026 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.coupling import static org.gmetrics.result.FunctionNames.* import org.gmetrics.metric.Metric import org.gmetrics.metric.MetricLevel import org.gmetrics.result.MutableMapMetricResult /** * Maintains a mapping of packageName -> packages that reference it, as well as a reference * to a MetricResult for the associated PackageResultsNode. As this object is updated with * subsequent package references, the MetricResult is updated as well. So, the MetricResult * for a PackageResultNode is not "complete" until all of the packages have been processed. * * @author Chris Mair */ class AfferentCouplingReferenceManager extends AbstractCouplingReferenceManager { protected static final String REFERENCED_FROM_PACKAGES = 'referencedFromPackages' AfferentCouplingReferenceManager(Metric metric) { super(metric) } void updateStatisticsForAllPackages() { referencesFromPackage.each { packageName, referencedPackages -> applyReverseReferencesForPackage(packageName, referencedPackages) } updateStatisticsForAllAncestorPackages() } @Override protected MutableMapMetricResult createEmptyMetricResult() { new MutableMapMetricResult(metric, MetricLevel.PACKAGE, [(VALUE):0, (TOTAL):0, (AVERAGE):0, (REFERENCED_FROM_PACKAGES):[] as Set]) } //------------------------------------------------------------------------------------ // Helper Methods //------------------------------------------------------------------------------------ private void applyReverseReferencesForPackage(packageName, Set referencedPackages) { referencedPackages.each { referencedPackage -> if (isSourcePackageOrAncestor(referencedPackage)) { def metricResult = metricResultMap[referencedPackage] metricResult[REFERENCED_FROM_PACKAGES] << packageName def numberOfReferences = metricResult[REFERENCED_FROM_PACKAGES].size() metricResult[VALUE] = numberOfReferences metricResult[TOTAL] = numberOfReferences metricResult[AVERAGE] = numberOfReferences metricResult.count = 1 } } } private void updateStatisticsForAllAncestorPackages() { def packagesWithReferences = sortPackagesWithReferencesWithParentFirst() packagesWithReferences.each { packageName -> def metricResult = metricResultMap[packageName] updateStatisticsForAncestorPackage(packageName, metricResult[TOTAL], metricResult.count) } } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/coupling/AfferentCouplingMetric.groovy0000644000175000017500000000367612107724355031064 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.coupling import org.gmetrics.metric.MetricLevel import org.gmetrics.metric.PostProcessingMetric import org.gmetrics.result.MetricResult /** * Metric for counting the number of other packages that depend on the classes within this package. * * @author Chris Mair */ class AfferentCouplingMetric extends AbstractPackageCouplingMetric implements PostProcessingMetric { final String name = 'AfferentCoupling' private final AfferentCouplingReferenceManager referenceManager = new AfferentCouplingReferenceManager(this) @Override protected MetricResult calculateForPackage(String path, String packageName, Collection childMetricResults) { childMetricResults.each { childMetricResult -> if (childMetricResult.metricLevel == MetricLevel.CLASS) { referenceManager.addReferencesFromPackage(packageName, childMetricResult[REFERENCED_PACKAGES]) } } return referenceManager.getPackageMetricResult(packageName) } @Override void afterAllSourceCodeProcessed() { referenceManager.updateStatisticsForAllPackages() } // For testing protected MetricResult getMetricResult(String packageName) { return referenceManager.getPackageMetricResult(packageName) } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/coupling/EfferentCouplingMetric.groovy0000644000175000017500000000367712107724355031071 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.coupling import org.gmetrics.metric.MetricLevel import org.gmetrics.result.MetricResult import org.gmetrics.metric.PostProcessingMetric /** * Metric for counting the number of other packages that the classes in the package depend upon. * * @author Chris Mair */ class EfferentCouplingMetric extends AbstractPackageCouplingMetric implements PostProcessingMetric { final String name = 'EfferentCoupling' private final EfferentCouplingReferenceManager referenceManager = new EfferentCouplingReferenceManager(this) @Override protected MetricResult calculateForPackage(String path, String packageName, Collection childMetricResults) { childMetricResults.each { childMetricResult -> if (childMetricResult.metricLevel == MetricLevel.CLASS) { referenceManager.addReferencesFromPackage(packageName, childMetricResult[REFERENCED_PACKAGES]) } } return referenceManager.getPackageMetricResult(packageName) } // For testing protected MetricResult getMetricResult(String packageName) { return referenceManager.getPackageMetricResult(packageName) } @Override void afterAllSourceCodeProcessed() { referenceManager.updateStatisticsForAllPackages() } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/coupling/AbstractPackageCouplingMetric.groovy0000644000175000017500000000362111762472605032343 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.coupling import org.codehaus.groovy.ast.ClassNode import org.gmetrics.metric.AbstractMetric import org.gmetrics.metric.MetricLevel import org.gmetrics.result.ClassMetricResult import org.gmetrics.result.MapMetricResult import org.gmetrics.source.SourceCode import org.gmetrics.result.FunctionNames /** * Abstract superclass for Metrics that measure package-level coupling. * * @author Chris Mair */ abstract class AbstractPackageCouplingMetric extends AbstractMetric { protected static final String REFERENCED_PACKAGES = 'referencedPackages' final MetricLevel baseLevel = MetricLevel.PACKAGE String ignorePackageNames AbstractPackageCouplingMetric() { this.functions = [FunctionNames.VALUE, FunctionNames.AVERAGE] } @Override protected ClassMetricResult calculateForClass(ClassNode classNode, SourceCode sourceCode) { def visitor = new PackageReferenceAstVisitor(ignorePackageNames) visitor.setSourceCode(sourceCode) visitor.visitClass(classNode) def otherPackages = visitor.otherPackages def metricResult = new MapMetricResult(this, MetricLevel.CLASS, [(REFERENCED_PACKAGES):otherPackages]) return new ClassMetricResult(metricResult) } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/coupling/EfferentCouplingReferenceManager.groovy0000644000175000017500000000633312107724355033027 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.coupling import static org.gmetrics.result.FunctionNames.* import org.gmetrics.metric.Metric import org.gmetrics.metric.MetricLevel import org.gmetrics.result.MutableMapMetricResult /** * Maintains a mapping of packageName -> packages it references, as well as a reference * to a MetricResult for the associated PackageResultsNode. As this object is updated with * subsequent package references, the MetricResult is updated as well. So, the MetricResult * for a PackageResultNode is not "complete" until all of the packages have been processed. * * @author Chris Mair */ class EfferentCouplingReferenceManager extends AbstractCouplingReferenceManager { protected static final String REFERENCED_PACKAGES = 'referencedPackages' EfferentCouplingReferenceManager(Metric metric) { super(metric) } void updateStatisticsForAllPackages() { referencesFromPackage.each { packageName, referencedPackages -> applyReferencesForPackage(packageName, referencedPackages) } updateStatisticsForAllAncestorPackages() } @Override protected MutableMapMetricResult createEmptyMetricResult() { new MutableMapMetricResult(metric, MetricLevel.PACKAGE, [(VALUE):0, (TOTAL):0, (AVERAGE):0, (REFERENCED_PACKAGES):[] as Set]) } //------------------------------------------------------------------------------------ // Helper Methods //------------------------------------------------------------------------------------ private void applyReferencesForPackage(packageName, Set allReferencedPackages) { Set referencedPackages = allReferencedPackages.findAll { String pkg -> isSourcePackage(pkg) } def metricResult = metricResultMap[packageName] metricResult[REFERENCED_PACKAGES] = referencedPackages def numberOfReferences = referencedPackages.size() metricResult[VALUE] = numberOfReferences metricResult[TOTAL] = numberOfReferences metricResult[AVERAGE] = numberOfReferences metricResult.count = 1 } private void updateStatisticsForAllAncestorPackages() { def packagesWithReferences = sortPackagesWithReferencesWithParentFirst() packagesWithReferences.each { packageName -> def metricResult = metricResultMap[packageName] updateStatisticsForAncestorPackage(packageName, metricResult[TOTAL], metricResult.count) } } private boolean isSourcePackage(String packageName) { return referencesFromPackage.containsKey(packageName) } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/coupling/PackageReferenceAstVisitor.groovy0000644000175000017500000001105311740162400031640 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.coupling import static org.gmetrics.util.ClassNameUtil.* import static org.gmetrics.util.AstUtil.isFromGeneratedSourceCode import org.gmetrics.metric.AbstractAstVisitor import org.gmetrics.util.ImportUtil import org.gmetrics.util.WildcardPattern import org.codehaus.groovy.ast.* import org.codehaus.groovy.ast.expr.* /** * AstVisitor that checks for references to other packages * * @author Chris Mair */ class PackageReferenceAstVisitor extends AbstractAstVisitor { final Set otherPackages = [] private WildcardPattern wildcard private WildcardPattern javaAndGroovyWildcard = new WildcardPattern('java.*,groovy.*') private String thisPackageName PackageReferenceAstVisitor(String ignorePackageNames) { wildcard = new WildcardPattern(ignorePackageNames, false) } @Override void visitClass(ClassNode node) { thisPackageName = node.packageName def superClassName = node.superClass.name checkTypeName(superClassName, node) node.interfaces.each { interfaceNode -> checkTypeName(interfaceNode.name, node) } super.visitClass(node) } @Override void visitField(FieldNode node) { checkType(node) } @Override void visitConstructorCallExpression(ConstructorCallExpression node) { checkType(node) super.visitConstructorCallExpression(node) } @Override void visitVariableExpression(VariableExpression expression) { checkType(expression) super.visitVariableExpression(expression) } @Override void visitConstructorOrMethod(MethodNode node, boolean isConstructor) { checkTypeName(node.returnType.name, node) node.parameters.each { parameter -> checkType(parameter) } super.visitConstructorOrMethod(node, isConstructor) } @Override void visitClosureExpression(ClosureExpression expression) { expression.parameters.each { parameter -> checkType(parameter) } super.visitClosureExpression(expression) } @Override void visitCastExpression(CastExpression expression) { checkTypeName(expression.type.name, expression) super.visitCastExpression(expression) } @Override void visitClassExpression(ClassExpression expression) { checkType(expression) super.visitClassExpression(expression) } @Override void visitPropertyExpression(PropertyExpression expression) { if (isClassName(expression.text)) { checkTypeName(expression.text, expression) } super.visitPropertyExpression(expression) } @Override void visitImports(ModuleNode node) { def allImports = node.imports + node.starImports + node.staticImports.values() + node.staticStarImports.values() allImports?.each { importNode -> def parentPackage = ImportUtil.packageNameForImport(importNode) checkPackageName(parentPackage, importNode) } super.visitImports(node) } //-------------------------------------------------------------------------- // Helper Methods //-------------------------------------------------------------------------- private void checkType(node) { checkTypeName(node.type.name, node) } private void checkTypeName(String typeName, node) { def parentPackage = parentPackageName(typeName) checkPackageName(parentPackage, node) } private void checkPackageName(String packageName, node) { def notGenerated = !isFromGeneratedSourceCode(node) || node instanceof ImportNode if (packageName && isValidPackageReference(packageName) && notGenerated) { otherPackages << packageName } } private boolean isValidPackageReference(String packageName) { return !wildcard.matches(packageName) && !javaAndGroovyWildcard.matches(packageName) && packageName != thisPackageName } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/coupling/AbstractCouplingReferenceManager.groovy0000644000175000017500000000772512107724355033042 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.coupling import static org.gmetrics.result.FunctionNames.AVERAGE import static org.gmetrics.result.FunctionNames.TOTAL import org.gmetrics.metric.Metric import org.gmetrics.result.MetricResult import org.gmetrics.result.MutableMapMetricResult import org.gmetrics.util.Calculator import org.gmetrics.util.ClassNameUtil /** * Abstract superclass for Afferent/efferent coupling reference manager classes. * * @author Chris Mair */ abstract class AbstractCouplingReferenceManager { protected static final String ROOT = '' final Metric metric protected Map> referencesFromPackage = [:].withDefault { [] as Set } protected Map metricResultMap = [:].withDefault { createEmptyMetricResult() } AbstractCouplingReferenceManager(Metric metric) { assert metric this.metric = metric } protected abstract MutableMapMetricResult createEmptyMetricResult() void addReferencesFromPackage(String rawPackageName, Collection rawPackages) { def packageName = normalizePackageName(rawPackageName) def packages = rawPackages.collect { pkg -> normalizePackageName(pkg) } referencesFromPackage[packageName].addAll(packages) metricResultMap[packageName].count = 1 } MetricResult getPackageMetricResult(String rawPackageName) { def packageName = normalizePackageName(rawPackageName) return metricResultMap[packageName] } //------------------------------------------------------------------------------------ // Helper Methods //------------------------------------------------------------------------------------ protected boolean isSourcePackageOrAncestor(String packageName) { if (referencesFromPackage.containsKey(packageName) || packageName == ROOT) { return true } return referencesFromPackage.keySet().find { fromPackageName -> fromPackageName.startsWith(packageName + '.') } } protected SortedSet sortPackagesWithReferencesWithParentFirst() { return new TreeSet(metricResultMap.keySet()) } protected void updateStatisticsForAncestorPackage(String packageName, int addToTotal, int addToCount) { def parentPackageName = parentPackageName(packageName) if (parentPackageName && isSourcePackageOrAncestor(parentPackageName)) { def metricResult = metricResultMap[parentPackageName] metricResult.count += addToCount metricResult.map[TOTAL] += addToTotal metricResult.map[AVERAGE] = Calculator.calculateAverage(metricResult.map[TOTAL], metricResult.count, 2) if (parentPackageName != ROOT) { updateStatisticsForAncestorPackage(parentPackageName, addToTotal, addToCount) } } } protected String parentPackageName(String packageName) { return ClassNameUtil.parentPackageName(packageName) ?: ROOT } protected Set getReferencesFromPackage(String rawPackageName) { def packageName = normalizePackageName(rawPackageName) return referencesFromPackage[packageName] } protected String normalizePackageName(String name) { return name ? name.replace('/', '.') : ROOT } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/abc/0000755000175000017500000000000012623566446022117 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/metric/abc/AbcAstVisitor.groovy0000644000175000017500000001415612107724355026102 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.abc import org.gmetrics.util.AstUtil import org.codehaus.groovy.ast.expr.* import org.codehaus.groovy.ast.stmt.* import org.gmetrics.metric.AbstractAstVisitor import org.codehaus.groovy.ast.MethodNode /** * AST Visitor for calculating the ABC Metric for a class/method. * * @see AbcMetric * * See http://www.softwarerenovation.com/ABCMetric.pdf * * @author Chris Mair * @version $Revision: 149 $ - $Date: 2011-09-24 17:12:21 -0400 (Sat, 24 Sep 2011) $ */ class AbcAstVisitor extends AbstractAstVisitor { private static final ASSIGNMENT_OPERATIONS = ['=', '++', '--', '+=', '-=', '/=', '*=', '%=', '<<=', '>>=', '>>>=', '&=', '|=', '^='] private static final COMPARISON_OPERATIONS = ['<', '>', '>=', '<=', '==', '!=', '<=>', '=~', '==~'] private static final BOOLEAN_LOGIC_OPERATIONS = ['&&', '||'] int numberOfAssignments = 0 int numberOfBranches = 0 int numberOfConditions = 0 private boolean visited = false void visitMethod(MethodNode methodNode) { if (!isSyntheticNonRunMethod(methodNode) && !methodNode.isAbstract()) { this.visited = true super.visitMethod(methodNode) } } void visitBinaryExpression(BinaryExpression expression) { handleExpressionContainingOperation(expression) super.visitBinaryExpression(expression) } void visitPrefixExpression(PrefixExpression expression) { handleExpressionContainingOperation(expression) super.visitPrefixExpression(expression) } void visitPostfixExpression(PostfixExpression expression) { handleExpressionContainingOperation(expression) super.visitPostfixExpression(expression) } void visitMethodCallExpression(MethodCallExpression call) { numberOfBranches ++ super.visitMethodCallExpression(call) } void visitPropertyExpression(PropertyExpression expression) { // Treat a property access as a method call numberOfBranches ++ super.visitPropertyExpression(expression) } void visitConstructorCallExpression(ConstructorCallExpression call) { numberOfBranches ++ super.visitConstructorCallExpression(call) } void visitIfElse(IfStatement ifElse) { if (isNotEmptyStatement(ifElse.elseBlock)) { numberOfConditions ++ } super.visitIfElse(ifElse) } void visitSwitch(SwitchStatement statement) { numberOfConditions += statement.caseStatements.size() if (isNotEmptyStatement(statement.defaultStatement)) { numberOfConditions ++ } super.visitSwitch(statement) } void visitTryCatchFinally(TryCatchStatement statement) { numberOfConditions ++ // for the 'try' numberOfConditions += statement.catchStatements.size() // for each 'catch' super.visitTryCatchFinally(statement) } void visitTernaryExpression(TernaryExpression expression) { numberOfConditions ++ super.visitTernaryExpression(expression) } void visitBooleanExpression(BooleanExpression booleanExpression) { if (isSingleVariable(booleanExpression.expression)) { numberOfConditions++ } super.visitBooleanExpression(booleanExpression) } void visitNotExpression(NotExpression notExpression) { if (isSingleVariable(notExpression.expression)) { numberOfConditions++ } super.visitNotExpression(notExpression) } //-------------------------------------------------------------------------- // Internal helper methods //-------------------------------------------------------------------------- private void handleExpressionContainingOperation(Expression expression) { def operationName = expression.operation.text if (operationName in ASSIGNMENT_OPERATIONS && !isFinalVariableDeclaration(expression)) { numberOfAssignments ++ } if (operationName in COMPARISON_OPERATIONS) { numberOfConditions ++ } if (operationName in BOOLEAN_LOGIC_OPERATIONS) { numberOfConditions += countUnaryConditionals(expression) } } // Use Groovy dynamic dispatch to achieve pseudo-polymorphism. // Call appropriate countUnaryConditionals() logic based on type of expression private int countUnaryConditionals(BinaryExpression binaryExpression) { def count = 0 def operationName = binaryExpression.operation.text if (operationName in BOOLEAN_LOGIC_OPERATIONS) { if (isSingleVariable(binaryExpression.leftExpression)) { count ++ } if (isSingleVariable(binaryExpression.rightExpression)) { count ++ } } return count } @SuppressWarnings('UnusedPrivateMethodParameter') private int countUnaryConditionals(Expression expression) { // Not necessary? return 0 } private boolean isSingleVariable(expression) { return expression instanceof VariableExpression } private boolean isFinalVariableDeclaration(expression) { return expression instanceof DeclarationExpression && AstUtil.isFinalVariable(expression, sourceCode) } private boolean isNotEmptyStatement(Statement statement) { statement.class != EmptyStatement } } GMetrics-0.7/src/main/groovy/org/gmetrics/metric/abc/result/0000755000175000017500000000000012623566446023435 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/metric/abc/result/AggregateAbcMetricResult.groovy0000644000175000017500000001115512107724356031537 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.abc.result import org.gmetrics.result.MetricResult import org.gmetrics.metric.Metric import org.gmetrics.metric.abc.AbcVector import org.gmetrics.metric.MetricLevel /** * An aggregate MetricResult implementation specifically for the ABC Metric. * * @author Chris Mair * @version $Revision: 163 $ - $Date: 2011-10-02 21:55:46 -0400 (Sun, 02 Oct 2011) $ */ class AggregateAbcMetricResult implements MetricResult { final Metric metric final MetricLevel metricLevel final Object value = null final Integer lineNumber private assignmentSum = 0 private branchSum = 0 private conditionSum = 0 private count private functionValues = [:] AggregateAbcMetricResult(Metric metric, MetricLevel metricLevel, Collection children, Integer lineNumber=null) { assert metric assert metricLevel assert children != null this.metric = metric this.metricLevel = metricLevel calculateFunctions(children) this.lineNumber = lineNumber } protected void calculateFunctions(Collection children) { addChildrenToAbcVector(children) count = children.inject(0) { value, child -> value + child.count } if (includesFunction('total')) { functionValues['total'] = getTotalAbcVector().getMagnitude() } if (includesFunction('average')) { functionValues['average'] = getAverageAbcVector().getMagnitude() } if (includesFunction('minimum')) { functionValues['minimum'] = calculateMinimum(children) } if (includesFunction('maximum')) { functionValues['maximum'] = calculateMaximum(children) } } int getCount() { return count } /** * Return the sum of this set of ABC vectors. Each component (A,B,C) of the result * is summed separately. The formula for each component is: * A1 + A2 + .. AN * and likewise for B and C values. */ Object getTotalAbcVector() { return new AbcVector(assignmentSum, branchSum, conditionSum) } Object getAbcVector() { return getTotalAbcVector() } /** * Return the average of this set of ABC vectors. Each component (A,B,C) of the result * is calculated and averaged separately. The formula for each component is: * (A1 + A2 + .. AN) / N * and likewise for B and C values. Each component of the result vector is rounded down to an integer. */ Object getAverageAbcVector() { def a = average(assignmentSum, count) def b = average(branchSum, count) def c = average(conditionSum, count) return new AbcVector(a, b, c) } Object getAt(String name) { return functionValues[name] } String toString() { "AggregateAbcMetricResult[count=$count, A=$assignmentSum, B=$branchSum, C=$conditionSum]" } private void addChildrenToAbcVector(children) { children.each { child -> def abcVector = child.abcVector assignmentSum += abcVector.assignments branchSum += abcVector.branches conditionSum += abcVector.conditions } } private Object calculateMinimum(children) { def minChild = children.min { child -> child['minimum'] } return minChild != null ? minChild['minimum'] : 0 } private Object calculateMaximum(children) { def maxChild = children.max { child -> child['maximum'] } return maxChild != null ? maxChild['maximum'] : 0 } private boolean includesFunction(String functionName) { return functionName in metric.functions } private average(int sum, int count) { if (sum && count) { def rawAverage = sum / count def rounded = rawAverage.setScale(0, BigDecimal.ROUND_HALF_UP) return rounded as Integer } return 0 } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/abc/result/AbcMetricResult.groovy0000644000175000017500000000342312107724356027727 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.abc.result import org.gmetrics.result.MetricResult import org.gmetrics.metric.Metric import org.gmetrics.metric.abc.AbcVector import org.gmetrics.metric.MetricLevel /** * A MetricResult specifically for the ABC metric * * @author Chris Mair * @version $Revision: 181 $ - $Date: 2011-11-28 21:30:53 -0500 (Mon, 28 Nov 2011) $ */ class AbcMetricResult implements MetricResult { final AbcVector abcVector final Metric metric final MetricLevel metricLevel final Integer lineNumber final int count = 1 private magnitude AbcMetricResult(Metric metric, MetricLevel metricLevel, AbcVector abcVector, Integer lineNumber=null) { assert abcVector assert metricLevel this.metricLevel = metricLevel this.abcVector = abcVector this.metric = metric this.magnitude = abcVector.magnitude this.lineNumber = lineNumber } Object getAt(String name) { return name in metric.functions ? magnitude : null } String toString() { "AbcMetricResult[$abcVector, vector=$abcVector, magnitude=$magnitude]" } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/abc/AbcVector.groovy0000644000175000017500000000353012107724355025227 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.abc /** * Represents a single ABC Metric result: a vector of the three A, B, C values. * * @author Chris Mair * @version $Revision: 96 $ - $Date: 2010-03-09 20:17:58 -0500 (Tue, 09 Mar 2010) $ */ class AbcVector { final int assignments final int branches final int conditions AbcVector(int assignments, int branches, int conditions) { assert assignments >= 0 assert branches >= 0 assert conditions >= 0 this.assignments = assignments this.branches = branches this.conditions = conditions } /** * Return the magnitude of this ABC vector, specifically: * |ABC| = sqrt((A*A)+(B*B)+(C*C)) * @return the magnitude of the ABC vector as a BigDecimal with scale of 1 */ BigDecimal getMagnitude() { def sumOfSquares = squared(assignments) + squared(branches) + squared(conditions) def result = Math.sqrt(sumOfSquares) return new BigDecimal(result).setScale(1, BigDecimal.ROUND_HALF_DOWN) } String toString() { "<$assignments, $branches, $conditions>" } private BigInteger squared(int val) { return val * val } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/abc/AbcMetric.groovy0000644000175000017500000000744312107724356025220 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.abc import org.codehaus.groovy.ast.MethodNode import org.codehaus.groovy.ast.expr.ClosureExpression import org.gmetrics.metric.AbstractMethodMetric import org.gmetrics.source.SourceCode import org.gmetrics.metric.abc.result.AggregateAbcMetricResult import org.gmetrics.metric.abc.result.AbcMetricResult import org.gmetrics.result.MetricResult import org.codehaus.groovy.ast.ASTNode import org.gmetrics.metric.MetricLevel /** * Calculate the ABC Metric for a class/method. * * The ABC Counting Rules for Groovy: *
 *   1. Add one to the assignment count for each occurrence of an assignment operator, excluding
 *      constant declarations:    = *= /= %= += <<= >>= &= |= ^= >>>=
 *   2. Add one to the assignment count for each occurrence of an increment or decrement operator
 *      (prefix or postfix):    ++ --
 *   3. Add one to the branch count for each function call or class method call.
 *   4. Add one to the branch count for each occurrence of the new operator.
 *   5. Add one to the condition count for each use of a conditional operator:
 *      == != <= >= < > <=> =~ ==~
 *   6. Add one to the condition count for each use of the following keywords:
 *      else case default try catch ?
 *   7. Add one to the condition count for each unary conditional expression. These are cases where
 *      a single variable/field/value is treated as a boolean value. Examples include if (x)
 *      and return !ready.
 * 
* * Additional notes: *
    *
  • A property access is treated like a method call (and thus increments the branch count).
  • *
  • If a class field is initialized to a Closure (ClosureExpression), then that Closure is * analyzed just like a method.
  • *
* * See http://www.softwarerenovation.com/ABCMetric.pdf * * @author Chris Mair * @version $Revision: 218 $ - $Date: 2012-01-07 08:40:26 -0500 (Sat, 07 Jan 2012) $ */ class AbcMetric extends AbstractMethodMetric { final String name = "ABC" MetricResult calculate(MethodNode methodNode, SourceCode sourceCode) { def visitor = new AbcAstVisitor(sourceCode:sourceCode) visitor.visitMethod(methodNode) if (visitor.visited) { def abcVector = new AbcVector(visitor.numberOfAssignments, visitor.numberOfBranches, visitor.numberOfConditions) return new AbcMetricResult(this, MetricLevel.METHOD, abcVector, methodNode.lineNumber) } return null } MetricResult calculate(ClosureExpression closureExpression, SourceCode sourceCode) { def visitor = new AbcAstVisitor(sourceCode:sourceCode) visitor.visitClosureExpression(closureExpression) def abcVector = new AbcVector(visitor.numberOfAssignments, visitor.numberOfBranches, visitor.numberOfConditions) return new AbcMetricResult(this, MetricLevel.METHOD, abcVector, closureExpression.lineNumber) } @Override protected MetricResult createAggregateMetricResult(MetricLevel metricLevel, Collection childMetricResults, ASTNode node) { new AggregateAbcMetricResult(this, metricLevel, childMetricResults, node?.lineNumber) } } GMetrics-0.7/src/main/groovy/org/gmetrics/metric/classcount/0000755000175000017500000000000012623566446023550 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/metric/classcount/ClassCountMetric.groovy0000644000175000017500000000363312107724360030232 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.classcount import org.codehaus.groovy.ast.ClassNode import org.gmetrics.metric.AbstractMetric import org.gmetrics.metric.MetricLevel import org.gmetrics.result.ClassMetricResult import org.gmetrics.result.MetricResult import org.gmetrics.result.SingleNumberMetricResult import org.gmetrics.source.SourceCode /** * Metric for counting the number of classes within each package. * * @author Chris Mair */ class ClassCountMetric extends AbstractMetric { final String name = 'ClassCount' final MetricLevel baseLevel = MetricLevel.PACKAGE @SuppressWarnings('UnusedMethodParameter') protected ClassMetricResult calculateForClass(ClassNode classNode, SourceCode sourceCode) { def metricResult = new SingleNumberMetricResult(this, MetricLevel.CLASS, 1, classNode.lineNumber) return new ClassMetricResult(metricResult) } @SuppressWarnings('UnusedMethodParameter') protected MetricResult calculateForPackage(String path, String packageName, Collection childMetricResults) { def numClasses = childMetricResults.inject(0) { sum, result -> result.metricLevel == MetricLevel.CLASS ? sum+1 : sum } return new SingleNumberMetricResult(this, MetricLevel.PACKAGE, numClasses) } } GMetrics-0.7/src/main/groovy/org/gmetrics/metric/coverage/0000755000175000017500000000000012623566446023165 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/metric/coverage/CoberturaSignatureParser.groovy0000644000175000017500000001241512107724357031416 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.coverage import org.gmetrics.util.PathUtil /** * Provides utility methods to parse and compare Cobertura method signatures * * @author Chris Mair */ class CoberturaSignatureParser { private static final PRIMITIVES = [ B:'byte', I:'int', C:'char', D:'double', F:'float', J:'long', S:'short', Z:'boolean' ] private static final PRIMITIVE_CODES = PRIMITIVES.keySet() static boolean matchesCoberturaMethod(String name, String signature, String coberturaName, String coberturaSignature) { if (name != coberturaName) { return false } def astTypes = parseSignatureParameterTypes(signature) def coberturaTypes = parseCoberturaSignatureParameterTypes(coberturaSignature) return astTypes == coberturaTypes } static int numberOfParameters(String coberturaSignature) { return parseCoberturaSignatureParameterTypes(coberturaSignature).size() } //------------------------------------------------------------------------------------ // Helper Methods //------------------------------------------------------------------------------------ private static List parseSignatureParameterTypes(String signature) { def parameterString = extractParameters(signature) if (!parameterString) { return [] } return parameterString.tokenize(',').collect { rawType -> def type = rawType.trim() if (type.startsWith('[')) { def arrayTypeCode = type.substring(1) PRIMITIVES[arrayTypeCode] + '[]' } else { classNameNoPackage(type) } } } private static String extractParameters(String signature) { final REGEX = /.*\((.*)\).*/ def parameterMatcher = signature =~ REGEX return parameterMatcher[0][1] } protected static List parseCoberturaSignatureParameterTypes(String signature) { def parameterString = extractParameters(signature) if (!parameterString) { return [] } return parseParameterTypes(parameterString) } private static class ParseContext { final parameters = [] private typeName = null private withinArray = false void startFullyQualifiedTypeName() { typeName = new StringBuilder() } boolean withinFullyQualifiedTypeName() { return typeName != null } void appendToFullyQualifiedTypeName(String c) { typeName.append(c) } void terminateFullyQualifiedTypeName() { def suffix = withinArray ? '[]' : '' parameters << PathUtil.getName(typeName.toString()) + suffix typeName = null withinArray = false } void startNewArrayType() { withinArray = true } void processPrimitiveTypeCode(String c) { def suffix = withinArray ? '[]' : '' parameters << PRIMITIVES[c] + suffix withinArray = false } } private static List parseParameterTypes(String parameterString) { def parseContext = new ParseContext() parameterString.each { c -> if (parseContext.withinFullyQualifiedTypeName()) { processCharacterWithinFullyQualifiedTypeName(c, parseContext) } else { processStandaloneCharacter(c, parseContext) } } return parseContext.parameters } private static void processStandaloneCharacter(String c, ParseContext parseContext) { if (c == '[') { parseContext.startNewArrayType() } else if (c == 'L') { parseContext.startFullyQualifiedTypeName() } else if (c in PRIMITIVE_CODES) { parseContext.processPrimitiveTypeCode(c) } } private static void processCharacterWithinFullyQualifiedTypeName(String c, ParseContext parseContext) { if (c == ';') { parseContext.terminateFullyQualifiedTypeName() } else { parseContext.appendToFullyQualifiedTypeName(c) } } private static String classNameNoPackage(String name) { def index = name.lastIndexOf('.') return index > -1 ? name.substring(index+1) : name } // Private constructor to prevent instantiation. All members are static. private CoberturaSignatureParser() { } } GMetrics-0.7/src/main/groovy/org/gmetrics/metric/coverage/Ratio.groovy0000644000175000017500000000313312107724356025503 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.coverage /** * Represents a simple ratio of X / Y * * @author Chris Mair */ class Ratio { public static final ZERO = new Ratio(0,0) final int numerator final int denominator Ratio(int numerator, int denominator) { this.numerator = numerator this.denominator = denominator } Ratio plus(Ratio ratio) { assert ratio new Ratio(numerator + ratio.numerator, denominator + ratio.denominator) } BigDecimal toBigDecimal(int scale, int roundingMode) { def bd = denominator == 0 ? BigDecimal.ZERO : new BigDecimal(numerator) / new BigDecimal(denominator) return bd.setScale(scale, roundingMode) } Object asType(Class theClass) { assert theClass == BigDecimal return new BigDecimal(numerator) / new BigDecimal(denominator) } @Override String toString() { return numerator + '/' + denominator } } GMetrics-0.7/src/main/groovy/org/gmetrics/metric/coverage/AbstractCoberturaCoverageMetric.groovy0000644000175000017500000002200312463030226032643 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.coverage import org.gmetrics.metric.AbstractMetric import org.gmetrics.metric.MetricLevel import groovy.util.slurpersupport.GPathResult import org.apache.log4j.Logger import org.codehaus.groovy.ast.MethodNode import org.gmetrics.result.SingleNumberMetricResult import org.gmetrics.result.MetricResult import org.codehaus.groovy.ast.ClassNode import org.gmetrics.result.MethodKey import org.gmetrics.result.ClassMetricResult import org.gmetrics.source.SourceCode import org.gmetrics.result.MetricResultBuilder import org.gmetrics.util.AstUtil import org.gmetrics.metric.MethodMetric import org.codehaus.groovy.ast.expr.ClosureExpression /** * Abstract superclass for metrics that provide test code coverage from a Cobertura XML file. * * @author Chris Mair */ abstract class AbstractCoberturaCoverageMetric extends AbstractMetric implements MethodMetric { protected static final int SCALE = 2 protected static final int ROUNDING_MODE = BigDecimal.ROUND_HALF_UP @SuppressWarnings('LoggerWithWrongModifiers') protected static final LOG = Logger.getLogger(AbstractCoberturaCoverageMetric) final MetricLevel baseLevel = MetricLevel.METHOD String coberturaFile private classMetricResultBuilder = new MetricResultBuilder(metric:this, metricLevel:MetricLevel.CLASS, scale:2) private CoberturaCoverageFile coberturaCoverageFile private Object coberturaLock = new Object() //------------------------------------------------------------------------------------ // Abstract Methods //------------------------------------------------------------------------------------ /** * @return the name of the desired coverage attribute within the Cobertura XML file (e.g. "line-rate") */ protected abstract String getAttributeName() /** * @return the calculated coverage ratio for the Cobertura XML class element */ protected abstract Ratio getCoverageRatioForSingleClass(matchingClassElement) //------------------------------------------------------------------------------------ // API and Template methods //------------------------------------------------------------------------------------ @Override MetricResult applyToMethod(MethodNode methodNode, SourceCode sourceCode) { if (!enabled) { return null } return calculate(methodNode, sourceCode) } @Override MetricResult applyToClosure(ClosureExpression closureExpression, SourceCode sourceCode) { throw new UnsupportedOperationException("The [${getName()}] metric does not support Closures") } @Override protected ClassMetricResult calculateForClass(ClassNode classNode, SourceCode sourceCode) { if (classNode.isInterface()) { return null } def className = classNode.name def matchingClassElement = getCoberturaCoverageFile().findClassElement(className) if (matchingClassElement.isEmpty()) { LOG.warn("No coverage information found for class [$className]") return null } def lineRate = getCoberturaCoverageFile().hasInnerClasses(className) ? calculateCoverageForClassAndInnerClasses(className) : getCoberturaCoverageFile().parseCoverageRate(matchingClassElement) Map methodResults = buildMethodResults(classNode, matchingClassElement) def classLevelMetricResult = classMetricResultBuilder.createAggregateMetricResult(methodResults.values(), classNode.lineNumber, [total:lineRate]) return new ClassMetricResult(classLevelMetricResult, methodResults) } @Override protected MetricResult calculateForPackage(String packagePath, String packageName, Collection childMetricResults) { if (packagePath == null) { return getOverallPackageMetricValue() } def matchingPackageElement = getCoberturaCoverageFile().findPackageElement(packageName) if (matchingPackageElement == null || matchingPackageElement.isEmpty()) { if (containsClasses(childMetricResults)) { LOG.warn("No coverage information found for package [$packageName]") } return null } def lineRate = getCoberturaCoverageFile().parseCoverageRate(matchingPackageElement) return new SingleNumberMetricResult(this, MetricLevel.PACKAGE, lineRate) } MetricResult calculate(MethodNode methodNode, SourceCode sourceCode) { def className = methodNode.declaringClass.name def classXmlElement = getCoberturaCoverageFile().findClassElement(className) return calculateMethodResult(methodNode, classXmlElement) } //------------------------------------------------------------------------------------ // Helper Methods //------------------------------------------------------------------------------------ protected Ratio getCoverageRatioForClass(String className) { def matchingClassElement = getCoberturaCoverageFile().findClassElement(className) def overallClassRatio = getCoverageRatioForSingleClass(matchingClassElement) def innerClasses = getCoberturaCoverageFile().findInnerClasses(className) innerClasses.each { innerClassElement -> overallClassRatio += getCoverageRatioForSingleClass(innerClassElement) } return overallClassRatio } private BigDecimal calculateCoverageForClassAndInnerClasses(String className) { def ratio = getCoverageRatioForClass(className) return ratio.toBigDecimal(SCALE, ROUNDING_MODE) } private MetricResult getOverallPackageMetricValue() { def lineRate = getCoberturaCoverageFile().getOverallCoverageRate() return new SingleNumberMetricResult(this, MetricLevel.PACKAGE, lineRate) } private Map buildMethodResults(ClassNode classNode, GPathResult classXmlElement) { Map childMetricResults = [:] def methodsPlusConstructors = classNode.getMethods() + classNode.getDeclaredConstructors() def validMethods = methodsPlusConstructors.findAll { methodNode -> !methodNode.isAbstract() && !AstUtil.isFromGeneratedSourceCode(methodNode) } validMethods.each { methodNode -> def metricResult = calculateMethodResult(methodNode, classXmlElement) if (metricResult) { def methodKey = new MethodKey(methodNode) childMetricResults[methodKey] = metricResult } } return childMetricResults } protected SingleNumberMetricResult calculateMethodResult(MethodNode methodNode, GPathResult classXmlElement) { def matchingMethodElement = findMethodElement(methodNode, classXmlElement) if (!matchingMethodElement.isEmpty()) { def lineRate = getCoberturaCoverageFile().parseCoverageRate(matchingMethodElement) return new SingleNumberMetricResult(this, MetricLevel.METHOD, lineRate, methodNode.lineNumber) } logMissingMethodCoverageInformation(methodNode) return null } private void logMissingMethodCoverageInformation(MethodNode methodNode) { if (!AstUtil.isEmptyMethod(methodNode)) { def className = methodNode.declaringClass.name LOG.warn("No coverage information found for method [${className}.${methodNode.name}]") } } protected GPathResult findMethodElement(MethodNode methodNode, GPathResult classXmlElement) { def numParameters = methodNode.parameters.size() def methodName = methodNode.name def methodSignature = methodNode.typeDescriptor return getCoberturaCoverageFile().findMethodElement(methodName, numParameters, methodSignature, classXmlElement) } private boolean containsClasses(Collection childMetricResults) { childMetricResults.find { metricResult -> metricResult.metricLevel == MetricLevel.CLASS } } private CoberturaCoverageFile getCoberturaCoverageFile() { synchronized(coberturaLock) { if (coberturaCoverageFile == null) { assert coberturaFile coberturaCoverageFile = new CoberturaCoverageFile(coberturaFile, getAttributeName()) } } return coberturaCoverageFile } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/coverage/CoberturaCoverageFile.groovy0000644000175000017500000001107212461574675030642 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.coverage import groovy.util.slurpersupport.GPathResult import org.gmetrics.util.io.ResourceFactory import org.gmetrics.util.io.DefaultResourceFactory import org.apache.log4j.Logger /** * Parses and provides access to a Cobertura "coverage.xml" * * @author Chris Mair */ class CoberturaCoverageFile { private static final LOG = Logger.getLogger(CoberturaCoverageFile) private static final int SCALE = 2 private static final int ROUNDING_MODE = BigDecimal.ROUND_HALF_UP private String coberturaFile private xml private String attributeName private coberturaXmlFileLoadLock = new Object() private ResourceFactory resourceFactory = new DefaultResourceFactory() CoberturaCoverageFile(String coberturaFile, String attributeName) { this.coberturaFile = coberturaFile this.attributeName = attributeName } protected BigDecimal getOverallCoverageRate() { def coverage = getCoberturaXml() return parseCoverageRate(coverage) } protected BigDecimal parseCoverageRate(GPathResult node) { def lineRateStr = node.@"$attributeName".text() def lineRate = lineRateStr as BigDecimal return lineRate.setScale(SCALE, ROUNDING_MODE) } protected GPathResult findPackageElement(String packageName) { def coverage = getCoberturaXml() return coverage.packages.package.find { it.@name == packageName } } protected GPathResult findClassElement(String className) { def coverage = getCoberturaXml() return coverage.packages.package.classes.class.find { it.@name == className } } protected GPathResult findInnerClasses(String className) { def coverage = getCoberturaXml() return coverage.packages.package.classes.class.findAll { it.@name.text().startsWith(className + '$_') } } protected boolean hasInnerClasses(String className) { return !findInnerClasses(className).isEmpty() } protected GPathResult findMethodElement(String methodName, int numParameters, String methodSignature, GPathResult classXmlElement) { def matchingMethodElements = findAllMethodElements(methodName, numParameters, classXmlElement) if (matchingMethodElements.size() == 1) { return matchingMethodElements[0] } return matchingMethodElements.find { CoberturaSignatureParser.matchesCoberturaMethod(methodName, methodSignature, it.@name.text(), it.@signature.text()) } } private findAllMethodElements(String methodName, int numParameters, GPathResult classXmlElement) { def matchingMethodElements = classXmlElement.methods.method.findAll { def xmlNumParameters = CoberturaSignatureParser.numberOfParameters(it.@signature.text()) methodName == it.@name.text() && numParameters == xmlNumParameters } return matchingMethodElements } protected GPathResult getCoberturaXml() { synchronized(coberturaXmlFileLoadLock) { if (xml == null) { assert coberturaFile LOG.info("Loading Cobertura XML file [$coberturaFile]") def inputStream = resourceFactory.getResource(coberturaFile).inputStream def xmlSlurper = createNonValidatingXmlSlurper() xml = xmlSlurper.parse(inputStream) } } return xml } private XmlSlurper createNonValidatingXmlSlurper() { def xmlSlurper = new XmlSlurper() // Do not try to validate using the DTD, which may refer to an unavailable URI xmlSlurper.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false) // See http://bugs.java.com/bugdatabase/view_bug.do?bug_id=7157610 xmlSlurper.setFeature("http://apache.org/xml/features/disallow-doctype-decl", false) return xmlSlurper } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/coverage/CoberturaBranchCoverageMetric.groovy0000644000175000017500000000416012107724356032312 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.coverage /** * Metric for test code coverage by branch (branch-rate) from a Cobertura XML file. * * @see "http://cobertura.sourceforge.net/" * * @author Chris Mair */ class CoberturaBranchCoverageMetric extends AbstractCoberturaCoverageMetric { final String name = 'CoberturaBranchCoverage' final String attributeName = 'branch-rate' @Override protected Ratio getCoverageRatioForSingleClass(matchingClassElement) { if (matchingClassElement.isEmpty()) { return null } def methodLines = findLineElementsWithBranches(matchingClassElement.methods.method.lines.line) def standaloneLines = findLineElementsWithBranches(matchingClassElement.lines.line) return getBranchCoverageRatio(methodLines) + getBranchCoverageRatio(standaloneLines) } private findLineElementsWithBranches(lines) { lines.findAll { line -> line.@branch == 'true' } } private Ratio getBranchCoverageRatio(linesElements) { final REGEX = /.*\((\d+)\/(\d+)\)/ def ratio = Ratio.ZERO linesElements.each { line -> def conditionCoverageString = line.@'condition-coverage' def m = conditionCoverageString =~ REGEX assert m, "$conditionCoverageString does not match $REGEX" def covered = m[0][1] as int def total = m[0][2] as int ratio += new Ratio(covered, total) } return ratio } }GMetrics-0.7/src/main/groovy/org/gmetrics/metric/coverage/CoberturaLineCoverageMetric.groovy0000644000175000017500000000322412107724356032004 0ustar ebourgebourg/* * Copyright 2011 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.metric.coverage /** * Metric for test code coverage by line (line-rate) from a Cobertura XML file. * * @see "http://cobertura.sourceforge.net/" * * @author Chris Mair */ class CoberturaLineCoverageMetric extends AbstractCoberturaCoverageMetric { final String name = 'CoberturaLineCoverage' final String attributeName = 'line-rate' @Override protected Ratio getCoverageRatioForSingleClass(matchingClassElement) { if (matchingClassElement.isEmpty()) { return null } def methodLines = matchingClassElement.methods.method.lines.line def standaloneLines = matchingClassElement.lines.line return getLinesCoverageRatio(methodLines) + getLinesCoverageRatio(standaloneLines) } private Ratio getLinesCoverageRatio(linesElements) { def numLines = linesElements.size() def numLinesCovered = linesElements.findAll { line -> line.@hits != '0' }.size() return new Ratio(numLinesCovered, numLines) } }GMetrics-0.7/src/main/groovy/org/gmetrics/resultsnode/0000755000175000017500000000000012623566446022456 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/resultsnode/MethodResultsNode.groovy0000644000175000017500000000347112107724360027326 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.resultsnode import org.gmetrics.metric.MetricLevel import org.gmetrics.result.MetricResult import org.gmetrics.metric.Metric /** * Represents a method result node in the hierarchy of metric result nodes * * @author Chris Mair */ class MethodResultsNode implements ResultsNode { final String name final String signature final MetricLevel level = MetricLevel.METHOD final List metricResults = [] MethodResultsNode(String name) { this.name = name } MethodResultsNode(String name, String signature) { this.name = name this.signature = signature } boolean containsClassResults() { return false } MetricResult getMetricResult(Metric metric) { assert metric return metricResults.find { metricResult -> metricResult.metric == metric } } void addMetricResult(MetricResult metricResult) { assert metricResult metricResults << metricResult } Map getChildren() { return Collections.EMPTY_MAP } String toString() { return "MethodResultsNode[metricResults=$metricResults]" } }GMetrics-0.7/src/main/groovy/org/gmetrics/resultsnode/PackageResultsNode.groovy0000644000175000017500000000535012107724360027437 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.resultsnode import org.gmetrics.metric.MetricLevel import org.gmetrics.result.MetricResult import org.gmetrics.metric.Metric /** * Represents a package node in the hierarchy of metric result nodes * * @author Chris Mair */ class PackageResultsNode implements ResultsNode { final String name final String packageName final String path final MetricLevel level = MetricLevel.PACKAGE final List metricResults = [] private Map children = [:] PackageResultsNode(String name, String packageName, String path) { this.name = name this.packageName = packageName this.path = path } Map getChildren() { return children } boolean containsClassResults() { return children.values().find { node -> node.containsClassResults() } } MetricResult getMetricResult(Metric metric) { assert metric return metricResults.find { metricResult -> metricResult.metric == metric } } void addChildIfNotEmpty(String name, ResultsNode child) { assert name assert child if (child.metricResults) { children[name] = child } } void addChild(String name, ResultsNode child) { assert name assert child children[name] = child } void applyMetric(Metric metric) { this.children = children.asImmutable() def childMetricResultsForMetric = [] children.values().each { child -> def metricResult = child.getMetricResult(metric) if (metricResult) { childMetricResultsForMetric << metricResult } } def packageMetricResult = metric.applyToPackage(path, packageName, childMetricResultsForMetric) if (packageMetricResult) { metricResults << packageMetricResult } } String toString() { return "PackageResultsNode[path=$path, packageName=$packageName, metricResults=$metricResults, children=$children]" } } GMetrics-0.7/src/main/groovy/org/gmetrics/resultsnode/ResultsNode.groovy0000644000175000017500000000215212107724360026160 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.resultsnode import org.gmetrics.metric.* import org.gmetrics.result.MetricResult /** * Represents the interface for a single node in the hierarchical network of results nodes * * @author Chris Mair */ interface ResultsNode { String getName() MetricLevel getLevel() boolean containsClassResults() List getMetricResults() Map getChildren() MetricResult getMetricResult(Metric metric) } GMetrics-0.7/src/main/groovy/org/gmetrics/resultsnode/ClassResultsNode.groovy0000644000175000017500000000477312107724360027161 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.resultsnode import org.gmetrics.metric.MetricLevel import org.gmetrics.result.MetricResult import org.gmetrics.metric.Metric import org.gmetrics.result.ClassMetricResult import org.gmetrics.result.MethodKey /** * Represents a node in the hierarchy of metric result nodes * * @author Chris Mair */ class ClassResultsNode implements ResultsNode { final String name final String fileName final String filePath final MetricLevel level = MetricLevel.CLASS final Map children = [:] final List metricResults = [] ClassResultsNode(String name) { this.name = name } ClassResultsNode(String name, String fileName, String filePath) { this.name = name this.fileName = fileName this.filePath = filePath } boolean containsClassResults() { return true } MetricResult getMetricResult(Metric metric) { assert metric return metricResults.find { metricResult -> metricResult.metric == metric } } void addClassMetricResult(ClassMetricResult classMetricResult) { if (classMetricResult) { metricResults << classMetricResult.classMetricResult def methodMetricResults = classMetricResult.getMethodMetricResults() methodMetricResults.each { k, v -> addMethodMetricResult(k, v) } } } String toString() { return "ClassResultsNode[$level: metricResults=$metricResults, children=$children]" } private void addMethodMetricResult(MethodKey methodKey, MetricResult metricResult) { if (children[methodKey] == null) { children[methodKey] = new MethodResultsNode(methodKey.methodName, methodKey.signature) } children[methodKey].addMetricResult(metricResult) } }GMetrics-0.7/src/main/groovy/org/gmetrics/formatter/0000755000175000017500000000000012623566446022112 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/formatter/Formatter.groovy0000644000175000017500000000147312107724354025320 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.formatter /** * Interface for objects that can format Object into a String * * @author Chris Mair */ interface Formatter { String format(Object value) } GMetrics-0.7/src/main/groovy/org/gmetrics/formatter/ToStringFormatter.groovy0000644000175000017500000000155712107724353027014 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.formatter /** * Formatter that just performs a toString() on the value * * @author Chris Mair */ class ToStringFormatter implements Formatter { String format(Object value) { return value } } GMetrics-0.7/src/main/groovy/org/gmetrics/formatter/FormatterFactory.groovy0000644000175000017500000000255212107724353026646 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.formatter /** * Factory for Formatter objects * * @author Chris Mair */ class FormatterFactory { /** * Create and return a Formatter instance based on a specification String, which is the class name. * @param formatterSpecification - the specification of the Formatter. It is of the form: * "fully-qualified-class-name" * e.g., "org.gmetrics.formatter.PercentageFormatter" * @return a Formatter instance */ Formatter getFormatter(String formatterSpecification) { assert formatterSpecification def formatterClass = getClass().classLoader.loadClass(formatterSpecification) return formatterClass.newInstance() } } GMetrics-0.7/src/main/groovy/org/gmetrics/formatter/PercentageFormatter.groovy0000644000175000017500000000231612107724354027313 0ustar ebourgebourg/* * Copyright 2012 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.formatter /** * Formatter that formats a number as a percentage, and appends a '%' * * @author Chris Mair */ class PercentageFormatter implements Formatter { String format(Object value) { if (value == null) { return null } assert value instanceof Number, "The value must be a Number, but was a ${value.getClass().name}" def percentage = value * 100 as BigDecimal def integerPercentage = percentage.setScale(0, BigDecimal.ROUND_HALF_UP) return integerPercentage.toString() + '%' } } GMetrics-0.7/src/main/groovy/org/gmetrics/ant/0000755000175000017500000000000012623566446020671 5ustar ebourgebourgGMetrics-0.7/src/main/groovy/org/gmetrics/ant/Report.groovy0000644000175000017500000000206312107724353023402 0ustar ebourgebourgpackage org.gmetrics.ant /* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * JavaBean class holding the properties for a element with the GMetrics Ant Task. * * @see GMetricsTask * * @author Chris Mair * @version $Revision: 24 $ - $Date: 2009-12-10 21:17:05 -0500 (Thu, 10 Dec 2009) $ */ class Report { String type final Map options = [:] void addConfiguredOption(ReportOption option) { options[option.name] = option.value } }GMetrics-0.7/src/main/groovy/org/gmetrics/ant/GMetricsTask.groovy0000644000175000017500000001117412107724353024472 0ustar ebourgebourg/* * Copyright 2010 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.ant import org.apache.tools.ant.Task import org.apache.log4j.Logger import org.gmetrics.metricset.MetricSet import org.apache.tools.ant.BuildException import org.apache.tools.ant.types.FileSet import org.gmetrics.metricset.DefaultMetricSet import org.gmetrics.GMetricsRunner import org.gmetrics.analyzer.SourceAnalyzer import org.gmetrics.metricset.GroovyDslMetricSet /** * Ant Task for GMetrics. *

* The metricSetFile property specifies the path to a Groovy MetricSet * definition file. By default, the path specified is relative to the classpath, but it may be optionally * prefixed by any of the valid java.net.URL prefixes, such as "file:" (to load from a relative or absolute * path on the filesystem), or "http:". The metricSetFile property is optional. If not * specified, it uses the set of Metrics defined by DefaultMetricSet. *

* At least one nested fileset element is required, and is used to specify the source files * to be analyzed. This is the standard Ant FileSet, and is quite powerful and flexible. * See the Apache Ant Manual for more information on FileSets. *

* The report nested element defines the type and report-specific options for the * output report. The report element includes a type attribute (which * specifies the fully-qualified class name of the ReportWriter class) and * can contain nested option elements. Each option, in turn, must * include a name and value attribue. Currently, the * BasicHtmlReportWriter class (org.gmetrics.report.BasicHtmlReportWriter) is the * only report type provided. * * @see "http://ant.apache.org/manual/index.html" * * @author Chris Mair * @version $Revision: 95 $ - $Date: 2010-03-08 22:00:10 -0500 (Mon, 08 Mar 2010) $ */ class GMetricsTask extends Task { private static final LOG = Logger.getLogger(GMetricsTask) String metricSetFile protected List reportWriters = [] protected List fileSets = [] // Abstract creation of the GMetricsNarcRunner instance to allow substitution of test spy for unit tests protected createGMetricsRunner = { return new GMetricsRunner() } /** * Execute this Ant Task */ void execute() throws BuildException { assert fileSets try { def sourceAnalyzer = createSourceAnalyzer() def gMetricsRunner = createGMetricsRunner() gMetricsRunner.metricSet = createMetricSet() gMetricsRunner.reportWriters = reportWriters gMetricsRunner.sourceAnalyzer = sourceAnalyzer gMetricsRunner.execute() } catch(Throwable t) { LOG.error("GMetricsTask error", t) throw t } } /** * Ant-defined method (by convention), called with each instance of a nested * element within this task. */ void addFileset(FileSet fileSet) { assert fileSet this.fileSets << fileSet } /** * Ant-defined method (by convention), called with each instance of a nested * element within this task. */ void addConfiguredReport(Report report) { if (!report.type) { throw new BuildException("Report type null or empty") } def reportClass = getClass().classLoader.loadClass(report.type) def reportWriter = reportClass.newInstance() report.options.each { name, value -> reportWriter[name] = value } LOG.debug("Adding report: $reportWriter") reportWriters << reportWriter } private MetricSet createMetricSet() { return metricSetFile ? new GroovyDslMetricSet(metricSetFile): new DefaultMetricSet() } private SourceAnalyzer createSourceAnalyzer() { return new AntFileSetSourceAnalyzer(getProject(), fileSets) } }GMetrics-0.7/src/main/groovy/org/gmetrics/ant/AntFileSetSourceAnalyzer.groovy0000644000175000017500000001663012107724353027021 0ustar ebourgebourg/* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.gmetrics.ant import org.gmetrics.analyzer.SourceAnalyzer import org.gmetrics.metricset.MetricSet import org.apache.tools.ant.Project import org.gmetrics.resultsnode.ResultsNode import org.gmetrics.resultsnode.PackageResultsNode import org.apache.log4j.Logger import org.gmetrics.source.SourceFile import org.gmetrics.resultsnode.ClassResultsNode import org.codehaus.groovy.ast.ClassNode import org.gmetrics.source.SourceCode import org.gmetrics.util.PathUtil import org.gmetrics.metric.PostProcessingMetric /** * SourceAnalyzer implementation that gets source files from one or more Ant FileSets. * This class is not reentrant. * * @author Chris Mair */ class AntFileSetSourceAnalyzer implements SourceAnalyzer { private static final LOG = Logger.getLogger(AntFileSetSourceAnalyzer) private Project project protected List fileSets = [] protected ResultsNode rootResultsNode = new PackageResultsNode(null, '', null) /** * Construct a new instance on the specified List of Ant FileSets. * @param project - the Ant Project * @param fileSets - the List of Ant FileSet; my be empty; must not be null */ AntFileSetSourceAnalyzer(Project project, List fileSets) { assert project assert fileSets != null this.project = project this.fileSets = fileSets } /** * Analyze all source code using the specified MetricSet and return the results node. * @param metricSet - the MetricSet to apply to each source component; must not be null. * @return the root ResultsNode resulting from applying the MetricSet to all of the source */ ResultsNode analyze(MetricSet metricSet) { fileSets.each { fileSet -> processFileSet(fileSet, metricSet) } calculatePackageLevelMetricResults(rootResultsNode, metricSet) afterAllSourceCodeProcessed(metricSet) return rootResultsNode } List getSourceDirectories() { def baseDir = project.baseDir.absolutePath return fileSets.collect { fileSet -> def path = fileSet.getDir(project).path removeBaseDirectoryPrefix(baseDir, path) } } //-------------------------------------------------------------------------- // Internal Helper Methods //-------------------------------------------------------------------------- private void calculatePackageLevelMetricResults(PackageResultsNode resultsNode, MetricSet metricSet) { resultsNode.children.each { name, child -> calculatePackageLevelMetricResults(child, metricSet) } metricSet.metrics.each { metric -> resultsNode.applyMetric(metric) } } @SuppressWarnings('EmptyMethod') @SuppressWarnings('UnusedPrivateMethodParameter') private void calculatePackageLevelMetricResults(ResultsNode resultsNode, MetricSet metricSet) { // do nothing } private void processFileSet(fileSet, metricSet) { def dirScanner = fileSet.getDirectoryScanner(project) def baseDir = fileSet.getDir(project) def includedFiles = dirScanner.includedFiles if (!includedFiles) { LOG.info("No matching files found for FileSet with basedir [$baseDir]") } includedFiles.each {filePath -> processFile(baseDir, filePath, metricSet) } } private void processFile(File baseDir, String filePath, MetricSet metricSet) { def parentPath = PathUtil.getParent(filePath) def file = new File(baseDir, filePath) def sourceCode = new SourceFile(file) def ast = sourceCode.ast if (ast) { def parentResultsNode ast.classes.each { classNode -> if (!parentResultsNode) { def packageName = classNode.packageName ?: '' parentResultsNode = findOrAddResultsNodeForPath(parentPath, packageName) } def classResultsNode = applyMetricsToClass(classNode, metricSet, sourceCode) def className = classNode.name parentResultsNode.addChildIfNotEmpty(className, classResultsNode) } } } // TODO Harvest? private ClassResultsNode applyMetricsToClass(ClassNode classNode, MetricSet metricSet, SourceCode sourceCode) { def classResultsNode = new ClassResultsNode(classNode.name, sourceCode.getName(), sourceCode.getPath()) metricSet.metrics.each { metric -> def classMetricResult = metric.applyToClass(classNode, sourceCode) classResultsNode.addClassMetricResult(classMetricResult) } return classResultsNode } protected ResultsNode findResultsNodeForPath(String path) { return findPackageResultsNodeForPath(rootResultsNode, path) } private ResultsNode findPackageResultsNodeForPath(PackageResultsNode resultsNode, String path) { if (resultsNode.path == path) { return resultsNode } def children = resultsNode.children.values() return resultFromFirstMatchOrElseNull(children) { child -> findPackageResultsNodeForPath(child, path) } } @SuppressWarnings('UnusedPrivateMethodParameter') private ResultsNode findPackageResultsNodeForPath(ResultsNode resultsNode, String path) { return null } private resultFromFirstMatchOrElseNull(collection, closure) { def result def found = collection.find { child -> result = closure(child) } return found ? result : null } protected ResultsNode findOrAddResultsNodeForPath(String path, String packageName) { def resultsNode = findResultsNodeForPath(path) if (resultsNode) { return resultsNode } def parentPath = PathUtil.getParent(path) def name = PathUtil.getName(path) def newPackageNode = new PackageResultsNode(name, packageName, path) def parentNode = parentPath ? findOrAddResultsNodeForPath(parentPath, '') : rootResultsNode parentNode.addChild(name, newPackageNode) return newPackageNode } private String removeBaseDirectoryPrefix(String baseDir, String path) { if (path.startsWith(baseDir)) { path = path - baseDir return removeLeadingSlash(path) } return path } private String removeLeadingSlash(path) { return (path.startsWith('\\') || path.startsWith('/')) ? path.substring(1) : path } private void afterAllSourceCodeProcessed(MetricSet metricSet) { metricSet.metrics.each { metric -> if (metric instanceof PostProcessingMetric) { metric.afterAllSourceCodeProcessed() } } } }GMetrics-0.7/src/main/groovy/org/gmetrics/ant/ReportOption.groovy0000644000175000017500000000174512107724353024601 0ustar ebourgebourgpackage org.gmetrics.ant /* * Copyright 2009 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * JavaBean class holding the properties for a

GMetrics Report: Sample

Report timestamp: Jan 31, 2015 3:48:36 PM

Metric Results

Package/Class/MethodComplexity (total)Complexity (average)Method Lines (total)Method Lines (average)
[p] All packages7371.727866.3
[p] org7371.727866.3
[p] org/gmetrics7371.727866.3
[c] org.gmetrics.GMetricsRunner11.01919.0
[m] execute111919
[p] org/gmetrics/analyzer00.000.0
[c] org.gmetrics.analyzer.AnalysisContextN/AN/AN/AN/A
[c] org.gmetrics.analyzer.SourceAnalyzerN/AN/AN/AN/A
[p] org/gmetrics/ant381.71607.0
[c] org.gmetrics.ant.AntFileSetSourceAnalyzer281.81217.6
[m] analyze1188
[m] getSourceDirectories1177
[m] calculatePackageLevelMetricResults1144
[m] calculatePackageLevelMetricResults1155
[m] processFileSet221313
[m] processFile442020
[m] applyMetricsToClass1188
[m] findResultsNodeForPath1133
[m] findPackageResultsNodeForPath2277
[m] findPackageResultsNodeForPath1144
[m] resultFromFirstMatchOrElseNull2277
[m] findOrAddResultsNodeForPath331212
[m] removeBaseDirectoryPrefix2277
[m] removeLeadingSlash3333
[m] afterAllSourceCodeProcessed2277
[m] <init>1166
[c] org.gmetrics.ant.GMetricsTask91.5366.0
[m] execute221515
[m] addFileset1144
[m] addConfiguredReport221010
[m] createMetricSet2233
[m] createSourceAnalyzer1133
[m] createGMetricsRunner1111
[c] org.gmetrics.ant.Report11.033.0
[m] addConfiguredOption1133
[c] org.gmetrics.ant.ReportOptionN/AN/AN/AN/A
[p] org/gmetrics/formatter41.3196.3
[c] org.gmetrics.formatter.FormatterN/AN/AN/AN/A
[c] org.gmetrics.formatter.FormatterFactory11.066.0
[m] getFormatter1166
[c] org.gmetrics.formatter.PercentageFormatter22.01010.0
[m] format221010
[c] org.gmetrics.formatter.ToStringFormatter11.033.0
[m] format1133
[p] org/gmetrics/metric3051.611206.1
[c] org.gmetrics.metric.AbstractAstVisitor61.5164.0
[m] isFirstVisit2277
[m] sourceLine1133
[m] getSourceUnit1133
[m] isSyntheticNonRunMethod2233
[c] org.gmetrics.metric.AbstractMethodMetric122.45511.0
[m] applyToMethod2277
[m] applyToClosure2277
[m] calculateForClass331616
[m] addClosureFieldsToMetricResults331414
[m] addMethodsToMetricResults221111
[c] org.gmetrics.metric.AbstractMetric81.6234.6
[m] applyToPackage2266
[m] calculateForPackage1144
[m] applyToClass2266
[m] isNotAnInterface1133
[m] createAggregateMetricResult2244
[c] org.gmetrics.metric.AstVisitorN/AN/AN/AN/A
[c] org.gmetrics.metric.MethodMetricN/AN/AN/AN/A
[c] org.gmetrics.metric.MetricN/AN/AN/AN/A
[c] org.gmetrics.metric.MetricLevel51.0183.6
[m] parse1133
[m] parseCommaSeparatedList1166
[m] getNames1133
[m] toString1133
[m] <init>1133
[c] org.gmetrics.metric.PostProcessingMetricN/AN/AN/AN/A
[p] org/gmetrics/metric/abc671.62315.5
[c] org.gmetrics.metric.abc.AbcAstVisitor331.71045.5
[m] visitMethod3366
[m] visitBinaryExpression1144
[m] visitPrefixExpression1144
[m] visitPostfixExpression1144
[m] visitMethodCallExpression1144
[m] visitPropertyExpression1155
[m] visitConstructorCallExpression1144
[m] visitIfElse2266
[m] visitSwitch2277
[m] visitTryCatchFinally1155
[m] visitTernaryExpression1144
[m] visitBooleanExpression2266
[m] visitNotExpression2266
[m] handleExpressionContainingOperation551212
[m] countUnaryConditionals441313
[m] countUnaryConditionals1144
[m] isSingleVariable1133
[m] isFinalVariableDeclaration2244
[m] isNotEmptyStatement1133
[c] org.gmetrics.metric.abc.AbcMetric51.7196.3
[m] calculate2299
[m] calculate1166
[m] createAggregateMetricResult2244
[c] org.gmetrics.metric.abc.AbcVector41.0194.8
[m] getMagnitude1155
[m] toString1133
[m] squared1133
[m] <init>1188
[p] org/gmetrics/metric/abc/result251.6895.6
[c] org.gmetrics.metric.abc.result.AbcMetricResult41.3155.0
[m] getAt2233
[m] toString1133
[m] <init>1199
[c] org.gmetrics.metric.abc.result.AggregateAbcMetricResult211.6745.7
[m] calculateFunctions551717
[m] getCount1133
[m] getTotalAbcVector1133
[m] getAbcVector1133
[m] getAverageAbcVector1166
[m] getAt1133
[m] toString1133
[m] addChildrenToAbcVector1188
[m] calculateMinimum2244
[m] calculateMaximum2244
[m] includesFunction1133
[m] average3388
[m] <init>1199
[p] org/gmetrics/metric/classcount31.5105.0
[c] org.gmetrics.metric.classcount.ClassCountMetric31.5105.0
[m] calculateForClass1155
[m] calculateForPackage2255
[p] org/gmetrics/metric/coupling611.42425.6
[c] org.gmetrics.metric.coupling.AbstractCouplingReferenceManager161.8475.2
[m] addReferencesFromPackage1166
[m] getPackageMetricResult1144
[m] isSourcePackageOrAncestor3388
[m] sortPackagesWithReferencesWithParentFirst1133
[m] updateStatisticsForAncestorPackage441212
[m] parentPackageName2233
[m] getReferencesFromPackage1144
[m] normalizePackageName2233
[m] <init>1144
[c] org.gmetrics.metric.coupling.AbstractPackageCouplingMetric21.0136.5
[m] calculateForClass111010
[m] <init>1133
[c] org.gmetrics.metric.coupling.AfferentCouplingMetric41.3165.3
[m] calculateForPackage2299
[m] afterAllSourceCodeProcessed1144
[m] getMetricResult1133
[c] org.gmetrics.metric.coupling.AfferentCouplingReferenceManager61.2336.6
[m] updateStatisticsForAllPackages1166
[m] createEmptyMetricResult1144
[m] applyReverseReferencesForPackage221313
[m] updateStatisticsForAllAncestorPackages1177
[m] <init>1133
[c] org.gmetrics.metric.coupling.EfferentCouplingMetric41.3165.3
[m] calculateForPackage2299
[m] getMetricResult1133
[m] afterAllSourceCodeProcessed1144
[c] org.gmetrics.metric.coupling.EfferentCouplingReferenceManager61.0335.5
[m] updateStatisticsForAllPackages1166
[m] createEmptyMetricResult1144
[m] applyReferencesForPackage111010
[m] updateStatisticsForAllAncestorPackages1177
[m] isSourcePackage1133
[m] <init>1133
[c] org.gmetrics.metric.coupling.PackageReferenceAstVisitor231.5845.6
[m] visitClass111010
[m] visitField1144
[m] visitConstructorCallExpression1155
[m] visitVariableExpression1155
[m] visitConstructorOrMethod1188
[m] visitClosureExpression1177
[m] visitCastExpression1155
[m] visitClassExpression1155
[m] visitPropertyExpression2277
[m] visitImports2299
[m] checkType1133
[m] checkTypeName1144
[m] checkPackageName5566
[m] isValidPackageReference3333
[m] <init>1133
[p] org/gmetrics/metric/coverage821.63486.8
[c] org.gmetrics.metric.coverage.AbstractCoberturaCoverageMetric271.91208.6
[m] applyToMethod2277
[m] applyToClosure1144
[m] calculateForClass442121
[m] calculateForPackage551616
[m] calculate1155
[m] getCoverageRatioForClass111010
[m] calculateCoverageForClassAndInnerClasses1144
[m] getOverallPackageMetricValue1144
[m] buildMethodResults331616
[m] calculateMethodResult2299
[m] logMissingMethodCoverageInformation2266
[m] findMethodElement1166
[m] containsClasses1133
[m] getCoberturaCoverageFile2299
[c] org.gmetrics.metric.coverage.CoberturaBranchCoverageMetric41.3268.7
[m] getCoverageRatioForSingleClass221010
[m] findLineElementsWithBranches1133
[m] getBranchCoverageRatio111313
[c] org.gmetrics.metric.coverage.CoberturaCoverageFile141.3696.3
[m] getOverallCoverageRate1144
[m] parseCoverageRate1155
[m] findPackageElement1144
[m] findClassElement1144
[m] findInnerClasses1144
[m] hasInnerClasses1133
[m] findMethodElement221111
[m] findAllMethodElements2277
[m] getCoberturaXml221212
[m] createNonValidatingXmlSlurper111111
[m] <init>1144
[c] org.gmetrics.metric.coverage.CoberturaLineCoverageMetric31.5157.5
[m] getCoverageRatioForSingleClass221010
[m] getLinesCoverageRatio1155
[c] org.gmetrics.metric.coverage.CoberturaSignatureParser202.0757.5
[m] matchesCoberturaMethod2288
[m] numberOfParameters1133
[m] parseSignatureParameterTypes331616
[m] extractParameters1155
[m] parseCoberturaSignatureParameterTypes2277
[m] parseParameterTypes221212
[m] processStandaloneCharacter441111
[m] processCharacterWithinFullyQualifiedTypeName2288
[m] classNameNoPackage2244
[m] <init>1111
[c] org.gmetrics.metric.coverage.CoberturaSignatureParser$ParseContext81.3233.8
[m] startFullyQualifiedTypeName1133
[m] withinFullyQualifiedTypeName1133
[m] appendToFullyQualifiedTypeName1133
[m] terminateFullyQualifiedTypeName2266
[m] startNewArrayType1133
[m] processPrimitiveTypeCode2255
[c] org.gmetrics.metric.coverage.Ratio61.2204.0
[m] plus1144
[m] toBigDecimal2244
[m] asType1144
[m] toString1144
[m] <init>1144
[p] org/gmetrics/metric/crap103.33612.0
[c] org.gmetrics.metric.crap.CrapMetric103.33612.0
[m] calculate1144
[m] calculate662424
[m] calculateCrapScore3388
[p] org/gmetrics/metric/cyclomatic201.5634.8
[c] org.gmetrics.metric.cyclomatic.CyclomaticComplexityAstVisitor161.5514.6
[m] visitMethod3388
[m] visitIfElse1144
[m] visitWhileLoop1144
[m] visitForLoop1144
[m] visitSwitch1144
[m] visitCatchStatement1144
[m] visitBinaryExpression1144
[m] visitTernaryExpression1144
[m] visitMethodCallExpression2255
[m] visitPropertyExpression2244
[m] handleExpressionContainingOperation2266
[c] org.gmetrics.metric.cyclomatic.CyclomaticComplexityMetric42.0126.0
[m] calculate2266
[m] calculate2266
[p] org/gmetrics/metric/fieldcount82.7196.3
[c] org.gmetrics.metric.fieldcount.FieldCountAstVisitor63.0105.0
[m] getNumberOfFields1133
[m] visitClass5577
[c] org.gmetrics.metric.fieldcount.FieldCountMetric22.099.0
[m] calculateForClass2299
[p] org/gmetrics/metric/linecount122.0386.3
[c] org.gmetrics.metric.linecount.ClassLineCountMetric22.099.0
[m] calculateForClass2299
[c] org.gmetrics.metric.linecount.ClassLineCountAstVisitor22.066.0
[m] visitClass2266
[c] org.gmetrics.metric.linecount.MethodLineCountAstVisitor52.5126.0
[m] visitMethod3366
[m] visitClosureExpression2266
[c] org.gmetrics.metric.linecount.MethodLineCountMetric31.5115.5
[m] calculate2266
[m] calculate1155
[p] org/gmetrics/metric/methodcount113.7217.0
[c] org.gmetrics.metric.methodcount.MethodCountAstVisitor94.5126.0
[m] getNumberOfMethods1133
[m] visitClass8899
[c] org.gmetrics.metric.methodcount.MethodCountMetric22.099.0
[m] calculateForClass2299
[p] org/gmetrics/metricregistry31.0165.3
[c] org.gmetrics.metricregistry.DefaultMetricRegistry31.0165.3
[m] getMetricClass1144
[m] getAllMetricNames1144
[m] buildMetricClassMap1188
[c] org.gmetrics.metricregistry.MetricRegistryN/AN/AN/AN/A
[c] org.gmetrics.metricregistry.MetricRegistryHolderN/AN/AN/AN/A
[p] org/gmetrics/metricset301.21435.7
[c] org.gmetrics.metricset.CompositeMetricSet31.0113.7
[m] addMetric1144
[m] addMetricSet1144
[m] getMetrics1133
[c] org.gmetrics.metricset.DefaultMetricSet11.033.0
[m] getMetrics1133
[c] org.gmetrics.metricset.GroovyDslMetricSet21.02110.5
[m] getMetrics1133
[m] <init>111818
[c] org.gmetrics.metricset.ListMetricSet21.0105.0
[m] getMetrics1133
[m] <init>1177
[c] org.gmetrics.metricset.MetricSetN/AN/AN/AN/A
[c] org.gmetrics.metricset.MetricSetBuilder21.073.5
[m] metricset1144
[m] getMetricSet1133
[c] org.gmetrics.metricset.TopLevelDelegate161.3705.8
[m] metricset1144
[m] metricset111010
[m] metric1155
[m] metric1166
[m] metric111010
[m] propertyMissing2255
[m] methodMissing331010
[m] description1144
[m] getMetricSet1133
[m] assertClassImplementsMetricInterface1144
[m] addMetric2266
[m] isNotWithinAnotherMetricDefinition1133
[c] org.gmetrics.metricset.MetricSetDelegate41.3217.0
[m] methodMissing221515
[m] findMetric1133
[m] <init>1133
[p] org/gmetrics/report1381.76587.9
[c] org.gmetrics.report.AbstractMetricCriteriaFilterN/AN/AN/AN/A
[c] org.gmetrics.report.AbstractReportWriter201.7766.3
[m] writeReport221414
[m] writeReportToStandardOut1144
[m] writeReportToFile2288
[m] initializeDefaultResourceBundle221212
[m] getResourceBundleString2299
[m] getResourceBundleStringOrNull2299
[m] initializeFormatters2277
[m] formatMetricResultValue2244
[m] getFormattedTimestamp1144
[m] isWriteToStandardOut2233
[m] initializeResourceBundle1111
[m] getTimestamp1111
[c] org.gmetrics.report.BasicHtmlReportWriter321.917710.4
[m] writeReport111717
[m] buildMetricResultColumns221111
[m] buildCSS1188
[m] buildHeaderSection1188
[m] buildBodySection111111
[m] buildReportTimestamp1166
[m] buildResultsTable221717
[m] getMetricResultColumnHeading1144
[m] setReportLevels1133
[m] includesReportLevel2233
[m] buildResultsTableRowRecursively10103333
[m] buildResultsRowsForChildren2299
[m] prefixForResultsNodeLevel1188
[m] buildMetricDescriptions222424
[m] getDescriptionForMetricName1144
[m] buildVersionFooter1188
[m] buildTitle2233
[c] org.gmetrics.report.FunctionsCriteriaFilter21.063.0
[m] setFunctions1133
[m] includesFunction1133
[c] org.gmetrics.report.LevelsCriteriaFilter21.063.0
[m] setLevels1133
[m] includesLevel1133
[c] org.gmetrics.report.MetricCriteriaFilterHelper61.5235.8
[m] includesName3377
[m] parseCriteria1177
[m] parseCriteriaForSingleMetric1166
[m] parseCommaSeparatedList1133
[c] org.gmetrics.report.MetricsCriteriaFilter31.563.0
[m] setMetrics1133
[m] includesMetric2233
[c] org.gmetrics.report.ReportWriterN/AN/AN/AN/A
[c] org.gmetrics.report.SeriesValue21.073.5
[m] toString1133
[m] <init>1144
[c] org.gmetrics.report.SingleSeriesCriteriaFilter331.91317.7
[m] buildSeriesData112020
[m] findMatchingValuesForChildren1166
[m] getResultsNodeFullName331414
[m] findMatchingValues441515
[m] sortValuesIfApplicable3399
[m] limitToGreaterThanIfApplicable2277
[m] limitToLessThanIfApplicable2277
[m] limitToMaxResultsIfApplicable3399
[m] assertMetricExists1144
[m] assertLevelExists1133
[m] assertFunctionExists1144
[m] assertValidSortValue2233
[m] assertValidMaxResultsValue331111
[m] assertValidGreaterThanValue1133
[m] assertValidLessThanValue1133
[m] assertValidNumberValue331010
[m] findMetric1133
[c] org.gmetrics.report.SingleSeriesHtmlReportWriter111.1969.6
[m] writeReport111818
[m] buildHeaderSection1188
[m] buildCSS1188
[m] buildBodySection221313
[m] buildReportTimestamp1166
[m] buildResultsTable111313
[m] getSeriesValueNameHeading1199
[m] buildSeriesValueRow1199
[m] getMetricResultColumnHeading1144
[m] buildVersionFooter1188
[c] org.gmetrics.report.XmlReportWriter271.81308.7
[m] writeReport111717
[m] buildReportElement1155
[m] buildProjectElement1199
[m] buildPackageElements1133
[m] buildElement4477
[m] buildPackageElement552020
[m] buildClassElement111111
[m] buildMethodElement111010
[m] buildMetricElements1177
[m] buildMetricElement661414
[m] isRoot1133
[m] buildMetricsElement111414
[m] getDescriptionForMetric1144
[m] isPackage1133
[m] cdata1133
[p] org/gmetrics/result491.51604.8
[c] org.gmetrics.result.ClassMetricResult21.073.5
[m] toString1133
[m] <init>1144
[c] org.gmetrics.result.FunctionNamesN/AN/AN/AN/A
[c] org.gmetrics.result.MapMetricResult31.0175.7
[m] getAt1144
[m] toString1144
[m] <init>1199
[c] org.gmetrics.result.MethodKey61.2234.6
[m] equals2244
[m] hashCode1144
[m] toString1144
[m] <init>1155
[m] <init>1166
[c] org.gmetrics.result.MetricResultN/AN/AN/AN/A
[c] org.gmetrics.result.MetricResultBuilder282.2614.7
[m] createAggregateMetricResult111313
[m] toString1133
[m] calculateFunctions111010
[m] calculateCount1133
[m] total3333
[m] calculateTotal2255
[m] minimum3333
[m] calculateMinimum2244
[m] maximum3333
[m] calculateMaximum2244
[m] average3333
[m] shouldCalculateFunction3333
[m] isFunctionSpecifiedOrImplied3344
[c] org.gmetrics.result.MutableMapMetricResult41.0194.8
[m] getAt1144
[m] putAt1133
[m] toString1144
[m] <init>1188
[c] org.gmetrics.result.NumberMetricResult31.0165.3
[m] getAt1133
[m] toString1133
[m] <init>111010
[c] org.gmetrics.result.SingleNumberMetricResult31.0175.7
[m] getAt1144
[m] toString1144
[m] <init>1199
[p] org/gmetrics/resultsnode271.21024.6
[c] org.gmetrics.resultsnode.ClassResultsNode91.3344.9
[m] containsClassResults1133
[m] getMetricResult1144
[m] addClassMetricResult221010
[m] toString1133
[m] addMethodMetricResult2266
[m] <init>1133
[m] <init>1155
[c] org.gmetrics.resultsnode.MethodResultsNode71.0243.4
[m] containsClassResults1133
[m] getMetricResult1144
[m] addMetricResult1144
[m] getChildren1133
[m] toString1133
[m] <init>1133
[m] <init>1144
[c] org.gmetrics.resultsnode.PackageResultsNode111.4445.5
[m] getChildren1133
[m] containsClassResults1133
[m] getMetricResult1144
[m] addChildIfNotEmpty2277
[m] addChild1155
[m] applyMetric331414
[m] toString1133
[m] <init>1155
[c] org.gmetrics.resultsnode.ResultsNodeN/AN/AN/AN/A
[p] org/gmetrics/source372.11116.2
[c] org.gmetrics.source.AbstractSourceCode162.7518.5
[m] getLines2266
[m] line3355
[m] getAst331616
[m] getLineNumberForCharacterIndex551616
[m] isValid1133
[m] normalizePath2255
[c] org.gmetrics.source.SourceCodeN/AN/AN/AN/A
[c] org.gmetrics.source.SourceCodeCriteria88.01717.0
[m] matches881717
[c] org.gmetrics.source.SourceFile71.2254.2
[m] getName1133
[m] getPath1133
[m] getText2266
[m] toString1133
[m] createSourceUnit1155
[m] <init>1155
[c] org.gmetrics.source.SourceString61.2183.6
[m] getText1133
[m] setPath2233
[m] toString1133
[m] createSourceUnit1133
[m] <init>1166
[p] org/gmetrics/util1052.42786.5
[c] org.gmetrics.util.AstUtil402.7946.3
[m] isEmptyMethod1133
[m] isClosureField2233
[m] isBlock1133
[m] isEmptyBlock4455
[m] getMethodArguments331010
[m] isMethodCall331010
[m] isMethodCall2244
[m] isMethodCall3399
[m] isMethodNamed1144
[m] getAnnotation2266
[m] getVariableExpressions991616
[m] isFinalVariable441414
[m] isFromGeneratedSourceCode3333
[m] respondsTo1133
[m] <init>1111
[c] org.gmetrics.util.Calculator42.084.0
[m] calculateAverage3377
[m] <init>1111
[c] org.gmetrics.util.ClassNameUtil142.3264.3
[m] parentPackageName3377
[m] isPackageName2244
[m] isClassName2244
[m] getNameOnly4477
[m] isCapitalized2233
[m] <init>1111
[c] org.gmetrics.util.GMetricsVersion11.033.0
[m] getVersion1133
[c] org.gmetrics.util.ImportUtil44.01111.0
[m] packageNameForImport441111
[c] org.gmetrics.util.PathUtil132.6336.6
[m] getName3377
[m] getParent3399
[m] normalize2233
[m] toPackageName441313
[m] <init>1111
[c] org.gmetrics.util.PropertyUtil63.02311.5
[m] setPropertyFromString552222
[m] <init>1111
[c] org.gmetrics.util.WildcardPattern133.34611.5
[m] matches4477
[m] containsWildcards1133
[m] convertStringWithWildcardsToRegex552424
[m] <init>331212
[p] org/gmetrics/util/io101.4344.9
[c] org.gmetrics.util.io.ClassPathResource41.3144.7
[m] getInputStream1133
[m] getInputStream2277
[m] <init>1144
[c] org.gmetrics.util.io.DefaultResourceFactory42.0126.0
[m] getResource3399
[m] isUrl1133
[c] org.gmetrics.util.io.ResourceN/AN/AN/AN/A
[c] org.gmetrics.util.io.ResourceFactoryN/AN/AN/AN/A
[c] org.gmetrics.util.io.UrlResource21.084.0
[m] getInputStream1144
[m] <init>1144

Metric Descriptions

#Metric NameDescription
1CyclomaticComplexityMeasures the (McCabe) Cyclomatic Complexity of source code. See the Wikipedia entry for Cyclomatic Complexity.
2MethodLineCountCounts the number of lines in each method.

GMetrics 0.7

GMetrics-0.7/src/site/resources/SampleGMetricsXmlReport.xml0000644000175000017500000027767412463237652023511 0ustar ebourgebourg src\main\groovyGMetrics-0.7/src/site/resources/SampleGMetricsSingleSeriesReport.html0000644000175000017500000001002512463237652025460 0ustar ebourgebourgMethods With Highest Line Count

Methods With Highest Line Count

Report timestamp: Jan 31, 2015 3:48:42 PM

MethodMethod Lines (total)
org.gmetrics.report.BasicHtmlReportWriter#buildResultsTableRowRecursively33
org.gmetrics.metric.crap.CrapMetric#calculate24
org.gmetrics.report.BasicHtmlReportWriter#buildMetricDescriptions24
org.gmetrics.util.WildcardPattern#convertStringWithWildcardsToRegex24
org.gmetrics.util.PropertyUtil#setPropertyFromString22
org.gmetrics.metric.coverage.AbstractCoberturaCoverageMetric#calculateForClass21
org.gmetrics.ant.AntFileSetSourceAnalyzer#processFile20
org.gmetrics.report.SingleSeriesCriteriaFilter#buildSeriesData20
org.gmetrics.report.XmlReportWriter#buildPackageElement20
org.gmetrics.GMetricsRunner#execute19
org.gmetrics.metricset.GroovyDslMetricSet#<init>18
org.gmetrics.report.SingleSeriesHtmlReportWriter#writeReport18
org.gmetrics.metric.abc.result.AggregateAbcMetricResult#calculateFunctions17
org.gmetrics.report.BasicHtmlReportWriter#writeReport17
org.gmetrics.report.BasicHtmlReportWriter#buildResultsTable17
org.gmetrics.report.XmlReportWriter#writeReport17
org.gmetrics.source.SourceCodeCriteria#matches17
org.gmetrics.metric.AbstractMethodMetric#calculateForClass16
org.gmetrics.metric.coverage.AbstractCoberturaCoverageMetric#calculateForPackage16
org.gmetrics.metric.coverage.AbstractCoberturaCoverageMetric#buildMethodResults16

GMetrics 0.7

GMetrics-0.7/src/site/resources/css/0000755000175000017500000000000012623566446017000 5ustar ebourgebourgGMetrics-0.7/src/site/resources/css/site.css0000644000175000017500000000006712006632014020435 0ustar ebourgebourgtt { font-size: 110%; font-weight: bolder; }GMetrics-0.7/src/site/site.xml0000644000175000017500000000644712107724364015667 0ustar ebourgebourg GMetrics / ${reports} GMetrics-0.7/src/site/apt/0000755000175000017500000000000012623566446014762 5ustar ebourgebourgGMetrics-0.7/src/site/apt/gmetrics-FieldCountMetric.apt0000644000175000017500000000455712107724365022507 0ustar ebourgebourg -------------------------------------------------- GMetrics - FieldCount Metric -------------------------------------------------- FieldCount Metric ~~~~~~~~~~~~~~~~~~~~~~ Metric for counting the number of fields within each class. Note that <<>> fields (i.e., ) are included as well. Implemented by the <<>> class. * Metric Properties ~~~~~~~~~~~~~~~~~~~ The following properties can be configured for this metric within a . See {{{./gmetrics-creating-metricset.html}Creating a MetricSet}} for information on the syntax of setting a metric property. *-----------------+--------------------------------------------------------------------+------------------------+ | <> | <> | <> | *-----------------+--------------------------------------------------------------------+------------------------+ | enabled | This <<>> property controls whether the metric is | <<>> | | | . If set to <<>>, then the metric is not included | | | | as part of the results or the output reports. | | *-----------------+--------------------------------------------------------------------+------------------------+ | functions | This <<>>> property contains the names of the functions| <<<["total","average"]>>>| | | to be calculated at the , and levels | | | | and (potentially) included within the report(s). Valid values are: | | | | - "total" | | | | - "average" | | | | - "minimum" | | | | - "maximum" | | *-----------------+--------------------------------------------------------------------+------------------------+ GMetrics-0.7/src/site/apt/gmetrics-ant-task.apt0000644000175000017500000001055212107724365021021 0ustar ebourgebourg -------------------------------------------------- GMetrics Ant Task -------------------------------------------------- GMetrics - Ant Task ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Description ~~~~~~~~~~~~~ The <> Ant Task is implemented by the <<>> class. * Attributes ~~~~~~~~~~~~ *------------------------+----------------------------------------------------------------+------------------------+ | <> | <> | <> | *------------------------+----------------------------------------------------------------+------------------------+ | metricSetFile | The paths to a Groovy DSL file. By default, | NO | | | the path specified is relative to the classpath, but it may be | | | | optionally prefixed by any of the valid <<>> | | | | prefixes, such as "file:" (to load from a relative or absolute | | | | filesystem path), or "http:". | | | | | | | | If not specified, it uses the set of <<>>s defined by | | | | the {{{./gmetrics-DefaultMetricSet.html}Default MetricSet}}. | | *------------------------+----------------------------------------------------------------+------------------------+ * Report Nested Element ~~~~~~~~~~~~~~~~~~~~~~~ The <<\>> nested element defines the type and options for a report. It includes a <> attribute and contains optional nested <<\>> elements. NOTE: Currently, <<>> is the only report provided with <>. *---------------------+----------------------------------------------------------------+------------------------+ | <> | <> | <> | *---------------------+----------------------------------------------------------------+------------------------+ | type | The fully-qualified class name for the report class. This class| Yes | | | must implement the <<>> | | | | interface. | | *---------------------+----------------------------------------------------------------+------------------------+ ** Option Nested Element ~~~~~~~~~~~~~~~~~~~~~~~~ The <<\>> element is a child of the <<\>> element and defines a report-specific option for a report. See the {{{Example}Example}} below. * Fileset Nested Element ~~~~~~~~~~~~~~~~~~~~~~~ At least one <> nested element is required, and is used to specify the source files that <> should analyze. This is the standard Ant , and is quite powerful and flexible. See the {{{http://ant.apache.org/manual/index.html}Apache Ant Manual}} for more information on . * {Example} ~~~~~~~~~~~ Here is an example Ant XML build file. +---------------------------------------------------------------------------------------- +---------------------------------------------------------------------------------------- Things to note: * The specifies that all ".groovy" files are analyzed. * Remember that you need the <> jar (and possibly a "log4j.properties" file) on the classpath. GMetrics-0.7/src/site/apt/gmetrics-roadmap.apt0000644000175000017500000000062512107724365020722 0ustar ebourgebourg -------------------------------------------------- GMetrics Road Map -------------------------------------------------- GMetrics - Road Map ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Suggestions or preferences? Create a {{{http://sourceforge.net/tracker/?atid=1220658&group_id=288180&func=browse}Feature Request}}. * On the Horizon ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * More metrics GMetrics-0.7/src/site/apt/gmetrics-AfferentCouplingMetric.apt0000644000175000017500000000754712107724365023710 0ustar ebourgebourg -------------------------------------------------- GMetrics - Afferent Coupling Metric -------------------------------------------------- Afferent Coupling Metric ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Calculates the <> for a package. This is a count of the number of other packages that depend on the classes within this package. It is an indicator of the package's responsibility ([1]). This is a <>-level metric. Implemented by the <<>> class. * Metric Properties ~~~~~~~~~~~~~~~~~~~ The following properties can be configured for this metric within a . See {{{./gmetrics-creating-metricset.html}Creating a MetricSet}} for information on the syntax of setting a metric property. *--------------------+--------------------------------------------------------------------+------------------------+ | <> | <> | <> | *--------------------+--------------------------------------------------------------------+------------------------+ | enabled | This <<>> property controls whether the metric is | <<>> | | | . If set to <<>>, then the metric is not included | | | | as part of the results or the output reports. | | *--------------------+--------------------------------------------------------------------+------------------------+ | functions | This <<>>> property contains the names of the functions| <<<["value","average"]>>>| | | to be calculated at the level. | | | | and (potentially) included within the report(s). Valid values are:\ | | | | \ - "value" - the value for the current package\ | | | | \ - "total" - the total value for the current package and its descendant packages\ | | | | \ - "average" - the average value for the current package and its descendant packages\ | | | | \ - "referencedFromPackages" - the list of packages that reference classes within the current package | | *--------------------+--------------------------------------------------------------------+------------------------+ | ignorePackageNames | The names of packages to ignore when calculating afferent coupling.| | | | This pattern string may contain wildcard characters ('*' or '?'); | | | | it may also contain more than one pattern, separated by commas. | | *--------------------+--------------------------------------------------------------------+------------------------+ * References ~~~~~~~~~~~~ * <<[1]>> The {{{http://en.wikipedia.org/wiki/Software_package_metrics}The page for }}. * <<[2]>> {{{http://www.ibm.com/developerworks/java/library/j-cq04256/}"CodeQuality for Software Architects: Use coupling metrics to support your system architecture"}} - Andrew Glover, part of the "In pursuit of code quality" series in . This article includes a discussion of <>, among other metrics. * <<[3]>> {{{http://www.spinellis.gr/codequality/}Code Quality: The Open Source Perspective}} - Diomidis Spinellis. Addison Wesley, 2006. * <<[4]>> {{{http://www.amazon.com/exec/obidos/ASIN/0135974445/objectmentorinc}Agile Software Development, Principles, Patterns, and Practices}} - Robert C. Martin. Prentice Hall, 2002. GMetrics-0.7/src/site/apt/index.apt0000644000175000017500000000425512107724365016576 0ustar ebourgebourg -------------------------------------------------- GMetrics - Home -------------------------------------------------- GMetrics ~~~~~~~~ The <> project provides calculation and reporting of size and complexity metrics for Groovy source code. <> scans Groovy source code, applying a set of metrics, and generates an HTML or XML report of the results. You can run <> using the supplied {{{./gmetrics-ant-task.html}Ant Task}}. Total and average values for the following metrics are provided: * {{{./gmetrics-CyclomaticComplexityMetric.html}Cyclomatic Complexity}}. * {{{./gmetrics-ABCMetric.html}ABC}} Size/Complexity. Also see the {{{http://c2.com/cgi/wiki?AbcMetric}C2 Wiki page}}. * {{{./gmetrics-CoberturaLineCoverageMetric.html}Cobertura line coverage}} and {{{./gmetrics-CoberturaBranchCoverageMetric.html}Cobertura branch coverage}} * {{{./gmetrics-CrapMetric.html}CRAP}} - (Change Risk Anti-Patterns) score * {{{./gmetrics-AfferentCouplingMetric.html}Afferent Coupling}} * {{{./gmetrics-EfferentCouplingMetric.html}Efferent Coupling}} * {{{./gmetrics-MethodLineCountMetric.html}Lines per method}} * {{{./gmetrics-ClassLineCountMetric.html}Lines per class}} * {{{./gmetrics-ClassCountMetric.html}Number of classes per package}} * {{{./gmetrics-FieldCountMetric.html}Number of field per class}} [] See the site navigation menu for a list of the metrics and reports provided out of the box by <>. Take a look at a {{{./SampleGMetricsReport.html}Sample GMetrics Report}}. * Requirements ~~~~~~~~~~~~~~ The <> project requires: * Groovy version 1.7 or later * Java 1.5 or later * The {{{http://logging.apache.org/log4j/index.html}Log4J}} jar, version 1.2.13 or later, accessible on the classpath. * Getting GMetrics from the Maven2 Central Repository ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ For projects built using {{{http://maven.apache.org/}Maven}}, <> is now available from the <>. * <> = <<>> * <> = <<>> GMetrics-0.7/src/site/apt/gmetrics-DefaultMetricSet.apt0000644000175000017500000000155512107724365022506 0ustar ebourgebourg -------------------------------------------------- GMetrics - DefaultMetricSet -------------------------------------------------- The Default MetricSet ~~~~~~~~~~~~~~~~~~~~~ If no is configured for the {{{./gmetrics-ant-task.html}GMetrics Ant Task}} then it uses the set of defined by the <<>> class. That includes the following : * {{{./gmetrics-CyclomaticComplexityMetric.html}Cyclomatic Complexity}} - implemented by <<>> * {{{./gmetrics-MethodLineCountMetric.html}Method Line Count}} - implemented by <<>> * {{{./gmetrics-ClassLineCountMetric.html}Class Line Count}} - implemented by <<>> GMetrics-0.7/src/site/apt/gmetrics-AbcMetric.apt0000644000175000017500000001327212107724365021132 0ustar ebourgebourg -------------------------------------------------- GMetrics - ABC Metric -------------------------------------------------- ABC Metric ~~~~~~~~~~ Calculates the <> Metric for a class or method. <> is a metric of size/complexity that counts the number of <> (A), <> (B) and <> (C) and assigns a single numerical score calculated as: ------------------------------------------------------------------------------- |ABC| = sqrt((A*A)+(B*B)+(C*C)) ------------------------------------------------------------------------------- Implemented by the <<>> class. The <> Metric calculation rules for Groovy: * Add one to the assignment count for each occurrence of an assignment operator, excluding constant declarations: <<< = *= /= %= += \<\<= \>\>= &= |= ^= \>\>\>= >>> * Add one to the assignment count for each occurrence of an increment or decrement operator (prefix or postfix): <<< ++ -- >>> * Add one to the branch count for each function call or class method call. * Add one to the branch count for each occurrence of the new operator. * Add one to the condition count for each use of a conditional operator: <<< == != \<= \>= \< \> \<=\> =~ ==~ >>> * Add one to the condition count for each use of the following keywords: <<>> * Add one to the condition count for each unary conditional expression. These are cases where a single variable/field/value is treated as a boolean value. Examples include <<>> and <<>>. * Additional notes ~~~~~~~~~~~~~~~~~~ * A property access is treated like a method call (and thus increments the branch count). * If a class field is initialized to a Closure (ClosureExpression), then that Closure is analyzed just like a method. * Guidelines for Interpreting ABC Metric Values ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ A frequently-referenced {{{http://jakescruggs.blogspot.com/2008/08/whats-good-flog-score.html}blog post}} by Jake Scruggs ([4]) offers the following guidelines for interpreting an <> score. Note that these values refer to the score (magnitude) calculated for a single method: * 0-10 = * 11-20 = * 21-40 = * 41-60 = * 61-100 = * 100-200 = * 200 + = * Metric Properties ~~~~~~~~~~~~~~~~~~~ The following properties can be configured for this metric within a . See {{{./gmetrics-creating-metricset.html}Creating a MetricSet}} for information on the syntax of setting a metric property. *-----------------+--------------------------------------------------------------------+------------------------+ | <> | <> | <> | *-----------------+--------------------------------------------------------------------+------------------------+ | enabled | This <<>> property controls whether the metric is | <<>> | | | . If set to <<>>, then the metric is not included | | | | as part of the results or the output reports. | | *-----------------+--------------------------------------------------------------------+------------------------+ | functions | This <<>>> property contains the names of the functions| <<<["total","average"]>>>| | | to be calculated at the , and levels | | | | and (potentially) included within the report(s). Valid values are: | | | | - "total" | | | | - "average" | | | | - "minimum" | | | | - "maximum" | | *-----------------+--------------------------------------------------------------------+------------------------+ | includeClosureFields | This <<>> property controls whether metric values are| <<>> | | | calculated for and treated as . A | | | | is a field that is initialized to a , | | | | e.g., <<>>. | | *-----------------+--------------------------------------------------------------------+------------------------+ * References ~~~~~~~~~~~~ * <<[1]>> The {{{http://www.softwarerenovation.com/ABCMetric.pdf}ABC Metric specification}}. * <<[2]>> The {{{http://c2.com/cgi/wiki?AbcMetric}The C2 Wiki page}} for the ABC Metric. * <<[3]>> {{{http://ruby.sadi.st/Flog.html}Flog}} is the popular Ruby tool that uses ABC. * <<[4]>> {{{http://jakescruggs.blogspot.com/2008/08/whats-good-flog-score.html}This blog post}} describes some guidelines for interpreting the ABC score. The post refers to the <> tool, but the <> score is calculated similarly (though adapted somewhat to account for language specifics) and the guidelines should be transferable. GMetrics-0.7/src/site/apt/gmetrics-SingleSeriesHtmlReportWriter.apt0000644000175000017500000001407112107724365025111 0ustar ebourgebourg -------------------------------------------------- GMetrics SingleSeriesHtmlReportWriter -------------------------------------------------- SingleSeriesHtmlReportWriter ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * Description ~~~~~~~~~~~~~ The <<>> class produces an HTML report of metric results based on a single metric, single level and single function to provide a single series of data. The , and properties are required (must be non-null and non-empty). These three properties uniquely identify a single series of metric values. See a {{{./SampleGMetricsSingleSeriesReport.html}Sample Report}}. * Option Nested Elements ~~~~~~~~~~~~~~~~~~~~~~~~ The <