cg@36: "{ Package: 'stx:goodies/builder/codechecker' }" cg@36: cg@36: Object subclass:#SmalllintReportGenerator cg@36: instanceVariableNames:'rules environment' cg@36: classVariableNames:'' cg@36: poolDictionaries:'' cg@36: category:'Refactory-Lint' cg@36: ! cg@36: cg@36: !SmalllintReportGenerator class methodsFor:'documentation'! cg@36: cg@36: documentation cg@36: " cg@36: a standalone smallint runner. cg@36: Generates an xml report compatible to pmd, to be processed by hudson. cg@36: cg@36: [author:] cg@36: Claus Gittinger cg@36: " cg@36: ! cg@36: cg@36: examples cg@36: " cg@37: [exBegin] cg@36: |checker| cg@36: cg@36: checker := self new. cg@36: checker addPackage:'exept:workflow'. cg@36: checker performChecks. cg@36: checker generateReportAs:'checkstyle.xml' cg@37: [exEnd] cg@37: cg@37: [exBegin] cg@37: |checker| cg@37: cg@37: checker := self new. cg@37: checker addClasses:(Workflow::Datatype withAllSubclasses). cg@37: checker performChecks. cg@37: checker generateReportAs:'checkstyle.xml' cg@37: [exEnd] cg@36: " cg@36: ! ! cg@36: cg@36: !SmalllintReportGenerator class methodsFor:'instance creation'! cg@36: cg@36: new cg@36: "return an initialized instance" cg@36: cg@36: ^ self basicNew initialize. cg@36: ! ! cg@36: cg@36: !SmalllintReportGenerator methodsFor:'checking'! cg@36: cg@36: performChecks cg@36: rules withIndexDo:[:rule :index| cg@36: Stdout showCR:('Checking: ', rule name). cg@36: (SmalllintChecker runRule: rule onEnvironment: environment) cg@36: ]. cg@36: cg@36: "Created: / 07-08-2011 / 01:10:00 / cg" cg@36: ! ! cg@36: cg@36: !SmalllintReportGenerator methodsFor:'initialization'! cg@36: cg@36: initialize cg@36: self setupRules. cg@36: self setupEnvironment cg@36: cg@36: "Modified: / 07-08-2011 / 01:14:51 / cg" cg@36: ! ! cg@36: cg@36: !SmalllintReportGenerator methodsFor:'reporting'! cg@36: cg@36: generateReportAs:aFilename cg@36: aFilename asFilename writingFileDo:[:s | self generateReportOn:s]. cg@36: cg@36: "Created: / 07-08-2011 / 01:17:46 / cg" cg@36: ! cg@36: cg@36: generateReportOn:aStream cg@37: |perClass| cg@37: cg@37: perClass := IdentityDictionary new. cg@37: cg@36: rules do:[:eachRule | cg@36: eachRule problemCount > 0 ifTrue:[ cg@36: eachRule failedMethods do:[:method | cg@37: |class perMethodAndClassMethod perMethod| cg@36: cg@36: class := method mclass. cg@37: perMethodAndClassMethod := perClass at:class theNonMetaclass ifAbsentPut:[{ IdentityDictionary new. IdentityDictionary new }]. cg@37: perMethod := perMethodAndClassMethod at:(class isMeta ifTrue:[2] ifFalse:[1]). cg@37: rules := perMethod at:method ifAbsentPut:[IdentitySet new]. cg@37: rules add:eachRule. cg@37: ] cg@37: ] cg@37: ]. cg@37: cg@37: aStream nextPutLine: ''. cg@37: aStream nextPutLine: ''. cg@37: cg@37: perClass keysAndValuesDo:[:class :perMethodAndClassMethod | cg@37: |fullSource sourceStream classFileName| cg@37: cg@37: fullSource := class source. cg@37: sourceStream := class localSourceStreamFor:(class classFilename). cg@37: sourceStream notNil ifTrue:[ cg@37: classFileName := sourceStream pathName. cg@37: sourceStream close. cg@37: ] ifFalse:[ cg@37: classFileName := class packageDirectory construct:(class classFilename). cg@37: ]. cg@37: cg@37: aStream nextPutLine:(' ' bindWith:classFileName asFilename pathName). cg@37: cg@37: { (perMethodAndClassMethod at:2). cg@37: (perMethodAndClassMethod at:1) } do:[:perMethod | cg@37: (perMethod keys copyAsOrderedCollection sort:[:a :b | a selector < b selector]) do:[:eachMethod | cg@37: |rulesPerMethod charPosOfMethod lineNumberOfMethod| cg@37: cg@37: rulesPerMethod := perMethod at:eachMethod. cg@37: cg@37: charPosOfMethod := eachMethod sourcePosition ? 1. cg@37: "/ q&d hack - editor knows how to compute line number - should go cg@37: "/ somewhere else... cg@37: lineNumberOfMethod := (ListView basicNew setList:fullSource) lineOfCharacterPosition:charPosOfMethod. cg@37: rulesPerMethod do:[:eachRule | cg@37: |ruleName rationale| cg@37: cg@37: ruleName := eachRule name. cg@37: rationale := eachRule rationale. cg@37: cg@37: aStream nextPutLine:(' ' cg@37: bindWith:lineNumberOfMethod with:ruleName). cg@37: aStream nextPutLine:('%1' bindWith:rationale). cg@37: aStream nextPutLine: ' '. cg@37: cg@36: ]. cg@36: ]. cg@37: ]. cg@37: aStream nextPutLine: ' '. cg@36: ]. cg@36: aStream nextPutLine: ''. cg@36: cg@36: "Created: / 07-08-2011 / 01:17:00 / cg" cg@36: ! ! cg@36: cg@36: !SmalllintReportGenerator methodsFor:'setup'! cg@36: cg@36: addClass:aClass cg@36: environment addClass: aClass. cg@36: cg@36: "Created: / 07-08-2011 / 01:11:33 / cg" cg@36: ! cg@36: cg@37: addClasses:aCollectionOfClasses cg@37: aCollectionOfClasses do:[:eachClass | self addClass: eachClass ]. cg@37: cg@37: "Created: / 07-08-2011 / 11:51:52 / cg" cg@37: ! cg@37: cg@36: addPackage:aPackage cg@36: Smalltalk loadPackage:aPackage. cg@36: Smalltalk allClassesInPackage:aPackage do:[:cls | self addClass:cls] cg@36: cg@36: "Created: / 07-08-2011 / 01:12:31 / cg" cg@36: ! cg@36: cg@36: setupEnvironment cg@36: environment := ClassEnvironment new. cg@36: cg@36: "Created: / 07-08-2011 / 01:10:56 / cg" cg@36: ! cg@36: cg@36: setupRules cg@37: self setupRules:(RBCompositeLintRule allRules). cg@37: cg@37: "Created: / 07-08-2011 / 01:08:56 / cg" cg@37: ! cg@36: cg@37: setupRules:rulesArg cg@37: | checks| cg@37: cg@37: checks := rulesArg rules detect:[ :each | each name = 'Lint checks' ]. cg@36: checks rules: (checks rules reject: [ :each | each name = 'Squeak bugs' ]). cg@36: cg@37: rules := rulesArg flattened. cg@36: cg@37: "Created: / 07-08-2011 / 11:48:30 / cg" cg@36: ! ! cg@36: cg@36: !SmalllintReportGenerator class methodsFor:'documentation'! cg@36: cg@36: version cg@36: ^ '$Header$' cg@36: ! cg@36: cg@36: version_CVS cg@36: ^ '$Header$' cg@36: ! !