Codebase list findbugs / a74e782
Merge tag 'upstream/3.0.1' Upstream version 3.0.1 tony mancill 8 years ago
1146 changed file(s) with 42677 addition(s) and 37834 deletion(s). Raw diff Collapse all Expand all
66 <classpathentry kind="src" path="src/xsl"/>
77 <classpathentry kind="src" path="src/junit"/>
88 <classpathentry kind="src" path="src/gui"/>
9 <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5">
9 <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7">
1010 <accessrules>
1111 <accessrule kind="accessible" pattern="com/sun/management/*"/>
1212 <accessrule kind="nonaccessible" pattern="com/sun/**"/>
1515 </accessrules>
1616 </classpathentry>
1717 <classpathentry exported="true" kind="lib" path="lib/AppleJavaExtensions.jar"/>
18 <classpathentry exported="true" kind="lib" path="lib/bcel.jar">
19 <attributes>
20 <attribute name="javadoc_location" value="http://jakarta.apache.org/bcel/apidocs/"/>
21 </attributes>
22 </classpathentry>
2318 <classpathentry exported="true" kind="lib" path="lib/dom4j-1.6.1.jar">
2419 <attributes>
2520 <attribute name="javadoc_location" value="http://dom4j.org/apidocs/"/>
3227 <classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
3328 <classpathentry exported="true" kind="lib" path="lib/jsr305.jar" sourcepath="lib/jsr305.jar"/>
3429 <classpathentry exported="true" kind="lib" path="lib/jaxen-1.1.6.jar"/>
35 <classpathentry exported="true" kind="lib" path="lib/jFormatString.jar"/>
30 <classpathentry exported="true" kind="lib" path="lib/jFormatString.jar"/>
3631 <classpathentry exported="true" kind="lib" path="lib/jdepend-2.9.jar"/>
37 <classpathentry exported="true" kind="lib" path="lib/asm-3.3.jar" sourcepath="lib/asm-src-3.3.zip"/>
38 <classpathentry exported="true" kind="lib" path="lib/asm-commons-3.3.jar" sourcepath="lib/asm-src-3.3.zip"/>
39 <classpathentry exported="true" kind="lib" path="lib/asm-tree-3.3.jar" sourcepath="lib/asm-src-3.3.zip"/>
40 <classpathentry exported="true" kind="lib" path="lib/ant.jar"/>
41 <classpathentry exported="true" kind="lib" path="lib/asm-analysis-3.3.jar" sourcepath="lib/asm-src-3.3.zip"/>
42 <classpathentry exported="true" kind="lib" path="lib/asm-util-3.3.jar" sourcepath="lib/asm-src-3.3.zip"/>
43 <classpathentry exported="true" kind="lib" path="lib/asm-xml-3.3.jar" sourcepath="lib/asm-src-3.3.zip"/>
32 <classpathentry exported="true" kind="lib" path="lib/ant.jar" sourcepath="lib/apache-ant-1.8.3-src.zip"/>
33 <classpathentry exported="true" kind="lib" path="lib/asm-debug-all-5.0.2.jar" sourcepath="/asm3"/>
34 <classpathentry exported="true" kind="lib" path="lib/bcel-6.0-SNAPSHOT.jar">
35 <attributes>
36 <attribute name="javadoc_location" value="http://commons.apache.org/proper/commons-bcel/apidocs/"/>
37 </attributes>
38 </classpathentry>
4439 <classpathentry kind="output" path="classesEclipse"/>
4540 </classpath>
0 #Sun Sep 19 07:41:57 CEST 2010
1 activeContentFilterList=*.makefile,makefile,*.Makefile,Makefile,Makefile.*,*.mk
0 activeContentFilterList=*.makefile,makefile,*.Makefile,Makefile,Makefile.*,*.mk,.classpath,*.properties,.project
21 addNewLine=true
32 convertActionOnSaave=AnyEdit.CnvrtTabToSpaces
43 eclipse.preferences.version=1
4 fixLineDelimiters=true
5 ignoreBlankLinesWhenTrimming=false
56 inActiveContentFilterList=
67 javaTabWidthForJava=true
78 org.eclipse.jdt.ui.editor.tab.width=4
1112 replaceAllTabs=false
1213 saveAndAddLine=true
1314 saveAndConvert=true
15 saveAndFixLineDelimiters=true
1416 saveAndTrim=true
1517 useModulo4Tabs=false
00 #FindBugs User Preferences
1 #Thu Nov 07 08:16:06 EST 2013
1 #Wed Jun 18 10:04:41 CEST 2014
22 cloud_id=edu.umd.cs.findbugs.cloud.appengine.findbugs-cloud
3 detectorAbnormalFinallyBlockReturn=AbnormalFinallyBlockReturn|true
4 detectorAbstractClassEmptyMethods=AbstractClassEmptyMethods|true
5 detectorAbstractOverriddenMethod=AbstractOverriddenMethod|true
63 detectorAppendingToAnObjectOutputStream=AppendingToAnObjectOutputStream|true
7 detectorArrayBasedCollections=ArrayBasedCollections|true
8 detectorArrayWrappedCallByReference=ArrayWrappedCallByReference|true
94 detectorAtomicityProblem=AtomicityProblem|true
10 detectorBCPMethodReturnCheck=BCPMethodReturnCheck|false
115 detectorBadAppletConstructor=BadAppletConstructor|false
126 detectorBadResultSetAccess=BadResultSetAccess|true
137 detectorBadSyntaxForRegularExpression=BadSyntaxForRegularExpression|true
148 detectorBadUseOfReturnValue=BadUseOfReturnValue|true
159 detectorBadlyOverriddenAdapter=BadlyOverriddenAdapter|true
16 detectorBogusExceptionDeclaration=BogusExceptionDeclaration|true
1710 detectorBooleanReturnNull=BooleanReturnNull|true
18 detectorBuildInterproceduralCallGraph=BuildInterproceduralCallGraph|false
19 detectorBuildObligationPolicyDatabase=BuildObligationPolicyDatabase|true
2011 detectorCallToUnsupportedMethod=CallToUnsupportedMethod|true
21 detectorCalledMethods=CalledMethods|true
22 detectorCheckCalls=CheckCalls|false
2312 detectorCheckExpectedWarnings=CheckExpectedWarnings|false
2413 detectorCheckImmutableAnnotation=CheckImmutableAnnotation|true
2514 detectorCheckRelaxingNullnessAnnotation=CheckRelaxingNullnessAnnotation|true
2716 detectorCloneIdiom=CloneIdiom|true
2817 detectorComparatorIdiom=ComparatorIdiom|true
2918 detectorConfusedInheritance=ConfusedInheritance|true
30 detectorConfusingAutoboxedOverloading=ConfusingAutoboxedOverloading|true
31 detectorConfusingFunctionSemantics=ConfusingFunctionSemantics|true
3219 detectorConfusionBetweenInheritedAndOuterMethod=ConfusionBetweenInheritedAndOuterMethod|true
33 detectorConstantListIndex=ConstantListIndex|true
34 detectorCopiedOverriddenMethod=CopiedOverriddenMethod|true
35 detectorCrossSiteScripting=CrossSiteScripting|false
36 detectorCustomBuiltXML=CustomBuiltXML|true
37 detectorCyclomaticComplexity=CyclomaticComplexity|true
38 detectorDateComparison=DateComparison|true
39 detectorDeclaredRuntimeException=DeclaredRuntimeException|true
20 detectorCrossSiteScripting=CrossSiteScripting|true
4021 detectorDefaultEncodingDetector=DefaultEncodingDetector|true
41 detectorDeletingWhileIterating=DeletingWhileIterating|true
42 detectorDeprecatedTypesafeEnumPattern=DeprecatedTypesafeEnumPattern|true
4322 detectorDoInsideDoPrivileged=DoInsideDoPrivileged|true
4423 detectorDontCatchIllegalMonitorStateException=DontCatchIllegalMonitorStateException|true
4524 detectorDontIgnoreResultOfPutIfAbsent=DontIgnoreResultOfPutIfAbsent|true
4625 detectorDontUseEnum=DontUseEnum|true
4726 detectorDroppedException=DroppedException|true
48 detectorDubiousListCollection=DubiousListCollection|true
49 detectorDubiousSetOfCollections=DubiousSetOfCollections|true
5027 detectorDumbMethodInvocations=DumbMethodInvocations|true
5128 detectorDumbMethods=DumbMethods|true
5229 detectorDuplicateBranches=DuplicateBranches|true
5330 detectorEmptyZipFileEntry=EmptyZipFileEntry|true
54 detectorEqStringTest=EqStringTest|false
5531 detectorEqualsOperandShouldHaveClassCompatibleWithThis=EqualsOperandShouldHaveClassCompatibleWithThis|true
56 detectorExceptionSoftening=ExceptionSoftening|true
5732 detectorExplicitSerialization=ExplicitSerialization|true
58 detectorFieldCouldBeLocal=FieldCouldBeLocal|true
59 detectorFieldItemSummary=FieldItemSummary|true
60 detectorFinalParameters=FinalParameters|true
6133 detectorFinalizerNullsFields=FinalizerNullsFields|true
62 detectorFindBadCast=FindBadCast|false
6334 detectorFindBadCast2=FindBadCast2|true
64 detectorFindBadEqualsImplementation=FindBadEqualsImplementation|false
6535 detectorFindBadForLoop=FindBadForLoop|true
66 detectorFindBugsSummaryStats=FindBugsSummaryStats|true
6736 detectorFindCircularDependencies=FindCircularDependencies|false
6837 detectorFindDeadLocalStores=FindDeadLocalStores|true
6938 detectorFindDoubleCheck=FindDoubleCheck|true
7140 detectorFindFieldSelfAssignment=FindFieldSelfAssignment|true
7241 detectorFindFinalizeInvocations=FindFinalizeInvocations|true
7342 detectorFindFloatEquality=FindFloatEquality|true
74 detectorFindFloatMath=FindFloatMath|false
7543 detectorFindHEmismatch=FindHEmismatch|true
7644 detectorFindInconsistentSync2=FindInconsistentSync2|true
7745 detectorFindJSR166LockMonitorenter=FindJSR166LockMonitorenter|true
7947 detectorFindMaskedFields=FindMaskedFields|true
8048 detectorFindMismatchedWaitOrNotify=FindMismatchedWaitOrNotify|true
8149 detectorFindNakedNotify=FindNakedNotify|true
82 detectorFindNonSerializableStoreIntoSession=FindNonSerializableStoreIntoSession|true
83 detectorFindNonSerializableValuePassedToWriteObject=FindNonSerializableValuePassedToWriteObject|true
8450 detectorFindNonShortCircuit=FindNonShortCircuit|true
8551 detectorFindNullDeref=FindNullDeref|true
8652 detectorFindNullDerefsInvolvingNonShortCircuitEvaluation=FindNullDerefsInvolvingNonShortCircuitEvaluation|true
8753 detectorFindOpenStream=FindOpenStream|true
8854 detectorFindPuzzlers=FindPuzzlers|true
8955 detectorFindRefComparison=FindRefComparison|true
90 detectorFindReturnRef=FindReturnRef|false
56 detectorFindReturnRef=FindReturnRef|true
9157 detectorFindRunInvocations=FindRunInvocations|true
9258 detectorFindSelfComparison=FindSelfComparison|true
9359 detectorFindSelfComparison2=FindSelfComparison2|true
9460 detectorFindSleepWithLockHeld=FindSleepWithLockHeld|true
9561 detectorFindSpinLoop=FindSpinLoop|true
96 detectorFindSqlInjection=FindSqlInjection|false
62 detectorFindSqlInjection=FindSqlInjection|true
9763 detectorFindTwoLockWait=FindTwoLockWait|true
9864 detectorFindUncalledPrivateMethods=FindUncalledPrivateMethods|true
9965 detectorFindUnconditionalWait=FindUnconditionalWait|true
10470 detectorFindUnsyncGet=FindUnsyncGet|true
10571 detectorFindUseOfNonSerializableValue=FindUseOfNonSerializableValue|true
10672 detectorFindUselessControlFlow=FindUselessControlFlow|true
107 detectorFloatingPointLoops=FloatingPointLoops|true
108 detectorFormatStringChecker=FormatStringChecker|false
73 detectorFormatStringChecker=FormatStringChecker|true
10974 detectorHugeSharedStringConstants=HugeSharedStringConstants|true
11075 detectorIDivResultCastToDouble=IDivResultCastToDouble|true
111 detectorImproperPropertiesUse=ImproperPropertiesUse|true
112 detectorInappropriateToStringUse=InappropriateToStringUse|true
11376 detectorIncompatMask=IncompatMask|true
11477 detectorInconsistentAnnotations=InconsistentAnnotations|true
115 detectorInconsistentKeyNameCasing=InconsistentKeyNameCasing|true
116 detectorIncorrectInternalClassUse=IncorrectInternalClassUse|true
11778 detectorInefficientMemberAccess=InefficientMemberAccess|false
118 detectorInefficientStringBuffering=InefficientStringBuffering|true
11979 detectorInefficientToArray=InefficientToArray|true
12080 detectorInfiniteLoop=InfiniteLoop|true
12181 detectorInfiniteRecursiveLoop=InfiniteRecursiveLoop|true
122 detectorInfiniteRecursiveLoop2=InfiniteRecursiveLoop2|false
123 detectorInheritanceTypeChecking=InheritanceTypeChecking|true
12482 detectorInheritanceUnsafeGetResource=InheritanceUnsafeGetResource|true
12583 detectorInitializationChain=InitializationChain|true
12684 detectorInitializeNonnullFieldsInConstructor=InitializeNonnullFieldsInConstructor|true
12886 detectorIntCast2LongAsInstant=IntCast2LongAsInstant|true
12987 detectorInvalidJUnitTest=InvalidJUnitTest|true
13088 detectorIteratorIdioms=IteratorIdioms|true
131 detectorJDBCVendorReliance=JDBCVendorReliance|true
132 detectorJUnitAssertionOddities=JUnitAssertionOddities|true
13389 detectorLazyInit=LazyInit|true
134 detectorListIndexedIterating=ListIndexedIterating|true
135 detectorLiteralStringComparison=LiteralStringComparison|true
13690 detectorLoadOfKnownNullValue=LoadOfKnownNullValue|true
137 detectorLocalSynchronizedCollection=LocalSynchronizedCollection|true
138 detectorLockedFields=LockedFields|false
139 detectorLoggerOddities=LoggerOddities|true
140 detectorLostExceptionStackTrace=LostExceptionStackTrace|true
14191 detectorLostLoggerDueToWeakReference=LostLoggerDueToWeakReference|true
142 detectorManualArrayCopy=ManualArrayCopy|true
14392 detectorMethodReturnCheck=MethodReturnCheck|true
144 detectorMethodReturnsConstant=MethodReturnsConstant|true
145 detectorMethods=Methods|true
146 detectorMisleadingOverloadModel=MisleadingOverloadModel|true
147 detectorMoreDumbMethods=MoreDumbMethods|true
14893 detectorMultithreadedInstanceAccess=MultithreadedInstanceAccess|true
14994 detectorMutableLock=MutableLock|true
15095 detectorMutableStaticFields=MutableStaticFields|true
15196 detectorNaming=Naming|true
152 detectorNeedlessAutoboxing=NeedlessAutoboxing|true
153 detectorNeedlessCustomSerialization=NeedlessCustomSerialization|true
154 detectorNeedlessInstanceRetrieval=NeedlessInstanceRetrieval|true
155 detectorNeedlessMemberCollectionSynchronization=NeedlessMemberCollectionSynchronization|true
156 detectorNonCollectionMethodUse=NonCollectionMethodUse|true
157 detectorNonFunctionalField=NonFunctionalField|true
158 detectorNonOwnedSynchronization=NonOwnedSynchronization|true
159 detectorNonRecycleableTaglibs=NonRecycleableTaglibs|true
160 detectorNonSymmetricEquals=NonSymmetricEquals|true
161 detectorNoteAnnotationRetention=NoteAnnotationRetention|true
162 detectorNoteCheckReturnValue=NoteCheckReturnValue|true
163 detectorNoteCheckReturnValueAnnotations=NoteCheckReturnValueAnnotations|true
164 detectorNoteDirectlyRelevantTypeQualifiers=NoteDirectlyRelevantTypeQualifiers|true
165 detectorNoteJCIPAnnotation=NoteJCIPAnnotation|true
166 detectorNoteNonNullAnnotations=NoteNonNullAnnotations|true
167 detectorNoteNonnullReturnValues=NoteNonnullReturnValues|true
168 detectorNoteSuppressedWarnings=NoteSuppressedWarnings|true
16997 detectorNoteUnconditionalParamDerefs=NoteUnconditionalParamDerefs|true
17098 detectorNumberConstructor=NumberConstructor|true
171 detectorOrphanedDOMNode=OrphanedDOMNode|true
172 detectorOverlyConcreteParameter=OverlyConcreteParameter|true
99 detectorOptionalReturnNull=OptionalReturnNull|true
173100 detectorOverridingEqualsNotSymmetrical=OverridingEqualsNotSymmetrical|true
174 detectorOverzealousCasting=OverzealousCasting|true
175 detectorParallelLists=ParallelLists|true
176 detectorPartiallyConstructedObjectAccess=PartiallyConstructedObjectAccess|true
177 detectorPoorlyDefinedParameter=PoorlyDefinedParameter|true
178 detectorPossibleConstantAllocationInLoop=PossibleConstantAllocationInLoop|true
179 detectorPossibleIncompleteSerialization=PossibleIncompleteSerialization|true
180 detectorPossibleMemoryBloat=PossibleMemoryBloat|true
181 detectorPossibleUnsuspectedSerialization=PossibleUnsuspectedSerialization|true
182 detectorPossiblyRedundantMethodCalls=PossiblyRedundantMethodCalls|true
183101 detectorPreferZeroLengthArrays=PreferZeroLengthArrays|true
184102 detectorPublicSemaphores=PublicSemaphores|true
185103 detectorQuestionableBooleanAssignment=QuestionableBooleanAssignment|true
186104 detectorReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass=ReadOfInstanceFieldInMethodInvokedByConstructorInSuperclass|true
187105 detectorReadReturnShouldBeChecked=ReadReturnShouldBeChecked|true
188106 detectorRedundantInterfaces=RedundantInterfaces|true
189 detectorReflectionOnObjectMethods=ReflectionOnObjectMethods|true
190 detectorReflectiveClasses=ReflectiveClasses|true
191107 detectorRepeatedConditionals=RepeatedConditionals|true
192 detectorResolveAllReferences=ResolveAllReferences|false
193108 detectorRuntimeExceptionCapture=RuntimeExceptionCapture|true
194 detectorSQLInLoop=SQLInLoop|true
195 detectorSection508Compliance=Section508Compliance|true
196109 detectorSerializableIdiom=SerializableIdiom|true
197 detectorSideEffectConstructor=SideEffectConstructor|true
198 detectorSillynessPotPourri=SillynessPotPourri|true
199 detectorSloppyClassReflection=SloppyClassReflection|true
200 detectorSluggishGui=SluggishGui|true
201 detectorSpoiledChildInterfaceImplementor=SpoiledChildInterfaceImplementor|true
202 detectorSpuriousThreadStates=SpuriousThreadStates|true
203110 detectorStartInConstructor=StartInConstructor|true
204 detectorStaticArrayCreatedInMethod=StaticArrayCreatedInMethod|true
205111 detectorStaticCalendarDetector=StaticCalendarDetector|true
206 detectorStaticMethodInstanceInvocation=StaticMethodInstanceInvocation|true
207112 detectorStringConcatenation=StringConcatenation|true
208113 detectorSuperfluousInstanceOf=SuperfluousInstanceOf|true
209 detectorSuspiciousCloneAlgorithm=SuspiciousCloneAlgorithm|true
210 detectorSuspiciousClusteredSessionSupport=SuspiciousClusteredSessionSupport|true
211 detectorSuspiciousComparatorReturnValues=SuspiciousComparatorReturnValues|true
212 detectorSuspiciousGetterSetterUse=SuspiciousGetterSetterUse|true
213 detectorSuspiciousJDKVersionUse=SuspiciousJDKVersionUse|false
214 detectorSuspiciousNullGuard=SuspiciousNullGuard|true
215114 detectorSuspiciousThreadInterrupted=SuspiciousThreadInterrupted|true
216 detectorSuspiciousUninitializedArray=SuspiciousUninitializedArray|true
217 detectorSuspiciousWaitOnConcurrentObject=SuspiciousWaitOnConcurrentObject|true
218115 detectorSwitchFallthrough=SwitchFallthrough|true
219 detectorSyncCollectionIterators=SyncCollectionIterators|true
220 detectorSynchronizationOnSharedBuiltinConstant=SynchronizationOnSharedBuiltinConstant|true
221116 detectorSynchronizeAndNullCheckField=SynchronizeAndNullCheckField|true
222117 detectorSynchronizeOnClassLiteralNotGetClass=SynchronizeOnClassLiteralNotGetClass|true
223118 detectorSynchronizingOnContentsOfFieldToProtectField=SynchronizingOnContentsOfFieldToProtectField|true
224 detectorTailRecursion=TailRecursion|true
225 detectorTestASM=TestASM|false
226 detectorTestDataflowAnalysis=TestDataflowAnalysis|false
227 detectorTestingGround=TestingGround|false
228 detectorTrainFieldStoreTypes=TrainFieldStoreTypes|true
229 detectorTrainNonNullAnnotations=TrainNonNullAnnotations|true
230 detectorTrainUnconditionalDerefParams=TrainUnconditionalDerefParams|true
231 detectorTristateBooleanPattern=TristateBooleanPattern|true
232119 detectorURLProblems=URLProblems|true
233120 detectorUncallableMethodOfAnonymousClass=UncallableMethodOfAnonymousClass|true
234121 detectorUnnecessaryMath=UnnecessaryMath|true
235 detectorUnnecessaryNewNullCheck=UnnecessaryNewNullCheck|true
236 detectorUnnecessaryStoreBeforeReturn=UnnecessaryStoreBeforeReturn|true
237122 detectorUnreadFields=UnreadFields|true
238 detectorUnrelatedCollectionContents=UnrelatedCollectionContents|true
239 detectorUnrelatedReturnValues=UnrelatedReturnValues|true
240 detectorUseAddAll=UseAddAll|true
241 detectorUseCharacterParameterizedMethod=UseCharacterParameterizedMethod|true
242 detectorUseEnumCollections=UseEnumCollections|true
243 detectorUseObjectEquals=UseObjectEquals|true
244 detectorUseSplit=UseSplit|true
245 detectorUseToArray=UseToArray|true
246 detectorUseVarArgs=UseVarArgs|true
247123 detectorUselessSubclassMethod=UselessSubclassMethod|true
248124 detectorVarArgsProblems=VarArgsProblems|true
249125 detectorVolatileUsage=VolatileUsage|true
250126 detectorWaitInLoop=WaitInLoop|true
251 detectorWeakExceptionMessaging=WeakExceptionMessaging|true
252 detectorWriteOnlyCollection=WriteOnlyCollection|true
253127 detectorWrongMapIterator=WrongMapIterator|true
254128 detectorXMLFactoryBypass=XMLFactoryBypass|true
255 detector_threshold=2
129 detector_threshold=3
256130 effort=default
257131 excludefilter0=findbugsExclude.xml|true
258 filter_settings=Medium|BAD_PRACTICE,CORRECTNESS,EXPERIMENTAL,I18N,MT_CORRECTNESS,PERFORMANCE,SECURITY,STYLE|false|15
132 filter_settings=Low|BAD_PRACTICE,CORRECTNESS,EXPERIMENTAL,I18N,MT_CORRECTNESS,PERFORMANCE,SECURITY,STYLE|false|20
259133 filter_settings_neg=MALICIOUS_CODE,NOISE|
260134 run_at_full_build=false
00 eclipse.preferences.version=1
1 edu.umd.cs.findbugs.plugin.eclipse.findbugsMarkerOfConcern=Info
21 runAnalysisAsExtraJob=true
0 eclipse.preferences.version=1
1 encoding/<project>=UTF-8
0 eclipse.preferences.version=1
1 line.separator=\n
00 eclipse.preferences.version=1
1 instance/org.eclipse.core.net/org.eclipse.core.net.hasMigrated=true
21 org.eclipse.jdt.core.builder.cleanOutputFolder=clean
32 org.eclipse.jdt.core.builder.duplicateResourceTask=warning
43 org.eclipse.jdt.core.builder.invalidClasspath=abort
2625 org.eclipse.jdt.core.compiler.annotation.nullable=javax.annotation.Nullable
2726 org.eclipse.jdt.core.compiler.annotation.nullanalysis=enabled
2827 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
29 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5
28 org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
3029 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
31 org.eclipse.jdt.core.compiler.compliance=1.5
30 org.eclipse.jdt.core.compiler.compliance=1.7
3231 org.eclipse.jdt.core.compiler.debug.lineNumber=generate
3332 org.eclipse.jdt.core.compiler.debug.localVariable=generate
3433 org.eclipse.jdt.core.compiler.debug.sourceFile=generate
3938 org.eclipse.jdt.core.compiler.problem.autoboxing=ignore
4039 org.eclipse.jdt.core.compiler.problem.comparingIdentical=warning
4140 org.eclipse.jdt.core.compiler.problem.deadCode=warning
42 org.eclipse.jdt.core.compiler.problem.deprecation=warning
41 org.eclipse.jdt.core.compiler.problem.deprecation=ignore
4342 org.eclipse.jdt.core.compiler.problem.deprecationInDeprecatedCode=disabled
4443 org.eclipse.jdt.core.compiler.problem.deprecationWhenOverridingDeprecatedMethod=enabled
4544 org.eclipse.jdt.core.compiler.problem.discouragedReference=warning
134133 org.eclipse.jdt.core.compiler.problem.unusedTypeParameter=ignore
135134 org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
136135 org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
137 org.eclipse.jdt.core.compiler.source=1.5
136 org.eclipse.jdt.core.compiler.source=1.7
138137 org.eclipse.jdt.core.compiler.taskCaseSensitive=enabled
139138 org.eclipse.jdt.core.compiler.taskPriorities=NORMAL,HIGH,NORMAL,HIGH
140139 org.eclipse.jdt.core.compiler.taskTags=TODO,FIXME,XXX,MUSTFIX
221220 org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true
222221 org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false
223222 org.eclipse.jdt.core.formatter.indentation.size=4
224 org.eclipse.jdt.core.formatter.insert_new_line_after_annotation=insert
225223 org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert
226224 org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert
227 org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_member=insert
228225 org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert
229226 org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert
230227 org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=insert
0 cleanup.add_default_serial_version_id=true
1 cleanup.add_generated_serial_version_id=false
2 cleanup.add_missing_annotations=true
3 cleanup.add_missing_deprecated_annotations=true
4 cleanup.add_missing_methods=false
5 cleanup.add_missing_nls_tags=false
6 cleanup.add_missing_override_annotations=true
7 cleanup.add_missing_override_annotations_interface_methods=true
8 cleanup.add_serial_version_id=false
9 cleanup.always_use_blocks=true
10 cleanup.always_use_parentheses_in_expressions=false
11 cleanup.always_use_this_for_non_static_field_access=false
12 cleanup.always_use_this_for_non_static_method_access=false
13 cleanup.convert_functional_interfaces=false
14 cleanup.convert_to_enhanced_for_loop=false
15 cleanup.correct_indentation=true
16 cleanup.format_source_code=false
17 cleanup.format_source_code_changes_only=false
18 cleanup.insert_inferred_type_arguments=false
19 cleanup.make_local_variable_final=false
20 cleanup.make_parameters_final=false
21 cleanup.make_private_fields_final=true
22 cleanup.make_type_abstract_if_missing_method=false
23 cleanup.make_variable_declarations_final=true
24 cleanup.never_use_blocks=false
25 cleanup.never_use_parentheses_in_expressions=true
26 cleanup.organize_imports=true
27 cleanup.qualify_static_field_accesses_with_declaring_class=false
28 cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true
29 cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true
30 cleanup.qualify_static_member_accesses_with_declaring_class=true
31 cleanup.qualify_static_method_accesses_with_declaring_class=false
32 cleanup.remove_private_constructors=true
33 cleanup.remove_redundant_type_arguments=true
34 cleanup.remove_trailing_whitespaces=true
35 cleanup.remove_trailing_whitespaces_all=true
36 cleanup.remove_trailing_whitespaces_ignore_empty=false
37 cleanup.remove_unnecessary_casts=true
38 cleanup.remove_unnecessary_nls_tags=true
39 cleanup.remove_unused_imports=true
40 cleanup.remove_unused_local_variables=false
41 cleanup.remove_unused_private_fields=true
42 cleanup.remove_unused_private_members=false
43 cleanup.remove_unused_private_methods=true
44 cleanup.remove_unused_private_types=true
45 cleanup.sort_members=false
46 cleanup.sort_members_all=false
47 cleanup.use_anonymous_class_creation=false
48 cleanup.use_blocks=true
49 cleanup.use_blocks_only_for_return_and_throw=false
50 cleanup.use_lambda=true
51 cleanup.use_parentheses_in_expressions=false
52 cleanup.use_this_for_non_static_field_access=false
53 cleanup.use_this_for_non_static_field_access_only_if_necessary=true
54 cleanup.use_this_for_non_static_method_access=false
55 cleanup.use_this_for_non_static_method_access_only_if_necessary=true
56 cleanup.use_type_arguments=false
57 cleanup_profile=_FindBugs
58 cleanup_settings_version=2
059 eclipse.preferences.version=1
160 editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true
261 formatter_profile=_FindBugs
362 formatter_settings_version=12
463 org.eclipse.jdt.ui.exception.name=e
564 org.eclipse.jdt.ui.gettersetter.use.is=true
65 org.eclipse.jdt.ui.ignorelowercasenames=true
66 org.eclipse.jdt.ui.importorder=java;javax;org;com;
667 org.eclipse.jdt.ui.javadoc=true
768 org.eclipse.jdt.ui.keywordthis=false
69 org.eclipse.jdt.ui.ondemandthreshold=99
870 org.eclipse.jdt.ui.overrideannotation=true
71 org.eclipse.jdt.ui.staticondemandthreshold=2
972 org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\n * @return Returns the ${bare_field_name}.\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\n * @param ${param} The ${bare_field_name} to set.\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\n * ${tags}\n */</template><template autoinsert\="false" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/*\n * FindBugs - Find Bugs in Java programs\n * Copyright (C) 2003-2008 University of Maryland\n * \n * This library is free software; you can redistribute it and/or\n * modify it under the terms of the GNU Lesser General Public\n * License as published by the Free Software Foundation; either\n * version 2.1 of the License, or (at your option) any later version.\n * \n * This library is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU\n * Lesser General Public License for more details.\n * \n * You should have received a copy of the GNU Lesser General Public\n * License along with this library; if not, write to the Free Software\n * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\n */\n</template><template autoinsert\="true" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\n * @author ${user}\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\n * \n */</template><template autoinsert\="true" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\n * ${tags}\n */</template><template autoinsert\="false" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment"/><template autoinsert\="true" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">${filecomment}\n${package_declaration}\n\n${typecomment}\n${type_declaration}</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\n * ${tags}\n * ${see_to_target}\n */</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\n</template></templates>
1073 sp_cleanup.add_default_serial_version_id=true
1174 sp_cleanup.add_generated_serial_version_id=false
1477 sp_cleanup.add_missing_methods=false
1578 sp_cleanup.add_missing_nls_tags=false
1679 sp_cleanup.add_missing_override_annotations=true
17 sp_cleanup.add_missing_override_annotations_interface_methods=false
80 sp_cleanup.add_missing_override_annotations_interface_methods=true
1881 sp_cleanup.add_serial_version_id=false
1982 sp_cleanup.always_use_blocks=true
2083 sp_cleanup.always_use_parentheses_in_expressions=false
2184 sp_cleanup.always_use_this_for_non_static_field_access=false
2285 sp_cleanup.always_use_this_for_non_static_method_access=false
86 sp_cleanup.convert_functional_interfaces=false
2387 sp_cleanup.convert_to_enhanced_for_loop=false
24 sp_cleanup.correct_indentation=false
88 sp_cleanup.correct_indentation=true
2589 sp_cleanup.format_source_code=false
2690 sp_cleanup.format_source_code_changes_only=false
91 sp_cleanup.insert_inferred_type_arguments=false
2792 sp_cleanup.make_local_variable_final=false
2893 sp_cleanup.make_parameters_final=false
2994 sp_cleanup.make_private_fields_final=true
3095 sp_cleanup.make_type_abstract_if_missing_method=false
31 sp_cleanup.make_variable_declarations_final=false
96 sp_cleanup.make_variable_declarations_final=true
3297 sp_cleanup.never_use_blocks=false
3398 sp_cleanup.never_use_parentheses_in_expressions=true
3499 sp_cleanup.on_save_use_additional_actions=true
39104 sp_cleanup.qualify_static_member_accesses_with_declaring_class=false
40105 sp_cleanup.qualify_static_method_accesses_with_declaring_class=false
41106 sp_cleanup.remove_private_constructors=true
107 sp_cleanup.remove_redundant_type_arguments=false
42108 sp_cleanup.remove_trailing_whitespaces=true
43 sp_cleanup.remove_trailing_whitespaces_all=false
44 sp_cleanup.remove_trailing_whitespaces_ignore_empty=true
109 sp_cleanup.remove_trailing_whitespaces_all=true
110 sp_cleanup.remove_trailing_whitespaces_ignore_empty=false
45111 sp_cleanup.remove_unnecessary_casts=true
46112 sp_cleanup.remove_unnecessary_nls_tags=false
47113 sp_cleanup.remove_unused_imports=true
52118 sp_cleanup.remove_unused_private_types=true
53119 sp_cleanup.sort_members=false
54120 sp_cleanup.sort_members_all=false
55 sp_cleanup.use_blocks=false
121 sp_cleanup.use_anonymous_class_creation=false
122 sp_cleanup.use_blocks=true
56123 sp_cleanup.use_blocks_only_for_return_and_throw=false
124 sp_cleanup.use_lambda=false
57125 sp_cleanup.use_parentheses_in_expressions=false
58126 sp_cleanup.use_this_for_non_static_field_access=false
59127 sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true
60128 sp_cleanup.use_this_for_non_static_method_access=false
61129 sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true
130 sp_cleanup.use_type_arguments=false
55 Bundle-Version: 1.0.0
66 Bundle-ClassPath: findbugs.jar,
77 lib/AppleJavaExtensions.jar,
8 lib/bcel.jar,
8 lib/bcel-6.0-SNAPSHOT.jar,
99 lib/dom4j-1.6.1.jar,
1010 lib/junit.jar,
11 lib/asm-3.3.jar,
12 lib/asm-tree-3.3.jar,
13 lib/asm-commons-3.3.jar,
11 lib/asm-debug-all-5.0.2.jar,
1412 lib/jaxen-1.1.6.jar,
1513 lib/jFormatString.jar,
1614 lib/commons-lang-2.6.jar,
1715 lib/jsr305.jar,
1816 lib/ant.jar,
19 lib/asm-analysis-3.3.jar,
20 lib/asm-util-3.3.jar,
21 lib/asm-xml-3.3.jar,
2217 lib/jcip-annotations.jar,
2318 lib/jdepend-2.9.jar,
2419 lib/yjp-controller-api-redist.jar
7671 edu.umd.cs.findbugs.tools.html,
7772 edu.umd.cs.findbugs.tools.junit,
7873 edu.umd.cs.findbugs.tools.xml,
74 edu.umd.cs.findbugs.updates,
7975 edu.umd.cs.findbugs.util,
8076 edu.umd.cs.findbugs.visitclass,
8177 edu.umd.cs.findbugs.workflow,
9591 org.objectweb.asm,
9692 org.objectweb.asm.commons,
9793 org.objectweb.asm.signature,
98 org.objectweb.asm.tree
99 Bundle-RequiredExecutionEnvironment: J2SE-1.5
94 org.objectweb.asm.tree,
95 org.objectweb.asm.tree.analysis,
96 org.objectweb.asm.util,
97 org.objectweb.asm.xml
98 Bundle-RequiredExecutionEnvironment: JavaSE-1.7
3636 <property name="samplesrc.dir" value="src/sampleXml"/>
3737 <property name="etc.dir" value="etc"/>
3838 <property name="test.dir" value="test"/>
39 <property name="doc.dir" value="doc"/>
39 <property name="doc.dir" value="build/doc"/>
4040 <property name="web.dir" value="web"/>
4141 <property name="apiDoc.dir" value="apiJavaDoc"/>
4242 <property name="annotationDoc.dir" value="annotationJavaDoc"/>
5555 <property name="scripts.stamp" value="${build.dir}/scripts.stamp"/>
5656 <property name="allClassFiles.jar" value="${build.dir}/allClassFiles.jar"/>
5757 <property name="doc.props.file" value="${etc.dir}/doc.properties"/>
58 <property name="svnrnum" value="Unknown"/>
5958 <property name="version.props.file" value="${build.dir}/classes/edu/umd/cs/findbugs/version.properties"/>
6059
6160 <path id="findbugs.classpath">
62 <pathelement location="${jar.dir}/bcel.jar"/>
63 <pathelement location="${jar.dir}/dom4j-1.6.1.jar"/>
64 <pathelement location="${jar.dir}/AppleJavaExtensions.jar"/>
65 <pathelement location="${jar.dir}/junit.jar"/>
66 <pathelement location="${jar.dir}/asm-3.3.jar"/>
67 <pathelement location="${jar.dir}/asm-commons-3.3.jar"/>
68 <pathelement location="${jar.dir}/asm-tree-3.3.jar"/>
69 <pathelement location="${jar.dir}/jaxen-1.1.6.jar"/>
70 <pathelement location="${jar.dir}/jsr305.jar"/>
71 <pathelement location="${jar.dir}/jFormatString.jar"/>
72 <pathelement location="${jar.dir}/commons-lang-2.6.jar"/>
73 <pathelement location="${jar.dir}/jcip-annotations.jar"/>
61 <fileset dir="${jar.dir}" includes="*.jar"/>
7462 </path>
7563
7664 <path id="tools.classpath">
8068 <path refid="findbugs.classpath"/>
8169 </path>
8270
83 <path id="svn.classpath">
84
85 <fileset dir="${svnant.home}/lib">
86 <include name="*.jar"/>
87 </fileset>
88 </path>
89
9071
9172 <patternset id="codebase.data.pats">
9273 <include name="**/*.properties"/>
120101 <!-- Rebuild from scratch. -->
121102 <target name="rebuild" depends="clean,build"/>
122103
123 <target name="mondo" depends="jars">
124 <jar destfile="build/mondo.jar"
125 manifest="etc/MANIFEST-findbugs-mondo.MF">
126 <zipfileset src="${jar.dir}/bcel.jar"/>
127 <zipfileset src="${jar.dir}/dom4j-1.6.1.jar"/>
128 <zipfileset src="${jar.dir}/AppleJavaExtensions.jar"/>
129 <zipfileset src="${jar.dir}/junit.jar"/>
130 <zipfileset src="${jar.dir}/asm-3.3.jar"/>
131 <zipfileset src="${jar.dir}/asm-commons-3.3.jar"/>
132 <zipfileset src="${jar.dir}/asm-tree-3.3.jar"/>
133 <zipfileset src="${jar.dir}/jaxen-1.1.6.jar"/>
134 <zipfileset src="${jar.dir}/jsr305.jar"/>
135 <zipfileset src="${jar.dir}/jFormatString.jar"/>
136 <zipfileset src="${jar.dir}/commons-lang-2.6.jar"/>
137 <zipfileset src="${jar.dir}/findbugs.jar"/>
138 </jar>
139 </target>
140
141104 <!-- Compile Java source files, and copy other files (properties,
142105 images, html, XSL stylesheets) that need to be part of the codebase. -->
143106 <target name="errorprone" depends="clean,init">
144107 <javac
145108 destdir="${classes.dir}"
146 source="1.5"
147 target="1.5"
109 source="1.7"
110 target="1.7"
148111 includeantruntime="false"
149112 encoding="ISO-8859-1"
150113 deprecation="off"
151114 debug="on"
152 compiler="com.google.errorprone.ErrorProneAntCompilerAdapter"
153 >
115 compiler="com.google.errorprone.ErrorProneAntCompilerAdapter"
116 >
154117 <compilerarg value="-Xlint:unchecked"/>
155118 <src path="${src.dir}"/>
156119 <src path="${src5.dir}"/>
157120 <classpath refid="findbugs.classpath"/>
158121 </javac>
159 </target>
122 </target>
160123 <target name="classes" depends="init">
124 <echo>Requires JDK 7.x (not 8!), using: ${ant.java.version}</echo>
125 <fail message="Requires JDK 7.x (not 8!), using: ${ant.java.version}">
126 <condition>
127 <equals arg1="1.8" arg2="${ant.java.version}"/>
128 </condition>
129 </fail>
161130
162131 <!-- Compile Java source files. -->
163132 <echo level="info" message="compiling findbugs"/>
164133 <javac
165134 destdir="${classes.dir}"
166 source="1.5"
167 target="1.5"
135 source="1.7"
136 target="1.7"
168137 includeantruntime="false"
169138 encoding="ISO-8859-1"
170139 deprecation="off"
196165 <echo level="info" message="compiling junit test cases"/>
197166 <javac srcdir="${junitsrc.dir}"
198167 destdir="${junitclasses.dir}"
199 source="1.5"
200 target="1.5"
168 source="1.7"
169 target="1.7"
201170 includeantruntime="false"
202171 encoding="ISO-8859-1"
203172 deprecation="off"
208177 <echo level="info" message="compiling tools"/>
209178 <javac srcdir="${toolsrc.dir}"
210179 destdir="${classes.dir}"
211 source="1.5"
212 target="1.5"
180 source="1.7"
181 target="1.7"
213182 includeantruntime="false"
214183 encoding="ISO-8859-1"
215184 debug="on"
222191 <echo level="info" message="compiling ant task"/>
223192 <javac srcdir="${anttasksrc.dir}"
224193 destdir="${classes.dir}"
225 source="1.5"
226 target="1.5"
194 source="1.7"
195 target="1.7"
227196 includeantruntime="false"
228197 encoding="ISO-8859-1"
229198 deprecation="off"
333302 </jar>
334303 </target>
335304
336 <!--
337 Create one giant executable Jar file containing
338 Findbugs, textui and gui, detectors, and all required
339 libraries.
340 -->
341 <target name="fulljar" depends="classes,validate,version">
342 <property name="fullJarName" value="findbugs-full-${release.number}.jar"/>
343 <jar destfile="${jar.dir}/${fullJarName}" manifest="etc/MANIFEST-findbugs-full.MF">
344 <fileset dir="${classes.dir}">
345 <exclude name="${pkg.base}/tools/**"/>
346 <exclude name="${pkg.base}/antTask/**"/>
347 <include name="**/*.class"/>
348 <include name="**/*.properties"/>
349 <include name="**/*.db"/>
350 <include name="**/*.xsl"/>
351 <include name="${pkg.base}/sourceViewer/**/*.class"/>
352 <include name="${pkg.base}/gui/**/*.png"/>
353 <include name="${pkg.base}/gui/**/*.html"/>
354 <include name="${pkg.base}/gui2/**/*.png"/>
355 <include name="${pkg.base}/gui2/**/*.html"/>
356 </fileset>
357 <fileset dir="${etc.dir}">
358 <include name="bugrank.txt"/>
359 <include name="findbugs.xml"/>
360 <include name="bugrank.txt"/>
361 <include name="messages*.xml"/>
362 </fileset>
363
364 <zipfileset src="${jar.dir}/bcel.jar" excludes="META-INF/**"/>
365
366 <zipfileset src="${jar.dir}/asm-3.3.jar" excludes="META-INF/**"/>
367 <zipfileset src="${jar.dir}/asm-analysis-3.3.jar" excludes="META-INF/**"/>
368 <zipfileset src="${jar.dir}/asm-commons-3.3.jar" excludes="META-INF/**"/>
369 <zipfileset src="${jar.dir}/asm-tree-3.3.jar" excludes="META-INF/**"/>
370 <zipfileset src="${jar.dir}/asm-util-3.3.jar" excludes="META-INF/**"/>
371 <zipfileset src="${jar.dir}/asm-xml-3.3.jar" excludes="META-INF/**"/>
372
373 <zipfileset src="${jar.dir}/dom4j-1.6.1.jar" excludes="META-INF/**"/>
374 <zipfileset src="${jar.dir}/jaxen-1.1.6.jar" excludes="META-INF/**"/>
375 <zipfileset src="${jar.dir}/jsr305.jar" excludes="META-INF/**"/>
376 <zipfileset src="${jar.dir}/jFormatString.jar" excludes="META-INF/**"/>
377 <zipfileset src="${jar.dir}/commons-lang-2.6.jar" excludes="META-INF/**"/>
378 </jar>
379 </target>
380305
381306
382307 <!-- Generate front-end scripts. -->
443368
444369
445370 <target name="applystylesheets" depends="jars,runanttask">
446 <property name="findbugs.home" value="." />
371 <property name="findbugs.home" value="${basedir}" />
447372 <java classname="edu.umd.cs.findbugs.PrintingBugReporter"
448373 fork="true"
449374 failonerror="true"
452377 <classpath>
453378 <pathelement location="${jar.dir}/findbugs.jar"/>
454379 </classpath>
455 <jvmarg value="-ea"/>
456 <jvmarg value="-Dfindbugs.home=${findbugs.home}"/>
380 <jvmarg value="-ea"/>
381 <jvmarg value="-Dfindbugs.home=${findbugs.home}"/>
457382 <arg value="-html:fancy.xsl"/>
458383 <arg value="${sampleoutput.dir}/bcel.xml"/>
459384 </java>
465390 <classpath>
466391 <pathelement location="${jar.dir}/findbugs.jar"/>
467392 </classpath>
468 <jvmarg value="-ea"/>
469 <jvmarg value="-Dfindbugs.home=${findbugs.home}"/>
393 <jvmarg value="-ea"/>
394 <jvmarg value="-Dfindbugs.home=${findbugs.home}"/>
470395 <arg value="-html:default.xsl"/>
471396 <arg value="${sampleoutput.dir}/bcel.xml"/>
472397 </java>
478403 <classpath>
479404 <pathelement location="${jar.dir}/findbugs.jar"/>
480405 </classpath>
481 <jvmarg value="-ea"/>
482 <jvmarg value="-Dfindbugs.home=${findbugs.home}"/>
406 <jvmarg value="-ea"/>
407 <jvmarg value="-Dfindbugs.home=${findbugs.home}"/>
483408 <arg value="-html:plain.xsl"/>
484409 <arg value="${sampleoutput.dir}/bcel.xml"/>
485410 </java>
491416 <classpath>
492417 <pathelement location="${jar.dir}/findbugs.jar"/>
493418 </classpath>
494 <jvmarg value="-ea"/>
495 <jvmarg value="-Dfindbugs.home=${findbugs.home}"/>
419 <jvmarg value="-ea"/>
420 <jvmarg value="-Dfindbugs.home=${findbugs.home}"/>
496421 <arg value="-html:summary.xsl"/>
497422 <arg value="${sampleoutput.dir}/bcel.xml"/>
498423 </java>
499
500
501
502
503424 </target>
425
504426 <target name="runanttask" depends="anttask,jars">
505 <property name="findbugs.home" value="." />
427 <property name="findbugs.home" value="${basedir}" />
506428 <taskdef resource="edu/umd/cs/findbugs/anttask/tasks.properties" classpath="${anttask.jar}"/>
507 <findbugs home="."
429 <findbugs home="${basedir}"
508430 output="xml:withMessages"
509431 jvmargs="-ea -Xmx1200m"
510432 projectName="Byte code Engineering Library (BCEL)"
511433 outputFile="${sampleoutput.dir}/bcel.xml"
512434 >
513 <class location="${jar.dir}/bcel.jar" />
435 <class location="${jar.dir}/bcel-6.0-SNAPSHOT.jar" />
514436 </findbugs>
515 <findbugs home="."
437 <findbugs home="${basedir}"
516438 output="xml:withMessages"
517439 cloud="edu.umd.cs.findbugs.cloud.appengine.findbugs-cloud"
518440 jvmargs="-ea -Xmx1200m"
519441 projectName="Byte code Engineering Library (BCEL)"
520442 outputFile="${sampleoutput.dir}/bcel-cloud-appengine.xml" >
521 <class location="${jar.dir}/bcel.jar" />
443 <class location="${jar.dir}/bcel-6.0-SNAPSHOT.jar" />
522444 </findbugs>
523 <!--
524 <findbugs home="."
525 output="xml:withMessages"
526 cloud="edu.umd.cs.findbugs.cloud.appengine.findbugs-cloud"
527 jvmargs="-ea -Xmx1200m"
528 projectName="Byte code Engineering Library (BCEL)"
529 outputFile="${sampleoutput.dir}/bcel-cloud.xml" >
530 <class location="${jar.dir}/bcel.jar" />
531 </findbugs>
532 -->
533
534 <!--
535 <findbugs home="${findbugs.home}"
536 output="xml"
537 jvmargs="-ea -Xmx940m"
538 outputFile="${sampleoutput.dir}/bcel-3.xml"
539 projectFile="${sampleoutput.dir}/bcel.fbp"
540 applySuppression="true"
541 />
542
543 <convertXmlToText
544 home="${findbugs.home}"
545 jvmargs="-ea -Xmx940m"
546 input="${sampleoutput.dir}/bcel-3.xml"
547 output="${sampleoutput.dir}/bcel-3.txt"
548 applySuppression="true"
549 />
550 -->
551
552
553 <setBugDatabaseInfo home="."
445
446
447 <setBugDatabaseInfo home="${basedir}"
554448 withMessages="true"
555449 name="Set name test"
556450 input="${sampleoutput.dir}/bcel.xml"
572466 <sourcePath path="src/java:src/gui:src/junit:src/tools:src/antTask"/>
573467 <auxClasspath refid="tools.classpath"/>
574468 <auxClasspath path="lib/ant.jar"/>
575 </findbugs>
469 </findbugs>
576470 </target>
577471
578472
579473 <target name="findbugscheck" depends="anttask,junittests,jars">
580 <property name="findbugs.home" value="." />
474 <property name="findbugs.home" value="${basedir}" />
581475 <ant dir="${pluginsSrc.dir}/findbugsCommunalCloud" target="install" inheritAll="false" />
582476 <taskdef resource="edu/umd/cs/findbugs/anttask/tasks.properties" classpath="${anttask.jar}"/>
583 <findbugs home="."
477 <findbugs home="${basedir}"
584478 output="xml:withMessages"
585479 cloud="edu.umd.cs.findbugs.cloud.appengine.findbugs-cloud"
586480 jvmargs="-ea -Xmx1200m -Dfindbugs.failOnCloudError=true -Dfindbugs.cloud.token=238b6fc80cec17ec"
594488 <auxClasspath refid="tools.classpath"/>
595489 <auxClasspath path="lib/ant.jar"/>
596490 </findbugs>
597 <filterBugs home="."
491 <filterBugs home="${basedir}"
598492 withMessages="true"
599493 notAProblem="false"
600494 jvmargs="-ea -Xmx1200m"
604498 </target>
605499
606500 <target name="findbugscheck-cloud-fails" depends="anttask,junittests,jars">
607 <property name="findbugs.home" value="." />
501 <property name="findbugs.home" value="${basedir}" />
608502 <ant dir="${pluginsSrc.dir}/findbugsCommunalCloud" target="install" inheritAll="false" />
609503 <taskdef resource="edu/umd/cs/findbugs/anttask/tasks.properties" classpath="${anttask.jar}"/>
610 <findbugs home="."
504 <findbugs home="${basedir}"
611505 output="xml:withMessages"
612506 cloud="edu.umd.cs.findbugs.cloud.appengine.findbugs-cloud"
613507 jvmargs="-ea -Xmx1200m -Dfindbugs.failOnCloudError=true -Dwebcloud.host=example.com"
629523 </target>
630524
631525 <target name="findbugsTestCases" depends="anttask,junittests,jars,findbugsTestCases.check" if="findbugsTestCases.exists">
632 <property name="findbugs.home" value="." />
526 <property name="findbugs.home" value="${basedir}" />
633527 <taskdef resource="edu/umd/cs/findbugs/anttask/tasks.properties" classpath="${anttask.jar}"/>
634 <findbugs home="."
528 <findbugs home="${basedir}"
635529 output="xml"
636530 jvmargs="-ea -Xmx1200m"
637531 projectName="FindBugsTestCases"
646540 interface with plugins. Note that the generated .xml file is
647541 kept in the ${sampleoutput.dir} for later perusal. -->
648542 <target name="plugincheck" depends="anttask">
649 <property name="findbugs.home" value="." />
543 <property name="findbugs.home" value="${basedir}" />
650544 <taskdef resource="edu/umd/cs/findbugs/anttask/tasks.properties" classpath="${anttask.jar}"/>
651545 <!-- download the plugin -->
652546 <echo message="curl ${plugincheck.jar} -o ${plugin.dir}/${plugincheck.jar}"/>
687581 <target name="test" depends="runjunit,foundFindbugsTestCases" description="Run tests"/>
688582
689583 <target name="runjunit" depends="junittests,jars,compileFindbugsTestCases">
690 <echo>Running JUnit test cases for FindBugs...</echo>
584 <echo>Running JUnit test cases for FindBugs, results will be in: ${junit.dir}</echo>
691585 <delete dir="${junit.dir}"/>
692586 <mkdir dir="${junit.dir}"/>
693 <junit fork="yes" printsummary="true">
587 <junit fork="yes" printsummary="true" haltonfailure="true" haltonerror="true" dir="${basedir}">
694588 <jvmarg value="-ea"/>
695 <jvmarg value="-Dfindbugs.home=."/>
589 <jvmarg value="-Xmx1200m"/>
590 <jvmarg value="-Dfindbugs.home=${basedir}"/>
696591 <jvmarg value="-DfindbugsTestCases.home=${findbugsTestCases.dir}"/>
697592 <formatter type="xml"/> <!-- Hudson reads generated xml -->
698593 <classpath refid="tools.classpath"/>
737632
738633
739634 <!-- Get version properties. -->
740 <target name="version" depends="classes,-get-svn-revision" >
741 <echo>${svnrnum}</echo>
635
636 <target name="-get-git-revision">
637 <exec executable="git" spawn="false" outputproperty="gitrnum">
638 <arg value="log"/>
639 <arg value="--pretty=format:%h"/>
640 <arg value="-n"/>
641 <arg value="1"/>
642 </exec>
643 <echo>Got git revision ${gitrnum}</echo>
644 </target>
645
646
647 <target name="version" depends="classes,-get-git-revision" >
648 <echo>${gitrnum}</echo>
742649 <java classpathref="tools.classpath"
743650 output="${version.props.file}"
744651 classname="edu.umd.cs.findbugs.Version"
745652 failonerror="true">
746653 <arg value="-props"/>
747 <sysproperty key="svn.revision" value="${svnrnum}"/>
654 <sysproperty key="git.revision" value="${gitrnum}"/>
748655 </java>
749656
750657 <loadproperties srcfile="${version.props.file}"/>
751658 <copy todir="${classes.dir}" file="${version.props.file}"/>
752659 <loadproperties srcfile="${version.props.file}"/>
753 </target>
754
755 <target name="-get-svn-revision" if="svnant.home">
756 <taskdef resource="org/tigris/subversion/svnant/svnantlib.xml"
757 classpathref="svn.classpath"/>
758 <svn>
759 <status path="." revisionProperty="svnrnum"/>
760 </svn>
761 <echo>${svnrnum}</echo>
762660 </target>
763661
764662
841739 <param name="bugdesc.title" value="FindBugs Bug Descriptions (Unabridged)"/>
842740 <param name="bugdesc.prologue" value="${doc.html.gen.allBugDescriptions.prologue}"/>
843741 <param name="bugdesc.unabridged" value="true"/>
742 <param name="bugdesc.user.language" value="en"/>
743 </antcall>
744 <antcall target="generatebugdesc">
745 <param name="bugdesc.output" value="${docsrc.dir}/bugDescriptions_ja.html"/>
746 <param name="bugdesc.title" value="FindBugs Bug Descriptions"/>
747 <param name="bugdesc.prologue" value="${doc.html.gen.bugDescriptions.prologue}"/>
748 <param name="bugdesc.unabridged" value="false"/>
749 <param name="bugdesc.user.language" value="ja"/>
750 </antcall>
751 <antcall target="generatebugdesc">
752 <param name="bugdesc.output" value="${docsrc.dir}/bugDescriptions_fr.html"/>
753 <param name="bugdesc.title" value="FindBugs Bug Descriptions"/>
754 <param name="bugdesc.prologue" value="${doc.html.gen.bugDescriptions.prologue}"/>
755 <param name="bugdesc.unabridged" value="false"/>
756 <param name="bugdesc.user.language" value="fr"/>
844757 </antcall>
845758 </target>
846759
860773 logError="true"
861774 output="${bugdesc.output}">
862775 <jvmarg value="-ea"/>
863 <jvmarg value="-Dfindbugs.home=."/>
776 <jvmarg value="-Duser.language=${bugdesc.user.language}"/>
777 <jvmarg value="-Dfindbugs.home=${basedir}"/>
864778 <jvmarg value="-Dfindbugs.bugdesc.unabridged=${bugdesc.unabridged}"/>
865779 <classpath refid="tools.classpath"/>
866780 <arg value="${bugdesc.title}"/>
893807 <filter token="VERSION_BASE" value="${release.base}" />
894808 <filter token="VERSION" value="${release.number}" />
895809 <filter token="RELEASE_DATE" value="${release.date}" />
896 <filter token="FINDBUGS_SVN_REVISION" value="${findbugs.svn.revision}" />
810 <filter token="FINDBUGS_GIT_REVISION" value="${findbugs.git.revision}" />
897811 <filter token="ECLIPSE_UI_VERSION" value="${eclipse.ui.version}" />
898812 <filter token="WEBSITE" value="${findbugs.website}"/>
899813 <filter token="DOWNLOADS_WEBSITE" value="${findbugs.downloads.website}"/>
11491063 </patternset>
11501064
11511065 <zip destfile="${build.dir}/findbugs-${release.number}.zip" compress="true">
1152 <zipfileset prefix="findbugs-${release.number}" dir=".">
1066 <zipfileset prefix="findbugs-${release.number}" dir="${basedir}">
11531067 <patternset refid="bindist.miscfile.pats"/>
11541068 </zipfileset>
1155 <zipfileset prefix="findbugs-${release.number}" dir="." filemode="555">
1069 <zipfileset prefix="findbugs-${release.number}" dir="${basedir}" filemode="555">
11561070 <patternset refid="bindist.unixscript.pats"/>
11571071 </zipfileset>
1158 <zipfileset prefix="findbugs-${release.number}" dir=".">
1072 <zipfileset prefix="findbugs-${release.number}" dir="${basedir}">
11591073 <patternset refid="bindist.jar.pats"/>
11601074 </zipfileset>
11611075 <zipfileset prefix="findbugs-${release.number}/doc" dir="${doc.dir}">
11651079 </zip>
11661080
11671081 <tar destfile="${build.dir}/findbugs-${release.number}.tar.gz" compression="gzip">
1168 <tarfileset prefix="findbugs-${release.number}" dir=".">
1082 <tarfileset prefix="findbugs-${release.number}" dir="${basedir}">
11691083 <patternset refid="bindist.miscfile.pats"/>
11701084 </tarfileset>
1171 <tarfileset prefix="findbugs-${release.number}" dir="." mode="555">
1085 <tarfileset prefix="findbugs-${release.number}" dir="${basedir}" mode="555">
11721086 <patternset refid="bindist.unixscript.pats"/>
11731087 </tarfileset>
1174 <tarfileset prefix="findbugs-${release.number}" dir=".">
1088 <tarfileset prefix="findbugs-${release.number}" dir="${basedir}">
11751089 <patternset refid="bindist.jar.pats"/>
11761090 </tarfileset>
11771091 <tarfileset prefix="findbugs-${release.number}/doc" dir="${doc.dir}">
11831097 <move file="optionalPlugin/noUpdateChecks.jar" todir="plugin"/>
11841098
11851099 <zip destfile="${build.dir}/findbugs-noUpdateChecks-${release.number}.zip" compress="true">
1186 <zipfileset prefix="findbugs-${release.number}" dir=".">
1100 <zipfileset prefix="findbugs-${release.number}" dir="${basedir}">
11871101 <patternset refid="bindist.miscfile.pats"/>
11881102 </zipfileset>
1189 <zipfileset prefix="findbugs-${release.number}" dir="." filemode="555">
1103 <zipfileset prefix="findbugs-${release.number}" dir="${basedir}" filemode="555">
11901104 <patternset refid="bindist.unixscript.pats"/>
11911105 </zipfileset>
1192 <zipfileset prefix="findbugs-${release.number}" dir=".">
1106 <zipfileset prefix="findbugs-${release.number}" dir="${basedir}">
11931107 <patternset refid="bindist2.jar.pats"/>
11941108 </zipfileset>
11951109 <zipfileset prefix="findbugs-${release.number}/doc" dir="${doc.dir}">
11991113 </zip>
12001114
12011115 <tar destfile="${build.dir}/findbugs-noUpdateChecks-${release.number}.tar.gz" compression="gzip">
1202 <tarfileset prefix="findbugs-${release.number}" dir=".">
1116 <tarfileset prefix="findbugs-${release.number}" dir="${basedir}">
12031117 <patternset refid="bindist.miscfile.pats"/>
12041118 </tarfileset>
1205 <tarfileset prefix="findbugs-${release.number}" dir="." mode="555">
1119 <tarfileset prefix="findbugs-${release.number}" dir="${basedir}" mode="555">
12061120 <patternset refid="bindist.unixscript.pats"/>
12071121 </tarfileset>
1208 <tarfileset prefix="findbugs-${release.number}" dir=".">
1122 <tarfileset prefix="findbugs-${release.number}" dir="${basedir}">
12091123 <patternset refid="bindist2.jar.pats"/>
12101124 </tarfileset>
12111125 <tarfileset prefix="findbugs-${release.number}/doc" dir="${doc.dir}">
12201134
12211135 <!-- Build source distribution. -->
12221136 <target name="srcdist" description="Source distribution" unless="doNotExportSrc" depends="version">
1223 <echo>
1224 ${svnant.home}
1225 </echo>
1137 <delete dir="${build.dir}/src"/>
12261138 <mkdir dir="${build.dir}/src"/>
1227 <delete dir="${build.dir}/src/findbugs-${release.number}"/>
1228 <typedef resource="org/tigris/subversion/svnant/svnantlib.xml" classpathref="svn.classpath"/>
1229
1230 <svnSetting username="guest" password="" id="svn.settings" />
1231 <svn refid="svn.settings" >
1232 <!-- Use 'export' rather than 'checkout' to exclude the .svn stuff -->
1233 <export srcUrl="http://findbugs.googlecode.com/svn/trunk/findbugs"
1234 revision="HEAD"
1235 destPath="${build.dir}/src/findbugs-${release.number}"/>
1236 </svn>
1237
1238 <zip destfile="${build.dir}/findbugs-${release.number}-source.zip" compress="true">
1239 <zipfileset dir="${build.dir}/src" includes="findbugs-${release.number}/**"/>
1240 <zipfileset prefix="findbugs-${release.number}/doc" dir="${doc.dir}">
1241 <patternset refid="doc.src.pats"/>
1242 <patternset refid="doc.img.pats"/>
1243 </zipfileset>
1244 </zip>
1139 <exec executable="git" spawn="true">
1140 <arg value="archive"/>
1141 <arg value="-o"/>
1142 <arg value="${build.dir}/src/findbugs-${release.number}-source.zip"/>
1143 <arg value="--prefix"/>
1144 <arg value="findbugs-${release.number}/"/>
1145 <arg value="HEAD"/>
1146 </exec>
12451147 </target>
12461148
12471149 <!-- Delete generated files. -->
13541256 <fileset dir="${toolsrc.dir}"/>
13551257 <fileset dir="${anttasksrc.dir}"/>
13561258 <fileset dir="${junitsrc.dir}"/>
1357 <fileset dir=".">
1259 <fileset dir="${basedir}">
13581260 <include name="LICENSE*.txt"/>
13591261 </fileset>
13601262 </jar>
11 Bundle-ManifestVersion: 2
22 Bundle-Name: FindbugsAnnotations
33 Bundle-SymbolicName: findbugsAnnotations
4 Bundle-Version: 2.0.3
4 Bundle-Version: 3.0.1
55 Export-Package: edu.umd.cs.findbugs.annotations,
66 javax.annotation,
77 javax.annotation.concurrent,
11 Bundle-ManifestVersion: 2
22 Bundle-Name: FindbugsInternalAnnotations
33 Bundle-SymbolicName: findbugsInternalAnnotations
4 Bundle-Version: 2.0.3
4 Bundle-Version: 3.0.1
55 Export-Package: edu.umd.cs.findbugs.annotations
66 Bundle-RequiredExecutionEnvironment: J2SE-1.5
00 Manifest-Version: 1.0
11 Main-Class: edu.umd.cs.findbugs.LaunchAppropriateUI
2 Class-Path: bcel.jar dom4j-1.6.1.jar jaxen-1.1.6.jar asm-3.3.jar asm-tree-3.3.jar asm-commons-3.3.jar jsr305.jar jFormatString.jar commons-lang-2.6.jar
2 Class-Path: bcel-6.0-SNAPSHOT.jar dom4j-1.6.1.jar jaxen-1.1.6.jar asm-debug-all-5.0.2.jar jsr305.jar jFormatString.jar commons-lang-2.6.jar
00 Manifest-Version: 1.0
11 Main-Class: edu.umd.cs.findbugs.LaunchAppropriateUI
2 Class-Path: bcel.jar dom4j-1.6.1.jar jaxen-1.1.6.jar asm-3.3.jar asm-tree-3.3.jar asm-commons-3.3.jar jsr305.jar jFormatString.jar commons-lang-2.6.jar plastic.jar
2 Class-Path: bcel-6.0-SNAPSHOT.jar dom4j-1.6.1.jar jaxen-1.1.6.jar asm-debug-all-5.0.2.jar jsr305.jar jFormatString.jar commons-lang-2.6.jar plastic.jar
00 <?xml version="1.0" encoding="utf-8"?>
11 <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified"
2 xmlns:xs="http://www.w3.org/2001/XMLSchema">
3 <xs:annotation>
4 <xs:documentation>This scheme describes the XML format used by FindBugs to store the results
5 of analysis.</xs:documentation>
6 </xs:annotation>
7
8 <xs:element name="FindBugsFilter">
9 <xs:complexType>
10 <xs:sequence>
11 <xs:element ref="Matcher" minOccurs="1" maxOccurs="unbounded"/>
12 </xs:sequence>
13 </xs:complexType>
2 xmlns:xs="http://www.w3.org/2001/XMLSchema">
3 <xs:annotation>
4 <xs:documentation>This scheme describes the XML format used by FindBugs to store the results
5 of analysis.</xs:documentation>
6 </xs:annotation>
7
8 <xs:element name="FindBugsFilter">
9 <xs:complexType>
10 <xs:sequence>
11 <xs:element ref="Matcher" minOccurs="1" maxOccurs="unbounded"/>
12 </xs:sequence>
13 </xs:complexType>
1414 </xs:element>
15
16
17 <xs:element name="BugCollection">
18 <xs:complexType>
19 <xs:sequence>
20 <xs:element name="Project">
21 <xs:complexType>
22 <xs:sequence>
23 <xs:element name="Jar" type="xs:string" minOccurs="0"
24 maxOccurs="unbounded"/>
25 <xs:element name="AuxClasspathEntry" type="xs:string" minOccurs="0"
26 maxOccurs="unbounded"/>
27 <xs:element name="SrcDir" type="xs:string" minOccurs="0"
28 maxOccurs="unbounded"/>
29 <xs:element name="WrkDir" type="xs:string" minOccurs="0"
30 maxOccurs="1"/>
31 <xs:element name="Plugin" minOccurs="0"
32 maxOccurs="unbounded"> <xs:complexType>
33 <xs:attribute name="id" type="xs:string"/>
34 <xs:attribute name="enabled" type="xs:string"/>
35 </xs:complexType></xs:element>
36 <xs:element name="SuppressionFilter" minOccurs="0"
37 maxOccurs="1"> <xs:complexType>
38 <xs:sequence>
39 <xs:element ref="Matcher" minOccurs="1" maxOccurs="unbounded"/>
40 </xs:sequence>
41 </xs:complexType></xs:element>
42 <xs:element name="Cloud" minOccurs="0" maxOccurs="1">
43 <xs:complexType>
44 <xs:sequence>
45 <xs:element name="Property" minOccurs="0"
46 maxOccurs="unbounded">
47 <xs:complexType>
48 <xs:simpleContent>
49 <xs:extension base="xs:string">
50 <xs:attribute name="key" type="xs:string"/>
51 </xs:extension>
52 </xs:simpleContent>
53 </xs:complexType>
54 </xs:element>
55 </xs:sequence>
56 <xs:attribute name="id" type="xs:string"/>
57 <xs:attribute name="online" type="xs:boolean" use="optional"/>
58 <xs:attribute name="synced" type="xs:boolean" use="optional"/>
59 <xs:attribute name="detailsUrl" type="xs:string" use="optional"/>
60 </xs:complexType>
61 </xs:element>
62 </xs:sequence>
63 <xs:attribute name="filename" type="xs:string" use="optional"/>
64 <xs:attribute name="projectName" type="xs:string" use="optional"/>
65 </xs:complexType>
66 </xs:element>
67 <xs:element name="BugInstance" minOccurs="0" maxOccurs="unbounded">
68 <xs:complexType>
69 <xs:annotation>
70 <xs:documentation>Each BugInstance can have a sequence of
71 annotations</xs:documentation>
72 </xs:annotation>
73 <xs:sequence>
74 <xs:element name="ShortMessage" type="xs:string" minOccurs="0"/>
75 <xs:element name="LongMessage" type="xs:string" minOccurs="0"/>
76
77 <xs:choice maxOccurs="unbounded">
78
79 <xs:element name="Class">
80 <xs:annotation>
81 <xs:documentation>This annotation describes a
82 class</xs:documentation>
83 </xs:annotation>
84 <xs:complexType>
85 <xs:sequence>
86 <xs:element ref="SourceLine"/>
87 <xs:element ref="Message" minOccurs="0"/>
88 </xs:sequence>
89 <xs:attribute name="classname" type="xs:string"
90 use="required"/>
91 <xs:attribute name="role" type="xs:string" use="optional"/>
92 <xs:attribute name="primary" type="xs:boolean"
93 use="optional"/>
94 </xs:complexType>
95 </xs:element>
96 <xs:element name="Type">
97 <xs:complexType>
98 <xs:sequence>
99 <xs:element ref="SourceLine" minOccurs="0"/>
100 <xs:element ref="Message" minOccurs="0"/>
101 </xs:sequence>
102 <xs:attribute name="descriptor" type="xs:string"
103 use="required"/>
104 <xs:attribute name="role" type="xs:string" use="optional"/>
105 <xs:attribute name="typeParameters" type="xs:string"
106 use="optional"/>
107 </xs:complexType>
108 </xs:element>
109 <xs:element name="Method">
110 <xs:annotation>
111 <xs:documentation>This annotation describes a
112 method</xs:documentation>
113 </xs:annotation>
114 <xs:complexType>
115 <xs:sequence minOccurs="0">
116 <xs:element ref="SourceLine"/>
117 <xs:element ref="Message" minOccurs="0"/>
118 </xs:sequence>
119 <xs:attribute name="classname" type="xs:string"
120 use="required"/>
121 <xs:attribute name="name" type="xs:string" use="required"/>
122 <xs:attribute name="signature" type="xs:string"
123 use="required"/>
124 <xs:attribute name="isStatic" type="xs:boolean"
125 use="required"/>
126 <xs:attribute name="role" type="xs:string" use="optional"/>
127 <xs:attribute name="primary" type="xs:boolean"
128 use="optional"/>
129 </xs:complexType>
130 </xs:element>
131 <xs:element ref="SourceLine"/>
132 <xs:element name="LocalVariable">
133 <xs:annotation>
134 <xs:documentation>This annotation describes a local
135 variable</xs:documentation>
136 </xs:annotation>
137 <xs:complexType>
138 <xs:sequence>
139 <xs:element ref="Message" minOccurs="0"/>
140 </xs:sequence>
141 <xs:attribute name="name" type="xs:string" use="required"/>
142 <xs:attribute name="register" type="xs:short" use="required"/>
143 <xs:attribute name="pc" type="xs:int" use="required"/>
144 <xs:attribute name="role" type="xs:string" use="required"/>
145 </xs:complexType>
146 </xs:element>
147 <xs:element name="Field">
148 <xs:annotation>
149 <xs:documentation>This annotation describes a
150 field</xs:documentation>
151 </xs:annotation>
152 <xs:complexType>
153 <xs:sequence>
154 <xs:element ref="SourceLine"/>
155 <xs:element ref="Message" minOccurs="0"/>
156 </xs:sequence>
157 <xs:attribute name="classname" type="xs:string"
158 use="required"/>
159 <xs:attribute name="name" type="xs:string" use="required"/>
160 <xs:attribute name="signature" type="xs:string"
161 use="required"/>
162 <xs:attribute name="sourceSignature" type="xs:string"
163 use="optional"/>
164 <xs:attribute name="isStatic" type="xs:boolean"
165 use="required"/>
166 <xs:attribute name="role" type="xs:string" use="optional"/>
167 <xs:attribute name="primary" type="xs:boolean"
168 use="optional"/>
169
170 </xs:complexType>
171 </xs:element>
172 <xs:element name="Int">
173 <xs:complexType>
174 <xs:sequence>
175 <xs:element ref="Message" minOccurs="0"/>
176 </xs:sequence>
177 <xs:attribute name="value" type="xs:long" use="required"/>
178 <xs:attribute name="role" type="xs:string" use="optional"/>
179 </xs:complexType>
180 </xs:element>
181 <xs:element name="String">
182 <xs:complexType>
183 <xs:sequence>
184 <xs:element ref="Message" minOccurs="0"/>
185 </xs:sequence>
186 <xs:attribute name="value" type="xs:string" use="required"/>
187 <xs:attribute name="role" type="xs:string" use="optional"/>
188 </xs:complexType>
189 </xs:element>
190 <xs:element name="Property">
191 <xs:complexType>
192 <xs:attribute name="name" type="xs:string" use="required"/>
193 <xs:attribute name="value" type="xs:string" use="required"/>
194 </xs:complexType>
195 </xs:element>
196 <xs:element name="UserAnnotation" minOccurs="0">
197 <xs:complexType>
198 <xs:simpleContent>
199 <xs:extension base="xs:string">
200 <xs:attribute name="designation" type="designationType"
201 use="optional"/>
202 <xs:attribute name="user" type="xs:string"
203 use="optional"/>
204 <xs:attribute name="needsSync" type="xs:boolean"
15
16
17 <xs:element name="BugCollection">
18 <xs:complexType>
19 <xs:sequence>
20 <xs:element name="Project">
21 <xs:complexType>
22 <xs:sequence>
23 <xs:element name="Jar" type="xs:string" minOccurs="0"
24 maxOccurs="unbounded"/>
25 <xs:element name="AuxClasspathEntry" type="xs:string" minOccurs="0"
26 maxOccurs="unbounded"/>
27 <xs:element name="SrcDir" type="xs:string" minOccurs="0"
28 maxOccurs="unbounded"/>
29 <xs:element name="WrkDir" type="xs:string" minOccurs="0"
30 maxOccurs="1"/>
31 <xs:element name="Plugin" minOccurs="0"
32 maxOccurs="unbounded"> <xs:complexType>
33 <xs:attribute name="id" type="xs:string"/>
34 <xs:attribute name="enabled" type="xs:string"/>
35 </xs:complexType></xs:element>
36 <xs:element name="SuppressionFilter" minOccurs="0"
37 maxOccurs="1"> <xs:complexType>
38 <xs:sequence>
39 <xs:element ref="Matcher" minOccurs="1" maxOccurs="unbounded"/>
40 </xs:sequence>
41 </xs:complexType></xs:element>
42 <xs:element name="Cloud" minOccurs="0" maxOccurs="1">
43 <xs:complexType>
44 <xs:sequence>
45 <xs:element name="Property" minOccurs="0"
46 maxOccurs="unbounded">
47 <xs:complexType>
48 <xs:simpleContent>
49 <xs:extension base="xs:string">
50 <xs:attribute name="key" type="xs:string"/>
51 </xs:extension>
52 </xs:simpleContent>
53 </xs:complexType>
54 </xs:element>
55 </xs:sequence>
56 <xs:attribute name="id" type="xs:string"/>
57 <xs:attribute name="online" type="xs:boolean" use="optional"/>
58 <xs:attribute name="synced" type="xs:boolean" use="optional"/>
59 <xs:attribute name="detailsUrl" type="xs:string" use="optional"/>
60 </xs:complexType>
61 </xs:element>
62 </xs:sequence>
63 <xs:attribute name="filename" type="xs:string" use="optional"/>
64 <xs:attribute name="projectName" type="xs:string" use="optional"/>
65 </xs:complexType>
66 </xs:element>
67 <xs:element name="BugInstance" minOccurs="0" maxOccurs="unbounded">
68 <xs:complexType>
69 <xs:annotation>
70 <xs:documentation>Each BugInstance can have a sequence of
71 annotations</xs:documentation>
72 </xs:annotation>
73 <xs:sequence>
74 <xs:element name="ShortMessage" type="xs:string" minOccurs="0"/>
75 <xs:element name="LongMessage" type="xs:string" minOccurs="0"/>
76
77 <xs:choice maxOccurs="unbounded">
78
79 <xs:element name="Class">
80 <xs:annotation>
81 <xs:documentation>This annotation describes a
82 class</xs:documentation>
83 </xs:annotation>
84 <xs:complexType>
85 <xs:sequence>
86 <xs:element ref="SourceLine"/>
87 <xs:element ref="Message" minOccurs="0"/>
88 </xs:sequence>
89 <xs:attribute name="classname" type="xs:string"
90 use="required"/>
91 <xs:attribute name="role" type="xs:string" use="optional"/>
92 <xs:attribute name="primary" type="xs:boolean"
93 use="optional"/>
94 </xs:complexType>
95 </xs:element>
96 <xs:element name="Type">
97 <xs:annotation>
98 <xs:documentation>This annotation describes a
99 type</xs:documentation>
100 </xs:annotation>
101 <xs:complexType>
102 <xs:sequence>
103 <xs:element ref="SourceLine" minOccurs="0"/>
104 <xs:element ref="Message" minOccurs="0"/>
105 </xs:sequence>
106 <xs:attribute name="descriptor" type="xs:string"
107 use="required"/>
108 <xs:attribute name="role" type="xs:string" use="optional"/>
109 <xs:attribute name="typeParameters" type="xs:string"
110 use="optional"/>
111 </xs:complexType>
112 </xs:element>
113 <xs:element name="Method">
114 <xs:annotation>
115 <xs:documentation>This annotation describes a
116 method</xs:documentation>
117 </xs:annotation>
118 <xs:complexType>
119 <xs:sequence minOccurs="0">
120 <xs:element ref="SourceLine"/>
121 <xs:element ref="Message" minOccurs="0"/>
122 </xs:sequence>
123 <xs:attribute name="classname" type="xs:string"
124 use="required"/>
125 <xs:attribute name="name" type="xs:string" use="required"/>
126 <xs:attribute name="signature" type="xs:string"
127 use="required"/>
128 <xs:attribute name="isStatic" type="xs:boolean"
129 use="required"/>
130 <xs:attribute name="role" type="xs:string" use="optional"/>
131 <xs:attribute name="primary" type="xs:boolean"
132 use="optional"/>
133 </xs:complexType>
134 </xs:element>
135 <xs:element ref="SourceLine"/>
136 <xs:element name="LocalVariable">
137 <xs:annotation>
138 <xs:documentation>This annotation describes a local
139 variable</xs:documentation>
140 </xs:annotation>
141 <xs:complexType>
142 <xs:sequence>
143 <xs:element ref="Message" minOccurs="0"/>
144 </xs:sequence>
145 <xs:attribute name="name" type="xs:string" use="required"/>
146 <xs:attribute name="register" type="xs:short" use="required"/>
147 <xs:attribute name="pc" type="xs:int" use="required"/>
148 <xs:attribute name="role" type="xs:string" use="required"/>
149 </xs:complexType>
150 </xs:element>
151 <xs:element name="Field">
152 <xs:annotation>
153 <xs:documentation>This annotation describes a
154 field</xs:documentation>
155 </xs:annotation>
156 <xs:complexType>
157 <xs:sequence>
158 <xs:element ref="SourceLine"/>
159 <xs:element ref="Message" minOccurs="0"/>
160 </xs:sequence>
161 <xs:attribute name="classname" type="xs:string"
162 use="required"/>
163 <xs:attribute name="name" type="xs:string" use="required"/>
164 <xs:attribute name="signature" type="xs:string"
165 use="required"/>
166 <xs:attribute name="sourceSignature" type="xs:string"
167 use="optional"/>
168 <xs:attribute name="isStatic" type="xs:boolean"
169 use="required"/>
170 <xs:attribute name="role" type="xs:string" use="optional"/>
171 <xs:attribute name="primary" type="xs:boolean"
172 use="optional"/>
173
174 </xs:complexType>
175 </xs:element>
176 <xs:element name="Int">
177 <xs:complexType>
178 <xs:sequence>
179 <xs:element ref="Message" minOccurs="0"/>
180 </xs:sequence>
181 <xs:attribute name="value" type="xs:long" use="required"/>
182 <xs:attribute name="role" type="xs:string" use="optional"/>
183 </xs:complexType>
184 </xs:element>
185 <xs:element name="String">
186 <xs:complexType>
187 <xs:sequence>
188 <xs:element ref="Message" minOccurs="0"/>
189 </xs:sequence>
190 <xs:attribute name="value" type="xs:string" use="required"/>
191 <xs:attribute name="role" type="xs:string" use="optional"/>
192 </xs:complexType>
193 </xs:element>
194 <xs:element name="Property">
195 <xs:complexType>
196 <xs:attribute name="name" type="xs:string" use="required"/>
197 <xs:attribute name="value" type="xs:string" use="required"/>
198 </xs:complexType>
199 </xs:element>
200 <xs:element name="UserAnnotation" minOccurs="0">
201 <xs:complexType>
202 <xs:simpleContent>
203 <xs:extension base="xs:string">
204 <xs:attribute name="designation" type="designationType"
205 use="optional"/>
206 <xs:attribute name="user" type="xs:string"
207 use="optional"/>
208 <xs:attribute name="needsSync" type="xs:boolean"
205209 use="optional"/>
206210 <xs:attribute name="timestamp" type="xs:unsignedLong"
207 use="optional"/>
208 </xs:extension>
209 </xs:simpleContent>
210 </xs:complexType>
211 </xs:element>
212 </xs:choice>
213
214 </xs:sequence>
215 <xs:attribute name="type" type="xs:string" use="required"/>
216 <xs:attribute name="priority" type="xs:unsignedByte" use="required"/>
217 <xs:attribute name="abbrev" type="xs:string" use="required"/>
218 <xs:attribute name="category" type="xs:string" use="required"/>
219 <xs:attribute name="uid" type="xs:unsignedLong" use="optional"/>
220
221 <xs:attribute name="reviews" type="xs:unsignedInt" use="optional"/>
222 <xs:attribute name="firstSeen" type="xs:string" use="optional"/>
223 <xs:attribute name="consensus" type="xs:string" use="optional"/>
224 <xs:attribute name="isInCloud" type="xs:boolean" use="optional"/>
225
226 <!-- The following only appear with multi-version analysis files -->
227 <xs:attribute name="last" type="xs:unsignedInt" use="optional"/>
211 use="optional"/>
212 </xs:extension>
213 </xs:simpleContent>
214 </xs:complexType>
215 </xs:element>
216 </xs:choice>
217
218 </xs:sequence>
219 <xs:attribute name="type" type="xs:string" use="required"/>
220 <xs:attribute name="priority" type="xs:unsignedByte" use="required"/>
221 <xs:attribute name="abbrev" type="xs:string" use="required"/>
222 <xs:attribute name="category" type="xs:string" use="required"/>
223 <xs:attribute name="uid" type="xs:unsignedLong" use="optional"/>
224
225 <xs:attribute name="reviews" type="xs:unsignedInt" use="optional"/>
226 <xs:attribute name="firstSeen" type="xs:string" use="optional"/>
227 <xs:attribute name="consensus" type="xs:string" use="optional"/>
228 <xs:attribute name="isInCloud" type="xs:boolean" use="optional"/>
229
230 <!-- The following only appear with multi-version analysis files -->
231 <xs:attribute name="last" type="xs:unsignedInt" use="optional"/>
228232 <xs:attribute name="removedByChange" type="xs:boolean" use="optional"/>
229233 <xs:attribute name="first" type="xs:unsignedInt" use="optional"/>
230234 <xs:attribute name="introducedByChange" type="xs:boolean" use="optional"/>
231
232
233 <!-- The following only appear withMessages -->
234 <xs:attribute name="shouldFix" type="xs:boolean" use="optional"/>
235 <xs:attribute name="ageInDays" type="xs:unsignedInt" use="optional"/>
235
236
237 <!-- The following only appear withMessages -->
238 <xs:attribute name="shouldFix" type="xs:boolean" use="optional"/>
239 <xs:attribute name="ageInDays" type="xs:unsignedInt" use="optional"/>
236240 <xs:attribute name="notAProblem" type="xs:boolean" use="optional"/>
237241 <xs:attribute name="instanceHash" type="xs:string" use="optional"/>
238242 <xs:attribute name="instanceOccurrenceNum" type="xs:unsignedInt"
239243 use="optional"/>
240244 <xs:attribute name="instanceOccurrenceMax" type="xs:unsignedInt"
241245 use="optional"/>
242
243 <xs:attribute name="rank" type="xs:unsignedInt" use="optional"/>
244 <xs:attribute name="cweid" type="xs:unsignedInt" use="optional"/>
245
246
247 </xs:complexType>
248 </xs:element>
249 <xs:element name="BugCategory" minOccurs="0" maxOccurs="unbounded">
250 <xs:complexType>
251 <xs:sequence>
252 <xs:element name="Description" type="xs:string"/>
253 <xs:element name="Abbreviation" type="xs:NMTOKEN" minOccurs="0"/>
254 <xs:element name="Details" type="xs:string" minOccurs="0"/>
255 </xs:sequence>
256 <xs:attribute name="category" type="xs:string" use="required"/>
257 </xs:complexType>
258 </xs:element>
259 <xs:element name="BugPattern" minOccurs="0" maxOccurs="unbounded">
260 <xs:complexType>
261 <xs:sequence>
262 <xs:element name="ShortDescription" type="xs:string"/>
263 <xs:element name="Details" type="xs:string"/>
264 </xs:sequence>
265 <xs:attribute name="type" type="xs:string" use="required"/>
266 <xs:attribute name="abbrev" type="xs:string" use="required"/>
267 <xs:attribute name="category" type="xs:string" use="required"/>
268 <xs:attribute name="cweid" type="xs:positiveInteger" use="optional"/>
269 </xs:complexType>
270 </xs:element>
271 <xs:element name="BugCode" minOccurs="0" maxOccurs="unbounded">
272 <xs:complexType>
273 <xs:sequence>
274 <xs:element name="Description" type="xs:string"/>
275 </xs:sequence>
276 <xs:attribute name="abbrev" type="xs:string" use="required"/>
277 <xs:attribute name="cweid" type="xs:positiveInteger" use="optional"/>
278 </xs:complexType>
279 </xs:element>
280 <xs:element name="Errors">
281 <xs:complexType>
282 <xs:sequence>
283 <xs:element name="MissingClass" type="xs:string" minOccurs="0"
284 maxOccurs="unbounded"/>
285 </xs:sequence>
286 <xs:attribute name="errors" type="xs:unsignedInt" use="optional"/>
287 <xs:attribute name="missingClasses" type="xs:unsignedInt" use="optional"/>
288 </xs:complexType>
289 </xs:element>
290
291 <xs:element name="FindBugsSummary">
292 <xs:complexType>
293 <xs:sequence>
294 <xs:element name="FileStats" minOccurs="0" maxOccurs="unbounded">
295 <xs:complexType>
296 <xs:attribute name="path" type="xs:string" use="required"/>
297 <xs:attribute name="bugCount" type="xs:unsignedInt"
298 use="required"/>
299 <xs:attribute name="size" type="xs:unsignedInt" use="optional"/>
300 <xs:attribute name="bugHash" type="xs:string" use="optional"/>
301 </xs:complexType>
302 </xs:element>
303 <xs:element name="PackageStats" minOccurs="0" maxOccurs="unbounded">
304 <xs:complexType>
305 <xs:sequence>
306 <xs:element name="ClassStats" minOccurs="0"
307 maxOccurs="unbounded">
308 <xs:complexType>
309 <xs:attribute name="class" type="xs:string"
310 use="required"/>
311 <xs:attribute name="sourceFile" type="xs:string"
312 use="optional"/>
313 <xs:attribute name="interface" type="xs:boolean"
314 use="required"/>
315 <xs:attribute name="size" type="xs:unsignedLong"
316 use="required"/>
317 <xs:attribute name="bugs" type="xs:unsignedInt"
318 use="required"/>
319 <xs:attribute name="priority_1"
320 type="xs:unsignedInt" use="optional"/>
321 <xs:attribute name="priority_2"
322 type="xs:unsignedInt" use="optional"/>
323 <xs:attribute name="priority_3"
324 type="xs:unsignedInt" use="optional"/>
325 </xs:complexType>
326 </xs:element>
327 </xs:sequence>
328 <xs:attribute name="package" type="xs:string" use="required"/>
329 <xs:attribute name="total_bugs" type="xs:unsignedInt"
330 use="required"/>
331 <xs:attribute name="total_types" type="xs:unsignedInt"
332 use="required"/>
333 <xs:attribute name="total_size" type="xs:unsignedLong"
334 use="required"/>
335 <xs:attribute name="priority_1" type="xs:unsignedInt"
336 use="optional"/>
337 <xs:attribute name="priority_2" type="xs:unsignedInt"
338 use="optional"/>
339 <xs:attribute name="priority_3" type="xs:unsignedInt"
340 use="optional"/>
341 </xs:complexType>
342 </xs:element>
343 <xs:element name="FindBugsProfile" minOccurs="0" maxOccurs="1">
344 <xs:complexType>
345 <xs:sequence>
346 <xs:element name="ClassProfile" minOccurs="0"
347 maxOccurs="unbounded">
348 <xs:complexType>
349 <xs:attribute name="name" type="xs:string"
350 use="required"/>
351 <xs:attribute name="totalMilliseconds"
352 type="xs:unsignedInt" use="required"/>
353 <xs:attribute name="invocations"
354 type="xs:unsignedInt" use="required"/>
355 <xs:attribute name="avgMicrosecondsPerInvocation"
356 type="xs:unsignedInt" use="required"/>
357 <xs:attribute name="maxMicrosecondsPerInvocation"
358 type="xs:unsignedInt" use="optional"/>
359 <xs:attribute name="maxContext" type="xs:string"
360 use="optional"/>
361 <xs:attribute
362 name="standardDeviationMircosecondsPerInvocation"
363 type="xs:unsignedInt" use="optional"/>
364 </xs:complexType>
365 </xs:element>
366 </xs:sequence>
367 </xs:complexType>
368 </xs:element>
369 </xs:sequence>
370 <xs:attribute name="timestamp" type="xs:string" use="required"/>
371 <xs:attribute name="total_classes" type="xs:unsignedInt" use="required"/>
372 <xs:attribute name="referenced_classes" type="xs:unsignedInt" use="optional"/>
373 <xs:attribute name="total_bugs" type="xs:unsignedInt" use="required"/>
374 <xs:attribute name="total_size" type="xs:unsignedInt" use="required"/>
375 <xs:attribute name="num_packages" type="xs:unsignedInt" use="required"/>
376 <xs:attribute name="vm_version" type="xs:string" use="optional"/>
377 <xs:attribute name="cpu_seconds" type="xs:float" use="optional"/>
378 <!-- could move to xs:duration -->
379 <xs:attribute name="clock_seconds" type="xs:float" use="optional"/>
380 <xs:attribute name="peak_mbytes" type="xs:float" use="optional"/>
381 <xs:attribute name="alloc_mbytes" type="xs:float" use="optional"/>
382 <xs:attribute name="gc_seconds" type="xs:float" use="optional"/>
383 <xs:attribute name="priority_1" type="xs:unsignedInt" use="optional"/>
384 <xs:attribute name="priority_2" type="xs:unsignedInt" use="optional"/>
385 <xs:attribute name="priority_3" type="xs:unsignedInt" use="optional"/>
386 </xs:complexType>
387 </xs:element>
388 <xs:element name="SummaryHTML" type="xs:string" minOccurs="0"/>
389 <xs:element name="ClassFeatures">
390 <xs:complexType>
391 <xs:sequence>
392 <xs:element name="ClassFeatureSet" minOccurs="0" maxOccurs="unbounded">
393 <xs:complexType>
394 <xs:sequence>
395 <xs:element name="Feature" minOccurs="0"
396 maxOccurs="unbounded">
397 <xs:complexType>
398 <xs:attribute name="value" type="xs:string"
399 use="required"/>
400 </xs:complexType>
401 </xs:element>
402 </xs:sequence>
403 <xs:attribute name="class" type="xs:string" use="required"/>
404 </xs:complexType>
405 </xs:element>
406 </xs:sequence>
407 </xs:complexType>
408 </xs:element>
409 <xs:element name="History">
410 <xs:complexType>
411 <xs:sequence>
412 <xs:element name="AppVersion" minOccurs="0" maxOccurs="unbounded">
413 <xs:complexType>
414 <xs:attribute name="sequence" type="xs:unsignedInt"
415 use="required"/>
416 <xs:attribute name="timestamp" type="xs:unsignedLong"
417 use="required"/>
418 <xs:attribute name="release" type="xs:string" use="required"/>
419 <xs:attribute name="codeSize" type="xs:unsignedInt"
420 use="required"/>
421 <xs:attribute name="numClasses" type="xs:unsignedInt"
422 use="required"/>
423 </xs:complexType>
424 </xs:element>
425 </xs:sequence>
426 </xs:complexType>
427 </xs:element>
428 </xs:sequence>
429 <xs:attribute name="version" type="xs:string" use="required"/>
430 <xs:attribute name="sequence" type="xs:unsignedInt" use="required"/>
431 <xs:attribute name="timestamp" type="xs:unsignedLong" use="required"/>
432 <xs:attribute name="analysisTimestamp" type="xs:unsignedLong" use="required"/>
433 <xs:attribute name="release" type="xs:string" use="required"/>
434 </xs:complexType>
435 </xs:element>
436 <xs:element name="SourceLine">
437 <xs:complexType>
438 <xs:sequence>
439 <xs:element ref="Message" minOccurs="0"/>
440 </xs:sequence>
441 <xs:attribute name="classname" type="xs:string" use="required"/>
442 <xs:attribute name="start" type="xs:int" use="optional"/>
443 <xs:attribute name="end" type="xs:int" use="optional"/>
444 <xs:attribute name="startBytecode" type="xs:int" use="optional"/>
445 <xs:attribute name="endBytecode" type="xs:int" use="optional"/>
446 <xs:attribute name="sourcefile" type="xs:string" use="optional"/>
447 <xs:attribute name="sourcepath" type="xs:string" use="optional"/>
448 <xs:attribute name="relSourcepath" type="xs:string" use="optional"/>
449 <xs:attribute name="synthetic" type="xs:boolean" use="optional"/>
450 <xs:attribute name="role" type="xs:string" use="optional"/>
451 <xs:attribute name="primary" type="xs:boolean" use="optional"/>
452 </xs:complexType>
453 </xs:element>
454 <xs:element name="Message" type="xs:string"/>
455
456 <xs:simpleType name="designationType">
457 <xs:restriction base="xs:token">
458 <xs:enumeration value="UNCLASSIFIED"/>
459 <xs:enumeration value="BAD_ANALYSIS"/>
460 <xs:enumeration value="NOT_A_BUG"/>
461 <xs:enumeration value="MOSTLY_HARMLESS"/>
462 <xs:enumeration value="SHOULD_FIX"/>
463 <xs:enumeration value="MUST_FIX"/>
464 <xs:enumeration value="I_WILL_FIX"/>
465 <xs:enumeration value="OBSOLETE_CODE"/>
466 </xs:restriction>
467 </xs:simpleType>
246
247 <xs:attribute name="rank" type="xs:unsignedInt" use="optional"/>
248 <xs:attribute name="cweid" type="xs:unsignedInt" use="optional"/>
249
250
251 </xs:complexType>
252 </xs:element>
253 <xs:element name="BugCategory" minOccurs="0" maxOccurs="unbounded">
254 <xs:complexType>
255 <xs:sequence>
256 <xs:element name="Description" type="xs:string"/>
257 <xs:element name="Abbreviation" type="xs:NMTOKEN" minOccurs="0"/>
258 <xs:element name="Details" type="xs:string" minOccurs="0"/>
259 </xs:sequence>
260 <xs:attribute name="category" type="xs:string" use="required"/>
261 </xs:complexType>
262 </xs:element>
263 <xs:element name="BugPattern" minOccurs="0" maxOccurs="unbounded">
264 <xs:complexType>
265 <xs:sequence>
266 <xs:element name="ShortDescription" type="xs:string"/>
267 <xs:element name="Details" type="xs:string"/>
268 </xs:sequence>
269 <xs:attribute name="type" type="xs:string" use="required"/>
270 <xs:attribute name="abbrev" type="xs:string" use="required"/>
271 <xs:attribute name="category" type="xs:string" use="required"/>
272 <xs:attribute name="cweid" type="xs:positiveInteger" use="optional"/>
273 </xs:complexType>
274 </xs:element>
275 <xs:element name="BugCode" minOccurs="0" maxOccurs="unbounded">
276 <xs:complexType>
277 <xs:sequence>
278 <xs:element name="Description" type="xs:string"/>
279 </xs:sequence>
280 <xs:attribute name="abbrev" type="xs:string" use="required"/>
281 <xs:attribute name="cweid" type="xs:positiveInteger" use="optional"/>
282 </xs:complexType>
283 </xs:element>
284 <xs:element name="Errors">
285 <xs:complexType>
286 <xs:sequence>
287 <xs:element name="MissingClass" type="xs:string" minOccurs="0"
288 maxOccurs="unbounded"/>
289 </xs:sequence>
290 <xs:attribute name="errors" type="xs:unsignedInt" use="optional"/>
291 <xs:attribute name="missingClasses" type="xs:unsignedInt" use="optional"/>
292 </xs:complexType>
293 </xs:element>
294
295 <xs:element name="FindBugsSummary">
296 <xs:complexType>
297 <xs:sequence>
298 <xs:element name="FileStats" minOccurs="0" maxOccurs="unbounded">
299 <xs:complexType>
300 <xs:attribute name="path" type="xs:string" use="required"/>
301 <xs:attribute name="bugCount" type="xs:unsignedInt"
302 use="required"/>
303 <xs:attribute name="size" type="xs:unsignedInt" use="optional"/>
304 <xs:attribute name="bugHash" type="xs:string" use="optional"/>
305 </xs:complexType>
306 </xs:element>
307 <xs:element name="PackageStats" minOccurs="0" maxOccurs="unbounded">
308 <xs:complexType>
309 <xs:sequence>
310 <xs:element name="ClassStats" minOccurs="0"
311 maxOccurs="unbounded">
312 <xs:complexType>
313 <xs:attribute name="class" type="xs:string"
314 use="required"/>
315 <xs:attribute name="sourceFile" type="xs:string"
316 use="optional"/>
317 <xs:attribute name="interface" type="xs:boolean"
318 use="required"/>
319 <xs:attribute name="size" type="xs:unsignedLong"
320 use="required"/>
321 <xs:attribute name="bugs" type="xs:unsignedInt"
322 use="required"/>
323 <xs:attribute name="priority_1"
324 type="xs:unsignedInt" use="optional"/>
325 <xs:attribute name="priority_2"
326 type="xs:unsignedInt" use="optional"/>
327 <xs:attribute name="priority_3"
328 type="xs:unsignedInt" use="optional"/>
329 </xs:complexType>
330 </xs:element>
331 </xs:sequence>
332 <xs:attribute name="package" type="xs:string" use="required"/>
333 <xs:attribute name="total_bugs" type="xs:unsignedInt"
334 use="required"/>
335 <xs:attribute name="total_types" type="xs:unsignedInt"
336 use="required"/>
337 <xs:attribute name="total_size" type="xs:unsignedLong"
338 use="required"/>
339 <xs:attribute name="priority_1" type="xs:unsignedInt"
340 use="optional"/>
341 <xs:attribute name="priority_2" type="xs:unsignedInt"
342 use="optional"/>
343 <xs:attribute name="priority_3" type="xs:unsignedInt"
344 use="optional"/>
345 </xs:complexType>
346 </xs:element>
347 <xs:element name="FindBugsProfile" minOccurs="0" maxOccurs="1">
348 <xs:complexType>
349 <xs:sequence>
350 <xs:element name="ClassProfile" minOccurs="0"
351 maxOccurs="unbounded">
352 <xs:complexType>
353 <xs:attribute name="name" type="xs:string"
354 use="required"/>
355 <xs:attribute name="totalMilliseconds"
356 type="xs:unsignedInt" use="required"/>
357 <xs:attribute name="invocations"
358 type="xs:unsignedInt" use="required"/>
359 <xs:attribute name="avgMicrosecondsPerInvocation"
360 type="xs:unsignedInt" use="required"/>
361 <xs:attribute name="maxMicrosecondsPerInvocation"
362 type="xs:unsignedInt" use="optional"/>
363 <xs:attribute name="maxContext" type="xs:string"
364 use="optional"/>
365 <xs:attribute
366 name="standardDeviationMircosecondsPerInvocation"
367 type="xs:unsignedInt" use="optional"/>
368 </xs:complexType>
369 </xs:element>
370 </xs:sequence>
371 </xs:complexType>
372 </xs:element>
373 </xs:sequence>
374 <xs:attribute name="timestamp" type="xs:string" use="required"/>
375 <xs:attribute name="total_classes" type="xs:unsignedInt" use="required"/>
376 <xs:attribute name="referenced_classes" type="xs:unsignedInt" use="optional"/>
377 <xs:attribute name="total_bugs" type="xs:unsignedInt" use="required"/>
378 <xs:attribute name="total_size" type="xs:unsignedInt" use="required"/>
379 <xs:attribute name="num_packages" type="xs:unsignedInt" use="required"/>
380 <xs:attribute name="java_version" type="xs:string" use="optional"/>
381 <xs:attribute name="vm_version" type="xs:string" use="optional"/>
382 <xs:attribute name="cpu_seconds" type="xs:float" use="optional"/>
383 <!-- could move to xs:duration -->
384 <xs:attribute name="clock_seconds" type="xs:float" use="optional"/>
385 <xs:attribute name="peak_mbytes" type="xs:float" use="optional"/>
386 <xs:attribute name="alloc_mbytes" type="xs:float" use="optional"/>
387 <xs:attribute name="gc_seconds" type="xs:float" use="optional"/>
388 <xs:attribute name="priority_1" type="xs:unsignedInt" use="optional"/>
389 <xs:attribute name="priority_2" type="xs:unsignedInt" use="optional"/>
390 <xs:attribute name="priority_3" type="xs:unsignedInt" use="optional"/>
391 </xs:complexType>
392 </xs:element>
393 <xs:element name="SummaryHTML" type="xs:string" minOccurs="0"/>
394 <xs:element name="ClassFeatures">
395 <xs:complexType>
396 <xs:sequence>
397 <xs:element name="ClassFeatureSet" minOccurs="0" maxOccurs="unbounded">
398 <xs:complexType>
399 <xs:sequence>
400 <xs:element name="Feature" minOccurs="0"
401 maxOccurs="unbounded">
402 <xs:complexType>
403 <xs:attribute name="value" type="xs:string"
404 use="required"/>
405 </xs:complexType>
406 </xs:element>
407 </xs:sequence>
408 <xs:attribute name="class" type="xs:string" use="required"/>
409 </xs:complexType>
410 </xs:element>
411 </xs:sequence>
412 </xs:complexType>
413 </xs:element>
414 <xs:element name="History">
415 <xs:complexType>
416 <xs:sequence>
417 <xs:element name="AppVersion" minOccurs="0" maxOccurs="unbounded">
418 <xs:complexType>
419 <xs:attribute name="sequence" type="xs:unsignedInt"
420 use="required"/>
421 <xs:attribute name="timestamp" type="xs:unsignedLong"
422 use="required"/>
423 <xs:attribute name="release" type="xs:string" use="required"/>
424 <xs:attribute name="codeSize" type="xs:unsignedInt"
425 use="required"/>
426 <xs:attribute name="numClasses" type="xs:unsignedInt"
427 use="required"/>
428 </xs:complexType>
429 </xs:element>
430 </xs:sequence>
431 </xs:complexType>
432 </xs:element>
433 </xs:sequence>
434 <xs:attribute name="version" type="xs:string" use="required"/>
435 <xs:attribute name="sequence" type="xs:unsignedInt" use="required"/>
436 <xs:attribute name="timestamp" type="xs:unsignedLong" use="required"/>
437 <xs:attribute name="analysisTimestamp" type="xs:unsignedLong" use="required"/>
438 <xs:attribute name="release" type="xs:string" use="required"/>
439 </xs:complexType>
440 </xs:element>
441 <xs:element name="SourceLine">
442 <xs:complexType>
443 <xs:sequence>
444 <xs:element ref="Message" minOccurs="0"/>
445 </xs:sequence>
446 <xs:attribute name="classname" type="xs:string" use="required"/>
447 <xs:attribute name="start" type="xs:int" use="optional"/>
448 <xs:attribute name="end" type="xs:int" use="optional"/>
449 <xs:attribute name="startBytecode" type="xs:int" use="optional"/>
450 <xs:attribute name="endBytecode" type="xs:int" use="optional"/>
451 <xs:attribute name="sourcefile" type="xs:string" use="optional"/>
452 <xs:attribute name="sourcepath" type="xs:string" use="optional"/>
453 <xs:attribute name="relSourcepath" type="xs:string" use="optional"/>
454 <xs:attribute name="synthetic" type="xs:boolean" use="optional"/>
455 <xs:attribute name="role" type="xs:string" use="optional"/>
456 <xs:attribute name="primary" type="xs:boolean" use="optional"/>
457 </xs:complexType>
458 </xs:element>
459 <xs:element name="Message" type="xs:string"/>
460
461 <xs:simpleType name="designationType">
462 <xs:restriction base="xs:token">
463 <xs:enumeration value="UNCLASSIFIED"/>
464 <xs:enumeration value="BAD_ANALYSIS"/>
465 <xs:enumeration value="NOT_A_BUG"/>
466 <xs:enumeration value="MOSTLY_HARMLESS"/>
467 <xs:enumeration value="SHOULD_FIX"/>
468 <xs:enumeration value="MUST_FIX"/>
469 <xs:enumeration value="I_WILL_FIX"/>
470 <xs:enumeration value="OBSOLETE_CODE"/>
471 </xs:restriction>
472 </xs:simpleType>
468473
469474
470475
472477
473478 <xs:element name="Bug" substitutionGroup="Matcher" type="BugMatcherType" />
474479 <xs:element name="Class" substitutionGroup="Matcher" type="ClassMatcherType" />
480 <xs:element name="Type" substitutionGroup="Matcher" type="TypeMatcherType" />
475481 <xs:element name="FirstVersion" substitutionGroup="Matcher" type="FirstVersionMatcherType" />
476482 <xs:element name="LastVersion" substitutionGroup="Matcher" type="LastVersionMatcherType" />
477483 <xs:element name="Designation" substitutionGroup="Matcher" type="DesignationMatcherType" />
490496
491497
492498
493 <xs:complexType name="MatcherType" abstract="true" ></xs:complexType>
494
495 <xs:complexType name="BugMatcherType">
496 <xs:complexContent>
497 <xs:extension base="MatcherType">
498 <xs:attribute name="code" type="xs:string" use="optional" />
499 <xs:attribute name="pattern" type="xs:string" use="optional" />
500 <xs:attribute name="category" type="xs:string" use="optional" />
501 </xs:extension>
502 </xs:complexContent>
503 </xs:complexType>
504
505 <xs:complexType name="ClassMatcherType">
506 <xs:complexContent>
507 <xs:extension base="MatcherType">
508 <xs:attribute name="name" type="xs:string" use="required" />
509 </xs:extension>
510 </xs:complexContent>
511 </xs:complexType>
512
513 <xs:complexType name="FirstVersionMatcherType">
514 <xs:complexContent>
515 <xs:extension base="MatcherType">
516 <xs:attribute name="value" type="xs:long" use="required" />
517 <xs:attribute name="relOp" type="xs:string" use="required" />
518 </xs:extension>
519 </xs:complexContent>
520 </xs:complexType>
521
522 <xs:complexType name="LastVersionMatcherType">
523 <xs:complexContent>
524 <xs:extension base="MatcherType">
525 <xs:attribute name="value" type="xs:long" use="required" />
526 <xs:attribute name="relOp" type="xs:string" use="required" />
527 </xs:extension>
528 </xs:complexContent>
529 </xs:complexType>
530
531 <xs:complexType name="DesignationMatcherType">
532 <xs:complexContent>
533 <xs:extension base="MatcherType">
534 <xs:attribute name="designation" type="xs:string" use="required" />
535 </xs:extension>
536 </xs:complexContent>
537 </xs:complexType>
538
539 <xs:complexType name="BugCodeMatcherType">
540 <xs:complexContent>
541 <xs:extension base="MatcherType">
542 <xs:attribute name="name" type="xs:string" use="required" />
543 </xs:extension>
544 </xs:complexContent>
545 </xs:complexType>
546 <xs:complexType name="LocalMatcherType">
547 <xs:complexContent>
548 <xs:extension base="MatcherType">
549 <xs:attribute name="name" type="xs:string" use="required" />
550 </xs:extension>
551 </xs:complexContent>
552 </xs:complexType>
553 <xs:complexType name="BugPatternMatcherType">
554 <xs:complexContent>
555 <xs:extension base="MatcherType">
556 <xs:attribute name="name" type="xs:string" use="required" />
557 </xs:extension>
558 </xs:complexContent>
559 </xs:complexType>
560
561 <xs:complexType name="PriorityMatcherType">
562 <xs:complexContent>
563 <xs:extension base="MatcherType">
564 <xs:attribute name="value" type="xs:int" use="required" />
565 </xs:extension>
566 </xs:complexContent>
567 </xs:complexType>
568
569 <xs:complexType name="RankMatcherType">
570 <xs:complexContent>
571 <xs:extension base="MatcherType">
572 <xs:attribute name="value" type="xs:int" use="required" />
573 </xs:extension>
574 </xs:complexContent>
575 </xs:complexType>
576
577 <xs:complexType name="PackageMatcherType">
578 <xs:complexContent>
579 <xs:extension base="MatcherType">
580 <xs:attribute name="name" type="xs:string" use="required" />
581 </xs:extension>
582 </xs:complexContent>
583 </xs:complexType>
584
585 <xs:complexType name="MethodMatcherType">
586 <xs:complexContent>
587 <xs:extension base="MatcherType">
588 <xs:attribute name="name" type="xs:string" use="optional" />
589 <xs:attribute name="params" type="xs:string" use="optional" />
590 <xs:attribute name="returns" type="xs:string" use="optional" />
591 <xs:attribute name="role" type="xs:string" use="optional" />
592 </xs:extension>
593 </xs:complexContent>
594 </xs:complexType>
595
596 <xs:complexType name="FieldMatcherType">
597 <xs:complexContent>
598 <xs:extension base="MatcherType">
599 <xs:attribute name="name" type="xs:string" use="optional" />
600 <xs:attribute name="type" type="xs:string" use="optional" />
601 </xs:extension>
602 </xs:complexContent>
603 </xs:complexType>
604
605 <xs:complexType name="OrMatcherType">
606 <xs:complexContent>
607 <xs:extension base="MatcherType">
608 <xs:sequence>
609 <xs:element ref="Matcher" minOccurs="1" maxOccurs="unbounded"/>
610 </xs:sequence>
611 </xs:extension>
612 </xs:complexContent>
613 </xs:complexType>
614
615 <xs:complexType name="AndMatcherType">
616 <xs:complexContent>
617 <xs:extension base="MatcherType">
618 <xs:sequence>
619 <xs:element ref="Matcher" minOccurs="1" maxOccurs="unbounded" />
620 </xs:sequence>
621 </xs:extension>
622 </xs:complexContent>
623 </xs:complexType>
624 <xs:complexType name="MatchMatcherType">
625 <xs:complexContent>
626 <xs:extension base="MatcherType">
627 <xs:sequence>
628 <xs:element ref="Matcher" minOccurs="1" maxOccurs="unbounded" />
629 </xs:sequence>
630 <xs:attribute name="classregex" type="xs:string" use="optional" />
631 <xs:attribute name="class" type="xs:string" use="optional" />
632 </xs:extension>
633 </xs:complexContent>
634 </xs:complexType>
635
636 <xs:complexType name="NotMatcherType">
637 <xs:complexContent>
638 <xs:extension base="MatcherType">
639 <xs:sequence>
640 <xs:element ref="Matcher" minOccurs="1" maxOccurs="1" />
641 </xs:sequence>
642 </xs:extension>
643 </xs:complexContent>
644 </xs:complexType>
645
499 <xs:complexType name="MatcherType" abstract="true" ></xs:complexType>
500
501 <xs:complexType name="BugMatcherType">
502 <xs:complexContent>
503 <xs:extension base="MatcherType">
504 <xs:attribute name="code" type="xs:string" use="optional" />
505 <xs:attribute name="pattern" type="xs:string" use="optional" />
506 <xs:attribute name="category" type="xs:string" use="optional" />
507 </xs:extension>
508 </xs:complexContent>
509 </xs:complexType>
510
511 <xs:complexType name="ClassMatcherType">
512 <xs:complexContent>
513 <xs:extension base="MatcherType">
514 <xs:attribute name="name" type="xs:string" use="required" />
515 <xs:attribute name="role" type="xs:string" use="optional" />
516 </xs:extension>
517 </xs:complexContent>
518 </xs:complexType>
519
520 <xs:complexType name="TypeMatcherType">
521 <xs:complexContent>
522 <xs:extension base="MatcherType">
523 <xs:attribute name="descriptor" type="xs:string" use="required" />
524 <xs:attribute name="role" type="xs:string" use="optional" />
525 <xs:attribute name="typeParameters" type="xs:string" use="optional" />
526 </xs:extension>
527 </xs:complexContent>
528 </xs:complexType>
529
530 <xs:complexType name="FirstVersionMatcherType">
531 <xs:complexContent>
532 <xs:extension base="MatcherType">
533 <xs:attribute name="value" type="xs:long" use="required" />
534 <xs:attribute name="relOp" type="xs:string" use="required" />
535 </xs:extension>
536 </xs:complexContent>
537 </xs:complexType>
538
539 <xs:complexType name="LastVersionMatcherType">
540 <xs:complexContent>
541 <xs:extension base="MatcherType">
542 <xs:attribute name="value" type="xs:long" use="required" />
543 <xs:attribute name="relOp" type="xs:string" use="required" />
544 </xs:extension>
545 </xs:complexContent>
546 </xs:complexType>
547
548 <xs:complexType name="DesignationMatcherType">
549 <xs:complexContent>
550 <xs:extension base="MatcherType">
551 <xs:attribute name="designation" type="xs:string" use="required" />
552 </xs:extension>
553 </xs:complexContent>
554 </xs:complexType>
555
556 <xs:complexType name="BugCodeMatcherType">
557 <xs:complexContent>
558 <xs:extension base="MatcherType">
559 <xs:attribute name="name" type="xs:string" use="required" />
560 </xs:extension>
561 </xs:complexContent>
562 </xs:complexType>
563 <xs:complexType name="LocalMatcherType">
564 <xs:complexContent>
565 <xs:extension base="MatcherType">
566 <xs:attribute name="name" type="xs:string" use="required" />
567 </xs:extension>
568 </xs:complexContent>
569 </xs:complexType>
570 <xs:complexType name="BugPatternMatcherType">
571 <xs:complexContent>
572 <xs:extension base="MatcherType">
573 <xs:attribute name="name" type="xs:string" use="required" />
574 </xs:extension>
575 </xs:complexContent>
576 </xs:complexType>
577
578 <xs:complexType name="PriorityMatcherType">
579 <xs:complexContent>
580 <xs:extension base="MatcherType">
581 <xs:attribute name="value" type="xs:int" use="required" />
582 </xs:extension>
583 </xs:complexContent>
584 </xs:complexType>
585
586 <xs:complexType name="RankMatcherType">
587 <xs:complexContent>
588 <xs:extension base="MatcherType">
589 <xs:attribute name="value" type="xs:int" use="required" />
590 </xs:extension>
591 </xs:complexContent>
592 </xs:complexType>
593
594 <xs:complexType name="PackageMatcherType">
595 <xs:complexContent>
596 <xs:extension base="MatcherType">
597 <xs:attribute name="name" type="xs:string" use="required" />
598 </xs:extension>
599 </xs:complexContent>
600 </xs:complexType>
601
602 <xs:complexType name="MethodMatcherType">
603 <xs:complexContent>
604 <xs:extension base="MatcherType">
605 <xs:attribute name="name" type="xs:string" use="optional" />
606 <xs:attribute name="params" type="xs:string" use="optional" />
607 <xs:attribute name="returns" type="xs:string" use="optional" />
608 <xs:attribute name="role" type="xs:string" use="optional" />
609 </xs:extension>
610 </xs:complexContent>
611 </xs:complexType>
612
613 <xs:complexType name="FieldMatcherType">
614 <xs:complexContent>
615 <xs:extension base="MatcherType">
616 <xs:attribute name="name" type="xs:string" use="optional" />
617 <xs:attribute name="type" type="xs:string" use="optional" />
618 <xs:attribute name="role" type="xs:string" use="optional" />
619 </xs:extension>
620 </xs:complexContent>
621 </xs:complexType>
622
623 <xs:complexType name="OrMatcherType">
624 <xs:complexContent>
625 <xs:extension base="MatcherType">
626 <xs:sequence>
627 <xs:element ref="Matcher" minOccurs="1" maxOccurs="unbounded"/>
628 </xs:sequence>
629 </xs:extension>
630 </xs:complexContent>
631 </xs:complexType>
632
633 <xs:complexType name="AndMatcherType">
634 <xs:complexContent>
635 <xs:extension base="MatcherType">
636 <xs:sequence>
637 <xs:element ref="Matcher" minOccurs="1" maxOccurs="unbounded" />
638 </xs:sequence>
639 </xs:extension>
640 </xs:complexContent>
641 </xs:complexType>
642 <xs:complexType name="MatchMatcherType">
643 <xs:complexContent>
644 <xs:extension base="MatcherType">
645 <xs:sequence>
646 <xs:element ref="Matcher" minOccurs="1" maxOccurs="unbounded" />
647 </xs:sequence>
648 <xs:attribute name="classregex" type="xs:string" use="optional" />
649 <xs:attribute name="class" type="xs:string" use="optional" />
650 </xs:extension>
651 </xs:complexContent>
652 </xs:complexType>
653
654 <xs:complexType name="NotMatcherType">
655 <xs:complexContent>
656 <xs:extension base="MatcherType">
657 <xs:sequence>
658 <xs:element ref="Matcher" minOccurs="1" maxOccurs="1" />
659 </xs:sequence>
660 </xs:extension>
661 </xs:complexContent>
662 </xs:complexType>
663
646664 </xs:schema>
00 +2 BugPattern SF_SWITCH_NO_DEFAULT
11 -2 BugPattern RV_RETURN_VALUE_IGNORED
2 -3 BugPattern DM_INVALID_MIN_MAX
23 -4 BugPattern RV_EXCEPTION_NOT_THROWN
34 -2 BugPattern NP_LOAD_OF_KNOWN_NULL_VALUE
45 +1 BugPattern EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC
2526 -6 BugPattern NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT
2627 +5 BugPattern SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING
2728 -4 BugPattern ICAST_BAD_SHIFT_AMOUNT
29 +5 BugPattern BSHIFT_WRONG_ADD_PRIORITY
2830 -2 BugPattern DMI_INVOKING_HASHCODE_ON_ARRAY
2931 +3 BugPattern UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR
3032 +4 BugPattern VA_FORMAT_STRING_ILLEGAL
5254 +5 BugPattern SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE
5355 +4 BugPattern RE_BAD_SYNTAX_FOR_REGULAR_EXPRESSION
5456 -2 BugPattern NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE
55 -3 BugPattern INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE
5657 -8 BugPattern LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE
58 +2 BugPattern RANGE_STRING_INDEX
5759 +2 BugPattern ICAST_INT_CAST_TO_FLOAT_PASSED_TO_ROUND
5860 +2 BugPattern RV_ABSOLUTE_VALUE_OF_HASHCODE
5961 -3 BugPattern RE_POSSIBLE_UNINTENDED_PATTERN
108110 -2 BugPattern PZ_DONT_REUSE_ENTRY_OBJECTS_IN_ITERATORS
109111 -2 BugPattern DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS
110112 +3 BugPattern RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE
113 +5 BugPattern CAA_COVARIANT_ARRAY_ELEMENT_STORE
114 -1 BugPattern UC_USELESS_CONDITION
115 -1 BugPattern UC_USELESS_CONDITION_TYPE
111116 -4 BugKind FB
112117 -4 BugKind EC
113118 +1 BugKind RCN
(No changes)
55 <tr><td>&nbsp;</td></tr>\n\
66 \n\
77 <tr><td><b>Docs and Info</b></td></tr> \n\
8 <tr><td><font size="-1"><a class="sidebar" href="findbugs2.html">FindBugs 2.0</a></font></td></tr> \n\
9 <tr><td><font size="-1"><a class="sidebar" href="demo.html">Demo and data</a></font></td></tr> \n\
10 <tr><td><font size="-1"><a class="sidebar" href="users.html">Users and supporters</a></font></td></tr> \n\
11 <tr><td><font size="-1"><a class="sidebar" href="http://findbugs.blogspot.com/">FindBugs blog</a></font></td></tr> \n\
12 <tr><td><font size="-1"><a class="sidebar" href="factSheet.html">Fact sheet</a></font></td></tr> \n\
13 <tr><td><font size="-1"><a class="sidebar" href="manual/index.html">Manual</a></font></td></tr> \n\
14 <tr><td><font size="-1"><a class="sidebar" href="ja/manual/index.html">Manual(ja/&#26085;&#26412;&#35486;)</a></font></td></tr> \n\
15 <tr><td><font size="-1"><a class="sidebar" href="FAQ.html">FAQ</a></font></td></tr> \n\
16 <tr><td><font size="-1"><a class="sidebar" href="bugDescriptions.html">Bug descriptions</a></font></td></tr> \n\
17 <tr><td><font size="-1"><a class="sidebar" href="mailingLists.html">Mailing lists</a></font></td></tr> \n\
18 <tr><td><font size="-1"><a class="sidebar" href="publications.html">Documents and Publications</a></font></td></tr> \n\
19 <tr><td><font size="-1"><a class="sidebar" href="links.html">Links</a></font></td></tr> \n\
8 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="findbugs2.html">FindBugs 2.0</a></font></td></tr> \n\
9 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="demo.html">Demo and data</a></font></td></tr> \n\
10 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="users.html">Users and supporters</a></font></td></tr> \n\
11 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="http://findbugs.blogspot.com/">FindBugs blog</a></font></td></tr> \n\
12 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="factSheet.html">Fact sheet</a></font></td></tr> \n\
13 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="manual/index.html">Manual</a></font></td></tr> \n\
14 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="ja/manual/index.html">Manual(ja/&#26085;&#26412;&#35486;)</a></font></td></tr> \n\
15 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="FAQ.html">FAQ</a></font></td></tr> \n\
16 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="bugDescriptions.html">Bug descriptions</a></font></td></tr> \n\
17 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="bugDescriptions_ja.html">Bug descriptions(ja/&#26085;&#26412;&#35486;)</a></font></td></tr> \n\
18 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="bugDescriptions_fr.html">Bug descriptions(fr)</a></font></td></tr> \n\
19 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="mailingLists.html">Mailing lists</a></font></td></tr> \n\
20 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="publications.html">Documents and Publications</a></font></td></tr> \n\
21 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="links.html">Links</a></font></td></tr> \n\
2022 \n\
2123 <tr><td>&nbsp;</td></tr>\n\
2224 \n\
2931 <tr><td>&nbsp;</td></tr>\n\
3032 \n\
3133 <tr><td><b>Development</b></td></tr> \n\
32 <tr><td><font size="-1"><a class="sidebar" href="http://sourceforge.net/tracker/?group_id=96405">Open bugs</a></font></td></tr> \n\
33 <tr><td><font size="-1"><a class="sidebar" href="reportingBugs.html">Reporting bugs</a></font></td></tr> \n\
34 <tr><td><font size="-1"><a class="sidebar" href="contributing.html">Contributing</a></font></td></tr> \n\
35 <tr><td><font size="-1"><a class="sidebar" href="team.html">Dev team</a></font></td></tr> \n\
36 <tr><td><font size="-1"><a class="sidebar" href="api/index.html">API</a> <a class="sidebar" href="api/overview-summary.html">[no frames]</a></font></td></tr> \n\
37 <tr><td><font size="-1"><a class="sidebar" href="Changes.html">Change log</a></font></td></tr> \n\
38 <tr><td><font size="-1"><a class="sidebar" href="http://sourceforge.net/projects/findbugs">SF project page</a></font></td></tr> \n\
39 <tr><td><font size="-1"><a class="sidebar" href="http://code.google.com/p/findbugs/source/browse/">Browse source</a></font></td></tr> \n\
40 <tr><td><font size="-1"><a class="sidebar" href="http://code.google.com/p/findbugs/source/list">Latest code changes</a></font></td></tr> \n\
34 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="http://sourceforge.net/p/findbugs/bugs/">Open bugs</a></font></td></tr> \n\
35 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="reportingBugs.html">Reporting bugs</a></font></td></tr> \n\
36 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="contributing.html">Contributing</a></font></td></tr> \n\
37 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="team.html">Dev team</a></font></td></tr> \n\
38 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="api/index.html">API</a> <a class="sidebar" href="api/overview-summary.html">[no frames]</a></font></td></tr> \n\
39 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="Changes.html">Change log</a></font></td></tr> \n\
40 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="http://sourceforge.net/projects/findbugs">SF project page</a></font></td></tr> \n\
41 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="http://code.google.com/p/findbugs/source/browse/">Browse source</a></font></td></tr> \n\
42 <tr><td><font size="-1">&nbsp;<a class="sidebar" href="http://code.google.com/p/findbugs/source/list">Latest code changes</a></font></td></tr> \n\
4143 </table> \n\
4244 </td>
4345 doc.html.footer= \n\
4951 </script> \n\
5052 <p> Send comments to <a class="sidebar" href="mailto:findbugs@cs.umd.edu">findbugs@cs.umd.edu</a> \n\
5153 <p> \n\
52 <A href="http://sourceforge.net"><IMG src="http://sourceforge.net/sflogo.php?group_id=96405&amp;type=5" width="210" height="62" border="0" alt="SourceForge.net Logo" /></A>
54 <a href="http://sourceforge.net/projects/findbugs/"><img src="http://sourceforge.net/sflogo.php?group_id=96405&amp;type=5" width="210" height="62" border="0" alt="FindBugs on SourceForge.net" /></a>
5355 doc.html.googleanalytics= \n\
5456 <script type="text/javascript"> \n\
5557 var _gaq = _gaq || []; \n\
7676
7777 <!ENTITY % local.ndxterm.class "">
7878 <!ENTITY % ndxterm.class
79 "indexterm %local.ndxterm.class;">
79 "indexterm %local.ndxterm.class;">
8080
8181 <!-- Object-level classes ................................................. -->
8282
8383 <!ENTITY % local.list.class "">
8484 <!ENTITY % list.class
85 "calloutlist|glosslist|itemizedlist|orderedlist|segmentedlist
86 |simplelist|variablelist %local.list.class;">
85 "calloutlist|glosslist|itemizedlist|orderedlist|segmentedlist
86 |simplelist|variablelist %local.list.class;">
8787
8888 <!ENTITY % local.admon.class "">
8989 <!ENTITY % admon.class
90 "caution|important|note|tip|warning %local.admon.class;">
90 "caution|important|note|tip|warning %local.admon.class;">
9191
9292 <!ENTITY % local.linespecific.class "">
9393 <!ENTITY % linespecific.class
94 "literallayout|programlisting|programlistingco|screen
95 |screenco|screenshot %local.linespecific.class;">
94 "literallayout|programlisting|programlistingco|screen
95 |screenco|screenshot %local.linespecific.class;">
9696
9797 <!ENTITY % local.method.synop.class "">
9898 <!ENTITY % method.synop.class
99 "constructorsynopsis
99 "constructorsynopsis
100100 |destructorsynopsis
101101 |methodsynopsis %local.method.synop.class;">
102102
103103 <!ENTITY % local.synop.class "">
104104 <!ENTITY % synop.class
105 "synopsis|cmdsynopsis|funcsynopsis
105 "synopsis|cmdsynopsis|funcsynopsis
106106 |classsynopsis|fieldsynopsis
107107 |%method.synop.class; %local.synop.class;">
108108
109109 <!ENTITY % local.para.class "">
110110 <!ENTITY % para.class
111 "formalpara|para|simpara %local.para.class;">
111 "formalpara|para|simpara %local.para.class;">
112112
113113 <!ENTITY % local.informal.class "">
114114 <!ENTITY % informal.class
115 "address|blockquote
115 "address|blockquote
116116 |graphic|graphicco|mediaobject|mediaobjectco
117117 |informalequation
118 |informalexample
118 |informalexample
119119 |informalfigure
120120 |informaltable %local.informal.class;">
121121
122122 <!ENTITY % local.formal.class "">
123123 <!ENTITY % formal.class
124 "equation|example|figure|table %local.formal.class;">
124 "equation|example|figure|table %local.formal.class;">
125125
126126 <!-- The DocBook TC may produce an official EBNF module for DocBook. -->
127127 <!-- This PE provides the hook by which it can be inserted into the DTD. -->
129129
130130 <!ENTITY % local.compound.class "">
131131 <!ENTITY % compound.class
132 "msgset|procedure|sidebar|qandaset
132 "msgset|procedure|sidebar|qandaset
133133 %ebnf.block.hook;
134134 %local.compound.class;">
135135
136136 <!ENTITY % local.genobj.class "">
137137 <!ENTITY % genobj.class
138 "anchor|bridgehead|remark|highlights
139 %local.genobj.class;">
138 "anchor|bridgehead|remark|highlights
139 %local.genobj.class;">
140140
141141 <!ENTITY % local.descobj.class "">
142142 <!ENTITY % descobj.class
143 "abstract|authorblurb|epigraph
144 %local.descobj.class;">
143 "abstract|authorblurb|epigraph
144 %local.descobj.class;">
145145
146146 <!-- Character-level classes .............................................. -->
147147
148148 <!ENTITY % local.xref.char.class "">
149149 <!ENTITY % xref.char.class
150 "footnoteref|xref %local.xref.char.class;">
150 "footnoteref|xref %local.xref.char.class;">
151151
152152 <!ENTITY % local.gen.char.class "">
153153 <!ENTITY % gen.char.class
154 "abbrev|acronym|citation|citerefentry|citetitle|emphasis
155 |firstterm|foreignphrase|glossterm|footnote|phrase
156 |quote|trademark|wordasword|personname %local.gen.char.class;">
154 "abbrev|acronym|citation|citerefentry|citetitle|emphasis
155 |firstterm|foreignphrase|glossterm|footnote|phrase
156 |quote|trademark|wordasword|personname %local.gen.char.class;">
157157
158158 <!ENTITY % local.link.char.class "">
159159 <!ENTITY % link.char.class
160 "link|olink|ulink %local.link.char.class;">
160 "link|olink|ulink %local.link.char.class;">
161161
162162 <!-- The DocBook TC may produce an official EBNF module for DocBook. -->
163163 <!-- This PE provides the hook by which it can be inserted into the DTD. -->
165165
166166 <!ENTITY % local.tech.char.class "">
167167 <!ENTITY % tech.char.class
168 "action|application
168 "action|application
169169 |classname|methodname|interfacename|exceptionname
170170 |ooclass|oointerface|ooexception
171171 |command|computeroutput
172 |database|email|envar|errorcode|errorname|errortype|errortext|filename
173 |function|guibutton|guiicon|guilabel|guimenu|guimenuitem
174 |guisubmenu|hardware|interface|keycap
175 |keycode|keycombo|keysym|literal|constant|markup|medialabel
176 |menuchoice|mousebutton|option|optional|parameter
177 |prompt|property|replaceable|returnvalue|sgmltag|structfield
178 |structname|symbol|systemitem|token|type|userinput|varname
172 |database|email|envar|errorcode|errorname|errortype|errortext|filename
173 |function|guibutton|guiicon|guilabel|guimenu|guimenuitem
174 |guisubmenu|hardware|interface|keycap
175 |keycode|keycombo|keysym|literal|constant|markup|medialabel
176 |menuchoice|mousebutton|option|optional|parameter
177 |prompt|property|replaceable|returnvalue|sgmltag|structfield
178 |structname|symbol|systemitem|token|type|userinput|varname
179179 %ebnf.inline.hook;
180 %local.tech.char.class;">
180 %local.tech.char.class;">
181181
182182 <!ENTITY % local.base.char.class "">
183183 <!ENTITY % base.char.class
184 "anchor %local.base.char.class;">
184 "anchor %local.base.char.class;">
185185
186186 <!ENTITY % local.docinfo.char.class "">
187187 <!ENTITY % docinfo.char.class
188 "author|authorinitials|corpauthor|modespec|othercredit
189 |productname|productnumber|revhistory
190 %local.docinfo.char.class;">
188 "author|authorinitials|corpauthor|modespec|othercredit
189 |productname|productnumber|revhistory
190 %local.docinfo.char.class;">
191191
192192 <!ENTITY % local.other.char.class "">
193193 <!ENTITY % other.char.class
194 "remark|subscript|superscript %local.other.char.class;">
194 "remark|subscript|superscript %local.other.char.class;">
195195
196196 <!ENTITY % local.inlineobj.char.class "">
197197 <!ENTITY % inlineobj.char.class
198 "inlinegraphic|inlinemediaobject|inlineequation %local.inlineobj.char.class;">
198 "inlinegraphic|inlinemediaobject|inlineequation %local.inlineobj.char.class;">
199199
200200 <!-- ...................................................................... -->
201201 <!-- Entities for content models .......................................... -->
240240
241241 <!ENTITY % local.component.mix "">
242242 <!ENTITY % component.mix
243 "%list.class; |%admon.class;
244 |%linespecific.class; |%synop.class;
245 |%para.class; |%informal.class;
246 |%formal.class; |%compound.class;
247 |%genobj.class; |%descobj.class;
248 |%ndxterm.class; |beginpage
249 %local.component.mix;">
243 "%list.class; |%admon.class;
244 |%linespecific.class; |%synop.class;
245 |%para.class; |%informal.class;
246 |%formal.class; |%compound.class;
247 |%genobj.class; |%descobj.class;
248 |%ndxterm.class; |beginpage
249 %local.component.mix;">
250250
251251 <!ENTITY % local.sidebar.mix "">
252252 <!ENTITY % sidebar.mix
253 "%list.class; |%admon.class;
254 |%linespecific.class; |%synop.class;
255 |%para.class; |%informal.class;
256 |%formal.class; |procedure
257 |%genobj.class;
258 |%ndxterm.class; |beginpage
259 %local.sidebar.mix;">
253 "%list.class; |%admon.class;
254 |%linespecific.class; |%synop.class;
255 |%para.class; |%informal.class;
256 |%formal.class; |procedure
257 |%genobj.class;
258 |%ndxterm.class; |beginpage
259 %local.sidebar.mix;">
260260
261261 <!ENTITY % local.qandaset.mix "">
262262 <!ENTITY % qandaset.mix
263 "%list.class; |%admon.class;
264 |%linespecific.class; |%synop.class;
265 |%para.class; |%informal.class;
266 |%formal.class; |procedure
267 |%genobj.class;
268 |%ndxterm.class;
269 %local.qandaset.mix;">
263 "%list.class; |%admon.class;
264 |%linespecific.class; |%synop.class;
265 |%para.class; |%informal.class;
266 |%formal.class; |procedure
267 |%genobj.class;
268 |%ndxterm.class;
269 %local.qandaset.mix;">
270270
271271 <!ENTITY % local.revdescription.mix "">
272272 <!ENTITY % revdescription.mix
273 "%list.class; |%admon.class;
274 |%linespecific.class; |%synop.class;
275 |%para.class; |%informal.class;
276 |%formal.class; |procedure
277 |%genobj.class;
278 |%ndxterm.class;
279 %local.revdescription.mix;">
273 "%list.class; |%admon.class;
274 |%linespecific.class; |%synop.class;
275 |%para.class; |%informal.class;
276 |%formal.class; |procedure
277 |%genobj.class;
278 |%ndxterm.class;
279 %local.revdescription.mix;">
280280
281281 <!ENTITY % local.footnote.mix "">
282282 <!ENTITY % footnote.mix
283 "%list.class;
284 |%linespecific.class; |%synop.class;
285 |%para.class; |%informal.class;
286 %local.footnote.mix;">
283 "%list.class;
284 |%linespecific.class; |%synop.class;
285 |%para.class; |%informal.class;
286 %local.footnote.mix;">
287287
288288 <!ENTITY % local.example.mix "">
289289 <!ENTITY % example.mix
290 "%list.class;
291 |%linespecific.class; |%synop.class;
292 |%para.class; |%informal.class;
293 |%ndxterm.class; |beginpage
294 %local.example.mix;">
290 "%list.class;
291 |%linespecific.class; |%synop.class;
292 |%para.class; |%informal.class;
293 |%ndxterm.class; |beginpage
294 %local.example.mix;">
295295
296296 <!ENTITY % local.highlights.mix "">
297297 <!ENTITY % highlights.mix
298 "%list.class; |%admon.class;
299 |%para.class;
300 |%ndxterm.class;
301 %local.highlights.mix;">
298 "%list.class; |%admon.class;
299 |%para.class;
300 |%ndxterm.class;
301 %local.highlights.mix;">
302302
303303 <!-- %formal.class; is explicitly excluded from many contexts in which
304304 paragraphs are used -->
305305 <!ENTITY % local.para.mix "">
306306 <!ENTITY % para.mix
307 "%list.class; |%admon.class;
308 |%linespecific.class;
309 |%informal.class;
310 |%formal.class;
311 %local.para.mix;">
307 "%list.class; |%admon.class;
308 |%linespecific.class;
309 |%informal.class;
310 |%formal.class;
311 %local.para.mix;">
312312
313313 <!ENTITY % local.admon.mix "">
314314 <!ENTITY % admon.mix
315 "%list.class;
316 |%linespecific.class; |%synop.class;
317 |%para.class; |%informal.class;
318 |%formal.class; |procedure|sidebar
319 |anchor|bridgehead|remark
320 |%ndxterm.class; |beginpage
321 %local.admon.mix;">
315 "%list.class;
316 |%linespecific.class; |%synop.class;
317 |%para.class; |%informal.class;
318 |%formal.class; |procedure|sidebar
319 |anchor|bridgehead|remark
320 |%ndxterm.class; |beginpage
321 %local.admon.mix;">
322322
323323 <!ENTITY % local.figure.mix "">
324324 <!ENTITY % figure.mix
325 "%linespecific.class; |%synop.class;
326 |%informal.class;
327 |%ndxterm.class; |beginpage
328 %local.figure.mix;">
325 "%linespecific.class; |%synop.class;
326 |%informal.class;
327 |%ndxterm.class; |beginpage
328 %local.figure.mix;">
329329
330330 <!ENTITY % local.tabentry.mix "">
331331 <!ENTITY % tabentry.mix
332 "%list.class; |%admon.class;
333 |%linespecific.class;
334 |%para.class; |graphic|mediaobject
335 %local.tabentry.mix;">
332 "%list.class; |%admon.class;
333 |%linespecific.class;
334 |%para.class; |graphic|mediaobject
335 %local.tabentry.mix;">
336336
337337 <!ENTITY % local.glossdef.mix "">
338338 <!ENTITY % glossdef.mix
339 "%list.class;
340 |%linespecific.class; |%synop.class;
341 |%para.class; |%informal.class;
342 |%formal.class;
343 |remark
344 |%ndxterm.class; |beginpage
345 %local.glossdef.mix;">
339 "%list.class;
340 |%linespecific.class; |%synop.class;
341 |%para.class; |%informal.class;
342 |%formal.class;
343 |remark
344 |%ndxterm.class; |beginpage
345 %local.glossdef.mix;">
346346
347347 <!ENTITY % local.legalnotice.mix "">
348348 <!ENTITY % legalnotice.mix
349 "%list.class; |%admon.class;
350 |%linespecific.class;
351 |%para.class; |blockquote
352 |%ndxterm.class; |beginpage
353 %local.legalnotice.mix;">
349 "%list.class; |%admon.class;
350 |%linespecific.class;
351 |%para.class; |blockquote
352 |%ndxterm.class; |beginpage
353 %local.legalnotice.mix;">
354354
355355 <!ENTITY % local.textobject.mix "">
356356 <!ENTITY % textobject.mix
357 "%list.class; |%admon.class;
358 |%linespecific.class;
359 |%para.class; |blockquote
360 %local.textobject.mix;">
357 "%list.class; |%admon.class;
358 |%linespecific.class;
359 |%para.class; |blockquote
360 %local.textobject.mix;">
361361
362362 <!ENTITY % local.mediaobject.mix "">
363363 <!ENTITY % mediaobject.mix
364 "videoobject|audioobject|imageobject|textobject %local.mediaobject.mix;">
364 "videoobject|audioobject|imageobject|textobject %local.mediaobject.mix;">
365365
366366 <!ENTITY % local.listpreamble.mix "">
367367 <!ENTITY % listpreamble.mix
368 " %admon.class;
369 |%linespecific.class; |%synop.class;
370 |%para.class; |%informal.class;
371 |%genobj.class; |%descobj.class;
372 |%ndxterm.class; |beginpage
373 %local.listpreamble.mix;">
368 " %admon.class;
369 |%linespecific.class; |%synop.class;
370 |%para.class; |%informal.class;
371 |%genobj.class; |%descobj.class;
372 |%ndxterm.class; |beginpage
373 %local.listpreamble.mix;">
374374
375375 <!-- Character-level mixtures ............................................. -->
376376
436436
437437 <!ENTITY % local.para.char.mix "">
438438 <!ENTITY % para.char.mix
439 "#PCDATA
440 |%xref.char.class; |%gen.char.class;
441 |%link.char.class; |%tech.char.class;
442 |%base.char.class; |%docinfo.char.class;
443 |%other.char.class; |%inlineobj.char.class;
444 |%synop.class;
445 |%ndxterm.class; |beginpage
439 "#PCDATA
440 |%xref.char.class; |%gen.char.class;
441 |%link.char.class; |%tech.char.class;
442 |%base.char.class; |%docinfo.char.class;
443 |%other.char.class; |%inlineobj.char.class;
444 |%synop.class;
445 |%ndxterm.class; |beginpage
446446 %forminlines.hook;
447 %local.para.char.mix;">
447 %local.para.char.mix;">
448448
449449 <!ENTITY % local.title.char.mix "">
450450 <!ENTITY % title.char.mix
451 "#PCDATA
452 |%xref.char.class; |%gen.char.class;
453 |%link.char.class; |%tech.char.class;
454 |%base.char.class; |%docinfo.char.class;
455 |%other.char.class; |%inlineobj.char.class;
456 |%ndxterm.class;
457 %local.title.char.mix;">
451 "#PCDATA
452 |%xref.char.class; |%gen.char.class;
453 |%link.char.class; |%tech.char.class;
454 |%base.char.class; |%docinfo.char.class;
455 |%other.char.class; |%inlineobj.char.class;
456 |%ndxterm.class;
457 %local.title.char.mix;">
458458
459459 <!ENTITY % local.ndxterm.char.mix "">
460460 <!ENTITY % ndxterm.char.mix
461 "#PCDATA
462 |%xref.char.class; |%gen.char.class;
463 |%link.char.class; |%tech.char.class;
464 |%base.char.class; |%docinfo.char.class;
465 |%other.char.class; |inlinegraphic|inlinemediaobject
466 %local.ndxterm.char.mix;">
461 "#PCDATA
462 |%xref.char.class; |%gen.char.class;
463 |%link.char.class; |%tech.char.class;
464 |%base.char.class; |%docinfo.char.class;
465 |%other.char.class; |inlinegraphic|inlinemediaobject
466 %local.ndxterm.char.mix;">
467467
468468 <!ENTITY % local.cptr.char.mix "">
469469 <!ENTITY % cptr.char.mix
470 "#PCDATA
471 |%link.char.class; |%tech.char.class;
472 |%base.char.class;
473 |%other.char.class; |inlinegraphic|inlinemediaobject
474 |%ndxterm.class; |beginpage
475 %local.cptr.char.mix;">
470 "#PCDATA
471 |%link.char.class; |%tech.char.class;
472 |%base.char.class;
473 |%other.char.class; |inlinegraphic|inlinemediaobject
474 |%ndxterm.class; |beginpage
475 %local.cptr.char.mix;">
476476
477477 <!ENTITY % local.smallcptr.char.mix "">
478478 <!ENTITY % smallcptr.char.mix
479 "#PCDATA
480 |replaceable
481 |inlinegraphic|inlinemediaobject
482 |%ndxterm.class; |beginpage
483 %local.smallcptr.char.mix;">
479 "#PCDATA
480 |replaceable
481 |inlinegraphic|inlinemediaobject
482 |%ndxterm.class; |beginpage
483 %local.smallcptr.char.mix;">
484484
485485 <!ENTITY % local.word.char.mix "">
486486 <!ENTITY % word.char.mix
487 "#PCDATA
488 |acronym|emphasis|trademark
489 |%link.char.class;
490 |%base.char.class;
491 |%other.char.class; |inlinegraphic|inlinemediaobject
492 |%ndxterm.class; |beginpage
493 %local.word.char.mix;">
487 "#PCDATA
488 |acronym|emphasis|trademark
489 |%link.char.class;
490 |%base.char.class;
491 |%other.char.class; |inlinegraphic|inlinemediaobject
492 |%ndxterm.class; |beginpage
493 %local.word.char.mix;">
494494
495495 <!ENTITY % local.docinfo.char.mix "">
496496 <!ENTITY % docinfo.char.mix
497 "#PCDATA
498 |%link.char.class;
499 |emphasis|trademark
500 |replaceable
501 |%other.char.class; |inlinegraphic|inlinemediaobject
502 |%ndxterm.class;
503 %local.docinfo.char.mix;">
497 "#PCDATA
498 |%link.char.class;
499 |emphasis|trademark
500 |replaceable
501 |%other.char.class; |inlinegraphic|inlinemediaobject
502 |%ndxterm.class;
503 %local.docinfo.char.mix;">
504504 <!--ENTITY % bibliocomponent.mix (see Bibliographic section, below)-->
505505 <!--ENTITY % person.ident.mix (see Bibliographic section, below)-->
506506
511511
512512
513513 <!-- Arch: Computer or chip architecture to which element applies; no
514 default -->
514 default -->
515515
516516 <!ENTITY % arch.attrib
517 "arch CDATA #IMPLIED">
517 "arch CDATA #IMPLIED">
518518
519519 <!-- Condition: General-purpose effectivity attribute -->
520520
521521 <!ENTITY % condition.attrib
522 "condition CDATA #IMPLIED">
522 "condition CDATA #IMPLIED">
523523
524524 <!-- Conformance: Standards conformance characteristics -->
525525
526526 <!ENTITY % conformance.attrib
527 "conformance NMTOKENS #IMPLIED">
527 "conformance NMTOKENS #IMPLIED">
528528
529529
530530 <!-- OS: Operating system to which element applies; no default -->
531531
532532 <!ENTITY % os.attrib
533 "os CDATA #IMPLIED">
533 "os CDATA #IMPLIED">
534534
535535
536536 <!-- Revision: Editorial revision to which element belongs; no default -->
537537
538538 <!ENTITY % revision.attrib
539 "revision CDATA #IMPLIED">
539 "revision CDATA #IMPLIED">
540540
541541 <!-- Security: Security classification; no default -->
542542
543543 <!ENTITY % security.attrib
544 "security CDATA #IMPLIED">
544 "security CDATA #IMPLIED">
545545
546546 <!-- UserLevel: Level of user experience to which element applies; no
547 default -->
547 default -->
548548
549549 <!ENTITY % userlevel.attrib
550 "userlevel CDATA #IMPLIED">
550 "userlevel CDATA #IMPLIED">
551551
552552
553553 <!-- Vendor: Computer vendor to which element applies; no default -->
554554
555555 <!ENTITY % vendor.attrib
556 "vendor CDATA #IMPLIED">
556 "vendor CDATA #IMPLIED">
557557
558558 <!ENTITY % local.effectivity.attrib "">
559559 <!ENTITY % effectivity.attrib
560 "%arch.attrib;
560 "%arch.attrib;
561561 %condition.attrib;
562 %conformance.attrib;
563 %os.attrib;
564 %revision.attrib;
562 %conformance.attrib;
563 %os.attrib;
564 %revision.attrib;
565565 %security.attrib;
566 %userlevel.attrib;
567 %vendor.attrib;
568 %local.effectivity.attrib;"
566 %userlevel.attrib;
567 %vendor.attrib;
568 %local.effectivity.attrib;"
569569 >
570570
571571 <!-- Common attributes .................................................... -->
574574 <!-- Id: Unique identifier of element; no default -->
575575
576576 <!ENTITY % id.attrib
577 "id ID #IMPLIED">
577 "id ID #IMPLIED">
578578
579579
580580 <!-- Id: Unique identifier of element; a value must be supplied; no
581 default -->
581 default -->
582582
583583 <!ENTITY % idreq.attrib
584 "id ID #REQUIRED">
584 "id ID #REQUIRED">
585585
586586
587587 <!-- Lang: Indicator of language in which element is written, for
588 translation, character set management, etc.; no default -->
588 translation, character set management, etc.; no default -->
589589
590590 <!ENTITY % lang.attrib
591 "lang CDATA #IMPLIED">
591 "lang CDATA #IMPLIED">
592592
593593
594594 <!-- Remap: Previous role of element before conversion; no default -->
595595
596596 <!ENTITY % remap.attrib
597 "remap CDATA #IMPLIED">
597 "remap CDATA #IMPLIED">
598598
599599
600600 <!-- Role: New role of element in local environment; no default -->
601601
602602 <!ENTITY % role.attrib
603 "role CDATA #IMPLIED">
603 "role CDATA #IMPLIED">
604604
605605
606606 <!-- XRefLabel: Alternate labeling string for XRef text generation;
607 default is usually title or other appropriate label text already
608 contained in element -->
607 default is usually title or other appropriate label text already
608 contained in element -->
609609
610610 <!ENTITY % xreflabel.attrib
611 "xreflabel CDATA #IMPLIED">
611 "xreflabel CDATA #IMPLIED">
612612
613613
614614 <!-- RevisionFlag: Revision status of element; default is that element
615 wasn't revised -->
615 wasn't revised -->
616616
617617 <!ENTITY % revisionflag.attrib
618 "revisionflag (changed
619 |added
620 |deleted
621 |off) #IMPLIED">
618 "revisionflag (changed
619 |added
620 |deleted
621 |off) #IMPLIED">
622622
623623 <!ENTITY % local.common.attrib "">
624624
625625 <!-- Role is included explicitly on each element -->
626626
627627 <!ENTITY % common.attrib
628 "%id.attrib;
629 %lang.attrib;
630 %remap.attrib;
631 %xreflabel.attrib;
632 %revisionflag.attrib;
633 %effectivity.attrib;
634 %local.common.attrib;"
628 "%id.attrib;
629 %lang.attrib;
630 %remap.attrib;
631 %xreflabel.attrib;
632 %revisionflag.attrib;
633 %effectivity.attrib;
634 %local.common.attrib;"
635635 >
636636
637637
638638 <!-- Role is included explicitly on each element -->
639639
640640 <!ENTITY % idreq.common.attrib
641 "%idreq.attrib;
642 %lang.attrib;
643 %remap.attrib;
644 %xreflabel.attrib;
645 %revisionflag.attrib;
646 %effectivity.attrib;
647 %local.common.attrib;"
641 "%idreq.attrib;
642 %lang.attrib;
643 %remap.attrib;
644 %xreflabel.attrib;
645 %revisionflag.attrib;
646 %effectivity.attrib;
647 %local.common.attrib;"
648648 >
649649
650650 <!-- Semi-common attributes and other attribute entities .................. -->
652652 <!ENTITY % local.graphics.attrib "">
653653
654654 <!-- EntityRef: Name of an external entity containing the content
655 of the graphic -->
655 of the graphic -->
656656 <!-- FileRef: Filename, qualified by a pathname if desired,
657 designating the file containing the content of the graphic -->
657 designating the file containing the content of the graphic -->
658658 <!-- Format: Notation of the element content, if any -->
659659 <!-- SrcCredit: Information about the source of the Graphic -->
660660 <!-- Width: Same as CALS reprowid (desired width) -->
661661 <!-- Depth: Same as CALS reprodep (desired depth) -->
662662 <!-- Align: Same as CALS hplace with 'none' removed; #IMPLIED means
663 application-specific -->
663 application-specific -->
664664 <!-- Scale: Conflation of CALS hscale and vscale -->
665665 <!-- Scalefit: Same as CALS scalefit -->
666666
667667 <!ENTITY % graphics.attrib
668 "
669 entityref ENTITY #IMPLIED
670 fileref CDATA #IMPLIED
671 format (%notation.class;) #IMPLIED
672 srccredit CDATA #IMPLIED
673 width CDATA #IMPLIED
674 contentwidth CDATA #IMPLIED
675 depth CDATA #IMPLIED
676 contentdepth CDATA #IMPLIED
677 align (left
678 |right
679 |center) #IMPLIED
680 valign (top
681 |middle
682 |bottom) #IMPLIED
683 scale CDATA #IMPLIED
684 scalefit %yesorno.attvals;
685 #IMPLIED
686 %local.graphics.attrib;"
668 "
669 entityref ENTITY #IMPLIED
670 fileref CDATA #IMPLIED
671 format (%notation.class;) #IMPLIED
672 srccredit CDATA #IMPLIED
673 width CDATA #IMPLIED
674 contentwidth CDATA #IMPLIED
675 depth CDATA #IMPLIED
676 contentdepth CDATA #IMPLIED
677 align (left
678 |right
679 |center) #IMPLIED
680 valign (top
681 |middle
682 |bottom) #IMPLIED
683 scale CDATA #IMPLIED
684 scalefit %yesorno.attvals;
685 #IMPLIED
686 %local.graphics.attrib;"
687687 >
688688
689689 <!ENTITY % local.keyaction.attrib "">
690690
691691 <!-- Action: Key combination type; default is unspecified if one
692 child element, Simul if there is more than one; if value is
693 Other, the OtherAction attribute must have a nonempty value -->
692 child element, Simul if there is more than one; if value is
693 Other, the OtherAction attribute must have a nonempty value -->
694694 <!-- OtherAction: User-defined key combination type -->
695695
696696 <!ENTITY % keyaction.attrib
697 "
698 action (click
699 |double-click
700 |press
701 |seq
702 |simul
703 |other) #IMPLIED
704 otheraction CDATA #IMPLIED
705 %local.keyaction.attrib;"
697 "
698 action (click
699 |double-click
700 |press
701 |seq
702 |simul
703 |other) #IMPLIED
704 otheraction CDATA #IMPLIED
705 %local.keyaction.attrib;"
706706 >
707707
708708
709709 <!-- Label: Identifying number or string; default is usually the
710 appropriate number or string autogenerated by a formatter -->
710 appropriate number or string autogenerated by a formatter -->
711711
712712 <!ENTITY % label.attrib
713 "label CDATA #IMPLIED">
713 "label CDATA #IMPLIED">
714714
715715
716716 <!-- Format: whether element is assumed to contain significant white
717 space -->
717 space -->
718718
719719 <!ENTITY % linespecific.attrib
720 "format NOTATION
721 (linespecific) 'linespecific'
720 "format NOTATION
721 (linespecific) 'linespecific'
722722 linenumbering (numbered|unnumbered) #IMPLIED">
723723
724724
725725 <!-- Linkend: link to related information; no default -->
726726
727727 <!ENTITY % linkend.attrib
728 "linkend IDREF #IMPLIED">
728 "linkend IDREF #IMPLIED">
729729
730730
731731 <!-- Linkend: required link to related information -->
732732
733733 <!ENTITY % linkendreq.attrib
734 "linkend IDREF #REQUIRED">
734 "linkend IDREF #REQUIRED">
735735
736736
737737 <!-- Linkends: link to one or more sets of related information; no
738 default -->
738 default -->
739739
740740 <!ENTITY % linkends.attrib
741 "linkends IDREFS #IMPLIED">
741 "linkends IDREFS #IMPLIED">
742742
743743
744744 <!ENTITY % local.mark.attrib "">
745745 <!ENTITY % mark.attrib
746 "mark CDATA #IMPLIED
747 %local.mark.attrib;"
746 "mark CDATA #IMPLIED
747 %local.mark.attrib;"
748748 >
749749
750750
751751 <!-- MoreInfo: whether element's content has an associated RefEntry -->
752752
753753 <!ENTITY % moreinfo.attrib
754 "moreinfo (refentry|none) 'none'">
754 "moreinfo (refentry|none) 'none'">
755755
756756
757757 <!-- Pagenum: number of page on which element appears; no default -->
758758
759759 <!ENTITY % pagenum.attrib
760 "pagenum CDATA #IMPLIED">
760 "pagenum CDATA #IMPLIED">
761761
762762 <!ENTITY % local.status.attrib "">
763763
764764 <!-- Status: Editorial or publication status of the element
765 it applies to, such as "in review" or "approved for distribution" -->
765 it applies to, such as "in review" or "approved for distribution" -->
766766
767767 <!ENTITY % status.attrib
768 "status CDATA #IMPLIED
769 %local.status.attrib;"
768 "status CDATA #IMPLIED
769 %local.status.attrib;"
770770 >
771771
772772
773773 <!-- Width: width of the longest line in the element to which it
774 pertains, in number of characters -->
774 pertains, in number of characters -->
775775
776776 <!ENTITY % width.attrib
777 "width CDATA #IMPLIED">
777 "width CDATA #IMPLIED">
778778
779779 <!-- ...................................................................... -->
780780 <!-- Title elements ....................................................... -->
792792 <!ENTITY % title.attlist "INCLUDE">
793793 <![%title.attlist;[
794794 <!ATTLIST title
795 %pagenum.attrib;
796 %common.attrib;
797 %title.role.attrib;
798 %local.title.attrib;
795 %pagenum.attrib;
796 %common.attrib;
797 %title.role.attrib;
798 %local.title.attrib;
799799 >
800800 <!--end of title.attlist-->]]>
801801 <!--end of title.module-->]]>
813813 <!ENTITY % titleabbrev.attlist "INCLUDE">
814814 <![%titleabbrev.attlist;[
815815 <!ATTLIST titleabbrev
816 %common.attrib;
817 %titleabbrev.role.attrib;
818 %local.titleabbrev.attrib;
816 %common.attrib;
817 %titleabbrev.role.attrib;
818 %local.titleabbrev.attrib;
819819 >
820820 <!--end of titleabbrev.attlist-->]]>
821821 <!--end of titleabbrev.module-->]]>
833833 <!ENTITY % subtitle.attlist "INCLUDE">
834834 <![%subtitle.attlist;[
835835 <!ATTLIST subtitle
836 %common.attrib;
837 %subtitle.role.attrib;
838 %local.subtitle.attrib;
836 %common.attrib;
837 %subtitle.role.attrib;
838 %local.subtitle.attrib;
839839 >
840840 <!--end of subtitle.attlist-->]]>
841841 <!--end of subtitle.module-->]]>
850850
851851 <!ENTITY % local.person.ident.mix "">
852852 <!ENTITY % person.ident.mix
853 "honorific|firstname|surname|lineage|othername|affiliation
854 |authorblurb|contrib %local.person.ident.mix;">
853 "honorific|firstname|surname|lineage|othername|affiliation
854 |authorblurb|contrib %local.person.ident.mix;">
855855
856856 <!ENTITY % local.bibliocomponent.mix "">
857857 <!ENTITY % bibliocomponent.mix
858 "abbrev|abstract|address|artpagenums|author
859 |authorgroup|authorinitials|bibliomisc|biblioset
860 |collab|confgroup|contractnum|contractsponsor
861 |copyright|corpauthor|corpname|date|edition
862 |editor|invpartnumber|isbn|issn|issuenum|orgname
863 |biblioid|citebiblioid|bibliosource|bibliorelation|bibliocoverage
864 |othercredit|pagenums|printhistory|productname
865 |productnumber|pubdate|publisher|publishername
866 |pubsnumber|releaseinfo|revhistory|seriesvolnums
867 |subtitle|title|titleabbrev|volumenum|citetitle
868 |personname|%person.ident.mix;
869 |%ndxterm.class;
870 %local.bibliocomponent.mix;">
858 "abbrev|abstract|address|artpagenums|author
859 |authorgroup|authorinitials|bibliomisc|biblioset
860 |collab|confgroup|contractnum|contractsponsor
861 |copyright|corpauthor|corpname|date|edition
862 |editor|invpartnumber|isbn|issn|issuenum|orgname
863 |biblioid|citebiblioid|bibliosource|bibliorelation|bibliocoverage
864 |othercredit|pagenums|printhistory|productname
865 |productnumber|pubdate|publisher|publishername
866 |pubsnumber|releaseinfo|revhistory|seriesvolnums
867 |subtitle|title|titleabbrev|volumenum|citetitle
868 |personname|%person.ident.mix;
869 |%ndxterm.class;
870 %local.bibliocomponent.mix;">
871871
872872 <!-- I don't think this is well placed, but it needs to be here because of -->
873873 <!-- the reference to bibliocomponent.mix -->
874874 <!ENTITY % local.info.class "">
875875 <!ENTITY % info.class
876 "graphic | mediaobject | legalnotice | modespec
877 | subjectset | keywordset | itermset | %bibliocomponent.mix;
876 "graphic | mediaobject | legalnotice | modespec
877 | subjectset | keywordset | itermset | %bibliocomponent.mix;
878878 %local.info.class;">
879879
880880 <!ENTITY % biblioentry.module "INCLUDE">
891891 <!ENTITY % biblioentry.attlist "INCLUDE">
892892 <![%biblioentry.attlist;[
893893 <!ATTLIST biblioentry
894 %common.attrib;
895 %biblioentry.role.attrib;
896 %local.biblioentry.attrib;
894 %common.attrib;
895 %biblioentry.role.attrib;
896 %local.biblioentry.attrib;
897897 >
898898 <!--end of biblioentry.attlist-->]]>
899899 <!--end of biblioentry.module-->]]>
912912 <!ENTITY % bibliomixed.attlist "INCLUDE">
913913 <![%bibliomixed.attlist;[
914914 <!ATTLIST bibliomixed
915 %common.attrib;
916 %bibliomixed.role.attrib;
917 %local.bibliomixed.attrib;
915 %common.attrib;
916 %bibliomixed.role.attrib;
917 %local.bibliomixed.attrib;
918918 >
919919 <!--end of bibliomixed.attlist-->]]>
920920 <!--end of bibliomixed.module-->]]>
927927 <!ENTITY % articleinfo.element "INCLUDE">
928928 <![%articleinfo.element;[
929929 <!ELEMENT articleinfo %ho; ((%info.class;)+)
930 %beginpage.exclusion;>
930 %beginpage.exclusion;>
931931 <!--end of articleinfo.element-->]]>
932932
933933 <!ENTITY % articleinfo.attlist "INCLUDE">
934934 <![%articleinfo.attlist;[
935935 <!ATTLIST articleinfo
936 %common.attrib;
937 %articleinfo.role.attrib;
938 %local.articleinfo.attrib;
936 %common.attrib;
937 %articleinfo.role.attrib;
938 %local.articleinfo.attrib;
939939 >
940940 <!--end of articleinfo.attlist-->]]>
941941 <!--end of articleinfo.module-->]]>
957957 <!ENTITY % biblioset.attlist "INCLUDE">
958958 <![%biblioset.attlist;[
959959 <!ATTLIST biblioset
960 relation CDATA #IMPLIED
961 %common.attrib;
962 %biblioset.role.attrib;
963 %local.biblioset.attrib;
960 relation CDATA #IMPLIED
961 %common.attrib;
962 %biblioset.role.attrib;
963 %local.biblioset.attrib;
964964 >
965965 <!--end of biblioset.attlist-->]]>
966966 <!--end of biblioset.module-->]]>
982982 <!ENTITY % bibliomset.attlist "INCLUDE">
983983 <![%bibliomset.attlist;[
984984 <!ATTLIST bibliomset
985 relation CDATA #IMPLIED
986 %common.attrib;
987 %bibliomset.role.attrib;
988 %local.bibliomset.attrib;
985 relation CDATA #IMPLIED
986 %common.attrib;
987 %bibliomset.role.attrib;
988 %local.bibliomset.attrib;
989989 >
990990 <!--end of bibliomset.attlist-->]]>
991991 <!--end of bibliomset.module-->]]>
10031003 <!ENTITY % bibliomisc.attlist "INCLUDE">
10041004 <![%bibliomisc.attlist;[
10051005 <!ATTLIST bibliomisc
1006 %common.attrib;
1007 %bibliomisc.role.attrib;
1008 %local.bibliomisc.attrib;
1006 %common.attrib;
1007 %bibliomisc.role.attrib;
1008 %local.bibliomisc.attrib;
10091009 >
10101010 <!--end of bibliomisc.attlist-->]]>
10111011 <!--end of bibliomisc.module-->]]>
10311031 <!ENTITY % subjectset.attlist "INCLUDE">
10321032 <![%subjectset.attlist;[
10331033 <!ATTLIST subjectset
1034 scheme NMTOKEN #IMPLIED
1035 %common.attrib;
1036 %subjectset.role.attrib;
1037 %local.subjectset.attrib;
1034 scheme NMTOKEN #IMPLIED
1035 %common.attrib;
1036 %subjectset.role.attrib;
1037 %local.subjectset.attrib;
10381038 >
10391039 <!--end of subjectset.attlist-->]]>
10401040 <!--end of subjectset.module-->]]>
10501050 <!--end of subject.element-->]]>
10511051
10521052 <!-- Weight: Ranking of this group of SubjectTerms relative
1053 to others, 0 is low, no highest value specified -->
1053 to others, 0 is low, no highest value specified -->
10541054
10551055
10561056 <!ENTITY % subject.attlist "INCLUDE">
10571057 <![%subject.attlist;[
10581058 <!ATTLIST subject
1059 weight CDATA #IMPLIED
1060 %common.attrib;
1061 %subject.role.attrib;
1062 %local.subject.attrib;
1059 weight CDATA #IMPLIED
1060 %common.attrib;
1061 %subject.role.attrib;
1062 %local.subject.attrib;
10631063 >
10641064 <!--end of subject.attlist-->]]>
10651065 <!--end of subject.module-->]]>
10771077 <!ENTITY % subjectterm.attlist "INCLUDE">
10781078 <![%subjectterm.attlist;[
10791079 <!ATTLIST subjectterm
1080 %common.attrib;
1081 %subjectterm.role.attrib;
1082 %local.subjectterm.attrib;
1080 %common.attrib;
1081 %subjectterm.role.attrib;
1082 %local.subjectterm.attrib;
10831083 >
10841084 <!--end of subjectterm.attlist-->]]>
10851085 <!--end of subjectterm.module-->]]>
11001100 <!ENTITY % keywordset.attlist "INCLUDE">
11011101 <![%keywordset.attlist;[
11021102 <!ATTLIST keywordset
1103 %common.attrib;
1104 %keywordset.role.attrib;
1105 %local.keywordset.attrib;
1103 %common.attrib;
1104 %keywordset.role.attrib;
1105 %local.keywordset.attrib;
11061106 >
11071107 <!--end of keywordset.attlist-->]]>
11081108 <!--end of keywordset.module-->]]>
11201120 <!ENTITY % keyword.attlist "INCLUDE">
11211121 <![%keyword.attlist;[
11221122 <!ATTLIST keyword
1123 %common.attrib;
1124 %keyword.role.attrib;
1125 %local.keyword.attrib;
1123 %common.attrib;
1124 %keyword.role.attrib;
1125 %local.keyword.attrib;
11261126 >
11271127 <!--end of keyword.attlist-->]]>
11281128 <!--end of keyword.module-->]]>
11411141 <!ENTITY % itermset.attlist "INCLUDE">
11421142 <![%itermset.attlist;[
11431143 <!ATTLIST itermset
1144 %common.attrib;
1145 %itermset.role.attrib;
1146 %local.itermset.attrib;
1144 %common.attrib;
1145 %itermset.role.attrib;
1146 %local.itermset.attrib;
11471147 >
11481148 <!--end of itermset.attlist-->]]>
11491149 <!--end of itermset.module-->]]>
11581158 <!ENTITY % blockinfo.element "INCLUDE">
11591159 <![ %blockinfo.element; [
11601160 <!ELEMENT blockinfo %ho; ((%info.class;)+)
1161 %beginpage.exclusion;>
1161 %beginpage.exclusion;>
11621162 <!--end of blockinfo.element-->]]>
11631163
11641164 <!ENTITY % blockinfo.attlist "INCLUDE">
11651165 <![ %blockinfo.attlist; [
11661166 <!ATTLIST blockinfo
1167 %common.attrib;
1168 %blockinfo.role.attrib;
1169 %local.blockinfo.attrib;
1167 %common.attrib;
1168 %blockinfo.role.attrib;
1169 %local.blockinfo.attrib;
11701170 >
11711171 <!--end of blockinfo.attlist-->]]>
11721172 <!--end of blockinfo.module-->]]>
11921192 <!ENTITY % msgset.attlist "INCLUDE">
11931193 <![%msgset.attlist;[
11941194 <!ATTLIST msgset
1195 %common.attrib;
1196 %msgset.role.attrib;
1197 %local.msgset.attrib;
1195 %common.attrib;
1196 %msgset.role.attrib;
1197 %local.msgset.attrib;
11981198 >
11991199 <!--end of msgset.attlist-->]]>
12001200 <!--end of msgset.module-->]]>
12121212 <!ENTITY % msgentry.attlist "INCLUDE">
12131213 <![%msgentry.attlist;[
12141214 <!ATTLIST msgentry
1215 %common.attrib;
1216 %msgentry.role.attrib;
1217 %local.msgentry.attrib;
1215 %common.attrib;
1216 %msgentry.role.attrib;
1217 %local.msgentry.attrib;
12181218 >
12191219 <!--end of msgentry.attlist-->]]>
12201220 <!--end of msgentry.module-->]]>
12321232 <!ENTITY % simplemsgentry.attlist "INCLUDE">
12331233 <![ %simplemsgentry.attlist; [
12341234 <!ATTLIST simplemsgentry
1235 audience CDATA #IMPLIED
1236 level CDATA #IMPLIED
1237 origin CDATA #IMPLIED
1238 %common.attrib;
1239 %simplemsgentry.role.attrib;
1240 %local.simplemsgentry.attrib;
1235 audience CDATA #IMPLIED
1236 level CDATA #IMPLIED
1237 origin CDATA #IMPLIED
1238 %common.attrib;
1239 %simplemsgentry.role.attrib;
1240 %local.simplemsgentry.attrib;
12411241 >
12421242 <!--end of simplemsgentry.attlist-->]]>
12431243 <!--end of simplemsgentry.module-->]]>
12551255 <!ENTITY % msg.attlist "INCLUDE">
12561256 <![%msg.attlist;[
12571257 <!ATTLIST msg
1258 %common.attrib;
1259 %msg.role.attrib;
1260 %local.msg.attrib;
1258 %common.attrib;
1259 %msg.role.attrib;
1260 %local.msg.attrib;
12611261 >
12621262 <!--end of msg.attlist-->]]>
12631263 <!--end of msg.module-->]]>
12751275 <!ENTITY % msgmain.attlist "INCLUDE">
12761276 <![%msgmain.attlist;[
12771277 <!ATTLIST msgmain
1278 %common.attrib;
1279 %msgmain.role.attrib;
1280 %local.msgmain.attrib;
1278 %common.attrib;
1279 %msgmain.role.attrib;
1280 %local.msgmain.attrib;
12811281 >
12821282 <!--end of msgmain.attlist-->]]>
12831283 <!--end of msgmain.module-->]]>
12951295 <!ENTITY % msgsub.attlist "INCLUDE">
12961296 <![%msgsub.attlist;[
12971297 <!ATTLIST msgsub
1298 %common.attrib;
1299 %msgsub.role.attrib;
1300 %local.msgsub.attrib;
1298 %common.attrib;
1299 %msgsub.role.attrib;
1300 %local.msgsub.attrib;
13011301 >
13021302 <!--end of msgsub.attlist-->]]>
13031303 <!--end of msgsub.module-->]]>
13151315 <!ENTITY % msgrel.attlist "INCLUDE">
13161316 <![%msgrel.attlist;[
13171317 <!ATTLIST msgrel
1318 %common.attrib;
1319 %msgrel.role.attrib;
1320 %local.msgrel.attrib;
1318 %common.attrib;
1319 %msgrel.role.attrib;
1320 %local.msgrel.attrib;
13211321 >
13221322 <!--end of msgrel.attlist-->]]>
13231323 <!--end of msgrel.module-->]]>
13371337 <!ENTITY % msginfo.attlist "INCLUDE">
13381338 <![%msginfo.attlist;[
13391339 <!ATTLIST msginfo
1340 %common.attrib;
1341 %msginfo.role.attrib;
1342 %local.msginfo.attrib;
1340 %common.attrib;
1341 %msginfo.role.attrib;
1342 %local.msginfo.attrib;
13431343 >
13441344 <!--end of msginfo.attlist-->]]>
13451345 <!--end of msginfo.module-->]]>
13571357 <!ENTITY % msglevel.attlist "INCLUDE">
13581358 <![%msglevel.attlist;[
13591359 <!ATTLIST msglevel
1360 %common.attrib;
1361 %msglevel.role.attrib;
1362 %local.msglevel.attrib;
1360 %common.attrib;
1361 %msglevel.role.attrib;
1362 %local.msglevel.attrib;
13631363 >
13641364 <!--end of msglevel.attlist-->]]>
13651365 <!--end of msglevel.module-->]]>
13771377 <!ENTITY % msgorig.attlist "INCLUDE">
13781378 <![%msgorig.attlist;[
13791379 <!ATTLIST msgorig
1380 %common.attrib;
1381 %msgorig.role.attrib;
1382 %local.msgorig.attrib;
1380 %common.attrib;
1381 %msgorig.role.attrib;
1382 %local.msgorig.attrib;
13831383 >
13841384 <!--end of msgorig.attlist-->]]>
13851385 <!--end of msgorig.module-->]]>
13971397 <!ENTITY % msgaud.attlist "INCLUDE">
13981398 <![%msgaud.attlist;[
13991399 <!ATTLIST msgaud
1400 %common.attrib;
1401 %msgaud.role.attrib;
1402 %local.msgaud.attrib;
1400 %common.attrib;
1401 %msgaud.role.attrib;
1402 %local.msgaud.attrib;
14031403 >
14041404 <!--end of msgaud.attlist-->]]>
14051405 <!--end of msgaud.module-->]]>
14171417 <!ENTITY % msgexplan.attlist "INCLUDE">
14181418 <![%msgexplan.attlist;[
14191419 <!ATTLIST msgexplan
1420 %common.attrib;
1421 %msgexplan.role.attrib;
1422 %local.msgexplan.attrib;
1420 %common.attrib;
1421 %msgexplan.role.attrib;
1422 %local.msgexplan.attrib;
14231423 >
14241424 <!--end of msgexplan.attlist-->]]>
14251425 <!--end of msgexplan.module-->]]>
14361436 <!ENTITY % qandaset.element "INCLUDE">
14371437 <![ %qandaset.element; [
14381438 <!ELEMENT qandaset %ho; (blockinfo?, (%formalobject.title.content;)?,
1439 (%qandaset.mix;)*,
1439 (%qandaset.mix;)*,
14401440 (qandadiv+|qandaentry+))>
14411441 <!--end of qandaset.element-->]]>
14421442
14431443 <!ENTITY % qandaset.attlist "INCLUDE">
14441444 <![ %qandaset.attlist; [
14451445 <!ATTLIST qandaset
1446 defaultlabel (qanda|number|none) #IMPLIED
1447 %common.attrib;
1448 %qandaset.role.attrib;
1449 %local.qandaset.attrib;>
1446 defaultlabel (qanda|number|none) #IMPLIED
1447 %common.attrib;
1448 %qandaset.role.attrib;
1449 %local.qandaset.attrib;>
14501450 <!--end of qandaset.attlist-->]]>
14511451 <!--end of qandaset.module-->]]>
14521452
14581458 <!ENTITY % qandadiv.element "INCLUDE">
14591459 <![ %qandadiv.element; [
14601460 <!ELEMENT qandadiv %ho; (blockinfo?, (%formalobject.title.content;)?,
1461 (%qandaset.mix;)*,
1462 (qandadiv+|qandaentry+))>
1461 (%qandaset.mix;)*,
1462 (qandadiv+|qandaentry+))>
14631463 <!--end of qandadiv.element-->]]>
14641464
14651465 <!ENTITY % qandadiv.attlist "INCLUDE">
14661466 <![ %qandadiv.attlist; [
14671467 <!ATTLIST qandadiv
1468 %common.attrib;
1469 %qandadiv.role.attrib;
1470 %local.qandadiv.attrib;>
1468 %common.attrib;
1469 %qandadiv.role.attrib;
1470 %local.qandadiv.attrib;>
14711471 <!--end of qandadiv.attlist-->]]>
14721472 <!--end of qandadiv.module-->]]>
14731473
14841484 <!ENTITY % qandaentry.attlist "INCLUDE">
14851485 <![ %qandaentry.attlist; [
14861486 <!ATTLIST qandaentry
1487 %common.attrib;
1488 %qandaentry.role.attrib;
1489 %local.qandaentry.attrib;>
1487 %common.attrib;
1488 %qandaentry.role.attrib;
1489 %local.qandaentry.attrib;>
14901490 <!--end of qandaentry.attlist-->]]>
14911491 <!--end of qandaentry.module-->]]>
14921492
15031503 <!ENTITY % question.attlist "INCLUDE">
15041504 <![ %question.attlist; [
15051505 <!ATTLIST question
1506 %common.attrib;
1507 %question.role.attrib;
1508 %local.question.attrib;
1506 %common.attrib;
1507 %question.role.attrib;
1508 %local.question.attrib;
15091509 >
15101510 <!--end of question.attlist-->]]>
15111511 <!--end of question.module-->]]>
15231523 <!ENTITY % answer.attlist "INCLUDE">
15241524 <![ %answer.attlist; [
15251525 <!ATTLIST answer
1526 %common.attrib;
1527 %answer.role.attrib;
1528 %local.answer.attrib;
1526 %common.attrib;
1527 %answer.role.attrib;
1528 %local.answer.attrib;
15291529 >
15301530 <!--end of answer.attlist-->]]>
15311531 <!--end of answer.module-->]]>
15431543 <!ENTITY % label.attlist "INCLUDE">
15441544 <![ %label.attlist; [
15451545 <!ATTLIST label
1546 %common.attrib;
1547 %label.role.attrib;
1548 %local.label.attrib;
1546 %common.attrib;
1547 %label.role.attrib;
1548 %local.label.attrib;
15491549 >
15501550 <!--end of label.attlist-->]]>
15511551 <!--end of label.module-->]]>
15691569 <!ENTITY % procedure.attlist "INCLUDE">
15701570 <![%procedure.attlist;[
15711571 <!ATTLIST procedure
1572 %common.attrib;
1573 %procedure.role.attrib;
1574 %local.procedure.attrib;
1572 %common.attrib;
1573 %procedure.role.attrib;
1574 %local.procedure.attrib;
15751575 >
15761576 <!--end of procedure.attlist-->]]>
15771577 <!--end of procedure.module-->]]>
15841584 <!ENTITY % step.element "INCLUDE">
15851585 <![%step.element;[
15861586 <!ELEMENT step %ho; (title?, (((%component.mix;)+, (substeps,
1587 (%component.mix;)*)?) | (substeps, (%component.mix;)*)))>
1587 (%component.mix;)*)?) | (substeps, (%component.mix;)*)))>
15881588 <!--end of step.element-->]]>
15891589
15901590 <!-- Performance: Whether the Step must be performed -->
15941594 <!ENTITY % step.attlist "INCLUDE">
15951595 <![%step.attlist;[
15961596 <!ATTLIST step
1597 performance (optional
1598 |required) "required"
1599 %common.attrib;
1600 %step.role.attrib;
1601 %local.step.attrib;
1597 performance (optional
1598 |required) "required"
1599 %common.attrib;
1600 %step.role.attrib;
1601 %local.step.attrib;
16021602 >
16031603 <!--end of step.attlist-->]]>
16041604 <!--end of step.module-->]]>
16201620 <!ENTITY % substeps.attlist "INCLUDE">
16211621 <![%substeps.attlist;[
16221622 <!ATTLIST substeps
1623 performance (optional
1624 |required) "required"
1625 %common.attrib;
1626 %substeps.role.attrib;
1627 %local.substeps.attrib;
1623 performance (optional
1624 |required) "required"
1625 %common.attrib;
1626 %substeps.role.attrib;
1627 %local.substeps.attrib;
16281628 >
16291629 <!--end of substeps.attlist-->]]>
16301630 <!--end of substeps.module-->]]>
16431643 <!ENTITY % sidebarinfo.element "INCLUDE">
16441644 <![ %sidebarinfo.element; [
16451645 <!ELEMENT sidebarinfo %ho; ((%info.class;)+)
1646 %beginpage.exclusion;>
1646 %beginpage.exclusion;>
16471647 <!--end of sidebarinfo.element-->]]>
16481648
16491649 <!ENTITY % sidebarinfo.attlist "INCLUDE">
16501650 <![ %sidebarinfo.attlist; [
16511651 <!ATTLIST sidebarinfo
1652 %common.attrib;
1653 %sidebarinfo.role.attrib;
1654 %local.sidebarinfo.attrib;
1652 %common.attrib;
1653 %sidebarinfo.role.attrib;
1654 %local.sidebarinfo.attrib;
16551655 >
16561656 <!--end of sidebarinfo.attlist-->]]>
16571657 <!--end of sidebarinfo.module-->]]>
16711671 <!ENTITY % sidebar.attlist "INCLUDE">
16721672 <![%sidebar.attlist;[
16731673 <!ATTLIST sidebar
1674 %common.attrib;
1675 %sidebar.role.attrib;
1676 %local.sidebar.attrib;
1674 %common.attrib;
1675 %sidebar.role.attrib;
1676 %local.sidebar.attrib;
16771677 >
16781678 <!--end of sidebar.attlist-->]]>
16791679 <!--end of sidebar.module-->]]>
16951695 <!ENTITY % abstract.attlist "INCLUDE">
16961696 <![%abstract.attlist;[
16971697 <!ATTLIST abstract
1698 %common.attrib;
1699 %abstract.role.attrib;
1700 %local.abstract.attrib;
1698 %common.attrib;
1699 %abstract.role.attrib;
1700 %local.abstract.attrib;
17011701 >
17021702 <!--end of abstract.attlist-->]]>
17031703 <!--end of abstract.module-->]]>
17151715 <!ENTITY % authorblurb.attlist "INCLUDE">
17161716 <![%authorblurb.attlist;[
17171717 <!ATTLIST authorblurb
1718 %common.attrib;
1719 %authorblurb.role.attrib;
1720 %local.authorblurb.attrib;
1718 %common.attrib;
1719 %authorblurb.role.attrib;
1720 %local.authorblurb.attrib;
17211721 >
17221722 <!--end of authorblurb.attlist-->]]>
17231723 <!--end of authorblurb.module-->]]>
17351735 <!ENTITY % personblurb.attlist "INCLUDE">
17361736 <![%personblurb.attlist;[
17371737 <!ATTLIST personblurb
1738 %common.attrib;
1739 %personblurb.role.attrib;
1740 %local.personblurb.attrib;
1738 %common.attrib;
1739 %personblurb.role.attrib;
1740 %local.personblurb.attrib;
17411741 >
17421742 <!--end of personblurb.attlist-->]]>
17431743 <!--end of personblurb.module-->]]>
17571757 <!ENTITY % blockquote.attlist "INCLUDE">
17581758 <![%blockquote.attlist;[
17591759 <!ATTLIST blockquote
1760 %common.attrib;
1761 %blockquote.role.attrib;
1762 %local.blockquote.attrib;
1760 %common.attrib;
1761 %blockquote.role.attrib;
1762 %local.blockquote.attrib;
17631763 >
17641764 <!--end of blockquote.attlist-->]]>
17651765 <!--end of blockquote.module-->]]>
17771777 <!ENTITY % attribution.attlist "INCLUDE">
17781778 <![%attribution.attlist;[
17791779 <!ATTLIST attribution
1780 %common.attrib;
1781 %attribution.role.attrib;
1782 %local.attribution.attrib;
1780 %common.attrib;
1781 %attribution.role.attrib;
1782 %local.attribution.attrib;
17831783 >
17841784 <!--end of attribution.attlist-->]]>
17851785 <!--end of attribution.module-->]]>
17951795 <!--end of bridgehead.element-->]]>
17961796
17971797 <!-- Renderas: Indicates the format in which the BridgeHead
1798 should appear -->
1798 should appear -->
17991799
18001800
18011801 <!ENTITY % bridgehead.attlist "INCLUDE">
18021802 <![%bridgehead.attlist;[
18031803 <!ATTLIST bridgehead
1804 renderas (other
1805 |sect1
1806 |sect2
1807 |sect3
1808 |sect4
1809 |sect5) #IMPLIED
1810 %common.attrib;
1811 %bridgehead.role.attrib;
1812 %local.bridgehead.attrib;
1804 renderas (other
1805 |sect1
1806 |sect2
1807 |sect3
1808 |sect4
1809 |sect5) #IMPLIED
1810 %common.attrib;
1811 %bridgehead.role.attrib;
1812 %local.bridgehead.attrib;
18131813 >
18141814 <!--end of bridgehead.attlist-->]]>
18151815 <!--end of bridgehead.module-->]]>
18281828 <!ENTITY % remark.attlist "INCLUDE">
18291829 <![%remark.attlist;[
18301830 <!ATTLIST remark
1831 %common.attrib;
1832 %remark.role.attrib;
1833 %local.remark.attrib;
1831 %common.attrib;
1832 %remark.role.attrib;
1833 %local.remark.attrib;
18341834 >
18351835 <!--end of remark.attlist-->]]>
18361836 <!--end of remark.module-->]]>
18481848 <!ENTITY % epigraph.attlist "INCLUDE">
18491849 <![%epigraph.attlist;[
18501850 <!ATTLIST epigraph
1851 %common.attrib;
1852 %epigraph.role.attrib;
1853 %local.epigraph.attrib;
1851 %common.attrib;
1852 %epigraph.role.attrib;
1853 %local.epigraph.attrib;
18541854 >
18551855 <!--end of epigraph.attlist-->]]>
18561856 <!-- Attribution (defined above)-->
18701870 <!ENTITY % footnote.attlist "INCLUDE">
18711871 <![%footnote.attlist;[
18721872 <!ATTLIST footnote
1873 %label.attrib;
1874 %common.attrib;
1875 %footnote.role.attrib;
1876 %local.footnote.attrib;
1873 %label.attrib;
1874 %common.attrib;
1875 %footnote.role.attrib;
1876 %local.footnote.attrib;
18771877 >
18781878 <!--end of footnote.attlist-->]]>
18791879 <!--end of footnote.module-->]]>
18921892 <!ENTITY % highlights.attlist "INCLUDE">
18931893 <![%highlights.attlist;[
18941894 <!ATTLIST highlights
1895 %common.attrib;
1896 %highlights.role.attrib;
1897 %local.highlights.attrib;
1895 %common.attrib;
1896 %highlights.role.attrib;
1897 %local.highlights.attrib;
18981898 >
18991899 <!--end of highlights.attlist-->]]>
19001900 <!--end of highlights.module-->]]>
19121912 <!ENTITY % formalpara.attlist "INCLUDE">
19131913 <![%formalpara.attlist;[
19141914 <!ATTLIST formalpara
1915 %common.attrib;
1916 %formalpara.role.attrib;
1917 %local.formalpara.attrib;
1915 %common.attrib;
1916 %formalpara.role.attrib;
1917 %local.formalpara.attrib;
19181918 >
19191919 <!--end of formalpara.attlist-->]]>
19201920 <!--end of formalpara.module-->]]>
19321932 <!ENTITY % para.attlist "INCLUDE">
19331933 <![%para.attlist;[
19341934 <!ATTLIST para
1935 %common.attrib;
1936 %para.role.attrib;
1937 %local.para.attrib;
1935 %common.attrib;
1936 %para.role.attrib;
1937 %local.para.attrib;
19381938 >
19391939 <!--end of para.attlist-->]]>
19401940 <!--end of para.module-->]]>
19521952 <!ENTITY % simpara.attlist "INCLUDE">
19531953 <![%simpara.attlist;[
19541954 <!ATTLIST simpara
1955 %common.attrib;
1956 %simpara.role.attrib;
1957 %local.simpara.attrib;
1955 %common.attrib;
1956 %simpara.role.attrib;
1957 %local.simpara.attrib;
19581958 >
19591959 <!--end of simpara.attlist-->]]>
19601960 <!--end of simpara.module-->]]>
19741974 <!ENTITY % caution.attlist "INCLUDE">
19751975 <![%caution.attlist;[
19761976 <!ATTLIST caution
1977 %common.attrib;
1978 %admon.role.attrib;
1979 %local.admon.attrib;
1977 %common.attrib;
1978 %admon.role.attrib;
1979 %local.admon.attrib;
19801980 >
19811981 <!--end of caution.attlist-->]]>
19821982
19901990 <!ENTITY % important.attlist "INCLUDE">
19911991 <![%important.attlist;[
19921992 <!ATTLIST important
1993 %common.attrib;
1994 %admon.role.attrib;
1995 %local.admon.attrib;
1993 %common.attrib;
1994 %admon.role.attrib;
1995 %local.admon.attrib;
19961996 >
19971997 <!--end of important.attlist-->]]>
19981998
20062006 <!ENTITY % note.attlist "INCLUDE">
20072007 <![%note.attlist;[
20082008 <!ATTLIST note
2009 %common.attrib;
2010 %admon.role.attrib;
2011 %local.admon.attrib;
2009 %common.attrib;
2010 %admon.role.attrib;
2011 %local.admon.attrib;
20122012 >
20132013 <!--end of note.attlist-->]]>
20142014
20212021 <!ENTITY % tip.attlist "INCLUDE">
20222022 <![%tip.attlist;[
20232023 <!ATTLIST tip
2024 %common.attrib;
2025 %admon.role.attrib;
2026 %local.admon.attrib;
2024 %common.attrib;
2025 %admon.role.attrib;
2026 %local.admon.attrib;
20272027 >
20282028 <!--end of tip.attlist-->]]>
20292029
20372037 <!ENTITY % warning.attlist "INCLUDE">
20382038 <![%warning.attlist;[
20392039 <!ATTLIST warning
2040 %common.attrib;
2041 %admon.role.attrib;
2042 %local.admon.attrib;
2040 %common.attrib;
2041 %admon.role.attrib;
2042 %local.admon.attrib;
20432043 >
20442044 <!--end of warning.attlist-->]]>
20452045
20632063 <!ENTITY % glosslist.attlist "INCLUDE">
20642064 <![%glosslist.attlist;[
20652065 <!ATTLIST glosslist
2066 %common.attrib;
2067 %glosslist.role.attrib;
2068 %local.glosslist.attrib;
2066 %common.attrib;
2067 %glosslist.role.attrib;
2068 %local.glosslist.attrib;
20692069 >
20702070 <!--end of glosslist.attlist-->]]>
20712071 <!--end of glosslist.module-->]]>
20852085 <!--end of glossentry.element-->]]>
20862086
20872087 <!-- SortAs: String by which the GlossEntry is to be sorted
2088 (alphabetized) in lieu of its proper content -->
2088 (alphabetized) in lieu of its proper content -->
20892089
20902090
20912091 <!ENTITY % glossentry.attlist "INCLUDE">
20922092 <![%glossentry.attlist;[
20932093 <!ATTLIST glossentry
2094 sortas CDATA #IMPLIED
2095 %common.attrib;
2096 %glossentry.role.attrib;
2097 %local.glossentry.attrib;
2094 sortas CDATA #IMPLIED
2095 %common.attrib;
2096 %glossentry.role.attrib;
2097 %local.glossentry.attrib;
20982098 >
20992099 <!--end of glossentry.attlist-->]]>
21002100 <!--end of glossentry.module-->]]>
21162116 <!ENTITY % glossdef.attlist "INCLUDE">
21172117 <![%glossdef.attlist;[
21182118 <!ATTLIST glossdef
2119 subject CDATA #IMPLIED
2120 %common.attrib;
2121 %glossdef.role.attrib;
2122 %local.glossdef.attrib;
2119 subject CDATA #IMPLIED
2120 %common.attrib;
2121 %glossdef.role.attrib;
2122 %local.glossdef.attrib;
21232123 >
21242124 <!--end of glossdef.attlist-->]]>
21252125 <!--end of glossdef.module-->]]>
21352135 <!--end of glosssee.element-->]]>
21362136
21372137 <!-- OtherTerm: Reference to the GlossEntry whose GlossTerm
2138 should be displayed at the point of the GlossSee -->
2138 should be displayed at the point of the GlossSee -->
21392139
21402140
21412141 <!ENTITY % glosssee.attlist "INCLUDE">
21422142 <![%glosssee.attlist;[
21432143 <!ATTLIST glosssee
2144 otherterm IDREF #IMPLIED
2145 %common.attrib;
2146 %glosssee.role.attrib;
2147 %local.glosssee.attrib;
2144 otherterm IDREF #IMPLIED
2145 %common.attrib;
2146 %glosssee.role.attrib;
2147 %local.glosssee.attrib;
21482148 >
21492149 <!--end of glosssee.attlist-->]]>
21502150 <!--end of glosssee.module-->]]>
21602160 <!--end of glossseealso.element-->]]>
21612161
21622162 <!-- OtherTerm: Reference to the GlossEntry whose GlossTerm
2163 should be displayed at the point of the GlossSeeAlso -->
2163 should be displayed at the point of the GlossSeeAlso -->
21642164
21652165
21662166 <!ENTITY % glossseealso.attlist "INCLUDE">
21672167 <![%glossseealso.attlist;[
21682168 <!ATTLIST glossseealso
2169 otherterm IDREF #IMPLIED
2170 %common.attrib;
2171 %glossseealso.role.attrib;
2172 %local.glossseealso.attrib;
2169 otherterm IDREF #IMPLIED
2170 %common.attrib;
2171 %glossseealso.role.attrib;
2172 %local.glossseealso.attrib;
21732173 >
21742174 <!--end of glossseealso.attlist-->]]>
21752175 <!--end of glossseealso.module-->]]>
21852185 <!ENTITY % itemizedlist.element "INCLUDE">
21862186 <![%itemizedlist.element;[
21872187 <!ELEMENT itemizedlist %ho; (blockinfo?, (%formalobject.title.content;)?,
2188 (%listpreamble.mix;)*, listitem+)>
2188 (%listpreamble.mix;)*, listitem+)>
21892189
21902190 <!--end of itemizedlist.element-->]]>
21912191
21922192 <!-- Spacing: Whether the vertical space in the list should be
2193 compressed -->
2193 compressed -->
21942194 <!-- Mark: Keyword, e.g., bullet, dash, checkbox, none;
2195 list of keywords and defaults are implementation specific -->
2195 list of keywords and defaults are implementation specific -->
21962196
21972197
21982198 <!ENTITY % itemizedlist.attlist "INCLUDE">
21992199 <![%itemizedlist.attlist;[
22002200 <!ATTLIST itemizedlist spacing (normal
2201 |compact) #IMPLIED
2202 %mark.attrib;
2203 %common.attrib;
2204 %itemizedlist.role.attrib;
2205 %local.itemizedlist.attrib;
2201 |compact) #IMPLIED
2202 %mark.attrib;
2203 %common.attrib;
2204 %itemizedlist.role.attrib;
2205 %local.itemizedlist.attrib;
22062206 >
22072207 <!--end of itemizedlist.attlist-->]]>
22082208 <!--end of itemizedlist.module-->]]>
22152215 <!ENTITY % orderedlist.element "INCLUDE">
22162216 <![%orderedlist.element;[
22172217 <!ELEMENT orderedlist %ho; (blockinfo?, (%formalobject.title.content;)?,
2218 (%listpreamble.mix;)*, listitem+)>
2218 (%listpreamble.mix;)*, listitem+)>
22192219
22202220 <!--end of orderedlist.element-->]]>
22212221
22222222 <!-- Numeration: Style of ListItem numbered; default is expected
2223 to be Arabic -->
2223 to be Arabic -->
22242224 <!-- InheritNum: Specifies for a nested list that the numbering
2225 of ListItems should include the number of the item
2226 within which they are nested (e.g., 1a and 1b within 1,
2227 rather than a and b) -->
2225 of ListItems should include the number of the item
2226 within which they are nested (e.g., 1a and 1b within 1,
2227 rather than a and b) -->
22282228 <!-- Continuation: Where list numbering begins afresh (Restarts,
2229 the default) or continues that of the immediately preceding
2230 list (Continues) -->
2229 the default) or continues that of the immediately preceding
2230 list (Continues) -->
22312231 <!-- Spacing: Whether the vertical space in the list should be
2232 compressed -->
2232 compressed -->
22332233
22342234
22352235 <!ENTITY % orderedlist.attlist "INCLUDE">
22362236 <![%orderedlist.attlist;[
22372237 <!ATTLIST orderedlist
2238 numeration (arabic
2239 |upperalpha
2240 |loweralpha
2241 |upperroman
2242 |lowerroman) #IMPLIED
2243 inheritnum (inherit
2244 |ignore) "ignore"
2245 continuation (continues
2246 |restarts) "restarts"
2247 spacing (normal
2248 |compact) #IMPLIED
2249 %common.attrib;
2250 %orderedlist.role.attrib;
2251 %local.orderedlist.attrib;
2238 numeration (arabic
2239 |upperalpha
2240 |loweralpha
2241 |upperroman
2242 |lowerroman) #IMPLIED
2243 inheritnum (inherit
2244 |ignore) "ignore"
2245 continuation (continues
2246 |restarts) "restarts"
2247 spacing (normal
2248 |compact) #IMPLIED
2249 %common.attrib;
2250 %orderedlist.role.attrib;
2251 %local.orderedlist.attrib;
22522252 >
22532253 <!--end of orderedlist.attlist-->]]>
22542254 <!--end of orderedlist.module-->]]>
22642264 <!--end of listitem.element-->]]>
22652265
22662266 <!-- Override: Indicates the mark to be used for this ListItem
2267 instead of the default mark or the mark specified by
2268 the Mark attribute on the enclosing ItemizedList -->
2267 instead of the default mark or the mark specified by
2268 the Mark attribute on the enclosing ItemizedList -->
22692269
22702270
22712271 <!ENTITY % listitem.attlist "INCLUDE">
22722272 <![%listitem.attlist;[
22732273 <!ATTLIST listitem
2274 override CDATA #IMPLIED
2275 %common.attrib;
2276 %listitem.role.attrib;
2277 %local.listitem.attrib;
2274 override CDATA #IMPLIED
2275 %common.attrib;
2276 %listitem.role.attrib;
2277 %local.listitem.attrib;
22782278 >
22792279 <!--end of listitem.attlist-->]]>
22802280 <!--end of listitem.module-->]]>
22972297 <!ENTITY % segmentedlist.attlist "INCLUDE">
22982298 <![%segmentedlist.attlist;[
22992299 <!ATTLIST segmentedlist
2300 %common.attrib;
2301 %segmentedlist.role.attrib;
2302 %local.segmentedlist.attrib;
2300 %common.attrib;
2301 %segmentedlist.role.attrib;
2302 %local.segmentedlist.attrib;
23032303 >
23042304 <!--end of segmentedlist.attlist-->]]>
23052305 <!--end of segmentedlist.module-->]]>
23172317 <!ENTITY % segtitle.attlist "INCLUDE">
23182318 <![%segtitle.attlist;[
23192319 <!ATTLIST segtitle
2320 %common.attrib;
2321 %segtitle.role.attrib;
2322 %local.segtitle.attrib;
2320 %common.attrib;
2321 %segtitle.role.attrib;
2322 %local.segtitle.attrib;
23232323 >
23242324 <!--end of segtitle.attlist-->]]>
23252325 <!--end of segtitle.module-->]]>
23372337 <!ENTITY % seglistitem.attlist "INCLUDE">
23382338 <![%seglistitem.attlist;[
23392339 <!ATTLIST seglistitem
2340 %common.attrib;
2341 %seglistitem.role.attrib;
2342 %local.seglistitem.attrib;
2340 %common.attrib;
2341 %seglistitem.role.attrib;
2342 %local.seglistitem.attrib;
23432343 >
23442344 <!--end of seglistitem.attlist-->]]>
23452345 <!--end of seglistitem.module-->]]>
23572357 <!ENTITY % seg.attlist "INCLUDE">
23582358 <![%seg.attlist;[
23592359 <!ATTLIST seg
2360 %common.attrib;
2361 %seg.role.attrib;
2362 %local.seg.attrib;
2360 %common.attrib;
2361 %seg.role.attrib;
2362 %local.seg.attrib;
23632363 >
23642364 <!--end of seg.attlist-->]]>
23652365 <!--end of seg.module-->]]>
23812381
23822382 <!-- Columns: The number of columns the array should contain -->
23832383 <!-- Type: How the Members of the SimpleList should be
2384 formatted: Inline (members separated with commas etc.
2385 inline), Vert (top to bottom in n Columns), or Horiz (in
2386 the direction of text flow) in n Columns. If Column
2387 is 1 or implied, Type=Vert and Type=Horiz give the same
2388 results. -->
2384 formatted: Inline (members separated with commas etc.
2385 inline), Vert (top to bottom in n Columns), or Horiz (in
2386 the direction of text flow) in n Columns. If Column
2387 is 1 or implied, Type=Vert and Type=Horiz give the same
2388 results. -->
23892389
23902390
23912391 <!ENTITY % simplelist.attlist "INCLUDE">
23922392 <![%simplelist.attlist;[
23932393 <!ATTLIST simplelist
2394 columns CDATA #IMPLIED
2395 type (inline
2396 |vert
2397 |horiz) "vert"
2398 %common.attrib;
2399 %simplelist.role.attrib;
2400 %local.simplelist.attrib;
2394 columns CDATA #IMPLIED
2395 type (inline
2396 |vert
2397 |horiz) "vert"
2398 %common.attrib;
2399 %simplelist.role.attrib;
2400 %local.simplelist.attrib;
24012401 >
24022402 <!--end of simplelist.attlist-->]]>
24032403 <!--end of simplelist.module-->]]>
24152415 <!ENTITY % member.attlist "INCLUDE">
24162416 <![%member.attlist;[
24172417 <!ATTLIST member
2418 %common.attrib;
2419 %member.role.attrib;
2420 %local.member.attrib;
2418 %common.attrib;
2419 %member.role.attrib;
2420 %local.member.attrib;
24212421 >
24222422 <!--end of member.attlist-->]]>
24232423 <!--end of member.module-->]]>
24352435 <!ENTITY % variablelist.element "INCLUDE">
24362436 <![%variablelist.element;[
24372437 <!ELEMENT variablelist %ho; (blockinfo?, (%formalobject.title.content;)?,
2438 (%listpreamble.mix;)*, varlistentry+)>
2438 (%listpreamble.mix;)*, varlistentry+)>
24392439 <!--end of variablelist.element-->]]>
24402440
24412441 <!-- TermLength: Length beyond which the presentation engine
2442 may consider the Term too long and select an alternate
2443 presentation of the Term and, or, its associated ListItem. -->
2442 may consider the Term too long and select an alternate
2443 presentation of the Term and, or, its associated ListItem. -->
24442444
24452445
24462446 <!ENTITY % variablelist.attlist "INCLUDE">
24472447 <![%variablelist.attlist;[
24482448 <!ATTLIST variablelist
2449 termlength CDATA #IMPLIED
2450 %common.attrib;
2451 %variablelist.role.attrib;
2452 %local.variablelist.attrib;
2449 termlength CDATA #IMPLIED
2450 %common.attrib;
2451 %variablelist.role.attrib;
2452 %local.variablelist.attrib;
24532453 >
24542454 <!--end of variablelist.attlist-->]]>
24552455 <!--end of variablelist.module-->]]>
24612461
24622462 <!ENTITY % varlistentry.element "INCLUDE">
24632463 <![%varlistentry.element;[
2464 <!ELEMENT varlistentry %ho; (term+, listitem)>
2464 <!ELEMENT varlistentry %ho; (term+, listitem+)>
24652465 <!--end of varlistentry.element-->]]>
24662466
24672467 <!ENTITY % varlistentry.attlist "INCLUDE">
24682468 <![%varlistentry.attlist;[
24692469 <!ATTLIST varlistentry
2470 %common.attrib;
2471 %varlistentry.role.attrib;
2472 %local.varlistentry.attrib;
2470 %common.attrib;
2471 %varlistentry.role.attrib;
2472 %local.varlistentry.attrib;
24732473 >
24742474 <!--end of varlistentry.attlist-->]]>
24752475 <!--end of varlistentry.module-->]]>
24872487 <!ENTITY % term.attlist "INCLUDE">
24882488 <![%term.attlist;[
24892489 <!ATTLIST term
2490 %common.attrib;
2491 %term.role.attrib;
2492 %local.term.attrib;
2490 %common.attrib;
2491 %term.role.attrib;
2492 %local.term.attrib;
24932493 >
24942494 <!--end of term.attlist-->]]>
24952495 <!--end of term.module-->]]>
25142514 <!ENTITY % calloutlist.attlist "INCLUDE">
25152515 <![%calloutlist.attlist;[
25162516 <!ATTLIST calloutlist
2517 %common.attrib;
2518 %calloutlist.role.attrib;
2519 %local.calloutlist.attrib;
2517 %common.attrib;
2518 %calloutlist.role.attrib;
2519 %local.calloutlist.attrib;
25202520 >
25212521 <!--end of calloutlist.attlist-->]]>
25222522 <!--end of calloutlist.module-->]]>
25322532 <!--end of callout.element-->]]>
25332533
25342534 <!-- AreaRefs: IDs of one or more Areas or AreaSets described
2535 by this Callout -->
2535 by this Callout -->
25362536
25372537
25382538 <!ENTITY % callout.attlist "INCLUDE">
25392539 <![%callout.attlist;[
25402540 <!ATTLIST callout
2541 arearefs IDREFS #REQUIRED
2542 %common.attrib;
2543 %callout.role.attrib;
2544 %local.callout.attrib;
2541 arearefs IDREFS #REQUIRED
2542 %common.attrib;
2543 %callout.role.attrib;
2544 %local.callout.attrib;
25452545 >
25462546 <!--end of callout.attlist-->]]>
25472547 <!--end of callout.module-->]]>
25602560 <!ENTITY % example.element "INCLUDE">
25612561 <![%example.element;[
25622562 <!ELEMENT example %ho; (blockinfo?, (%formalobject.title.content;), (%example.mix;)+)
2563 %formal.exclusion;>
2563 %formal.exclusion;>
25642564 <!--end of example.element-->]]>
25652565
25662566 <!ENTITY % example.attlist "INCLUDE">
25672567 <![%example.attlist;[
25682568 <!ATTLIST example
2569 %label.attrib;
2570 %width.attrib;
2571 %common.attrib;
2572 %example.role.attrib;
2573 %local.example.attrib;
2569 %label.attrib;
2570 %width.attrib;
2571 %common.attrib;
2572 %example.role.attrib;
2573 %local.example.attrib;
25742574 >
25752575 <!--end of example.attlist-->]]>
25762576 <!--end of example.module-->]]>
25882588 <!ENTITY % informalexample.attlist "INCLUDE">
25892589 <![%informalexample.attlist;[
25902590 <!ATTLIST informalexample
2591 %width.attrib;
2592 %common.attrib;
2593 %informalexample.role.attrib;
2594 %local.informalexample.attrib;
2591 %width.attrib;
2592 %common.attrib;
2593 %informalexample.role.attrib;
2594 %local.informalexample.attrib;
25952595 >
25962596 <!--end of informalexample.attlist-->]]>
25972597 <!--end of informalexample.module-->]]>
26092609 <!ENTITY % programlistingco.attlist "INCLUDE">
26102610 <![%programlistingco.attlist;[
26112611 <!ATTLIST programlistingco
2612 %common.attrib;
2613 %programlistingco.role.attrib;
2614 %local.programlistingco.attrib;
2612 %common.attrib;
2613 %programlistingco.role.attrib;
2614 %local.programlistingco.attrib;
26152615 >
26162616 <!--end of programlistingco.attlist-->]]>
26172617 <!-- CalloutList (defined above in Lists)-->
26302630 <!--end of areaspec.element-->]]>
26312631
26322632 <!-- Units: global unit of measure in which coordinates in
2633 this spec are expressed:
2634
2635 - CALSPair "x1,y1 x2,y2": lower-left and upper-right
2636 coordinates in a rectangle describing repro area in which
2637 graphic is placed, where X and Y dimensions are each some
2638 number 0..10000 (taken from CALS graphic attributes)
2639
2640 - LineColumn "line column": line number and column number
2641 at which to start callout text in "linespecific" content
2642
2643 - LineRange "startline endline": whole lines from startline
2644 to endline in "linespecific" content
2645
2646 - LineColumnPair "line1 col1 line2 col2": starting and ending
2647 points of area in "linespecific" content that starts at
2648 first position and ends at second position (including the
2649 beginnings of any intervening lines)
2650
2651 - Other: directive to look at value of OtherUnits attribute
2652 to get implementation-specific keyword
2653
2654 The default is implementation-specific; usually dependent on
2655 the parent element (GraphicCO gets CALSPair, ProgramListingCO
2656 and ScreenCO get LineColumn) -->
2633 this spec are expressed:
2634
2635 - CALSPair "x1,y1 x2,y2": lower-left and upper-right
2636 coordinates in a rectangle describing repro area in which
2637 graphic is placed, where X and Y dimensions are each some
2638 number 0..10000 (taken from CALS graphic attributes)
2639
2640 - LineColumn "line column": line number and column number
2641 at which to start callout text in "linespecific" content
2642
2643 - LineRange "startline endline": whole lines from startline
2644 to endline in "linespecific" content
2645
2646 - LineColumnPair "line1 col1 line2 col2": starting and ending
2647 points of area in "linespecific" content that starts at
2648 first position and ends at second position (including the
2649 beginnings of any intervening lines)
2650
2651 - Other: directive to look at value of OtherUnits attribute
2652 to get implementation-specific keyword
2653
2654 The default is implementation-specific; usually dependent on
2655 the parent element (GraphicCO gets CALSPair, ProgramListingCO
2656 and ScreenCO get LineColumn) -->
26572657 <!-- OtherUnits: User-defined units -->
26582658
26592659
26602660 <!ENTITY % areaspec.attlist "INCLUDE">
26612661 <![%areaspec.attlist;[
26622662 <!ATTLIST areaspec
2663 units (calspair
2664 |linecolumn
2665 |linerange
2666 |linecolumnpair
2667 |other) #IMPLIED
2668 otherunits NMTOKEN #IMPLIED
2669 %common.attrib;
2670 %areaspec.role.attrib;
2671 %local.areaspec.attrib;
2663 units (calspair
2664 |linecolumn
2665 |linerange
2666 |linecolumnpair
2667 |other) #IMPLIED
2668 otherunits NMTOKEN #IMPLIED
2669 %common.attrib;
2670 %areaspec.role.attrib;
2671 %local.areaspec.attrib;
26722672 >
26732673 <!--end of areaspec.attlist-->]]>
26742674 <!--end of areaspec.module-->]]>
26862686 <!-- bug number/symbol override or initialization -->
26872687 <!-- to any related information -->
26882688 <!-- Units: unit of measure in which coordinates in this
2689 area are expressed; inherits from AreaSet and AreaSpec -->
2689 area are expressed; inherits from AreaSet and AreaSpec -->
26902690 <!-- OtherUnits: User-defined units -->
26912691
26922692
26932693 <!ENTITY % area.attlist "INCLUDE">
26942694 <![%area.attlist;[
26952695 <!ATTLIST area
2696 %label.attrib;
2697 %linkends.attrib;
2698 units (calspair
2699 |linecolumn
2700 |linerange
2701 |linecolumnpair
2702 |other) #IMPLIED
2703 otherunits NMTOKEN #IMPLIED
2704 coords CDATA #REQUIRED
2705 %idreq.common.attrib;
2706 %area.role.attrib;
2707 %local.area.attrib;
2696 %label.attrib;
2697 %linkends.attrib;
2698 units (calspair
2699 |linecolumn
2700 |linerange
2701 |linecolumnpair
2702 |other) #IMPLIED
2703 otherunits NMTOKEN #IMPLIED
2704 coords CDATA #REQUIRED
2705 %idreq.common.attrib;
2706 %area.role.attrib;
2707 %local.area.attrib;
27082708 >
27092709 <!--end of area.attlist-->]]>
27102710 <!--end of area.module-->]]>
27212721
27222722 <!-- bug number/symbol override or initialization -->
27232723 <!-- Units: unit of measure in which coordinates in this
2724 area are expressed; inherits from AreaSpec -->
2724 area are expressed; inherits from AreaSpec -->
27252725
27262726
27272727 <!ENTITY % areaset.attlist "INCLUDE">
27282728 <![%areaset.attlist;[
27292729 <!ATTLIST areaset
2730 %label.attrib;
2731 units (calspair
2732 |linecolumn
2733 |linerange
2734 |linecolumnpair
2735 |other) #IMPLIED
2736 otherunits NMTOKEN #IMPLIED
2737 coords CDATA #REQUIRED
2738 %idreq.common.attrib;
2739 %areaset.role.attrib;
2740 %local.areaset.attrib;
2730 %label.attrib;
2731 units (calspair
2732 |linecolumn
2733 |linerange
2734 |linecolumnpair
2735 |other) #IMPLIED
2736 otherunits NMTOKEN #IMPLIED
2737 coords CDATA #REQUIRED
2738 %idreq.common.attrib;
2739 %areaset.role.attrib;
2740 %local.areaset.attrib;
27412741 >
27422742 <!--end of areaset.attlist-->]]>
27432743 <!--end of areaset.module-->]]>
27562756 <!ENTITY % programlisting.attlist "INCLUDE">
27572757 <![%programlisting.attlist;[
27582758 <!ATTLIST programlisting
2759 %width.attrib;
2760 %linespecific.attrib;
2761 %common.attrib;
2762 %programlisting.role.attrib;
2763 %local.programlisting.attrib;
2759 %width.attrib;
2760 %linespecific.attrib;
2761 %common.attrib;
2762 %programlisting.role.attrib;
2763 %local.programlisting.attrib;
27642764 >
27652765 <!--end of programlisting.attlist-->]]>
27662766 <!--end of programlisting.module-->]]>
27782778 <!ENTITY % literallayout.attlist "INCLUDE">
27792779 <![%literallayout.attlist;[
27802780 <!ATTLIST literallayout
2781 %width.attrib;
2782 %linespecific.attrib;
2783 class (monospaced|normal) "normal"
2784 %common.attrib;
2785 %literallayout.role.attrib;
2786 %local.literallayout.attrib;
2781 %width.attrib;
2782 %linespecific.attrib;
2783 class (monospaced|normal) "normal"
2784 %common.attrib;
2785 %literallayout.role.attrib;
2786 %local.literallayout.attrib;
27872787 >
27882788 <!--end of literallayout.attlist-->]]>
27892789 <!-- LineAnnotation (defined in the Inlines section, below)-->
28022802 <!ENTITY % screenco.attlist "INCLUDE">
28032803 <![%screenco.attlist;[
28042804 <!ATTLIST screenco
2805 %common.attrib;
2806 %screenco.role.attrib;
2807 %local.screenco.attrib;
2805 %common.attrib;
2806 %screenco.role.attrib;
2807 %local.screenco.attrib;
28082808 >
28092809 <!--end of screenco.attlist-->]]>
28102810 <!-- AreaSpec (defined above)-->
28242824 <!ENTITY % screen.attlist "INCLUDE">
28252825 <![%screen.attlist;[
28262826 <!ATTLIST screen
2827 %width.attrib;
2828 %linespecific.attrib;
2829 %common.attrib;
2830 %screen.role.attrib;
2831 %local.screen.attrib;
2827 %width.attrib;
2828 %linespecific.attrib;
2829 %common.attrib;
2830 %screen.role.attrib;
2831 %local.screen.attrib;
28322832 >
28332833 <!--end of screen.attlist-->]]>
28342834 <!--end of screen.module-->]]>
28502850 <!ENTITY % screenshot.attlist "INCLUDE">
28512851 <![%screenshot.attlist;[
28522852 <!ATTLIST screenshot
2853 %common.attrib;
2854 %screenshot.role.attrib;
2855 %local.screenshot.attrib;
2853 %common.attrib;
2854 %screenshot.role.attrib;
2855 %local.screenshot.attrib;
28562856 >
28572857 <!--end of screenshot.attlist-->]]>
28582858 <!--end of screenshot.module-->]]>
28652865 <!ENTITY % screeninfo.element "INCLUDE">
28662866 <![%screeninfo.element;[
28672867 <!ELEMENT screeninfo %ho; (%para.char.mix;)*
2868 %ubiq.exclusion;>
2868 %ubiq.exclusion;>
28692869 <!--end of screeninfo.element-->]]>
28702870
28712871 <!ENTITY % screeninfo.attlist "INCLUDE">
28722872 <![%screeninfo.attlist;[
28732873 <!ATTLIST screeninfo
2874 %common.attrib;
2875 %screeninfo.role.attrib;
2876 %local.screeninfo.attrib;
2874 %common.attrib;
2875 %screeninfo.role.attrib;
2876 %local.screeninfo.attrib;
28772877 >
28782878 <!--end of screeninfo.attlist-->]]>
28792879 <!--end of screeninfo.module-->]]>
28932893 <!--end of figure.element-->]]>
28942894
28952895 <!-- Float: Whether the Figure is supposed to be rendered
2896 where convenient (yes (1) value) or at the place it occurs
2897 in the text (no (0) value, the default) -->
2896 where convenient (yes (1) value) or at the place it occurs
2897 in the text (no (0) value, the default) -->
28982898
28992899
29002900 <!ENTITY % figure.attlist "INCLUDE">
29012901 <![%figure.attlist;[
29022902 <!ATTLIST figure
2903 float %yesorno.attvals; '0'
2904 pgwide %yesorno.attvals; #IMPLIED
2905 %label.attrib;
2906 %common.attrib;
2907 %figure.role.attrib;
2908 %local.figure.attrib;
2903 float %yesorno.attvals; '0'
2904 pgwide %yesorno.attvals; #IMPLIED
2905 %label.attrib;
2906 %common.attrib;
2907 %figure.role.attrib;
2908 %local.figure.attrib;
29092909 >
29102910 <!--end of figure.attlist-->]]>
29112911 <!--end of figure.module-->]]>
29282928 in the text (no (0) value, the default)
29292929 -->
29302930 <!ATTLIST informalfigure
2931 float %yesorno.attvals; "0"
2932 pgwide %yesorno.attvals; #IMPLIED
2933 %label.attrib;
2934 %common.attrib;
2935 %informalfigure.role.attrib;
2936 %local.informalfigure.attrib;
2931 float %yesorno.attvals; "0"
2932 pgwide %yesorno.attvals; #IMPLIED
2933 %label.attrib;
2934 %common.attrib;
2935 %informalfigure.role.attrib;
2936 %local.informalfigure.attrib;
29372937 >
29382938 <!--end of informalfigure.attlist-->]]>
29392939 <!--end of informalfigure.module-->]]>
29512951 <!ENTITY % graphicco.attlist "INCLUDE">
29522952 <![%graphicco.attlist;[
29532953 <!ATTLIST graphicco
2954 %common.attrib;
2955 %graphicco.role.attrib;
2956 %local.graphicco.attrib;
2954 %common.attrib;
2955 %graphicco.role.attrib;
2956 %local.graphicco.attrib;
29572957 >
29582958 <!--end of graphicco.attlist-->]]>
29592959 <!-- AreaSpec (defined above in Examples)-->
29772977 <!ENTITY % graphic.attlist "INCLUDE">
29782978 <![%graphic.attlist;[
29792979 <!ATTLIST graphic
2980 %graphics.attrib;
2981 %common.attrib;
2982 %graphic.role.attrib;
2983 %local.graphic.attrib;
2980 %graphics.attrib;
2981 %common.attrib;
2982 %graphic.role.attrib;
2983 %local.graphic.attrib;
29842984 >
29852985 <!--end of graphic.attlist-->]]>
29862986 <!--end of graphic.module-->]]>
29982998 <!ENTITY % inlinegraphic.attlist "INCLUDE">
29992999 <![%inlinegraphic.attlist;[
30003000 <!ATTLIST inlinegraphic
3001 %graphics.attrib;
3002 %common.attrib;
3003 %inlinegraphic.role.attrib;
3004 %local.inlinegraphic.attrib;
3001 %graphics.attrib;
3002 %common.attrib;
3003 %inlinegraphic.role.attrib;
3004 %local.inlinegraphic.attrib;
30053005 >
30063006 <!--end of inlinegraphic.attlist-->]]>
30073007 <!--end of inlinegraphic.module-->]]>
30183018 <![ %mediaobject.element; [
30193019 <!ELEMENT mediaobject %ho; (objectinfo?,
30203020 (%mediaobject.mix;)+,
3021 caption?)>
3021 caption?)>
30223022 <!--end of mediaobject.element-->]]>
30233023
30243024 <!ENTITY % mediaobject.attlist "INCLUDE">
30253025 <![ %mediaobject.attlist; [
30263026 <!ATTLIST mediaobject
3027 %common.attrib;
3028 %mediaobject.role.attrib;
3029 %local.mediaobject.attrib;
3027 %common.attrib;
3028 %mediaobject.role.attrib;
3029 %local.mediaobject.attrib;
30303030 >
30313031 <!--end of mediaobject.attlist-->]]>
30323032 <!--end of mediaobject.module-->]]>
30393039 <!ENTITY % inlinemediaobject.element "INCLUDE">
30403040 <![ %inlinemediaobject.element; [
30413041 <!ELEMENT inlinemediaobject %ho; (objectinfo?,
3042 (%mediaobject.mix;)+)>
3042 (%mediaobject.mix;)+)>
30433043 <!--end of inlinemediaobject.element-->]]>
30443044
30453045 <!ENTITY % inlinemediaobject.attlist "INCLUDE">
30463046 <![ %inlinemediaobject.attlist; [
30473047 <!ATTLIST inlinemediaobject
3048 %common.attrib;
3049 %inlinemediaobject.role.attrib;
3050 %local.inlinemediaobject.attrib;
3048 %common.attrib;
3049 %inlinemediaobject.role.attrib;
3050 %local.inlinemediaobject.attrib;
30513051 >
30523052 <!--end of inlinemediaobject.attlist-->]]>
30533053 <!--end of inlinemediaobject.module-->]]>
30653065 <!ENTITY % videoobject.attlist "INCLUDE">
30663066 <![ %videoobject.attlist; [
30673067 <!ATTLIST videoobject
3068 %common.attrib;
3069 %videoobject.role.attrib;
3070 %local.videoobject.attrib;
3068 %common.attrib;
3069 %videoobject.role.attrib;
3070 %local.videoobject.attrib;
30713071 >
30723072 <!--end of videoobject.attlist-->]]>
30733073 <!--end of videoobject.module-->]]>
30853085 <!ENTITY % audioobject.attlist "INCLUDE">
30863086 <![ %audioobject.attlist; [
30873087 <!ATTLIST audioobject
3088 %common.attrib;
3089 %audioobject.role.attrib;
3090 %local.audioobject.attrib;
3088 %common.attrib;
3089 %audioobject.role.attrib;
3090 %local.audioobject.attrib;
30913091 >
30923092 <!--end of audioobject.attlist-->]]>
30933093 <!--end of audioobject.module-->]]>
31053105 <!ENTITY % imageobject.attlist "INCLUDE">
31063106 <![ %imageobject.attlist; [
31073107 <!ATTLIST imageobject
3108 %common.attrib;
3109 %imageobject.role.attrib;
3110 %local.imageobject.attrib;
3108 %common.attrib;
3109 %imageobject.role.attrib;
3110 %local.imageobject.attrib;
31113111 >
31123112 <!--end of imageobject.attlist-->]]>
31133113 <!--end of imageobject.module-->]]>
31253125 <!ENTITY % textobject.attlist "INCLUDE">
31263126 <![ %textobject.attlist; [
31273127 <!ATTLIST textobject
3128 %common.attrib;
3129 %textobject.role.attrib;
3130 %local.textobject.attrib;
3128 %common.attrib;
3129 %textobject.role.attrib;
3130 %local.textobject.attrib;
31313131 >
31323132 <!--end of textobject.attlist-->]]>
31333133 <!--end of textobject.module-->]]>
31403140 <!ENTITY % objectinfo.element "INCLUDE">
31413141 <![ %objectinfo.element; [
31423142 <!ELEMENT objectinfo %ho; ((%info.class;)+)
3143 %beginpage.exclusion;>
3143 %beginpage.exclusion;>
31443144 <!--end of objectinfo.element-->]]>
31453145
31463146 <!ENTITY % objectinfo.attlist "INCLUDE">
31473147 <![ %objectinfo.attlist; [
31483148 <!ATTLIST objectinfo
3149 %common.attrib;
3150 %objectinfo.role.attrib;
3151 %local.objectinfo.attrib;
3149 %common.attrib;
3150 %objectinfo.role.attrib;
3151 %local.objectinfo.attrib;
31523152 >
31533153 <!--end of objectinfo.attlist-->]]>
31543154 <!--end of objectinfo.module-->]]>
31553155
31563156 <!--EntityRef: Name of an external entity containing the content
3157 of the object data-->
3157 of the object data-->
31583158 <!--FileRef: Filename, qualified by a pathname if desired,
3159 designating the file containing the content of the object data-->
3159 designating the file containing the content of the object data-->
31603160 <!--Format: Notation of the element content, if any-->
31613161 <!--SrcCredit: Information about the source of the image-->
31623162 <!ENTITY % local.objectdata.attrib "">
31633163 <!ENTITY % objectdata.attrib
3164 "
3165 entityref ENTITY #IMPLIED
3166 fileref CDATA #IMPLIED
3167 format (%notation.class;)
3168 #IMPLIED
3169 srccredit CDATA #IMPLIED
3170 %local.objectdata.attrib;"
3164 "
3165 entityref ENTITY #IMPLIED
3166 fileref CDATA #IMPLIED
3167 format (%notation.class;)
3168 #IMPLIED
3169 srccredit CDATA #IMPLIED
3170 %local.objectdata.attrib;"
31713171 >
31723172
31733173 <!ENTITY % videodata.module "INCLUDE">
31863186 <!--Width: Same as CALS reprowid (desired width)-->
31873187 <!--Depth: Same as CALS reprodep (desired depth)-->
31883188 <!--Align: Same as CALS hplace with 'none' removed; #IMPLIED means
3189 application-specific-->
3189 application-specific-->
31903190 <!--Scale: Conflation of CALS hscale and vscale-->
31913191 <!--Scalefit: Same as CALS scalefit-->
31923192 <!ATTLIST videodata
3193 width CDATA #IMPLIED
3194 contentwidth CDATA #IMPLIED
3195 depth CDATA #IMPLIED
3196 contentdepth CDATA #IMPLIED
3197 align (left
3198 |right
3199 |center) #IMPLIED
3200 valign (top
3201 |middle
3202 |bottom) #IMPLIED
3203 scale CDATA #IMPLIED
3204 scalefit %yesorno.attvals;
3205 #IMPLIED
3206 %objectdata.attrib;
3207 %common.attrib;
3208 %videodata.role.attrib;
3209 %local.videodata.attrib;
3193 width CDATA #IMPLIED
3194 contentwidth CDATA #IMPLIED
3195 depth CDATA #IMPLIED
3196 contentdepth CDATA #IMPLIED
3197 align (left
3198 |right
3199 |center) #IMPLIED
3200 valign (top
3201 |middle
3202 |bottom) #IMPLIED
3203 scale CDATA #IMPLIED
3204 scalefit %yesorno.attvals;
3205 #IMPLIED
3206 %objectdata.attrib;
3207 %common.attrib;
3208 %videodata.role.attrib;
3209 %local.videodata.attrib;
32103210 >
32113211 <!--end of videodata.attlist-->]]>
32123212 <!--end of videodata.module-->]]>
32243224 <!ENTITY % audiodata.attlist "INCLUDE">
32253225 <![ %audiodata.attlist; [
32263226 <!ATTLIST audiodata
3227 %objectdata.attrib;
3228 %common.attrib;
3229 %audiodata.role.attrib;
3230 %local.audiodata.attrib;
3227 %objectdata.attrib;
3228 %common.attrib;
3229 %audiodata.role.attrib;
3230 %local.audiodata.attrib;
32313231 >
32323232 <!--end of audiodata.attlist-->]]>
32333233 <!--end of audiodata.module-->]]>
32483248 <!--Width: Same as CALS reprowid (desired width)-->
32493249 <!--Depth: Same as CALS reprodep (desired depth)-->
32503250 <!--Align: Same as CALS hplace with 'none' removed; #IMPLIED means
3251 application-specific-->
3251 application-specific-->
32523252 <!--Scale: Conflation of CALS hscale and vscale-->
32533253 <!--Scalefit: Same as CALS scalefit-->
32543254 <!ATTLIST imagedata
3255 width CDATA #IMPLIED
3256 contentwidth CDATA #IMPLIED
3257 depth CDATA #IMPLIED
3258 contentdepth CDATA #IMPLIED
3259 align (left
3260 |right
3261 |center) #IMPLIED
3262 valign (top
3263 |middle
3264 |bottom) #IMPLIED
3265 scale CDATA #IMPLIED
3266 scalefit %yesorno.attvals;
3267 #IMPLIED
3268 %objectdata.attrib;
3269 %common.attrib;
3270 %imagedata.role.attrib;
3271 %local.imagedata.attrib;
3255 width CDATA #IMPLIED
3256 contentwidth CDATA #IMPLIED
3257 depth CDATA #IMPLIED
3258 contentdepth CDATA #IMPLIED
3259 align (left
3260 |right
3261 |center) #IMPLIED
3262 valign (top
3263 |middle
3264 |bottom) #IMPLIED
3265 scale CDATA #IMPLIED
3266 scalefit %yesorno.attvals;
3267 #IMPLIED
3268 %objectdata.attrib;
3269 %common.attrib;
3270 %imagedata.role.attrib;
3271 %local.imagedata.attrib;
32723272 >
32733273 <!--end of imagedata.attlist-->]]>
32743274 <!--end of imagedata.module-->]]>
32863286 <!ENTITY % textdata.attlist "INCLUDE">
32873287 <![ %textdata.attlist; [
32883288 <!ATTLIST textdata
3289 encoding CDATA #IMPLIED
3290 %objectdata.attrib;
3291 %common.attrib;
3292 %textdata.role.attrib;
3293 %local.textdata.attrib;
3289 encoding CDATA #IMPLIED
3290 %objectdata.attrib;
3291 %common.attrib;
3292 %textdata.role.attrib;
3293 %local.textdata.attrib;
32943294 >
32953295 <!--end of textdata.attlist-->]]>
32963296 <!--end of textdata.module-->]]>
33083308 <!ENTITY % caption.attlist "INCLUDE">
33093309 <![ %caption.attlist; [
33103310 <!ATTLIST caption
3311 %common.attrib;
3312 %caption.role.attrib;
3313 %local.caption.attrib;
3311 %common.attrib;
3312 %caption.role.attrib;
3313 %local.caption.attrib;
33143314 >
33153315 <!--end of caption.attlist-->]]>
33163316 <!--end of caption.module-->]]>
33233323 <!ENTITY % mediaobjectco.element "INCLUDE">
33243324 <![ %mediaobjectco.element; [
33253325 <!ELEMENT mediaobjectco %ho; (objectinfo?, imageobjectco,
3326 (imageobjectco|textobject)*)>
3326 (imageobjectco|textobject)*)>
33273327 <!--end of mediaobjectco.element-->]]>
33283328
33293329 <!ENTITY % mediaobjectco.attlist "INCLUDE">
33303330 <![ %mediaobjectco.attlist; [
33313331 <!ATTLIST mediaobjectco
3332 %common.attrib;
3333 %mediaobjectco.role.attrib;
3334 %local.mediaobjectco.attrib;
3332 %common.attrib;
3333 %mediaobjectco.role.attrib;
3334 %local.mediaobjectco.attrib;
33353335 >
33363336 <!--end of mediaobjectco.attlist-->]]>
33373337 <!--end of mediaobjectco.module-->]]>
33493349 <!ENTITY % imageobjectco.attlist "INCLUDE">
33503350 <![ %imageobjectco.attlist; [
33513351 <!ATTLIST imageobjectco
3352 %common.attrib;
3353 %imageobjectco.role.attrib;
3354 %local.imageobjectco.attrib;
3352 %common.attrib;
3353 %imageobjectco.role.attrib;
3354 %local.imageobjectco.attrib;
33553355 >
33563356 <!--end of imageobjectco.attlist-->]]>
33573357 <!--end of imageobjectco.module-->]]>
33783378 <!ENTITY % equation.attlist "INCLUDE">
33793379 <![%equation.attlist;[
33803380 <!ATTLIST equation
3381 %label.attrib;
3382 %common.attrib;
3383 %equation.role.attrib;
3384 %local.equation.attrib;
3381 %label.attrib;
3382 %common.attrib;
3383 %equation.role.attrib;
3384 %local.equation.attrib;
33853385 >
33863386 <!--end of equation.attlist-->]]>
33873387 <!--end of equation.module-->]]>
33993399 <!ENTITY % informalequation.attlist "INCLUDE">
34003400 <![%informalequation.attlist;[
34013401 <!ATTLIST informalequation
3402 %common.attrib;
3403 %informalequation.role.attrib;
3404 %local.informalequation.attrib;
3402 %common.attrib;
3403 %informalequation.role.attrib;
3404 %local.informalequation.attrib;
34053405 >
34063406 <!--end of informalequation.attlist-->]]>
34073407 <!--end of informalequation.module-->]]>
34193419 <!ENTITY % inlineequation.attlist "INCLUDE">
34203420 <![%inlineequation.attlist;[
34213421 <!ATTLIST inlineequation
3422 %common.attrib;
3423 %inlineequation.role.attrib;
3424 %local.inlineequation.attrib;
3422 %common.attrib;
3423 %inlineequation.role.attrib;
3424 %local.inlineequation.attrib;
34253425 >
34263426 <!--end of inlineequation.attlist-->]]>
34273427 <!--end of inlineequation.module-->]]>
34393439 <!ENTITY % alt.attlist "INCLUDE">
34403440 <![%alt.attlist;[
34413441 <!ATTLIST alt
3442 %common.attrib;
3443 %alt.role.attrib;
3444 %local.alt.attrib;
3442 %common.attrib;
3443 %alt.role.attrib;
3444 %local.alt.attrib;
34453445 >
34463446 <!--end of alt.attlist-->]]>
34473447 <!--end of alt.module-->]]>
34683468 <!-- Add common attributes to Table, TGroup, TBody, THead, TFoot, Row,
34693469 EntryTbl, and Entry (and InformalTable element). -->
34703470 <!ENTITY % secur
3471 "%common.attrib;
3472 %tables.role.attrib;">
3471 "%common.attrib;
3472 %tables.role.attrib;">
34733473
34743474 <!ENTITY % common.table.attribs
3475 "%bodyatt;
3476 %secur;">
3475 "%bodyatt;
3476 %secur;">
34773477
34783478 <!-- Content model for Table. -->
34793479 <!ENTITY % tbl.table.mdl
3480 "(blockinfo?, (%formalobject.title.content;), (%ndxterm.class;)*,
3481 textobject*,
3480 "(blockinfo?, (%formalobject.title.content;), (%ndxterm.class;)*,
3481 textobject*,
34823482 (graphic+|mediaobject+|tgroup+))">
34833483
34843484 <!-- Allow either objects or inlines; beware of REs between elements. -->
34943494 <!-- Add common attributes and the Label attribute to Table and -->
34953495 <!-- InformalTable. -->
34963496 <!ENTITY % bodyatt
3497 "%common.attrib;
3498 %label.attrib;
3499 %tables.role.attrib;">
3497 "%common.attrib;
3498 %label.attrib;
3499 %tables.role.attrib;">
35003500
35013501 <!ENTITY % common.table.attribs
3502 "%bodyatt;">
3502 "%bodyatt;">
35033503
35043504 <!-- Add common attributes to TGroup, ColSpec, TBody, THead, Row, Entry -->
35053505
35123512
35133513 <!-- Content model for Table. -->
35143514 <!ENTITY % tbl.table.mdl
3515 "(blockinfo?, (%formalobject.title.content;), (%ndxterm.class;)*,
3516 textobject*,
3515 "(blockinfo?, (%formalobject.title.content;), (%ndxterm.class;)*,
3516 textobject*,
35173517 (graphic+|mediaobject+|tgroup+))">
35183518
35193519 <!-- Allow either objects or inlines; beware of REs between elements. -->
35433543 <!--end of informaltable.element-->]]>
35443544
35453545 <!-- Frame, Colsep, and Rowsep must be repeated because
3546 they are not in entities in the table module. -->
3546 they are not in entities in the table module. -->
35473547 <!-- includes TabStyle, ToCentry, ShortEntry,
3548 Orient, PgWide -->
3548 Orient, PgWide -->
35493549 <!-- includes Label -->
35503550 <!-- includes common attributes -->
35513551
35533553 <!ENTITY % informaltable.attlist "INCLUDE">
35543554 <![%informaltable.attlist;[
35553555 <!ATTLIST informaltable
3556 frame (top
3557 |bottom
3558 |topbot
3559 |all
3560 |sides
3561 |none) #IMPLIED
3562 colsep %yesorno.attvals; #IMPLIED
3563 rowsep %yesorno.attvals; #IMPLIED
3564 %common.table.attribs;
3565 %tbl.table.att;
3566 %local.informaltable.attrib;
3556 frame (top
3557 |bottom
3558 |topbot
3559 |all
3560 |sides
3561 |none) #IMPLIED
3562 colsep %yesorno.attvals; #IMPLIED
3563 rowsep %yesorno.attvals; #IMPLIED
3564 %common.table.attribs;
3565 %tbl.table.att;
3566 %local.informaltable.attrib;
35673567 >
35683568 <!--end of informaltable.attlist-->]]>
35693569 <!--end of informaltable.module-->]]>
35863586 <!ENTITY % synopsis.attlist "INCLUDE">
35873587 <![%synopsis.attlist;[
35883588 <!ATTLIST synopsis
3589 %label.attrib;
3590 %linespecific.attrib;
3591 %common.attrib;
3592 %synopsis.role.attrib;
3593 %local.synopsis.attrib;
3589 %label.attrib;
3590 %linespecific.attrib;
3591 %common.attrib;
3592 %synopsis.role.attrib;
3593 %local.synopsis.attrib;
35943594 >
35953595 <!--end of synopsis.attlist-->]]>
35963596
36123612 <!--end of cmdsynopsis.element-->]]>
36133613
36143614 <!-- Sepchar: Character that should separate command and all
3615 top-level arguments; alternate value might be e.g., &Delta; -->
3615 top-level arguments; alternate value might be e.g., &Delta; -->
36163616
36173617
36183618 <!ENTITY % cmdsynopsis.attlist "INCLUDE">
36193619 <![%cmdsynopsis.attlist;[
36203620 <!ATTLIST cmdsynopsis
3621 %label.attrib;
3622 sepchar CDATA " "
3623 cmdlength CDATA #IMPLIED
3624 %common.attrib;
3625 %cmdsynopsis.role.attrib;
3626 %local.cmdsynopsis.attrib;
3621 %label.attrib;
3622 sepchar CDATA " "
3623 cmdlength CDATA #IMPLIED
3624 %common.attrib;
3625 %cmdsynopsis.role.attrib;
3626 %local.cmdsynopsis.attrib;
36273627 >
36283628 <!--end of cmdsynopsis.attlist-->]]>
36293629 <!--end of cmdsynopsis.module-->]]>
36363636 <!ENTITY % arg.element "INCLUDE">
36373637 <![%arg.element;[
36383638 <!ELEMENT arg %ho; (#PCDATA
3639 | arg
3640 | group
3641 | option
3642 | synopfragmentref
3643 | replaceable
3644 | sbr)*>
3639 | arg
3640 | group
3641 | option
3642 | synopfragmentref
3643 | replaceable
3644 | sbr)*>
36453645 <!--end of arg.element-->]]>
36463646
36473647 <!-- Choice: Whether Arg must be supplied: Opt (optional to
3648 supply, e.g. [arg]; the default), Req (required to supply,
3649 e.g. {arg}), or Plain (required to supply, e.g. arg) -->
3648 supply, e.g. [arg]; the default), Req (required to supply,
3649 e.g. {arg}), or Plain (required to supply, e.g. arg) -->
36503650 <!-- Rep: whether Arg is repeatable: Norepeat (e.g. arg without
3651 ellipsis; the default), or Repeat (e.g. arg...) -->
3651 ellipsis; the default), or Repeat (e.g. arg...) -->
36523652
36533653
36543654 <!ENTITY % arg.attlist "INCLUDE">
36553655 <![%arg.attlist;[
36563656 <!ATTLIST arg
3657 choice (opt
3658 |req
3659 |plain) 'opt'
3660 rep (norepeat
3661 |repeat) 'norepeat'
3662 %common.attrib;
3663 %arg.role.attrib;
3664 %local.arg.attrib;
3657 choice (opt
3658 |req
3659 |plain) 'opt'
3660 rep (norepeat
3661 |repeat) 'norepeat'
3662 %common.attrib;
3663 %arg.role.attrib;
3664 %local.arg.attrib;
36653665 >
36663666 <!--end of arg.attlist-->]]>
36673667 <!--end of arg.module-->]]>
36753675 <!ENTITY % group.element "INCLUDE">
36763676 <![%group.element;[
36773677 <!ELEMENT group %ho; ((arg | group | option | synopfragmentref
3678 | replaceable | sbr)+)>
3678 | replaceable | sbr)+)>
36793679 <!--end of group.element-->]]>
36803680
36813681 <!-- Choice: Whether Group must be supplied: Opt (optional to
3682 supply, e.g. [g1|g2|g3]; the default), Req (required to
3683 supply, e.g. {g1|g2|g3}), Plain (required to supply,
3684 e.g. g1|g2|g3), OptMult (can supply zero or more, e.g.
3685 [[g1|g2|g3]]), or ReqMult (must supply one or more, e.g.
3686 {{g1|g2|g3}}) -->
3682 supply, e.g. [g1|g2|g3]; the default), Req (required to
3683 supply, e.g. {g1|g2|g3}), Plain (required to supply,
3684 e.g. g1|g2|g3), OptMult (can supply zero or more, e.g.
3685 [[g1|g2|g3]]), or ReqMult (must supply one or more, e.g.
3686 {{g1|g2|g3}}) -->
36873687 <!-- Rep: whether Group is repeatable: Norepeat (e.g. group
3688 without ellipsis; the default), or Repeat (e.g. group...) -->
3688 without ellipsis; the default), or Repeat (e.g. group...) -->
36893689
36903690
36913691 <!ENTITY % group.attlist "INCLUDE">
36923692 <![%group.attlist;[
36933693 <!ATTLIST group
3694 choice (opt
3695 |req
3696 |plain) 'opt'
3697 rep (norepeat
3698 |repeat) 'norepeat'
3699 %common.attrib;
3700 %group.role.attrib;
3701 %local.group.attrib;
3694 choice (opt
3695 |req
3696 |plain) 'opt'
3697 rep (norepeat
3698 |repeat) 'norepeat'
3699 %common.attrib;
3700 %group.role.attrib;
3701 %local.group.attrib;
37023702 >
37033703 <!--end of group.attlist-->]]>
37043704 <!--end of group.module-->]]>
37173717 <!ENTITY % sbr.attlist "INCLUDE">
37183718 <![%sbr.attlist;[
37193719 <!ATTLIST sbr
3720 %common.attrib;
3721 %sbr.role.attrib;
3722 %local.sbr.attrib;
3720 %common.attrib;
3721 %sbr.role.attrib;
3722 %local.sbr.attrib;
37233723 >
37243724 <!--end of sbr.attlist-->]]>
37253725 <!--end of sbr.module-->]]>
37353735 <!--end of synopfragmentref.element-->]]>
37363736
37373737 <!-- to SynopFragment of complex synopsis
3738 material for separate referencing -->
3738 material for separate referencing -->
37393739
37403740
37413741 <!ENTITY % synopfragmentref.attlist "INCLUDE">
37423742 <![%synopfragmentref.attlist;[
37433743 <!ATTLIST synopfragmentref
3744 %linkendreq.attrib; %common.attrib;
3745 %synopfragmentref.role.attrib;
3746 %local.synopfragmentref.attrib;
3744 %linkendreq.attrib; %common.attrib;
3745 %synopfragmentref.role.attrib;
3746 %local.synopfragmentref.attrib;
37473747 >
37483748 <!--end of synopfragmentref.attlist-->]]>
37493749 <!--end of synopfragmentref.module-->]]>
37613761 <!ENTITY % synopfragment.attlist "INCLUDE">
37623762 <![%synopfragment.attlist;[
37633763 <!ATTLIST synopfragment
3764 %idreq.common.attrib;
3765 %synopfragment.role.attrib;
3766 %local.synopfragment.attrib;
3764 %idreq.common.attrib;
3765 %synopfragment.role.attrib;
3766 %local.synopfragment.attrib;
37673767 >
37683768 <!--end of synopfragment.attlist-->]]>
37693769 <!--end of synopfragment.module-->]]>
37913791 <!ENTITY % funcsynopsis.attlist "INCLUDE">
37923792 <![%funcsynopsis.attlist;[
37933793 <!ATTLIST funcsynopsis
3794 %label.attrib;
3795 %common.attrib;
3796 %funcsynopsis.role.attrib;
3797 %local.funcsynopsis.attrib;
3794 %label.attrib;
3795 %common.attrib;
3796 %funcsynopsis.role.attrib;
3797 %local.funcsynopsis.attrib;
37983798 >
37993799 <!--end of funcsynopsis.attlist-->]]>
38003800 <!--end of funcsynopsis.module-->]]>
38123812 <!ENTITY % funcsynopsisinfo.attlist "INCLUDE">
38133813 <![%funcsynopsisinfo.attlist;[
38143814 <!ATTLIST funcsynopsisinfo
3815 %linespecific.attrib;
3816 %common.attrib;
3817 %funcsynopsisinfo.role.attrib;
3818 %local.funcsynopsisinfo.attrib;
3815 %linespecific.attrib;
3816 %common.attrib;
3817 %funcsynopsisinfo.role.attrib;
3818 %local.funcsynopsisinfo.attrib;
38193819 >
38203820 <!--end of funcsynopsisinfo.attlist-->]]>
38213821 <!--end of funcsynopsisinfo.module-->]]>
38333833 <!ENTITY % funcprototype.attlist "INCLUDE">
38343834 <![%funcprototype.attlist;[
38353835 <!ATTLIST funcprototype
3836 %common.attrib;
3837 %funcprototype.role.attrib;
3838 %local.funcprototype.attrib;
3836 %common.attrib;
3837 %funcprototype.role.attrib;
3838 %local.funcprototype.attrib;
38393839 >
38403840 <!--end of funcprototype.attlist-->]]>
38413841 <!--end of funcprototype.module-->]]>
38483848 <!ENTITY % funcdef.element "INCLUDE">
38493849 <![%funcdef.element;[
38503850 <!ELEMENT funcdef %ho; (#PCDATA
3851 | type
3852 | replaceable
3853 | function)*>
3851 | type
3852 | replaceable
3853 | function)*>
38543854 <!--end of funcdef.element-->]]>
38553855
38563856 <!ENTITY % funcdef.attlist "INCLUDE">
38573857 <![%funcdef.attlist;[
38583858 <!ATTLIST funcdef
3859 %common.attrib;
3860 %funcdef.role.attrib;
3861 %local.funcdef.attrib;
3859 %common.attrib;
3860 %funcdef.role.attrib;
3861 %local.funcdef.attrib;
38623862 >
38633863 <!--end of funcdef.attlist-->]]>
38643864 <!--end of funcdef.module-->]]>
38763876 <!ENTITY % void.attlist "INCLUDE">
38773877 <![%void.attlist;[
38783878 <!ATTLIST void
3879 %common.attrib;
3880 %void.role.attrib;
3881 %local.void.attrib;
3879 %common.attrib;
3880 %void.role.attrib;
3881 %local.void.attrib;
38823882 >
38833883 <!--end of void.attlist-->]]>
38843884 <!--end of void.module-->]]>
38963896 <!ENTITY % varargs.attlist "INCLUDE">
38973897 <![%varargs.attlist;[
38983898 <!ATTLIST varargs
3899 %common.attrib;
3900 %varargs.role.attrib;
3901 %local.varargs.attrib;
3899 %common.attrib;
3900 %varargs.role.attrib;
3901 %local.varargs.attrib;
39023902 >
39033903 <!--end of varargs.attlist-->]]>
39043904 <!--end of varargs.module-->]]>
39163916 <!ENTITY % paramdef.element "INCLUDE">
39173917 <![%paramdef.element;[
39183918 <!ELEMENT paramdef %ho; (#PCDATA
3919 | type
3920 | replaceable
3921 | parameter
3922 | funcparams)*>
3919 | type
3920 | replaceable
3921 | parameter
3922 | funcparams)*>
39233923 <!--end of paramdef.element-->]]>
39243924
39253925 <!ENTITY % paramdef.attlist "INCLUDE">
39263926 <![%paramdef.attlist;[
39273927 <!ATTLIST paramdef
3928 %common.attrib;
3929 %paramdef.role.attrib;
3930 %local.paramdef.attrib;
3928 %common.attrib;
3929 %paramdef.role.attrib;
3930 %local.paramdef.attrib;
39313931 >
39323932 <!--end of paramdef.attlist-->]]>
39333933 <!--end of paramdef.module-->]]>
39453945 <!ENTITY % funcparams.attlist "INCLUDE">
39463946 <![%funcparams.attlist;[
39473947 <!ATTLIST funcparams
3948 %common.attrib;
3949 %funcparams.role.attrib;
3950 %local.funcparams.attrib;
3948 %common.attrib;
3949 %funcparams.role.attrib;
3950 %local.funcparams.attrib;
39513951 >
39523952 <!--end of funcparams.attlist-->]]>
39533953 <!--end of funcparams.module-->]]>
39783978 <!ENTITY % classsynopsis.attlist "INCLUDE">
39793979 <![%classsynopsis.attlist;[
39803980 <!ATTLIST classsynopsis
3981 language CDATA #IMPLIED
3982 class (class|interface) "class"
3983 %common.attrib;
3984 %classsynopsis.role.attrib;
3985 %local.classsynopsis.attrib;
3981 language CDATA #IMPLIED
3982 class (class|interface) "class"
3983 %common.attrib;
3984 %classsynopsis.role.attrib;
3985 %local.classsynopsis.attrib;
39863986 >
39873987 <!--end of classsynopsis.attlist-->]]>
39883988 <!--end of classsynopsis.module-->]]>
40004000 <!ENTITY % classsynopsisinfo.attlist "INCLUDE">
40014001 <![ %classsynopsisinfo.attlist; [
40024002 <!ATTLIST classsynopsisinfo
4003 %linespecific.attrib;
4004 %common.attrib;
4005 %classsynopsisinfo.role.attrib;
4006 %local.classsynopsisinfo.attrib;
4003 %linespecific.attrib;
4004 %common.attrib;
4005 %classsynopsisinfo.role.attrib;
4006 %local.classsynopsisinfo.attrib;
40074007 >
40084008 <!--end of classsynopsisinfo.attlist-->]]>
40094009 <!--end of classsynopsisinfo.module-->]]>
40214021 <!ENTITY % ooclass.attlist "INCLUDE">
40224022 <![%ooclass.attlist;[
40234023 <!ATTLIST ooclass
4024 %common.attrib;
4025 %ooclass.role.attrib;
4026 %local.ooclass.attrib;
4024 %common.attrib;
4025 %ooclass.role.attrib;
4026 %local.ooclass.attrib;
40274027 >
40284028 <!--end of ooclass.attlist-->]]>
40294029 <!--end of ooclass.module-->]]>
40414041 <!ENTITY % oointerface.attlist "INCLUDE">
40424042 <![%oointerface.attlist;[
40434043 <!ATTLIST oointerface
4044 %common.attrib;
4045 %oointerface.role.attrib;
4046 %local.oointerface.attrib;
4044 %common.attrib;
4045 %oointerface.role.attrib;
4046 %local.oointerface.attrib;
40474047 >
40484048 <!--end of oointerface.attlist-->]]>
40494049 <!--end of oointerface.module-->]]>
40614061 <!ENTITY % ooexception.attlist "INCLUDE">
40624062 <![%ooexception.attlist;[
40634063 <!ATTLIST ooexception
4064 %common.attrib;
4065 %ooexception.role.attrib;
4066 %local.ooexception.attrib;
4064 %common.attrib;
4065 %ooexception.role.attrib;
4066 %local.ooexception.attrib;
40674067 >
40684068 <!--end of ooexception.attlist-->]]>
40694069 <!--end of ooexception.module-->]]>
40814081 <!ENTITY % modifier.attlist "INCLUDE">
40824082 <![%modifier.attlist;[
40834083 <!ATTLIST modifier
4084 %common.attrib;
4085 %modifier.role.attrib;
4086 %local.modifier.attrib;
4084 %common.attrib;
4085 %modifier.role.attrib;
4086 %local.modifier.attrib;
40874087 >
40884088 <!--end of modifier.attlist-->]]>
40894089 <!--end of modifier.module-->]]>
41014101 <!ENTITY % interfacename.attlist "INCLUDE">
41024102 <![%interfacename.attlist;[
41034103 <!ATTLIST interfacename
4104 %common.attrib;
4105 %interfacename.role.attrib;
4106 %local.interfacename.attrib;
4104 %common.attrib;
4105 %interfacename.role.attrib;
4106 %local.interfacename.attrib;
41074107 >
41084108 <!--end of interfacename.attlist-->]]>
41094109 <!--end of interfacename.module-->]]>
41214121 <!ENTITY % exceptionname.attlist "INCLUDE">
41224122 <![%exceptionname.attlist;[
41234123 <!ATTLIST exceptionname
4124 %common.attrib;
4125 %exceptionname.role.attrib;
4126 %local.exceptionname.attrib;
4124 %common.attrib;
4125 %exceptionname.role.attrib;
4126 %local.exceptionname.attrib;
41274127 >
41284128 <!--end of exceptionname.attlist-->]]>
41294129 <!--end of exceptionname.module-->]]>
41414141 <!ENTITY % fieldsynopsis.attlist "INCLUDE">
41424142 <![%fieldsynopsis.attlist;[
41434143 <!ATTLIST fieldsynopsis
4144 language CDATA #IMPLIED
4145 %common.attrib;
4146 %fieldsynopsis.role.attrib;
4147 %local.fieldsynopsis.attrib;
4144 language CDATA #IMPLIED
4145 %common.attrib;
4146 %fieldsynopsis.role.attrib;
4147 %local.fieldsynopsis.attrib;
41484148 >
41494149 <!--end of fieldsynopsis.attlist-->]]>
41504150 <!--end of fieldsynopsis.module-->]]>
41624162 <!ENTITY % initializer.attlist "INCLUDE">
41634163 <![%initializer.attlist;[
41644164 <!ATTLIST initializer
4165 %common.attrib;
4166 %initializer.role.attrib;
4167 %local.initializer.attrib;
4165 %common.attrib;
4166 %initializer.role.attrib;
4167 %local.initializer.attrib;
41684168 >
41694169 <!--end of initializer.attlist-->]]>
41704170 <!--end of initializer.module-->]]>
41854185 <!ENTITY % constructorsynopsis.attlist "INCLUDE">
41864186 <![%constructorsynopsis.attlist;[
41874187 <!ATTLIST constructorsynopsis
4188 language CDATA #IMPLIED
4189 %common.attrib;
4190 %constructorsynopsis.role.attrib;
4191 %local.constructorsynopsis.attrib;
4188 language CDATA #IMPLIED
4189 %common.attrib;
4190 %constructorsynopsis.role.attrib;
4191 %local.constructorsynopsis.attrib;
41924192 >
41934193 <!--end of constructorsynopsis.attlist-->]]>
41944194 <!--end of constructorsynopsis.module-->]]>
42094209 <!ENTITY % destructorsynopsis.attlist "INCLUDE">
42104210 <![%destructorsynopsis.attlist;[
42114211 <!ATTLIST destructorsynopsis
4212 language CDATA #IMPLIED
4213 %common.attrib;
4214 %destructorsynopsis.role.attrib;
4215 %local.destructorsynopsis.attrib;
4212 language CDATA #IMPLIED
4213 %common.attrib;
4214 %destructorsynopsis.role.attrib;
4215 %local.destructorsynopsis.attrib;
42164216 >
42174217 <!--end of destructorsynopsis.attlist-->]]>
42184218 <!--end of destructorsynopsis.module-->]]>
42354235 <!ENTITY % methodsynopsis.attlist "INCLUDE">
42364236 <![%methodsynopsis.attlist;[
42374237 <!ATTLIST methodsynopsis
4238 language CDATA #IMPLIED
4239 %common.attrib;
4240 %methodsynopsis.role.attrib;
4241 %local.methodsynopsis.attrib;
4238 language CDATA #IMPLIED
4239 %common.attrib;
4240 %methodsynopsis.role.attrib;
4241 %local.methodsynopsis.attrib;
42424242 >
42434243 <!--end of methodsynopsis.attlist-->]]>
42444244 <!--end of methodsynopsis.module-->]]>
42564256 <!ENTITY % methodname.attlist "INCLUDE">
42574257 <![%methodname.attlist;[
42584258 <!ATTLIST methodname
4259 %common.attrib;
4260 %methodname.role.attrib;
4261 %local.methodname.attrib;
4259 %common.attrib;
4260 %methodname.role.attrib;
4261 %local.methodname.attrib;
42624262 >
42634263 <!--end of methodname.attlist-->]]>
42644264 <!--end of methodname.module-->]]>
42794279 <!ENTITY % methodparam.attlist "INCLUDE">
42804280 <![%methodparam.attlist;[
42814281 <!ATTLIST methodparam
4282 choice (opt
4283 |req
4284 |plain) "req"
4285 rep (norepeat
4286 |repeat) "norepeat"
4287 %common.attrib;
4288 %methodparam.role.attrib;
4289 %local.methodparam.attrib;
4282 choice (opt
4283 |req
4284 |plain) "req"
4285 rep (norepeat
4286 |repeat) "norepeat"
4287 %common.attrib;
4288 %methodparam.role.attrib;
4289 %local.methodparam.attrib;
42904290 >
42914291 <!--end of methodparam.attlist-->]]>
42924292 <!--end of methodparam.module-->]]>
43204320 <!ENTITY % ackno.attlist "INCLUDE">
43214321 <![%ackno.attlist;[
43224322 <!ATTLIST ackno
4323 %common.attrib;
4324 %ackno.role.attrib;
4325 %local.ackno.attrib;
4323 %common.attrib;
4324 %ackno.role.attrib;
4325 %local.ackno.attrib;
43264326 >
43274327 <!--end of ackno.attlist-->]]>
43284328 <!--end of ackno.module-->]]>
43394339 <!ENTITY % address.element "INCLUDE">
43404340 <![%address.element;[
43414341 <!ELEMENT address %ho; (#PCDATA|personname|%person.ident.mix;
4342 |street|pob|postcode|city|state|country|phone
4343 |fax|email|otheraddr)*>
4342 |street|pob|postcode|city|state|country|phone
4343 |fax|email|otheraddr)*>
43444344 <!--end of address.element-->]]>
43454345
43464346 <!ENTITY % address.attlist "INCLUDE">
43474347 <![%address.attlist;[
43484348 <!ATTLIST address
4349 %linespecific.attrib;
4350 %common.attrib;
4351 %address.role.attrib;
4352 %local.address.attrib;
4349 %linespecific.attrib;
4350 %common.attrib;
4351 %address.role.attrib;
4352 %local.address.attrib;
43534353 >
43544354 <!--end of address.attlist-->]]>
43554355 <!--end of address.module-->]]>
43674367 <!ENTITY % street.attlist "INCLUDE">
43684368 <![%street.attlist;[
43694369 <!ATTLIST street
4370 %common.attrib;
4371 %street.role.attrib;
4372 %local.street.attrib;
4370 %common.attrib;
4371 %street.role.attrib;
4372 %local.street.attrib;
43734373 >
43744374 <!--end of street.attlist-->]]>
43754375 <!--end of street.module-->]]>
43874387 <!ENTITY % pob.attlist "INCLUDE">
43884388 <![%pob.attlist;[
43894389 <!ATTLIST pob
4390 %common.attrib;
4391 %pob.role.attrib;
4392 %local.pob.attrib;
4390 %common.attrib;
4391 %pob.role.attrib;
4392 %local.pob.attrib;
43934393 >
43944394 <!--end of pob.attlist-->]]>
43954395 <!--end of pob.module-->]]>
44074407 <!ENTITY % postcode.attlist "INCLUDE">
44084408 <![%postcode.attlist;[
44094409 <!ATTLIST postcode
4410 %common.attrib;
4411 %postcode.role.attrib;
4412 %local.postcode.attrib;
4410 %common.attrib;
4411 %postcode.role.attrib;
4412 %local.postcode.attrib;
44134413 >
44144414 <!--end of postcode.attlist-->]]>
44154415 <!--end of postcode.module-->]]>
44274427 <!ENTITY % city.attlist "INCLUDE">
44284428 <![%city.attlist;[
44294429 <!ATTLIST city
4430 %common.attrib;
4431 %city.role.attrib;
4432 %local.city.attrib;
4430 %common.attrib;
4431 %city.role.attrib;
4432 %local.city.attrib;
44334433 >
44344434 <!--end of city.attlist-->]]>
44354435 <!--end of city.module-->]]>
44474447 <!ENTITY % state.attlist "INCLUDE">
44484448 <![%state.attlist;[
44494449 <!ATTLIST state
4450 %common.attrib;
4451 %state.role.attrib;
4452 %local.state.attrib;
4450 %common.attrib;
4451 %state.role.attrib;
4452 %local.state.attrib;
44534453 >
44544454 <!--end of state.attlist-->]]>
44554455 <!--end of state.module-->]]>
44674467 <!ENTITY % country.attlist "INCLUDE">
44684468 <![%country.attlist;[
44694469 <!ATTLIST country
4470 %common.attrib;
4471 %country.role.attrib;
4472 %local.country.attrib;
4470 %common.attrib;
4471 %country.role.attrib;
4472 %local.country.attrib;
44734473 >
44744474 <!--end of country.attlist-->]]>
44754475 <!--end of country.module-->]]>
44874487 <!ENTITY % phone.attlist "INCLUDE">
44884488 <![%phone.attlist;[
44894489 <!ATTLIST phone
4490 %common.attrib;
4491 %phone.role.attrib;
4492 %local.phone.attrib;
4490 %common.attrib;
4491 %phone.role.attrib;
4492 %local.phone.attrib;
44934493 >
44944494 <!--end of phone.attlist-->]]>
44954495 <!--end of phone.module-->]]>
45074507 <!ENTITY % fax.attlist "INCLUDE">
45084508 <![%fax.attlist;[
45094509 <!ATTLIST fax
4510 %common.attrib;
4511 %fax.role.attrib;
4512 %local.fax.attrib;
4510 %common.attrib;
4511 %fax.role.attrib;
4512 %local.fax.attrib;
45134513 >
45144514 <!--end of fax.attlist-->]]>
45154515 <!--end of fax.module-->]]>
45294529 <!ENTITY % otheraddr.attlist "INCLUDE">
45304530 <![%otheraddr.attlist;[
45314531 <!ATTLIST otheraddr
4532 %common.attrib;
4533 %otheraddr.role.attrib;
4534 %local.otheraddr.attrib;
4532 %common.attrib;
4533 %otheraddr.role.attrib;
4534 %local.otheraddr.attrib;
45354535 >
45364536 <!--end of otheraddr.attlist-->]]>
45374537 <!--end of otheraddr.module-->]]>
45494549 <!ENTITY % affiliation.element "INCLUDE">
45504550 <![%affiliation.element;[
45514551 <!ELEMENT affiliation %ho; (shortaffil?, jobtitle*, orgname?, orgdiv*,
4552 address*)>
4552 address*)>
45534553 <!--end of affiliation.element-->]]>
45544554
45554555 <!ENTITY % affiliation.attlist "INCLUDE">
45564556 <![%affiliation.attlist;[
45574557 <!ATTLIST affiliation
4558 %common.attrib;
4559 %affiliation.role.attrib;
4560 %local.affiliation.attrib;
4558 %common.attrib;
4559 %affiliation.role.attrib;
4560 %local.affiliation.attrib;
45614561 >
45624562 <!--end of affiliation.attlist-->]]>
45634563 <!--end of affiliation.module-->]]>
45754575 <!ENTITY % shortaffil.attlist "INCLUDE">
45764576 <![%shortaffil.attlist;[
45774577 <!ATTLIST shortaffil
4578 %common.attrib;
4579 %shortaffil.role.attrib;
4580 %local.shortaffil.attrib;
4578 %common.attrib;
4579 %shortaffil.role.attrib;
4580 %local.shortaffil.attrib;
45814581 >
45824582 <!--end of shortaffil.attlist-->]]>
45834583 <!--end of shortaffil.module-->]]>
45954595 <!ENTITY % jobtitle.attlist "INCLUDE">
45964596 <![%jobtitle.attlist;[
45974597 <!ATTLIST jobtitle
4598 %common.attrib;
4599 %jobtitle.role.attrib;
4600 %local.jobtitle.attrib;
4598 %common.attrib;
4599 %jobtitle.role.attrib;
4600 %local.jobtitle.attrib;
46014601 >
46024602 <!--end of jobtitle.attlist-->]]>
46034603 <!--end of jobtitle.module-->]]>
46174617 <!ENTITY % orgdiv.attlist "INCLUDE">
46184618 <![%orgdiv.attlist;[
46194619 <!ATTLIST orgdiv
4620 %common.attrib;
4621 %orgdiv.role.attrib;
4622 %local.orgdiv.attrib;
4620 %common.attrib;
4621 %orgdiv.role.attrib;
4622 %local.orgdiv.attrib;
46234623 >
46244624 <!--end of orgdiv.attlist-->]]>
46254625 <!--end of orgdiv.module-->]]>
46424642 <!ENTITY % artpagenums.attlist "INCLUDE">
46434643 <![%artpagenums.attlist;[
46444644 <!ATTLIST artpagenums
4645 %common.attrib;
4646 %artpagenums.role.attrib;
4647 %local.artpagenums.attrib;
4645 %common.attrib;
4646 %artpagenums.role.attrib;
4647 %local.artpagenums.attrib;
46484648 >
46494649 <!--end of artpagenums.attlist-->]]>
46504650 <!--end of artpagenums.module-->]]>
46644664 <!ENTITY % personname.attlist "INCLUDE">
46654665 <![%personname.attlist;[
46664666 <!ATTLIST personname
4667 %common.attrib;
4668 %personname.role.attrib;
4669 %local.personname.attrib;
4667 %common.attrib;
4668 %personname.role.attrib;
4669 %local.personname.attrib;
46704670 >
46714671 <!--end of personname.attlist-->]]>
46724672 <!--end of personname.module-->]]>
46864686 <!ENTITY % author.attlist "INCLUDE">
46874687 <![%author.attlist;[
46884688 <!ATTLIST author
4689 %common.attrib;
4690 %author.role.attrib;
4691 %local.author.attrib;
4689 %common.attrib;
4690 %author.role.attrib;
4691 %local.author.attrib;
46924692 >
46934693 <!--end of author.attlist-->]]>
46944694 <!--(see "Personal identity elements" for %person.ident.mix;)-->
47114711 <!ENTITY % authorgroup.attlist "INCLUDE">
47124712 <![%authorgroup.attlist;[
47134713 <!ATTLIST authorgroup
4714 %common.attrib;
4715 %authorgroup.role.attrib;
4716 %local.authorgroup.attrib;
4714 %common.attrib;
4715 %authorgroup.role.attrib;
4716 %local.authorgroup.attrib;
47174717 >
47184718 <!--end of authorgroup.attlist-->]]>
47194719 <!--end of authorgroup.module-->]]>
47364736 <!ENTITY % collab.attlist "INCLUDE">
47374737 <![%collab.attlist;[
47384738 <!ATTLIST collab
4739 %common.attrib;
4740 %collab.role.attrib;
4741 %local.collab.attrib;
4739 %common.attrib;
4740 %collab.role.attrib;
4741 %local.collab.attrib;
47424742 >
47434743 <!--end of collab.attlist-->]]>
47444744 <!--end of collab.module-->]]>
47564756 <!ENTITY % collabname.attlist "INCLUDE">
47574757 <![%collabname.attlist;[
47584758 <!ATTLIST collabname
4759 %common.attrib;
4760 %collabname.role.attrib;
4761 %local.collabname.attrib;
4759 %common.attrib;
4760 %collabname.role.attrib;
4761 %local.collabname.attrib;
47624762 >
47634763 <!--end of collabname.attlist-->]]>
47644764 <!--end of collabname.module-->]]>
47864786 <!ENTITY % authorinitials.attlist "INCLUDE">
47874787 <![%authorinitials.attlist;[
47884788 <!ATTLIST authorinitials
4789 %common.attrib;
4790 %authorinitials.role.attrib;
4791 %local.authorinitials.attrib;
4789 %common.attrib;
4790 %authorinitials.role.attrib;
4791 %local.authorinitials.attrib;
47924792 >
47934793 <!--end of authorinitials.attlist-->]]>
47944794 <!--end of authorinitials.module-->]]>
48104810 <!ENTITY % confgroup.attlist "INCLUDE">
48114811 <![%confgroup.attlist;[
48124812 <!ATTLIST confgroup
4813 %common.attrib;
4814 %confgroup.role.attrib;
4815 %local.confgroup.attrib;
4813 %common.attrib;
4814 %confgroup.role.attrib;
4815 %local.confgroup.attrib;
48164816 >
48174817 <!--end of confgroup.attlist-->]]>
48184818 <!--end of confgroup.module-->]]>
48304830 <!ENTITY % confdates.attlist "INCLUDE">
48314831 <![%confdates.attlist;[
48324832 <!ATTLIST confdates
4833 %common.attrib;
4834 %confdates.role.attrib;
4835 %local.confdates.attrib;
4833 %common.attrib;
4834 %confdates.role.attrib;
4835 %local.confdates.attrib;
48364836 >
48374837 <!--end of confdates.attlist-->]]>
48384838 <!--end of confdates.module-->]]>
48504850 <!ENTITY % conftitle.attlist "INCLUDE">
48514851 <![%conftitle.attlist;[
48524852 <!ATTLIST conftitle
4853 %common.attrib;
4854 %conftitle.role.attrib;
4855 %local.conftitle.attrib;
4853 %common.attrib;
4854 %conftitle.role.attrib;
4855 %local.conftitle.attrib;
48564856 >
48574857 <!--end of conftitle.attlist-->]]>
48584858 <!--end of conftitle.module-->]]>
48704870 <!ENTITY % confnum.attlist "INCLUDE">
48714871 <![%confnum.attlist;[
48724872 <!ATTLIST confnum
4873 %common.attrib;
4874 %confnum.role.attrib;
4875 %local.confnum.attrib;
4873 %common.attrib;
4874 %confnum.role.attrib;
4875 %local.confnum.attrib;
48764876 >
48774877 <!--end of confnum.attlist-->]]>
48784878 <!--end of confnum.module-->]]>
48924892 <!ENTITY % confsponsor.attlist "INCLUDE">
48934893 <![%confsponsor.attlist;[
48944894 <!ATTLIST confsponsor
4895 %common.attrib;
4896 %confsponsor.role.attrib;
4897 %local.confsponsor.attrib;
4895 %common.attrib;
4896 %confsponsor.role.attrib;
4897 %local.confsponsor.attrib;
48984898 >
48994899 <!--end of confsponsor.attlist-->]]>
49004900 <!--end of confsponsor.module-->]]>
49154915 <!ENTITY % contractnum.attlist "INCLUDE">
49164916 <![%contractnum.attlist;[
49174917 <!ATTLIST contractnum
4918 %common.attrib;
4919 %contractnum.role.attrib;
4920 %local.contractnum.attrib;
4918 %common.attrib;
4919 %contractnum.role.attrib;
4920 %local.contractnum.attrib;
49214921 >
49224922 <!--end of contractnum.attlist-->]]>
49234923 <!--end of contractnum.module-->]]>
49374937 <!ENTITY % contractsponsor.attlist "INCLUDE">
49384938 <![%contractsponsor.attlist;[
49394939 <!ATTLIST contractsponsor
4940 %common.attrib;
4941 %contractsponsor.role.attrib;
4942 %local.contractsponsor.attrib;
4940 %common.attrib;
4941 %contractsponsor.role.attrib;
4942 %local.contractsponsor.attrib;
49434943 >
49444944 <!--end of contractsponsor.attlist-->]]>
49454945 <!--end of contractsponsor.module-->]]>
49614961 <!ENTITY % copyright.attlist "INCLUDE">
49624962 <![%copyright.attlist;[
49634963 <!ATTLIST copyright
4964 %common.attrib;
4965 %copyright.role.attrib;
4966 %local.copyright.attrib;
4964 %common.attrib;
4965 %copyright.role.attrib;
4966 %local.copyright.attrib;
49674967 >
49684968 <!--end of copyright.attlist-->]]>
49694969 <!--end of copyright.module-->]]>
49814981 <!ENTITY % year.attlist "INCLUDE">
49824982 <![%year.attlist;[
49834983 <!ATTLIST year
4984 %common.attrib;
4985 %year.role.attrib;
4986 %local.year.attrib;
4984 %common.attrib;
4985 %year.role.attrib;
4986 %local.year.attrib;
49874987 >
49884988 <!--end of year.attlist-->]]>
49894989 <!--end of year.module-->]]>
50015001 <!ENTITY % holder.attlist "INCLUDE">
50025002 <![%holder.attlist;[
50035003 <!ATTLIST holder
5004 %common.attrib;
5005 %holder.role.attrib;
5006 %local.holder.attrib;
5004 %common.attrib;
5005 %holder.role.attrib;
5006 %local.holder.attrib;
50075007 >
50085008 <!--end of holder.attlist-->]]>
50095009 <!--end of holder.module-->]]>
50245024 <!ENTITY % corpauthor.attlist "INCLUDE">
50255025 <![%corpauthor.attlist;[
50265026 <!ATTLIST corpauthor
5027 %common.attrib;
5028 %corpauthor.role.attrib;
5029 %local.corpauthor.attrib;
5027 %common.attrib;
5028 %corpauthor.role.attrib;
5029 %local.corpauthor.attrib;
50305030 >
50315031 <!--end of corpauthor.attlist-->]]>
50325032 <!--end of corpauthor.module-->]]>
50465046 <!ENTITY % corpname.attlist "INCLUDE">
50475047 <![%corpname.attlist;[
50485048 <!ATTLIST corpname
5049 %common.attrib;
5050 %corpname.role.attrib;
5051 %local.corpname.attrib;
5049 %common.attrib;
5050 %corpname.role.attrib;
5051 %local.corpname.attrib;
50525052 >
50535053 <!--end of corpname.attlist-->]]>
50545054 <!--end of corpname.module-->]]>
50685068 <!ENTITY % date.attlist "INCLUDE">
50695069 <![%date.attlist;[
50705070 <!ATTLIST date
5071 %common.attrib;
5072 %date.role.attrib;
5073 %local.date.attrib;
5071 %common.attrib;
5072 %date.role.attrib;
5073 %local.date.attrib;
50745074 >
50755075 <!--end of date.attlist-->]]>
50765076 <!--end of date.module-->]]>
50905090 <!ENTITY % edition.attlist "INCLUDE">
50915091 <![%edition.attlist;[
50925092 <!ATTLIST edition
5093 %common.attrib;
5094 %edition.role.attrib;
5095 %local.edition.attrib;
5093 %common.attrib;
5094 %edition.role.attrib;
5095 %local.edition.attrib;
50965096 >
50975097 <!--end of edition.attlist-->]]>
50985098 <!--end of edition.module-->]]>
51125112 <!ENTITY % editor.attlist "INCLUDE">
51135113 <![%editor.attlist;[
51145114 <!ATTLIST editor
5115 %common.attrib;
5116 %editor.role.attrib;
5117 %local.editor.attrib;
5115 %common.attrib;
5116 %editor.role.attrib;
5117 %local.editor.attrib;
51185118 >
51195119 <!--end of editor.attlist-->]]>
51205120 <!--(see "Personal identity elements" for %person.ident.mix;)-->
51355135 <!ENTITY % isbn.attlist "INCLUDE">
51365136 <![%isbn.attlist;[
51375137 <!ATTLIST isbn
5138 %common.attrib;
5139 %isbn.role.attrib;
5140 %local.isbn.attrib;
5138 %common.attrib;
5139 %isbn.role.attrib;
5140 %local.isbn.attrib;
51415141 >
51425142 <!--end of isbn.attlist-->]]>
51435143 <!--end of isbn.module-->]]>
51575157 <!ENTITY % issn.attlist "INCLUDE">
51585158 <![%issn.attlist;[
51595159 <!ATTLIST issn
5160 %common.attrib;
5161 %issn.role.attrib;
5162 %local.issn.attrib;
5160 %common.attrib;
5161 %issn.role.attrib;
5162 %local.issn.attrib;
51635163 >
51645164 <!--end of issn.attlist-->]]>
51655165 <!--end of issn.module-->]]>
51665166
51675167 <!-- BiblioId ................. -->
51685168 <!ENTITY % biblio.class.attrib
5169 "class (uri
5169 "class (uri
51705170 |doi
51715171 |isbn
51725172 |issn
51735173 |libraryofcongress
51745174 |pubnumber
51755175 |other) #IMPLIED
5176 otherclass CDATA #IMPLIED"
5176 otherclass CDATA #IMPLIED"
51775177 >
51785178
51795179 <!ENTITY % biblioid.module "INCLUDE">
51895189 <!ENTITY % biblioid.attlist "INCLUDE">
51905190 <![%biblioid.attlist;[
51915191 <!ATTLIST biblioid
5192 %biblio.class.attrib;
5193 %common.attrib;
5194 %biblioid.role.attrib;
5195 %local.biblioid.attrib;
5192 %biblio.class.attrib;
5193 %common.attrib;
5194 %biblioid.role.attrib;
5195 %local.biblioid.attrib;
51965196 >
51975197 <!--end of biblioid.attlist-->]]>
51985198 <!--end of biblioid.module-->]]>
52125212 <!ENTITY % citebiblioid.attlist "INCLUDE">
52135213 <![%citebiblioid.attlist;[
52145214 <!ATTLIST citebiblioid
5215 %biblio.class.attrib;
5216 %common.attrib;
5217 %citebiblioid.role.attrib;
5218 %local.citebiblioid.attrib;
5215 %biblio.class.attrib;
5216 %common.attrib;
5217 %citebiblioid.role.attrib;
5218 %local.citebiblioid.attrib;
52195219 >
52205220 <!--end of citebiblioid.attlist-->]]>
52215221 <!--end of citebiblioid.module-->]]>
52355235 <!ENTITY % bibliosource.attlist "INCLUDE">
52365236 <![%bibliosource.attlist;[
52375237 <!ATTLIST bibliosource
5238 %biblio.class.attrib;
5239 %common.attrib;
5240 %bibliosource.role.attrib;
5241 %local.bibliosource.attrib;
5238 %biblio.class.attrib;
5239 %common.attrib;
5240 %bibliosource.role.attrib;
5241 %local.bibliosource.attrib;
52425242 >
52435243 <!--end of bibliosource.attlist-->]]>
52445244 <!--end of bibliosource.module-->]]>
52655265 |hasformat
52665266 |othertype
52675267 %local.bibliorelation.types;) #IMPLIED
5268 othertype CDATA #IMPLIED
5268 othertype CDATA #IMPLIED
52695269 ">
52705270
52715271 <!ENTITY % bibliorelation.role.attrib "%role.attrib;">
52785278 <!ENTITY % bibliorelation.attlist "INCLUDE">
52795279 <![%bibliorelation.attlist;[
52805280 <!ATTLIST bibliorelation
5281 %biblio.class.attrib;
5282 %bibliorelation.type.attrib;
5283 %common.attrib;
5284 %bibliorelation.role.attrib;
5285 %local.bibliorelation.attrib;
5281 %biblio.class.attrib;
5282 %bibliorelation.type.attrib;
5283 %common.attrib;
5284 %bibliorelation.role.attrib;
5285 %local.bibliorelation.attrib;
52865286 >
52875287 <!--end of bibliorelation.attlist-->]]>
52885288 <!--end of bibliorelation.module-->]]>
53025302 <!ENTITY % bibliocoverage.attlist "INCLUDE">
53035303 <![%bibliocoverage.attlist;[
53045304 <!ATTLIST bibliocoverage
5305 spatial (dcmipoint|iso3166|dcmibox|tgn|otherspatial) #IMPLIED
5306 otherspatial CDATA #IMPLIED
5307 temporal (dcmiperiod|w3c-dtf|othertemporal) #IMPLIED
5308 othertemporal CDATA #IMPLIED
5309 %common.attrib;
5310 %bibliocoverage.role.attrib;
5311 %local.bibliocoverage.attrib;
5305 spatial (dcmipoint|iso3166|dcmibox|tgn|otherspatial) #IMPLIED
5306 otherspatial CDATA #IMPLIED
5307 temporal (dcmiperiod|w3c-dtf|othertemporal) #IMPLIED
5308 othertemporal CDATA #IMPLIED
5309 %common.attrib;
5310 %bibliocoverage.role.attrib;
5311 %local.bibliocoverage.attrib;
53125312 >
53135313 <!--end of bibliocoverage.attlist-->]]>
53145314 <!--end of bibliocoverage.module-->]]>
53285328 <!ENTITY % invpartnumber.attlist "INCLUDE">
53295329 <![%invpartnumber.attlist;[
53305330 <!ATTLIST invpartnumber
5331 %common.attrib;
5332 %invpartnumber.role.attrib;
5333 %local.invpartnumber.attrib;
5331 %common.attrib;
5332 %invpartnumber.role.attrib;
5333 %local.invpartnumber.attrib;
53345334 >
53355335 <!--end of invpartnumber.attlist-->]]>
53365336 <!--end of invpartnumber.module-->]]>
53505350 <!ENTITY % issuenum.attlist "INCLUDE">
53515351 <![%issuenum.attlist;[
53525352 <!ATTLIST issuenum
5353 %common.attrib;
5354 %issuenum.role.attrib;
5355 %local.issuenum.attrib;
5353 %common.attrib;
5354 %issuenum.role.attrib;
5355 %local.issuenum.attrib;
53565356 >
53575357 <!--end of issuenum.attlist-->]]>
53585358 <!--end of issuenum.module-->]]>
53675367 <!ENTITY % legalnotice.element "INCLUDE">
53685368 <![%legalnotice.element;[
53695369 <!ELEMENT legalnotice %ho; (blockinfo?, title?, (%legalnotice.mix;)+)
5370 %formal.exclusion;>
5370 %formal.exclusion;>
53715371 <!--end of legalnotice.element-->]]>
53725372
53735373 <!ENTITY % legalnotice.attlist "INCLUDE">
53745374 <![%legalnotice.attlist;[
53755375 <!ATTLIST legalnotice
5376 %common.attrib;
5377 %legalnotice.role.attrib;
5378 %local.legalnotice.attrib;
5376 %common.attrib;
5377 %legalnotice.role.attrib;
5378 %local.legalnotice.attrib;
53795379 >
53805380 <!--end of legalnotice.attlist-->]]>
53815381 <!--end of legalnotice.module-->]]>
53905390 <!ENTITY % modespec.element "INCLUDE">
53915391 <![%modespec.element;[
53925392 <!ELEMENT modespec %ho; (%docinfo.char.mix;)*
5393 %ubiq.exclusion;>
5393 %ubiq.exclusion;>
53945394 <!--end of modespec.element-->]]>
53955395
53965396 <!-- Application: Type of action required for completion
5397 of the links to which the ModeSpec is relevant (e.g.,
5398 retrieval query) -->
5397 of the links to which the ModeSpec is relevant (e.g.,
5398 retrieval query) -->
53995399
54005400
54015401 <!ENTITY % modespec.attlist "INCLUDE">
54025402 <![%modespec.attlist;[
54035403 <!ATTLIST modespec
5404 application NOTATION
5405 (%notation.class;) #IMPLIED
5406 %common.attrib;
5407 %modespec.role.attrib;
5408 %local.modespec.attrib;
5404 application NOTATION
5405 (%notation.class;) #IMPLIED
5406 %common.attrib;
5407 %modespec.role.attrib;
5408 %local.modespec.attrib;
54095409 >
54105410 <!--end of modespec.attlist-->]]>
54115411 <!--end of modespec.module-->]]>
54255425 <!ENTITY % orgname.attlist "INCLUDE">
54265426 <![%orgname.attlist;[
54275427 <!ATTLIST orgname
5428 %common.attrib;
5429 class (corporation|nonprofit|consortium|informal|other) #IMPLIED
5430 otherclass CDATA #IMPLIED
5431 %orgname.role.attrib;
5432 %local.orgname.attrib;
5428 %common.attrib;
5429 class (corporation|nonprofit|consortium|informal|other) #IMPLIED
5430 otherclass CDATA #IMPLIED
5431 %orgname.role.attrib;
5432 %local.orgname.attrib;
54335433 >
54345434 <!--end of orgname.attlist-->]]>
54355435 <!--end of orgname.module-->]]>
54505450 <!ENTITY % othercredit.attlist "INCLUDE">
54515451 <![%othercredit.attlist;[
54525452 <!ATTLIST othercredit
5453 %common.attrib;
5454 %othercredit.role.attrib;
5455 %local.othercredit.attrib;
5453 %common.attrib;
5454 %othercredit.role.attrib;
5455 %local.othercredit.attrib;
54565456 >
54575457 <!--end of othercredit.attlist-->]]>
54585458 <!--(see "Personal identity elements" for %person.ident.mix;)-->
54735473 <!ENTITY % pagenums.attlist "INCLUDE">
54745474 <![%pagenums.attlist;[
54755475 <!ATTLIST pagenums
5476 %common.attrib;
5477 %pagenums.role.attrib;
5478 %local.pagenums.attrib;
5476 %common.attrib;
5477 %pagenums.role.attrib;
5478 %local.pagenums.attrib;
54795479 >
54805480 <!--end of pagenums.attlist-->]]>
54815481 <!--end of pagenums.module-->]]>
55005500 <!ENTITY % contrib.attlist "INCLUDE">
55015501 <![%contrib.attlist;[
55025502 <!ATTLIST contrib
5503 %common.attrib;
5504 %contrib.role.attrib;
5505 %local.contrib.attrib;
5503 %common.attrib;
5504 %contrib.role.attrib;
5505 %local.contrib.attrib;
55065506 >
55075507 <!--end of contrib.attlist-->]]>
55085508 <!--end of contrib.module-->]]>
55205520 <!ENTITY % firstname.attlist "INCLUDE">
55215521 <![%firstname.attlist;[
55225522 <!ATTLIST firstname
5523 %common.attrib;
5524 %firstname.role.attrib;
5525 %local.firstname.attrib;
5523 %common.attrib;
5524 %firstname.role.attrib;
5525 %local.firstname.attrib;
55265526 >
55275527 <!--end of firstname.attlist-->]]>
55285528 <!--end of firstname.module-->]]>
55405540 <!ENTITY % honorific.attlist "INCLUDE">
55415541 <![%honorific.attlist;[
55425542 <!ATTLIST honorific
5543 %common.attrib;
5544 %honorific.role.attrib;
5545 %local.honorific.attrib;
5543 %common.attrib;
5544 %honorific.role.attrib;
5545 %local.honorific.attrib;
55465546 >
55475547 <!--end of honorific.attlist-->]]>
55485548 <!--end of honorific.module-->]]>
55605560 <!ENTITY % lineage.attlist "INCLUDE">
55615561 <![%lineage.attlist;[
55625562 <!ATTLIST lineage
5563 %common.attrib;
5564 %lineage.role.attrib;
5565 %local.lineage.attrib;
5563 %common.attrib;
5564 %lineage.role.attrib;
5565 %local.lineage.attrib;
55665566 >
55675567 <!--end of lineage.attlist-->]]>
55685568 <!--end of lineage.module-->]]>
55805580 <!ENTITY % othername.attlist "INCLUDE">
55815581 <![%othername.attlist;[
55825582 <!ATTLIST othername
5583 %common.attrib;
5584 %othername.role.attrib;
5585 %local.othername.attrib;
5583 %common.attrib;
5584 %othername.role.attrib;
5585 %local.othername.attrib;
55865586 >
55875587 <!--end of othername.attlist-->]]>
55885588 <!--end of othername.module-->]]>
56005600 <!ENTITY % surname.attlist "INCLUDE">
56015601 <![%surname.attlist;[
56025602 <!ATTLIST surname
5603 %common.attrib;
5604 %surname.role.attrib;
5605 %local.surname.attrib;
5603 %common.attrib;
5604 %surname.role.attrib;
5605 %local.surname.attrib;
56065606 >
56075607 <!--end of surname.attlist-->]]>
56085608 <!--end of surname.module-->]]>
56235623 <!ENTITY % printhistory.attlist "INCLUDE">
56245624 <![%printhistory.attlist;[
56255625 <!ATTLIST printhistory
5626 %common.attrib;
5627 %printhistory.role.attrib;
5628 %local.printhistory.attrib;
5626 %common.attrib;
5627 %printhistory.role.attrib;
5628 %local.printhistory.attrib;
56295629 >
56305630 <!--end of printhistory.attlist-->]]>
56315631 <!--end of printhistory.module-->]]>
56485648 <!ENTITY % productname.attlist "INCLUDE">
56495649 <![%productname.attlist;[
56505650 <!ATTLIST productname
5651 class (service
5652 |trade
5653 |registered
5654 |copyright) 'trade'
5655 %common.attrib;
5656 %productname.role.attrib;
5657 %local.productname.attrib;
5651 class (service
5652 |trade
5653 |registered
5654 |copyright) 'trade'
5655 %common.attrib;
5656 %productname.role.attrib;
5657 %local.productname.attrib;
56585658 >
56595659 <!--end of productname.attlist-->]]>
56605660 <!--end of productname.module-->]]>
56745674 <!ENTITY % productnumber.attlist "INCLUDE">
56755675 <![%productnumber.attlist;[
56765676 <!ATTLIST productnumber
5677 %common.attrib;
5678 %productnumber.role.attrib;
5679 %local.productnumber.attrib;
5677 %common.attrib;
5678 %productnumber.role.attrib;
5679 %local.productnumber.attrib;
56805680 >
56815681 <!--end of productnumber.attlist-->]]>
56825682 <!--end of productnumber.module-->]]>
56965696 <!ENTITY % pubdate.attlist "INCLUDE">
56975697 <![%pubdate.attlist;[
56985698 <!ATTLIST pubdate
5699 %common.attrib;
5700 %pubdate.role.attrib;
5701 %local.pubdate.attrib;
5699 %common.attrib;
5700 %pubdate.role.attrib;
5701 %local.pubdate.attrib;
57025702 >
57035703 <!--end of pubdate.attlist-->]]>
57045704 <!--end of pubdate.module-->]]>
57205720 <!ENTITY % publisher.attlist "INCLUDE">
57215721 <![%publisher.attlist;[
57225722 <!ATTLIST publisher
5723 %common.attrib;
5724 %publisher.role.attrib;
5725 %local.publisher.attrib;
5723 %common.attrib;
5724 %publisher.role.attrib;
5725 %local.publisher.attrib;
57265726 >
57275727 <!--end of publisher.attlist-->]]>
57285728 <!--end of publisher.module-->]]>
57405740 <!ENTITY % publishername.attlist "INCLUDE">
57415741 <![%publishername.attlist;[
57425742 <!ATTLIST publishername
5743 %common.attrib;
5744 %publishername.role.attrib;
5745 %local.publishername.attrib;
5743 %common.attrib;
5744 %publishername.role.attrib;
5745 %local.publishername.attrib;
57465746 >
57475747 <!--end of publishername.attlist-->]]>
57485748 <!--end of publishername.module-->]]>
57655765 <!ENTITY % pubsnumber.attlist "INCLUDE">
57665766 <![%pubsnumber.attlist;[
57675767 <!ATTLIST pubsnumber
5768 %common.attrib;
5769 %pubsnumber.role.attrib;
5770 %local.pubsnumber.attrib;
5768 %common.attrib;
5769 %pubsnumber.role.attrib;
5770 %local.pubsnumber.attrib;
57715771 >
57725772 <!--end of pubsnumber.attlist-->]]>
57735773 <!--end of pubsnumber.module-->]]>
57875787 <!ENTITY % releaseinfo.attlist "INCLUDE">
57885788 <![%releaseinfo.attlist;[
57895789 <!ATTLIST releaseinfo
5790 %common.attrib;
5791 %releaseinfo.role.attrib;
5792 %local.releaseinfo.attrib;
5790 %common.attrib;
5791 %releaseinfo.role.attrib;
5792 %local.releaseinfo.attrib;
57935793 >
57945794 <!--end of releaseinfo.attlist-->]]>
57955795 <!--end of releaseinfo.module-->]]>
58115811 <!ENTITY % revhistory.attlist "INCLUDE">
58125812 <![%revhistory.attlist;[
58135813 <!ATTLIST revhistory
5814 %common.attrib;
5815 %revhistory.role.attrib;
5816 %local.revhistory.attrib;
5814 %common.attrib;
5815 %revhistory.role.attrib;
5816 %local.revhistory.attrib;
58175817 >
58185818 <!--end of revhistory.attlist-->]]>
58195819 <!--end of revhistory.module-->]]>
58325832 <!ENTITY % revision.attlist "INCLUDE">
58335833 <![%revision.attlist;[
58345834 <!ATTLIST revision
5835 %common.attrib;
5836 %revision.role.attrib;
5837 %local.revision.attrib;
5835 %common.attrib;
5836 %revision.role.attrib;
5837 %local.revision.attrib;
58385838 >
58395839 <!--end of revision.attlist-->]]>
58405840 <!--end of revision.module-->]]>
58525852 <!ENTITY % revnumber.attlist "INCLUDE">
58535853 <![%revnumber.attlist;[
58545854 <!ATTLIST revnumber
5855 %common.attrib;
5856 %revnumber.role.attrib;
5857 %local.revnumber.attrib;
5855 %common.attrib;
5856 %revnumber.role.attrib;
5857 %local.revnumber.attrib;
58585858 >
58595859 <!--end of revnumber.attlist-->]]>
58605860 <!--end of revnumber.module-->]]>
58755875 <!ENTITY % revremark.attlist "INCLUDE">
58765876 <![%revremark.attlist;[
58775877 <!ATTLIST revremark
5878 %common.attrib;
5879 %revremark.role.attrib;
5880 %local.revremark.attrib;
5878 %common.attrib;
5879 %revremark.role.attrib;
5880 %local.revremark.attrib;
58815881 >
58825882 <!--end of revremark.attlist-->]]>
58835883 <!--end of revremark.module-->]]>
58955895 <!ENTITY % revdescription.attlist "INCLUDE">
58965896 <![ %revdescription.attlist; [
58975897 <!ATTLIST revdescription
5898 %common.attrib;
5899 %revdescription.role.attrib;
5900 %local.revdescription.attrib;
5898 %common.attrib;
5899 %revdescription.role.attrib;
5900 %local.revdescription.attrib;
59015901 >
59025902 <!--end of revdescription.attlist-->]]>
59035903 <!--end of revdescription.module-->]]>
59185918 <!ENTITY % seriesvolnums.attlist "INCLUDE">
59195919 <![%seriesvolnums.attlist;[
59205920 <!ATTLIST seriesvolnums
5921 %common.attrib;
5922 %seriesvolnums.role.attrib;
5923 %local.seriesvolnums.attrib;
5921 %common.attrib;
5922 %seriesvolnums.role.attrib;
5923 %local.seriesvolnums.attrib;
59245924 >
59255925 <!--end of seriesvolnums.attlist-->]]>
59265926 <!--end of seriesvolnums.module-->]]>
59405940 <!ENTITY % volumenum.attlist "INCLUDE">
59415941 <![%volumenum.attlist;[
59425942 <!ATTLIST volumenum
5943 %common.attrib;
5944 %volumenum.role.attrib;
5945 %local.volumenum.attrib;
5943 %common.attrib;
5944 %volumenum.role.attrib;
5945 %local.volumenum.attrib;
59465946 >
59475947 <!--end of volumenum.attlist-->]]>
59485948 <!--end of volumenum.module-->]]>
59695969 <!ENTITY % accel.attlist "INCLUDE">
59705970 <![%accel.attlist;[
59715971 <!ATTLIST accel
5972 %common.attrib;
5973 %accel.role.attrib;
5974 %local.accel.attrib;
5972 %common.attrib;
5973 %accel.role.attrib;
5974 %local.accel.attrib;
59755975 >
59765976 <!--end of accel.attlist-->]]>
59775977 <!--end of accel.module-->]]>
59895989 <!ENTITY % action.attlist "INCLUDE">
59905990 <![%action.attlist;[
59915991 <!ATTLIST action
5992 %moreinfo.attrib;
5993 %common.attrib;
5994 %action.role.attrib;
5995 %local.action.attrib;
5992 %moreinfo.attrib;
5993 %common.attrib;
5994 %action.role.attrib;
5995 %local.action.attrib;
59965996 >
59975997 <!--end of action.attlist-->]]>
59985998 <!--end of action.module-->]]>
60106010 <!ENTITY % application.attlist "INCLUDE">
60116011 <![%application.attlist;[
60126012 <!ATTLIST application
6013 class (hardware
6014 |software) #IMPLIED
6015 %moreinfo.attrib;
6016 %common.attrib;
6017 %application.role.attrib;
6018 %local.application.attrib;
6013 class (hardware
6014 |software) #IMPLIED
6015 %moreinfo.attrib;
6016 %common.attrib;
6017 %application.role.attrib;
6018 %local.application.attrib;
60196019 >
60206020 <!--end of application.attlist-->]]>
60216021 <!--end of application.module-->]]>
60336033 <!ENTITY % classname.attlist "INCLUDE">
60346034 <![%classname.attlist;[
60356035 <!ATTLIST classname
6036 %common.attrib;
6037 %classname.role.attrib;
6038 %local.classname.attrib;
6036 %common.attrib;
6037 %classname.role.attrib;
6038 %local.classname.attrib;
60396039 >
60406040 <!--end of classname.attlist-->]]>
60416041 <!--end of classname.module-->]]>
60596059 <!ENTITY % co.attlist "INCLUDE">
60606060 <![%co.attlist;[
60616061 <!ATTLIST co
6062 %label.attrib;
6063 %linkends.attrib;
6064 %idreq.common.attrib;
6065 %co.role.attrib;
6066 %local.co.attrib;
6062 %label.attrib;
6063 %linkends.attrib;
6064 %idreq.common.attrib;
6065 %co.role.attrib;
6066 %local.co.attrib;
60676067 >
60686068 <!--end of co.attlist-->]]>
60696069 <!--end of co.module-->]]>
60856085 <!ENTITY % coref.attlist "INCLUDE">
60866086 <![%coref.attlist;[
60876087 <!ATTLIST coref
6088 %label.attrib;
6089 %linkendreq.attrib;
6090 %common.attrib;
6091 %coref.role.attrib;
6092 %local.coref.attrib;
6088 %label.attrib;
6089 %linkendreq.attrib;
6090 %common.attrib;
6091 %coref.role.attrib;
6092 %local.coref.attrib;
60936093 >
60946094 <!--end of coref.attlist-->]]>
60956095 <!--end of coref.module-->]]>
61076107 <!ENTITY % command.attlist "INCLUDE">
61086108 <![%command.attlist;[
61096109 <!ATTLIST command
6110 %moreinfo.attrib;
6111 %common.attrib;
6112 %command.role.attrib;
6113 %local.command.attrib;
6110 %moreinfo.attrib;
6111 %common.attrib;
6112 %command.role.attrib;
6113 %local.command.attrib;
61146114 >
61156115 <!--end of command.attlist-->]]>
61166116 <!--end of command.module-->]]>
61286128 <!ENTITY % computeroutput.attlist "INCLUDE">
61296129 <![%computeroutput.attlist;[
61306130 <!ATTLIST computeroutput
6131 %moreinfo.attrib;
6132 %common.attrib;
6133 %computeroutput.role.attrib;
6134 %local.computeroutput.attrib;
6131 %moreinfo.attrib;
6132 %common.attrib;
6133 %computeroutput.role.attrib;
6134 %local.computeroutput.attrib;
61356135 >
61366136 <!--end of computeroutput.attlist-->]]>
61376137 <!--end of computeroutput.module-->]]>
61526152 <!ENTITY % database.attlist "INCLUDE">
61536153 <![%database.attlist;[
61546154 <!ATTLIST database
6155 class (name
6156 |table
6157 |field
6158 |key1
6159 |key2
6160 |record) #IMPLIED
6161 %moreinfo.attrib;
6162 %common.attrib;
6163 %database.role.attrib;
6164 %local.database.attrib;
6155 class (name
6156 |table
6157 |field
6158 |key1
6159 |key2
6160 |record) #IMPLIED
6161 %moreinfo.attrib;
6162 %common.attrib;
6163 %database.role.attrib;
6164 %local.database.attrib;
61656165 >
61666166 <!--end of database.attlist-->]]>
61676167 <!--end of database.module-->]]>
61796179 <!ENTITY % email.attlist "INCLUDE">
61806180 <![%email.attlist;[
61816181 <!ATTLIST email
6182 %common.attrib;
6183 %email.role.attrib;
6184 %local.email.attrib;
6182 %common.attrib;
6183 %email.role.attrib;
6184 %local.email.attrib;
61856185 >
61866186 <!--end of email.attlist-->]]>
61876187 <!--end of email.module-->]]>
61996199 <!ENTITY % envar.attlist "INCLUDE">
62006200 <![%envar.attlist;[
62016201 <!ATTLIST envar
6202 %common.attrib;
6203 %envar.role.attrib;
6204 %local.envar.attrib;
6202 %common.attrib;
6203 %envar.role.attrib;
6204 %local.envar.attrib;
62056205 >
62066206 <!--end of envar.attlist-->]]>
62076207 <!--end of envar.module-->]]>
62206220 <!ENTITY % errorcode.attlist "INCLUDE">
62216221 <![%errorcode.attlist;[
62226222 <!ATTLIST errorcode
6223 %moreinfo.attrib;
6224 %common.attrib;
6225 %errorcode.role.attrib;
6226 %local.errorcode.attrib;
6223 %moreinfo.attrib;
6224 %common.attrib;
6225 %errorcode.role.attrib;
6226 %local.errorcode.attrib;
62276227 >
62286228 <!--end of errorcode.attlist-->]]>
62296229 <!--end of errorcode.module-->]]>
62416241 <!ENTITY % errorname.attlist "INCLUDE">
62426242 <![%errorname.attlist;[
62436243 <!ATTLIST errorname
6244 %common.attrib;
6245 %errorname.role.attrib;
6246 %local.errorname.attrib;
6244 %common.attrib;
6245 %errorname.role.attrib;
6246 %local.errorname.attrib;
62476247 >
62486248 <!--end of errorname.attlist-->]]>
62496249 <!--end of errorname.module-->]]>
62616261 <!ENTITY % errortext.attlist "INCLUDE">
62626262 <![%errortext.attlist;[
62636263 <!ATTLIST errortext
6264 %common.attrib;
6265 %errortext.role.attrib;
6266 %local.errortext.attrib;
6264 %common.attrib;
6265 %errortext.role.attrib;
6266 %local.errortext.attrib;
62676267 >
62686268 <!--end of errortext.attlist-->]]>
62696269 <!--end of errortext.module-->]]>
62816281 <!ENTITY % errortype.attlist "INCLUDE">
62826282 <![%errortype.attlist;[
62836283 <!ATTLIST errortype
6284 %common.attrib;
6285 %errortype.role.attrib;
6286 %local.errortype.attrib;
6284 %common.attrib;
6285 %errortype.role.attrib;
6286 %local.errortype.attrib;
62876287 >
62886288 <!--end of errortype.attlist-->]]>
62896289 <!--end of errortype.module-->]]>
63006300
63016301 <!-- Class: Type of filename the element names; no default -->
63026302 <!-- Path: Search path (possibly system-specific) in which
6303 file can be found -->
6303 file can be found -->
63046304
63056305
63066306 <!ENTITY % filename.attlist "INCLUDE">
63076307 <![%filename.attlist;[
63086308 <!ATTLIST filename
6309 class (headerfile
6309 class (headerfile
63106310 |partition
63116311 |devicefile
63126312 |libraryfile
63136313 |directory
63146314 |extension
6315 |symlink) #IMPLIED
6316 path CDATA #IMPLIED
6317 %moreinfo.attrib;
6318 %common.attrib;
6319 %filename.role.attrib;
6320 %local.filename.attrib;
6315 |symlink) #IMPLIED
6316 path CDATA #IMPLIED
6317 %moreinfo.attrib;
6318 %common.attrib;
6319 %filename.role.attrib;
6320 %local.filename.attrib;
63216321 >
63226322 <!--end of filename.attlist-->]]>
63236323 <!--end of filename.module-->]]>
63356335 <!ENTITY % function.attlist "INCLUDE">
63366336 <![%function.attlist;[
63376337 <!ATTLIST function
6338 %moreinfo.attrib;
6339 %common.attrib;
6340 %function.role.attrib;
6341 %local.function.attrib;
6338 %moreinfo.attrib;
6339 %common.attrib;
6340 %function.role.attrib;
6341 %local.function.attrib;
63426342 >
63436343 <!--end of function.attlist-->]]>
63446344 <!--end of function.module-->]]>
63566356 <!ENTITY % guibutton.attlist "INCLUDE">
63576357 <![%guibutton.attlist;[
63586358 <!ATTLIST guibutton
6359 %moreinfo.attrib;
6360 %common.attrib;
6361 %guibutton.role.attrib;
6362 %local.guibutton.attrib;
6359 %moreinfo.attrib;
6360 %common.attrib;
6361 %guibutton.role.attrib;
6362 %local.guibutton.attrib;
63636363 >
63646364 <!--end of guibutton.attlist-->]]>
63656365 <!--end of guibutton.module-->]]>
63776377 <!ENTITY % guiicon.attlist "INCLUDE">
63786378 <![%guiicon.attlist;[
63796379 <!ATTLIST guiicon
6380 %moreinfo.attrib;
6381 %common.attrib;
6382 %guiicon.role.attrib;
6383 %local.guiicon.attrib;
6380 %moreinfo.attrib;
6381 %common.attrib;
6382 %guiicon.role.attrib;
6383 %local.guiicon.attrib;
63846384 >
63856385 <!--end of guiicon.attlist-->]]>
63866386 <!--end of guiicon.module-->]]>
63986398 <!ENTITY % guilabel.attlist "INCLUDE">
63996399 <![%guilabel.attlist;[
64006400 <!ATTLIST guilabel
6401 %moreinfo.attrib;
6402 %common.attrib;
6403 %guilabel.role.attrib;
6404 %local.guilabel.attrib;
6401 %moreinfo.attrib;
6402 %common.attrib;
6403 %guilabel.role.attrib;
6404 %local.guilabel.attrib;
64056405 >
64066406 <!--end of guilabel.attlist-->]]>
64076407 <!--end of guilabel.module-->]]>
64196419 <!ENTITY % guimenu.attlist "INCLUDE">
64206420 <![%guimenu.attlist;[
64216421 <!ATTLIST guimenu
6422 %moreinfo.attrib;
6423 %common.attrib;
6424 %guimenu.role.attrib;
6425 %local.guimenu.attrib;
6422 %moreinfo.attrib;
6423 %common.attrib;
6424 %guimenu.role.attrib;
6425 %local.guimenu.attrib;
64266426 >
64276427 <!--end of guimenu.attlist-->]]>
64286428 <!--end of guimenu.module-->]]>
64406440 <!ENTITY % guimenuitem.attlist "INCLUDE">
64416441 <![%guimenuitem.attlist;[
64426442 <!ATTLIST guimenuitem
6443 %moreinfo.attrib;
6444 %common.attrib;
6445 %guimenuitem.role.attrib;
6446 %local.guimenuitem.attrib;
6443 %moreinfo.attrib;
6444 %common.attrib;
6445 %guimenuitem.role.attrib;
6446 %local.guimenuitem.attrib;
64476447 >
64486448 <!--end of guimenuitem.attlist-->]]>
64496449 <!--end of guimenuitem.module-->]]>
64616461 <!ENTITY % guisubmenu.attlist "INCLUDE">
64626462 <![%guisubmenu.attlist;[
64636463 <!ATTLIST guisubmenu
6464 %moreinfo.attrib;
6465 %common.attrib;
6466 %guisubmenu.role.attrib;
6467 %local.guisubmenu.attrib;
6464 %moreinfo.attrib;
6465 %common.attrib;
6466 %guisubmenu.role.attrib;
6467 %local.guisubmenu.attrib;
64686468 >
64696469 <!--end of guisubmenu.attlist-->]]>
64706470 <!--end of guisubmenu.module-->]]>
64826482 <!ENTITY % hardware.attlist "INCLUDE">
64836483 <![%hardware.attlist;[
64846484 <!ATTLIST hardware
6485 %moreinfo.attrib;
6486 %common.attrib;
6487 %hardware.role.attrib;
6488 %local.hardware.attrib;
6485 %moreinfo.attrib;
6486 %common.attrib;
6487 %hardware.role.attrib;
6488 %local.hardware.attrib;
64896489 >
64906490 <!--end of hardware.attlist-->]]>
64916491 <!--end of hardware.module-->]]>
65066506 <!ENTITY % interface.attlist "INCLUDE">
65076507 <![%interface.attlist;[
65086508 <!ATTLIST interface
6509 %moreinfo.attrib;
6510 %common.attrib;
6511 %interface.role.attrib;
6512 %local.interface.attrib;
6509 %moreinfo.attrib;
6510 %common.attrib;
6511 %interface.role.attrib;
6512 %local.interface.attrib;
65136513 >
65146514 <!--end of interface.attlist-->]]>
65156515 <!--end of interface.module-->]]>
65276527 <!ENTITY % keycap.attlist "INCLUDE">
65286528 <![%keycap.attlist;[
65296529 <!ATTLIST keycap
6530 %moreinfo.attrib;
6531 %common.attrib;
6532 %keycap.role.attrib;
6533 %local.keycap.attrib;
6530 %moreinfo.attrib;
6531 %common.attrib;
6532 %keycap.role.attrib;
6533 %local.keycap.attrib;
65346534 >
65356535 <!--end of keycap.attlist-->]]>
65366536 <!--end of keycap.module-->]]>
65486548 <!ENTITY % keycode.attlist "INCLUDE">
65496549 <![%keycode.attlist;[
65506550 <!ATTLIST keycode
6551 %common.attrib;
6552 %keycode.role.attrib;
6553 %local.keycode.attrib;
6551 %common.attrib;
6552 %keycode.role.attrib;
6553 %local.keycode.attrib;
65546554 >
65556555 <!--end of keycode.attlist-->]]>
65566556 <!--end of keycode.module-->]]>
65686568 <!ENTITY % keycombo.attlist "INCLUDE">
65696569 <![%keycombo.attlist;[
65706570 <!ATTLIST keycombo
6571 %keyaction.attrib;
6572 %moreinfo.attrib;
6573 %common.attrib;
6574 %keycombo.role.attrib;
6575 %local.keycombo.attrib;
6571 %keyaction.attrib;
6572 %moreinfo.attrib;
6573 %common.attrib;
6574 %keycombo.role.attrib;
6575 %local.keycombo.attrib;
65766576 >
65776577 <!--end of keycombo.attlist-->]]>
65786578 <!--end of keycombo.module-->]]>
65906590 <!ENTITY % keysym.attlist "INCLUDE">
65916591 <![%keysym.attlist;[
65926592 <!ATTLIST keysym
6593 %common.attrib;
6594 %keysysm.role.attrib;
6595 %local.keysym.attrib;
6593 %common.attrib;
6594 %keysysm.role.attrib;
6595 %local.keysym.attrib;
65966596 >
65976597 <!--end of keysym.attlist-->]]>
65986598 <!--end of keysym.module-->]]>
66106610 <!ENTITY % lineannotation.attlist "INCLUDE">
66116611 <![%lineannotation.attlist;[
66126612 <!ATTLIST lineannotation
6613 %common.attrib;
6614 %lineannotation.role.attrib;
6615 %local.lineannotation.attrib;
6613 %common.attrib;
6614 %lineannotation.role.attrib;
6615 %local.lineannotation.attrib;
66166616 >
66176617 <!--end of lineannotation.attlist-->]]>
66186618 <!--end of lineannotation.module-->]]>
66306630 <!ENTITY % literal.attlist "INCLUDE">
66316631 <![%literal.attlist;[
66326632 <!ATTLIST literal
6633 %moreinfo.attrib;
6634 %common.attrib;
6635 %literal.role.attrib;
6636 %local.literal.attrib;
6633 %moreinfo.attrib;
6634 %common.attrib;
6635 %literal.role.attrib;
6636 %local.literal.attrib;
66376637 >
66386638 <!--end of literal.attlist-->]]>
66396639 <!--end of literal.module-->]]>
66516651 <!ENTITY % constant.attlist "INCLUDE">
66526652 <![ %constant.attlist; [
66536653 <!ATTLIST constant
6654 class (limit) #IMPLIED
6655 %common.attrib;
6656 %constant.role.attrib;
6657 %local.constant.attrib;
6654 class (limit) #IMPLIED
6655 %common.attrib;
6656 %constant.role.attrib;
6657 %local.constant.attrib;
66586658 >
66596659 <!--end of constant.attlist-->]]>
66606660 <!--end of constant.module-->]]>
66726672 <!ENTITY % varname.attlist "INCLUDE">
66736673 <![ %varname.attlist; [
66746674 <!ATTLIST varname
6675 %common.attrib;
6676 %varname.role.attrib;
6677 %local.varname.attrib;
6675 %common.attrib;
6676 %varname.role.attrib;
6677 %local.varname.attrib;
66786678 >
66796679 <!--end of varname.attlist-->]]>
66806680 <!--end of varname.module-->]]>
66926692 <!ENTITY % markup.attlist "INCLUDE">
66936693 <![%markup.attlist;[
66946694 <!ATTLIST markup
6695 %common.attrib;
6696 %markup.role.attrib;
6697 %local.markup.attrib;
6695 %common.attrib;
6696 %markup.role.attrib;
6697 %local.markup.attrib;
66986698 >
66996699 <!--end of markup.attlist-->]]>
67006700 <!--end of markup.module-->]]>
67156715 <!ENTITY % medialabel.attlist "INCLUDE">
67166716 <![%medialabel.attlist;[
67176717 <!ATTLIST medialabel
6718 class (cartridge
6719 |cdrom
6720 |disk
6721 |tape) #IMPLIED
6722 %common.attrib;
6723 %medialabel.role.attrib;
6724 %local.medialabel.attrib;
6718 class (cartridge
6719 |cdrom
6720 |disk
6721 |tape) #IMPLIED
6722 %common.attrib;
6723 %medialabel.role.attrib;
6724 %local.medialabel.attrib;
67256725 >
67266726 <!--end of medialabel.attlist-->]]>
67276727 <!--end of medialabel.module-->]]>
67366736 <!ENTITY % menuchoice.element "INCLUDE">
67376737 <![%menuchoice.element;[
67386738 <!ELEMENT menuchoice %ho; (shortcut?, (guibutton|guiicon|guilabel
6739 |guimenu|guimenuitem|guisubmenu|interface)+)>
6739 |guimenu|guimenuitem|guisubmenu|interface)+)>
67406740 <!--end of menuchoice.element-->]]>
67416741
67426742 <!ENTITY % menuchoice.attlist "INCLUDE">
67436743 <![%menuchoice.attlist;[
67446744 <!ATTLIST menuchoice
6745 %moreinfo.attrib;
6746 %common.attrib;
6747 %menuchoice.role.attrib;
6748 %local.menuchoice.attrib;
6745 %moreinfo.attrib;
6746 %common.attrib;
6747 %menuchoice.role.attrib;
6748 %local.menuchoice.attrib;
67496749 >
67506750 <!--end of menuchoice.attlist-->]]>
67516751 <!--end of menuchoice.module-->]]>
67646764 <!ENTITY % shortcut.attlist "INCLUDE">
67656765 <![%shortcut.attlist;[
67666766 <!ATTLIST shortcut
6767 %keyaction.attrib;
6768 %moreinfo.attrib;
6769 %common.attrib;
6770 %shortcut.role.attrib;
6771 %local.shortcut.attrib;
6767 %keyaction.attrib;
6768 %moreinfo.attrib;
6769 %common.attrib;
6770 %shortcut.role.attrib;
6771 %local.shortcut.attrib;
67726772 >
67736773 <!--end of shortcut.attlist-->]]>
67746774 <!--end of shortcut.module-->]]>
67876787 <!ENTITY % mousebutton.attlist "INCLUDE">
67886788 <![%mousebutton.attlist;[
67896789 <!ATTLIST mousebutton
6790 %moreinfo.attrib;
6791 %common.attrib;
6792 %mousebutton.role.attrib;
6793 %local.mousebutton.attrib;
6790 %moreinfo.attrib;
6791 %common.attrib;
6792 %mousebutton.role.attrib;
6793 %local.mousebutton.attrib;
67946794 >
67956795 <!--end of mousebutton.attlist-->]]>
67966796 <!--end of mousebutton.module-->]]>
68086808 <!ENTITY % msgtext.attlist "INCLUDE">
68096809 <![%msgtext.attlist;[
68106810 <!ATTLIST msgtext
6811 %common.attrib;
6812 %msgtext.role.attrib;
6813 %local.msgtext.attrib;
6811 %common.attrib;
6812 %msgtext.role.attrib;
6813 %local.msgtext.attrib;
68146814 >
68156815 <!--end of msgtext.attlist-->]]>
68166816 <!--end of msgtext.module-->]]>
68286828 <!ENTITY % option.attlist "INCLUDE">
68296829 <![%option.attlist;[
68306830 <!ATTLIST option
6831 %common.attrib;
6832 %option.role.attrib;
6833 %local.option.attrib;
6831 %common.attrib;
6832 %option.role.attrib;
6833 %local.option.attrib;
68346834 >
68356835 <!--end of option.attlist-->]]>
68366836 <!--end of option.module-->]]>
68486848 <!ENTITY % optional.attlist "INCLUDE">
68496849 <![%optional.attlist;[
68506850 <!ATTLIST optional
6851 %common.attrib;
6852 %optional.role.attrib;
6853 %local.optional.attrib;
6851 %common.attrib;
6852 %optional.role.attrib;
6853 %local.optional.attrib;
68546854 >
68556855 <!--end of optional.attlist-->]]>
68566856 <!--end of optional.module-->]]>
68716871 <!ENTITY % parameter.attlist "INCLUDE">
68726872 <![%parameter.attlist;[
68736873 <!ATTLIST parameter
6874 class (command
6875 |function
6876 |option) #IMPLIED
6877 %moreinfo.attrib;
6878 %common.attrib;
6879 %parameter.role.attrib;
6880 %local.parameter.attrib;
6874 class (command
6875 |function
6876 |option) #IMPLIED
6877 %moreinfo.attrib;
6878 %common.attrib;
6879 %parameter.role.attrib;
6880 %local.parameter.attrib;
68816881 >
68826882 <!--end of parameter.attlist-->]]>
68836883 <!--end of parameter.module-->]]>
68956895 <!ENTITY % prompt.attlist "INCLUDE">
68966896 <![%prompt.attlist;[
68976897 <!ATTLIST prompt
6898 %moreinfo.attrib;
6899 %common.attrib;
6900 %prompt.role.attrib;
6901 %local.prompt.attrib;
6898 %moreinfo.attrib;
6899 %common.attrib;
6900 %prompt.role.attrib;
6901 %local.prompt.attrib;
69026902 >
69036903 <!--end of prompt.attlist-->]]>
69046904 <!--end of prompt.module-->]]>
69166916 <!ENTITY % property.attlist "INCLUDE">
69176917 <![%property.attlist;[
69186918 <!ATTLIST property
6919 %moreinfo.attrib;
6920 %common.attrib;
6921 %property.role.attrib;
6922 %local.property.attrib;
6919 %moreinfo.attrib;
6920 %common.attrib;
6921 %property.role.attrib;
6922 %local.property.attrib;
69236923 >
69246924 <!--end of property.attlist-->]]>
69256925 <!--end of property.module-->]]>
69326932 <!ENTITY % replaceable.element "INCLUDE">
69336933 <![%replaceable.element;[
69346934 <!ELEMENT replaceable %ho; (#PCDATA
6935 | %link.char.class;
6936 | optional
6937 | %base.char.class;
6938 | %other.char.class;
6939 | inlinegraphic
6935 | %link.char.class;
6936 | optional
6937 | %base.char.class;
6938 | %other.char.class;
6939 | inlinegraphic
69406940 | inlinemediaobject
6941 | co)*>
6941 | co)*>
69426942 <!--end of replaceable.element-->]]>
69436943
69446944 <!-- Class: Type of information the element represents; no
6945 default -->
6945 default -->
69466946
69476947
69486948 <!ENTITY % replaceable.attlist "INCLUDE">
69496949 <![%replaceable.attlist;[
69506950 <!ATTLIST replaceable
6951 class (command
6952 |function
6953 |option
6954 |parameter) #IMPLIED
6955 %common.attrib;
6956 %replaceable.role.attrib;
6957 %local.replaceable.attrib;
6951 class (command
6952 |function
6953 |option
6954 |parameter) #IMPLIED
6955 %common.attrib;
6956 %replaceable.role.attrib;
6957 %local.replaceable.attrib;
69586958 >
69596959 <!--end of replaceable.attlist-->]]>
69606960 <!--end of replaceable.module-->]]>
69726972 <!ENTITY % returnvalue.attlist "INCLUDE">
69736973 <![%returnvalue.attlist;[
69746974 <!ATTLIST returnvalue
6975 %common.attrib;
6976 %returnvalue.role.attrib;
6977 %local.returnvalue.attrib;
6975 %common.attrib;
6976 %returnvalue.role.attrib;
6977 %local.returnvalue.attrib;
69786978 >
69796979 <!--end of returnvalue.attlist-->]]>
69806980 <!--end of returnvalue.module-->]]>
69956995 <!ENTITY % sgmltag.attlist "INCLUDE">
69966996 <![%sgmltag.attlist;[
69976997 <!ATTLIST sgmltag
6998 class (attribute
6999 |attvalue
7000 |element
7001 |endtag
6998 class (attribute
6999 |attvalue
7000 |element
7001 |endtag
70027002 |emptytag
7003 |genentity
7004 |numcharref
7005 |paramentity
7006 |pi
7003 |genentity
7004 |numcharref
7005 |paramentity
7006 |pi
70077007 |xmlpi
7008 |starttag
7009 |sgmlcomment) #IMPLIED
7010 %common.attrib;
7011 %sgmltag.role.attrib;
7012 %local.sgmltag.attrib;
7008 |starttag
7009 |sgmlcomment) #IMPLIED
7010 %common.attrib;
7011 %sgmltag.role.attrib;
7012 %local.sgmltag.attrib;
70137013 >
70147014 <!--end of sgmltag.attlist-->]]>
70157015 <!--end of sgmltag.module-->]]>
70277027 <!ENTITY % structfield.attlist "INCLUDE">
70287028 <![%structfield.attlist;[
70297029 <!ATTLIST structfield
7030 %common.attrib;
7031 %structfield.role.attrib;
7032 %local.structfield.attrib;
7030 %common.attrib;
7031 %structfield.role.attrib;
7032 %local.structfield.attrib;
70337033 >
70347034 <!--end of structfield.attlist-->]]>
70357035 <!--end of structfield.module-->]]>
70477047 <!ENTITY % structname.attlist "INCLUDE">
70487048 <![%structname.attlist;[
70497049 <!ATTLIST structname
7050 %common.attrib;
7051 %structname.role.attrib;
7052 %local.structname.attrib;
7050 %common.attrib;
7051 %structname.role.attrib;
7052 %local.structname.attrib;
70537053 >
70547054 <!--end of structname.attlist-->]]>
70557055 <!--end of structname.module-->]]>
70707070 <!ENTITY % symbol.attlist "INCLUDE">
70717071 <![%symbol.attlist;[
70727072 <!ATTLIST symbol
7073 class (limit) #IMPLIED
7074 %common.attrib;
7075 %symbol.role.attrib;
7076 %local.symbol.attrib;
7073 class (limit) #IMPLIED
7074 %common.attrib;
7075 %symbol.role.attrib;
7076 %local.symbol.attrib;
70777077 >
70787078 <!--end of symbol.attlist-->]]>
70797079 <!--end of symbol.module-->]]>
70937093 <!ENTITY % systemitem.attlist "INCLUDE">
70947094 <![%systemitem.attlist;[
70957095 <!ATTLIST systemitem
7096 class (constant
7097 |event
7098 |eventhandler
7099 |domainname
7100 |fqdomainname
7101 |ipaddress
7102 |netmask
7103 |etheraddress
7104 |groupname
7105 |library
7106 |macro
7107 |osname
7108 |filesystem
7109 |resource
7110 |systemname
7111 |username
7112 |newsgroup) #IMPLIED
7113 %moreinfo.attrib;
7114 %common.attrib;
7115 %systemitem.role.attrib;
7116 %local.systemitem.attrib;
7096 class (constant
7097 |event
7098 |eventhandler
7099 |domainname
7100 |fqdomainname
7101 |ipaddress
7102 |netmask
7103 |etheraddress
7104 |groupname
7105 |library
7106 |macro
7107 |osname
7108 |filesystem
7109 |resource
7110 |systemname
7111 |username
7112 |newsgroup) #IMPLIED
7113 %moreinfo.attrib;
7114 %common.attrib;
7115 %systemitem.role.attrib;
7116 %local.systemitem.attrib;
71177117 >
71187118 <!--end of systemitem.attlist-->]]>
71197119 <!--end of systemitem.module-->]]>
71327132 <!ENTITY % token.attlist "INCLUDE">
71337133 <![%token.attlist;[
71347134 <!ATTLIST token
7135 %common.attrib;
7136 %token.role.attrib;
7137 %local.token.attrib;
7135 %common.attrib;
7136 %token.role.attrib;
7137 %local.token.attrib;
71387138 >
71397139 <!--end of token.attlist-->]]>
71407140 <!--end of token.module-->]]>
71527152 <!ENTITY % type.attlist "INCLUDE">
71537153 <![%type.attlist;[
71547154 <!ATTLIST type
7155 %common.attrib;
7156 %type.role.attrib;
7157 %local.type.attrib;
7155 %common.attrib;
7156 %type.role.attrib;
7157 %local.type.attrib;
71587158 >
71597159 <!--end of type.attlist-->]]>
71607160 <!--end of type.module-->]]>
71727172 <!ENTITY % userinput.attlist "INCLUDE">
71737173 <![%userinput.attlist;[
71747174 <!ATTLIST userinput
7175 %moreinfo.attrib;
7176 %common.attrib;
7177 %userinput.role.attrib;
7178 %local.userinput.attrib;
7175 %moreinfo.attrib;
7176 %common.attrib;
7177 %userinput.role.attrib;
7178 %local.userinput.attrib;
71797179 >
71807180 <!--end of userinput.attlist-->]]>
71817181 <!--end of userinput.module-->]]>
71957195 <!ENTITY % abbrev.attlist "INCLUDE">
71967196 <![%abbrev.attlist;[
71977197 <!ATTLIST abbrev
7198 %common.attrib;
7199 %abbrev.role.attrib;
7200 %local.abbrev.attrib;
7198 %common.attrib;
7199 %abbrev.role.attrib;
7200 %local.abbrev.attrib;
72017201 >
72027202 <!--end of abbrev.attlist-->]]>
72037203 <!--end of abbrev.module-->]]>
72107210 <!ENTITY % acronym.element "INCLUDE">
72117211 <![%acronym.element;[
72127212 <!ELEMENT acronym %ho; (%word.char.mix;)*
7213 %acronym.exclusion;>
7213 %acronym.exclusion;>
72147214 <!--end of acronym.element-->]]>
72157215
72167216 <!ENTITY % acronym.attlist "INCLUDE">
72177217 <![%acronym.attlist;[
72187218 <!ATTLIST acronym
7219 %common.attrib;
7220 %acronym.role.attrib;
7221 %local.acronym.attrib;
7219 %common.attrib;
7220 %acronym.role.attrib;
7221 %local.acronym.attrib;
72227222 >
72237223 <!--end of acronym.attlist-->]]>
72247224 <!--end of acronym.module-->]]>
72367236 <!ENTITY % citation.attlist "INCLUDE">
72377237 <![%citation.attlist;[
72387238 <!ATTLIST citation
7239 %common.attrib;
7240 %citation.role.attrib;
7241 %local.citation.attrib;
7239 %common.attrib;
7240 %citation.role.attrib;
7241 %local.citation.attrib;
72427242 >
72437243 <!--end of citation.attlist-->]]>
72447244 <!--end of citation.module-->]]>
72567256 <!ENTITY % citerefentry.attlist "INCLUDE">
72577257 <![%citerefentry.attlist;[
72587258 <!ATTLIST citerefentry
7259 %common.attrib;
7260 %citerefentry.role.attrib;
7261 %local.citerefentry.attrib;
7259 %common.attrib;
7260 %citerefentry.role.attrib;
7261 %local.citerefentry.attrib;
72627262 >
72637263 <!--end of citerefentry.attlist-->]]>
72647264 <!--end of citerefentry.module-->]]>
72767276 <!ENTITY % refentrytitle.attlist "INCLUDE">
72777277 <![%refentrytitle.attlist;[
72787278 <!ATTLIST refentrytitle
7279 %common.attrib;
7280 %refentrytitle.role.attrib;
7281 %local.refentrytitle.attrib;
7279 %common.attrib;
7280 %refentrytitle.role.attrib;
7281 %local.refentrytitle.attrib;
72827282 >
72837283 <!--end of refentrytitle.attlist-->]]>
72847284 <!--end of refentrytitle.module-->]]>
72967296 <!ENTITY % manvolnum.attlist "INCLUDE">
72977297 <![%manvolnum.attlist;[
72987298 <!ATTLIST manvolnum
7299 %common.attrib;
7300 %namvolnum.role.attrib;
7301 %local.manvolnum.attrib;
7299 %common.attrib;
7300 %namvolnum.role.attrib;
7301 %local.manvolnum.attrib;
73027302 >
73037303 <!--end of manvolnum.attlist-->]]>
73047304 <!--end of manvolnum.module-->]]>
73197319 <!ENTITY % citetitle.attlist "INCLUDE">
73207320 <![%citetitle.attlist;[
73217321 <!ATTLIST citetitle
7322 pubwork (article
7323 |book
7324 |chapter
7325 |part
7326 |refentry
7327 |section
7328 |journal
7329 |series
7330 |set
7331 |manuscript) #IMPLIED
7332 %common.attrib;
7333 %citetitle.role.attrib;
7334 %local.citetitle.attrib;
7322 pubwork (article
7323 |book
7324 |chapter
7325 |part
7326 |refentry
7327 |section
7328 |journal
7329 |series
7330 |set
7331 |manuscript) #IMPLIED
7332 %common.attrib;
7333 %citetitle.role.attrib;
7334 %local.citetitle.attrib;
73357335 >
73367336 <!--end of citetitle.attlist-->]]>
73377337 <!--end of citetitle.module-->]]>
73497349 <!ENTITY % emphasis.attlist "INCLUDE">
73507350 <![%emphasis.attlist;[
73517351 <!ATTLIST emphasis
7352 %common.attrib;
7353 %emphasis.role.attrib;
7354 %local.emphasis.attrib;
7352 %common.attrib;
7353 %emphasis.role.attrib;
7354 %local.emphasis.attrib;
73557355 >
73567356 <!--end of emphasis.attlist-->]]>
73577357 <!--end of emphasis.module-->]]>
73727372 <!ENTITY % firstterm.attlist "INCLUDE">
73737373 <![%firstterm.attlist;[
73747374 <!ATTLIST firstterm
7375 %linkend.attrib; %common.attrib;
7376 %firstterm.role.attrib;
7377 %local.firstterm.attrib;
7375 %linkend.attrib; %common.attrib;
7376 %firstterm.role.attrib;
7377 %local.firstterm.attrib;
73787378 >
73797379 <!--end of firstterm.attlist-->]]>
73807380 <!--end of firstterm.module-->]]>
73927392 <!ENTITY % foreignphrase.attlist "INCLUDE">
73937393 <![%foreignphrase.attlist;[
73947394 <!ATTLIST foreignphrase
7395 %common.attrib;
7396 %foreignphrase.role.attrib;
7397 %local.foreignphrase.attrib;
7395 %common.attrib;
7396 %foreignphrase.role.attrib;
7397 %local.foreignphrase.attrib;
73987398 >
73997399 <!--end of foreignphrase.attlist-->]]>
74007400 <!--end of foreignphrase.module-->]]>
74077407 <!ENTITY % glossterm.element "INCLUDE">
74087408 <![%glossterm.element;[
74097409 <!ELEMENT glossterm %ho; (%para.char.mix;)*
7410 %glossterm.exclusion;>
7410 %glossterm.exclusion;>
74117411 <!--end of glossterm.element-->]]>
74127412
74137413 <!-- to GlossEntry if Glossterm used in text -->
74147414 <!-- BaseForm: Provides the form of GlossTerm to be used
7415 for indexing -->
7415 for indexing -->
74167416
74177417
74187418 <!ENTITY % glossterm.attlist "INCLUDE">
74197419 <![%glossterm.attlist;[
74207420 <!ATTLIST glossterm
7421 %linkend.attrib; baseform CDATA #IMPLIED
7422 %common.attrib;
7423 %glossterm.role.attrib;
7424 %local.glossterm.attrib;
7421 %linkend.attrib; baseform CDATA #IMPLIED
7422 %common.attrib;
7423 %glossterm.role.attrib;
7424 %local.glossterm.attrib;
74257425 >
74267426 <!--end of glossterm.attlist-->]]>
74277427 <!--end of glossterm.module-->]]>
74397439 <!ENTITY % phrase.attlist "INCLUDE">
74407440 <![%phrase.attlist;[
74417441 <!ATTLIST phrase
7442 %common.attrib;
7443 %phrase.role.attrib;
7444 %local.phrase.attrib;
7442 %common.attrib;
7443 %phrase.role.attrib;
7444 %local.phrase.attrib;
74457445 >
74467446 <!--end of phrase.attlist-->]]>
74477447 <!--end of phrase.module-->]]>
74597459 <!ENTITY % quote.attlist "INCLUDE">
74607460 <![%quote.attlist;[
74617461 <!ATTLIST quote
7462 %common.attrib;
7463 %quote.role.attrib;
7464 %local.quote.attrib;
7462 %common.attrib;
7463 %quote.role.attrib;
7464 %local.quote.attrib;
74657465 >
74667466 <!--end of quote.attlist-->]]>
74677467 <!--end of quote.module-->]]>
74747474 <!ENTITY % subscript.element "INCLUDE">
74757475 <![%subscript.element;[
74767476 <!ELEMENT subscript %ho; (#PCDATA
7477 | %link.char.class;
7478 | emphasis
7479 | replaceable
7480 | symbol
7481 | inlinegraphic
7477 | %link.char.class;
7478 | emphasis
7479 | replaceable
7480 | symbol
7481 | inlinegraphic
74827482 | inlinemediaobject
7483 | %base.char.class;
7484 | %other.char.class;)*
7485 %ubiq.exclusion;>
7483 | %base.char.class;
7484 | %other.char.class;)*
7485 %ubiq.exclusion;>
74867486 <!--end of subscript.element-->]]>
74877487
74887488 <!ENTITY % subscript.attlist "INCLUDE">
74897489 <![%subscript.attlist;[
74907490 <!ATTLIST subscript
7491 %common.attrib;
7492 %ssscript.role.attrib;
7493 %local.ssscript.attrib;
7491 %common.attrib;
7492 %ssscript.role.attrib;
7493 %local.ssscript.attrib;
74947494 >
74957495 <!--end of subscript.attlist-->]]>
74967496
74977497 <!ENTITY % superscript.element "INCLUDE">
74987498 <![%superscript.element;[
74997499 <!ELEMENT superscript %ho; (#PCDATA
7500 | %link.char.class;
7501 | emphasis
7502 | replaceable
7503 | symbol
7504 | inlinegraphic
7500 | %link.char.class;
7501 | emphasis
7502 | replaceable
7503 | symbol
7504 | inlinegraphic
75057505 | inlinemediaobject
7506 | %base.char.class;
7507 | %other.char.class;)*
7508 %ubiq.exclusion;>
7506 | %base.char.class;
7507 | %other.char.class;)*
7508 %ubiq.exclusion;>
75097509 <!--end of superscript.element-->]]>
75107510
75117511 <!ENTITY % superscript.attlist "INCLUDE">
75127512 <![%superscript.attlist;[
75137513 <!ATTLIST superscript
7514 %common.attrib;
7515 %ssscript.role.attrib;
7516 %local.ssscript.attrib;
7514 %common.attrib;
7515 %ssscript.role.attrib;
7516 %local.ssscript.attrib;
75177517 >
75187518 <!--end of superscript.attlist-->]]>
75197519 <!--end of ssscript.module-->]]>
75267526 <!ENTITY % trademark.element "INCLUDE">
75277527 <![%trademark.element;[
75287528 <!ELEMENT trademark %ho; (#PCDATA
7529 | %link.char.class;
7530 | %tech.char.class;
7531 | %base.char.class;
7532 | %other.char.class;
7533 | inlinegraphic
7529 | %link.char.class;
7530 | %tech.char.class;
7531 | %base.char.class;
7532 | %other.char.class;
7533 | inlinegraphic
75347534 | inlinemediaobject
7535 | emphasis)*>
7535 | emphasis)*>
75367536 <!--end of trademark.element-->]]>
75377537
75387538 <!-- Class: More precisely identifies the item the element names -->
75417541 <!ENTITY % trademark.attlist "INCLUDE">
75427542 <![%trademark.attlist;[
75437543 <!ATTLIST trademark
7544 class (service
7545 |trade
7546 |registered
7547 |copyright) 'trade'
7548 %common.attrib;
7549 %trademark.role.attrib;
7550 %local.trademark.attrib;
7544 class (service
7545 |trade
7546 |registered
7547 |copyright) 'trade'
7548 %common.attrib;
7549 %trademark.role.attrib;
7550 %local.trademark.attrib;
75517551 >
75527552 <!--end of trademark.attlist-->]]>
75537553 <!--end of trademark.module-->]]>
75657565 <!ENTITY % wordasword.attlist "INCLUDE">
75667566 <![%wordasword.attlist;[
75677567 <!ATTLIST wordasword
7568 %common.attrib;
7569 %wordasword.role.attrib;
7570 %local.wordasword.attrib;
7568 %common.attrib;
7569 %wordasword.role.attrib;
7570 %local.wordasword.attrib;
75717571 >
75727572 <!--end of wordasword.attlist-->]]>
75737573 <!--end of wordasword.module-->]]>
75827582 <!ENTITY % link.element "INCLUDE">
75837583 <![%link.element;[
75847584 <!ELEMENT link %ho; (%para.char.mix;)*
7585 %links.exclusion;>
7585 %links.exclusion;>
75867586 <!--end of link.element-->]]>
75877587
75887588 <!-- Endterm: ID of element containing text that is to be
7589 fetched from elsewhere in the document to appear as
7590 the content of this element -->
7589 fetched from elsewhere in the document to appear as
7590 the content of this element -->
75917591 <!-- to linked-to object -->
75927592 <!-- Type: Freely assignable parameter -->
75937593
75957595 <!ENTITY % link.attlist "INCLUDE">
75967596 <![%link.attlist;[
75977597 <!ATTLIST link
7598 endterm IDREF #IMPLIED
7599 %linkendreq.attrib; type CDATA #IMPLIED
7600 %common.attrib;
7601 %link.role.attrib;
7602 %local.link.attrib;
7598 endterm IDREF #IMPLIED
7599 %linkendreq.attrib; type CDATA #IMPLIED
7600 %common.attrib;
7601 %link.role.attrib;
7602 %local.link.attrib;
76037603 >
76047604 <!--end of link.attlist-->]]>
76057605 <!--end of link.module-->]]>
76127612 <!ENTITY % olink.element "INCLUDE">
76137613 <![%olink.element;[
76147614 <!ELEMENT olink %ho; (%para.char.mix;)*
7615 %links.exclusion;>
7615 %links.exclusion;>
76167616 <!--end of olink.element-->]]>
76177617
76187618 <!-- TargetDocEnt: Name of an entity to be the target of the link -->
76197619 <!-- LinkMode: ID of a ModeSpec containing instructions for
7620 operating on the entity named by TargetDocEnt -->
7620 operating on the entity named by TargetDocEnt -->
76217621 <!-- LocalInfo: Information that may be passed to ModeSpec -->
76227622 <!-- Type: Freely assignable parameter -->
76237623
76257625 <!ENTITY % olink.attlist "INCLUDE">
76267626 <![%olink.attlist;[
76277627 <!ATTLIST olink
7628 targetdocent ENTITY #IMPLIED
7629 linkmode IDREF #IMPLIED
7630 localinfo CDATA #IMPLIED
7631 type CDATA #IMPLIED
7632 targetdoc CDATA #IMPLIED
7633 targetptr CDATA #IMPLIED
7634 %common.attrib;
7635 %olink.role.attrib;
7636 %local.olink.attrib;
7628 targetdocent ENTITY #IMPLIED
7629 linkmode IDREF #IMPLIED
7630 localinfo CDATA #IMPLIED
7631 type CDATA #IMPLIED
7632 targetdoc CDATA #IMPLIED
7633 targetptr CDATA #IMPLIED
7634 %common.attrib;
7635 %olink.role.attrib;
7636 %local.olink.attrib;
76377637 >
76387638 <!--end of olink.attlist-->]]>
76397639 <!--end of olink.module-->]]>
76467646 <!ENTITY % ulink.element "INCLUDE">
76477647 <![%ulink.element;[
76487648 <!ELEMENT ulink %ho; (%para.char.mix;)*
7649 %links.exclusion;>
7649 %links.exclusion;>
76507650 <!--end of ulink.element-->]]>
76517651
76527652 <!-- URL: uniform resource locator; the target of the ULink -->
76567656 <!ENTITY % ulink.attlist "INCLUDE">
76577657 <![%ulink.attlist;[
76587658 <!ATTLIST ulink
7659 url CDATA #REQUIRED
7660 type CDATA #IMPLIED
7661 %common.attrib;
7662 %ulink.role.attrib;
7663 %local.ulink.attrib;
7659 url CDATA #REQUIRED
7660 type CDATA #IMPLIED
7661 %common.attrib;
7662 %ulink.role.attrib;
7663 %local.ulink.attrib;
76647664 >
76657665 <!--end of ulink.attlist-->]]>
76667666 <!--end of ulink.module-->]]>
76817681 <!ENTITY % footnoteref.attlist "INCLUDE">
76827682 <![%footnoteref.attlist;[
76837683 <!ATTLIST footnoteref
7684 %linkendreq.attrib; %label.attrib;
7685 %common.attrib;
7686 %footnoteref.role.attrib;
7687 %local.footnoteref.attrib;
7684 %linkendreq.attrib; %label.attrib;
7685 %common.attrib;
7686 %footnoteref.role.attrib;
7687 %local.footnoteref.attrib;
76887688 >
76897689 <!--end of footnoteref.attlist-->]]>
76907690 <!--end of footnoteref.module-->]]>
77007700 <!--end of xref.element-->]]>
77017701
77027702 <!-- Endterm: ID of element containing text that is to be
7703 fetched from elsewhere in the document to appear as
7704 the content of this element -->
7703 fetched from elsewhere in the document to appear as
7704 the content of this element -->
77057705 <!-- to linked-to object -->
77067706
77077707
77087708 <!ENTITY % xref.attlist "INCLUDE">
77097709 <![%xref.attlist;[
77107710 <!ATTLIST xref
7711 endterm IDREF #IMPLIED
7712 %linkendreq.attrib; %common.attrib;
7713 %xref.role.attrib;
7714 %local.xref.attrib;
7711 endterm IDREF #IMPLIED
7712 %linkendreq.attrib; %common.attrib;
7713 %xref.role.attrib;
7714 %local.xref.attrib;
77157715 >
77167716 <!--end of xref.attlist-->]]>
77177717 <!--end of xref.module-->]]>
77357735 <!ENTITY % anchor.attlist "INCLUDE">
77367736 <![%anchor.attlist;[
77377737 <!ATTLIST anchor
7738 %idreq.attrib; %pagenum.attrib; %remap.attrib;
7739 %xreflabel.attrib;
7740 %revisionflag.attrib;
7741 %effectivity.attrib;
7742 %anchor.role.attrib;
7743 %local.anchor.attrib;
7738 %idreq.attrib; %pagenum.attrib; %remap.attrib;
7739 %xreflabel.attrib;
7740 %revisionflag.attrib;
7741 %effectivity.attrib;
7742 %anchor.role.attrib;
7743 %local.anchor.attrib;
77447744 >
77457745 <!--end of anchor.attlist-->]]>
77467746 <!--end of anchor.module-->]]>
77617761 <!ENTITY % beginpage.attlist "INCLUDE">
77627762 <![%beginpage.attlist;[
77637763 <!ATTLIST beginpage
7764 %pagenum.attrib;
7765 %common.attrib;
7766 %beginpage.role.attrib;
7767 %local.beginpage.attrib;
7764 %pagenum.attrib;
7765 %common.attrib;
7766 %beginpage.role.attrib;
7767 %local.beginpage.attrib;
77687768 >
77697769 <!--end of beginpage.attlist-->]]>
77707770 <!--end of beginpage.module-->]]>
77827782 <!ENTITY % indexterm.element "INCLUDE">
77837783 <![%indexterm.element;[
77847784 <!ELEMENT indexterm %ho; (primary?, ((secondary, ((tertiary, (see|seealso+)?)
7785 | see | seealso+)?) | see | seealso+)?)
7786 %ubiq.exclusion;>
7785 | see | seealso+)?) | see | seealso+)?)
7786 %ubiq.exclusion;>
77877787 <!--end of indexterm.element-->]]>
77887788
77897789 <!-- Scope: Indicates which generated indices the IndexTerm
7790 should appear in: Global (whole document set), Local (this
7791 document only), or All (both) -->
7790 should appear in: Global (whole document set), Local (this
7791 document only), or All (both) -->
77927792 <!-- Significance: Whether this IndexTerm is the most pertinent
7793 of its series (Preferred) or not (Normal, the default) -->
7793 of its series (Preferred) or not (Normal, the default) -->
77947794 <!-- Class: Indicates type of IndexTerm; default is Singular,
7795 or EndOfRange if StartRef is supplied; StartOfRange value
7796 must be supplied explicitly on starts of ranges -->
7795 or EndOfRange if StartRef is supplied; StartOfRange value
7796 must be supplied explicitly on starts of ranges -->
77977797 <!-- StartRef: ID of the IndexTerm that starts the indexing
7798 range ended by this IndexTerm -->
7798 range ended by this IndexTerm -->
77997799 <!-- Zone: IDs of the elements to which the IndexTerm applies,
7800 and indicates that the IndexTerm applies to those entire
7801 elements rather than the point at which the IndexTerm
7802 occurs -->
7800 and indicates that the IndexTerm applies to those entire
7801 elements rather than the point at which the IndexTerm
7802 occurs -->
78037803
78047804
78057805 <!ENTITY % indexterm.attlist "INCLUDE">
78067806 <![%indexterm.attlist;[
78077807 <!ATTLIST indexterm
7808 %pagenum.attrib;
7809 scope (all
7810 |global
7811 |local) #IMPLIED
7812 significance (preferred
7813 |normal) "normal"
7814 class (singular
7815 |startofrange
7816 |endofrange) #IMPLIED
7817 startref IDREF #IMPLIED
7818 zone IDREFS #IMPLIED
7819 %common.attrib;
7820 %indexterm.role.attrib;
7821 %local.indexterm.attrib;
7808 %pagenum.attrib;
7809 scope (all
7810 |global
7811 |local) #IMPLIED
7812 significance (preferred
7813 |normal) "normal"
7814 class (singular
7815 |startofrange
7816 |endofrange) #IMPLIED
7817 startref IDREF #IMPLIED
7818 zone IDREFS #IMPLIED
7819 %common.attrib;
7820 %indexterm.role.attrib;
7821 %local.indexterm.attrib;
78227822 >
78237823 <!--end of indexterm.attlist-->]]>
78247824 <!--end of indexterm.module-->]]>
78347834 <!ELEMENT primary %ho; (%ndxterm.char.mix;)*>
78357835 <!--end of primary.element-->]]>
78367836 <!-- SortAs: Alternate sort string for index sorting, e.g.,
7837 "fourteen" for an element containing "14" -->
7837 "fourteen" for an element containing "14" -->
78387838
78397839 <!ENTITY % primary.attlist "INCLUDE">
78407840 <![%primary.attlist;[
78417841 <!ATTLIST primary
7842 sortas CDATA #IMPLIED
7843 %common.attrib;
7844 %primsecter.role.attrib;
7845 %local.primsecter.attrib;
7842 sortas CDATA #IMPLIED
7843 %common.attrib;
7844 %primsecter.role.attrib;
7845 %local.primsecter.attrib;
78467846 >
78477847 <!--end of primary.attlist-->]]>
78487848
78527852 <!ELEMENT secondary %ho; (%ndxterm.char.mix;)*>
78537853 <!--end of secondary.element-->]]>
78547854 <!-- SortAs: Alternate sort string for index sorting, e.g.,
7855 "fourteen" for an element containing "14" -->
7855 "fourteen" for an element containing "14" -->
78567856
78577857 <!ENTITY % secondary.attlist "INCLUDE">
78587858 <![%secondary.attlist;[
78597859 <!ATTLIST secondary
7860 sortas CDATA #IMPLIED
7861 %common.attrib;
7862 %primsecter.role.attrib;
7863 %local.primsecter.attrib;
7860 sortas CDATA #IMPLIED
7861 %common.attrib;
7862 %primsecter.role.attrib;
7863 %local.primsecter.attrib;
78647864 >
78657865 <!--end of secondary.attlist-->]]>
78667866
78707870 <!ELEMENT tertiary %ho; (%ndxterm.char.mix;)*>
78717871 <!--end of tertiary.element-->]]>
78727872 <!-- SortAs: Alternate sort string for index sorting, e.g.,
7873 "fourteen" for an element containing "14" -->
7873 "fourteen" for an element containing "14" -->
78747874
78757875 <!ENTITY % tertiary.attlist "INCLUDE">
78767876 <![%tertiary.attlist;[
78777877 <!ATTLIST tertiary
7878 sortas CDATA #IMPLIED
7879 %common.attrib;
7880 %primsecter.role.attrib;
7881 %local.primsecter.attrib;
7878 sortas CDATA #IMPLIED
7879 %common.attrib;
7880 %primsecter.role.attrib;
7881 %local.primsecter.attrib;
78827882 >
78837883 <!--end of tertiary.attlist-->]]>
78847884
78977897 <!ENTITY % see.attlist "INCLUDE">
78987898 <![%see.attlist;[
78997899 <!ATTLIST see
7900 %common.attrib;
7901 %seeseealso.role.attrib;
7902 %local.seeseealso.attrib;
7900 %common.attrib;
7901 %seeseealso.role.attrib;
7902 %local.seeseealso.attrib;
79037903 >
79047904 <!--end of see.attlist-->]]>
79057905
79117911 <!ENTITY % seealso.attlist "INCLUDE">
79127912 <![%seealso.attlist;[
79137913 <!ATTLIST seealso
7914 %common.attrib;
7915 %seeseealso.role.attrib;
7916 %local.seeseealso.attrib;
7914 %common.attrib;
7915 %seeseealso.role.attrib;
7916 %local.seeseealso.attrib;
79177917 >
79187918 <!--end of seealso.attlist-->]]>
79197919 <!--end of seeseealso.module-->]]>
170170 />
171171 </SplitPass>
172172 <SplitPass>
173 <Earlier class="edu.umd.cs.findbugs.detect.FindNoSideEffectMethods"/>
174 <Later
175 class="edu.umd.cs.findbugs.detect.MethodReturnCheck"
176 />
177 </SplitPass>
178 <SplitPass>
179 <Earlier class="edu.umd.cs.findbugs.detect.FindNoSideEffectMethods"/>
180 <Later
181 class="edu.umd.cs.findbugs.detect.FindUselessObjects"
182 />
183 </SplitPass>
184 <SplitPass>
185 <Earlier class="edu.umd.cs.findbugs.detect.FindNoSideEffectMethods"/>
186 <Later
187 class="edu.umd.cs.findbugs.detect.FindDoubleCheck"
188 />
189 </SplitPass>
190 <SplitPass>
191 <Earlier class="edu.umd.cs.findbugs.detect.FindNoSideEffectMethods"/>
192 <Later
193 class="edu.umd.cs.findbugs.detect.RepeatedConditionals"
194 />
195 </SplitPass>
196 <SplitPass>
197 <Earlier class="edu.umd.cs.findbugs.detect.BuildStringPassthruGraph"/>
198 <Later class="edu.umd.cs.findbugs.detect.DumbMethodInvocations"/>
199 </SplitPass>
200 <SplitPass>
201 <Earlier class="edu.umd.cs.findbugs.detect.BuildStringPassthruGraph"/>
202 <Later class="edu.umd.cs.findbugs.detect.CrossSiteScripting"/>
203 </SplitPass>
204 <SplitPass>
205 <Earlier class="edu.umd.cs.findbugs.detect.BuildStringPassthruGraph"/>
206 <Later class="edu.umd.cs.findbugs.detect.FindSqlInjection"/>
207 </SplitPass>
208 <SplitPass>
173209 <Earlier class="edu.umd.cs.findbugs.detect.CalledMethods"/>
174210 <Later class="edu.umd.cs.findbugs.detect.FindNullDeref"/>
175211 </SplitPass>
252288 <Later class="edu.umd.cs.findbugs.detect.CheckExpectedWarnings"/>
253289 </SplitPass>
254290 </OrderingConstraints>
291 <Detector class="edu.umd.cs.findbugs.detect.FindRoughConstants"
292 reports="CNT_ROUGH_CONSTANT_VALUE" speed="fast" hidden="false"/>
293
255294 <Detector class="edu.umd.cs.findbugs.detect.FunctionsThatMightBeMistakenForProcedures" speed="fast"
256295 reports="" hidden="true"/>
257296 <Detector class="edu.umd.cs.findbugs.detect.NoteSuppressedWarnings" speed="fast"
270309 reports="NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" hidden="false"/>
271310 <Detector class="edu.umd.cs.findbugs.detect.BooleanReturnNull"
272311 reports="NP_BOOLEAN_RETURN_NULL" hidden="false"/>
312 <Detector class="edu.umd.cs.findbugs.detect.OptionalReturnNull"
313 reports="NP_OPTIONAL_RETURN_NULL" hidden="false"/>
273314 <Detector class="edu.umd.cs.findbugs.detect.NoteJCIPAnnotation" speed="fast" reports=""
274315 hidden="true"/>
275316 <Detector class="edu.umd.cs.findbugs.detect.Methods" speed="fast" reports=""
278319 disabled="false" hidden="true"/>
279320 <Detector class="edu.umd.cs.findbugs.detect.CalledMethods" speed="fast" reports=""
280321 disabled="false" hidden="true"/>
322 <Detector class="edu.umd.cs.findbugs.detect.FindNoSideEffectMethods" speed="moderate" reports=""
323 disabled="false" hidden="true"/>
324 <Detector class="edu.umd.cs.findbugs.detect.BuildStringPassthruGraph" speed="fast" reports=""
325 disabled="false" hidden="true"/>
281326 <Detector class="edu.umd.cs.findbugs.detect.ConfusionBetweenInheritedAndOuterMethod"
282327 speed="moderate" reports="IA_AMBIGUOUS_INVOCATION_OF_INHERITED_OR_OUTER_METHOD"
283328 disabled="false" hidden="false"/>
284329 <Detector class="edu.umd.cs.findbugs.detect.SynchronizationOnSharedBuiltinConstant"
285330 speed="fast"
286331 reports="DL_SYNCHRONIZATION_ON_BOOLEAN,DL_SYNCHRONIZATION_ON_UNSHARED_BOXED_PRIMITIVE,DL_SYNCHRONIZATION_ON_BOXED_PRIMITIVE,DL_SYNCHRONIZATION_ON_SHARED_CONSTANT"
287 hidden="true"/>
332 hidden="false"/>
288333 <Detector class="edu.umd.cs.findbugs.detect.NoteCheckReturnValueAnnotations" speed="fast"
289334 reports="" requirejre="1.5" hidden="true"/>
290335 <Detector class="edu.umd.cs.findbugs.detect.FieldItemSummary" speed="fast" reports=""
314359 reports="HSC_HUGE_SHARED_STRING_CONSTANT"/>
315360 <Detector class="edu.umd.cs.findbugs.detect.FinalizerNullsFields" speed="fast"
316361 reports="FI_FINALIZER_NULLS_FIELDS,FI_FINALIZER_ONLY_NULLS_FIELDS"/>
362 <Detector class="edu.umd.cs.findbugs.detect.MutableEnum" speed="fast"
363 reports="ME_MUTABLE_ENUM_FIELD,ME_ENUM_FIELD_SETTER"/>
317364 <Detector class="edu.umd.cs.findbugs.detect.InconsistentAnnotations" speed="fast"
318365 reports="NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE"/>
319366 <Detector class="edu.umd.cs.findbugs.detect.RepeatedConditionals" speed="fast"
320367 reports="RpC_REPEATED_CONDITIONAL_TEST"/>
368 <Detector class="edu.umd.cs.findbugs.detect.RedundantConditions" speed="fast"
369 reports="UC_USELESS_CONDITION,UC_USELESS_CONDITION_TYPE"/>
321370 <Detector class="edu.umd.cs.findbugs.detect.CallToUnsupportedMethod" speed="fast"
322371 disabled="true" reports="DMI_UNSUPPORTED_METHOD"/>
323372 <Detector class="edu.umd.cs.findbugs.detect.FormatStringChecker" speed="fast"
363412 reports="DMI_EMPTY_DB_PASSWORD,DMI_CONSTANT_DB_PASSWORD,DMI_USELESS_SUBSTRING,DMI_HARDCODED_ABSOLUTE_FILENAME"/>
364413 <Detector class="edu.umd.cs.findbugs.detect.URLProblems" speed="fast"
365414 reports="DMI_BLOCKING_METHODS_ON_URL,DMI_COLLECTION_OF_URLS"/>
415 <Detector class="edu.umd.cs.findbugs.detect.CovariantArrayAssignment" speed="fast" disabled="true"
416 reports="CAA_COVARIANT_ARRAY_FIELD,CAA_COVARIANT_ARRAY_RETURN,CAA_COVARIANT_ARRAY_LOCAL,CAA_COVARIANT_ARRAY_ELEMENT_STORE"/>
366417 <Detector class="edu.umd.cs.findbugs.detect.DumbMethods" speed="fast"
367 reports="NP_IMMEDIATE_DEREFERENCE_OF_READLINE,RV_01_TO_INT,DM_RUN_FINALIZERS_ON_EXIT,DM_STRING_CTOR,DM_STRING_VOID_CTOR,DM_STRING_TOSTRING,DM_GC,DM_BOOLEAN_CTOR,DM_EXIT,DM_CONVERT_CASE,SW_SWING_METHODS_INVOKED_IN_SWING_THREAD,DM_BOXED_PRIMITIVE_TOSTRING,DM_BOXED_PRIMITIVE_FOR_PARSING,DM_NEW_FOR_GETCLASS,DM_NEXTINT_VIA_NEXTDOUBLE,DM_USELESS_THREAD,DM_MONITOR_WAIT_ON_CONDITION,DMI_CALLING_NEXT_FROM_HASNEXT,RV_REM_OF_HASHCODE,RV_REM_OF_RANDOM_INT,RV_ABSOLUTE_VALUE_OF_RANDOM_INT,RV_ABSOLUTE_VALUE_OF_HASHCODE,BIT_ADD_OF_SIGNED_BYTE,BIT_IOR_OF_SIGNED_BYTE,INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE,INT_BAD_COMPARISON_WITH_SIGNED_BYTE,INT_BAD_REM_BY_1,DMI_ANNOTATION_IS_NOT_VISIBLE_TO_REFLECTION,INT_VACUOUS_COMPARISON,BC_EQUALS_METHOD_SHOULD_WORK_FOR_ALL_OBJECTS,DMI_RANDOM_USED_ONLY_ONCE,DMI_LONG_BITS_TO_DOUBLE_INVOKED_ON_INT,DMI_THREAD_PASSED_WHERE_RUNNABLE_EXPECTED,DMI_FUTILE_ATTEMPT_TO_CHANGE_MAXPOOL_SIZE_OF_SCHEDULED_THREAD_POOL_EXECUTOR,DMI_SCHEDULED_THREAD_POOL_EXECUTOR_WITH_ZERO_CORE_THREADS,DMI_VACUOUS_CALL_TO_EASYMOCK_METHOD,DMI_BIGDECIMAL_CONSTRUCTED_FROM_DOUBLE,INT_VACUOUS_BIT_OPERATION,DMI_COLLECTION_OF_URLS,INT_BAD_COMPARISON_WITH_INT_VALUE,DMI_DOH,DMI_ARGUMENTS_WRONG_ORDER"/>
418 reports="NP_IMMEDIATE_DEREFERENCE_OF_READLINE,RV_01_TO_INT,DM_INVALID_MIN_MAX,DM_RUN_FINALIZERS_ON_EXIT,DM_STRING_CTOR,DM_STRING_VOID_CTOR,DM_STRING_TOSTRING,DM_GC,DM_BOOLEAN_CTOR,DM_EXIT,DM_CONVERT_CASE,SW_SWING_METHODS_INVOKED_IN_SWING_THREAD,DM_BOXED_PRIMITIVE_TOSTRING,DM_BOXED_PRIMITIVE_FOR_PARSING,DM_BOXED_PRIMITIVE_FOR_COMPARE,DM_NEW_FOR_GETCLASS,DM_NEXTINT_VIA_NEXTDOUBLE,DM_USELESS_THREAD,DM_MONITOR_WAIT_ON_CONDITION,DMI_CALLING_NEXT_FROM_HASNEXT,RV_REM_OF_HASHCODE,RV_REM_OF_RANDOM_INT,RV_ABSOLUTE_VALUE_OF_RANDOM_INT,RV_ABSOLUTE_VALUE_OF_HASHCODE,BIT_ADD_OF_SIGNED_BYTE,BIT_IOR_OF_SIGNED_BYTE,INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE,INT_BAD_COMPARISON_WITH_SIGNED_BYTE,INT_BAD_REM_BY_1,DMI_ANNOTATION_IS_NOT_VISIBLE_TO_REFLECTION,INT_VACUOUS_COMPARISON,BC_EQUALS_METHOD_SHOULD_WORK_FOR_ALL_OBJECTS,DMI_RANDOM_USED_ONLY_ONCE,DMI_LONG_BITS_TO_DOUBLE_INVOKED_ON_INT,DMI_THREAD_PASSED_WHERE_RUNNABLE_EXPECTED,DMI_FUTILE_ATTEMPT_TO_CHANGE_MAXPOOL_SIZE_OF_SCHEDULED_THREAD_POOL_EXECUTOR,DMI_SCHEDULED_THREAD_POOL_EXECUTOR_WITH_ZERO_CORE_THREADS,DMI_VACUOUS_CALL_TO_EASYMOCK_METHOD,DMI_BIGDECIMAL_CONSTRUCTED_FROM_DOUBLE,INT_VACUOUS_BIT_OPERATION,DMI_COLLECTION_OF_URLS,INT_BAD_COMPARISON_WITH_INT_VALUE,DMI_DOH,DMI_ARGUMENTS_WRONG_ORDER,RANGE_ARRAY_INDEX,RANGE_ARRAY_OFFSET,RANGE_ARRAY_LENGTH,RANGE_STRING_INDEX"/>
419 "/>
368420 <Detector class="edu.umd.cs.findbugs.detect.NumberConstructor" speed="fast"
369421 disabled="false" reports="DM_NUMBER_CTOR,DM_FP_NUMBER_CTOR"/>
370422 <Detector class="edu.umd.cs.findbugs.detect.FindSqlInjection" speed="moderate"
371423 reports="SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE,SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING"
372424 disabled="false"/>
373425 <Detector class="edu.umd.cs.findbugs.detect.FindDoubleCheck" speed="fast"
374 reports="DC_DOUBLECHECK"/>
426 reports="DC_DOUBLECHECK,DC_PARTIALLY_CONSTRUCTED"/>
375427 <Detector class="edu.umd.cs.findbugs.detect.FindFinalizeInvocations" speed="fast"
376428 reports="FI_PUBLIC_SHOULD_BE_PROTECTED,FI_EMPTY,FI_NULLIFY_SUPER,FI_USELESS,FI_MISSING_SUPER_CALL,FI_EXPLICIT_INVOCATION"/>
377429 <Detector class="edu.umd.cs.findbugs.detect.FindHEmismatch" speed="fast"
410462 speed="fast" reports="ML_SYNC_ON_FIELD_TO_GUARD_CHANGING_THAT_FIELD"/>
411463 <Detector class="edu.umd.cs.findbugs.detect.MutableLock" speed="fast"
412464 reports="ML_SYNC_ON_UPDATED_FIELD"/>
465 <Detector class="edu.umd.cs.findbugs.detect.FindUselessObjects" speed="fast"
466 reports="UC_USELESS_OBJECT,UC_USELESS_OBJECT_STACK"/>
413467 <Detector class="edu.umd.cs.findbugs.detect.MutableStaticFields" speed="fast"
414 reports="MS_OOI_PKGPROTECT,MS_FINAL_PKGPROTECT,MS_SHOULD_BE_REFACTORED_TO_BE_FINAL,MS_SHOULD_BE_FINAL,MS_PKGPROTECT,MS_MUTABLE_HASHTABLE,MS_MUTABLE_ARRAY,MS_CANNOT_BE_FINAL"/>
468 reports="MS_OOI_PKGPROTECT,MS_FINAL_PKGPROTECT,MS_SHOULD_BE_REFACTORED_TO_BE_FINAL,MS_SHOULD_BE_FINAL,MS_PKGPROTECT,MS_MUTABLE_HASHTABLE,MS_MUTABLE_ARRAY,MS_CANNOT_BE_FINAL,MS_MUTABLE_COLLECTION,MS_MUTABLE_COLLECTION_PKGPROTECT"/>
415469 <Detector class="edu.umd.cs.findbugs.detect.Naming" speed="fast"
416470 reports="NM_WRONG_PACKAGE,NM_WRONG_PACKAGE_INTENTIONAL,NM_VERY_CONFUSING,NM_VERY_CONFUSING_INTENTIONAL,NM_CONFUSING,NM_METHOD_CONSTRUCTOR_CONFUSION,NM_LCASE_HASHCODE,NM_LCASE_TOSTRING,NM_BAD_EQUAL,NM_CLASS_NAMING_CONVENTION,NM_FIELD_NAMING_CONVENTION,NM_METHOD_NAMING_CONVENTION,NM_CLASS_NOT_EXCEPTION,NM_SAME_SIMPLE_NAME_AS_SUPERCLASS,NM_SAME_SIMPLE_NAME_AS_INTERFACE"/>
417471 <Detector class="edu.umd.cs.findbugs.detect.ReadReturnShouldBeChecked" speed="fast"
428482 reports="ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD,NP_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD,NP_UNWRITTEN_FIELD,UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR,UWF_NULL_FIELD,UWF_UNWRITTEN_FIELD,SS_SHOULD_BE_STATIC,UUF_UNUSED_FIELD,URF_UNREAD_FIELD,SIC_INNER_SHOULD_BE_STATIC,SIC_INNER_SHOULD_BE_STATIC_ANON,SIC_INNER_SHOULD_BE_STATIC_NEEDS_THIS,SIC_THREADLOCAL_DEADLY_EMBRACE,UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD,UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD,URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD"/>
429483 <Detector class="edu.umd.cs.findbugs.detect.WaitInLoop" speed="fast"
430484 reports="WA_NOT_IN_LOOP,WA_AWAIT_NOT_IN_LOOP,NO_NOTIFY_NOT_NOTIFYALL"/>
485 <Detector class="edu.umd.cs.findbugs.detect.FindComparatorProblems" speed="fast"
486 reports="CO_COMPARETO_RESULTS_MIN_VALUE,CO_COMPARETO_INCORRECT_FLOATING"/>
431487 <Detector class="edu.umd.cs.findbugs.detect.FindNullDeref" speed="slow"
432488 reports="NP_DEREFERENCE_OF_READLINE_VALUE,NP_NULL_ON_SOME_PATH_MIGHT_BE_INFEASIBLE,NP_TOSTRING_COULD_RETURN_NULL,NP_CLONE_COULD_RETURN_NULL,NP_ALWAYS_NULL_EXCEPTION,NP_ALWAYS_NULL,NP_STORE_INTO_NONNULL_FIELD,NP_NULL_ON_SOME_PATH_EXCEPTION,NP_NULL_ON_SOME_PATH,NP_NULL_PARAM_DEREF_NONVIRTUAL,NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS,NP_NULL_PARAM_DEREF,RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE,RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE,RCN_REDUNDANT_NULLCHECK_OF_NONNULL_VALUE,RCN_REDUNDANT_COMPARISON_TWO_NULL_VALUES,RCN_REDUNDANT_COMPARISON_OF_NULL_AND_NONNULL_VALUE,NP_NONNULL_PARAM_VIOLATION,NP_NONNULL_RETURN_VIOLATION,NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE,NP_GUARANTEED_DEREF,NP_GUARANTEED_DEREF_ON_EXCEPTION_PATH,NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT,NP_ARGUMENT_MIGHT_BE_NULL,NP_CLOSING_NULL"/>
433489 <Detector
462518 reports="UMAC_UNCALLABLE_METHOD_OF_ANONYMOUS_CLASS"/>
463519 <Detector class="edu.umd.cs.findbugs.detect.StringConcatenation" speed="fast"
464520 reports="SBSC_USE_STRINGBUFFER_CONCATENATION"/>
465 <Detector class="edu.umd.cs.findbugs.detect.InefficientToArray" speed="fast"
521 <Detector class="edu.umd.cs.findbugs.detect.InefficientInitializationInsideLoop" speed="fast" disabled="true"
522 reports="IIL_PREPARE_STATEMENT_IN_LOOP,IIL_PATTERN_COMPILE_IN_LOOP,IIL_PATTERN_COMPILE_IN_LOOP_INDIRECT,IIL_ELEMENTS_GET_LENGTH_IN_LOOP"/>
523 <Detector class="edu.umd.cs.findbugs.detect.InefficientIndexOf" speed="fast" disabled="true"
524 reports="IIO_INEFFICIENT_INDEX_OF,IIO_INEFFICIENT_LAST_INDEX_OF"/>
525 <Detector class="edu.umd.cs.findbugs.detect.InefficientToArray" speed="fast" disabled="true"
466526 reports="ITA_INEFFICIENT_TO_ARRAY"/>
467527 <Detector class="edu.umd.cs.findbugs.detect.InvalidJUnitTest" speed="fast"
468528 reports="IJU_SETUP_NO_SUPER,IJU_TEARDOWN_NO_SUPER,IJU_SUITE_NOT_STATIC,IJU_NO_TESTS,IJU_BAD_SUITE_METHOD"/>
503563 <Detector class="edu.umd.cs.findbugs.detect.BadUseOfReturnValue" speed="fast"
504564 reports="RV_CHECK_FOR_POSITIVE_INDEXOF,RV_DONT_JUST_NULL_CHECK_READLINE"/>
505565 <Detector class="edu.umd.cs.findbugs.detect.MethodReturnCheck" speed="fast"
506 reports="RV_RETURN_VALUE_IGNORED,RV_RETURN_VALUE_IGNORED_BAD_PRACTICE,RV_EXCEPTION_NOT_THROWN,RV_CHECK_COMPARETO_FOR_SPECIFIC_RETURN_VALUE,RV_RETURN_VALUE_IGNORED_INFERRED"/>
566 reports="RV_RETURN_VALUE_IGNORED,RV_RETURN_VALUE_IGNORED_BAD_PRACTICE,RV_EXCEPTION_NOT_THROWN,RV_CHECK_COMPARETO_FOR_SPECIFIC_RETURN_VALUE,RV_RETURN_VALUE_IGNORED_INFERRED,RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT,UC_USELESS_VOID_METHOD"/>
507567 <Detector class="edu.umd.cs.findbugs.detect.IDivResultCastToDouble" speed="fast"
508568 reports="ICAST_IDIV_CAST_TO_DOUBLE,ICAST_INT_CAST_TO_DOUBLE_PASSED_TO_CEIL,ICAST_INT_CAST_TO_FLOAT_PASSED_TO_ROUND"/>
509569 <Detector class="edu.umd.cs.findbugs.detect.FindBadCast2" speed="fast"
524584 <Detector class="edu.umd.cs.findbugs.detect.VarArgsProblems" speed="fast"
525585 reports="VA_PRIMITIVE_ARRAY_PASSED_TO_OBJECT_VARARG"/>
526586 <Detector class="edu.umd.cs.findbugs.detect.FindPuzzlers" speed="fast"
527 reports="DLS_DEAD_LOCAL_STORE_IN_RETURN,EC_BAD_ARRAY_COMPARE,DLS_OVERWRITTEN_INCREMENT,ICAST_BAD_SHIFT_AMOUNT,ICAST_QUESTIONABLE_UNSIGNED_RIGHT_SHIFT,DMI_BAD_MONTH,IM_MULTIPLYING_RESULT_OF_IREM,IM_BAD_CHECK_FOR_ODD,DMI_INVOKING_TOSTRING_ON_ARRAY,DMI_INVOKING_TOSTRING_ON_ANONYMOUS_ARRAY,IM_AVERAGE_COMPUTATION_COULD_OVERFLOW,IC_SUPERCLASS_USES_SUBCLASS_DURING_INITIALIZATION,ICAST_INTEGER_MULTIPLY_CAST_TO_LONG,BX_UNBOXED_AND_COERCED_FOR_TERNARY_OPERATOR,BX_BOXING_IMMEDIATELY_UNBOXED,BX_BOXING_IMMEDIATELY_UNBOXED_TO_PERFORM_COERCION,IJU_ASSERT_METHOD_INVOKED_FROM_RUN_METHOD,DMI_INVOKING_HASHCODE_ON_ARRAY,CO_COMPARETO_RESULTS_MIN_VALUE,BX_UNBOXING_IMMEDIATELY_REBOXED,RV_NEGATING_RESULT_OF_COMPARETO,DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS,PZ_DONT_REUSE_ENTRY_OBJECTS_IN_ITERATORS"/>
587 reports="DLS_DEAD_LOCAL_STORE_IN_RETURN,EC_BAD_ARRAY_COMPARE,DLS_OVERWRITTEN_INCREMENT,ICAST_BAD_SHIFT_AMOUNT,BSHIFT_WRONG_ADD_PRIORITY,ICAST_QUESTIONABLE_UNSIGNED_RIGHT_SHIFT,DMI_BAD_MONTH,IM_MULTIPLYING_RESULT_OF_IREM,IM_BAD_CHECK_FOR_ODD,DMI_INVOKING_TOSTRING_ON_ARRAY,DMI_INVOKING_TOSTRING_ON_ANONYMOUS_ARRAY,IM_AVERAGE_COMPUTATION_COULD_OVERFLOW,IC_SUPERCLASS_USES_SUBCLASS_DURING_INITIALIZATION,ICAST_INTEGER_MULTIPLY_CAST_TO_LONG,BX_UNBOXED_AND_COERCED_FOR_TERNARY_OPERATOR,BX_BOXING_IMMEDIATELY_UNBOXED,BX_BOXING_IMMEDIATELY_UNBOXED_TO_PERFORM_COERCION,IJU_ASSERT_METHOD_INVOKED_FROM_RUN_METHOD,DMI_INVOKING_HASHCODE_ON_ARRAY,BX_UNBOXING_IMMEDIATELY_REBOXED,RV_NEGATING_RESULT_OF_COMPARETO,DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS,PZ_DONT_REUSE_ENTRY_OBJECTS_IN_ITERATORS"/>
528588 <Detector class="edu.umd.cs.findbugs.detect.IntCast2LongAsInstant" speed="fast"
529589 reports="ICAST_INT_2_LONG_AS_INSTANT"/>
530590 <Detector class="edu.umd.cs.findbugs.detect.FindSleepWithLockHeld" speed="slow"
612672 <BugCode abbrev="FB"/>
613673 <BugCode abbrev="AT"/>
614674 <!-- Bug patterns -->
675 <BugPattern abbrev="CNT" type="CNT_ROUGH_CONSTANT_VALUE" category="BAD_PRACTICE"/>
676
615677 <BugPattern abbrev="AT" type="AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION"
616678 category="MT_CORRECTNESS"/>
617679 <BugPattern abbrev="XSS" type="XSS_REQUEST_PARAMETER_TO_SEND_ERROR" category="SECURITY" cweid="81"/>
633695 <BugPattern abbrev="NP" type="NP_SYNC_AND_NULL_CHECK_FIELD" category="MT_CORRECTNESS"
634696 cweid="585"/>
635697 <BugPattern abbrev="NP" type="NP_BOOLEAN_RETURN_NULL" category="BAD_PRACTICE"/>
636 <BugPattern abbrev="NP" type="NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" category="CORRECTNESS"/>
698 <BugPattern abbrev="NP" type="NP_OPTIONAL_RETURN_NULL" category="CORRECTNESS"/>
699 <BugPattern abbrev="NP" type="NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" category="CORRECTNESS"/>
637700 <BugPattern abbrev="VR" type="VR_UNRESOLVABLE_REFERENCE" category="CORRECTNESS"
638701 experimental="true"/>
639702 <BugPattern abbrev="SW" type="SW_SWING_METHODS_INVOKED_IN_SWING_THREAD"
666729 category="MALICIOUS_CODE"/>
667730 <BugPattern abbrev="IMSE" type="IMSE_DONT_CATCH_IMSE" category="BAD_PRACTICE"/>
668731 <BugPattern abbrev="FL" type="FL_MATH_USING_FLOAT_PRECISION" category="CORRECTNESS"/>
732 <BugPattern abbrev="CAA" type="CAA_COVARIANT_ARRAY_FIELD" category="STYLE"
733 experimental="true"/>
734 <BugPattern abbrev="CAA" type="CAA_COVARIANT_ARRAY_RETURN" category="STYLE"
735 experimental="true"/>
736 <BugPattern abbrev="CAA" type="CAA_COVARIANT_ARRAY_LOCAL" category="STYLE"
737 experimental="true"/>
738 <BugPattern abbrev="CAA" type="CAA_COVARIANT_ARRAY_ELEMENT_STORE" category="CORRECTNESS"
739 experimental="true"/>
669740 <BugPattern abbrev="CN" type="CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE"
670741 category="BAD_PRACTICE"/>
671742 <BugPattern abbrev="CN" type="CN_IDIOM" category="BAD_PRACTICE"/>
705776 <BugPattern abbrev="Dm" type="DM_CONVERT_CASE" category="I18N"/>
706777 <BugPattern abbrev="Bx" type="DM_BOXED_PRIMITIVE_TOSTRING" category="PERFORMANCE"/>
707778 <BugPattern abbrev="Bx" type="DM_BOXED_PRIMITIVE_FOR_PARSING" category="PERFORMANCE"/>
779 <BugPattern abbrev="Bx" type="DM_BOXED_PRIMITIVE_FOR_COMPARE" category="PERFORMANCE"/>
708780 <BugPattern abbrev="Bx" type="BX_UNBOXED_AND_COERCED_FOR_TERNARY_OPERATOR"
709 category="CORRECTNESS"/>
781 category="PERFORMANCE"/>
710782 <BugPattern abbrev="Bx" type="BX_UNBOXING_IMMEDIATELY_REBOXED" category="PERFORMANCE"/>
711783 <BugPattern abbrev="Bx" type="BX_BOXING_IMMEDIATELY_UNBOXED" category="PERFORMANCE"/>
712784 <BugPattern abbrev="Bx" type="BX_BOXING_IMMEDIATELY_UNBOXED_TO_PERFORM_COERCION"
723795 <BugPattern abbrev="RV" type="RV_REM_OF_RANDOM_INT" category="STYLE"/>
724796 <BugPattern abbrev="RV" type="RV_REM_OF_HASHCODE" category="STYLE"/>
725797 <BugPattern abbrev="RV" type="RV_01_TO_INT" category="CORRECTNESS"/>
798 <BugPattern abbrev="Dm" type="DM_INVALID_MIN_MAX" category="CORRECTNESS"/>
726799 <BugPattern abbrev="Dm" type="DM_NEXTINT_VIA_NEXTDOUBLE" category="PERFORMANCE"/>
727800 <BugPattern abbrev="Dm" type="DM_USELESS_THREAD" category="MT_CORRECTNESS"/>
728801 <BugPattern abbrev="SQL" type="SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE"
730803 <BugPattern abbrev="SQL" type="SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING"
731804 category="SECURITY" cweid="89"/>
732805 <BugPattern abbrev="DC" type="DC_DOUBLECHECK" category="MT_CORRECTNESS" cweid="609"/>
806 <BugPattern abbrev="DC" type="DC_PARTIALLY_CONSTRUCTED" category="MT_CORRECTNESS" cweid="609"/>
733807 <BugPattern abbrev="FI" type="FI_PUBLIC_SHOULD_BE_PROTECTED" category="MALICIOUS_CODE"
734808 cweid="583"/>
735809 <BugPattern abbrev="FI" type="FI_EMPTY" category="BAD_PRACTICE"/>
752826 <BugPattern abbrev="Eq" type="EQ_OTHER_NO_OBJECT" category="CORRECTNESS"/>
753827 <BugPattern abbrev="Eq" type="EQ_SELF_NO_OBJECT" category="BAD_PRACTICE"/>
754828 <BugPattern abbrev="Co" type="CO_SELF_NO_OBJECT" category="BAD_PRACTICE"/>
755 <BugPattern abbrev="Co" type="CO_COMPARETO_RESULTS_MIN_VALUE" category="CORRECTNESS"/>
829 <BugPattern abbrev="Co" type="CO_COMPARETO_RESULTS_MIN_VALUE" category="BAD_PRACTICE"/>
830 <BugPattern abbrev="Co" type="CO_COMPARETO_INCORRECT_FLOATING" category="BAD_PRACTICE"/>
756831 <BugPattern abbrev="RV" type="RV_NEGATING_RESULT_OF_COMPARETO" category="BAD_PRACTICE"/>
757832 <BugPattern abbrev="ES" type="ES_COMPARING_STRINGS_WITH_EQ" category="BAD_PRACTICE"/>
758833 <BugPattern abbrev="ES" type="ES_COMPARING_PARAMETER_STRING_WITH_EQ"
815890 <BugPattern abbrev="MS" type="MS_PKGPROTECT" category="MALICIOUS_CODE"/>
816891 <BugPattern abbrev="MS" type="MS_MUTABLE_HASHTABLE" category="MALICIOUS_CODE"/>
817892 <BugPattern abbrev="MS" type="MS_MUTABLE_ARRAY" category="MALICIOUS_CODE"/>
893 <BugPattern abbrev="MS" type="MS_MUTABLE_COLLECTION" category="MALICIOUS_CODE"/>
894 <BugPattern abbrev="MS" type="MS_MUTABLE_COLLECTION_PKGPROTECT" category="MALICIOUS_CODE"/>
818895 <BugPattern abbrev="MS" type="MS_CANNOT_BE_FINAL" category="MALICIOUS_CODE"/>
896 <BugPattern abbrev="ME" type="ME_MUTABLE_ENUM_FIELD" category="BAD_PRACTICE"/>
897 <BugPattern abbrev="ME" type="ME_ENUM_FIELD_SETTER" category="BAD_PRACTICE"/>
819898 <BugPattern abbrev="Nm" type="NM_METHOD_NAMING_CONVENTION" category="BAD_PRACTICE"/>
820899 <BugPattern abbrev="Nm" type="NM_FIELD_NAMING_CONVENTION" category="BAD_PRACTICE"/>
821900 <BugPattern abbrev="Nm" type="NM_SAME_SIMPLE_NAME_AS_INTERFACE" category="BAD_PRACTICE"/>
884963 <BugPattern abbrev="Wa" type="WA_NOT_IN_LOOP" category="MT_CORRECTNESS"/>
885964 <BugPattern abbrev="Wa" type="WA_AWAIT_NOT_IN_LOOP" category="MT_CORRECTNESS"/>
886965 <BugPattern abbrev="No" type="NO_NOTIFY_NOT_NOTIFYALL" category="MT_CORRECTNESS"/>
966 <BugPattern abbrev="UC" type="UC_USELESS_VOID_METHOD" category="STYLE"/>
967 <BugPattern abbrev="UC" type="UC_USELESS_CONDITION" category="STYLE"/>
968 <BugPattern abbrev="UC" type="UC_USELESS_CONDITION_TYPE" category="STYLE"/>
969 <BugPattern abbrev="UC" type="UC_USELESS_OBJECT" category="STYLE"/>
970 <BugPattern abbrev="UC" type="UC_USELESS_OBJECT_STACK" category="STYLE"/>
971 <BugPattern abbrev="RANGE" type="RANGE_ARRAY_INDEX" category="CORRECTNESS"/>
972 <BugPattern abbrev="RANGE" type="RANGE_ARRAY_OFFSET" category="CORRECTNESS"/>
973 <BugPattern abbrev="RANGE" type="RANGE_ARRAY_LENGTH" category="CORRECTNESS"/>
974 <BugPattern abbrev="RANGE" type="RANGE_STRING_INDEX" category="CORRECTNESS"/>
887975 <BugPattern abbrev="RV" type="RV_RETURN_VALUE_IGNORED" category="CORRECTNESS"/>
888976 <BugPattern abbrev="RV" type="RV_RETURN_VALUE_IGNORED_INFERRED" category="STYLE"/>
977 <BugPattern abbrev="RV" type="RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT" category="STYLE"/>
889978 <BugPattern abbrev="RV" type="RV_RETURN_VALUE_IGNORED_BAD_PRACTICE"
890979 category="BAD_PRACTICE" cweid="253"/>
891980 <BugPattern abbrev="RV" type="RV_EXCEPTION_NOT_THROWN" category="CORRECTNESS"/>
10011090 category="BAD_PRACTICE"/>
10021091 <BugPattern abbrev="SBSC" type="SBSC_USE_STRINGBUFFER_CONCATENATION"
10031092 category="PERFORMANCE"/>
1004 <BugPattern abbrev="ITA" type="ITA_INEFFICIENT_TO_ARRAY" category="PERFORMANCE"/>
1093 <BugPattern abbrev="IIL" type="IIL_ELEMENTS_GET_LENGTH_IN_LOOP" category="PERFORMANCE"
1094 experimental="true"/>
1095 <BugPattern abbrev="IIL" type="IIL_PREPARE_STATEMENT_IN_LOOP" category="PERFORMANCE"
1096 experimental="true"/>
1097 <BugPattern abbrev="IIL" type="IIL_PATTERN_COMPILE_IN_LOOP" category="PERFORMANCE"
1098 experimental="true"/>
1099 <BugPattern abbrev="IIL" type="IIL_PATTERN_COMPILE_IN_LOOP_INDIRECT" category="PERFORMANCE"
1100 experimental="true"/>
1101 <BugPattern abbrev="IIO" type="IIO_INEFFICIENT_INDEX_OF" category="PERFORMANCE"
1102 experimental="true"/>
1103 <BugPattern abbrev="IIO" type="IIO_INEFFICIENT_LAST_INDEX_OF" category="PERFORMANCE"
1104 experimental="true"/>
1105 <BugPattern abbrev="ITA" type="ITA_INEFFICIENT_TO_ARRAY" category="PERFORMANCE"
1106 experimental="true"/>
10051107 <BugPattern abbrev="IJU" type="IJU_ASSERT_METHOD_INVOKED_FROM_RUN_METHOD"
10061108 category="CORRECTNESS"/>
10071109 <BugPattern abbrev="IJU" type="IJU_BAD_SUITE_METHOD" category="CORRECTNESS"/>
10791181 category="CORRECTNESS"/>
10801182 <BugPattern abbrev="DLS" type="DLS_OVERWRITTEN_INCREMENT" category="CORRECTNESS"/>
10811183 <BugPattern abbrev="BSHIFT" type="ICAST_BAD_SHIFT_AMOUNT" category="CORRECTNESS"/>
1184 <BugPattern abbrev="BSHIFT" type="BSHIFT_WRONG_ADD_PRIORITY" category="CORRECTNESS"/>
10821185 <BugPattern abbrev="IM" type="IM_MULTIPLYING_RESULT_OF_IREM" category="CORRECTNESS"/>
10831186 <BugPattern abbrev="IM" type="IM_BAD_CHECK_FOR_ODD" category="STYLE"/>
10841187 <BugPattern abbrev="IM" type="IM_AVERAGE_COMPUTATION_COULD_OVERFLOW" category="STYLE"/>
0 <?xml version="1.0" encoding="UTF-8"?>
1 <schema targetNamespace="http://findbugs.sourceforge.net/filter/3.0.0" elementFormDefault="unqualified"
2 xmlns="http://www.w3.org/2001/XMLSchema" xmlns:fb="http://findbugs.sourceforge.net/filter/3.0.0">
3
4 <element name="FindBugsFilter" type="fb:FindBugsFilterType"></element>
5
6 <element name="Match" type="fb:MatchType"></element>
7
8 <element name="Bug" type="fb:BugType">
9 <annotation>
10 <documentation>This element specifies a particular bug pattern or patterns to match.
11 If more than one attribute is specified on the same Bug element, all bug patterns that match either one of specified
12 pattern names, abbreviations, or categories will be matched.</documentation>
13 </annotation>
14 </element>
15
16 <element name="BugCode" type="fb:BugCodeType">
17 <annotation>
18 <documentation>This element specifies a particular bug code or multiple bug codes to match.
19 </documentation>
20 </annotation>
21 </element>
22
23 <element name="BugPattern" type="fb:BugPatternType">
24 <annotation>
25 <documentation>This element specifies a particular bug pattern or multiple patterns to match.</documentation>
26 </annotation>
27 </element>
28
29 <element name="Priority" type="fb:PriorityType">
30 <annotation>
31 <documentation>This element matches warnings with a particular priority.
32 This is deprecated, 'Confidence' should be used instead</documentation>
33 </annotation>
34 </element>
35
36 <element name="Confidence" type="fb:ConfidenceType">
37 <annotation>
38 <documentation>This element matches warnings with a particular confidence.</documentation>
39 </annotation>
40 </element>
41
42 <element name="Rank" type="fb:RankType">
43 <annotation>
44 <documentation>This element matches warnings with a particular rank.</documentation>
45 </annotation>
46 </element>
47
48 <element name="Package" type="fb:PackageType">
49 <annotation>
50 <documentation>This element matches warnings associated with classes within the package specified using name
51 attribute.</documentation>
52 </annotation>
53 </element>
54
55 <element name="Class" type="fb:ClassType">
56 <annotation>
57 <documentation>This element matches warnings associated with a particular class.</documentation>
58 </annotation>
59 </element>
60
61 <element name="Type" type="fb:TypeType">
62 <annotation>
63 <documentation>This element matches warnings associated with a particular type.</documentation>
64 </annotation>
65 </element>
66
67 <element name="Source" type="fb:SourceType">
68 <annotation>
69 <documentation>This element matches warnings associated with a particular source file name.</documentation>
70 </annotation>
71 </element>
72
73 <element name="Method" type="fb:MethodType">
74 <annotation>
75 <documentation>This element specifies a method.</documentation>
76 </annotation>
77 </element>
78
79 <element name="Field" type="fb:FieldType">
80 <annotation>
81 <documentation>This element specifies a field.</documentation>
82 </annotation>
83 </element>
84
85 <element name="Local" type="fb:LocalType">
86 <annotation>
87 <documentation>This element specifies a local variable.</documentation>
88 </annotation>
89 </element>
90
91 <element name="Or" type="fb:OrType">
92 <annotation>
93 <documentation>This element combines Match clauses as disjuncts.
94 I.e., you can put two Method elements in an Or clause in order to match either method.</documentation>
95 </annotation>
96 </element>
97
98 <element name="And" type="fb:AndType">
99 <annotation>
100 <documentation>This element combines Match clauses which both must evaluate to true.
101 I.e., you can put Bug and Confidence elements in an And clause in order to match specific bugs with given confidence only.</documentation>
102 </annotation>
103 </element>
104
105 <element name="Not" type="fb:NotType">
106 <annotation>
107 <documentation>This element inverts the included child Match.
108 I.e., you can put a Bug element in a Not clause in order to match any bug excluding the given one.</documentation>
109 </annotation>
110 </element>
111
112 <complexType name="FindBugsFilterType">
113 <sequence>
114 <element ref="fb:Match" maxOccurs="unbounded" minOccurs="0"></element>
115 </sequence>
116 </complexType>
117
118
119 <complexType name="BugCodeType">
120 <annotation>
121 <documentation></documentation>
122 </annotation>
123 <attribute name="name" type="string">
124 <annotation>
125 <documentation>A comma-separated list of bug code types. You can find the bug code types for particular warnings by looking at the output produced by the -xml output option (the type attribute of BugInstance elements), or from the bug descriptions document.</documentation>
126 </annotation>
127 </attribute>
128 </complexType>
129
130 <complexType name="BugPatternType">
131 <annotation>
132 <documentation></documentation>
133 </annotation>
134 <attribute name="name" type="string">
135 <annotation>
136 <documentation>A comma-separated list of bug pattern types. You can find the bug pattern types for particular warnings by looking at the output produced by the -xml output option (the type attribute of BugInstance elements), or from the bug descriptions document.</documentation>
137 </annotation>
138 </attribute>
139 </complexType>
140
141 <complexType name="BugType">
142 <annotation>
143 <documentation></documentation>
144 </annotation>
145 <attribute name="pattern" type="string">
146 <annotation>
147 <documentation>A comma-separated list of bug pattern types. You can find the bug pattern types for particular warnings by looking at the output produced by the -xml output option (the type attribute of BugInstance elements), or from the bug descriptions document.</documentation>
148 </annotation></attribute>
149 <attribute name="code" type="string">
150 <annotation>
151 <documentation>A comma-separated list of bug abbreviations.</documentation>
152 </annotation></attribute>
153 <attribute name="category" type="string">
154 <annotation>
155 <documentation>A comma separated list of bug category names.</documentation>
156 </annotation></attribute>
157 </complexType>
158
159 <complexType name="ClassType">
160 <annotation>
161 <documentation></documentation>
162 </annotation>
163 <attribute name="name" type="string">
164 <annotation>
165 <documentation>The exact or regex match pattern for a class name. If the name starts with the ~ character the rest of attribute content is interpreted as a Java regular expression.</documentation>
166 </annotation></attribute>
167 <attribute name="role" type="string">
168 <annotation>
169 <documentation>The class role</documentation>
170 </annotation></attribute>
171 </complexType>
172
173 <complexType name="TypeType">
174 <annotation>
175 <documentation></documentation>
176 </annotation>
177 <attribute name="descriptor" type="string">
178 <annotation>
179 <documentation>The exact or regex match pattern for type descriptor. If the descriptor starts with the ~ character the rest of attribute content is interpreted as a Java regular expression.</documentation>
180 </annotation></attribute>
181 <attribute name="role" type="string">
182 <annotation>
183 <documentation>The type role</documentation>
184 </annotation></attribute>
185 <attribute name="typeParameters" type="string">
186 <annotation>
187 <documentation>The type parameters</documentation>
188 </annotation></attribute>
189 </complexType>
190
191 <complexType name="SourceType">
192 <annotation>
193 <documentation></documentation>
194 </annotation>
195 <attribute name="name" type="string">
196 <annotation>
197 <documentation>The exact or regex match pattern for a source file name. If the name starts with the ~ character the rest of attribute content is interpreted as a Java regular expression.</documentation>
198 </annotation></attribute>
199 </complexType>
200
201 <complexType name="FieldType">
202 <annotation>
203 <documentation></documentation>
204 </annotation>
205 <attribute name="name" type="string">
206 <annotation>
207 <documentation>The exact or regex match pattern for a field name. If the name starts with the ~ character the rest of attribute content is interpreted as a Java regular expression.</documentation>
208 </annotation></attribute>
209 <attribute name="type" type="string">
210 <annotation>
211 <documentation>Fully qualified type of the field</documentation>
212 </annotation></attribute>
213 <attribute name="role" type="string">
214 <annotation>
215 <documentation>The field's role</documentation>
216 </annotation></attribute>
217 </complexType>
218
219 <complexType name="LocalType">
220 <annotation>
221 <documentation></documentation>
222 </annotation>
223 <attribute name="name" type="string">
224 <annotation>
225 <documentation>The exact or regex match pattern for a local variable name. If the name starts with the ~ character the rest of attribute content is interpreted as a Java regular expression.</documentation></annotation></attribute>
226 </complexType>
227
228 <complexType name="MethodType">
229 <annotation>
230 <documentation></documentation>
231 </annotation>
232 <attribute name="name" type="string">
233 <annotation>
234 <documentation>The exact or regex match pattern for a method name.. If the name starts with the ~ character the rest of attribute content is interpreted as a Java regular expression.</documentation>
235 </annotation></attribute>
236 <attribute name="params" type="string">
237 <annotation>
238 <documentation>A comma-separated list of the fully qualified types of the method's parameters.</documentation>
239 </annotation></attribute>
240 <attribute name="returns" type="string">
241 <annotation>
242 <documentation>The method's fully qualified return type</documentation>
243 </annotation></attribute>
244 <attribute name="role" type="string">
245 <annotation>
246 <documentation>The method's role</documentation>
247 </annotation></attribute>
248 </complexType>
249
250 <complexType name="PackageType">
251 <annotation>
252 <documentation></documentation>
253 </annotation>
254 <attribute name="name" type="string">
255 <annotation>
256 <documentation>The exact or regex match pattern for a package name. Nested packages are not included (along the lines of Java import statement). If the name starts with the ~ character the rest of attribute content is interpreted as a Java regular expression.</documentation>
257 </annotation></attribute>
258 </complexType>
259
260 <complexType name="PriorityType">
261 <annotation>
262 <documentation>Deprecated. Use ConfidenceType instead</documentation>
263 </annotation>
264 <attribute name="value" type="fb:PriorityValueType">
265 <annotation>
266 <documentation>1: high-priority warnings, 2: medium-priority warnings, 3: low-priority warnings</documentation>
267 </annotation></attribute>
268 </complexType>
269
270 <complexType name="ConfidenceType">
271 <annotation>
272 <documentation></documentation>
273 </annotation>
274 <attribute name="value" type="fb:ConfidenceValueType">
275 <annotation>
276 <documentation>1: high-priority warnings, 2: medium-priority warnings, 3: low-priority warnings</documentation>
277 </annotation></attribute>
278 </complexType>
279
280 <complexType name="RankType">
281 <annotation>
282 <documentation></documentation>
283 </annotation>
284 <attribute name="value" type="fb:RankValueType">
285 <annotation>
286 <documentation>1 to 4 are scariest, 5 to 9 scary, 10 to 14 troubling, and 15 to 20 of concern bugs.</documentation>
287 </annotation></attribute>
288 </complexType>
289
290 <simpleType name="PriorityValueType">
291 <restriction base="int">
292 <minInclusive value="1"></minInclusive>
293 <maxInclusive value="3"></maxInclusive>
294 </restriction>
295 </simpleType>
296
297 <simpleType name="ConfidenceValueType">
298 <restriction base="int">
299 <minInclusive value="1"></minInclusive>
300 <maxInclusive value="3"></maxInclusive>
301 </restriction>
302 </simpleType>
303
304 <simpleType name="RankValueType">
305 <restriction base="int">
306 <minInclusive value="1"></minInclusive>
307 <maxInclusive value="20"></maxInclusive>
308 </restriction>
309 </simpleType>
310
311 <complexType name="MatchType">
312 <sequence maxOccurs="unbounded" minOccurs="0">
313 <element ref="fb:Bug" maxOccurs="unbounded" minOccurs="0"></element>
314 <element ref="fb:Class" maxOccurs="unbounded" minOccurs="0"></element>
315 <element ref="fb:Source" maxOccurs="unbounded" minOccurs="0"></element>
316 <element ref="fb:Field" maxOccurs="unbounded" minOccurs="0"></element>
317 <element ref="fb:Local" maxOccurs="unbounded" minOccurs="0"></element>
318 <element ref="fb:Method" maxOccurs="unbounded" minOccurs="0"></element>
319 <element ref="fb:Or" maxOccurs="unbounded" minOccurs="0"></element>
320 <element ref="fb:And" maxOccurs="unbounded" minOccurs="0"></element>
321 <element ref="fb:Package" maxOccurs="unbounded" minOccurs="0"></element>
322 <element ref="fb:Priority" maxOccurs="unbounded" minOccurs="0"></element>
323 <element ref="fb:Confidence" maxOccurs="unbounded" minOccurs="0"></element>
324 <element ref="fb:Rank" maxOccurs="unbounded" minOccurs="0"></element>
325 <element ref="fb:Not" maxOccurs="unbounded" minOccurs="0"></element>
326 <element ref="fb:BugCode" maxOccurs="unbounded" minOccurs="0"></element>
327 <element ref="fb:BugPattern" maxOccurs="unbounded" minOccurs="0"></element>
328 </sequence>
329 </complexType>
330
331 <complexType name="NotType">
332 <annotation>
333 <documentation></documentation>
334 </annotation>
335 <sequence maxOccurs="unbounded" minOccurs="0">
336 <element ref="fb:And" maxOccurs="unbounded" minOccurs="0"></element>
337 <element ref="fb:Bug" maxOccurs="unbounded" minOccurs="0"></element>
338 <element ref="fb:BugCode" maxOccurs="unbounded" minOccurs="0"></element>
339 <element ref="fb:BugPattern" maxOccurs="unbounded" minOccurs="0"></element>
340 <element ref="fb:Class" maxOccurs="unbounded" minOccurs="0"></element>
341 <element ref="fb:Confidence" maxOccurs="unbounded" minOccurs="0"></element>
342 <element ref="fb:Field" maxOccurs="unbounded" minOccurs="0"></element>
343 <element ref="fb:Local" maxOccurs="unbounded" minOccurs="0"></element>
344 <element ref="fb:Method" maxOccurs="unbounded" minOccurs="0"></element>
345 <element ref="fb:Not" maxOccurs="unbounded" minOccurs="0"></element>
346 <element ref="fb:Or" maxOccurs="unbounded" minOccurs="0"></element>
347 <element ref="fb:Package" maxOccurs="unbounded" minOccurs="0"></element>
348 <element ref="fb:Priority" maxOccurs="unbounded" minOccurs="0"></element>
349 <element ref="fb:Rank" maxOccurs="unbounded" minOccurs="0"></element>
350 <element ref="fb:Source" maxOccurs="unbounded" minOccurs="0"></element>
351 </sequence>
352 </complexType>
353
354 <complexType name="OrType">
355 <annotation>
356 <documentation></documentation>
357 </annotation>
358 <sequence maxOccurs="unbounded" minOccurs="0">
359 <element ref="fb:And" maxOccurs="unbounded" minOccurs="0"></element>
360 <element ref="fb:Bug" maxOccurs="unbounded" minOccurs="0"></element>
361 <element ref="fb:BugCode" maxOccurs="unbounded" minOccurs="0"></element>
362 <element ref="fb:BugPattern" maxOccurs="unbounded" minOccurs="0"></element>
363 <element ref="fb:Class" maxOccurs="unbounded" minOccurs="0"></element>
364 <element ref="fb:Confidence" maxOccurs="unbounded" minOccurs="0"></element>
365 <element ref="fb:Field" maxOccurs="unbounded" minOccurs="0"></element>
366 <element ref="fb:Local" maxOccurs="unbounded" minOccurs="0"></element>
367 <element ref="fb:Method" maxOccurs="unbounded" minOccurs="0"></element>
368 <element ref="fb:Not" maxOccurs="unbounded" minOccurs="0"></element>
369 <element ref="fb:Or" maxOccurs="unbounded" minOccurs="0"></element>
370 <element ref="fb:Package" maxOccurs="unbounded" minOccurs="0"></element>
371 <element ref="fb:Priority" maxOccurs="unbounded" minOccurs="0"></element>
372 <element ref="fb:Rank" maxOccurs="unbounded" minOccurs="0"></element>
373 <element ref="fb:Source" maxOccurs="unbounded" minOccurs="0"></element>
374 </sequence>
375 </complexType>
376
377 <complexType name="AndType">
378 <annotation>
379 <documentation></documentation>
380 </annotation>
381 <sequence maxOccurs="unbounded" minOccurs="0">
382 <element ref="fb:And" maxOccurs="unbounded" minOccurs="0"></element>
383 <element ref="fb:Bug" maxOccurs="unbounded" minOccurs="0"></element>
384 <element ref="fb:BugCode" maxOccurs="unbounded" minOccurs="0"></element>
385 <element ref="fb:BugPattern" maxOccurs="unbounded" minOccurs="0"></element>
386 <element ref="fb:Class" maxOccurs="unbounded" minOccurs="0"></element>
387 <element ref="fb:Confidence" maxOccurs="unbounded" minOccurs="0"></element>
388 <element ref="fb:Field" maxOccurs="unbounded" minOccurs="0"></element>
389 <element ref="fb:Local" maxOccurs="unbounded" minOccurs="0"></element>
390 <element ref="fb:Method" maxOccurs="unbounded" minOccurs="0"></element>
391 <element ref="fb:Not" maxOccurs="unbounded" minOccurs="0"></element>
392 <element ref="fb:Or" maxOccurs="unbounded" minOccurs="0"></element>
393 <element ref="fb:Package" maxOccurs="unbounded" minOccurs="0"></element>
394 <element ref="fb:Priority" maxOccurs="unbounded" minOccurs="0"></element>
395 <element ref="fb:Rank" maxOccurs="unbounded" minOccurs="0"></element>
396 <element ref="fb:Source" maxOccurs="unbounded" minOccurs="0"></element>
397 </sequence>
398 </complexType>
399 </schema>
1414
1515 <xsd:complexType name="PluginType">
1616 <xsd:sequence>
17 <xsd:element name="ShortDescription" type="xsd:string"/>
17 <xsd:element name="ShortDescription" type="xsd:string" />
1818 <xsd:element name="Details" type="xsd:string" />
19 <xsd:element name="BugsUrl" type="xsd:string" maxOccurs="1" minOccurs="0"></xsd:element>
20 <xsd:element name="AllBugsUrl" type="xsd:string" maxOccurs="1" minOccurs="0"></xsd:element>
1921 </xsd:sequence>
2022 </xsd:complexType>
2123
7981 <xsd:element name="Plugin" type="PluginType"/>
8082 <xsd:element name="FindBugsMain" type="FindBugsMainType" minOccurs="0" maxOccurs="unbounded"/>
8183 <xsd:element name="Cloud" type="CloudType" minOccurs="0" maxOccurs="unbounded"/>
82 <xsd:element name="PluginComponent" type="PluginComponentType" minOccurs="0" maxOccurs="unbounded"/>
84 <xsd:element name="PluginComponent" type="PluginComponentType" minOccurs="0" maxOccurs="unbounded"/>
8385 <xsd:element name="BugCategory" type="BugCategoryType" minOccurs="0" maxOccurs="unbounded"/>
8486 <xsd:element name="Detector" type="DetectorType" minOccurs="1" maxOccurs="unbounded"/>
8587 <xsd:element name="BugPattern" type="BugPatternType" minOccurs="1" maxOccurs="unbounded"/>
1515 </p>
1616 ]]>
1717 </Details>
18 <BugsUrl>http://findbugs.sourceforge.net/bugDescriptions.html</BugsUrl>
19 <AllBugsUrl>http://findbugs.sourceforge.net/allBugDescriptions.html</AllBugsUrl>
1820 </Plugin>
1921 <FindBugsMain cmd="addMessages" class="edu.umd.cs.findbugs.AddMessages">
20 <Description>add msgs (e.g., textual descriptions of bugs) to analysis results
21 </Description>
22 <Description>Add msgs (e.g., textual descriptions of bugs) to analysis results</Description>
2223 </FindBugsMain>
2324 <FindBugsMain cmd="analyze" class="edu.umd.cs.findbugs.FindBugs2">
2425 <Description>Perform FindBugs Analysis</Description>
4243 <Description>Set project configuration/options</Description>
4344 </FindBugsMain>
4445 <FindBugsMain cmd="history" class="edu.umd.cs.findbugs.workflow.MineBugHistory">
45 <Description>List details from multi-version analysis results</Description>
46 <Description>List details from multi-version analysis results</Description>
4647 </FindBugsMain>
4748 <FindBugsMain cmd="union" class="edu.umd.cs.findbugs.workflow.UnionResults">
4849 <Description>Merge analysis results from disjoint components</Description>
4950 </FindBugsMain>
5051 <FindBugsMain cmd="merge" class="edu.umd.cs.findbugs.workflow.Update">
51 <Description>Combine analysis results from different versions of software to produce multi-version analysis results </Description>
52 <Description>Combine analysis results from different versions of software to produce multi-version analysis results</Description>
5253 </FindBugsMain>
5354
5455 <FindBugsMain cmd="dis" class="edu.umd.cs.findbugs.workflow.PrintClass">
5556 <Description>Disassemble a class file</Description>
5657 </FindBugsMain>
5758 <FindBugsMain cmd="errors" class="edu.umd.cs.findbugs.workflowListErrors">
58 <Description>List analysis errors stored in results file
59 </Description>
59 <Description>List analysis errors stored in results file</Description>
6060 </FindBugsMain>
6161
6262 <!-- On changing this, please also update default cloud id in FindbugsPlugin -->
164164 Detectors
165165 **********************************************************************
166166 -->
167 <Detector class="edu.umd.cs.findbugs.detect.FindRoughConstants">
168 <Details>
169 <![CDATA[
170 <p> Finds constants which roughly (but not precisely) equal to known values like Math.PI.
171 </p>
172 ]]>
173 </Details>
174 </Detector>
167175 <Detector class="edu.umd.cs.findbugs.detect.InitializeNonnullFieldsInConstructor">
168176 <Details>
169177 <![CDATA[
170 <p> Finds nonnull fields that are not written to in constructors.
178 <p> Finds non-null fields that are not written to in constructors.
171179 </p>
172180 ]]>
173181 </Details>
175183 <Detector class="edu.umd.cs.findbugs.detect.IntCast2LongAsInstant">
176184 <Details>
177185 <![CDATA[
178 <p> Finds uses of 32-bit values to describe milliseconds since the epoc.
186 <p> Finds uses of 32-bit values to describe milliseconds since the epoch.
179187 </p>
180188 ]]>
181189 </Details>
184192 <Details>
185193 <![CDATA[
186194 <p> Builds database of parameters that take a 64 bit value describing
187 milliseconds since the epoc.</p>
195 milliseconds since the epoch.</p>
188196 ]]>
189197 </Details>
190198 </Detector>
285293 <Detector class="edu.umd.cs.findbugs.detect.Methods">
286294 <Details>
287295 <![CDATA[
288 <p> Builds of database of all methods defined in analyzed classes, for use
296 <p> Builds a database of all methods defined in analyzed classes, for use
289297 by other detectors.</p>
290298 ]]>
291299 </Details>
300308 <Detector class="edu.umd.cs.findbugs.detect.CalledMethods">
301309 <Details>
302310 <![CDATA[
303 <p> Builds of database of all methods invoked in analyzed classes, for use
311 <p> Builds a database of all methods invoked in analyzed classes, for use
304312 by other detectors.</p>
305313 ]]>
306314 </Details>
307315 </Detector>
316 <Detector class="edu.umd.cs.findbugs.detect.FindNoSideEffectMethods">
317 <Details>
318 <![CDATA[
319 <p> Looks for the methods which have no side effect, just return some value.</p>
320 ]]>
321 </Details>
322 </Detector>
323 <Detector class="edu.umd.cs.findbugs.detect.BuildStringPassthruGraph">
324 <Details>
325 <![CDATA[
326 <p> Builds the database of string parameters passed from method to method unchanged.</p>
327 ]]>
328 </Details>
329 </Detector>
308330 <Detector class="edu.umd.cs.findbugs.detect.FunctionsThatMightBeMistakenForProcedures">
309331 <Details>
310332 <![CDATA[
311333 <p> Looks for immutable classes with methods that return new instances of that class,
312 where people might accidently think those methods mutate the instance they are invoked on.
334 where people might accidentally think those methods mutate the instance they are invoked on.
313335 </p>
314336 ]]>
315337 </Details>
332354 <Detector class="edu.umd.cs.findbugs.detect.SynchronizeOnClassLiteralNotGetClass">
333355 <Details>
334356 <![CDATA[
335 <p> Look for code that synchronizes on the results of getClass rather than on class
357 <p> Looks for code that synchronizes on the results of getClass rather than on class
336358 literals.
337359 </p>
338360 ]]>
352374 <Details>
353375 <![CDATA[
354376 <p>
355 Looks for @NonNull annotations on methods, fields, and parameters.
377 Looks for @Nonnull annotations on methods, fields, and parameters.
356378 These can be used by the FindNullDeref detector to generate warnings
357379 when a possibly-null value is used in a context where only
358380 non-null values should be used.
380402 <![CDATA[
381403 <p>
382404 Analyze all methods in the application to determine which
383 methods always return nonnull values.
405 methods always return non-null values.
384406 </p>
385407 ]]>
386408 </Details>
393415 ]]>
394416 </Details>
395417 </Detector>
418 <Detector class="edu.umd.cs.findbugs.detect.OptionalReturnNull">
419 <Details>
420 <![CDATA[
421 <p> Looks for methods with Optional return type that return explicit null values.</p>
422
423 ]]>
424 </Details>
425 </Detector>
426 <Detector class="edu.umd.cs.findbugs.detect.FindUselessObjects">
427 <Details>
428 <![CDATA[
429 <p> Looks for useless objects.</p>
430
431 ]]>
432 </Details>
433 </Detector>
434 <Detector class="edu.umd.cs.findbugs.detect.MutableEnum">
435 <Details>
436 <![CDATA[
437 <p> Looks and warns about mutable enum fields.</p>
438 ]]>
439 </Details>
440 </Detector>
396441 <Detector class="edu.umd.cs.findbugs.detect.BadUseOfReturnValue">
397442 <Details>
398443 <![CDATA[
486531 <Details>
487532 <![CDATA[
488533 <p> This detector looks for code containing repeated conditional tests, such as (x == 5 || x == 5).
534 ]]>
535 </Details>
536 </Detector>
537 <Detector class="edu.umd.cs.findbugs.detect.RedundantConditions">
538 <Details>
539 <![CDATA[
540 <p> This detector looks for code containing useless conditions like the second condition in this expression: (x >= 10 && x >= 5).
489541 ]]>
490542 </Details>
491543 </Detector>
693745 ]]>
694746 </Details>
695747 </Detector>
748 <Detector class="edu.umd.cs.findbugs.detect.CovariantArrayAssignment">
749 <Details>
750 <![CDATA[
751 <p> This detector looks for covariant array assignments like Object[] array = new String[10] which may cause ArrayStoreException at runtime.
752 </p>
753 ]]>
754 </Details>
755 </Detector>
696756 <Detector class="edu.umd.cs.findbugs.detect.NumberConstructor">
697757 <Details>
698758 <![CDATA[
922982 <![CDATA[
923983 <p> This detector looks for calls to methods where the return value
924984 is suspiciously ignored. It is a slow detector.</p>
985 ]]>
986 </Details>
987 </Detector>
988 <Detector class="edu.umd.cs.findbugs.detect.FindComparatorProblems">
989 <Details>
990 <![CDATA[
991 <p> This detector looks for problems in Comparator.compare or Comparable.compareTo implementation.</p>
925992 ]]>
926993 </Details>
927994 </Detector>
10581125 <Details>
10591126 <![CDATA[
10601127 <p> This detector looks for String concatenation in loops using +.
1128 </p>
1129 ]]>
1130 </Details>
1131 </Detector>
1132 <Detector class="edu.umd.cs.findbugs.detect.InefficientInitializationInsideLoop">
1133 <Details>
1134 <![CDATA[
1135 <p> This detector looks for objects initialized within loop which can be moved outside for better performance.
1136 </p>
1137 ]]>
1138 </Details>
1139 </Detector>
1140 <Detector class="edu.umd.cs.findbugs.detect.InefficientIndexOf">
1141 <Details>
1142 <![CDATA[
1143 <p> This detector looks for code that uses String.indexOf(String) or String.lastIndexOf(String),
1144 passing a constant string of length 1. It is recommended to use the more efficient integer implementations.
1145 A fast detector.
10611146 </p>
10621147 ]]>
10631148 </Details>
14471532 <Details>
14481533 <![CDATA[
14491534 <p>
1450 TrainNonNullAnnotations collects @NonNull and @PossiblyNull annotations
1535 TrainNonNullAnnotations collects @Nonnull and @PossiblyNull annotations
14511536 and stores them to database files. This is a fast detector.
14521537 </p>
14531538 ]]>
15701655 BugPatterns
15711656 **********************************************************************
15721657 -->
1658 <BugPattern type="CNT_ROUGH_CONSTANT_VALUE">
1659 <ShortDescription>Rough value of known constant found</ShortDescription>
1660 <LongDescription>Rough value of {3} found: {2}</LongDescription>
1661 <Details>
1662 <![CDATA[
1663 <p>It's recommended to use the predefined library constant for code clarity and better precision.</p>
1664 ]]>
1665 </Details>
1666 </BugPattern>
15731667 <BugPattern type="SKIPPED_CLASS_TOO_BIG">
15741668 <ShortDescription>Class too big for analysis</ShortDescription>
15751669 <LongDescription>{0} is too big for analysis</LongDescription>
19442038 ]]>
19452039 </Details>
19462040 </BugPattern>
2041 <BugPattern type="NP_OPTIONAL_RETURN_NULL">
2042 <ShortDescription>Method with Optional return type returns explicit null</ShortDescription>
2043 <LongDescription>{1} has Optional return type and returns explicit null</LongDescription>
2044 <Details>
2045 <![CDATA[
2046 <p>
2047 The usage of Optional return type (java.util.Optional or com.google.common.base.Optiona)
2048 always mean that explicit null returns were not desired by design.
2049 Returning a null value in such case is a contract violation and will most likely break clients code.
2050 </p>
2051 ]]>
2052 </Details>
2053 </BugPattern>
19472054 <BugPattern type="NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR">
1948 <ShortDescription>Nonnull field is not initialized</ShortDescription>
1949 <LongDescription>Nonnull field {2.name} is not initialized by {1}</LongDescription>
2055 <ShortDescription>Non-null field is not initialized</ShortDescription>
2056 <LongDescription>Non-null field {2.name} is not initialized by {1}</LongDescription>
19502057 <Details>
19512058 <![CDATA[
1952 <p> The field is marked as nonnull, but isn't written to by the constructor.
2059 <p> The field is marked as non-null, but isn't written to by the constructor.
19532060 The field might be initialized elsewhere during constructor, or might always
19542061 be initialized before use.
19552062 </p>
20812188 The method performs math operations using floating point precision.
20822189 Floating point precision is very imprecise. For example,
20832190 16777216.0f + 1.0f = 16777216.0f. Consider using double math instead.</p>
2191 ]]>
2192 </Details>
2193 </BugPattern>
2194 <BugPattern type="CAA_COVARIANT_ARRAY_FIELD">
2195 <ShortDescription>Covariant array assignment to a field</ShortDescription>
2196 <LongDescription>Array of type {2} is assigned to the field of type {3}</LongDescription>
2197 <Details>
2198 <![CDATA[
2199 <p>Array of covariant type is assigned to a field. This is confusing and may lead to ArrayStoreException at runtime
2200 if the reference of some other type will be stored in this array later like in the following code:
2201 </p>
2202 <p><code>Number[] arr = new Integer[10];
2203 arr[0] = 1.0;
2204 </code></p>
2205 <p>Consider changing the type of created array or the field type.</p>
2206 ]]>
2207 </Details>
2208 </BugPattern>
2209 <BugPattern type="CAA_COVARIANT_ARRAY_LOCAL">
2210 <ShortDescription>Covariant array assignment to a local variable</ShortDescription>
2211 <LongDescription>Array of type {2} is assigned to the variable of type {3}</LongDescription>
2212 <Details>
2213 <![CDATA[
2214 <p>Array of covariant type is assigned to a local variable. This is confusing and may lead to ArrayStoreException at runtime
2215 if the reference of some other type will be stored in this array later like in the following code:
2216 </p>
2217 <p><code>Number[] arr = new Integer[10];
2218 arr[0] = 1.0;
2219 </code></p>
2220 <p>Consider changing the type of created array or the local variable type.</p>
2221 ]]>
2222 </Details>
2223 </BugPattern>
2224 <BugPattern type="CAA_COVARIANT_ARRAY_RETURN">
2225 <ShortDescription>Covariant array is returned from the method</ShortDescription>
2226 <LongDescription>Array of type {2} is returned from the method which return type is {3}</LongDescription>
2227 <Details>
2228 <![CDATA[
2229 <p>Array of covariant type is returned from the method. This is confusing and may lead to ArrayStoreException at runtime
2230 if the calling code will try to store the reference of some other type in the returned array.
2231 </p>
2232 <p>Consider changing the type of created array or the method return type.</p>
2233 ]]>
2234 </Details>
2235 </BugPattern>
2236 <BugPattern type="CAA_COVARIANT_ARRAY_ELEMENT_STORE">
2237 <ShortDescription>Possibly incompatible element is stored in covariant array</ShortDescription>
2238 <LongDescription>Value of type {2} is stored into array which element type is {3}</LongDescription>
2239 <Details>
2240 <![CDATA[
2241 <p>Value is stored into the array and the value type doesn't match the array type.
2242 It's known from the analysis that actual array type is narrower than the declared type of its variable or field
2243 and this assignment doesn't satisfy the original array type. This assignment may cause ArrayStoreException
2244 at runtime.
2245 </p>
20842246 ]]>
20852247 </Details>
20862248 </BugPattern>
24812643 ]]>
24822644 </Details>
24832645 </BugPattern>
2646 <BugPattern type="DM_BOXED_PRIMITIVE_FOR_COMPARE">
2647 <ShortDescription>Boxing a primitive to compare</ShortDescription>
2648 <LongDescription>Primitive is boxed to call {2}: use {3} instead</LongDescription>
2649 <Details>
2650 <![CDATA[
2651 <p>A boxed primitive is created just to call compareTo method. It's more efficient to use static compare method
2652 (for double and float since Java 1.4, for other primitive types since Java 1.7) which works on primitives directly.
2653 </p>
2654 ]]>
2655 </Details>
2656 </BugPattern>
24842657 <BugPattern type="DM_NEW_FOR_GETCLASS">
24852658 <ShortDescription>Method allocates an object, only to get the class object</ShortDescription>
24862659 <LongDescription>{1} allocates an object, only to get the class object</LongDescription>
25162689 ]]>
25172690 </Details>
25182691 </BugPattern>
2692 <BugPattern type="DM_INVALID_MIN_MAX">
2693 <ShortDescription>Incorrect combination of Math.max and Math.min</ShortDescription>
2694 <LongDescription>Incorrect combination of Math.max and Math.min: this code always returns {2}</LongDescription>
2695 <Details>
2696 <![CDATA[
2697 <p>This code tries to limit the value bounds using the construct like Math.min(0, Math.max(100, value)). However the order of
2698 the constants is incorrect: it should be Math.min(100, Math.max(0, value)). As the result this code always produces the same result
2699 (or NaN if the value is NaN).</p>
2700 ]]>
2701 </Details>
2702 </BugPattern>
25192703 <BugPattern type="DM_NEXTINT_VIA_NEXTDOUBLE">
25202704 <ShortDescription>Use the nextInt method of Random rather than nextDouble to generate a random integer</ShortDescription>
25212705 <LongDescription>{1} uses the nextDouble method of Random to generate a random integer; using nextInt is more efficient</LongDescription>
25312715 </Details>
25322716 </BugPattern>
25332717 <BugPattern type="SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE">
2534 <ShortDescription>Nonconstant string passed to execute method on an SQL statement</ShortDescription>
2535 <LongDescription>{1} passes a nonconstant String to an execute method on an SQL statement</LongDescription>
2536 <Details>
2537 <![CDATA[
2538 <p>The method invokes the execute method on an SQL statement with a String that seems
2718 <ShortDescription>Nonconstant string passed to execute or addBatch method on an SQL statement</ShortDescription>
2719 <LongDescription>{1} passes a nonconstant String to an execute or addBatch method on an SQL statement</LongDescription>
2720 <Details>
2721 <![CDATA[
2722 <p>The method invokes the execute or addBatch method on an SQL statement with a String that seems
25392723 to be dynamically generated. Consider using
25402724 a prepared statement instead. It is more efficient and less vulnerable to
25412725 SQL injection attacks.
25792763 ]]>
25802764 </Details>
25812765 </BugPattern>
2766 <BugPattern type="DC_PARTIALLY_CONSTRUCTED">
2767 <ShortDescription>Possible exposure of partially initialized object</ShortDescription>
2768 <LongDescription>Possible exposure of partially initialized object in {1}</LongDescription>
2769 <Details>
2770 <![CDATA[
2771 <p>Looks like this method uses lazy field initialization with double-checked locking.
2772 While the field is correctly declared as volatile, it's possible that the internal structure of
2773 the object is changed after the field assignment, thus another thread may see the partially initialized object.</p>
2774 <p>To fix this problem consider storing the object into the local variable first
2775 and save it to the volatile field only after it's fully constructed.
2776 </p>
2777 ]]>
2778 </Details>
2779 </BugPattern>
25822780 <BugPattern type="FI_FINALIZER_NULLS_FIELDS">
25832781 <ShortDescription>Finalizer nulls fields</ShortDescription>
25842782 <LongDescription>{3} is set to null inside finalize method in {1.class}</LongDescription>
25852783 <Details>
25862784 <![CDATA[
25872785 <p> This finalizer nulls out fields. This is usually an error, as it does not aid garbage collection,
2588 and the object is going to be garbage collected anyway.
2786 and the object is going to be garbage collected anyway.</p>
25892787 ]]>
25902788 </Details>
25912789 </BugPattern>
25962794 <![CDATA[
25972795 <p> This finalizer does nothing except null out fields. This is completely pointless, and requires that
25982796 the object be garbage collected, finalized, and then garbage collected again. You should just remove the finalize
2599 method.
2797 method.</p>
26002798 ]]>
26012799 </Details>
26022800 </BugPattern>
26662864 <p>If a connected set of objects beings finalizable, then the VM will invoke the
26672865 finalize method on all the finalizable object, possibly at the same time in different threads.
26682866 Thus, it is a particularly bad idea, in the finalize method for a class X, invoke finalize
2669 on objects referenced by X, because they may already be getting finalized in a separate thread.
2867 on objects referenced by X, because they may already be getting finalized in a separate thread.</p>
26702868 ]]>
26712869 </Details>
26722870 </BugPattern>
27522950 </BugPattern>
27532951 <BugPattern type="EQ_DOESNT_OVERRIDE_EQUALS">
27542952 <ShortDescription>Class doesn't override equals in superclass</ShortDescription>
2755 <LongDescription>{0} doesn't override {1.givenClass}</LongDescription>
2953 <LongDescription>{0} doesn't override {2.givenClass}</LongDescription>
27562954 <Details>
27572955 <![CDATA[
27582956 <p> This class extends a class that defines an equals method and adds fields, but doesn't
29243122 But people will sometimes negate the return value of compareTo, expecting that this will negate
29253123 the sign of the result. And it will, except in the case where the value returned is Integer.MIN_VALUE.
29263124 So just return -1 rather than Integer.MIN_VALUE.
3125 ]]>
3126 </Details>
3127 </BugPattern>
3128 <BugPattern type="CO_COMPARETO_INCORRECT_FLOATING">
3129 <ShortDescription>compareTo()/compare() incorrectly handles float or double value</ShortDescription>
3130 <LongDescription>{1} incorrectly handles {2} value</LongDescription>
3131 <Details>
3132 <![CDATA[
3133 <p>This method compares double or float values using pattern like this: val1 &gt; val2 ? 1 : val1 &lt; val2 ? -1 : 0.
3134 This pattern works incorrectly for -0.0 and NaN values which may result in incorrect sorting result or broken collection
3135 (if compared values are used as keys). Consider using Double.compare or Float.compare static methods which handle all
3136 the special cases correctly.</p>
29273137 ]]>
29283138 </Details>
29293139 </BugPattern>
29983208 equals returns true. If this is violated, weird and unpredictable
29993209 failures will occur in classes such as PriorityQueue.
30003210 In Java 5 the PriorityQueue.remove method uses the compareTo method,
3001 while in Java 6 it uses the equals method.
3211 while in Java 6 it uses the equals method.</p>
30023212
30033213 <p>From the JavaDoc for the compareTo method in the Comparable interface:
30043214 <blockquote>
30063216 Generally speaking, any class that implements the Comparable interface and violates this condition
30073217 should clearly indicate this fact. The recommended language
30083218 is "Note: this class has a natural ordering that is inconsistent with equals."
3009 </blockquote>
3219 </blockquote></p>
30103220 ]]>
30113221 </Details>
30123222 </BugPattern>
37353945 ]]>
37363946 </Details>
37373947 </BugPattern>
3948 <BugPattern type="MS_MUTABLE_COLLECTION">
3949 <ShortDescription>Field is a mutable collection</ShortDescription>
3950 <LongDescription>{1} is a mutable collection</LongDescription>
3951 <Details>
3952 <![CDATA[
3953 <p>A mutable collection instance is assigned to a final static field,
3954 thus can be changed by malicious code or by accident from another package.
3955 Consider wrapping this field into Collections.unmodifiableSet/List/Map/etc.
3956 to avoid this vulnerability.</p>
3957 ]]>
3958 </Details>
3959 </BugPattern>
3960 <BugPattern type="MS_MUTABLE_COLLECTION_PKGPROTECT">
3961 <ShortDescription>Field is a mutable collection which should be package protected</ShortDescription>
3962 <LongDescription>{1} is a mutable collection which should be package protected</LongDescription>
3963 <Details>
3964 <![CDATA[
3965 <p>A mutable collection instance is assigned to a final static field,
3966 thus can be changed by malicious code or by accident from another package.
3967 The field could be made package protected to avoid this vulnerability.
3968 Alternatively you may wrap this field into Collections.unmodifiableSet/List/Map/etc.
3969 to avoid this vulnerability.</p>
3970 ]]>
3971 </Details>
3972 </BugPattern>
37383973 <BugPattern type="MS_MUTABLE_ARRAY">
37393974 <ShortDescription>Field is a mutable array</ShortDescription>
37403975 <LongDescription>{1} is a mutable array</LongDescription>
37603995 ]]>
37613996 </Details>
37623997 </BugPattern>
3998 <BugPattern type="ME_MUTABLE_ENUM_FIELD">
3999 <ShortDescription>Enum field is public and mutable</ShortDescription>
4000 <LongDescription>{1} field is public and mutable</LongDescription>
4001 <Details>
4002 <![CDATA[
4003 <p>A mutable public field is defined inside a public enum, thus can be changed by malicious code or by accident from another package.
4004 Though mutable enum fields may be used for lazy initialization, it's a bad practice to expose them to the outer world.
4005 Consider declaring this field final and/or package-private.</p>
4006 ]]>
4007 </Details>
4008 </BugPattern>
4009 <BugPattern type="ME_ENUM_FIELD_SETTER">
4010 <ShortDescription>Public enum method unconditionally sets its field</ShortDescription>
4011 <LongDescription>{1} unconditionally sets the field {2.name}</LongDescription>
4012 <Details>
4013 <![CDATA[
4014 <p>This public method declared in public enum unconditionally sets enum field, thus this field can be changed by malicious code
4015 or by accident from another package. Though mutable enum fields may be used for lazy initialization, it's a bad practice to expose them to the outer world.
4016 Consider removing this method or declaring it package-private.</p>
4017 ]]>
4018 </Details>
4019 </BugPattern>
37634020 <BugPattern type="IA_AMBIGUOUS_INVOCATION_OF_INHERITED_OR_OUTER_METHOD">
37644021 <ShortDescription>Potentially ambiguous invocation of either an inherited or outer method</ShortDescription>
37654022 <LongDescription>Potentially ambiguous invocation of either an outer or inherited method {2} in {1}</LongDescription>
37664023 <Details>
37674024 <![CDATA[
3768 <p>
3769 An inner class is invoking a method that could be resolved to either a inherited method or a method defined in an outer class.
4025 <p>
4026 An inner class is invoking a method that could be resolved to either a inherited method or a method defined in an outer class.
37704027 For example, you invoke <code>foo(17)</code>, which is defined in both a superclass and in an outer method.
37714028 By the Java semantics,
37724029 it will be resolved to invoke the inherited method, but this may not be want
3773 you intend.
4030 you intend.
37744031 </p>
37754032 <p>If you really intend to invoke the inherited method,
37764033 invoke it by invoking the method on super (e.g., invoke super.foo(17)), and
37784035 that you want to invoke the inherited method, not the method in the outer class.
37794036 </p>
37804037 <p>If you call <code>this.foo(17)</code>, then the inherited method will be invoked. However, since FindBugs only looks at
3781 classfiles, it
4038 classfiles, it
37824039 can't tell the difference between an invocation of <code>this.foo(17)</code> and <code>foo(17)</code>, it will still
37834040 complain about a potential ambiguous invocation.
37844041 </p>
37944051 that its superclass is in a different package (e.g., <code>alpha.Foo</code> extends <code>beta.Foo</code>).
37954052 This can be exceptionally confusing, create lots of situations in which you have to look at import statements
37964053 to resolve references and creates many
3797 opportunities to accidently define methods that do not override methods in their superclasses.
4054 opportunities to accidentally define methods that do not override methods in their superclasses.
37984055 </p>
37994056 ]]>
38004057 </Details>
38084065 that the interface is in a different package (e.g., <code>alpha.Foo</code> extends <code>beta.Foo</code>).
38094066 This can be exceptionally confusing, create lots of situations in which you have to look at import statements
38104067 to resolve references and creates many
3811 opportunities to accidently define methods that do not override methods in their superclasses.
4068 opportunities to accidentally define methods that do not override methods in their superclasses.
38124069 </p>
38134070 ]]>
38144071 </Details>
39534210 <![CDATA[
39544211 <p> This regular method has the same name as the class it is defined in. It is likely that this was intended to be a constructor.
39554212 If it was intended to be a constructor, remove the declaration of a void return value.
3956 If you had accidently defined this method, realized the mistake, defined a proper constructor
4213 If you had accidentally defined this method, realized the mistake, defined a proper constructor
39574214 but can't get rid of this method due to backwards compatibility, deprecate the method.
39584215 </p>
39594216 ]]>
41834440 <p> This method contains a switch statement where default case is missing.
41844441 Usually you need to provide a default case.</p>
41854442 <p>Because the analysis only looks at the generated bytecode, this warning can be incorrect triggered if
4186 the default case is at the end of the switch statement and doesn't end with a break statement.
4443 the default case is at the end of the switch statement and the switch statement doesn't contain break statements for other
4444 cases.
41874445 ]]>
41884446 </Details>
41894447 </BugPattern>
44504708 <p> The variable referenced at this point is known to be null due to an earlier
44514709 check against null. Although this is valid, it might be a mistake (perhaps you
44524710 intended to refer to a different variable, or perhaps the earlier check to see if the
4453 variable is null should have been a check to see if it was nonnull).
4711 variable is null should have been a check to see if it was non-null).
44544712 </p>
44554713 ]]>
44564714 </Details>
46164874 ]]>
46174875 </Details>
46184876 </BugPattern>
4877 <BugPattern type="UC_USELESS_VOID_METHOD">
4878 <ShortDescription>Useless non-empty void method</ShortDescription>
4879 <LongDescription>Method {1} seems to be useless</LongDescription>
4880 <Details>
4881 <![CDATA[
4882 <p>Our analysis shows that this non-empty void method does not actually perform any useful work.
4883 Please check it: probably there's a mistake in its code or its body can be fully removed.
4884 </p>
4885 <p>We are trying to reduce the false positives as much as possible, but in some cases this warning might be wrong.
4886 Common false-positive cases include:</p>
4887 <p>- The method is intended to trigger loading of some class which may have a side effect.</p>
4888 <p>- The method is intended to implicitly throw some obscure exception.</p>
4889 ]]>
4890 </Details>
4891 </BugPattern>
4892 <BugPattern type="UC_USELESS_CONDITION">
4893 <ShortDescription>Condition has no effect</ShortDescription>
4894 <LongDescription>Useless condition: it's known that {2} at this point</LongDescription>
4895 <Details>
4896 <![CDATA[
4897 <p>This condition always produces the same result as the value of the involved variable was narrowed before.
4898 Probably something else was meant or condition can be removed.</p>
4899 ]]>
4900 </Details>
4901 </BugPattern>
4902 <BugPattern type="UC_USELESS_CONDITION_TYPE">
4903 <ShortDescription>Condition has no effect due to the variable type</ShortDescription>
4904 <LongDescription>Useless condition: it's always {2} because variable type is {3}</LongDescription>
4905 <Details>
4906 <![CDATA[
4907 <p>This condition always produces the same result due to the type range of the involved variable.
4908 Probably something else was meant or condition can be removed.</p>
4909 ]]>
4910 </Details>
4911 </BugPattern>
4912 <BugPattern type="UC_USELESS_OBJECT">
4913 <ShortDescription>Useless object created</ShortDescription>
4914 <LongDescription>Useless object stored in variable {2} of method {1}</LongDescription>
4915 <Details>
4916 <![CDATA[
4917 <p>Our analysis shows that this object is useless.
4918 It's created and modified, but its value never go outside of the method or produce any side-effect.
4919 Either there is a mistake and object was intended to be used or it can be removed.</p>
4920 <p>This analysis rarely produces false-positives. Common false-positive cases include:</p>
4921 <p>- This object used to implicitly throw some obscure exception.</p>
4922 <p>- This object used as a stub to generalize the code.</p>
4923 <p>- This object used to hold strong references to weak/soft-referenced objects.</p>
4924 ]]>
4925 </Details>
4926 </BugPattern>
4927 <BugPattern type="UC_USELESS_OBJECT_STACK">
4928 <ShortDescription>Useless object created on stack</ShortDescription>
4929 <LongDescription>Useless object created in method {1}</LongDescription>
4930 <Details>
4931 <![CDATA[
4932 <p>This object is created just to perform some modifications which don't have any side-effect.
4933 Probably something else was meant or the object can be removed.</p>
4934 ]]>
4935 </Details>
4936 </BugPattern>
4937 <BugPattern type="RANGE_ARRAY_INDEX">
4938 <ShortDescription>Array index is out of bounds</ShortDescription>
4939 <LongDescription>Array index is out of bounds: {3}</LongDescription>
4940 <Details>
4941 <![CDATA[
4942 <p> Array operation is performed, but array index is out of bounds, which will result in ArrayIndexOutOfBoundsException at runtime.</p>
4943 ]]>
4944 </Details>
4945 </BugPattern>
4946 <BugPattern type="RANGE_ARRAY_OFFSET">
4947 <ShortDescription>Array offset is out of bounds</ShortDescription>
4948 <LongDescription>Array offset is out of bounds: {3}</LongDescription>
4949 <Details>
4950 <![CDATA[
4951 <p> Method is called with array parameter and offset parameter, but the offset is out of bounds. This will result in IndexOutOfBoundsException at runtime. </p>
4952 ]]>
4953 </Details>
4954 </BugPattern>
4955 <BugPattern type="RANGE_ARRAY_LENGTH">
4956 <ShortDescription>Array length is out of bounds</ShortDescription>
4957 <LongDescription>Array length is out of bounds: {3}</LongDescription>
4958 <Details>
4959 <![CDATA[
4960 <p> Method is called with array parameter and length parameter, but the length is out of bounds. This will result in IndexOutOfBoundsException at runtime. </p>
4961 ]]>
4962 </Details>
4963 </BugPattern>
4964 <BugPattern type="RANGE_STRING_INDEX">
4965 <ShortDescription>String index is out of bounds</ShortDescription>
4966 <LongDescription>String index is out of bounds when calling {5}: {3}</LongDescription>
4967 <Details>
4968 <![CDATA[
4969 <p> String method is called and specified string index is out of bounds. This will result in StringIndexOutOfBoundsException at runtime. </p>
4970 ]]>
4971 </Details>
4972 </BugPattern>
46194973 <BugPattern type="RV_CHECK_FOR_POSITIVE_INDEXOF">
46204974 <ShortDescription>Method checks to see if result of String.indexOf is positive</ShortDescription>
46214975 <LongDescription>{1} checks to see if result of String.indexOf is positive</LongDescription>
46294983 </Details>
46304984 </BugPattern>
46314985 <BugPattern type="RV_DONT_JUST_NULL_CHECK_READLINE">
4632 <ShortDescription>Method discards result of readLine after checking if it is nonnull</ShortDescription>
4633 <LongDescription>{1} discards result of readLine after checking if it is nonnull</LongDescription>
4986 <ShortDescription>Method discards result of readLine after checking if it is non-null</ShortDescription>
4987 <LongDescription>{1} discards result of readLine after checking if it is non-null</LongDescription>
46344988 <Details>
46354989 <![CDATA[
46364990 <p> The value returned by readLine is discarded after checking to see if the return
46565010 is important or acceptable.
46575011 </p>
46585012 <p>Please investigate this closely to decide whether it is OK to ignore the return value.
5013 </p>
5014 ]]>
5015 </Details>
5016 </BugPattern>
5017
5018 <BugPattern type="RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT">
5019 <ShortDescription>Return value of method without side effect is ignored</ShortDescription>
5020 <LongDescription>Return value of {2.givenClass} ignored, but method has no side effect</LongDescription>
5021 <Details>
5022 <![CDATA[
5023 <p>This code calls a method and ignores the return value. However our analysis shows that
5024 the method (including its implementations in subclasses if any) does not produce any effect
5025 other than return value. Thus this call can be removed.
5026 </p>
5027 <p>We are trying to reduce the false positives as much as possible, but in some cases this warning might be wrong.
5028 Common false-positive cases include:</p>
5029 <p>- The method is designed to be overridden and produce a side effect in other projects which are out of the scope of the analysis.</p>
5030 <p>- The method is called to trigger the class loading which may have a side effect.</p>
5031 <p>- The method is called just to get some exception.</p>
5032 <p>If you feel that our assumption is incorrect, you can use a @CheckReturnValue annotation
5033 to instruct FindBugs that ignoring the return value of this method is acceptable.
46595034 </p>
46605035 ]]>
46615036 </Details>
47645139 </Details>
47655140 </BugPattern>
47665141 <BugPattern type="NP_STORE_INTO_NONNULL_FIELD">
4767 <ShortDescription>Store of null value into field annotated NonNull</ShortDescription>
4768 <LongDescription>Store of null value into field {2.givenClass} annotated NonNull in {1}</LongDescription>
5142 <ShortDescription>Store of null value into field annotated @Nonnull</ShortDescription>
5143 <LongDescription>Store of null value into field {2.givenClass} annotated @Nonnull in {1}</LongDescription>
47695144 <Details>
47705145 <![CDATA[
4771 <p> A value that could be null is stored into a field that has been annotated as NonNull. </p>
5146 <p> A value that could be null is stored into a field that has been annotated as @Nonnull. </p>
47725147 ]]>
47735148 </Details>
47745149 </BugPattern>
47885163 </Details>
47895164 </BugPattern>
47905165 <BugPattern type="NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE">
4791 <ShortDescription>Parameter must be nonnull but is marked as nullable</ShortDescription>
4792 <LongDescription>{2} must be nonnull but is marked as nullable</LongDescription>
4793 <Details>
4794 <![CDATA[
4795 <p> This parameter is always used in a way that requires it to be nonnull,
5166 <ShortDescription>Parameter must be non-null but is marked as nullable</ShortDescription>
5167 <LongDescription>{2} must be non-null but is marked as nullable</LongDescription>
5168 <Details>
5169 <![CDATA[
5170 <p> This parameter is always used in a way that requires it to be non-null,
47965171 but the parameter is explicitly annotated as being Nullable. Either the use
47975172 of the parameter or the annotation is wrong.
47985173 </p>
48585233 </Details>
48595234 </BugPattern>
48605235 <BugPattern type="NP_NULL_PARAM_DEREF_NONVIRTUAL">
4861 <ShortDescription>Non-virtual method call passes null for nonnull parameter</ShortDescription>
4862 <LongDescription>Non-virtual method call in {1} passes null for nonnull parameter of {2.givenClass}</LongDescription>
5236 <ShortDescription>Non-virtual method call passes null for non-null parameter</ShortDescription>
5237 <LongDescription>Non-virtual method call in {1} passes null for non-null parameter of {2.givenClass}</LongDescription>
48635238 <Details>
48645239 <![CDATA[
48655240 <p>
4866 A possibly-null value is passed to a nonnull method parameter.
5241 A possibly-null value is passed to a non-null method parameter.
48675242 Either the parameter is annotated as a parameter that should
4868 always be nonnull, or analysis has shown that it will always be
5243 always be non-null, or analysis has shown that it will always be
48695244 dereferenced.
48705245 </p>
48715246 ]]>
48725247 </Details>
48735248 </BugPattern>
48745249 <BugPattern type="NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS">
4875 <ShortDescription>Method call passes null for nonnull parameter</ShortDescription>
4876 <LongDescription>Null passed for nonnull parameter of {2.givenClass} in {1}</LongDescription>
5250 <ShortDescription>Method call passes null for non-null parameter</ShortDescription>
5251 <LongDescription>Null passed for non-null parameter of {2.givenClass} in {1}</LongDescription>
48775252 <Details>
48785253 <![CDATA[
48795254 <p>
48805255 A possibly-null value is passed at a call site where all known
4881 target methods require the parameter to be nonnull.
5256 target methods require the parameter to be non-null.
48825257 Either the parameter is annotated as a parameter that should
4883 always be nonnull, or analysis has shown that it will always be
5258 always be non-null, or analysis has shown that it will always be
48845259 dereferenced.
48855260 </p>
48865261 ]]>
48875262 </Details>
48885263 </BugPattern>
48895264 <BugPattern type="NP_NULL_PARAM_DEREF">
4890 <ShortDescription>Method call passes null for nonnull parameter</ShortDescription>
4891 <LongDescription>Null passed for nonnull parameter of {2.givenClass} in {1}</LongDescription>
5265 <ShortDescription>Method call passes null for non-null parameter</ShortDescription>
5266 <LongDescription>Null passed for non-null parameter of {2.givenClass} in {1}</LongDescription>
48925267 <Details>
48935268 <![CDATA[
48945269 <p>
4895 This method call passes a null value for a nonnull method parameter.
5270 This method call passes a null value for a non-null method parameter.
48965271 Either the parameter is annotated as a parameter that should
4897 always be nonnull, or analysis has shown that it will always be
5272 always be non-null, or analysis has shown that it will always be
48985273 dereferenced.
48995274 </p>
49005275 ]]>
49015276 </Details>
49025277 </BugPattern>
49035278 <BugPattern type="NP_NONNULL_PARAM_VIOLATION">
4904 <ShortDescription>Method call passes null to a nonnull parameter </ShortDescription>
4905 <LongDescription>Null passed for nonnull parameter of {2.givenClass} in {1}</LongDescription>
5279 <ShortDescription>Method call passes null to a non-null parameter </ShortDescription>
5280 <LongDescription>Null passed for non-null parameter of {2.givenClass} in {1}</LongDescription>
49065281 <Details>
49075282 <![CDATA[
49085283 <p>
49095284 This method passes a null value as the parameter of a method which
4910 must be nonnull. Either this parameter has been explicitly marked
5285 must be non-null. Either this parameter has been explicitly marked
49115286 as @Nonnull, or analysis has determined that this parameter is
49125287 always dereferenced.
49135288 </p>
49155290 </Details>
49165291 </BugPattern>
49175292 <BugPattern type="NP_NONNULL_RETURN_VIOLATION">
4918 <ShortDescription>Method may return null, but is declared @NonNull</ShortDescription>
4919 <LongDescription>{1} may return null, but is declared @NonNull</LongDescription>
5293 <ShortDescription>Method may return null, but is declared @Nonnull</ShortDescription>
5294 <LongDescription>{1} may return null, but is declared @Nonnull</LongDescription>
49205295 <Details>
49215296 <![CDATA[
49225297 <p>
49235298 This method may return a null value, but the method (or a superclass method
4924 which it overrides) is declared to return @NonNull.
5299 which it overrides) is declared to return @Nonnull.
49255300 </p>
49265301 ]]>
49275302 </Details>
49655340 </p>
49665341 <p>Note that a check such as
49675342 <code>if (x == null) throw new NullPointerException();</code>
4968 is treated as a dereference of <code>x</code>.
5343 is treated as a dereference of <code>x</code>.</p>
49695344 ]]>
49705345 </Details>
49715346 </BugPattern>
52555630 <Details>
52565631 <![CDATA[
52575632 <p> This method calls equals(Object) on two references of different
5258 class types with no common subclasses.
5259 Therefore, the objects being compared
5260 are unlikely to be members of the same class at runtime
5261 (unless some application classes were not analyzed, or dynamic class
5262 loading can occur at runtime).
5263 According to the contract of equals(),
5264 objects of different
5265 classes should always compare as unequal; therefore, according to the
5266 contract defined by java.lang.Object.equals(Object),
5267 the result of this comparison will always be false at runtime.
5633 class types and analysis suggests they will be to objects of different classes
5634 at runtime. Further, examination of the equals methods that would be invoked suggest that either
5635 this call will always return false, or else the equals method is not be symmetric (which is
5636 a property required by the contract
5637 for equals in class Object).
52685638 </p>
52695639 ]]>
52705640 </Details>
55905960 </Details>
55915961 </BugPattern>
55925962 <BugPattern type="INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE">
5593 <ShortDescription>Bad comparison of nonnegative value with negative constant</ShortDescription>
5963 <ShortDescription>Bad comparison of nonnegative value with negative constant or zero</ShortDescription>
55945964 <LongDescription>Bad comparison of nonnegative value with {2} in {1}</LongDescription>
55955965 <Details>
55965966 <![CDATA[
5597 <p> This code compares a value that is guaranteed to be non-negative with a negative constant.
5967 <p> This code compares a value that is guaranteed to be non-negative with a negative constant or zero.
55985968 </p>
55995969 ]]>
56005970 </Details>
59956365 ]]>
59966366 </Details>
59976367 </BugPattern>
6368 <BugPattern type="IIL_PREPARE_STATEMENT_IN_LOOP">
6369 <ShortDescription>Method calls prepareStatement in a loop</ShortDescription>
6370 <LongDescription>{1} calls prepareStatement with the constant arguments in a loop</LongDescription>
6371 <Details>
6372 <![CDATA[
6373 <p> The method calls Connection.prepareStatement inside the loop passing the constant arguments.
6374 If the PreparedStatement should be executed several times there's no reason to recreate it for each loop iteration.
6375 Move this call outside of the loop.</p>
6376 ]]>
6377 </Details>
6378 </BugPattern>
6379 <BugPattern type="IIL_ELEMENTS_GET_LENGTH_IN_LOOP">
6380 <ShortDescription>NodeList.getLength() called in a loop</ShortDescription>
6381 <LongDescription>{1} calls NodeList.getLength() in a loop for getElementsByTagName return value</LongDescription>
6382 <Details>
6383 <![CDATA[
6384 <p> The method calls NodeList.getLength() inside the loop and NodeList was produced by getElementsByTagName call.
6385 This NodeList doesn't store its length, but computes it every time in not very optimal way.
6386 Consider storing the length to the variable before the loop.
6387 </p>
6388 ]]>
6389 </Details>
6390 </BugPattern>
6391 <BugPattern type="IIL_PATTERN_COMPILE_IN_LOOP">
6392 <ShortDescription>Method calls Pattern.compile in a loop</ShortDescription>
6393 <LongDescription>{1} calls Pattern.compile with the constant arguments in a loop</LongDescription>
6394 <Details>
6395 <![CDATA[
6396 <p> The method calls Pattern.compile inside the loop passing the constant arguments.
6397 If the Pattern should be used several times there's no reason to compile it for each loop iteration.
6398 Move this call outside of the loop or even into static final field.</p>
6399 ]]>
6400 </Details>
6401 </BugPattern>
6402 <BugPattern type="IIL_PATTERN_COMPILE_IN_LOOP_INDIRECT">
6403 <ShortDescription>Method compiles the regular expression in a loop</ShortDescription>
6404 <LongDescription>{1} compiles the regular expression in a loop</LongDescription>
6405 <Details>
6406 <![CDATA[
6407 <p> The method creates the same regular expression inside the loop, so it will be compiled every iteration.
6408 It would be more optimal to precompile this regular expression using Pattern.compile outside of the loop.</p>
6409 ]]>
6410 </Details>
6411 </BugPattern>
6412 <BugPattern type="IIO_INEFFICIENT_INDEX_OF">
6413 <ShortDescription>Inefficient use of String.indexOf(String)</ShortDescription>
6414 <LongDescription>{1} uses String.indexOf(String) instead of String.indexOf(int)</LongDescription>
6415 <Details>
6416 <![CDATA[
6417 <p> This code passes a constant string of length 1 to String.indexOf().
6418 It is more efficient to use the integer implementations of String.indexOf().
6419 f. e. call <code>myString.indexOf('.')</code> instead of <code>myString.indexOf(".")</code></p>
6420 ]]>
6421 </Details>
6422 </BugPattern>
6423 <BugPattern type="IIO_INEFFICIENT_LAST_INDEX_OF">
6424 <ShortDescription>Inefficient use of String.lastIndexOf(String)</ShortDescription>
6425 <LongDescription>{1} uses String.lastIndexOf(String) instead of String.lastIndexOf(int)</LongDescription>
6426 <Details>
6427 <![CDATA[
6428 <p> This code passes a constant string of length 1 to String.lastIndexOf().
6429 It is more efficient to use the integer implementations of String.lastIndexOf().
6430 f. e. call <code>myString.lastIndexOf('.')</code> instead of <code>myString.lastIndexOf(".")</code></p>
6431 ]]>
6432 </Details>
6433 </BugPattern>
59986434 <BugPattern type="ITA_INEFFICIENT_TO_ARRAY">
59996435 <ShortDescription>Method uses toArray() with zero-length array argument</ShortDescription>
60006436 <LongDescription>{1} uses Collection.toArray() with zero-length array argument</LongDescription>
62776713 <LongDescription>Useless increment in return from {1}</LongDescription>
62786714 <Details>
62796715 <![CDATA[
6280 <p>This statement has a return such as <code>return x++;</code>.
6716 <p>This statement has a return such as <code>return x++;</code>.
62816717 A postfix increment/decrement does not impact the value of the expression,
6282 so this increment/decrement has no effect.
6718 so this increment/decrement has no effect.
62836719 Please verify that this statement does the right thing.
62846720 </p>
62856721 ]]>
66217057 passes that value for a method parameter that requires an absolute time value.
66227058 An absolute time value is the number
66237059 of milliseconds since the standard base time known as "the epoch", namely January 1, 1970, 00:00:00 GMT.
6624 For example, the following method, intended to convert seconds since the epoc into a Date, is badly
7060 For example, the following method, intended to convert seconds since the epoch into a Date, is badly
66257061 broken:</p>
66267062 <pre>
66277063 Date getDate(int seconds) { return new Date(seconds * 1000); }
67847220 <![CDATA[
67857221 <p>
67867222 An argument not of type Boolean is being formatted with a %b format specifier. This won't throw an
6787 exception; instead, it will print true for any nonnull value, and false for null.
7223 exception; instead, it will print true for any non-null value, and false for null.
67887224 This feature of format strings is strange, and may not be what you intended.
67897225 </p>
67907226 ]]>
70467482 </BugPattern>
70477483 <BugPattern type="BC_VACUOUS_INSTANCEOF">
70487484 <ShortDescription>instanceof will always return true</ShortDescription>
7049 <LongDescription>instanceof will always return true for all nonnull values in {1}, since all {2} are instances of {3}</LongDescription>
7485 <LongDescription>instanceof will always return true for all non-null values in {1}, since all {2} are instances of {3}</LongDescription>
70507486 <Details>
70517487 <![CDATA[
70527488 <p>
71727608 cast to a short or byte, which discards the upper bits of the result.
71737609 Since the upper bits are discarded, there may be no difference between
71747610 a signed and unsigned right shift (depending upon the size of the shift).
7611 </p>
7612 ]]>
7613 </Details>
7614 </BugPattern>
7615 <BugPattern type="BSHIFT_WRONG_ADD_PRIORITY">
7616 <ShortDescription>Possible bad parsing of shift operation</ShortDescription>
7617 <LongDescription>Possible bad parsing of shift operation in {1}</LongDescription>
7618 <Details>
7619 <![CDATA[
7620 <p>
7621 The code performs an operation like (x &lt;&lt; 8 + y). Although this might be correct, probably it was meant
7622 to perform (x &lt;&lt; 8) + y, but shift operation has
7623 a lower precedence, so it's actually parsed as x &lt;&lt; (8 + y).
71757624 </p>
71767625 ]]>
71777626 </Details>
80678516 <Details>
80688517 <![CDATA[<p>
80698518 A method should always implement the contract of a method it overrides. Thus, if a method takes a parameter
8070 that is marked as @Nullable, you shouldn't override that method in a subclass with a method where that parameter is @Nonnull.
8071 Doing so violates the contract that the method should handle a null parameter.
8519 that is marked as @Nullable, you shouldn't override that method in a subclass with a method where that parameter is @Nonnull.
8520 Doing so violates the contract that the method should handle a null parameter.
80728521 </p>]]>
80738522 </Details>
80748523 </BugPattern>
80788527 <Details>
80798528 <![CDATA[<p>
80808529 A method should always implement the contract of a method it overrides. Thus, if a method takes a parameter
8081 that is marked as @Nullable, you shouldn't override that method in a subclass with a method where that parameter is @Nonnull.
8082 Doing so violates the contract that the method should handle a null parameter.
8530 that is marked as @Nullable, you shouldn't override that method in a subclass with a method where that parameter is @Nonnull.
8531 Doing so violates the contract that the method should handle a null parameter.
80838532 </p>]]>
80848533 </Details>
80858534 </BugPattern>
80908539 <Details>
80918540 <![CDATA[<p>
80928541 A method should always implement the contract of a method it overrides. Thus, if a method takes is annotated
8093 as returning a @Nonnull value,
8094 you shouldn't override that method in a subclass with a method annotated as returning a @Nullable or @CheckForNull value.
8095 Doing so violates the contract that the method shouldn't return null.
8542 as returning a @Nonnull value,
8543 you shouldn't override that method in a subclass with a method annotated as returning a @Nullable or @CheckForNull value.
8544 Doing so violates the contract that the method shouldn't return null.
80968545 </p>]]>
80978546 </Details>
80988547 </BugPattern>
81108559 <BugCode abbrev="TEST">Testing prototype and incomplete bug pattern</BugCode>
81118560 <BugCode abbrev="IMSE">Dubious catching of IllegalMonitorStateException</BugCode>
81128561 <BugCode abbrev="CN">Bad implementation of cloneable idiom</BugCode>
8562 <BugCode abbrev="CAA">Covariant array assignment</BugCode>
81138563 <BugCode abbrev="AT">Possible atomicity violation</BugCode>
81148564 <BugCode abbrev="FI">Incorrect use of finalizers</BugCode>
81158565 <BugCode abbrev="ES">Checking String equality using == or !=</BugCode>
81318581 <BugCode abbrev="NN">Naked notify</BugCode>
81328582 <BugCode abbrev="UW">Unconditional wait</BugCode>
81338583 <BugCode abbrev="SP">Method spins on field</BugCode>
8134 <BugCode abbrev="DC">Possible double check of field</BugCode>
8584 <BugCode abbrev="DC">Double check pattern</BugCode>
81358585 <BugCode abbrev="Wa">Wait not in loop</BugCode>
81368586 <BugCode abbrev="No">Using notify() rather than notifyAll()</BugCode>
81378587 <BugCode abbrev="DE">Dropped or ignored exception</BugCode>
81438593 <BugCode abbrev="RS">Class's readObject() method is synchronized</BugCode>
81448594 <BugCode abbrev="SC">Constructor invokes Thread.start()</BugCode>
81458595 <BugCode abbrev="MS">Mutable static field</BugCode>
8596 <BugCode abbrev="ME">Mutable enum field</BugCode>
81468597 <BugCode abbrev="EI">Method returning array may expose internal representation</BugCode>
81478598 <BugCode abbrev="Nm">Confusing method name</BugCode>
81488599 <BugCode abbrev="SS">Unread field should be static</BugCode>
81518602 <BugCode abbrev="UwF">Unwritten field</BugCode>
81528603 <BugCode abbrev="SIC">Inner class could be made static</BugCode>
81538604 <BugCode abbrev="TLW">Wait with two locks held</BugCode>
8605 <BugCode abbrev="RANGE">Range checks</BugCode>
81548606 <BugCode abbrev="RV">Bad use of return value from method</BugCode>
81558607 <BugCode abbrev="LG">Logger problem</BugCode>
81568608 <BugCode abbrev="IA">Ambiguous invocation</BugCode>
81808632 <BugCode abbrev="NS">Suspicious use of non-short-circuit boolean operator</BugCode>
81818633 <BugCode abbrev="ODR">Database resource not closed on all paths</BugCode>
81828634 <BugCode abbrev="SBSC">String concatenation in loop using + operator</BugCode>
8635 <BugCode abbrev="IIL">Inefficient code which can be moved outside of the loop</BugCode>
8636 <BugCode abbrev="IIO">Inefficient use of String.indexOf(String) or String.lastIndexOf(String)</BugCode>
81838637 <BugCode abbrev="ITA">Inefficient use of collection.toArray(new Foo[0])</BugCode>
81848638 <BugCode abbrev="SW">Swing coding rules</BugCode>
81858639 <BugCode abbrev="IJU">Improperly implemented JUnit TestCase</BugCode>
81978651 <BugCode abbrev="REC">RuntimeException capture</BugCode>
81988652 <BugCode abbrev="FE">Test for floating point equality</BugCode>
81998653 <BugCode abbrev="UM">Unnecessary Math on constants</BugCode>
8654 <BugCode abbrev="UC">Useless code</BugCode>
8655 <BugCode abbrev="CNT">Rough value of known constant</BugCode>
82008656 <BugCode abbrev="CD">Circular Dependencies</BugCode>
82018657 <BugCode abbrev="RI">Redundant Interfaces</BugCode>
82028658 <BugCode abbrev="MTIA">Multithreaded Instance Access</BugCode>
99 <p>Ce plugin contient tous les détecteurs standards de FindBugs.</p>
1010 ]]>
1111 </Details>
12 <BugsUrl>http://findbugs.sourceforge.net/bugDescriptions_fr.html</BugsUrl>
13 <AllBugsUrl>http://findbugs.sourceforge.net/bugDescriptions_fr.html</AllBugsUrl>
1214 </Plugin>
1315
1416
00 <?xml version="1.0" encoding="UTF-8"?>
1
21 <MessageCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="messagecollection.xsd">
32
43 <!--
1514 </p>
1615 ]]>
1716 </Details>
17 <BugsUrl>http://findbugs.sourceforge.net/bugDescriptions_ja.html</BugsUrl>
18 <AllBugsUrl>http://findbugs.sourceforge.net/bugDescriptions_ja.html</AllBugsUrl>
1819 </Plugin>
1920
2021 <FindBugsMain cmd="addMessages" class="edu.umd.cs.findbugs.AddMessages">
6061 <!-- これを変えたら FindbugsPlugin のデフォルト ID も更新して下さい -->
6162 <Cloud id="edu.umd.cs.findbugs.cloud.doNothingCloud">
6263 <Description>(クラウド無効)</Description>
63 <Details>このプラグインを使用すると、バグレビューは無効になります。</Details>
64 <Details>このプラグインを使うとバグレビューは無効になります。</Details>
6465 </Cloud>
6566
66 <PluginComponent
67 id="edu.umd.cs.findbugs.bugReporter.SuppressMultithreaded">
67 <PluginComponent id="edu.umd.cs.findbugs.bugReporter.SuppressMultithreaded">
6868 <Description>マルチスレッドの正確性問題を抑止する</Description>
6969 <Details>すべてのマルチスレッドの正確性問題を抑止します</Details>
70 </PluginComponent>
71
72 <PluginComponent
73 id="edu.umd.cs.findbugs.bugReporter.SuppressI18N">
70 </PluginComponent>
71
72 <PluginComponent id="edu.umd.cs.findbugs.bugReporter.SuppressI18N">
7473 <Description>国際化問題を抑止する</Description>
7574 <Details>すべての国際化問題を抑止します</Details>
76 </PluginComponent>
77
78 <PluginComponent
79 id="edu.umd.cs.findbugs.bugReporter.SelectivelySuppressI18N">
75 </PluginComponent>
76
77 <PluginComponent id="edu.umd.cs.findbugs.bugReporter.SelectivelySuppressI18N">
8078 <Description>選択したパッケージ以外のすべての国際化問題を抑止する</Description>
8179 <Details>i18n.properties リソースで指定した以外のすべての国際化問題を抑止します</Details>
8280 </PluginComponent>
8381
84 <PluginComponent
85 id="edu.umd.cs.findbugs.bugReporter.MaxRank14">
82 <PluginComponent id="edu.umd.cs.findbugs.bugReporter.MaxRank14">
8683 <Description>ランク14を越えるすべての問題を抑止する</Description>
8784 <Details>ランク14を越えるすべての問題を抑止します</Details>
8885 </PluginComponent>
8986
90 <PluginComponent
91 id="edu.umd.cs.findbugs.bugReporter.SuppressMalicious">
92 <Description>悪質なコード脆弱性について警告を抑止する</Description>
93 <Details>悪質なコード脆弱性について警告を抑止します</Details>
94 </PluginComponent>
95
87 <PluginComponent id="edu.umd.cs.findbugs.bugReporter.SuppressMalicious">
88 <Description>悪意のあるコード脆弱性について警告を抑止する</Description>
89 <Details>悪意のあるコード脆弱性について警告を抑止します</Details>
90 </PluginComponent>
91
9692 <!--
9793 **********************************************************************
9894 Categories (replacing the BugCategoryDescriptions.properties file)
10399 <Description>正確性</Description>
104100 <Abbreviation>C</Abbreviation>
105101 <Details>バグの可能性 - おそらく、開発者が意図していなかったコードになっている明らかなコーディング間違いです。
106 我々は、低い誤検出率のために努力します。
102 我々は低い誤検出率のために努力します。
107103 </Details>
108104 </BugCategory>
109105
125121 <Description>バッドプラクティス</Description>
126122 <Abbreviation>B</Abbreviation>
127123 <Details>推奨または必須のコーディングプラクティスの違反です。たとえば、hashCode と equals の問題、cloneable イディオム、捨てられた例外、Serializable の問題、finalize の誤用などです。
128 いくつかのグループは、バッドプラクティスを気にしないかもしれないが、我々は正確な解析をしようと努力します。
124 いくつかのグループはバッドプラクティスを気にしないかもしれないが、我々は正確な解析をしようと努力します。
129125 </Details>
130126 </BugCategory>
131127
146142 </BugCategory>
147143
148144 <BugCategory category="MALICIOUS_CODE">
149 <Description>悪質なコード脆弱性</Description>
145 <Description>悪意のあるコード脆弱性</Description>
150146 <Abbreviation>V</Abbreviation>
151147 <Details>信頼できないコードからの攻撃に脆弱であるコード</Details>
152148 </BugCategory>
177173 Detectors
178174 **********************************************************************
179175 -->
176 <Detector class="edu.umd.cs.findbugs.detect.FindRoughConstants">
177 <Details>
178 <![CDATA[
179 <p>
180 このディテクタは、ほぼ (しかし、正確ではなく) 同じである Math.PI のような既知の定数を見つけます。
181 </p>
182 ]]>
183 </Details>
184 </Detector>
180185 <Detector class="edu.umd.cs.findbugs.detect.InitializeNonnullFieldsInConstructor">
181186 <Details>
182187 <![CDATA[
183188 <p> Finds nonnull fields that are not written to in constructors.
184 このディテクタは、コンストラクタで書き込まれない非 null フィールドを発券します。
189 このディテクタは、コンストラクタで書き込まれない非 null フィールドを見つけます。
185190 </p>
186191 ]]>
187192 </Details>
190195 <Details>
191196 <![CDATA[
192197 <p>
193 このディテクタは、エポックからミリ秒を記述する32ビット値の使い方を発見します。
198 このディテクタは、エポックからミリ秒を記述する32ビット値の使い方を見つけます。
194199 </p>
195200 ]]>
196201 </Details>
210215 <Details>
211216 <![CDATA[
212217 <p>
213 このディテクタは、型修飾子が直接適用されるメソッドパラメータとそれらのメソッドパラメータの使い方との間で矛盾を発見します。
218 このディテクタは、型修飾子が直接適用されるメソッドパラメータとそれらのメソッドパラメータの使い方との間で矛盾を見つけます。
214219 </p>
215220 ]]>
216221 </Details>
220225 <Details>
221226 <![CDATA[
222227 <p>
223 このディテクタは、原子的に実行されない並行抽象化に関して、演算(たとえば、get/put)のシーケンスを発見します。
228 このディテクタは、原子的に実行されない並行抽象化に関して、演算(たとえば、get/put)のシーケンスを見つけます。
224229 </p>
225230 ]]>
226231 </Details>
335340 <Details>
336341 <![CDATA[
337342 <p>
338 このディテクタは、他のディテクタが使用するために、解析されたクラスで定義されているすべてのメソッドのデータベースを構築します。
343 このディテクタは、他のディテクタが使用するための解析されたクラスで定義されているすべてのメソッドのデータベースを構築します。
339344 </p>
340345 ]]>
341346 </Details>
353358 <Details>
354359 <![CDATA[
355360 <p>
356 このディテクタは、他のディテクタが使用するために、解析されたクラスで呼び出されるすべてのメソッドのデータベースを構築します。
361 このディテクタは、他のディテクタが使用するための解析されたクラスで呼び出されるすべてのメソッドのデータベースを構築します。
362 </p>
363 ]]>
364 </Details>
365 </Detector>
366 <Detector class="edu.umd.cs.findbugs.detect.FindNoSideEffectMethods">
367 <Details>
368 <![CDATA[
369 <p>
370 値を返すだけの副作用を持たないメソッドを探します。
371 </p>
372 ]]>
373 </Details>
374 </Detector>
375 <Detector class="edu.umd.cs.findbugs.detect.BuildStringPassthruGraph">
376 <Details>
377 <![CDATA[
378 <p>
379 このディテクタは、メソッドから変化していないメソッドに渡された文字列パラメータのデータベースを構築します。
357380 </p>
358381 ]]>
359382 </Details>
363386 <![CDATA[
364387 <p>
365388 このディテクタは、クラスの新しいインスタンスを返すメソッドがある不変クラスを探します。
366 それらのメソッドが呼び出されるところでインスタンスを変化させると事故が起きるかもしれません。
389 メソッドが呼び出されるとインスタンスが変化させられると思います。
367390 </p>
368391 ]]>
369392 </Details>
409432 <Details>
410433 <![CDATA[
411434 <p>
412 このディテクタは、メソッド、フィールド、パラメータで @NonNull アノテーションを探します。
413 null かもしれない値が null でない値だけが使われるべき文脈で使われたときに FindNullDeref ディテクタが警告を生成するために使われます。
435 このディテクタは、メソッド、フィールド、パラメータで @Nonnull アノテーションを探します。
436 null かもしれない値が null でない値だけが使われるべき文脈で使われたとき警告を生成するために FindNullDeref ディテクタはこれらを使用できます。
414437 </p>
415438 ]]>
416439 </Details>
420443 <Details>
421444 <![CDATA[
422445 <p>
423 このディテクタは、無条件でパラメータの null 値が利用されるのか判定するためにアプリケーションのすべてのメソッドを解析します。
446 このディテクタは、無条件にパラメータの参照外しが行われるのかを決定するためにアプリケーションのすべてのメソッドを解析します。
424447 この情報は、null 値がメソッドに渡されるかもしれない呼び出し場所を発見するために後の解析パスで使われます。
425448 </p>
426449 <p>
434457 <Details>
435458 <![CDATA[
436459 <p>
437 このディテクタは、どのメソッドが常に非 null 値を返すのか判定するためにアプリケーションで、すべてのメソッドを解析します。
460 このディテクタは、どのメソッドが常に非 null 値を返すのかを決定するためにアプリケーションのすべてのメソッドを解析します。
438461 </p>
439462 ]]>
440463 </Details>
450473 ]]>
451474 </Details>
452475 </Detector>
453
476 <Detector class="edu.umd.cs.findbugs.detect.OptionalReturnNull">
477 <Details>
478 <![CDATA[
479 <p>
480 このディテクタは、Optional の戻り型が明示的に null 値を返すメソッドを探します。
481 </p>
482
483 ]]>
484 </Details>
485 </Detector>
486 <Detector class="edu.umd.cs.findbugs.detect.FindUselessObjects">
487 <Details>
488 <![CDATA[
489 <p>
490 このディテクタは役に立たないオブジェクトを探します。
491 </p>
492 ]]>
493 </Details>
494 </Detector>
495 <Detector class="edu.umd.cs.findbugs.detect.MutableEnum">
496 <Details>
497 <![CDATA[
498 <p>
499 このディテクタは、可変列挙型フィールドを探して警告します。
500 </p>
501 ]]>
502 </Details>
503 </Detector>
454504 <Detector class="edu.umd.cs.findbugs.detect.BadUseOfReturnValue">
455505 <Details>
456506 <![CDATA[
457507 <p>
458 このディテクタは、メソッドの戻り値が null でないのかチェックされた後に捨てられるケースを探します。
508 このディテクタは、メソッドの戻り値を null でないのかチェックした後で捨てるケースを探します。
459509 </p>
460510
461511 ]]>
493543 <Details>
494544 <![CDATA[
495545 <p>
496 このディテクタは、volatile フィールドの使い方でバグパターンを探します。
546 このディテクタは、volatile フィールドの使い方のバグパターンを探します。
497547 </p>
498548 ]]>
499549 </Details>
537587 <![CDATA[
538588 <p>
539589 <code>java.net.URL</code> の equals と hashCode メソッドはドメイン名の解決をします。
540 その結果、これらの演算は非常に高くつく可能性があります。このディテクタは、メソッドが呼び出されるかもしれない場所を探します。
590 その結果、これらの演算はかなり高くつきます。このディテクタは、メソッドが呼び出されるかもしれない場所を探します。
541591 </p>
542592 ]]>
543593 </Details>
574624 </Details>
575625 </Detector>
576626
627 <Detector class="edu.umd.cs.findbugs.detect.RedundantConditions">
628 <Details>
629 <![CDATA[
630 <p>
631 このディテクタは、この式の2番目の条件のような役に立たない条件を含んでいるコードを探します (x >= 10 && x >= 5).
632 </p>
633 ]]>
634 </Details>
635 </Detector>
636
577637 <Detector class="edu.umd.cs.findbugs.detect.CallToUnsupportedMethod">
578638 <Details>
579639 <![CDATA[
607667 <Details>
608668 <![CDATA[
609669 <p>
610 このディテクタは、新しいディテクタをテストするためのフックです。一般に、このディテクタは何もしません。
670 このディテクタは、新しいディテクタをテストするためのフックです。通常このディテクタは何もしません。
611671 </p>
612672 ]]>
613673 </Details>
617677 <Details>
618678 <![CDATA[
619679 <p>
620 このディテクタは、新しいディテクタをテストするためのフックです。一般に、このディテクタは何もしません。
680 このディテクタは、新しいディテクタをテストするためのフックです。通常このディテクタは何もしません。
621681 </p>
622682 ]]>
623683 </Details>
637697 <Details>
638698 <![CDATA[
639699 <p>
640 このディテクタは、新しいディテクタをテストするためのフックです。一般に、このディテクタは何もしません。
700 このディテクタは、新しいディテクタをテストするためのフックです。通常このディテクタは何もしません。
641701 </p>
642702 ]]>
643703 </Details>
651711 これらの警告はソフトウェアで実際のバグを発見するのではなく、データマイニング実験のコントロールとして役に立つことを目的とした偽のランダムノイズです。
652712 </p>
653713 <p>
654 このディテクタは、新しいディテクタをテストするためのフックです。一般に、このディテクタは何もしません。
714 このディテクタは、新しいディテクタをテストするためのフックです。通常このディテクタは何もしません。
655715 </p>
656716 ]]>
657717 </Details>
822882 <![CDATA[
823883 <p>
824884 このディテクタは、引数のない String コンストラクタのような無意味なメソッドの呼び出しを探します。
885 </p>
886 ]]>
887 </Details>
888 </Detector>
889
890 <Detector class="edu.umd.cs.findbugs.detect.CovariantArrayAssignment">
891 <Details>
892 <![CDATA[
893 <p>
894 このディテクタは、実行時に ArrayStoreException を引き起こすかもしれない Object[] array = new String[10] のような共変配列代入を探します。
825895 </p>
826896 ]]>
827897 </Details>
10071077 <Details>
10081078 <![CDATA[
10091079 <p>
1010 このディテクタは、変更されたフィールドから読み込まれるオブジェクトで同期化を探します。
1080 このディテクタは、変更されたフィールドから読み込まれるオブジェクトの同期化を探します。
10111081 </p>
10121082 ]]>
10131083 </Details>
10271097 <Details>
10281098 <![CDATA[
10291099 <p>
1030 このディテクタは、悪質なコードによって変更されるかもしれない static フィールドを探します。
1100 このディテクタは、悪意のあるコードによって変更されるかもしれない static フィールドを探します。
10311101 </p>
10321102 ]]>
10331103 </Details>
10571127 <Details>
10581128 <![CDATA[
10591129 <p>
1060 このディテクタは、Serializable クラスの実装で潜在的な問題を探します。
1130 このディテクタは、Serializable クラスの実装の潜在的な問題を探します。
10611131 </p>
10621132 ]]>
10631133 </Details>
10821152 ]]>
10831153 </Details>
10841154 </Detector>
1085 <Detector class="edu.umd.cs.findbugs.detect.ExplicitSerialization" >
1155 <Detector class="edu.umd.cs.findbugs.detect.ExplicitSerialization">
10861156 <Details>
10871157 <![CDATA[
10881158 <p>
11201190 <p>
11211191 低速ディテクタです。
11221192 </p>
1193 ]]>
1194 </Details>
1195 </Detector>
1196
1197 <Detector class="edu.umd.cs.findbugs.detect.FindComparatorProblems">
1198 <Details>
1199 <![CDATA[
1200 <p>
1201 このディテクタは、Comparator.compare または Comparable.compareTo の実装における問題を探します。
1202 </p>
11231203 ]]>
11241204 </Details>
11251205 </Detector>
11921272 中速ディテクタです。
11931273 </p>
11941274 <p>
1195 このディテクタを使用するために、補助クラスパスに java.util.concurrent パッケージ (またはパッケージ自体を解析している) が必要であることに注意してください。
1275 補助クラスパスに java.util.concurrent パッケージ (またはパッケージ自体を解析している) が必要であることに注意してこのディテクタを使用してください
11961276 </p>
11971277 ]]>
11981278 </Details>
12031283 <![CDATA[
12041284 <p>
12051285 このディテクタは、2つの参照値を == や != 演算子で比較している場所を探します。
1206 <code>java.lang.String</code> のような型のクラスで参照値を比較することは、一般に誤りです。
1286 <code>java.lang.String</code> のような型のクラスの参照値を比較することは一般的に誤りです。
12071287 </p>
12081288 </p>
12091289 低速ディテクタです。
12871367 <Details>
12881368 <![CDATA[
12891369 <p>
1290 このディテクタは、JSR-166のロックで実行される一般にの同期化を探します。
1370 このディテクタは、JSR-166のロックで実行される通常の同期化を探します。
12911371 </p>
12921372 <p>
12931373 中速ディテクタです。
13151395 ]]>
13161396 </Details>
13171397 </Detector>
1318
1398 <Detector class="edu.umd.cs.findbugs.detect.InefficientInitializationInsideLoop">
1399 <Details>
1400 <![CDATA[
1401 <p>
1402 このディテクタは、パフォーマンスを向上させるために外側に移せるループ内で初期化しているオブジェクトを探します。
1403 </p>
1404 ]]>
1405 </Details>
1406 </Detector>
1407 <Detector class="edu.umd.cs.findbugs.detect.InefficientIndexOf">
1408 <Details>
1409 <![CDATA[
1410 <p>
1411 このディテクタは、String.indexOf(String) または String.lastIndexOf(String) を使用して定数に長さ1の文字列を渡すコードを探します。
1412 より効率的な整数実装を使用することを推奨します。
1413 高速ディテクタです。
1414 </p>
1415 ]]>
1416 </Details>
1417 </Detector>
13191418 <Detector class="edu.umd.cs.findbugs.detect.InefficientToArray">
13201419 <Details>
13211420 <![CDATA[
13601459 <Details>
13611460 <![CDATA[
13621461 <p>
1363 このディテクタは、判定が静的に行われる可能性がある instanceof 演算子を使用している型チェックを探します。
1462 このディテクタは、静的に判定される instanceof 演算子を使用している型チェックを探します。
13641463 </p>
13651464 ]]>
13661465 </Details>
15721671 ]]>
15731672 </Details>
15741673 </Detector>
1575
1674
15761675 <Detector class="edu.umd.cs.findbugs.detect.FindUseOfNonSerializableValue">
15771676 <Details>
15781677 <![CDATA[
15821681 ]]>
15831682 </Details>
15841683 </Detector>
1585
1684
15861685 <Detector class="edu.umd.cs.findbugs.detect.FindNonSerializableValuePassedToWriteObject">
15871686 <Details>
15881687 <![CDATA[
16641763 <![CDATA[
16651764 <p>
16661765 このディテクタは、スーパークラスで定義されたメソッドを実装して、スーパークラスのメソッドにパラメータをそのまま渡しているだけのサブクラスを探します。
1667 これらのメソッドは削除できます。
1766 これらのメソッドは除去できます。
16681767 </p>
16691768 ]]>
16701769 </Details>
16761775 <p>
16771776 このディテクタは、protected メンバを宣言する final クラスを探します。
16781777 このクラスは派生させることができないので、protected メンバの使用は正しくありません。
1679 アクセス権は、メンバの正しい意図を表すために、public か private に変更するべきです。
1778 アクセス権は、メンバの正しい意図を表すために public か private に変更するべきです。
16801779 </p>
16811780 <p>
16821781 おそらく、新しいパラダイムにクラスのすべてを完全に変更することではなく、クラスの使用中の変化が原因となりました。
16991798 <![CDATA[
17001799 <p>
17011800 このディテクタは、どのメソッドが null を返すのかを決定してファイルに保存します。
1702 結果ファイルは、null 間接参照ディテクタの精度を向上させるために後続のパスで使われるかもしれません。
1801 結果ファイルは、null 間接参照ディテクタの精度を向上させるために後続のパスで使われることがあります。
17031802 これはトレーニングパスなので警告は報告されません。
17041803 </p>
17051804 <p>
17131812 <Details>
17141813 <![CDATA[
17151814 <p>
1716 このディテクタは、どのメソッドが null 値のパラメータを無条件で利用するのかを決定してファイルに保存します。
1717 結果ファイルは、 null 間接参照ディテクタの精度を向上させるために後続のパスで使われるかもしれません。
1815 このディテクタは、どのメソッドが無条件に null の参照外しを行うのかを決定してファイルに保存します。
1816 結果ファイルは、 null 間接参照ディテクタの精度を向上させるために後続のパスで使われることがあります。
17181817 これはトレーニングパスなので警告は報告されません。
17191818 </p>
17201819 <p>
17291828 <![CDATA[
17301829 <p>
17311830 このディテクタは、フィールドに格納される型を解析してデータベースに保存します。
1732 データベースは、型解析をより正確にするために後続のパスで使われるかもしれません。
1831 データベースは、型解析をより正確にするために後続のパスで使われることがあります。
17331832 </p>
17341833 <p>
17351834 低速ディテクタです。
17421841 <Details>
17431842 <![CDATA[
17441843 <p>
1745 このディテクタは、@NonNull と @PossiblyNull アノテーションを集めて、データベースに保存します。
1844 このディテクタは、@Nonnull と @PossiblyNull アノテーションを集めて、データベースに保存します。
17461845 </p>
17471846 <p>
17481847 高速ディテクタです。
17871886 <Details>
17881887 <![CDATA[
17891888 <p>
1790 このディテクタは、引数の型がコレクションのパラメータに関連しているかどうか確かめるために、<code>java.lang.Object</code> を受け取る総称型コレクションメソッドへの呼び出しの引数を見ます。
1889 このディテクタは、引数の型がコレクションのパラメータに関連しているかどうか確かめるために <code>java.lang.Object</code> を受け取る総称型コレクションメソッドへの呼び出しの引数を見ます。
17911890 無関係なクラス型による引数は決してコレクションの中に格納されることはありません。
17921891 たとえば、<code>foo</code> が <code>List&lt;String&gt;</code> で、<code>bar</code> が <code>StringBuffer</code> なら <code>foo.contains(bar)</code> の呼び出しは常に false を返すことになります。
17931892 </p>
18751974 <![CDATA[
18761975 <p>
18771976 このディテクタは、ユーザのデフォルトプラットホームエンコーディングを使用して、バイトから String (またはString からバイト) に変換するメソッドの呼び出しをチェックします。
1878 これは、アプリケーションの振る舞いがプラットホーム間で異なる原因となります。
1977 これはアプリケーションの振る舞いがプラットホーム間で異なる原因となります。
18791978 </p>
18801979 ]]>
18811980 </Details>
18851984 <Details>
18861985 <![CDATA[
18871986 <p>
1888 このディテクタは、オーバライドメソッドが 戻り値またはパラメータの @CheckForNull (@Nonnullで作られた) で @Nonnull (@CheckForNull で作られた) を緩和していないことをチェックします。
1987 このディテクタは、オーバライドメソッドが 戻り値またはパラメータの @CheckForNull (@Nonnull で作られた) で @Nonnull (@CheckForNull で作られた) を緩和していないことをチェックします。
18891988 </p>
18901989 ]]>
18911990 </Details>
18971996 **********************************************************************
18981997 -->
18991998
1999 <BugPattern type="CNT_ROUGH_CONSTANT_VALUE">
2000 <ShortDescription>既知の定数の雑な値を見つける</ShortDescription>
2001 <LongDescription>{3} の雑な値を見つけました: {2}</LongDescription>
2002 <Details>
2003 <![CDATA[
2004 <p>
2005 コードの明確さともっと良い正確さのために定義済みライブラリ定数を使用することを推奨します。
2006 </p>
2007 ]]>
2008 </Details>
2009 </BugPattern>
2010
19002011 <BugPattern type="SKIPPED_CLASS_TOO_BIG">
19012012 <ShortDescription>解析するにはあまりにも大きいクラス</ShortDescription>
19022013 <LongDescription>{0} は、解析するにはあまりにも大きいです。</LongDescription>
19032014 <Details>
19042015 <![CDATA[
19052016 <p>
1906 このクラスは有効に処理することができないほど大きいです。また、エラーのために完全に解析されませんでした。
2017 このクラスは効率的に処理できないほど大きいです。また、エラーのために完全に解析されませんでした。
19072018 </p>
19082019 ]]>
19092020 </Details>
19642075 <![CDATA[
19652076 <p>
19662077 このコードは、10進数の数にうまく変換されない double 値から BigDecimal を作成しています。
1967 たとえば、Java で <code>new BigDecimal(0.1)</code> と書くと、0.1と正確に等しい BigDecimal (スケールが1でスケールなしの値が1) が作成されると思うかもしれませんが
2078 たとえば、Java で <code>new BigDecimal(0.1)</code> と書くと、0.1と正確に等しい BigDecimal (スケールが1でスケールなしの値が1) が作成されると思うかもしれませんが、
19682079 実際には0.1000000000000000055511151231257827021181583404541015625と等しくなります。
19692080 </p>
19702081 <p>
19832094 この部分的なメソッド呼び出しは、検査から明らかな理由で意味がありません。
19842095 </p>
19852096 ]]>
1986 </Details>
2097 </Details>
19872098 </BugPattern>
19882099
19892100 <BugPattern type="DMI_VACUOUS_CALL_TO_EASYMOCK_METHOD">
20182129 <Details>
20192130 <![CDATA[
20202131 <p>
2021 <code>ScheduledThreadPoolExecutor</code> は <code>ThreadPoolExecutor</code> から継承されますが継承されたチューニングメソッドの一部は有効ではありません。
2022 特に、corePoolSize スレッドとアンバウンド形式のキューを使用する固定サイズプールとして動作するので、maximumPoolSize の調整は有効な効果がありません。<br>
2132 <code>ScheduledThreadPoolExecutor</code> は <code>ThreadPoolExecutor</code> から継承されますが継承されたチューニングメソッドの一部は有用ではありません。
2133 特に、corePoolSize スレッドとアンバウンド形式のキューを使用する固定サイズプールとして動作するので、maximumPoolSize の調整は有用な効果がありません。<br>
20232134 (<a href="http://java.sun.com/javase/ja/6/docs/ja/api/java/util/concurrent/ScheduledThreadPoolExecutor.html">Javadoc</a>)
20242135 </p>
20252136 ]]>
21962307 <![CDATA[
21972308 <p>
21982309 (<a href="http://web.archive.org/web/20090526170426/http://java.sun.com/developer/JDCTechTips/2003/tt1208.html">From JDC Tech Tip</a>)<br>
2199 に解説されているとおり、Swing のメソッド、<code>show</code> メソッド、<code>setVisible</code> メソッド、<code>pack</code> メソッドは、フレームのために関連したピアを作成します。
2310 に解説されているとおり、Swing のメソッド、<code>show</code> メソッド、<code>setVisible</code> メソッド、<code>pack</code> メソッドは、フレームのための関連したピアを作成します。
22002311 ピアの作成で、システムはイベントディスパッチスレッドを作成します。
22012312 これが問題になることがあります。なぜなら <code>pack</code> メソッドと <code>validate</code> メソッドがまだ処理中でもイベントディスパッチスレッドがリスナに通知できるからです。
22022313 この状況は、2つのスレッドが Swing コンポーネントにアクセスする可能性があり、デッドロックや、その他のスレッドに関する問題になる可能性がある重大な欠陥です。
22242335 <Details>
22252336 <![CDATA[
22262337 <p>
2227 このメソッドは、無条件で自分自身を呼び出します。これは、スタックオーバーフローになる無限再帰ループを示しています。
2338 このメソッドは、無条件で自分自身を呼び出します。これはスタックオーバーフローになる無限再帰ループを示しています。
22282339 </p>
22292340 ]]>
22302341 </Details>
22892400 <![CDATA[
22902401 <p>
22912402 Boolean.TRUE、Boolean.FALSE、null を返すメソッドはいつ事故が起きてもおかしくないです。
2292 まるで論理型の値を返すかのように、このメソッドは呼び出される可能性があります。
2403 このメソッドは、まるで論理型の値を返すかのように呼び出されます。
22932404 コンパイラは Boolean 値のオートアンボクシングを挿入します。
22942405 null 値が返されるなら NullPointerException が発生することになります。
2406 </p>
2407 ]]>
2408 </Details>
2409 </BugPattern>
2410
2411 <BugPattern type="NP_OPTIONAL_RETURN_NULL">
2412 <ShortDescription>Optional の戻り型を持つメソッドが明示的に null を返す</ShortDescription>
2413 <LongDescription>{1} は、Optional の戻り型を持っていて、 明示的に null を返しています。</LongDescription>
2414 <Details>
2415 <![CDATA[
2416 <p>
2417 Optional の戻り型の使い方は、常に明示的に null を返すのは設計が望ましくないことを意味します。
2418 null 値をこのようなケースで返すことは契約違反で、多分クライアントコードを破壊するでしょう。
22952419 </p>
22962420 ]]>
22972421 </Details>
24342558 <Details>
24352559 <![CDATA[
24362560 <p>
2437 <code>IllegalMonitorStateException</code> は、一般に設計上の欠陥 (ロックを保持していないオブジェクトで <code>wait</code> メソッドまたは <code>notify</code> メソッドを呼び出す) の場合にだけスローされます。
2561 <code>IllegalMonitorStateException</code> は、一般的に設計上の欠陥 (ロックを保持していないオブジェクトで <code>wait</code> メソッドまたは <code>notify</code> メソッドを呼び出す) の場合にだけスローされます。
24382562 </p>
24392563 ]]>
24402564 </Details>
24552579 </Details>
24562580 </BugPattern>
24572581
2582 <BugPattern type="CAA_COVARIANT_ARRAY_FIELD">
2583 <ShortDescription>フィールドへの共変配列代入</ShortDescription>
2584 <LongDescription>型 {2} の配列が型 {3} のフィールドに割り当てられています。</LongDescription>
2585 <Details>
2586 <![CDATA[
2587 <p>
2588 共変型の配列がフィールドに割り当てられています。
2589 紛らわしくて、次のコードのように他の型の参照が後で配列に格納されるなら、実行時に ArrayStoreException を引き起こすことがあります。
2590 </p>
2591 <blockquote><pre>
2592 Number[] arr = new Integer[10];
2593 arr[0] = 1.0;
2594 </pre></blockquote>
2595 <p>
2596 作成した配列の型またはフィールド型を変更することを検討してください。
2597 </p>
2598 ]]>
2599 </Details>
2600 </BugPattern>
2601
2602 <BugPattern type="CAA_COVARIANT_ARRAY_LOCAL">
2603 <ShortDescription>ローカル変数への共変配列代入</ShortDescription>
2604 <LongDescription>型 {2} の配列が型 {3} の変数に割り当てられています。</LongDescription>
2605 <Details>
2606 <![CDATA[
2607 <p>
2608 共変型の配列がローカル変数に割り当てられています。
2609 紛らわしくて、次のコードのように他の型の参照が後で配列に格納されるなら、実行時に ArrayStoreException を引き起こすことがあります。
2610 </p>
2611 <blockquote><pre>
2612 Number[] arr = new Integer[10];
2613 arr[0] = 1.0;
2614 </pre></blockquote>
2615 <p>
2616 作成した配列の型またはローカル変数型を変更することを検討してください。
2617 </p>
2618 ]]>
2619 </Details>
2620 </BugPattern>
2621
2622 <BugPattern type="CAA_COVARIANT_ARRAY_RETURN">
2623 <ShortDescription>共変配列がメソッドから返される</ShortDescription>
2624 <LongDescription>型 {2} の配列が戻り型 {3} であるメソッドから返されます。</LongDescription>
2625 <Details>
2626 <![CDATA[
2627 <p>
2628 共変型の配列がメソッドから返されます。
2629 呼び出し元のコードが返された配列に他の型の参照を格納しようとするなら、実行時に ArrayStoreException を引き起こすことがあります。
2630 </p>
2631 <p>
2632 作成した配列の型またはメソッドの戻り型を変更することを検討してください。
2633 </p>
2634 ]]>
2635 </Details>
2636 </BugPattern>
2637
2638 <BugPattern type="CAA_COVARIANT_ARRAY_ELEMENT_STORE">
2639 <ShortDescription>おそら互換性のない要素をく共変配列に格納している</ShortDescription>
2640 <LongDescription>型 {2} の値を要素の型が {3} の配列に格納しています。</LongDescription>
2641 <Details>
2642 <![CDATA[
2643 <p>
2644 配列に格納していてる値の型が配列型と一致していません。
2645 実際の配列型が宣言された変数またはフィールドの型よりも狭くなっていて、この割り当てがオリジナルの配列型を満たしていないことが解析でわかっています。
2646 この割り当ては実行時に ArrayStoreException を引き起こすことがあります。
2647 </p>
2648 ]]>
2649 </Details>
2650 </BugPattern>
2651
24582652 <BugPattern type="CN_IDIOM">
24592653 <ShortDescription>Cloneable を実装したクラスが clone メソッドを定義していないか、使用していない</ShortDescription>
24602654 <LongDescription>クラス {0} は、Cloneable を実装していますが、clone メソッドを定義していないか使用していません。</LongDescription>
25302724 <![CDATA[
25312725 <p>
25322726 このメソッドは、例外を捨てているかもしれません。
2533 一般にキャッチした例外は何らかの方法で処理、または報告するべきです、あるいはメソッドからスローするべきです。
2727 一般的にキャッチした例外は何らかの方法で処理、または報告するべきです、またはメソッドからスローするべきです。
25342728 </p>
25352729 ]]>
25362730 </Details>
25432737 <![CDATA[
25442738 <p>
25452739 このメソッドは例外を無視しているかもしれません。
2546 一般に例外は何らかの方法で処理、または報告するべきです、あるいはメソッドからスローするべきです。
2740 一般的に例外は何らかの方法で処理、または報告するべきです、またはメソッドからスローするべきです。
25472741 </p>
25482742 ]]>
25492743 </Details>
26072801 <![CDATA[
26082802 <p>
26092803 Threadオブジェクトが Runnable が期待されているメソッドへのパラメータとして渡されています。
2610 これはかなり異常で、論理エラーを示すか、予想外の振る舞いの原因になるかもしれません。
2804 これはかなり異常で、論理エラーを示すか、予想外の振る舞いの原因になることがあります。
26112805 </p>
26122806 ]]>
26132807 </Details>
26142808 </BugPattern>
26152809
26162810 <BugPattern type="DMI_COLLECTION_OF_URLS">
2617 <ShortDescription>URL の Map や Set はひどい性能になる可能性がある</ShortDescription>
2618 <LongDescription>{1} は、URL の Map か Set を使用しているので、ひどい性能になる可能性があります。</LongDescription>
2811 <ShortDescription>URL の Map や Set はひどい性能になる</ShortDescription>
2812 <LongDescription>{1} は、URL の Map か Set を使用しているので、ひどい性能になります。</LongDescription>
26192813 <Details>
26202814 <![CDATA[
26212815 <p>
26222816 このメソッドまたはフィールドは、URL の <code>Map</code> か <code>Set</code> を使用しています。
2623 URL の <code>equals</code> と <code>hashCode</code> は、ドメイン名の解決を行うので、ひどい性能になる可能性があります。<br>
2817 URL の <code>equals</code> と <code>hashCode</code> は、ドメイン名の解決を行うので、ひどい性能になります。<br>
26242818 詳細は、<a href="http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html">http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html</a> を参照してください。<br>
2625 その代わりに <code>java.net.URI</code> を使うことをを検討してください。
2819 その代わりに <code>java.net.URI</code> を使用することを検討してください。
26262820 </p>
26272821 ]]>
26282822 </Details>
26362830 <p>
26372831 URL の <code>equals</code> メソッドと <code>hashCode</code> メソッドは、ドメイン名の解決を行うので、ひどい性能になる可能性があります。<br>
26382832 詳細は、<a href="http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html">http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html</a> を参照してください。<br>
2639 その代わりに <code>java.net.URI</code> を使うことを検討してください。
2833 その代わりに <code>java.net.URI</code> を使用することを検討してください。
26402834 </p>
26412835 ]]>
26422836 </Details>
26622856 <p>
26632857 <code>System.exit(...)</code> を呼び出すことは、Java 仮想マシン全体をシャットダウンさせてしまいます。
26642858 それが適切な場合にだけ使用するべきです。
2665 <code>System.exit(...)</code> の呼び出しはコードが他のコードによって呼び出されることを困難か不可能にします。
2859 <code>System.exit(...)</code> の呼び出しは、他のコードによる呼び出しを困難か不可能にします。
26662860 その代わりに RuntimeException をスローすることを検討してください。
26672861 </p>
26682862 ]]>
27332927 </p>
27342928 <p>
27352929 過去に、<code>close</code> メソッドや <code>finalize</code> メソッドでガベージコレクタを明示的に呼び出していた状況は、巨大なパフォーマンスブラックホールの原因となりました。
2736 ガベージコレクションは、高くつく可能性があります。何百、何千ものガベージコレクションを強制する状況は、システムの停滞をもたらすでしょう。
2930 ガベージコレクションは高くつきます。何百、何千ものガベージコレクションを強制する状況は、システムの停滞をもたらすでしょう。
27372931 </p>
27382932 ]]>
27392933 </Details>
27462940 <![CDATA[
27472941 <p>
27482942 <code>java.lang.Boolean</code> の新しいインスタンスを作成するとメモリを浪費します。
2749 <code>Boolean</code> オブジェクトは不変で、2つの有効な値 (<code>Boolean.TRUE</code> と <code>Boolean.FALSE</code>) があります。
2750 その代わりに <code>Boolean</code> オブジェクトを作成するために、<code>Boolean.valueOf</code> メソッド (または J2SE 5.0 のオートボクシング) を使用してください。
2943 <code>Boolean</code> オブジェクトは不変で、2つの有用な値 (<code>Boolean.TRUE</code> と <code>Boolean.FALSE</code>) があります。
2944 その代わりに <code>Boolean.valueOf</code> メソッド (または J2SE 5.0 のオートボクシング) を使用して <code>Boolean</code> オブジェクトを作成してください。
27512945 </p>
27522946 ]]>
27532947 </Details>
27612955 <p>
27622956 <code>new Integer(int)</code> の使用は、常に新しいブジェクトになることが保証されています。
27632957 これに対して、<code>Integer.valueOf(int)</code> は、コンパイラ、クラスライブラリ、Java 仮想マシンで値がキャッシュされます。
2764 キャッシュに格納された値を使うことはインスタンスの作成を回避し、コードはより高速になります。
2958 キャッシュに格納された値を使用することはインスタンスの作成を回避し、コードはより高速になります。
27652959 </p>
27662960 <p>
27672961 -128から127までの値は対応するキャッシュされたインスタンスを持つことが保証されています。
27822976 <p>
27832977 <code>new Double(double)</code> の使用は、常に新しいブジェクトになることが保証されています。
27842978 これに対して、<code>Double.valueOf(double)</code> は、コンパイラ、クラスライブラリ、Java 仮想マシンで値がキャッシュされます。
2785 キャッシュに格納された値を使うことはインスタンス生成を回避し、コードはより高速になります。
2979 キャッシュに格納された値を使用することはインスタンス生成を回避し、コードはより高速になります。
27862980 </p>
27872981 <p>
27882982 クラスが J2SE 5.0より前の Java 仮想マシンとの互換性が不要なら、オートボクシングか <code>Double</code>、<code>Float</code> の <code>valueOf</code> メソッドを使用してください。
27982992 <![CDATA[
27992993 <p>
28002994 文字列がプラットホームのデフォルトエンコーディングを使用して大文字、小文字に変換されています。
2801 国際文字で使われると不適切な変換になるかもしれません。
2995 国際文字で使われると不適切な変換になることがあります。
28022996 </p>
28032997 <ul>
28042998 <li>String.toUpperCase(Locale l)</li>
28303024 <![CDATA[
28313025 <p>
28323026 プリミティブ値がボクシングされて、すぐにアンボクシングされます。
2833 おそらくアンボクシングされた値が必要な場所で、手動でボクシングをしているためです。
3027 おそらくアンボクシングされた値が必要な場所で手動でボクシングをしているためです。
28343028 その結果、コンパイラにボクシングの機能を取り消すことを強制しています。
28353029 </p>
28363030 ]]>
28673061 <Details>
28683062 <![CDATA[
28693063 <p>
2870 <code>toString</code> メソッドを呼び出すために、プリミティブ型のラッパクラスのインスタンスを作成しています。
3064 <code>toString</code> メソッドを呼び出すためにプリミティブ型のラッパクラスのインスタンスを作成しています。
28713065 それよりもプリミティブ値を引数にとる static な <code>toString</code> メソッドを使用したほうが効率的です。
28723066 </p>
28733067 <table>
28973091 </Details>
28983092 </BugPattern>
28993093
3094 <BugPattern type="DM_BOXED_PRIMITIVE_FOR_COMPARE">
3095 <ShortDescription>プリミティブが比較でボクシングされている</ShortDescription>
3096 <LongDescription>プリミティブが {2} を呼び出すためにボクシングされています: 代わりに {3} を使用してください。</LongDescription>
3097 <Details>
3098 <![CDATA[
3099 <p>
3100 ボクシングされたプリミティブが単に compareTo メソッドを呼び出すために作られています。
3101 直接プリミティブで働く static compare メソッド (double と float は Java 1.4から、他のプリミティブ型は Java 1.7から) を使うほうがより効率的です。
3102 </p>
3103 ]]>
3104 </Details>
3105 </BugPattern>
3106
29003107 <BugPattern type="DM_NEW_FOR_GETCLASS">
29013108 <ShortDescription>クラスオブジェクトを得るためだけにインスタンスを作成しているメソッド</ShortDescription>
29023109 <LongDescription>{1} は、クラスオブジェクトを得るためだけにインスタンスを作成しています。</LongDescription>
29033110 <Details>
29043111 <![CDATA[
29053112 <p>
2906 メソッドは、クラスオブジェクトを得るために、インスタンスを生成して <code>getClass</code> メソッドを呼び出しています。
3113 メソッドは、クラスオブジェクトを得るためにインスタンスを生成して <code>getClass</code> メソッドを呼び出しています。
29073114 クラスリテラル (<code>Foo.class</code>) を使うほうが簡単です。
29083115 </p>
29093116 ]]>
29363143 </Details>
29373144 </BugPattern>
29383145
3146 <BugPattern type="DM_INVALID_MIN_MAX">
3147 <ShortDescription>Math.max と Math.min の間違った組み合わせ</ShortDescription>
3148 <LongDescription>Math.max と Math.min の組み合わせが間違っています: このコードは常に {2} を返します。</LongDescription>
3149 <Details>
3150 <![CDATA[
3151 <p>
3152 このコードは、<code>Math.min(0, Math.max(100, value))</code> のような構文を使用して境界値を制限しようとしています。
3153 しかしながら、定数の順序が間違っています。 <code>Math.min(100, Math.max(0, value))</code> とすべきです。
3154 結果としてこのコードは常に同じ結果 (もし値が NaN なら NaN) を作り出します。
3155 </p>
3156 ]]>
3157 </Details>
3158 </BugPattern>
3159 <BugPattern type="DM_INVALID_MIN_MAX">
3160 <ShortDescription>Incorrect combination of Math.max and Math.min</ShortDescription>
3161 <LongDescription>Incorrect combination of Math.max and Math.min: this code always returns {2}</LongDescription>
3162 <Details>
3163 <![CDATA[
3164 <p>This code tries to limit the value bounds using the construct like Math.min(0, Math.max(100, value)). However the order of
3165 the constants is incorrect: it should be Math.min(100, Math.max(0, value)). As the result this code always produces the same result
3166 (or NaN if the value is NaN).</p>
3167 ]]>
3168 </Details>
3169 </BugPattern>
3170
29393171 <BugPattern type="DM_NEXTINT_VIA_NEXTDOUBLE">
29403172 <ShortDescription>整数の乱数を生成するためには nextDouble メソッド ではなく nextInt メソッドを使用する</ShortDescription>
29413173 <LongDescription>{1} は、整数の乱数を生成するために nextDouble メソッドを使用しています。nextInt メソッドを使用した方が効率的です。</LongDescription>
29533185 </BugPattern>
29543186
29553187 <BugPattern type="SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE">
2956 <ShortDescription>SQL の Statement の execute メソッドに定数でない文字列を渡している</ShortDescription>
2957 <LongDescription>{1} は、SQL の Statement の execute メソッドに定数でない文字列を渡しています。</LongDescription>
2958 <Details>
2959 <![CDATA[
2960 <p>
2961 このメソッドは、動的に生成されるように思われる文字列で、 SQL の <code>Statement</code> の <code>execute</code> メソッドを呼び出しています。
2962 その代わりに <code>PreparedStatement</code> を使うことを検討してください。
3188 <ShortDescription>SQL の Statement の execute または addBatch メソッドに定数でない文字列を渡している</ShortDescription>
3189 <LongDescription>{1} は、SQL の Statement の execute または addBatch メソッドに定数でない文字列を渡しています。</LongDescription>
3190 <Details>
3191 <![CDATA[
3192 <p>
3193 このメソッドは、動的に生成されるように思われる文字列で、 SQL 文 の <code>execute</code> または <code>addBatch</code> メソッドを呼び出しています。
3194 その代わりに <code>PreparedStatement</code> を使用することを検討してください。
29633195 効率的で、SQL インジェクション攻撃に強いです。
29643196 </p>
29653197 ]]>
29733205 <![CDATA[
29743206 <p>
29753207 このコードは、定数でない文字列から SQL の <code>PreparedStatement</code> を作成しています。
2976 ユーザからのチェックされていない汚染されたデータがこの文字列を作る際に使われるなら、PreparedStatement で予想外で望ましくない何かをするために SQL インジェクションが使われる可能性があります。
3208 ユーザからのチェックされていない汚染されたデータがこの文字列を作る際に使われるなら、<code>PreparedStatement</code> で予想外で望ましくない何かをするために SQL インジェクションが使われる可能性があります。
29773209 </p>
29783210 ]]>
29793211 </Details>
30053237 </Details>
30063238 </BugPattern>
30073239
3240 <BugPattern type="DC_PARTIALLY_CONSTRUCTED">
3241 <ShortDescription>部分的に初期化されたオブジェクトを暴露する可能性がある</ShortDescription>
3242 <LongDescription>部分的に初期化されたオブジェクトを暴露する可能性があります。{1}</LongDescription>
3243 <Details>
3244 <![CDATA[
3245 <p>
3246 ダブルチェックロッキングと共に遅延初期化フィールドを使用するメソッドのようです。
3247 フィールドが正しく volatile として宣言される間にオブジェクトの内部構造がフィールドに割り当てられた後で変更される可能性があります。
3248 したがって、他のスレッドが部分的に初期化されたオブジェクトを見るかもしれません。
3249 </p>
3250 <p>
3251 この問題を直すために、最初にローカル変数をオブジェクトに格納して、完全に構築した後で volatile フィールドを保存することを考えてください。
3252 </p>
3253 ]]>
3254 </Details>
3255 </BugPattern>
3256
30083257 <BugPattern type="FI_FINALIZER_NULLS_FIELDS">
30093258 <ShortDescription>ファイナライザはフィールドを null にする</ShortDescription>
30103259 <LongDescription>{3} は、finalize メソッドの中で null を設定しています。{1.class}</LongDescription>
30253274 <p>
30263275 このファイナライザは、フィールドを null にすること以外に何もしません。
30273276 これはまったく無意味であり、オブジェクトがガベージされ、ファイナライズされ、再びガベージされることを要求しています。
3028 <code>finalize</code> メソッドを削除するべきです。
3277 <code>finalize</code> メソッドを除去するべきです。
30293278 </p>
30303279 ]]>
30313280 </Details>
30573306
30583307 <BugPattern type="FI_NULLIFY_SUPER">
30593308 <ShortDescription>ファイナライザはスーパークラスのファイナライザを無効にしている</ShortDescription>
3060 <LongDescription>{1} は、{2}.finalize() を無効にしています。これは、意図したことですか?</LongDescription>
3309 <LongDescription>{1} は、{2}.finalize() を無効にしています。これは意図したことですか?</LongDescription>
30613310 <Details>
30623311 <![CDATA[
30633312 <p>
31093358 参照によってつながった複数のオブジェクトがファイナライズ可能になると、Java 仮想マシンはすべてのオブジェクトの <code>finalize</code> メソッドを呼び出します。
31103359 おそらく異なるスレッドで同時にです。
31113360 したがって、クラス <i>X</i> の <code>finalize</code> メソッドの中から <i>X</i> によって参照されているオブジェクトの <code>finalize</code> メソッドを呼び出すのは、とりわけ間違った考えです。
3112 なぜなら、オブジェクトがすでに別のスレッドによってファイナライズされているかもしれないからです。
3361 なぜなら、オブジェクトが既に別のスレッドによってファイナライズされているかもしれないからです。
31133362 </p>
31143363 ]]>
31153364 </Details>
31343383 }
31353384 </pre></blockquote>
31363385 <p>
3137 これは対称的で推移的である <code>equals</code> メソッドを実現するのはとても難しいので、間違ったプラクティスと見なされています。
3386 これは対称的で推移的である <code>equals</code> メソッドを実現するのはとても難しいので、バッドプラクティスと見なされています。
31383387 プロパティがなければまったく予想していない振る舞いが起こりえます。
31393388 </p>
31403389 ]]>
31483397 <![CDATA[
31493398 <p>
31503399 このクラスは列挙を定義していて、列挙の等価性はオブジェクト同一性を使用して定義されています。
3151 列挙値のために共変な <code>equals</code> メソッドを定義することは、非常に間違ったプラクティスです。
3152 2つの異なる列挙値が一般にの <code>equals</code> メソッドでは「等価ではない」と判定され、共変な <code>equals</code> メソッドでは「等価」と判定されるからです。
3400 列挙値のために共変な <code>equals</code> メソッドを定義することは、ひどいバッドプラクティスです。
3401 2つの異なる列挙値が <code>equals</code> メソッドでは「等価ではない」と判定され、共変な <code>equals</code> メソッドでは「等価」と判定されるからです。
31533402 共変な <code>equals</code> メソッドを定義しないでください。
31543403 </p>
31553404 ]]>
31963445
31973446 <BugPattern type="EQ_DOESNT_OVERRIDE_EQUALS">
31983447 <ShortDescription>スーパークラスの equals メソッドをオーバーライドしていないクラス</ShortDescription>
3199 <LongDescription>{0} は、{1.givenClass} をオーバーライドしていません。</LongDescription>
3448 <LongDescription>{0} は、{2.givenClass} をオーバーライドしていません。</LongDescription>
32003449 <Details>
32013450 <![CDATA[
32023451 <p>
32033452 このクラスは、<code>equals</code> メソッドを定義しているクラスを拡張してフィールドを追加していますが、<code>equals</code> メソッドを定義していません。
32043453 したがって、このクラスのインスタンスの等価性は、サブクラスと追加されたフィールドの同一性を無視します。
32053454 これが意図したことで、しかも、<code>equals</code> メソッドをオーバーライドする必要がないことを確実にしてください。
3206 たとえ <code>equals</code> メソッドをオーバーライドする必要がないとしても、サブクラスのための <code>equals</code> メソッドが <code>super.equals(o)</code> を呼び出して結果を返すという事実を実証するために、いずれにしろ、<code>equals</code> メソッドをオーバーライドすることを検討してください。
3455 たとえ <code>equals</code> メソッドをオーバーライドする必要がないとしても、サブクラスのための <code>equals</code> メソッドが <code>super.equals(o)</code> を呼び出して結果を返すという事実を実証するためにいずれにしろ、<code>equals</code> メソッドをオーバーライドすることを検討してください。
32073456 </p>
32083457 ]]>
32093458 </Details>
32583507 <Details>
32593508 <![CDATA[
32603509 <p>
3261 このクラスの <code>equals</code> メソッドは、引数の型が <code>this</code> オブジェクトの型と互換性があるこをチェックするために、我々が認識しているパターンで何もしていません。
3510 このクラスの <code>equals</code> メソッドは、引数の型が <code>this</code> オブジェクトの型と互換性があるこをチェックするために我々が認識しているパターンで何もしていません。
32623511 このコードは何も間違っていないかもしれませんが、レビューする価値があります。
32633512 </p>
32643513 ]]>
32993548 <![CDATA[
33003549 <p>
33013550 このクラスでは、常に false を返す <code>equlas</code> メソッドを定義しています。
3302 これは、オブジェクトがそれ自身と等価ではないことを意味していて、このクラスの有効な Map や Set を作成できません。
3551 これはオブジェクトがそれ自身と等価ではないことを意味していて、このクラスの有用な Map や Set を作成できません。
33033552 より根本的に、<code>equals</code> メソッドの要件の一つである反射性を満たしていないことになります。
33043553 </p>
33053554 <p>
33663615 <![CDATA[
33673616 <p>
33683617 このコードは、<code>compareTo</code> または <code>compare</code> メソッドの戻り値を無効にしています。
3369 これは疑わしいか間違ったプログラミングプラクティスです。戻り値が Integer.MIN_VALUE なので、戻り値を無効にすることは結果の符号を無効にしません。
3618 これは疑わしいかバッドプログラミングプラクティスです。戻り値が Integer.MIN_VALUE なので、戻り値を無効にすることは結果の符号を無効にしません。
33703619 結果を無効にするのではなくオペランドの順序を逆にすることによって、同じ意図した結果を得ることができます。
33713620 </p>
33723621 ]]>
33793628 <Details>
33803629 <![CDATA[
33813630 <p>
3382 いくつかの状況下では、この <code>compareTo</code> または <code>compare</code> メソッドは Integer.MIN_VALUE を返します。それは、非常に間違ったプラクティスです。
3631 いくつかの状況下では、この <code>compareTo</code> または <code>compare</code> メソッドは Integer.MIN_VALUE を返します。ひどいバッドプラクティスです。
33833632 <code>compareTo</code> メソッドの戻り値で重要なことは結果の符号だけです。
33843633 しかし、結果の符号を無効にすることを期待して、<code>compareTo</code> メソッドの戻り値を無効にすることがあります。
3385 戻り値が Integer.MIN_VALUE である場合を除き、Integer.MIN_VALUE よりも-1を返します。
3386 </p>
3387 ]]>
3388 </Details>
3389 </BugPattern>
3390
3634 返された値が Integer.MIN_VALUE の場合を除いてです。 Integer.MIN_VALUE よりも-1を返してください。
3635 </p>
3636 ]]>
3637 </Details>
3638 </BugPattern>
3639
3640 <BugPattern type="CO_COMPARETO_INCORRECT_FLOATING">
3641 <ShortDescription>compareTo()/compare() は間違って float または double 値を処理する</ShortDescription>
3642 <LongDescription>{1} は、間違って {2} 値を処理します。</LongDescription>
3643 <Details>
3644 <![CDATA[
3645 <p>
3646 このメソッドはこのようなパターンを使用して double または float 値を比較しています : val1 &gt; val2 ? 1 : val1 &lt; val2 ? -1 : 0。
3647 このパターンは 正しくないソート結果や壊れたコレクションの原因になるかもしれない -0.0 や NaN 値のために間違って働きます (もし比較された値がキーとして使われるなら)。
3648 すべての特殊なケースを正確に処理するために Double.compare または Float.compare メソッドの使用を検討してください。
3649 </p>
3650 ]]>
3651 </Details>
3652 </BugPattern>
3653
33913654 <BugPattern type="CO_SELF_NO_OBJECT">
33923655 <ShortDescription>共変な compareTo メソッドの定義</ShortDescription>
33933656 <LongDescription>{0} は、compareTo(Object) ではなく compareTo({0.givenClass}) メソッドを定義しています。</LongDescription>
34593722 <![CDATA[
34603723 <p>
34613724 このクラスは、<code>compareTo(...)</code> メソッドを定義していますが、<code>equals</code> メソッドは <code>java.lang.Object</code> から継承しています。
3462 一般に、<code>equals</code> メソッドが true を返す場合に限り、<code>compareTo</code> メソッドは0を返すべきです。
3725 一般的に<code>equals</code> メソッドが true を返す場合に限り、<code>compareTo</code> メソッドは0を返すべきです。
34633726 これが違反されるなら奇妙で予測できない失敗が PriorityQueue などのクラスで発生します。
34643727 J2SE 5.0では、<code>PriorityQueue.remove()</code> は <code>compareTo</code> メソッドを使用しますが、Java SE 6では、<code>equals</code> メソッドを使用します。
34653728 </p>
34683731 </p>
34693732 <blockquote><p>
34703733 必須というわけではありませんが、<code>(x.compareTo(y)==0) == (x.equals(y))</code> であることが強く推奨されます。
3471 一般に、<code>Comparable</code> インタフェースを実装しているクラスで、この条件に違反するクラスは明確にこの事実を示す必要があります。
3734 一般的に<code>Comparable</code> インタフェースを実装しているクラスで、この条件に違反するクラスは明確にこの事実を示す必要があります。
34723735 「注:このクラスは <code>equals</code> と一貫性のない自然順序付けを持ちます」などと明示することをお勧めします。
3473 </p></blockquote>
3736 </blockquote></p>
34743737 ]]>
34753738 </Details>
34763739 </BugPattern>
35583821 <p>
35593822 このコードは、参照等価性のために == や != を使用して <code>java.lang.String</code> オブジェクトを比較しています。
35603823 両方の文字列がソースファイルの定数か、<code>String.intern()</code> を使用して正準化されていないかぎり、同じ文字列は2つの異なる String オブジェクトによって表されるかもしれません。
3561 その代わりに <code>equals(Object)</code> メソッドを使うことを検討してください。
3824 その代わりに <code>equals(Object)</code> メソッドを使用することを検討してください。
35623825 </p>
35633826 ]]>
35643827 </Details>
35723835 <p>
35733836 このコードは、参照等価性のために == や != を使用して <code>java.lang.String</code> パラメータを比較しています。
35743837 文字列定数または正準化された文字列だけをメソッドに渡すことを呼び出し元に要求することは必要以上に脆弱で測定可能な性能の向上をもたらしません。
3575 その代わりに <code>equals(Object)</code> メソッドを使うことを検討してください。
3838 その代わりに <code>equals(Object)</code> メソッドを使用することを検討してください。
35763839 </p>
35773840 ]]>
35783841 </Details>
36113874 <![CDATA[
36123875 <p>
36133876 Web サーバは、一般的にサーブレットや JSP クラスのインスタンスを1つだけ作成します (すなわち、シングルトンとして扱います)。
3614 複数のスレッドが複数同時に発生するリクエストを処理するためにインスタンスのメソッドを呼び出します。
3615 したがって、可変インスタンスフィールドは一般に競合状態を作成します。
3877 複数のスレッドが複数同時のリクエストに応えるためにそのインスタンスでメソッドを呼び出します。
3878 したがって、一般に可変インスタンスフィールドは競合状態を作ります。
36163879 </p>
36173880 ]]>
36183881 </Details>
36413904 </p>
36423905 <p>
36433906 不正確ないろいろな原因がこのディテクタにあることに注意してください。
3644 たとえば、ディテクタはロックを保持されるすべての状況を静的に検出できるというわけではありません。
3907 たとえば、ディテクタはロックを保持されるすべての状況を静的に検出できるわけではありません。
36453908 また、ディテクタがロックされたアクセスとアンロックされたアクセスの区別が正確なときでも、問題のコードは依然として正しいかもしれません。
36463909 </p>
36473910 ]]>
36553918 <![CDATA[
36563919 <p>
36573920 <code>notify</code> メソッドまたは <code>notifyAll</code> メソッドへの呼び出しは可変オブジェクト状態にどんな (明らかな) 付随的な変更ももたらされませんでした。
3658 一般に、別のスレッドが期待しているいくつかの条件が真になったので、モニタで <code>notify</code> メソッドが呼び出されます。
3659 しかしながら、意味がある条件のために、両方のスレッドに見えるヒープオブジェクトを含まなければなりません。
3921 一般的に別のスレッドが期待しているいくつかの条件が真になったので、モニタで <code>notify</code> メソッドが呼び出されます。
3922 しかしながら、意味がある条件のために両方のスレッドに見えるヒープオブジェクトを含まなければなりません。
36603923 </p>
36613924 <p>
36623925 可変オブジェクトの状態変更が通知があるメソッドを呼び出したメソッドで起こったかもしれないので、このバグが必ずしもエラーを示すというわけではありません。
37273990 <![CDATA[
37283991 <p>
37293992 このメソッドは、スレッドで 明示的に <code>run</code> メソッドを呼び出しています。
3730 一般に、クラスは新しいスレッドで自己の <code>run</code> メソッドを呼び出してもらうために <code>Runnable</code> インタフェースを実装します。
3993 一般的にクラスは新しいスレッドで自己の <code>run</code> メソッドを呼び出してもらうために <code>Runnable</code> インタフェースを実装します。
37313994 その場合は、<code>Thread.start()</code> を呼び出すのが正しいです。
37323995 </p>
37333996 ]]>
37554018 <p>
37564019 このコードは、短絡論理 (&amp;&amp; や ||) ではなく非短絡論理 (&amp; や |) を使用していると思われます。
37574020 さらに、左辺値によって右辺を評価したくない (例外のスローや演算が高くつく副作用があるため) と思っているのかもしれません。
3758 非短絡論理は、左辺の結果がわかっていて推論できるときでも、両側の式が評価されます。
4021 非短絡論理は、左辺を知ることによって結果を推論できたとしても両側の式が評価されます。
37594022 これは効率が悪く、右辺の評価でエラーが発生するケースを左辺でガードしているなら、結果としてエラーになる可能性があります。
37604023 </p>
37614024 <p>
37714034 <![CDATA[
37724035 <p>
37734036 このコードは、短絡論理 (&amp;&amp; や ||) ではなく非短絡論理 (&amp; や |) を使用していると思われます。
3774 非短絡論理は、左辺の結果がわかっていて推論できるときでも、両側の式が評価されます。
4037 非短絡論理は、左辺を知ることによって結果を推論できたとしても両側の式が評価されます。
37754038 これは効率が悪く、右辺の評価でエラーが発生するケースを左辺でガードしているなら、結果としてエラーになる可能性があります。
37764039 </p>
37774040 <p>
37874050 <Details>
37884051 <![CDATA[
37894052 <p>
3790 2つ以上のロックを保持して、モニタで待機させるとデッドロックの原因になる場合があります。
4053 2つ以上のロックを保持して、モニタで待機させるとデッドロックを引き起こすことがあります。
37914054 <code>wait</code> メソッドを呼び出すと、待機しているオブジェクトのロックを解除するだけで、その他のロックは解除しません。
37924055 これは必ずしもバグではありませんが厳密に調べる価値があります。
37934056 </p>
38194082 <![CDATA[
38204083 <p>
38214084 このメソッドには条件制御フローによってガードされない <code>java.lang.Object.wait()</code> の呼び出しがあります。
3822 このコードは、<code>wait</code> メソッドを呼び出す前に待機するつもりだった条件がすでに満たされていないことを確かめるべきです。
4085 このコードは、<code>wait</code> メソッドを呼び出す前に待機するつもりだった条件が既に満たされていないことを確かめるべきです。
38234086 どんな前の通知も無視されます。
38244087 </p>
38254088 ]]>
38334096 <![CDATA[
38344097 <p>
38354098 このコンストラクタは、まだ値が代入されていないフィールドを読み出しています。
3836 多くの場合、プログラマがコンストラクタのパラメータの代わりに誤ってフィールドを使用するときに起きます。
4099 多くの場合、プログラマがコンストラクタのパラメータの代わりに誤ってフィールドを使うときに起きます。
38374100 </p>
38384101 ]]>
38394102 </Details>
38884151 <![CDATA[
38894152 <p>
38904153 このクラスには類似した名前の get メソッドと set メソッドがあり、set メソッドは同期化していて、get メソッドは同期化していません。
3891 get メソッドの呼び出し元がオブジェクトの一貫した状態を必ずしも見るというわけではないので、実行時に間違った振る舞いになるかもしれません。
4154 get メソッドの呼び出し元がオブジェクトの一貫した状態を必ずしも見るというわけではないので、実行時に間違った振る舞いの原因になることがあります。
38924155 get メソッドは同期化するべきです。
38934156 </p>
38944157 ]]>
39024165 <![CDATA[
39034166 <p>
39044167 バグインスタンスによって参照される2つのクラスのスタティックイニシャライザで循環が検出されました。
3905 多くの種類の予想外の振る舞いは、そのような循環に起因するかもしれません。
4168 さまざまな予想外の振る舞いはそのような循環に起因することがあります。
39064169 </p>
39074170 ]]>
39084171 </Details>
40174280 private static final Object fileLock = new Object();
40184281 </pre></blockquote>
40194282 <p>
4020 既存のコードとしては間違っていないのかもしれませんが、紛らわしいので将来リファクタリングするべきかもしれません。
4283 既存のコードとしては間違っていないかもしれないが、紛らわしいので将来リファクタリングするべきかもしれません。
40214284 たとえば、IntelliJ の "Remove Boxing" のようなリファクタリングは Java 仮想マシンを通して共有される正準化された <code>Integer</code> オブジェクトを使用するように置き換えてしまい、非常に紛らわしい振る舞いと潜在的デッドロックの原因になります。
40224285 </p>
40234286 ]]>
40404303 }
40414304 </pre></blockquote>
40424305 <p>
4043 <code>Integer</code> オブジェクトはキャッシュして共有される可能性があります。
4306 <code>Integer</code> オブジェクトはキャッシュして共有できます。
40444307 他の無関係なコードと同じオブジェクトで同期化している可能性があるので、無応答やデッドロックの原因になります。
40454308 </p>
40464309 <p>
40774340 <![CDATA[
40784341 <p>
40794342 このクラスのフィールドは、同期化に関して一貫性のないアクセスをしているように見えます。
4080 このバグレポートは、バグパターンディテクタが以下のように判断したことをを示します。
4343 このバグレポートは、バグパターンディテクタが以下のように判断したことを示します。
40814344 </p>
40824345 <ul>
40834346 <li>クラスにはロックされたアクセスとアンロックされたアクセスが混在していて</li>
40894352 </p>
40904353 <p>
40914354 不正確のいろいろなソースがこのディテクタにあることに注意してください。
4092 たとえば、ディテクタはロックを保持されるすべての状況を静的に検出できるというわけではありません。
4093 また、ディテクタがロックされたアクセスとアンロックされたアクセスの区別が正確なときでも、問題のコードは依然として正しいかもしれません。
4355 たとえば、ディテクタはロックを保持されるすべての状況を静的に検出できるわけではありません。
4356 また、ディテクタがロックされたアクセスとアンロックされたアクセスの区別が正確なときでも、問題のコードはまだ正しいかもしれません。
40944357 </p>
40954358 ]]>
40964359 </Details>
41034366 <![CDATA[
41044367 <p>
41054368 このメソッドは、フィールドの同時更新に対して同期化でガードしようとしています。しかし、フィールドをガードするとフィールドではなく、フィールドが参照するオブジェクトのロックを獲得します。
4106 これはあなたが必要とする相互排除を提供しないかもしれません。
4369 これはあなたが必要とする相互排除ができないかもしれません。
41074370 他のスレッドは (他の目的のための) 参照されたオブジェクトのロックを獲得するかもしれません。<br>
41084371 このパターンの例は以下のようになります。
41094372 </p>
41294392 <![CDATA[
41304393 <p>
41314394 このメソッドは、可変フィールドから参照されたオブジェクトで同期化しています。
4132 異なるスレッドが異なるオブジェクトで同期化しているかもしれないので、これは有効な意味を持っている可能性が低いです。
4395 異なるスレッドが異なるオブジェクトで同期化しているかもしれないので、有用な意味を持っている可能性が低いです。
41334396 </p>
41344397 ]]>
41354398 </Details>
41414404 <Details>
41424405 <![CDATA[
41434406 <p>
4144 インタフェースに定義された final static フィールドが、配列や Hashtable などの可変オブジェクトを参照しています。
4145 この可変オブジェクトは、悪質なコードや偶然別のパッケージによって変更される可能性があります。
4146 これを解決するために、フィールドはクラスへ移動する必要があり、脆弱性を回避するためにパッケージプロテクテッドにします。
4407 インタフェースに定義された final static フィールドが配列や Hashtable などの可変オブジェクトを参照しています。
4408 この可変オブジェクトは悪意のあるコードや偶然別のパッケージによって変更できます。
4409 これを解決するためにフィールドはクラスへ移動する必要があり、脆弱性を回避するためにパッケージプロテクテッドにします。
41474410 </p>
41484411 ]]>
41494412 </Details>
41554418 <Details>
41564419 <![CDATA[
41574420 <p>
4158 この可変 static フィールドは、悪質なコードや偶然別のパッケージによって変更される可能性があります。
4421 この可変 static フィールドは悪意のあるコードや偶然別のパッケージによって変更できます。
41594422 フィールドは、脆弱性を回避するために final および/またはパッケージプロテクテッドにします。
41604423 </p>
41614424 ]]>
41684431 <Details>
41694432 <![CDATA[
41704433 <p>
4171 final でない public static フィールドは、悪質なコードや偶然別のパッケージによって変更される可能性があります。
4434 final でない public static フィールドは悪意のあるコードや偶然別のパッケージによって変更できます。
41724435 フィールドは、脆弱性を回避するために final にします。
41734436 しかしながら、スタティックイニシャライザには複数のフィールドへの書き込みがあるので、何らかのリファクタリングを必要とするでしょう。
41744437 </p>
41754438 ]]>
41764439 </Details>
41774440 </BugPattern>
4178
4441
41794442 <BugPattern type="MS_SHOULD_BE_FINAL">
41804443 <ShortDescription>final にすべきフィールド</ShortDescription>
4181 <LongDescription>{1} は、final ではありませんが、final にすべきです。</LongDescription>
4182 <Details>
4183 <![CDATA[
4184 <p>
4185 final でない public static フィールドは、悪質なコードや偶然別のパッケージによって変更される可能性があります。
4444 <LongDescription>{1} は、final ではありませんが final にすべきです。</LongDescription>
4445 <Details>
4446 <![CDATA[
4447 <p>
4448 final でない public static フィールドは悪意のあるコードや偶然別のパッケージによって変更できます。
41864449 フィールドは、脆弱性を回避するために final にします。
41874450 </p>
41884451 ]]>
41954458 <Details>
41964459 <![CDATA[
41974460 <p>
4198 この可変 static フィールドは、悪質なコードや偶然別のパッケージによって変更される可能性があります。
4461 この可変 static フィールドは悪意のあるコードや偶然別のパッケージによって変更できます。
41994462 フィールドは、脆弱性を回避するためにパッケージプロテクテッドにします。
42004463 </p>
42014464 ]]>
42084471 <Details>
42094472 <![CDATA[
42104473 <p>
4211 この final static フィールドは、Hashtable を参照しているので、悪質なコードや偶然別のパッケージによってアクセスされる可能性があります。
4474 この final static フィールドは Hashtable を参照しているので、悪意のあるコードや偶然別のパッケージによってアクセスできます。
42124475 このコードは、Hashtable のコンテンツを自由に変更できます。
4476 </p>
4477 ]]>
4478 </Details>
4479 </BugPattern>
4480
4481 <BugPattern type="MS_MUTABLE_COLLECTION">
4482 <ShortDescription>可変コレクションのフィールド</ShortDescription>
4483 <LongDescription>{1} は可変コレクションです。</LongDescription>
4484 <Details>
4485 <![CDATA[
4486 <p>
4487 可変コレクションのインスタンスが final static フィールドに割り当てられています。
4488 したがって、 悪意のあるコードや偶然別のパッケージによって変更できます。
4489 脆弱性を避けるために Collections.unmodifiableSet/List/Map などでこのフィールドをラップすることを検討してください。
4490 </p>
4491 ]]>
4492 </Details>
4493 </BugPattern>
4494
4495 <BugPattern type="MS_MUTABLE_COLLECTION_PKGPROTECT">
4496 <ShortDescription>パッケージプロテクテッドにすべき可変コレクションのフィールド</ShortDescription>
4497 <LongDescription>{1} は、パッケージプロテクテッドにすべき可変コレクションです。</LongDescription>
4498 <Details>
4499 <![CDATA[
4500 <p>
4501 可変コレクションのインスタンスが final static フィールドに割り当てられています。
4502 したがって、悪意のあるコードや偶然別のパッケージによって変更できます。
4503 フィールドは脆弱性を避けるためにパッケージプロテクテッドにできます。
4504 代わりに Collections.unmodifiableSet/List/Map などでこのフィールドをラップしても脆弱性を避けることができます。
42134505 </p>
42144506 ]]>
42154507 </Details>
42214513 <Details>
42224514 <![CDATA[
42234515 <p>
4224 この final static フィールドは、配列を参照しているので、悪質なコードや偶然別のパッケージによってアクセスされる可能性があります。
4516 この final static フィールドは配列を参照しているので、悪意のあるコードや偶然別のパッケージによってアクセスできます。
42254517 このコードは、配列のコンテンツを自由に変更できます。
42264518 </p>
42274519 ]]>
42294521 </BugPattern>
42304522
42314523 <BugPattern type="MS_CANNOT_BE_FINAL">
4232 <ShortDescription>final でないフィールドは悪質なコードから保護できない</ShortDescription>
4233 <LongDescription>{1} は、final でないので、悪質なコードから保護できません。 </LongDescription>
4234 <Details>
4235 <![CDATA[
4236 <p>
4237 この可変 static フィールドは、悪質なコードや偶然別のパッケージによって変更される可能性があります。
4524 <ShortDescription>final でないフィールドは悪意のあるコードから保護できない</ShortDescription>
4525 <LongDescription>{1} は、final でないので、悪意のあるコードから保護できません。 </LongDescription>
4526 <Details>
4527 <![CDATA[
4528 <p>
4529 この可変 static フィールドは悪意のあるコードや偶然別のパッケージによって変更できます。
42384530 残念ながらこのような使い方は簡単に解決できません。
4531 </p>
4532 ]]>
4533 </Details>
4534 </BugPattern>
4535
4536 <BugPattern type="ME_MUTABLE_ENUM_FIELD">
4537 <ShortDescription>列挙型フィールドは public で可変である</ShortDescription>
4538 <LongDescription>{1} フィールドは public で可変です。</LongDescription>
4539 <Details>
4540 <![CDATA[
4541 <p>
4542 可変 public フィールドが public 列挙型の中に定義されています。
4543 したがって、フィールドは悪意のあるコードや偶然別のパッケージによって変更できます。
4544 可変列挙型フィールドが遅延初期化で使用されるかもしれないとしても外界へ暴露するバッドプラクティスです。
4545 このメソッドを final およびパッケージプライベートとして宣言することを考えてください。
4546 </p>
4547 ]]>
4548 </Details>
4549 </BugPattern>
4550
4551 <BugPattern type="ME_ENUM_FIELD_SETTER">
4552 <ShortDescription>public 列挙型メソッドが無条件にフィールドを設定するPublic enum method unconditionally sets its field</ShortDescription>
4553 <LongDescription>{1} は無条件にフィールド {2.name} を設定しています。</LongDescription>
4554 <Details>
4555 <![CDATA[
4556 <p>
4557 無条件に列挙型フィールドを設定している public 列挙型で public メソッドを宣言しています。
4558 したがって、フィールドは悪意のあるコードや偶然別のパッケージによって変更できます。
4559 可変列挙型フィールドが遅延初期化で使用されるかもしれないとしても外界へ暴露するバッドプラクティスです。
4560 このメソッドを除去するかパッケージプライベートとして宣言することを考えてください。
42394561 </p>
42404562 ]]>
42414563 </Details>
42494571 <p>
42504572 内部クラスは、継承されたメソッドか外部クラスで定義されたメソッドなのかどちらとも解釈できるメソッドを呼び出しています。
42514573 たとえば、<code>foo(17)</code> を呼び出します。それはスーパークラスと外部のメソッドの両方で定義されています。
4252 Java のセマンティックスでは、継承したメソッドを呼び出しますが、これは意図したことではないかもしれません。
4574 Java のセマンティックスでは、継承したメソッドを呼び出しますが、これはあなたが意図したことではないかもしれません。
42534575 </p>
42544576 <p>
42554577 本当に継承されたメソッドを呼び出すつもりなら super を付けて (例:super.foo(17)) 呼び出してください。
42714593 <![CDATA[
42724594 <p>
42734595 このクラスは、スーパークラスが異なるパッケージであるということを除いて、スーパークラスと同一の単純名をです (たとえば、<code>alpha.Foo</code> が <code>beta.Foo</code> を拡張します)。
4274 これは非常に紛らわしく、参照関係を解決するために import 文を見なければならなかったり、スーパークラスに存在するメソッドを誤ってオーバーライドしてしまったりする状況を作り出します。
4596 これは非常に紛らわしく、参照関係を解決するために import 文を見なければならなかったり、スーパークラスのメソッドをオーバーライドしないで誤ってメソッドを定義する状況を作り出します。
42754597 </p>
42764598 ]]>
42774599 </Details>
42834605 <Details>
42844606 <![CDATA[
42854607 <p>
4286 このクラスまたはインタフェースは、、インタフェースが異なるパッケージであるということを除いて実装された/拡張されたインタフェースと同一の単純名です (たとえば、<code>alpha.Foo</code> が <code>beta.Foo</code> を継承しているような状況です)。
4287 これは非常に紛らわしく、参照関係を解決するために import 文を見なければならなかったり、スーパークラスに存在するメソッドを誤ってオーバーライドしてしまったりする状況を作り出します。
4608 このクラスまたはインタフェースは、インタフェースが異なるパッケージであるということを除いて実装された/拡張されたインタフェースと同一の単純名です (たとえば、<code>alpha.Foo</code> が <code>beta.Foo</code> を継承しているような状況です)。
4609 これは非常に紛らわしく、参照関係を解決するために import 文を見なければならなかったり、スーパークラスのメソッドをオーバーライドしないで誤ってメソッドを定義する状況を作り出します。
42884610 </p>
42894611 ]]>
42904612 </Details>
43504672 参照されたメソッドは、大文字の使い方だけによって異なる名前があります。
43514673 大文字の使い方が同一ならメソッドの1つが他のメソッドをオーバーライドするので、非常に紛らわしいです。
43524674 他のメソッドの存在から、これらのメソッドの両方の存在が意図的で、確実に混乱させていると思われます。
4353 APIの凍結によって両方とも持たざるを得ない場合を除き、それらのうちの1つを削除しようと努力するべきです。
4675 APIの凍結によって両方とも持たざるを得ない場合を除き、それらのうちの1つを除去しようと努力するべきです。
43544676 </p>
43554677 ]]>
43564678 </Details>
43574679 </BugPattern>
43584680
43594681 <BugPattern type="NM_WRONG_PACKAGE">
4360 <ShortDescription>パラメータの間違ったパッケージのために、スーパークラスのメソッドをオーバーライドしていないメソッド</ShortDescription>
4682 <ShortDescription>パラメータの間違ったパッケージのためにスーパークラスのメソッドをオーバーライドしていないメソッド</ShortDescription>
43614683 <LongDescription>パラメータの型 {4} がスーパークラスのパラメータの型 {5} と合致していないので、{1} はスーパークラスのメソッドをオーバーライドしていません。</LongDescription>
43624684 <Details>
43634685 <![CDATA[
44414763 <p>
44424764 この正規のメソッドは定義しているクラスと同じ名前です。
44434765 これはコンストラクタを意図していた可能性が高いです。もしそうなら void 戻り値の宣言を除去してください。
4444 このメソッドを定義したことが偶然間違っているとわかり、正しいコンストラクタを定義したが、下位互換性のためにこのメソッドを取り除くことができないなら、メソッドを非推奨にしてください。
4445 </p>
4446 ]]>
4447 </Details>
4448 </BugPattern>
4449
4766 偶然メソッドを定義したことが間違いだとわかり、適切なコンストラクタを定義したが、後方互換性のためにこのメソッドを除去できないならメソッドを非推奨にしてください。
4767 </p>
4768 ]]>
4769 </Details>
4770 </BugPattern>
4771
44504772 <BugPattern type="NM_LCASE_HASHCODE">
44514773 <ShortDescription>クラスは hashcode() を定義しています。hashCode() にすべきですか?</ShortDescription>
44524774 <LongDescription>クラス {0} は、hashcode() を定義しています。hashCode() にすべきですか?</LongDescription>
44594781 ]]>
44604782 </Details>
44614783 </BugPattern>
4462
4784
44634785 <BugPattern type="NM_LCASE_TOSTRING">
44644786 <ShortDescription>クラスは tostring() を定義しています。toString() にすべきですか?</ShortDescription>
44654787 <LongDescription>クラス {0} は、tostring() を定義しています。toString() にすべきですか?</LongDescription>
44934815 <![CDATA[
44944816 <p>
44954817 このクラスは、例外クラスから派生されていないのにクラス名が「Exception」で終わっています。
4496 これは、このクラスのユーザに紛らわしいです。
4818 これはこのクラスのユーザに紛らわしいです。
44974819 </p>
44984820 ]]>
44994821 </Details>
45734895 <![CDATA[
45744896 <p>
45754897 フィールドは、transient と宣言していますが、クラスは直列化可能ではないので、まったく効果がありません。
4576 クラスが transient だったときの名残かもしれません、あるいは直列化機構を誤解しているのかもしれません。
4898 クラスが transient だったときの名残かもしれません、または直列化機構を誤解しているのかもしれません。
45774899 </p>
45784900 ]]>
45794901 </Details>
45854907 <Details>
45864908 <![CDATA[
45874909 <p>
4588 このクラスには複数の場所で更新されるフィールドがあります。したがって、このクラスの状態の一部であると思われます。
4910 このクラスには複数の場所で更新されるフィールドがあります。したがって、このクラスの状態の一部だと思われます。
45894911 しかしながら、フィールドは transient と宣言しているので、 readObject/readResolve で値が設定されません。
45904912 クラスの直列化復元されたインスタンスにはデフォルト値が設定されます。
45914913 </p>
46434965 このクラスは <code>Serializable</code> インタフェースを実装していますが、<code>serialVersionUID</code> フィールドを定義していません。
46444966 .class オブジェクトへの参照を追加するのと同じくらい簡単な変更でクラスに合成フィールドを追加します。
46454967 それは、残念ながら暗黙の <code>serialVersionUID</code> を変えます (たとえば、<code>String.class</code> への参照を追加すると、<code>class$java$lang$String</code> という static フィールドを生成します)。
4646 また、バイトコードコンパイラへの異なるソースコードは、クラスオブジェクトまたは内部クラスに参照のために生成される合成変数のために異なる命名規則を使用するかもしれません。
4647 バージョンを横断する Serializable の相互運用性を保証するために、明示的に、serialVersionUID を追加することを検討してください。
4968 また、バイトコードコンパイラへの異なるソースコードは、クラスオブジェクトまたは内部クラスを参照するために生成される合成変数のために異なる命名規則を使用するかもしれません。
4969 バージョンを横断する Serializable の相互運用性を保証するために明示的に serialVersionUID を追加することを検討してください。
46484970 </p>
46494971 ]]>
46504972 </Details>
46724994 <![CDATA[
46734995 <p>
46744996 このメソッドには1つの case が次の case へと通り抜ける switch 文があります。
4675 通常、break か return でこの case を終わらせる必要があります。
4997 通常は、break か return でこの case を終わらせる必要があります。
46764998 </p>
46774999 ]]>
46785000 </Details>
46855007 <![CDATA[
46865008 <p>
46875009 このメソッドにはdefault がない switch 文があります。
4688 通常、default を用意する必要があります。
4689 </p>
4690 <p>
4691 解析は生成されたバイトコードを見るだけなので、default が switch 文の終わりにあって、break 文で終わらないならば、誤警告のトリガーとなります。
5010 通常は、default を用意する必要があります。
5011 </p>
5012 <p>
5013 解析は生成されたバイトコードを見るだけなので、default が switch 文の終わりにあって、他のケースに break 文が含まれていないなら誤検出のトリガーとなります。
46925014 </p>
46935015 ]]>
46945016 </Details>
47095031
47105032 <BugPattern type="SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH_TO_THROW">
47115033 <ShortDescription>スローする switch 文のフォールスルーのために格納が無効になっている</ShortDescription>
4712 <LongDescription>前の case からの値 {2.givenClass} が、スローする switch 文のフォールスルーのためにここで失われています。</LongDescription>
5034 <LongDescription>前の case からの値 {2.givenClass} がスローする switch 文のフォールスルーのためにここで失われています。</LongDescription>
47135035 <Details>
47145036 <![CDATA[
47155037 <p>
48275149 <![CDATA[
48285150 <p>
48295151 この直列化可能なクラスは内部クラスです。内部クラスを直列化しようとすると関連した外部クラスのインスタンスも直列化します。
4830 外部クラスのインスタンスは直列化可能なので失敗しませんが、意図したことよりもっとずっと多くのデータを直列化するかもしれません。
5152 外部クラスのインスタンスは直列化可能なので失敗しません。しかし、意図していたよりももっと多くのデータを直列化するかもしれません。
48315153 できれば、内部クラスを static にして問題を解決するべきです。
48325154 </p>
48335155 ]]>
48535175 <![CDATA[
48545176 <p>
48555177 コンストラクタがスレッドを開始しています。クラスが拡張され、サブクラスが作られるなら間違っていそうです。
4856 なぜなら、サブクラスのコンストラクタでスレッドが開始される前に、スーパークラスのスレッドが開始してしまうためです。
5178 なぜなら、サブクラスのコンストラクタでスレッドが開始される前にスーパークラスのスレッドが開始されてしまうためです。
48575179 </p>
48585180 ]]>
48595181 </Details>
49445266 <![CDATA[
49455267 <p>
49465268 このフィールドに定数値 null を書き込みます。したがって、フィールドの読み出しは null を返します。
4947 誤りをチェックしてください。使わないなら除去してください。
5269 誤りをチェックしてください。役に立たないなら除去してください。
49485270 </p>
49495271 ]]>
49505272 </Details>
49575279 <![CDATA[
49585280 <p>
49595281 この public または protected フィールドは書き込まれていません。このフィールドからの読み出しはデフォルト値を返します。
4960 誤りをチェックしてください (フィールドは初期化するべきでしたか?)。使わないなら除去してください。
5282 誤りをチェックしてください (フィールドは初期化するべきでしたか?)。役に立たないなら除去してください。
49615283 </p>
49625284 ]]>
49635285 </Details>
49705292 <![CDATA[
49715293 <p>
49725294 このフィールドは決して書き込まれません。このフィールドからの読み出しはデフォルト値を返します。
4973 誤りをチェックしてください (フィールドは初期化するべきでしたか?)。使わないなら除去してください。
5295 誤りをチェックしてください (フィールドは初期化するべきでしたか?)。役に立たないなら除去してください。
49745296 </p>
49755297 ]]>
49765298 </Details>
49835305 <![CDATA[
49845306 <p>
49855307 このインスタンスメソッドは、static フィールドに書き込みをしています。
4986 複数のインスタンスが操作されているなら、正しくさせるのは難しいです。一般的に間違ったプラクティスです。
5308 複数のインスタンスが操作されているなら、正しくさせるのは難しいです。一般的にバッドプラクティスです。
49875309 </p>
49885310 ]]>
49895311 </Details>
49965318 <![CDATA[
49975319 <p>
49985320 ここで参照されている変数は、以前に null なのかチェックしているため null であることがわかっています。
4999 これは有効かもしれないが、間違いかもしれません (多分異なる変数を参照することを意図してました、あるいは以前の null チェックで null でないのか確かめるべきでした)。
5321 これは有効ですが、間違いかもしれません (多分異なる変数を参照することを意図してました、または以前の null チェックで null でないのか確かめるべきでした)。
50005322 </p>
50015323 ]]>
50025324 </Details>
50765398 <![CDATA[
50775399 <p>
50785400 このクラスは内部クラスなのにそれを作成したオブジェクトへの埋め込まれた参照を使用していません。
5079 この参照はより大きなクラスのインスタンスを作成して、必要以上に長い間作成オブジェクトへの参照を存続しておくかもしれません。
5401 この参照はより大きなクラスのインスタンスを作成して、必要以上に作成オブジェクトへの参照を存続しておくことがあります。
50805402 できれば、クラスは static にすべきです。
50815403 </p>
50825404 ]]>
51045426 <![CDATA[
51055427 <p>
51065428 このクラスは内部クラスなのにそれを作成したオブジェクトへの埋め込まれた参照を使用していません。
5107 この参照はより大きなクラスのインスタンスを作成して、必要以上に長く作成オブジェクトへの参照を存続しておくかもしれません。
5429 この参照はより大きなクラスのインスタンスを作成して、必要以上に作成オブジェクトへの参照を存続しておくことがあります。
51085430 できれば、クラスは static 内部クラスにすべきです。
51095431 無名内部クラスは static にできないので、名前付き内部クラスにリファクタリングする必要があります。
51105432 </p>
51675489 </Details>
51685490 </BugPattern>
51695491
5492 <BugPattern type="UC_USELESS_VOID_METHOD">
5493 <ShortDescription>役に立たない空ではない void メソッド</ShortDescription>
5494 <LongDescription>メソッド {1} は、役に立たないようです。</LongDescription>
5495 <Details>
5496 <![CDATA[
5497 <p>
5498 我々の解析は、この空ではない void メソッドが実際に有用な仕事を行わないことを示しています。
5499 確認してください。おそらくそのコードが間違っているか、またはボディを完全に除去できます。
5500 </p>
5501 <p>
5502 我々はできる限り誤検出を減らそうと努力しているが、いくつかのケースでは警告は間違っているかもしれません。
5503 よくある誤検出例です。
5504 </p>
5505 <p>
5506 - メソッドは、副作用を持つかもしれないクラスのロードをトリガすることを意図している
5507 </p>
5508 <p>
5509 - メソッドは、暗黙のわかりにくい例外をスローするように意図されている
5510 </p>
5511 ]]>
5512 </Details>
5513 </BugPattern>
5514
5515 <BugPattern type="UC_USELESS_CONDITION">
5516 <ShortDescription>条件は効果がない</ShortDescription>
5517 <LongDescription>役に立たない条件: この時点 {2} で知られていました。</LongDescription>
5518 <Details>
5519 <![CDATA[
5520 <p>
5521 この条件は常に関係している変数の値が前に絞られたのと同じ結果を作り出します。
5522 おそらく何かほかのことを意味していたのか、あるいは条件を除去できます。
5523 </p>
5524 ]]>
5525 </Details>
5526 </BugPattern>
5527
5528 <BugPattern type="UC_USELESS_CONDITION_TYPE">
5529 <ShortDescription>条件は変数型のために効果がない</ShortDescription>
5530 <LongDescription>役に立たない条件: 変数型が {3} なので、常に {2} です。</LongDescription>
5531 <Details>
5532 <![CDATA[
5533 <p>
5534 この条件は関係している変数の型範囲のために常に同じ結果を作り出します。
5535 おそらく何かほかのことを意味していたのか、あるいは条件を除去できます。
5536 </p>
5537 ]]>
5538 </Details>
5539 </BugPattern>
5540
5541 <BugPattern type="UC_USELESS_OBJECT">
5542 <ShortDescription>役に立たないオブジェクトを作成した</ShortDescription>
5543 <LongDescription>役に立たないオブジェクトをメソッド {1} の変数 {2} で格納しました。</LongDescription>
5544 <Details>
5545 <![CDATA[
5546 <p>
5547 我々の解析でオブジェクトが役に立たないことを示しています。
5548 作成され、変更されていますが、値はメソッドの外に出ないし、副作用をもたらしません。
5549 間違いかオブジェクトが使われることを意図していたかのどちらか、あるいは除去できます。
5550 </p>
5551 <p>
5552 この解析はめったに誤検出することはありません。よくある誤検出のケースです。
5553 </p>
5554 <p>- 暗黙のうちに曖昧な例外をスローした</p>
5555 <p>- コードを一般化してスタブとして使用された</p>
5556 <p>- 弱/ソフト参照オブジェクトへの強い参照を持っていた</p>
5557 ]]>
5558 </Details>
5559 </BugPattern>
5560
5561 <BugPattern type="UC_USELESS_OBJECT_STACK">
5562 <ShortDescription>役に立たないオブジェクトをスタックで作成した</ShortDescription>
5563 <LongDescription>役に立たないオブジェクトをメソッド {1} で作成しました。</LongDescription>
5564 <Details>
5565 <![CDATA[
5566 <p>
5567 このオブジェクトは副作用を持たない修正を行うために作成されています。
5568 おそらく何かほかのことを意味していたのか、あるいはオブジェクトを除去できます。
5569 </p>
5570 ]]>
5571 </Details>
5572 </BugPattern>
5573
5574 <BugPattern type="RANGE_ARRAY_INDEX">
5575 <ShortDescription>配列インデックスは範囲外</ShortDescription>
5576 <LongDescription>配列インデックスは範囲外です: {3}</LongDescription>
5577 <Details>
5578 <![CDATA[
5579 <p>
5580 配列演算が行なわれますが、配列インデックスが範囲外なので実行時に ArrayIndexOutOfBoundsException が発生するでしょう。
5581 </p>
5582 ]]>
5583 </Details>
5584 </BugPattern>
5585
5586 <BugPattern type="RANGE_ARRAY_OFFSET">
5587 <ShortDescription>配列オフセットは範囲外</ShortDescription>
5588 <LongDescription>配列オフセットは範囲外です: {3}</LongDescription>
5589 <Details>
5590 <![CDATA[
5591 <p>
5592 メソッドは、配列パラメータとオフセットパラメータで呼び出されますが、オフセットは範囲外です。
5593 実行時に IndexOutOfBoundsException が発生するでしょう。
5594 </p>
5595 ]]>
5596 </Details>
5597 </BugPattern>
5598
5599 <BugPattern type="RANGE_ARRAY_LENGTH">
5600 <ShortDescription>配列の長さは範囲外</ShortDescription>
5601 <LongDescription>配列の長さは範囲外です: {3}</LongDescription>
5602 <Details>
5603 <![CDATA[
5604 <p>
5605 メソッドは、配列パラメータと長さパラメータで呼び出されますが、長さは範囲外です。
5606 実行時に IndexOutOfBoundsException が発生するでしょう。
5607 </p>
5608 ]]>
5609 </Details>
5610 </BugPattern>
5611
5612 <BugPattern type="RANGE_STRING_INDEX">
5613 <ShortDescription>文字列インデックスは範囲外</ShortDescription>
5614 <LongDescription>文字列インデックスは {5} を呼び出すときに範囲外です: {3}</LongDescription>
5615 <Details>
5616 <![CDATA[
5617 <p>
5618 文字列メソッドが呼び出されます。指定された文字列インデックスは範囲外です。
5619 実行時に StringIndexOutOfBoundsException が発生するでしょう。
5620 </p>
5621 ]]>
5622 </Details>
5623 </BugPattern>
5624
51705625 <BugPattern type="RV_CHECK_FOR_POSITIVE_INDEXOF">
51715626 <ShortDescription>String.indexOf の結果が正かどうか確かめている</ShortDescription>
51725627 <LongDescription>String.indexOf の結果が正かどうか確かめています。{1}</LongDescription>
51945649 </Details>
51955650 </BugPattern>
51965651
5652 <BugPattern type="RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT">
5653 <ShortDescription>副作用がないメソッドの戻り値は無視される</ShortDescription>
5654 <LongDescription>副作用がない {2.givenClass} の戻り値は無視されます。</LongDescription>
5655 <Details>
5656 <![CDATA[
5657 <p>
5658 このコードは、メソッドを呼び出して戻り値を無視しています。
5659 しかしながら、解析はメソッド (もしあればサブクラスの実装も含む) が戻り値以外の効果をもたらさないことを示しています。
5660 この呼び出しは除去できます。
5661 </p>
5662 <p>
5663 我々は、できる限り誤検出を減らそうとしていますが、いくつかのケースではこの警告が間違っているかもしれません。
5664 よくある誤検出です。
5665 </p>
5666 <p>
5667 - メソッドは、オーバライドされ解析対象外の他のプロジェクトで副作用がもたらされるように設計されている
5668 <p>
5669 <p>
5670 - メソッドは、副作用をもたらすかもしれないクラスローダをトリガーするように呼び出されている
5671 </p>
5672 <p>
5673 - メソッドは、例外を取得するために呼び出されている
5674 </p>
5675 <p>
5676 我々の仮定が正しくないと感じるなら、FindBugs にこのメソッドの戻り値が無視されることを許容するように指示する @CheckReturnValue アノテーションを使用することができます。
5677 </p>
5678 ]]>
5679 </Details>
5680 </BugPattern>
5681
51975682 <BugPattern type="RV_RETURN_VALUE_IGNORED_INFERRED">
51985683 <ShortDescription>メソッドは戻り値を無視しています、これは間違いではないですか?</ShortDescription>
51995684 <LongDescription>{2.givenClass} の戻り値を無視しています、これは間違いではないですか? {1}</LongDescription>
52085693 このメソッドの戻り値を無視することが重要であるか許容できるかどうかに関して、FindBugs に指示する @CheckReturnValue アノテーションが使えます。
52095694 </p>
52105695 <p>
5211 戻り値を無視することが間違いではないか決めるために、厳密に調査してください。
5696 戻り値を無視することが間違いではないか決めるために厳密に調査してください。
52125697 </p>
52135698 ]]>
52145699 </Details>
52695754 ]]>
52705755 </Details>
52715756 </BugPattern>
5272
5757
52735758 <BugPattern type="RV_EXCEPTION_NOT_THROWN">
52745759 <ShortDescription>作成した例外をスローするのではなく捨てている</ShortDescription>
52755760 <LongDescription>{2.givenClass} をスローしていません。{1}</LongDescription>
53245809 </BugPattern>
53255810
53265811 <BugPattern type="NP_STORE_INTO_NONNULL_FIELD">
5327 <ShortDescription>@NonNull でアノテートされたフィールドに null を格納している</ShortDescription>
5328 <LongDescription>@NonNull でアノテートされたフィールド {2.givenClass} に null を格納しています。{1}</LongDescription>
5329 <Details>
5330 <![CDATA[
5331 <p>
5332 @NonNull でアノテートされたフィールドに null かもしれない値を格納しています。
5812 <ShortDescription>@Nonnull でアノテートされたフィールドに null を格納している</ShortDescription>
5813 <LongDescription>@Nonnull でアノテートされたフィールド {2.givenClass} に null を格納しています。{1}</LongDescription>
5814 <Details>
5815 <![CDATA[
5816 <p>
5817 @Nonnull としてアノテートされたフィールドに null かもしれない値を格納しています。
53335818 </p>
53345819 ]]>
53355820 </Details>
53425827 <![CDATA[
53435828 <p>
53445829 例外経路上のここで null 値を利用しています。コードが実行されると NullPointerException が発生します。
5345 現在の FindBugs は実行不可能な例外経路を取り除いていないので、誤検出かもしれないことに注意してください。
5830 現在の FindBugs は実行不可能な例外経路を刈り取れていないので、誤検出かもしれないことに注意してください。
53465831 </p>
53475832 <p>
53485833 switch 文の default が多くの場合実行不可能なので FindBugs が例外経路である default を検討することに注意して下さい。
53535838
53545839 <BugPattern type="NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE">
53555840 <ShortDescription>パラメータは 非 null でなければならないが null 可能としてアノテートされている</ShortDescription>
5356 <LongDescription>{2} は、非 null でなければならないが、null 可能としてアノテートされています。</LongDescription>
5841 <LongDescription>{2} は、非 null でなければならないが null 可能としてアノテートされています。</LongDescription>
53575842 <Details>
53585843 <![CDATA[
53595844 <p>
53635848 ]]>
53645849 </Details>
53655850 </BugPattern>
5366
5851
53675852 <BugPattern type="NP_NULL_ON_SOME_PATH">
53685853 <ShortDescription>null 値を利用している可能性がある</ShortDescription>
53695854 <LongDescription>{2.givenClass} の null 値を利用している可能性があります。{1}</LongDescription>
53875872 分岐または文が実行されるなら、null 値が利用されて NullPointerException が発生します。
53885873 もちろん、問題は分岐または文が実行不可能で、NullPointerException が決して発生する可能性がないということかもしれません。
53895874 それを決めるのは FindBugs の能力を超えています。
5390 この値がすでに null であることを検査したという事実からこれは明確な可能性です。
5875 この値が既に null であることを検査したという事実からこれは明確な可能性です。
53915876 </p>
53925877 ]]>
53935878 </Details>
53995884 <Details>
54005885 <![CDATA[
54015886 <p>
5402 例外経路上のここで null 値が利用されています。コードが実行されると NullPointerException が発生するかもしれません。
5403 現在の FindBugs は実行不可能な例外経路を取り除かないので、誤検出かもしれないことに注意してください。
5887 例外経路上のここで null 値が利用されています。コードが実行されると NullPointerException を引き起こすことがあります。
5888 現在の FindBugs は実行不可能な例外経路を刈り取れていないので、誤検出かもしれないことに注意してください。
54045889 </p>
54055890 <p>
54065891 switch 文の default が多くの場合実行不可能なので FindBugs が例外経路である default を検討することに注意して下さい。
54165901 <![CDATA[
54175902 <p>
54185903 メソッドからの戻り値を null チェックしないで利用しています。メソッドの戻り値は null なのかチェックするべきです。
5419 コードが実行されると NullPointerException が発生するかもしれません。
5904 コードが実行されると NullPointerException を引き起こすことがあります。
54205905 </p>
54215906 ]]>
54225907 </Details>
54285913 <Details>
54295914 <![CDATA[
54305915 <p>
5431 null の可能性がある値が、非 null メソッドパラメータに渡されています。
5916 null の可能性がある値が 非 null メソッドパラメータに渡されています。
54325917 パラメータは、常に非 null とすべきパラメータとしてアノテートされていたか、または解析が常に null 値を利用すると示していました。
54335918 </p>
54345919 ]]>
54755960 </BugPattern>
54765961
54775962 <BugPattern type="NP_NONNULL_RETURN_VIOLATION">
5478 <ShortDescription>null を返すかもしれないメソッドが @NonNull 宣言されている</ShortDescription>
5479 <LongDescription>{1} は、null を返すかもしれないのに @NonNull 宣言されています。</LongDescription>
5480 <Details>
5481 <![CDATA[
5482 <p>
5483 このメソッドは、null 値を返すかもしれないのにメソッド (またはスーパークラスのメソッド) の戻り値に @NonNull が宣言されています。
5963 <ShortDescription>null を返すかもしれないメソッドが @Nonnull 宣言されている</ShortDescription>
5964 <LongDescription>{1} は、null を返すかもしれないのに @Nonnull 宣言されています。</LongDescription>
5965 <Details>
5966 <![CDATA[
5967 <p>
5968 このメソッドは、null 値を返すかもしれないのにメソッド (またはスーパークラスのメソッド) の戻り値に @Nonnull が宣言されています。
54845969 </p>
54855970 ]]>
54865971 </Details>
55476032 <Details>
55486033 <![CDATA[
55496034 <p>
5550 すべての static final フィールドが初期化される前に、スタティックイニシャライザがクラスのインスタンスを作成します。
6035 すべての static final フィールドが初期化される前にスタティックイニシャライザがクラスのインスタンスを作成します。
55516036 </p>
55526037 ]]>
55536038 </Details>
55606045 <![CDATA[
55616046 <p>
55626047 このメソッドは、入出力ストリームオブジェクトを作成していますが、どんなフィールドにも代入していないしクローズするかもしれない別のメソッドにも渡していなくて、戻り値にしてもいません。そして、メソッドからのすべての経路でクローズするように見えません。
5563 これは、ファイルディスクリプタリークになるかもしれません。
5564 ストリームがクローズされることを確実にするために <code>finally</code> ブロックを使うことは、一般に良い考えです。
6048 これはファイルディスクリプタリークの原因になることがあります。
6049 ストリームがクローズされることを確実にするために <code>finally</code> ブロックを使用することは一般的に良い考えです。
55656050 </p>
55666051 ]]>
55676052 </Details>
55746059 <![CDATA[
55756060 <p>
55766061 このメソッドは、入出力ストリームオブジェクトを作成していますが、どんなフィールドにも代入していないしクローズするかもしれない別のメソッドにも渡していないくて、戻り値にしてもいません。そして、メソッドからのすべての可能性がある例外経路でクローズするように見えません。
5577 これはファイルディスクリプターリークになるかもしれません。
5578 ストリームがクローズされることを確実にするために <code>finally</code> ブロックを使うことは、一般に良い考えです。
6062 これはファイルディスクリプターリークの原因になることがあります。
6063 ストリームがクローズされることを確実にするために <code>finally</code> ブロックを使用することは一般的に良い考えです。
55796064 </p>
55806065 ]]>
55816066 </Details>
55906075 結果がないこと (すなわち、結果の空のリスト) を示すために null 参照ではなく長さが0の配列 を返すことは、多くの場合より良い設計です。
55916076 </p>
55926077 <p>
5593 他方では、「この質問に対する答えがない」ことを示すために、null を使うことはおそらく適切です。
6078 他方では、「この質問に対する答えがない」ことを示すために null を使用することはおそらく適切です。
55946079 たとえば、<code>File.listFiles()</code> は、ファイルがないディレクトリを与えられた場合は空のリストを返し、ファイルがディレクトリでないなら null を返します。
55956080 </p>
55966081 ]]>
56336118 </BugPattern>
56346119
56356120 <BugPattern type="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE">
5636 <ShortDescription>すでに利用していた値の null チェック</ShortDescription>
5637 <LongDescription>すでに利用していた {2.givenClass} の値を {4.lineNumber} で null チェックしています。{1}</LongDescription>
5638 <Details>
5639 <![CDATA[
5640 <p>
5641 ここで値が null なのかチェックしていますが、すでに値を利用していたので null である可能性はありません。
6121 <ShortDescription>既に利用していた値の null チェック</ShortDescription>
6122 <LongDescription>既に利用していた {2.givenClass} の値を {4.lineNumber} で null チェックしています。{1}</LongDescription>
6123 <Details>
6124 <![CDATA[
6125 <p>
6126 ここで値が null なのかチェックしていますが、既に値を利用していたので null である可能性はありません。
56426127 値が null なら以前の利用で NullPointerException が発生していたでしょう。
56436128 基本的に、値が null であることを許すのかどうかに関係なく、このコードと以前の値の利用は一致しません。
56446129 チェックは冗長か、または以前の値の利用は誤りです。
57236208 <![CDATA[
57246209 <p>
57256210 このメソッドは、JSR-166(<code>java.util.concurrent</code>) のロックを獲得していますが、メソッドからのすべての経路で解除していません。
5726 一般に、JSR-166のロックを使用するための正しいイディオムは以下のようになります。
6211 一般的に JSR-166のロックを使用するための正しいイディオムは以下のようになります。
57276212 </p>
57286213 <blockquote><pre>
57296214 Lock l = ...;
57456230 <![CDATA[
57466231 <p>
57476232 このメソッドは、JSR-166(<code>java.util.concurrent</code>) のロックを獲得していますが、メソッドからのすべての例外経路で解除していません。
5748 一般に、JSR-166のロックを使用するための正しいイディオムは以下のようになります。
6233 一般的に JSR-166のロックを使用するための正しいイディオムは以下のようになります。
57496234 </p>
57506235 <blockquote><pre>
57516236 Lock l = ...;
57676252 <![CDATA[
57686253 <p>
57696254 このメソッドは、 == または != 演算子を使用して2つの参照値を比較しています。
5770 この型のインスタンスを比較する正しい方法は、一般に <code>equals</code> メソッドです。
6255 一般的にこの型のインスタンスを比較する正しい方法は <code>equals</code> メソッドです。
57716256 等価で識別可能なインスタンスを作成する可能性がありますが異なるオブジェクトなので == で比較しないでください。
57726257 参照によって一般に比較されるべきでないクラスの例は、<code>java.lang.Integer</code> 、<code>java.lang.Float</code> などです。
57736258 </p>
57826267 <![CDATA[
57836268 <p>
57846269 このメソッドは、 参照値を == または != 演算子を使用して定数と比較しています。
5785 この型のインスタンスを比較する正しい方法は、一般に <code>equals</code> メソッドです。
6270 一般的にこの型のインスタンスを比較する正しい方法は <code>equals</code> メソッドです。
57866271 等価で識別可能なインスタンスを作成する可能性がありますが異なるオブジェクトなので == で比較しないでください。
5787 参照によって一般に比較されるべきではないクラスの例は、<code>java.lang.Integer</code> 、<code>java.lang.Float</code> などです。
6272 一般的に参照によって比較されるべきではないクラスの例は、<code>java.lang.Integer</code> 、<code>java.lang.Float</code> などです。
57886273 </p>
57896274 ]]>
57906275 </Details>
57976282 <![CDATA[
57986283 <p>
57996284 このメソッドは、== または != 演算子を使用して2つの Boolean 値を比較しています。
5800 一般には2つの Boolean 値 (<code>Boolean.TRUE</code> と <code>Boolean.FALSE</code>) だけですが、
6285 一般的には2つの Boolean 値 (<code>Boolean.TRUE</code> と <code>Boolean.FALSE</code>) だけですが、
58016286 <code>new Boolean(b)</code> コンストラクタを使用して他の Boolean オブジェクトを作成する可能性があります。
58026287 そのようなオブジェクトを回避することは最高です。
58036288 しかし、それらが存在するなら、Boolean オブジェクトの等価性をチェックするために <code>.equals(...)</code> ではなく == または != を使用しているなら異なる結果をもたらします。
58256310 <Details>
58266311 <![CDATA[
58276312 <p>
5828 このメソッドは、共通のサブクラスがない異なるクラス型の2つのオブジェクト参照で <code>equals(Object)</code> メソッドを呼び出しています。
5829 したがって比較されている2つのオブジェクトは実行時に同じクラスのメンバである可能性が低いです (いくつかのアプリケーションクラスが解析できなかったか、動的クラスローディングが実行時に起こることができた場合を除く)。
5830 <code>equals</code> メソッドの規約によると、異なるクラスのオブジェクトは常に不等として比較するべきです。
5831 したがって、<code>java.lang.Object.equals(Object)</code> によって定義される規約によれば、この比較の結果は実行時に常に false になります。
6313 このメソッドは、異なるクラス型の2つの参照で <code>equals(Object)</code> メソッドを呼び出していて、解析が実行時に異なるクラスのオブジェクトになることを示唆しています。
6314 さらに、呼び出されるであろう equals メソッドの検査では、この呼び出しは常に false を返すか、
6315 あるいは equals メソッドが対称 (Object クラスの equals のための契約に必要な性質) ではないことのどちらかを示唆しています。
58326316 </p>
58336317 ]]>
58346318 </Details>
58436327 このメソッドは、どちらも他方のサブタイプでない無関係なインタフェース型の2つの参照で <code>equals(Object)</code> メソッドを呼び出しています。
58446328 そして、両方のインタフェースを実装する既知の非抽象クラスがありません。
58456329 したがって比較されている2つのオブジェクトは実行時に同じクラスのメンバである可能性が低いです (いくつかのアプリケーションクラスが解析できなかったか、動的クラスローディングが実行時に起こることができた場合を除く)。
5846 <code>equals</code> メソッドの規約によると、異なるクラスのオブジェクトは常に不等として比較するべきです。
6330 <code>equals</code> メソッドの規約によると、異なるクラスのオブジェクトは常に等しくないとして比較するべきです。
58476331 したがって、<code>java.lang.Object.equals(Object)</code> によって定義される規約によれば、この比較の結果は実行時に常に false になります。
58486332 </p>
58496333 ]]>
58596343 このメソッドは、一方がクラスで他方がインタフェースである2つの参照で <code>equals(Object)</code> メソッドを呼び出しています。
58606344 クラスは、そのクラスの非抽象サブクラスも含めてインタフェースを実装していません。
58616345 したがって比較されている2つのオブジェクトは実行時に同じクラスのメンバである可能性が低いです (いくつかのアプリケーションクラスが解析できなかったか、動的クラスローディングが実行時に起こることができた場合を除く)。
5862 <code>equals</code> メソッドの規約によると、異なるクラスのオブジェクトは常に不等として比較するべきです。
6346 <code>equals</code> メソッドの規約によると、異なるクラスのオブジェクトは常に等しくないとして比較するべきです。
58636347 したがって、<code>java.lang.Object.equals(Object)</code> によって定義される規約によれば、この比較の結果は実行時に常に false になります。
58646348 </p>
58656349 ]]>
60946578 ]]>
60956579 </Details>
60966580 </BugPattern>
6097
6581
60986582 <BugPattern type="DMI_RANDOM_USED_ONLY_ONCE">
60996583 <ShortDescription>Random オブジェクトが作成され1度しか使われない</ShortDescription>
61006584 <LongDescription>Random オブジェクトが生成され、1度しか使われていません。{1}</LongDescription>
61046588 このコードは、<code>java.util.Random</code> オブジェクトを作成して1つの乱数を生成するために使用して捨てています。
61056589 これはあまり良くない品質の乱数を作り出し、効率が悪いです。
61066590 できれば、<code>Random</code> オブジェクトを1つだけ作成して保存されるようにコードを書き直してください。
6107 そして、新しい乱数が必要なたびに既存の <code>Random</code> オブジェクトでメソッドを呼び出してください。
6591 そして、毎回新しい乱数は既存の <code>Random</code> オブジェクトを呼び出して取得することが必要です。
61086592 </p>
61096593 <p>
61106594 生成された乱数が推測可能でないことが重要なら、乱数ごとに新しい <code>Random</code> オブジェクトを作成してはいけません (値はあまりに簡単に推測可能です)。
61516635 <![CDATA[
61526636 <p>
61536637 このコードは符号付き整数の乱数を生成して別の値を法とする剰余を計算しています。
6154 乱数が負かもしれないので、剰余演算の結果も負かもしれません。これが意図したことであることを確実にしてください。
6638 乱数は負になり、剰余演算の結果も負になります。これが意図したことであることを確実にしてください。
61556639 その代わりに <code>Random.nextInt(int)</code> の使用を強く検討してください。
61566640 </p>
61576641 ]]>
61656649 <![CDATA[
61666650 <p>
61676651 このコードは、ハッシュコードを計算して別の値を法とする剰余を計算しています。
6168 ハッシュコードが負かもしれないので、剰余演算の結果も負かもしれません。
6652 ハッシュコードは負になり、剰余演算の結果も負なります。
61696653 </p>
61706654 <p>
61716655 計算結果が負ではないことを確認したいなら、コードを変更する必要があるかもしれません。
61786662 </BugPattern>
61796663
61806664 <BugPattern type="INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE">
6181 <ShortDescription>負でない値と負の定数との間違った比較</ShortDescription>
6665 <ShortDescription>負でない値と負の定数またはゼロとの間違った比較</ShortDescription>
61826666 <LongDescription>負でない値 と {2} との間違った比較です。{1}</LongDescription>
61836667 <Details>
61846668 <![CDATA[
61856669 <p>
6186 このコードは、負でないことが保証されている値と負の定数とを比較しています。
6670 このコードは、負でないことが保証されている値と負の定数またはゼロとを比較しています。
61876671 </p>
61886672 ]]>
61896673 </Details>
62086692 <Details>
62096693 <![CDATA[
62106694 <p>
6211 このコードは、int 値と int 値として表現できる値の範囲外にある long 定数を比較しています。
6695 このコードは、int 値と int 値として表される値の範囲外の long 定数を比較しています。
62126696 この比較は無意味で、おそらく間違っています。
62136697 </p>
62146698 ]]>
62156699 </Details>
62166700 </BugPattern>
6217
6701
62186702 <BugPattern type="INT_VACUOUS_BIT_OPERATION">
62196703 <ShortDescription>整数値の無意味なビットマスク演算</ShortDescription>
62206704 <LongDescription>{3} の無意味な {2} 演算です。{1}</LongDescription>
62216705 <Details>
62226706 <![CDATA[
62236707 <p>
6224 これはどんな有効な働きもしない整数ビット演算 (AND、OR、XOR) です (たとえば <code>v & 0xffffffff</code>)。
6708 どんな有用な仕事もしない整数ビット演算 (AND、OR、XOR) です (たとえば <code>v & 0xffffffff</code>)。
62256709 </p>
62266710 ]]>
62276711 </Details>
63186802
63196803 <BugPattern type="BIT_AND">
63206804 <ShortDescription>互換性のないビットマスク</ShortDescription>
6321 <LongDescription>(e &amp; {2} == {3}) の互換性のないビットマスクは、一定の結果をもたらします。{1}</LongDescription>
6805 <LongDescription>(e &amp; {2} == {3}) の互換性のないビットマスクは、不変の結果をもたらします。{1}</LongDescription>
63226806 <Details>
63236807 <![CDATA[
63246808 <p>
63256809 このメソッドは、<i>(e &amp; C)</i> 形式の式を <i>D</i> と比較しています。
6326 定数 <i>C</i> と <i>D</i> の特定の値と常に等しくないことを比較します。論理エラーかタイプミスかもしれません。
6810 定数 <i>C</i> の特定の値と <i>D</i> ために常に等しくないことを比較します。論理エラーかタイプミスかもしれません。
63276811 </p>
63286812 ]]>
63296813 </Details>
63386822 このメソッドは、<code>((event.detail &amp; SWT.SELECTED) &gt; 0)</code> のような式を比較しています。
63396823 ビット演算をより大きい演算子で比較することは、予想外の結果 (もちろん、<code>SWT.SELECTED</code> の値による) の原因になる可能性があります。
63406824 <code>SWT.SELECTED</code> が負数であるなら、これはバグの候補です。
6341 <code>SWT.SELECTED</code> が負ではないとしても、'&gt; 0' の代わりに '!= 0' を使うことは良いプラクティスと思われます。
6825 <code>SWT.SELECTED</code> が負ではないとしても、'&gt; 0' の代わりに '!= 0' を使用することは良いプラクティスと思われます。
63426826 </p>
63436827 <p>
63446828 Boris Bokowski
63566840 このメソッドは、<code>((event.detail &amp; SWT.SELECTED) &gt; 0)</code> のような式を比較しています。
63576841 ビット演算をより大きい演算子で比較することは、予想外の結果 (もちろん、<code>SWT.SELECTED</code> の値による) の原因になる可能性があります。
63586842 <code>SWT.SELECTED</code> が負数であるなら、これはバグの候補です。
6359 <code>SWT.SELECTED</code> が負ではないとしても、'&gt; 0' の代わりに '!= 0' を使うことは良いプラクティスと思われます。
6843 <code>SWT.SELECTED</code> が負ではないとしても、'&gt; 0' の代わりに '!= 0' を使用することは良いプラクティスと思われます。
63606844 </p>
63616845 <p>
63626846 Boris Bokowski
63796863
63806864 <BugPattern type="BIT_IOR">
63816865 <ShortDescription>互換性のないビットマスク</ShortDescription>
6382 <LongDescription>(e | {2} == {3}) の互換性のないビットマスクは、一定の結果をもたらします。{1}</LongDescription>
6866 <LongDescription>(e | {2} == {3}) の互換性のないビットマスクは、不変の結果をもたらします。{1}</LongDescription>
63836867 <Details>
63846868 <![CDATA[
63856869 <p>
63866870 このメソッドは、<code>(e | C)</code> 形式の式を <code>D</code> と比較しています。
6387 定数 <i>C</i> と <i>D</i> の特定の値と常に等しくないことを比較します。論理エラーかタイプミスかもしれません。
6871 定数 <i>C</i> の特定の値と <i>D</i> のために常に等しくないことを比較します。論理エラーかタイプミスかもしれません。
63886872 </p>
63896873 <p>
63906874 典型的に、このバグは、ビットセットで帰属関係のテストを実行したいコードで発生します。
64186902 <![CDATA[
64196903 <p>
64206904 このメソッドにはvolatile でない static フィールドの非同期な遅延初期化があります。
6421 コンパイラやプロセッサが命令を並べ替えるかもしれないので、メソッドが複数のスレッドによって呼び出されるかもしれないなら、
6905 コンパイラやプロセッサが命令を並べ替えるかもしれないので、メソッドが複数のスレッドによって呼び出されるなら、
64226906 スレッドは完全に初期化されたオブジェクトを見るとは保証されていません。
64236907 フィールドにアクセスした際に、中途半端に初期化されたインスタンスが見えてしまう危険があります。
6424 この問題を修正するために、フィールドを volatile にできます。<br>
6908 この問題を修正するためにフィールドを volatile にできます。<br>
64256909 詳細は、<a href="http://www.cs.umd.edu/~pugh/java/memoryModel/">Java Memory Model web site</a> を参照してください。
64266910 </p>
64276911 ]]>
64366920 <p>
64376921 このメソッドにはstatic フィールドの非同期な遅延初期化があります。
64386922 フィールドが設定された後で、その場所に格納されるオブジェクトはさらに更新されるかアクセスされます。
6439 それが設定されるとすぐに、フィールドの設定は他のスレッドに見えます。
6923 それが設定されるとすぐに、フィールドを設定することは他のスレッドに見えます。
64406924 フィールドを設定するさらなるアクセスがオブジェクトを初期化するのに役に立つなら、
64416925 それが完全に初期化されるまでどんな他のスレッドも格納されたオブジェクトにアクセスするのを防がないかぎり、非常に深刻なマルチスレッドバグがあります。
64426926 </p>
64646948 <BugPattern type="JML_JSR166_CALLING_WAIT_RATHER_THAN_AWAIT">
64656949 <ShortDescription>util.concurrent 抽象でモニタスタイルの wait メソッドを使用している</ShortDescription>
64666950 <LongDescription>{3.name} ではなく {2.name} を呼び出しています。{1}</LongDescription>
6467 <Details>
6951 <Details>
64686952 <![CDATA[
64696953 <p>
64706954 このメソッドは、<code>await()</code> メソッド、<code>signal</code> メソッド、<code>signalAll</code> メソッドを提供するオブジェクト
65126996 <![CDATA[
65136997 <p>
65146998 この無名クラスは、直接呼び出されないスーパークラスのメソッドをオーバーライドしていないメソッドを定義しています。
6515 他のクラスのメソッドが無名クラスで宣言されたメソッドを直接呼び出せないので、このメソッドは呼び出し不可能であると思われます。
6999 他のクラスのメソッドが無名クラスで宣言されたメソッドを直接呼び出せないので、このメソッドは呼び出し不可能だと思われます。
65167000 メソッドは単にデッドコードであるかもしれません。しかし、メソッドがスーパークラスで宣言されるメソッドをオーバーライドすることを意図した可能性もあります。
6517 そして、タイプミスまたは他の誤りのために、メソッドは、実際、それが意図されるメソッドをオーバーライドしません。
7001 そして、タイプミスまたは他の誤りのためにメソッドは、実際には意図しているメソッドをオーバーライドしません。
65187002 </p>
65197003 ]]>
65207004 </Details>
65287012 <p>
65297013 このメソッドは、データベースリソース (たとえば、データベースコネクションや行セット) を作成していますが、どんなフィールドにも代入していないか、他のメソッドにも渡していないか、戻り値にもしていません。
65307014 そして、メソッドからのすべての経路でオブジェクトをクローズするように見えません。
6531 メソッドからのすべての経路でデータベースリソースのクローズ失敗は良くない性能になるかもしれません。
7015 メソッドからのすべての経路でデータベースリソースのクローズが失敗すると性能低下になることがあります。
65327016 データベースとの通信で問題があるアプリケーションの原因になる可能性があります。
65337017 </p>
65347018 ]]>
65437027 <p>
65447028 このメソッドは、データベースリソース (たとえば、データベースコネクションや行セット) を作成していますが、どんなフィールドにも代入していないか、他のメソッドにも渡していないか、戻り値にもしていません。
65457029 そして、メソッドからのすべての例外経路でオブジェクトをクローズするように見えません。
6546 メソッドからのすべての経路でデータベースリソースのクローズ失敗は良くない性能になるかもしれません。
7030 メソッドからのすべての経路でデータベースリソースのクローズが失敗すると性能低下になることがあります。
65477031 データベースとの通信で問題があるアプリケーションの原因になる可能性があります。
65487032 </p>
65497033 ]]>
65617045 各々の繰り返しで文字列が再コピーされ、増大すると繰り返しの数で二次コストの原因になる可能性があります。
65627046 </p>
65637047 <p>
6564 明示的に、<code>StringBuffer</code> (または J2SE 5.0の <code>StringBuilder</code>) を使うことで、より良い性能を得られるかもしれません。
7048 <code>StringBuffer</code> (または J2SE 5.0の <code>StringBuilder</code>) を明示的に使うとより良い性能を得られます。
65657049 </p>
65667050 <p>
65677051 たとえば、
65847068 </Details>
65857069 </BugPattern>
65867070
7071 <BugPattern type="IIL_PREPARE_STATEMENT_IN_LOOP">
7072 <ShortDescription>ループの中で prepareStatement を呼び出しているメソッド</ShortDescription>
7073 <LongDescription>{1} は、ループの中で不変の引数で prepareStatement を呼び出しています。</LongDescription>
7074 <Details>
7075 <![CDATA[
7076 <p>
7077 メソッドは、ループの中で <code>Connection.prepareStatement</code> に不変の引数を渡して呼び出しています。
7078 <code>PreparedStatement</code> を複数回実行する必要があるなら、それぞれのループの繰り返しで再生成する理由がありません。
7079 ループの外に呼び出しを移動します。
7080 </p>
7081 ]]>
7082 </Details>
7083 </BugPattern>
7084 <BugPattern type="IIL_ELEMENTS_GET_LENGTH_IN_LOOP">
7085 <ShortDescription>ループの中で NodeList.getLength() を呼び出しているメソッド</ShortDescription>
7086 <LongDescription>{1} は、ループの中で getElementsByTagName の戻り値のために NodeList.getLength() を呼び出しています。</LongDescription>
7087 <Details>
7088 <![CDATA[
7089 <p>
7090 メソッドは、ループの中で <code>NodeList.getLength()</code> を呼び出し、<code>NodeList</code> は、<code>getElementsByTagName</code> の呼び出しによって作られます。
7091 <code>NodeList</code> は長さを格納しませんが、毎回とても最適ではない方法で計算されます。
7092 ループの前に変数に長さを格納することを検討してください。
7093 </p>
7094 ]]>
7095 </Details>
7096 </BugPattern>
7097 <BugPattern type="IIL_PATTERN_COMPILE_IN_LOOP">
7098 <ShortDescription>ループの中で Pattern.compile を呼び出しているメソッド</ShortDescription>
7099 <LongDescription>{1} は、ループの中で不変の定数で Pattern.compile を呼び出しています。</LongDescription>
7100 <Details>
7101 <![CDATA[
7102 <p>
7103 メソッドは、ループの中で <code>Pattern.compile</code> に不変の定数を渡して呼び出しています。
7104 <code>Pattern</code> を複数回使用する必要があるなら、それぞれのループの繰り返しでコンパイルする理由がありません。
7105 ループの外に呼び出しを移動するか static final フィールドにします。
7106 </p>
7107 ]]>
7108 </Details>
7109 </BugPattern>
7110
7111 <BugPattern type="IIL_PATTERN_COMPILE_IN_LOOP_INDIRECT">
7112 <ShortDescription>ループの中で正規表現をコンパイルしているメソッド</ShortDescription>
7113 <LongDescription>{1} は、ループの中で正規表現のコンパイルをしています。</LongDescription>
7114 <Details>
7115 <![CDATA[
7116 <p>
7117 メソッドは、ループの中で同じ正規表現を生成しているので、繰り返しごとにコンパイルされます。
7118 ループの外で <code>Pattern.compile</code> を使用して正規表現をプリコンパイルするのが最適でしょう。
7119 </p>
7120 ]]>
7121 </Details>
7122 </BugPattern>
7123
7124 <BugPattern type="IIO_INEFFICIENT_INDEX_OF">
7125 <ShortDescription>String.indexOf(String) の非効率的な使用</ShortDescription>
7126 <LongDescription>{1} は、String.indexOf(int) の代わりに String.indexOf(String) を使用しています。</LongDescription>
7127 <Details>
7128 <![CDATA[
7129 <p>
7130 このコードは、String.indexOf() に長さ1の文字列定数を渡しています。String.indexOf() の整数実装を使うほうが効率的です。
7131 たとえば、<code>myString.indexOf(".")</code> の代わりに <code>myString.indexOf('.')</code> を呼び出します。
7132 </p>
7133 ]]>
7134 </Details>
7135 </BugPattern>
7136
7137 <BugPattern type="IIO_INEFFICIENT_LAST_INDEX_OF">
7138 <ShortDescription>String.lastIndexOf(String) の非効率的な使用</ShortDescription>
7139 <LongDescription>{1} は、String.lastIndexOf(int) の代わりに String.lastIndexOf(String) を使用しています。</LongDescription>
7140 <Details>
7141 <![CDATA[
7142 <p>
7143 このコードは、String.lastIndexOf() に長さ1の文字列定数を渡しています。String.lastIndexOf() の整数実装を使うほうが効率的です。
7144 たとえば、<code>myString.lastIndexOf(".")</code> の代わりに <code>myString.lastIndexOf('.')</code> を呼び出します。
7145 </p>
7146 ]]>
7147 </Details>
7148 </BugPattern>
7149
65877150 <BugPattern type="ITA_INEFFICIENT_TO_ARRAY">
65887151 <ShortDescription>長さが0の配列の引数で toArray メソッドを使用しているメソッド</ShortDescription>
65897152 <LongDescription>{1} は、長さが0の配列の引数で Collection.toArray() を使用しています。</LongDescription>
65937156 このメソッドは、Collection 派生クラスの </code>toArray</code> メソッドを使用して長さが0の配列の引数を渡しています。
65947157 <code>myCollection.toArray(new Foo[myCollection.size()])</code> を使用するほうがより効率的です。
65957158 渡される配列がコレクションの要素のすべてを格納できるくらいの大きさなら、データが読み込まれて、そのまま返されます。
6596 これは、結果として返す2番目の配列 (リフレクションによって) を作成する必要を回避します。
7159 これは結果として返す2番目の配列 (リフレクションによって) を作成する必要を回避します。
65977160 </p>
65987161 ]]>
65997162 </Details>
67387301
67397302 <BugPattern type="SIO_SUPERFLUOUS_INSTANCEOF">
67407303 <ShortDescription>instanceof 演算子を使用した不必要な型チェック</ShortDescription>
6741 <LongDescription>{1} は、静的に確定している可能性があるのに instanceof 演算子を使用して不必要な型チェックをしています。</LongDescription>
6742 <Details>
6743 <![CDATA[
6744 <p>
6745 オブジェクトが要求する型であるかどうかにかかわらず、静的に確定している可能性があるのに instanceof 演算子を使用して型チェックをしています。
7304 <LongDescription>{1} は、静的に判定される instanceof 演算子を使用して不必要な型チェックをしています。</LongDescription>
7305 <Details>
7306 <![CDATA[
7307 <p>
7308 オブジェクトが要求する型であるかどうかにかかわらず、静的に判定される instanceof 演算子を使用して型チェックをしています。
67467309 </p>
67477310 ]]>
67487311 </Details>
67677330 <Details>
67687331 <![CDATA[
67697332 <p>
6770 このメソッドは、配列と配列であると思われない参照を比較するために <code>.equals(Object o)</code> を呼び出しています。
6771 比較されているものが違う型なら不等であることが保証されているので、比較はほぼ間違いなく誤りです。
6772 たとえそれらが両方とも配列であるとしても、配列の <code>equals</code> メソッドは2つの配列が同じオブジェクトであると決定するだけです。
7333 このメソッドは、配列と配列だと思われない参照を比較するために <code>.equals(Object o)</code> を呼び出しています。
7334 比較されているものが違う型なら等しくないことであることが保証されているので、比較はほぼ間違いなく誤りです。
7335 たとえそれらが両方とも配列だったとしても、配列の <code>equals</code> メソッドは2つの配列が同じオブジェクトだと決定するだけです。
67737336 配列のコンテンツを比較するためには <code>java.util.Arrays.equals(Object[], Object[])</code> を使用してください。
67747337 </p>
67757338 ]]>
67857348 このメソッドは、配列で <code>.equals(Object o)</code> を呼び出しています。
67867349 配列は、<code>Object</code> の <code>equals</code> メソッドをオーバーライドしないので、配列で <code>equals</code> メソッドを呼び出すことはアドレスを比較することと同じです。
67877350 配列のコンテンツを比較するためには <code>java.util.Arrays.equals(Object[], Object[])</code> を使用してください。
6788 配列のアドレスを比較するために、明示的に、<code>==</code> を使用して参照等価性をチェックすることは、それほど紛らわしくないでしょう。
7351 配列のアドレスを比較するために明示的に <code>==</code> を使用して参照等価性をチェックすることは、それほど紛らわしくないでしょう。
67897352 </p>
67907353 ]]>
67917354 </Details>
68887451 </BugPattern>
68897452
68907453 <BugPattern type="DLS_DEAD_LOCAL_INCREMENT_IN_RETURN">
6891 <ShortDescription>return 文に役に立たないインクリメントがある</ShortDescription>
6892 <LongDescription>{1} からの return に役に立たないインクリメントがあります。</LongDescription>
7454 <ShortDescription>return 文に無駄なインクリメントがある</ShortDescription>
7455 <LongDescription>{1} からの return に無駄なインクリメントがあります。</LongDescription>
68937456 <Details>
68947457 <![CDATA[
68957458 <p>
69007463 ]]>
69017464 </Details>
69027465 </BugPattern>
6903
7466
69047467 <BugPattern type="DLS_DEAD_STORE_OF_CLASS_LITERAL">
69057468 <ShortDescription>クラスリテラルの無効な代入</ShortDescription>
69067469 <LongDescription>{3}.class の無効な代入です。{1}</LongDescription>
69097472 <p>
69107473 この命令は変数にクラスリテラルを代入していますが、決して使われません。<br>
69117474 <a href="//java.sun.com/j2se/1.5.0/compatibility.html#literal">The behavior of this differs in Java 1.4 and in Java 5</a><br>
6912 J2SE 1.4 およびそれ以前のバージョンでは、<code>Foo.class</code> への参照は <code>Foo</code> のためのスタティックイニシャライザがすでに実行されていないなら実行することを強制します。
7475 J2SE 1.4 およびそれ以前のバージョンでは、<code>Foo.class</code> への参照は <code>Foo</code> のためのスタティックイニシャライザが既に実行されていないなら実行することを強制します。
69137476 J2SE 5.0 ではそうしません。
69147477 </p>
69157478 <p>
69267489 <![CDATA[
69277490 <p>
69287491 このコードはローカル変数に null を代入していますが代入された値は読み出されていません。
6929 この代入はガベージコレクタを手伝うために導入されたのかもしれませんが、Java SE 6 ではもはや必要とされないか有効ではありません。
7492 この代入はガベージコレクタを手伝うために導入されたのかもしれませんが、Java SE 6 ではもはや必要とされないか有用ではありません。
69307493 </p>
69317494 ]]>
69327495 </Details>
69397502 <![CDATA[
69407503 <p>
69417504 このメソッドは、このクラスまたはスーパークラスのフィールドと同じ名前でローカル変数を定義しています。
6942 これはフィールドから初期化されていない値を読み出したり初期化されていないフィールドをそのままにしておくメソッドの原因になるかもしれません。
7505 フィールドから初期化されていない値を読み出す、初期化されていないフィールドをそのままにしておくか、または両方を引き起こすかもしれません。
69437506 </p>
69447507 ]]>
69457508 </Details>
70207583 このコードは、浮動小数点が特別な非数値と等価であるか確かめています (たとえば <code>if (x == Double.NaN)</code>)。
70217584 しかしながら、<code>NaN</code> の特別な意味のため、値は <code>NaN</code> と等価ではありません。
70227585 したがって、<code>x == Double.NaN</code> は常に false と評価します。
7023 <code>x</code> という値が特別な非数値であるかどうか確かめるためには <code>Double.isNaN(x)</code> を使用します (あるいは <code>x</code> が浮動小数点精度であるなら <code>Float.isNaN(x)</code>)。
7586 <code>x</code> という値が特別な非数値であるかどうか確かめるためには <code>Double.isNaN(x)</code> を使用します (または <code>x</code> が浮動小数点精度であるなら <code>Float.isNaN(x)</code>)。
70247587 </p>
70257588 ]]>
70267589 </Details>
70347597 <p>
70357598 この演算は、等価性のために2つの浮動小数点値を比較しています。
70367599 浮動小数点の計算は丸めを伴うかもしれないので計算された float と double の値は正確ではないかもしれません。
7037 通貨のような正確でなければならない値のために、<code>BigDecimal</code> のような固定精度型を使うことを検討してください。
7600 通貨のような正確でなければならない値のために <code>BigDecimal</code> のような固定精度型を使用することを検討してください。
70387601 正確である必要がない値のためにいくつかの範囲の中で等価性のために比較することを検討してください。
70397602 たとえば、<code>if (Math.abs(x - y) &lt; .0000001)</code>。<br>
70407603 詳細は Java 言語仕様4.2.4を参照してください。
70507613 <![CDATA[
70517614 <p>
70527615 このメソッドは、定数値で <code>java.lang.Math</code> の static メソッドを呼び出しています。
7053 このメソッドの結果は、静的に確定している可能性があり、より高速で、ときには定数を使用するほうがより正確です。<br>
7616 このメソッドの結果は静的に判定でき、より高速で、ときには定数を使用するほうがより正確です。<br>
70547617 検出されるメソッドは、以下のとおりです。
70557618 </p>
70567619 <table>
71397702 <p>
71407703 このクラスは、他のクラスと循環依存関係があります。
71417704 それぞれが他のクラスの正確な構築に依存していて、クラスの構築を難しくしています。
7142 難しい依存関係を断つために、インタフェースの使用を検討してください。
7705 難しい依存関係を断つためにインタフェースの使用を検討してください。
71437706 </p>
71447707 ]]>
71457708 </Details>
71677730 <p>
71687731 Struts Action クラスを拡張したクラスで、インスタンス変数を使用しています。
71697732 Struts Action クラスの1つのインスタンスだけが Struts フレームワークによって作成され、マルチスレッドによって使われるので、このパラダイムは極めて問題があり、推奨できません。
7170 ローカル変数を使うことだけを検討してください。
7733 ローカル変数を使用することだけを検討してください。
71717734 モニタを除いて書き込まれるインスタンスフィールドだけが報告されます。
71727735 </p>
71737736 ]]>
71827745 <p>
71837746 Servletクラスを拡張したクラスで、インスタンス変数を使用しています。
71847747 Servlet クラスの1つのインスタンスだけが Java EE フレームワークによって作成され、マルチスレッドによって使われるので、このパラダイムは極めて問題があり、推奨できません。
7185 ローカル変数を使うことだけを検討してください。
7748 ローカル変数を使用することだけを検討してください。
71867749 </p>
71877750 ]]>
71887751 </Details>
71987761 このクラスを使用するクライアントクラスは、同期化のためのオブジェクトとしてこのクラスのインスタンスをさらに使用するかもしれません。
71997762 2つのクラスが同期化のために同じオブジェクトを使用するので、マルチスレッドの正確性は疑わしいです。
72007763 同期化するべきでもないし、公開参照でセマフォメソッドも呼び出すべきではありません。
7201 同期化の制御には内部の公開されないメンバ変数を使うことを検討してください。
7764 同期化の制御には内部の公開されないメンバ変数を使用することを検討してください。
72027765 </p>
72037766 ]]>
72047767 </Details>
72417804 <p>
72427805 このコードは、32ビット int 値を64ビット long 値に変換して、絶対時間値を必要とするメソッドパラメータに渡しています。
72437806 絶対時間値は、「エポック」(すなわち、1970年1月1日、00:00:00 GMT)としてわかっている標準的な基準時間からのミリ秒数です。<br>
7244 たとえば、次のメソッド(Date にエポックから秒を変換することを意図した)は、ひどく壊れています。
7807 たとえば、次のメソッド (エポックからの秒を Date へ変換することを意図した) はひどく壊れています。
72457808 </p>
72467809 <blockquote><pre>
72477810 Date getDate(int seconds) { return new Date(seconds * 1000); }
72517814 32ビット値は、64ビットに変換されて、絶対時間値を表すために使用されるとき、1969年12月と1970年1月の日付しか表せません。
72527815 </p>
72537816 <p>
7254 上記のメソッドのための正しい実装は、以下のとおりです。
7817 上記のメソッドの正しい実装は以下のとおりです。
72557818 </p>
72567819 <blockquote><pre>
72577820 // 失敗、2037年後の日付
73647927 <![CDATA[
73657928 <p>
73667929 この書式文字列には改行文字 (\n) が含まれています。
7367 一般に書式文字列には %n を使用することがより望ましいです。%n は、プラットホーム特有の行セパレータを作り出します。
7930 一般的に書式文字列には %n を使用することがより望ましいです。%n は、プラットホーム特有の行セパレータを作り出します。
73687931 </p>
73697932 ]]>
73707933 </Details>
74047967 <![CDATA[
74057968 <p>
74067969 書式文字列でフォーマットされている引数の1つは配列です。
7407 これは [I@304282 のようなかなり役に立たない書式を使用してフォーマットされます。それは配列のコンテンツを表示しません。
7970 [I@304282 のように配列のコンテンツを表示しないかなり役に立たない書式を使用してフォーマットされます。
74087971 フォーマットで扱う前に <code>Arrays.asList(...)</code> を使用して配列をラップすることを検討してください。
74097972 </p>
74107973 ]]>
74978060 <![CDATA[
74988061 <p>
74998062 このコードは可変長引数をとるメソッドにプリミティブ型の配列を渡しています。
7500 これは、プリミティブ型の配列を保持するために長さが1の配列を作成してメソッドに渡します。
8063 これはプリミティブ型の配列を保持するために長さが1の配列を作成してメソッドに渡します。
75018064 </p>
75028065 ]]>
75038066 </Details>
75048067 </BugPattern>
75058068 <BugPattern type="BC_EQUALS_METHOD_SHOULD_WORK_FOR_ALL_OBJECTS">
75068069 <ShortDescription>equals メソッドは引数の型を仮定するべきではない</ShortDescription>
7507 <LongDescription>{0} のための equals メソッドは、引数の型が {0.givenClass} であると仮定しています。</LongDescription>
8070 <LongDescription>{0} のための equals メソッドは、引数の型が {0.givenClass} だと仮定しています。</LongDescription>
75088071 <Details>
75098072 <![CDATA[
75108073 <p>
75228085 <![CDATA[
75238086 <p>
75248087 このコードは、Collection を抽象コレクションにキャストしています (たとえば <code>List</code>、<code>Set</code>、<code>Map</code>)。
7525 オブジェクトがキャストする型であると保証されることを確認してください。
8088 オブジェクトがキャストする型であるということが保証されていることを確認してください。
75268089 必要とするコレクションの反復処理ができるなら Set または List にキャストする必要はありません。
75278090 </p>
75288091 ]]>
75508113 このキャストは、常に ClassCastException をスローします。
75518114 FindBugs は、instanceof チェックから型情報を調査して、メソッドからの戻り値とフィールドからロードされた値の型について、より多くの正確な情報を使用します。
75528115 したがって、宣言された変数の型にはより多くの正確な情報があるかもしれないしれません。
7553 そして、キャストが常に実行時例外をスローするのか判定するために使用する可能性があります。
8116 また、キャストが常に実行時例外をスローするのかを決定するために利用する可能性があります。
75548117 </p>
75558118 ]]>
75568119 </Details>
75848147 }
75858148 </pre></blockquote>
75868149 <p>
7587 これは通常、ClassCastException をスローして失敗します。
8150 これは通常 ClassCastException をスローして失敗します。
75888151 ほとんどすべてのコレクションの <code>toArray</code> メソッドは、<code>Object[]</code> を返します。
75898152 Collection オブジェクトは宣言された総称型コレクションの参照がないので、本当に何もできません。
7590 コレクションから特定の型の配列を得る正しい方法は、<code>c.toArray(new String[]);</code> または <code>c.toArray(new String[c.size()]);</code> (後者はわずかにより効率的です) を使うことです。
8153 コレクションから特定の型の配列を得る正しい方法は、<code>c.toArray(new String[]);</code> または <code>c.toArray(new String[c.size()]);</code> (後者はわずかにより効率的です) を使用することです。
75918154 これに対する1つの共通の知られている例外があります。
7592 <code>Arrays.asList(...)</code>によって返されるリストの <code>toArray()</code> メソッドは、共変な配列を返します。
8155 <code>Arrays.asList(...)</code>によって返されるリストの <code>toArray()</code> メソッドは共変型配列を返します。
75938156 たとえば、<code>Arrays.asArray(new String[] { "a" }).toArray()</code> は <code>String []</code> を返します。
7594 FindBugs はそのようなケースを検出して抑止しようとしますが、いくつか見落とすかもしれません。
8157 FindBugs はそのようなケースを検出して抑止しようとしますが、見落としているかもしれません。
75958158 </p>
75968159 ]]>
75978160 </Details>
76568219 <Details>
76578220 <![CDATA[
76588221 <p>
7659 このキャストはチェックされていません。すべての型のインスタンスがキャストした型にキャストできるというわけではありません。
8222 このキャストはチェックされていません。すべての型のインスタンスをキャストする型へキャストできるわけではありません。
76608223 プログラムのロジックがこのキャストが失敗しないことを確実に確認してください。
76618224 </p>
76628225 ]]>
76638226 </Details>
76648227 </BugPattern>
76658228
7666 <BugPattern type="BC_UNCONFIRMED_CAST_OF_RETURN_VALUE">
8229 <BugPattern type="BC_UNCONFIRMED_CAST_OF_RETURN_VALUE">
76678230 <ShortDescription>メソッドからの戻り値の未チェック/未確認のキャスト</ShortDescription>
76688231 <LongDescription>{2} からの {3} 戻り値への未チェック/未確認のキャストです。{1}</LongDescription>
76698232 <Details>
76858248 <p>
76868249 このコードは抽象コレクション (たとえば、Collection、List、Set) を特定の具象実装 (たとえば、ArrayList、HashSet) にキャストしています。
76878250 これは正しくないかもしれません。そして、将来の時点で他の具象実装への切り替えをとても困難にするので、脆弱なコードになるかもしれません。
7688 そうするために特別な理由がないかぎり抽象コレクションクラスを使用してください。
8251 そうするための特別な理由がないかぎり抽象コレクションクラスを使用してください。
76898252 </p>
76908253 ]]>
76918254 </Details>
76988261 <![CDATA[
76998262 <p>
77008263 String 機能が呼び出されていて、"." または "|" が引数として正規表現を取るパラメータに渡されています。
7701 これは、意図したことですか?
8264 これは意図したことですか?
77028265 たとえば
77038266 </p>
77048267 <ul>
77308293 <Details>
77318294 <![CDATA[
77328295 <p>
7733 このコードは、正規表現が必要とされる場所で、<code>File.separator</code> を使用しています。
8296 このコードは、正規表現が必要な場所で、<code>File.separator</code> を使用しています。
77348297 これは <code>File.separator</code> がバックスラッシュである Windows プラットホームでは失敗します。
77358298 バックスラッシュは正規表現ではエスケープ文字として解釈されます。
77368299 その他の選択肢としては、<code>File.separator</code> の代わりに <code>File.separatorChar=='\\' ? "\\\\" : File.separator</code> を使用できます。
77598322 <![CDATA[
77608323 <p>
77618324 このコードは、符号なしキャストの実行結果を short または byte にキャストしています。結果の上位ビットは捨てられます。
7762 上位ビットが捨てられるので、符号付き、符号なし右シフトの (シフトのサイズによって) 違いがないかもしれません。
8325 上位ビットが捨てられるので、符号付きと符号なし右シフト (シフトのサイズによって) との違いがないかもしれません。
8326 </p>
8327 ]]>
8328 </Details>
8329 </BugPattern>
8330
8331 <BugPattern type="BSHIFT_WRONG_ADD_PRIORITY">
8332 <ShortDescription>シフト演算の正しくない構文解析の可能性がある</ShortDescription>
8333 <LongDescription>シフト演算の正しくない構文解析の可能性があります。{1}</LongDescription>
8334 <Details>
8335 <![CDATA[
8336 <p>
8337 コードは (x &lt;&lt; 8 + y) のような操作を行います。
8338 これは正しいかもしれませんが、おそらく (x &lt;&lt; 8) + y を行うことを意図していました。
8339 しかし、シフト演算は優先順位が低いので、実際には x &lt;&lt; (8 + y) として構文解析されます。
77638340 </p>
77648341 ]]>
77658342 </Details>
77728349 <![CDATA[
77738350 <p>
77748351 このコードは、32ビット int の-31から31の範囲を超えた量でシフトを実行しています。
7775 これの効果は、どのくらいシフトするのかを決めるために整数値の下位5ビット (32で割った余り) を使うことです (たとえば、40ビットでシフトすることは8ビットでシフトすることと同じで、32ビットでシフトすることは0ビットでシフトすることと同じです)。
7776 これは、おそらく期待されたことではなく、少なくとも紛らわしいです。
8352 これの効果は、どのくらいシフトするのかを決めるために整数値の下位5ビット (32で割った余り) を使用することです (たとえば、40ビットでシフトすることは8ビットでシフトすることと同じで、32ビットでシフトすることは0ビットでシフトすることと同じです)。
8353 これはおそらく期待されたことではなく、少なくとも紛らわしいです。
77778354 </p>
77788355 ]]>
77798356 </Details>
78148391 <![CDATA[
78158392 <p>
78168393 このコードは、配列で <code>toString</code> メソッドを呼び出しています。「[C@16f0472」のようなかなり役に立たない結果を生成します。
7817 配列のコンテンツを与え、読める文字列に変換するために、<code>Arrays.toString()</code> を使うことを検討してください。<br>
8394 配列のコンテンツを与え、読める文字列に変換するために <code>Arrays.toString()</code> を使用することを検討してください。<br>
78188395 『Programming Puzzlers』の第3章、パズル12を参照してください。
78198396 </p>
78208397 ]]>
78288405 <![CDATA[
78298406 <p>
78308407 このコードは、無名の配列で <code>toString</code> メソッドを呼び出しています。「[C@16f0472」のようなかなり役に立たない結果を生成します。
7831 配列のコンテンツを与え、読める文字列に変換するために、<code>Arrays.toString()</code> を使うことを検討してください。<br>
8408 配列のコンテンツを与え、読める文字列に変換するために <code>Arrays.toString()</code> を使用することを検討してください。<br>
78328409 『Programming Puzzlers』の第3章、パズル12を参照してください。
78338410 </p>
78348411 ]]>
78488425 </p>
78498426 <p>
78508427 このバグは、二分探索とマージソートの多くの以前の実装で存在します。
7851 Martin Buchholz が <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6412541">JDK ライブラリでバグを発見し、修正しています</a>。
8428 Martin Buchholz が <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6412541">JDK ライブラリのバグを発見して修正しています</a>。
78528429 Joshua Bloch が <a href="http://googleresearch.blogspot.com/2006/06/extra-extra-read-all-about-it-nearly.html">バグパターンとして公表しました</a>。
78538430 </p>
78548431 ]]>
78628439 <![CDATA[
78638440 <p>
78648441 このコードは、<code>x % 2 == 1</code> を使用して値が負数なのか確かめていますが、負数 (たとえば、<code>(-5) % 2 == -1</code>) なので機能しません。
7865 奇数チェックを意図しているなら、<code>x &amp; 1 == 1</code> または <code>x % 2 != 0</code> を使うことを検討してください。
8442 奇数チェックを意図しているなら、<code>x &amp; 1 == 1</code> または <code>x % 2 != 0</code> を使用することを検討してください。
78668443 </p>
78678444 ]]>
78688445 </Details>
79258502 <![CDATA[
79268503 <p>
79278504 このメソッドは、ロックを保持して、<code>Thread.sleep()</code> を呼び出しています。
7928 他のスレッドがロックを獲得するために待機しているかもしれないので、ひどい性能とスケーラビリティ、またはデッドロックになるかもしれません。
8505 他のスレッドがロックを獲得するために待機しているかもしれないので、ひどい性能とスケーラビリティ、またはデッドロックの原因になるかもしれません。
79298506 ロックで <code>wait</code> メソッドを呼び出すことはかなり良い考えで、ロックを解除して他のスレッドが実行するのを許可します。
79308507 </p>
79318508 ]]>
79518528 <![CDATA[
79528529 <p>
79538530 このメソッドは、switch 文の2つの case を実装するために同じコードを使用しています。
7954 複製コードの case かもしれないしコーディングミスかもしれません。
8531 複製コードの case かもしれないし、コーディングミスかもしれません。
79558532 </p>
79568533 ]]>
79578534 </Details>
79648541 <![CDATA[
79658542 <p>
79668543 この内部クラスのメソッドは、所有クラスの private メンバー変数への読み書きか、所有クラスの private メソッドを呼び出しています。
7967 コンパイラはこの private メンバーにアクセスするために特別なメソッドを生成しなければなりないので、効率を悪化させる原因になります。
7968 メンバー変数またはメソッドの保護を緩和することは、コンパイラが一般にのアクセスとして扱うのを許可します。
8544 コンパイラはこの private メンバーにアクセスするための特別なメソッドを生成しなければなりないので、効率を悪化させる原因になります。
8545 メンバー変数またはメソッドの保護を緩和することは、コンパイラが正常なアクセスとして扱うのを許可します。
79698546 </p>
79708547 ]]>
79718548 </Details>
79788555 <![CDATA[
79798556 <p>
79808557 このメソッドは、XMLインタフェースの特定の実装のインスタンスを作成しています。
7981 実装が実行時に変更できるように、これらのオブジェクトを作成するために提供されたファクトリクラスを使うことが望ましいです。<br>
8558 提供されたファクトリクラスを使用してオブジェクトを作成して実行時に実装を変更できるようにすることが望ましいです。<br>
79828559 詳細は、以下を参照してください。
79838560 </p>
79848561 <ul>
80058582 </BugPattern>
80068583
80078584 <BugPattern type="USM_USELESS_ABSTRACT_METHOD">
8008 <ShortDescription>実装されたインタフェースですでに定義された抽象メソッド</ShortDescription>
8009 <LongDescription>抽象メソッド {1} は、実装されたインタフェースですでに定義されています。</LongDescription>
8010 <Details>
8011 <![CDATA[
8012 <p>
8013 この抽象メソッドは、この抽象クラスによって実装されるインタフェースですでに定義されています。
8585 <ShortDescription>実装されたインタフェースで既に定義された抽象メソッド</ShortDescription>
8586 <LongDescription>抽象メソッド {1} は、実装されたインタフェースで既に定義されています。</LongDescription>
8587 <Details>
8588 <![CDATA[
8589 <p>
8590 この抽象メソッドは、この抽象クラスによって実装されるインタフェースで既に定義されています。
80148591 このメソッドは、付加価値が与えられないので除去できます。
80158592 </p>
80168593 ]]>
80638640 <![CDATA[
80648641 <p>
80658642 総称型パラメータからの特定の型が予想される Object 型をコンパイルするとき、総称型コレクションメソッドへの呼び出しは引数を渡します。
8066 したがって、標準の Java 型システムも静的解析もパラメータとして渡されているオブジェクトが適切な型かどうかに関する有効な情報を提供できません。
8643 したがって、標準の Java 型システムも静的解析もパラメータとして渡されているオブジェクトが適切な型かどうかに関する有用な情報を提供できません。
80678644 </p>
80688645 ]]>
80698646 </Details>
80788655 総称型コレクションメソッドへの呼び出しにコレクションのパラメータとは互換性のないクラスの引数があります (すなわち、引数の型は総称型引数に対応するスーパタイプでもサブタイプでもありません)。
80798656 したがって、コレクションにはここで使用されたメソッド引数と等価であるどんなオブジェクトも含まれていません。
80808657 多分間違った値がメソッドに渡されています。
8081 一般に、2つの無関係なクラスのインスタンスは等価ではありません。
8658 一般的に2つの無関係なクラスのインスタンスは等価ではありません。
80828659 たとえば、<code>Foo</code> と <code>Bar</code> クラスがサブタイプによって関係がないなら、<code>Foo</code> のインスタンスは <code>Bar</code> のインスタンスと等価のはずがありません。
80838660 その他の問題で対称的ではない <code>equals</code> メソッドになる可能性が高いです。
80848661 たとえば、<code>Foo</code> が <code>String</code> と等価であるように <code>Foo</code> クラスを定義するなら、<code>String</code> は <code>String</code> だけと等価であるので、<code>equals</code> メソッドは対称的ではありません。
80988675 <Details>
80998676 <![CDATA[
81008677 <p>
8101 この総称型コレクションメソッドへの呼び出しはコレクションに自分自身が含まれている (たとえば、<code>s.contains(s)</code> が true だとして) 場合にだけ意味があります。
8102 これは true である可能性が低くて、もし true なら問題の原因になります (たとえば、無限再帰になっているハッシュコードの計算)。
8678 この総称型コレクションメソッドへの呼び出しはコレクションに自分自身が含まれている場合 (たとえば、<code>s.contains(s)</code> が true) にだけ意味があります。
8679 これが本当だとは思えないし、もし本当なら問題の原因になります (たとえば、無限再帰になっているハッシュコードの計算)。
81038680 間違ったパラメータが渡されている可能性が高いです。
81048681 </p>
81058682 ]]>
81498726 ]]>
81508727 </Details>
81518728 </BugPattern>
8152
8729
81538730 <BugPattern type="DMI_USING_REMOVEALL_TO_CLEAR_COLLECTION">
81548731 <ShortDescription>コレクションを消去するために removeAll メソッドを使用しない</ShortDescription>
81558732 <LongDescription>コレクションを消去するために removeAll メソッドを使用しています。{1}</LongDescription>
81728749 たとえ JavaDoc にそれに関する手がかりがないとしても、Calendar はマルチスレッドでの使用は本質的に安全でありません。
81738750 正しい同期化をしないでスレッド境界の向こうで1つのインスタンスを共有することは、アプリケーションの誤動作になります。
81748751 JDK 5.0に比べて JDK 1.4 のほうが問題が表面化するように思われ、おそらく sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate() の ArrayIndexOutOfBoundsExceptions や IndexOutOfBoundsExceptions がランダムに発生します。
8175 直列化問題も経験するかもしれません。インスタンスフィールドを使うことを推奨します。<br>
8752 直列化問題も経験するかもしれません。インスタンスフィールドを使用することを推奨します。<br>
81768753 詳細については、<a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6231579">Sun Bug #6231579</a> や <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6178997">Sun Bug #6178997</a> を参照してください。
81778754 </p>
81788755 ]]>
82008777 <Details>
82018778 <![CDATA[
82028779 <p>
8203 JavaDoc に書かれているように、DateFormat はマルチスレッドでの使用は本質的に安全ではありません。
8780 JavaDoc に書かれているように DateFormat はマルチスレッドでの使用は本質的に安全ではありません。
82048781 正しい同期化をしないでスレッド境界の向こうで1つのインスタンスを共有することは、アプリケーションの誤動作になります。
82058782 JDK 5.0に比べて JDK 1.4 のほうが問題が表面化するように思われ、おそらく sun.util.calendar.BaseCalendar.getCalendarDateFromFixedDate() の ArrayIndexOutOfBoundsExceptions や IndexOutOfBoundsExceptions がランダムに発生します。
8206 直列化問題も経験するかもしれません。インスタンスフィールドを使うことを推奨します。<br>
8783 直列化問題も経験するかもしれません。インスタンスフィールドを使用することを推奨します。<br>
82078784 詳細については、<a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6231579">Sun Bug #6231579</a> や <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6178997">Sun Bug #6178997</a> を参照してください。
82088785 </p>
82098786 ]]>
82168793 <Details>
82178794 <![CDATA[
82188795 <p>
8219 JavaDoc に書かれているように、DateFormat はマルチスレッドでの使用は本質的に安全ではありません。
8796 JavaDoc に書かれているように DateFormat はマルチスレッドでの使用は本質的に安全ではありません。
82208797 ディテクタは、static フィールドから得られた DateFormat のインスタンスの呼び出しを発見しました。
82218798 これは疑わしく見えます。
82228799 詳細については、<a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6231579">Sun Bug #6231579</a> や <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6178997">Sun Bug #6178997</a> を参照してください。
82378814 より正確に、when=ALWAYS を指定した型修飾子でアノテートされた値が同じ型修飾子で when=NEVER を指定する値と比較しています。
82388815 </p>
82398816 <p>
8240 たとえば、@NonNegative が型修飾子アノテーション @Negative(when=When.NEVER) の略称だとします。
8817 たとえば、@NonNegative は型修飾子アノテーション @Negative(when=When.NEVER) の略称とします。
82418818 以下のコードは、return 文が @NonNegative 値を要求するが、@Negative としてマークされている値を受け取るのでこの警告を生成します。
82428819 </p>
82438820 <blockquote><pre>
82558832 <Details>
82568833 <![CDATA[
82578834 <p>
8258 型修飾子であのてーとされた値がその修飾子を付けてはならない値を必要とする場所で使われています。
8835 型修飾子でアノテートされた値がその修飾子を付けてはならない値を必要とする場所で使われています。
82598836 </p>
82608837 <p>
82618838 より正確に、when=ALWAYS を指定した型修飾子でアノテートされた値が到達することが保証されているか同じ型修飾子で when=NEVER を指定する場所で使用しています。
82628839 </p>
82638840 <p>
8264 たとえば、@NonNegative が型修飾子アノテーション @Negative(when=When.NEVER) の略称だとします。
8841 たとえば、@NonNegative は型修飾子アノテーション @Negative(when=When.NEVER) の略称とします。
82658842 以下のコードは、return 文が @NonNegative 値を要求するが @Negative としてマークされている値を受け取るのでこの警告を生成します。
82668843 </p>
82678844 <blockquote><pre>
82838860 </p>
82848861 <p>
82858862 厳密なアノテーションを指定しているので値を型変換します。戻り値が厳密なアノテーションでアノテートされる同一性機能を定義してください。
8286 これは、厳密な型修飾子アノテーションで非アノテート値を値に変える唯一の方法です。
8863 これは厳密な型修飾子アノテーションで非アノテート値を値に変える唯一の方法です。
82878864 </p>
82888865 ]]>
82898866 </Details>
83658942 <![CDATA[
83668943 <p>
83678944 このコードは、ファイルを追加モードで開いて、オブジェクト出力ストリームの中で結果をラップしています。
8368 これは、ファイルに格納された既存のオブジェクト出力ストリームに追加できないでしょう。
8945 これはファイルに格納された既存のオブジェクト出力ストリームに追加できないでしょう。
83698946 オブジェクト出力ストリームに追加したいなら、オブジェクト出力ストリームを開いておく必要があります。
83708947 </p>
83718948 <p>
84239000 <Details>
84249001 <![CDATA[
84259002 <p>
8426 このメソッドは、ストリーム、データベースオブジェクト、または明示的にクリーンアップ操作を必要としている他のリソースのクリーンアップ (クローズする、片付ける) に失敗するかもしれません。
9003 このメソッドは、ストリーム、データベースオブジェクト、またはクリーンアップ操作を明示的に必要としている他のリソースのクリーンアップ (クローズする、片付ける) に失敗するかもしれません。
84279004 </p>
84289005 <p>
8429 一般に、メソッドがストリープや他のリソースを開いたなら、メソッドはストリームやリソースがメソッドが戻る前にクリーンアップされることを確認するために、try/finally ブロックを使用するべきです。
9006 一般的にメソッドがストリープや他のリソースを開いたなら、メソッドはストリームやリソースがメソッドが戻る前にクリーンアップされることを確認するために try/finally ブロックを使用するべきです。
84309007 </p>
84319008 <p>
84329009 このバグパターンは、OS_OPEN_STREAM と ODR_OPEN_DATABASE_RESOURCE と基本的に同じですが異なる (そして、うまくいけばより良い) 静的解析技術に基づいています。
84539030 <Details>
84549031 <![CDATA[
84559032 <p>
8456 このメソッドは、ストリーム、データベースオブジェクト、または明示的にクリーンアップ操作を必要としている他のリソースのクリーンアップ (クローズする、片付ける) に失敗するかもしれません。
9033 このメソッドは、ストリーム、データベースオブジェクト、またはクリーンアップ操作を明示的必要としている他のリソースのクリーンアップ (クローズする、片付ける) に失敗するかもしれません。
84579034 </p>
84589035 <p>
8459 一般に、メソッドがストリープや他のリソースを開いたなら、メソッドはストリームやリソースがメソッドが戻る前にクリーンアップされることを確認するために、try/finally ブロックを使用するべきです。
9036 一般的にメソッドがストリープや他のリソースを開いたなら、メソッドはストリームやリソースがメソッドが戻る前にクリーンアップされることを確認するために try/finally ブロックを使用するべきです。
84609037 </p>
84619038 <p>
84629039 このバグパターンは、OS_OPEN_STREAM と ODR_OPEN_DATABASE_RESOURCE と基本的に同じですが異なる (そして、うまくいけばより良い) 静的解析技術に基づいています。
84769053 ]]>
84779054 </Details>
84789055 </BugPattern>
8479
9056
84809057 <BugPattern type="FB_UNEXPECTED_WARNING">
84819058 <ShortDescription>FindBugs からの予期しない/望ましくない警告</ShortDescription>
84829059 <LongDescription>予期しない/望ましくない {2} FindBugs 警告。{1}</LongDescription>
85079084 <Details>
85089085 <![CDATA[
85099086 <p>
8510 <code>putIfAbsent</code> メソッドは、一般に1つの値が与えられたキー (非存在が成功するかどうかの第一の値) と関連することを確認するために使われます。
9087 <code>putIfAbsent</code> メソッドは、1つの値が与えられたキー (非存在が成功するかどうかの第一の値) と関連することを確認するために使われます。
85119088 戻り値を無視して中で渡される値への参照を保持するなら、マップのキーと関連する1つではない値を保持する危険性を冒します。
8512 どれを使用するかが重要であり、マップに格納できないものを使用すると、プログラムは誤った振る舞いをします。
9089 どれを使用するかが重要であり、マップに格納できないものを使うとプログラムは誤った振る舞いをします。
85139090 </p>
85149091 ]]>
85159092 </Details>
85229099 <![CDATA[
85239100 <p>
85249101 OpenJDK は、潜在的非互換性を取り入れました。特に、<code>java.util.logging.Logger</code> は振る舞いが変更されています。
8525 強参照を使用する代わりに、現在、内部的に弱参照を使用しています。
9102 強参照を使用する代わりに弱参照を内部的に使用しています。
85269103 それは理にかなった変更ですが、残念ながらいくつかのコードは古い振る舞いに依存しています。
85279104 ロガーの構成を変更するとき、ロガーへの参照を捨てます。
85289105 つまり、ガベージコレクタはそのメモリを回収できます。それは、ロガーの構成が失われることを意味します。<br>
85729149 <Details>
85739150 <![CDATA[
85749151 <p>
8575 byte から String (または String から byte) への変換で、デフォルトプラットホームエンコーディングが適切であると仮定するメソッドの呼び出しを発見しました。
8576 これは、アプリケーションの振る舞いがプラットホーム間で異なる原因となります。代替 API を使用して、明示的に文字セット名または Charset オブジェクトを指定して下さい。
9152 byte から String (または String から byte) への変換で、デフォルトプラットホームエンコーディングが適切だと仮定するメソッドの呼び出しを発見しました。
9153 これはアプリケーションの振る舞いがプラットホーム間で異なる原因となります。代替 API を使用して、文字セット名または Charset オブジェクトを明示的に指定して下さい。
85779154 </p>
85789155 ]]>
85799156 </Details>
85809157 </BugPattern>
85819158
85829159 <BugPattern type="NP_METHOD_PARAMETER_RELAXING_ANNOTATION">
8583 <ShortDescription>メソッドはパラメータで nullness アノテーションを強化する</ShortDescription>
8584 <LongDescription>メソッド {1} は、パラメータで祖先メソッドの要件を緩和している nullness アノテーションをオーバライドしています。</LongDescription>
8585 <Details>
8586 <![CDATA[
8587 <p>
8588 メソッドは、オーバライドするメソッドの規約を常に実装するべきです。
9160 <ShortDescription>メソッドはパラメータに nullness アノテーションを強化している</ShortDescription>
9161 <LongDescription>メソッド {1} は、祖先メソッドの要件を緩和する nullness アノテーションをオーバーライドしています。</LongDescription>
9162 <Details>
9163 <![CDATA[
9164 <p>
9165 メソッドは、オーバーライドするメソッドの契約を常に実装するべきです。
85899166 したがって、メソッドが @Nullable としてマークされるパラメーターを取るならば、サブクラスでパラメーターを @Nonnull にしてメソッドをオーバーライドするべきでありません。
8590 そうするこによって、メソッドが null パラメーターを扱うべきである規約に違反します。
9167 そうするとメソッドが null パラメータを処理すべき契約を破ります。
85919168 </p>
85929169 ]]>
85939170 </Details>
85949171 </BugPattern>
85959172
85969173 <BugPattern type="NP_METHOD_PARAMETER_TIGHTENS_ANNOTATION">
8597 <ShortDescription>メソッドはパラメータで nullness アノテーションを強化する</ShortDescription>
8598 <LongDescription>メソッド {1} は、互換性のない方法でパラメーター {2} の nullness アノテーションをオーバーライドしています。</LongDescription>
8599 <Details>
8600 <![CDATA[
8601 <p>
8602 メソッドは、オーバライドするメソッドの規約を常に実装するべきです。
9174 <ShortDescription>メソッドはパラメータに nullness アノテーションを強化している</ShortDescription>
9175 <LongDescription>メソッド {1} は、互換性がない方法で nullness アノテーションのパラメータ {2} をオーバーライドしています。</LongDescription>
9176 <Details>
9177 <![CDATA[
9178 <p>
9179 メソッドは、オーバーライドするメソッドの契約を常に実装するべきです。
86039180 したがって、メソッドが @Nullable としてマークされるパラメーターを取るならば、サブクラスでパラメーターを @Nonnull にしてメソッドをオーバーライドするべきでありません。
8604 そうするこによって、メソッドが null パラメーターを扱うべきである規約に違反します。
9181 そうするとメソッドが null パラメータを処理すべき契約を破ります。
86059182 </p>
86069183 ]]>
86079184 </Details>
86089185 </BugPattern>
86099186
86109187 <BugPattern type="NP_METHOD_RETURN_RELAXING_ANNOTATION">
8611 <ShortDescription>メソッドは戻り値で nullness アノテーションを緩和させる</ShortDescription>
8612 <LongDescription>メソッド {1} は、互換性のない方法で戻り値の nullness アノテーションをオーバーライドしています。</LongDescription>
8613 <Details>
8614 <![CDATA[
8615 <p>
8616 メソッドは、オーバライドするメソッドの規約を常に実装するべきです。
9188 <ShortDescription>メソッドは戻り値の nullness アノテーションを緩和している</ShortDescription>
9189 <LongDescription>メソッド {1} は、互換性がない方法で戻り値の nullness アノテーションをオーバーライドしています。</LongDescription>
9190 <Details>
9191 <![CDATA[
9192 <p>
9193 メソッドは、オーバーライドするメソッドの契約を常に実装するべきです。
86179194 したがって、メソッドが @Nonnull 値を返すようにアノテートしているならば、サブクラスでメソッドが @Nullable または @CheckForNull 値を返すようにアノテートしてメソッドをオーバーライドするべきでありません。
8618 そうするこによって、メソッドが null を返すべきでない規約に違反します。
9195 そうするとメソッドが null を返すべできではない契約を破ります。
86199196 </p>
86209197 ]]>
86219198 </Details>
86359212 <BugCode abbrev="TEST">プロトタイプと未完成のバグパターンのテスト</BugCode>
86369213 <BugCode abbrev="IMSE">疑わしい IllegalMonitorStateException のキャッチ</BugCode>
86379214 <BugCode abbrev="CN">クローン可能イディオムの間違った実装</BugCode>
9215 <BugCode abbrev="CAA">共変配列代入</BugCode>
86389216 <BugCode abbrev="AT">原子性違反の可能性</BugCode>
86399217 <BugCode abbrev="FI">ファイナライザの間違った使用</BugCode>
86409218 <BugCode abbrev="ES">== や != を使用している文字列の等価性チェック</BugCode>
86569234 <BugCode abbrev="NN">裸の notify()</BugCode>
86579235 <BugCode abbrev="UW">無条件の wait()</BugCode>
86589236 <BugCode abbrev="SP">スピンロック</BugCode>
8659 <BugCode abbrev="DC">フィールドのダブルチェックの可能性</BugCode>
9237 <BugCode abbrev="DC">ダブルチェックパターン</BugCode>
86609238 <BugCode abbrev="Wa">ループの中にない wait()</BugCode>
86619239 <BugCode abbrev="No">notifyAll() ではなく notify() を使用している</BugCode>
86629240 <BugCode abbrev="DE">捨てられたか無視された例外</BugCode>
86689246 <BugCode abbrev="RS">readObject() を同期化している</BugCode>
86699247 <BugCode abbrev="SC">Thread.start() を呼び出すコンストラクタ</BugCode>
86709248 <BugCode abbrev="MS">可変 static フィールド</BugCode>
9249 <BugCode abbrev="ME">可変列挙型フィールド</BugCode>
86719250 <BugCode abbrev="EI">内部表現を暴露するかもしれない配列を返すメソッド</BugCode>
86729251 <BugCode abbrev="Nm">紛らわしいメソッド名</BugCode>
86739252 <BugCode abbrev="SS">読み出されないフィールドは static にすべき</BugCode>
86769255 <BugCode abbrev="UwF">書き込まれないフィールド</BugCode>
86779256 <BugCode abbrev="SIC">static にできる内部クラス</BugCode>
86789257 <BugCode abbrev="TLW">2つのロックを保持する wait()</BugCode>
9258 <BugCode abbrev="RANGE">範囲チェック</BugCode>
86799259 <BugCode abbrev="RV">メソッドからの戻り値の間違った使用</BugCode>
86809260 <BugCode abbrev="LG">Logger の問題</BugCode>
86819261 <BugCode abbrev="IA">あいまいな呼び出し</BugCode>
87059285 <BugCode abbrev="NS">疑わしい非短絡論理演算子の使用</BugCode>
87069286 <BugCode abbrev="ODR">すべての経路でクローズされないデータベースリソース</BugCode>
87079287 <BugCode abbrev="SBSC">ループの中で + 演算子を使用した文字列連結</BugCode>
9288 <BugCode abbrev="IIL">ループの外に移動できる非効率なコード</BugCode>
9289 <BugCode abbrev="IIO">非効率な String.indexOf(String) または String.lastIndexOf(String) の使用</BugCode>
87089290 <BugCode abbrev="ITA">効率が悪い collection.toArray(new Foo[0]) の使用</BugCode>
87099291 <BugCode abbrev="SW">Swing コーディング規約</BugCode>
87109292 <BugCode abbrev="IJU">誤って実装された JUnit TestCase</BugCode>
87229304 <BugCode abbrev="REC">RuntimeException の捕捉</BugCode>
87239305 <BugCode abbrev="FE">浮動小数点の等価性テスト</BugCode>
87249306 <BugCode abbrev="UM">定数に関する不必要な Math</BugCode>
9307 <BugCode abbrev="UC">役に立たないコード</BugCode>
9308 <BugCode abbrev="CNT">既知の定数の雑な値</BugCode>
87259309 <BugCode abbrev="CD">循環依存関係</BugCode>
87269310 <BugCode abbrev="RI">冗長なインタフェース</BugCode>
87279311 <BugCode abbrev="MTIA">マルチスレッドでのインスタンスアクセス</BugCode>
(No changes)
00 <?xml version="1.0" encoding="UTF-8"?>
11
2 <FindBugsFilter>
2 <FindBugsFilter xmlns="http://findbugs.sourceforge.net/filter/3.0.0"
3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4 xsi:schemaLocation="http://findbugs.sourceforge.net/filter/3.0.0 https://findbugs.googlecode.com/git/findbugs/etc/findbugsfilter.xsd">
5
36 <Match>
47 <Bug pattern="DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED,NM_CLASS_NOT_EXCEPTION,EI_EXPOSE_REP,EI_EXPOSE_REP2,MS_PKGPROTECT,MS_MUTABLE_ARRAY"/>
58 </Match>
1922 <Bug pattern="DM_EXIT"/>
2023 </Match>
2124 <Match>
22 <Priority value="3"/>
23 </Match>
24 <Match>
2525 <BugCode name="Se"/>
2626 <Class name="~edu.umd.cs.findbugs.gui.*"/>
2727 </Match>
2828 <Match>
2929 <Class name="~edu.umd.cs.findbugs.*Test"/>
3030 </Match>
31
3132 <Match>
32 <BugCode name="Se"/>
33 <Or>
34 <BugCode name="Se"/>
35 <Bug pattern="SE_NO_SERIALVERSIONID"/>
36 </Or>
3337 <Class name="~edu.umd.cs.findbugs.gui2.*"/>
3438 </Match>
35 <Match>
36 <Class name="edu.umd.cs.findbugs.ba.DFSEdgeTypes"/>
37 <Bug pattern="NM_SAME_SIMPLE_NAME_AS_INTERFACE"/>
38 </Match>
39 <Match>
40 <Class name="edu.umd.cs.findbugs.ba.DepthFirstSearch"/>
41 <Bug pattern="NM_SAME_SIMPLE_NAME_AS_SUPERCLASS"/>
42 </Match>
43 <Match>
44 <Class name="edu.umd.cs.findbugs.ba.ReverseDepthFirstSearch"/>
45 <Bug pattern="NM_SAME_SIMPLE_NAME_AS_SUPERCLASS"/>
46 </Match>
47 <Match>
48 <Class name="edu.umd.cs.findbugs.detect.IncompatMask"/>
49 <Bug pattern="SF_SWITCH_FALLTHROUGH"/>
50 </Match>
51 <Match>
52 <Class name="edu.umd.cs.findbugs.detect.TestingGround"/>
53 <Bug pattern="URF_UNREAD_FIELD"/>
54 </Match>
55 <Match>
56 <Class name="edu.umd.cs.findbugs.jaif.JAIFToken"/>
57 <Bug pattern="URF_UNREAD_FIELD"/>
58 </Match>
59 <Match>
60 <Bug pattern="OS_OPEN_STREAM"/>
61 <Class name="edu.umd.cs.findbugs.util.Util"/>
62 </Match>
63 <Match>
64 <Bug pattern="URF_UNREAD_FIELD"/>
65 <Class name="edu.umd.cs.findbugs.classfile.engine.ClassParser"/>
66 </Match>
67 <Match>
68 <Bug pattern="URF_UNREAD_FIELD"/>
69 <Class name="edu.umd.cs.findbugs.classfile.engine.ClassParserUsingASM$1$2"/>
70 </Match>
71 <And>
72 <Bug pattern="OBL_UNSATISFIED_OBLIGATION"/>
73 <Class name="edu.umd.cs.findbugs.gui2.MainFrame"/>
74 </And>
75 <And>
76 <Bug pattern="OBL_UNSATISFIED_OBLIGATION"/>
77 <Class name="edu.umd.cs.findbugs.ml.GenerateUIDs"/>
78 </And>
79 <And>
80 <Bug pattern="OBL_UNSATISFIED_OBLIGATION"/>
81 <Class name="edu.umd.cs.findbugs.userAnnotations.ri.XMLFileUserAnnotationPlugin"/>
82 </And>
39
8340 <Match>
84 <Bug pattern="OBL_UNSATISFIED_OBLIGATION"/>
85 <Class name="edu.umd.cs.findbugs.util.Util"/>
41 <Class name="edu.umd.cs.findbugs.ba.DFSEdgeTypes" />
42 <Bug pattern="NM_SAME_SIMPLE_NAME_AS_INTERFACE" />
8643 </Match>
8744 <Match>
88 <Bug pattern="OBL_UNSATISFIED_OBLIGATION"/>
89 <Class name="edu.umd.cs.findbugs.workflow.RejarClassesForAnalysis"/>
45 <Class name="edu.umd.cs.findbugs.ba.DepthFirstSearch" />
46 <Bug pattern="NM_SAME_SIMPLE_NAME_AS_SUPERCLASS" />
9047 </Match>
9148 <Match>
92 <Bug pattern="OBL_UNSATISFIED_OBLIGATION"/>
93 <Class name="edu.umd.cs.findbugs.xml.OutputStreamXMLOutput"/>
49 <Class name="edu.umd.cs.findbugs.ba.ReverseDepthFirstSearch" />
50 <Bug pattern="NM_SAME_SIMPLE_NAME_AS_SUPERCLASS" />
9451 </Match>
95 <Match>
96 <Bug pattern="URF_UNREAD_FIELD"/>
97 <Class name="edu.umd.cs.findbugs.classfile.engine.ClassParserUsingASM$1"/>
52
53 <Match>
54 <And>
55 <Priority value="3"/>
56 <Or>
57 <Bug pattern="DM_CONVERT_CASE"/>
58 <Bug pattern="SE_COMPARATOR_SHOULD_BE_SERIALIZABLE"/>
59 <Bug pattern="SE_NO_SERIALVERSIONID" />
60 <Bug pattern="URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD"/>
61 <Bug pattern="SIC_INNER_SHOULD_BE_STATIC_ANON" />
62 <Bug pattern="BC_UNCONFIRMED_CAST_OF_RETURN_VALUE" />
63 <Bug pattern="BC_UNCONFIRMED_CAST" />
64 <Bug pattern="UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR" />
65 <Bug pattern="NM_CONFUSING" />
66 <Rank value="20"/>
67 </Or>
68 </And>
9869 </Match>
99 <Match>
100 <Bug pattern="URF_UNREAD_FIELD"/>
101 <Class name="edu.umd.cs.findbugs.userAnnotations.SimpleWebPlugin"/>
102 </Match>
70
10371 </FindBugsFilter>
(No changes)
33 <groupId>org.sonatype.oss</groupId>
44 <artifactId>oss-parent</artifactId>
55 <version>7</version>
6 <relativePath></relativePath>
67 </parent>
78
89 <groupId>com.google.code.findbugs</groupId>
910 <artifactId>findbugs</artifactId>
10 <version>2.0.3-SNAPSHOT</version>
11 <version>3.0.1-SNAPSHOT</version>
1112 <packaging>jar</packaging>
1213
1314 <url>http://findbugs.sourceforge.net/</url>
2829
2930
3031 <scm>
31 <connection>scm:svn:http://findbugs.googlecode.com/svn/trunk/</connection>
32 <developerConnection>scm:svn:https://findbugs.googlecode.com/svn/trunk/</developerConnection>
33 <url>http://findbugs.googlecode.com/svn/trunk/</url>
32 <connection>scm:git:http://code.google.com/p/findbugs/</connection>
33 <developerConnection>scm:git:https://code.google.com/p/findbugs/</developerConnection>
34 <url>https://code.google.com/p/findbugs/</url>
3435 </scm>
3536
3637 <developers>
157158 </roles>
158159 <timezone>-5</timezone>
159160 </contributor>
161 <contributor>
162 <name>Tagir Valeev</name>
163 <email></email>
164 <url></url>
165 <roles>
166 <role></role>
167 </roles>
168 <timezone>+6</timezone>
169 </contributor>
160170 </contributors>
161171
162172 <properties>
163 <asmVersion>3.3</asmVersion>
173 <asmVersion>5.0.2</asmVersion>
164174 <junitVersion>4.11</junitVersion>
165175 </properties>
166176
177187 </dependency>
178188 <dependency>
179189 <groupId>com.google.code.findbugs</groupId>
180 <artifactId>bcel</artifactId>
181 <version>2.0.1</version>
190 <artifactId>bcel-findbugs</artifactId>
191 <version>6.0</version>
182192 </dependency>
183193 <dependency>
184194 <groupId>com.google.code.findbugs</groupId>
197207 <scope>provided</scope>
198208 </dependency>
199209 <dependency>
200 <groupId>asm</groupId>
201 <artifactId>asm</artifactId>
210 <groupId>org.ow2.asm</groupId>
211 <artifactId>asm-debug-all</artifactId>
202212 <version>${asmVersion}</version>
203213 </dependency>
204214 <dependency>
205 <groupId>asm</groupId>
215 <groupId>org.ow2.asm</groupId>
206216 <artifactId>asm-commons</artifactId>
207217 <version>${asmVersion}</version>
208218 </dependency>
297307 <plugins>
298308 <plugin>
299309 <groupId>org.apache.maven.plugins</groupId>
310 <artifactId>maven-javadoc-plugin</artifactId>
311 <version>2.9.1</version>
312 <configuration>
313 <sourcepath>src/java</sourcepath>
314 </configuration>
315 </plugin>
316 <plugin>
317 <groupId>org.apache.maven.plugins</groupId>
300318 <artifactId>maven-compiler-plugin</artifactId>
301319 <version>3.0</version>
302320 <configuration>
303 <source>1.5</source>
304 <target>1.5</target>
321 <source>1.7</source>
322 <target>1.7</target>
305323 <excludes>
306324 <exclude>**/junit/**</exclude>
307325 <exclude>**/infonodeJava/**</exclude>
0 #! /bin/sh
1 mvn install:install-file -Dfile=lib/bcel-6.0-SNAPSHOT.jar -DgroupId=com.google.code.findbugs -DartifactId=bcel -Dversion=6.0-SNAPSHOT -Dpackaging=jar
2 mvn install:install-file -Dfile=lib/asm-debug-all-5.0.2.jar -DgroupId=org.ow2.asm -DartifactId=asm-debug-all -Dversion=5.0.2 -Dpackaging=jar
3 mvn clean
4 mvn package
3232 /**
3333 * Abstract base class for Ant tasks that run programs (main() methods) in
3434 * findbugs.jar or findbugsGUI.jar.
35 *
35 *
3636 * @author David Hovemeyer
3737 */
3838 public abstract class AbstractFindBugsTask extends Task {
6868 }
6969 }
7070
71 private String mainClass;
71 private final String mainClass;
7272
7373 private boolean debug = false;
7474
8282
8383 private boolean failOnError = false;
8484
85 private String errorProperty = null;
86
87 private List<SystemProperty> systemPropertyList = new ArrayList<SystemProperty>();
85 protected String errorProperty = null;
86
87 private final List<SystemProperty> systemPropertyList = new ArrayList<SystemProperty>();
8888
8989 private Path classpath = null;
9090
139139
140140 /**
141141 * Set timeout in milliseconds.
142 *
142 *
143143 * @param timeout
144144 * the timeout
145145 */
199199 Path path = createClasspath();
200200 path.setRefid(r);
201201 path.toString(); // Evaluated for its side-effects (throwing a
202 // BuildException)
202 // BuildException)
203203 }
204204
205205 /**
267267 }
268268
269269 for (SystemProperty systemProperty : systemPropertyList) {
270 if (systemProperty.getName() == null || systemProperty.getValue() == null)
270 if (systemProperty.getName() == null || systemProperty.getValue() == null) {
271271 throw new BuildException("systemProperty elements must have name and value attributes");
272 }
272273 }
273274 }
274275
281282 findbugsEngine.setProject(getProject());
282283 findbugsEngine.setTaskName(getTaskName());
283284 findbugsEngine.setFork(true);
284 if (jvm.length() > 0)
285 if (jvm.length() > 0) {
285286 findbugsEngine.setJvm(jvm);
287 }
286288 findbugsEngine.setTimeout(timeout);
287289
288290 if (debug) {
301303 // Use findbugs.home to locate findbugs.jar and the standard
302304 // plugins. This is the usual means of initialization.
303305 File findbugsLib = new File(homeDir, "lib");
304 if (!findbugsLib.exists() && homeDir.getName().equals("lib")) {
306 if (!findbugsLib.exists() && "lib".equals(homeDir.getName())) {
305307 findbugsLib = homeDir;
306308 homeDir = homeDir.getParentFile();
307309 }
308310 File findbugsLibFindBugs = new File(findbugsLib, "findbugs.jar");
309311 // log("executing using home dir [" + homeDir + "]");
310 if (findbugsLibFindBugs.exists())
312 if (findbugsLibFindBugs.exists()) {
311313 findbugsEngine.setClasspath(new Path(getProject(), findbugsLibFindBugs.getPath()));
312 else
314 } else {
313315 throw new IllegalArgumentException("Can't find findbugs.jar in " + findbugsLib);
316 }
314317 findbugsEngine.createJvmarg().setValue("-Dfindbugs.home=" + homeDir.getPath());
315318 } else {
316319 // Use an explicitly specified classpath and list of plugin Jars
337340
338341 /**
339342 * Add an argument to the JVM used to execute FindBugs.
340 *
343 *
341344 * @param arg
342345 * the argument
343346 */
355358
356359 /**
357360 * Create a new JVM to do the work.
358 *
361 *
359362 * @since Ant 1.5
360363 */
361364 private void execFindbugs() throws BuildException {
403406 protected abstract void beforeExecuteJavaProcess();
404407
405408 protected void afterExecuteJavaProcess(int rc) {
406 if (rc != 0)
409 if (rc != 0) {
407410 throw new BuildException("execution of " + getTaskName() + " failed");
411 }
408412
409413 }
410414
2626
2727 /**
2828 * Ant task to create/update a bug history database.
29 *
29 *
3030 * @author David Hovemeyer
3131 */
3232 public class ComputeBugHistoryTask extends AbstractFindBugsTask {
4545
4646 private boolean withMessages;
4747
48 private List<DataFile> dataFileList;
48 private final List<DataFile> dataFileList;
4949
5050 public ComputeBugHistoryTask() {
5151 super("edu.umd.cs.findbugs.workflow.Update");
8585 /**
8686 * Called to create DataFile objects in response to nested &lt;DataFile&gt;
8787 * elements.
88 *
88 *
8989 * @return new DataFile object specifying the location of an input data file
9090 */
9191 public DataFile createDataFile() {
9696
9797 /*
9898 * (non-Javadoc)
99 *
99 *
100100 * @see edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#checkParameters()
101101 */
102102 @Override
110110
111111 /*
112112 * (non-Javadoc)
113 *
113 *
114114 * @see
115115 * edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#configureFindbugsEngine
116116 * ()
145145
146146 /*
147147 * (non-Javadoc)
148 *
148 *
149149 * @see
150150 * edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#beforeExecuteJavaProcess
151151 * ()
157157
158158 /*
159159 * (non-Javadoc)
160 *
160 *
161161 * @see
162162 * edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#afterExecuteJavaProcess
163163 * (int)
1919 package edu.umd.cs.findbugs.anttask;
2020
2121 import org.apache.tools.ant.BuildException;
22 import org.apache.tools.ant.Project;
23
24 import edu.umd.cs.findbugs.ExitCodes;
2225
2326 /**
2427 * Ant task to generate HTML or plain text from a saved XML analysis results
2528 * file.
26 *
29 *
2730 * @author David Hovemeyer
2831 */
2932 public class ConvertXmlToTextTask extends AbstractFindBugsTask {
3134 private boolean longBugCodes;
3235
3336 private boolean applySuppression;
37
38 private boolean failIfBugFound;
3439
3540 private String input;
3641
100105 this.format = format;
101106 }
102107
103 /*
104 * (non-Javadoc)
105 *
106 * @see edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#checkParameters()
108 /**
109 * @param failIfBugFound true to 'fail' at the end if at least one bug is reported
107110 */
111 public void setFailIfBugFound(boolean failIfBugFound) {
112 this.failIfBugFound = failIfBugFound;
113 }
114
108115 @Override
109116 protected void checkParameters() {
110117 if (input == null) {
111118 throw new BuildException("input attribute is required", getLocation());
112119 }
113 if (!format.equals("text") && !(format.equals("html") || format.startsWith("html:"))) {
120 if (!"text".equals(format) && !("html".equals(format) || format.startsWith("html:"))) {
114121 throw new BuildException("invalid value " + format + " for format attribute", getLocation());
115122 }
116123
117124 }
118125
119 /*
120 * (non-Javadoc)
121 *
122 * @see
123 * edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#configureFindbugsEngine
124 * ()
125 */
126126 @Override
127127 protected void configureFindbugsEngine() {
128 addArg("-exitcode");
128129 if (format.startsWith("html")) {
129130 addArg("-" + format);
130131 }
135136 addArg("-applySuppression");
136137 }
137138 addArg(input);
138 if (output != null)
139 if (output != null) {
139140 addArg(output);
141 }
140142 }
141143
142 /*
143 * (non-Javadoc)
144 *
145 * @see
146 * edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#beforeExecuteJavaProcess
147 * ()
148 */
149144 @Override
150145 protected void beforeExecuteJavaProcess() {
151 if (output != null)
146 if (output != null) {
152147 log("Converting " + input + " to " + output + " using format " + format);
153 else
148 } else {
154149 log("Converting " + input + " using format " + format);
150 }
155151 }
156152
157 /*
158 * (non-Javadoc)
159 *
160 * @see
161 * edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#afterExecuteJavaProcess
162 * (int)
163 */
164153 @Override
165154 protected void afterExecuteJavaProcess(int rc) {
166155 if (rc == 0) {
167156 log("Success");
157 } else {
158 if (errorProperty != null) {
159 getProject().setProperty(errorProperty, "true");
160 }
161 if ((rc & ExitCodes.ERROR_FLAG) != 0) {
162 String message = "At least one error occured!";
163 if (failIfBugFound) {
164 throw new BuildException(message);
165 } else {
166 log(message, Project.MSG_ERR);
167 }
168 }
169 if ((rc & ExitCodes.BUGS_FOUND_FLAG) != 0) {
170 String message = "At least one unexpected bug is reported!";
171 if (failIfBugFound) {
172 throw new BuildException(message);
173 } else {
174 log(message, Project.MSG_ERR);
175 }
176 }
168177 }
169178 }
170179
2020
2121 /**
2222 * Input file used with ComputeBugHistoryTask or FilterBugsTask.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public class DataFile {
2525 /**
2626 * Ant task to invoke the FilterBugs program in the workflow package (a.k.a. the
2727 * filterBugs script.)
28 *
28 *
2929 * @author David Hovemeyer
3030 */
3131 public class FilterBugsTask extends AbstractFindBugsTask {
233233 return;
234234 }
235235 attrVal = attrVal.toLowerCase();
236 if (!attrVal.equals("true") && !attrVal.equals("false")) {
236 if (!"true".equals(attrVal) && !"false".equals(attrVal)) {
237237 throw new BuildException("attribute " + attrName + " requires boolean value", getLocation());
238238 }
239239 }
240240
241241 /*
242242 * (non-Javadoc)
243 *
243 *
244244 * @see edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#checkParameters()
245245 */
246246 @Override
281281
282282 /*
283283 * (non-Javadoc)
284 *
284 *
285285 * @see
286286 * edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#configureFindbugsEngine
287287 * ()
328328
329329 /*
330330 * (non-Javadoc)
331 *
331 *
332332 * @see
333333 * edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#beforeExecuteJavaProcess
334334 * ()
340340
341341 /*
342342 * (non-Javadoc)
343 *
343 *
344344 * @see
345345 * edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#afterExecuteJavaProcess
346346 * (int)
6161 * of classes and packages - See the textui argument description for details)
6262 * <li>output (enum text|xml|xml:withMessages|html - default xml)
6363 * <li>outputFile (name of output file to create)
64 * <li>nested (boolean default true)
6465 * <li>noClassOk (boolean default false)
6566 * <li>pluginList (list of plugin Jar files to load)
6667 * <li>projectFile (project filename)
7778 * <li>visitors (collection - comma seperated)
7879 * <li>chooseVisitors (selectively enable/disable visitors)
7980 * <li>workHard (boolean default false)
81 * <li>setSetExitCode (boolean default true)
8082 * </ul>
8183 * Of these arguments, the <b>home</b> is required. <b>projectFile</b> is
8284 * required if nested &lt;class&gt; or &lt;auxAnalyzepath&gt elements are not
161163
162164 private boolean noClassOk;
163165
166 private boolean nested = true;
167
168 private boolean setExitCode = true;
169
164170 private final List<FileSet> filesets = new ArrayList<FileSet>();
165171
166172 public FindBugsTask() {
197203 }
198204
199205 /**
206 * Set the exit code flag.
207 *
208 * @param setExitCode
209 * If true then the exit code will be returned to
210 * the main ant job
211 */
212 public void setSetExitCode(boolean setExitCode) {
213 this.setExitCode = setExitCode;
214 }
215
216 /**
217 * Set the nested flag.
218 *
219 * @param nested
220 * This option enables or disables scanning of
221 * nested jar and zip files found in the list of files
222 * and directories to be analyzed. By default, scanning
223 * of nested jar/zip files is enabled
224 */
225 public void setNested(boolean nested) {
226 this.nested = nested;
227 }
228
229 /**
200230 * Set the noClassOk flag.
201231 *
202232 * @param noClassOk
349379 * Set the exclude filter file
350380 */
351381 public void setExcludeFilter(File filterFile) {
352 if (filterFile != null && filterFile.length() > 0)
353
382 if (filterFile != null && filterFile.length() > 0) {
354383 this.excludeFile = filterFile;
355 else
384 } else {
356385 this.excludeFile = null;
386 }
357387 }
358388
359389 /**
360390 * Set the exclude filter file
361391 */
362392 public void setIncludeFilter(File filterFile) {
363 if (filterFile != null && filterFile.length() > 0)
393 if (filterFile != null && filterFile.length() > 0) {
364394 this.includeFile = filterFile;
365 else
395 } else {
366396 this.includeFile = null;
397 }
367398 }
368399
369400 /**
370401 * Set the exclude filter file
371402 */
372403 public void setBaselineBugs(File baselineBugs) {
373 if (baselineBugs != null && baselineBugs.length() > 0)
404 if (baselineBugs != null && baselineBugs.length() > 0) {
374405 this.baselineBugs = baselineBugs;
375 else
406 } else {
376407 this.baselineBugs = null;
408 }
377409 }
378410
379411 /**
398430
399431 String[] elementList = src.list();
400432 for (String anElementList : elementList) {
401 if (!anElementList.equals("")) {
433 if (!"".equals(anElementList)) {
402434 nonEmpty = true;
403435 break;
404436 }
430462 Path path = createAuxClasspath();
431463 path.setRefid(r);
432464 path.toString(); // Evaluated for its side-effects (throwing a
433 // BuildException)
465 // BuildException)
434466 }
435467
436468 /**
441473
442474 String[] elementList = src.list();
443475 for (String anElementList : elementList) {
444 if (!anElementList.equals("")) {
476 if (!"".equals(anElementList)) {
445477 nonEmpty = true;
446478 break;
447479 }
514546 * Set name of output file.
515547 */
516548 public void setOutputFile(String outputFileName) {
517 if (outputFileName != null && outputFileName.length() > 0)
549 if (outputFileName != null && outputFileName.length() > 0) {
518550 this.outputFileName = outputFileName;
551 }
519552 }
520553
521554 /**
544577 + "elements must be defined for task <" + getTaskName() + "/>", getLocation());
545578 }
546579
547 if (cloudId != null && cloudId.contains(" "))
580 if (cloudId != null && cloudId.contains(" ")) {
548581 throw new BuildException("cloudId must not contain spaces: '" + cloudId + "'");
582 }
549583
550584 if (outputFormat != null
551 && !(outputFormat.trim().equalsIgnoreCase("xml") || outputFormat.trim().equalsIgnoreCase("xml:withMessages")
552 || outputFormat.trim().equalsIgnoreCase("html") || outputFormat.trim().equalsIgnoreCase("text")
553 || outputFormat.trim().equalsIgnoreCase("xdocs") || outputFormat.trim().equalsIgnoreCase("emacs"))) {
585 && !("xml".equalsIgnoreCase(outputFormat.trim()) || "xml:withMessages".equalsIgnoreCase(outputFormat.trim())
586 || "html".equalsIgnoreCase(outputFormat.trim()) || "text".equalsIgnoreCase(outputFormat.trim())
587 || "xdocs".equalsIgnoreCase(outputFormat.trim()) || "emacs".equalsIgnoreCase(outputFormat.trim()))) {
554588 throw new BuildException("output attribute must be either " + "'text', 'xml', 'html', 'xdocs' or 'emacs' for task <"
555589 + getTaskName() + "/>", getLocation());
556590 }
557591
558592 if (reportLevel != null
559 && !(reportLevel.trim().equalsIgnoreCase("experimental") || reportLevel.trim().equalsIgnoreCase("low")
560 || reportLevel.trim().equalsIgnoreCase("medium") || reportLevel.trim().equalsIgnoreCase("high"))) {
593 && !("experimental".equalsIgnoreCase(reportLevel.trim()) || "low".equalsIgnoreCase(reportLevel.trim())
594 || "medium".equalsIgnoreCase(reportLevel.trim()) || "high".equalsIgnoreCase(reportLevel.trim()))) {
561595 throw new BuildException("reportlevel attribute must be either "
562596 + "'experimental' or 'low' or 'medium' or 'high' for task <" + getTaskName() + "/>", getLocation());
563597 }
643677 addArg(adjustPriority);
644678 }
645679
646 if (sorted)
680 if (sorted) {
647681 addArg("-sortByClass");
648 if (timestampNow)
682 }
683 if (timestampNow) {
649684 addArg("-timestampNow");
650
651 if (outputFormat != null && !outputFormat.trim().equalsIgnoreCase("text")) {
685 }
686
687 if (outputFormat != null && !"text".equalsIgnoreCase(outputFormat.trim())) {
652688 outputFormat = outputFormat.trim();
653689 String outputArg = "-";
654690 int colon = outputFormat.indexOf(':');
665701 }
666702 addArg(outputArg);
667703 }
668 if (quietErrors)
704 if (quietErrors) {
669705 addArg("-quiet");
670 if (reportLevel != null)
706 }
707 if (reportLevel != null) {
671708 addArg("-" + reportLevel.trim().toLowerCase());
709 }
672710 if (projectFile != null) {
673711 addArg("-project");
674712 addArg(projectFile.getPath());
715753 @SuppressWarnings("unused")
716754 String unreadReference = auxClasspath.toString();
717755 String auxClasspathString = auxClasspath.toString();
718 if (auxClasspathString.length() > 100) {
719 addArg("-auxclasspathFromInput");
720 setInputString(auxClasspathString);
721 } else {
722 addArg("-auxclasspath");
723 addArg(auxClasspathString);
756 if (!auxClasspathString.isEmpty()) {
757 if (auxClasspathString.length() > 100) {
758 addArg("-auxclasspathFromInput");
759 setInputString(auxClasspathString);
760 } else {
761 addArg("-auxclasspath");
762 addArg(auxClasspathString);
763 }
724764 }
725765 } catch (Throwable t) {
726766 log("Warning: auxClasspath " + t + " not found.");
737777 if (relaxed) {
738778 addArg("-relaxed");
739779 }
780 if (!nested) {
781 addArg("-nested:false");
782 }
740783 if (noClassOk) {
741784 addArg("-noClassOk");
742785 }
786
743787 if (onlyAnalyze != null) {
744788 addArg("-onlyAnalyze");
745789 addArg(onlyAnalyze);
746790 }
747791
748 addArg("-exitcode");
792 if (setExitCode) {
793 addArg("-exitcode");
794 }
795
749796 for (ClassLocation classLocation : classLocations) {
750797 addArg(classLocation.toString());
751798 }
767814 }
768815 }
769816
770 // vim:ts=4
3030
3131 /**
3232 * FindBugsViewerTask.java -- Ant Task to launch the FindBugsFrame
33 *
33 *
3434 * To use, create a new task that refrences the ant task (such as
3535 * "findbugs-viewer"). Then call this task while passing in parameters to modify
3636 * it's behaviour. It supports several options that are the same as the findbugs
3737 * task:
38 *
38 *
3939 * -projectFile -debug -jvmargs -home -classpath -pluginList -timeout
40 *
40 *
4141 * It also adds some new options:
42 *
42 *
4343 * -look: string name representing look and feel. Can be "native", "plastic" or
4444 * "gtk" -loadbugs: file name of bug report to load
45 *
45 *
4646 * The below is an example of how this could be done in an ant script:
47 *
47 *
4848 * <taskdef name="findbugs" classname="edu.umd.cs.findbugs.anttask.FindBugsTask"
4949 * classpath="C:\dev\cvs.sourceforge.net\findbugs\lib\findbugs-ant.jar" />
5050 * <taskdef name="findbugs-viewer"
5151 * classname="edu.umd.cs.findbugs.anttask.FindBugsViewerTask"
5252 * classpath="C:\dev\cvs.sourceforge.net\findbugs\lib\findbugs-ant.jar" />
53 *
53 *
5454 * <property name="findbugs.home" location="C:\dev\cvs.sourceforge.net\findbugs"
5555 * /> <property name="findbugs.bugReport" location="bcel-fb.xml" />
56 *
56 *
5757 * <target name="findbugs-viewer" depends="jar"> <findbugs-viewer
5858 * home="${findbugs.home}" look="native" loadbugs="${findbugs.bugReport}"/>
5959 * </target>
60 *
60 *
6161 * Created on March 21, 2006, 12:57 PM
62 *
62 *
6363 * @author Mark McKay, mark@kitfox.com
6464 */
6565 public class FindBugsViewerTask extends Task {
9393
9494 /**
9595 * Sets the file that contains the XML output of a findbugs report.
96 *
96 *
9797 * @param loadbugs
9898 * XML output from a findbugs session
9999 */
183183
184184 /**
185185 * Set timeout in milliseconds.
186 *
186 *
187187 * @param timeout
188188 * the timeout
189189 */
193193
194194 /**
195195 * Add an argument to the JVM used to execute FindBugs.
196 *
196 *
197197 * @param arg
198198 * the argument
199199 */
225225 File findbugsLibFindBugs = new File(findbugsLib, "findbugs.jar");
226226 File findBugsFindBugs = new File(homeDir, "findbugs.jar");
227227 // log("executing using home dir [" + homeDir + "]");
228 if (findbugsLibFindBugs.exists())
228 if (findbugsLibFindBugs.exists()) {
229229 findbugsEngine.setClasspath(new Path(getProject(), findbugsLibFindBugs.getPath()));
230 else if (findBugsFindBugs.exists())
230 } else if (findBugsFindBugs.exists()) {
231231 findbugsEngine.setClasspath(new Path(getProject(), findBugsFindBugs.getPath()));
232 else
232 } else {
233233 throw new IllegalArgumentException("Can't find findbugs.jar in " + homeDir);
234 }
234235
235236 findbugsEngine.setClassname("edu.umd.cs.findbugs.LaunchAppropriateUI");
236237 findbugsEngine.createJvmarg().setValue("-Dfindbugs.home=" + homeDir.getPath());
2424
2525 /**
2626 * Ant task to invoke the MineBugHistory program in the workflow package
27 *
27 *
2828 * @author David Hovemeyer
2929 * @author Ben Langmead
3030 */
7979 return;
8080 }
8181 attrVal = attrVal.toLowerCase();
82 if (!attrVal.equals("true") && !attrVal.equals("false")) {
82 if (!"true".equals(attrVal) && !"false".equals(attrVal)) {
8383 throw new BuildException("attribute " + attrName + " requires boolean value", getLocation());
8484 }
8585 }
8686
8787 /*
8888 * (non-Javadoc)
89 *
89 *
9090 * @see edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#checkParameters()
9191 */
9292 @Override
110110
111111 /*
112112 * (non-Javadoc)
113 *
113 *
114114 * @see
115115 * edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#configureFindbugsEngine
116116 * ()
129129
130130 /*
131131 * (non-Javadoc)
132 *
132 *
133133 * @see
134134 * edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#beforeExecuteJavaProcess
135135 * ()
141141
142142 /*
143143 * (non-Javadoc)
144 *
144 *
145145 * @see
146146 * edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#afterExecuteJavaProcess
147147 * (int)
2222
2323 /**
2424 * Ant task to invoke the SetBugDatabaseInfo program in the workflow package
25 *
25 *
2626 * @author David Hovemeyer
2727 * @author Ben Langmead
2828 */
9292 return;
9393 }
9494 attrVal = attrVal.toLowerCase();
95 if (!attrVal.equals("true") && !attrVal.equals("false")) {
95 if (!"true".equals(attrVal) && !"false".equals(attrVal)) {
9696 throw new BuildException("attribute " + attrName + " requires boolean value", getLocation());
9797 }
9898 }
9999
100100 /*
101101 * (non-Javadoc)
102 *
102 *
103103 * @see edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#checkParameters()
104104 */
105105 @Override
133133
134134 /*
135135 * (non-Javadoc)
136 *
136 *
137137 * @see
138138 * edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#configureFindbugsEngine
139139 * ()
146146 addOption("-findSource", findSource);
147147 addOption("-suppress", suppress);
148148 addBoolOption("-withMessages", withMessages);
149 if (resetSource != null && resetSource.equals("true")) {
149 if (resetSource != null && "true".equals(resetSource)) {
150150 addArg("-resetSource");
151151 }
152152 addArg(inputFile);
157157
158158 /*
159159 * (non-Javadoc)
160 *
160 *
161161 * @see
162162 * edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#beforeExecuteJavaProcess
163163 * ()
169169
170170 /*
171171 * (non-Javadoc)
172 *
172 *
173173 * @see
174174 * edu.umd.cs.findbugs.anttask.AbstractFindBugsTask#afterExecuteJavaProcess
175175 * (int)
3535 /**
3636 * An ant task that is wraps the behavior of the UnionResults executable into an
3737 * ant task.
38 *
38 *
3939 * <taskdef name="UnionBugs" classname="edu.umd.cs.findbugs.anttask.UnionBugs"
4040 * classpath="...">
41 *
41 *
4242 * <UnionBugs to="${basedir}/findbugs.xml" > <fileset dir="plugins"> <include
4343 * name="*_findbugs_partial.xml" /> </fileset> </UnionBugs>
44 *
44 *
4545 * @author Peter Franza <a href="mailto:pfranza@gmail.com">pfranza@gmail.com</a>
4646 * @version 1.0
47 *
47 *
4848 * @ant.task category="utility"
49 *
49 *
5050 */
5151 @Deprecated
5252 public class UnionBugs extends Task {
5353
54 private List<FileSet> fileSets = new ArrayList<FileSet>();
54 private final List<FileSet> fileSets = new ArrayList<FileSet>();
5555
5656 private String into;
5757
5858 /**
5959 * The fileset containing all the findbugs xml files that need to be merged
60 *
60 *
6161 * @param arg
6262 */
6363 public void addFileset(FileSet arg) {
6666
6767 /**
6868 * The File everything should get merged into
69 *
69 *
7070 * @param file
7171 */
7272 public void setTo(String file) {
136136
137137 /**
138138 * Copy a File
139 *
140 * @param File
139 *
140 * @param in
141141 * to Copy From
142 * @param File
142 * @param out
143143 * to Copy To
144144 * @throws IOException
145145 */
146146 private static void copyFile(File in, File out) throws IOException {
147 FileChannel inChannel = new FileInputStream(in).getChannel();
148 FileChannel outChannel = new FileOutputStream(out).getChannel();
149 try {
150 inChannel.transferTo(0, inChannel.size(), outChannel);
151 } catch (IOException e) {
152 throw e;
153 } finally {
154 if (inChannel != null)
155 inChannel.close();
156 if (outChannel != null)
157 outChannel.close();
147 try (FileInputStream inStream = new FileInputStream(in);
148 FileOutputStream outStream = new FileOutputStream(out);) {
149 FileChannel inChannel = inStream.getChannel();
150 inChannel.transferTo(0, inChannel.size(), outStream.getChannel());
158151 }
159152 }
160153
2727 /**
2828 * An ant task that is wraps the behavior of the UnionResults executable into an
2929 * ant task.
30 *
30 *
3131 * <taskdef name="UnionBugs2" classname="edu.umd.cs.findbugs.anttask.UnionBugs2"
3232 * classpath="...">
33 *
33 *
3434 * <UnionBugs2 to="${basedir}/findbugs.xml" > <fileset dir="plugins"> <include
3535 * name="*_findbugs_partial.xml" /> </fileset> </UnionBugs>
36 *
37 *
36 *
37 *
3838 * @ant.task category="utility"
39 *
39 *
4040 */
4141
4242 public class UnionBugs2 extends AbstractFindBugsTask {
4343
4444 private String to;
4545
46 private ArrayList<FileSet> fileSets = new ArrayList<FileSet>();
46 private final ArrayList<FileSet> fileSets = new ArrayList<FileSet>();
4747
4848 public UnionBugs2() {
4949 super("edu.umd.cs.findbugs.workflow.UnionResults");
5252
5353 /**
5454 * The fileset containing all the findbugs xml files that need to be merged
55 *
55 *
5656 * @param arg
5757 */
5858 public void addFileset(FileSet arg) {
5959 this.fileSets.add(arg);
6060 }
61
61
6262 /**
6363 * The File everything should get merged into
64 *
64 *
6565 * @param arg
6666 */
6767 public void setTo(String arg) {
7272 protected void checkParameters() {
7373 super.checkParameters();
7474
75 if (to == null)
75 if (to == null) {
7676 throw new BuildException("to attribute is required", getLocation());
77 }
7778
78 if (fileSets.size() < 1)
79 if (fileSets.size() < 1) {
7980 throw new BuildException("fileset is required");
81 }
8082 }
8183
82
84
8385 @Override
8486 protected void beforeExecuteJavaProcess() {
8587 log("unioning bugs...");
77
88 <body>
99
10 <table width="100%">
11 <tr>
12
13 @HTML_SIDEBAR@
14
15 <td align="left" valign="top">
16
17
18 <h1>FindBugs Change Log, Version @VERSION@</h1>
19 <ul>
20 <li>New Bug patterns: <a
21 href="http://findbugs.sourceforge.net/bugDescriptions.html#DM_BOXED_PRIMITIVE_FOR_PARSING">DM_BOXED_PRIMITIVE_FOR_PARSING</a>,
22 <a
23 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_METHOD_RETURN_RELAXING_ANNOTATION">NP_METHOD_RETURN_RELAXING_ANNOTATION</a>,
24 and
25 <a
26 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_METHOD_PARAMETER_TIGHTENS_ANNOTATION">NP_METHOD_PARAMETER_TIGHTENS_ANNOTATION</a>
27 </li>
28 <li>Add the ability in the GUI to save the currently viewable/filtered bugs to HTML output.
29 <li>When dataflow does't terminate, make sure we continue with
30 analysis.
31
32 <li>Fix some problems that resulting in dataflow analysis not
33 terminating
34
35 <li>Get parameter annotations from default parameters
36 annotations applied to the method.
37 <li>Add subversion change number to eclipse plugin qualifier.
38
39 <li>Disabled detector for <a
40 href="http://findbugs.sourceforge.net/bugDescriptions.html#AM_CREATES_EMPTY_JAR_FILE_ENTRY">AM_CREATES_EMPTY_JAR_FILE_ENTRY</a>;
41 it complaints inappropriately about code that creates directory
42 entries.
43
44 <li>Add warnings about incompatible types passed to
45 org.testng.Assert.assertEquals</li>
46 <li>Add logic that understands more of the Google Guava APIs.
47 <li>Disable type qualifier validator execution within Eclipse plugin;
48 too many problems with class loading and security manager (see #1154 Random obscure Eclipse failures)
49 <li>Consistently check both access flags and attributes to see if something is synthetic. Compiler is
50 inconsistent about where synthetic elements are marked.
51
52 <li>Fixed false positives for the following bug patterns (17
53 occurrences in findbugsTestCases):
54 <ul>
55 <li><a
56 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC">BC</a>
57 <li><a
58 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC_IMPOSSIBLE_INSTANCEOF">BC_IMPOSSIBLE_INSTANCEOF</a>
59 <li><a
60 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC_UNCONFIRMED_CAST">BC_UNCONFIRMED_CAST</a>
61 <li><a
62 href="http://findbugs.sourceforge.net/bugDescriptions.html#EC_UNRELATED_TYPES">EC_UNRELATED_TYPES</a>
63 <li><a
64 href="http://findbugs.sourceforge.net/bugDescriptions.html#INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE">INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE</a>
65 <li><a
66 href="http://findbugs.sourceforge.net/bugDescriptions.html#IS2_INCONSISTENT_SYNC">IS2_INCONSISTENT_SYNC</a>
67 <li><a
68 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS">NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS</a>
69 <li><a
70 href="http://findbugs.sourceforge.net/bugDescriptions.html#OBL_UNSATISFIED_OBLIGATION">OBL_UNSATISFIED_OBLIGATION</a>
71 <li><a
72 href="http://findbugs.sourceforge.net/bugDescriptions.html#RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE">RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE</a>
73 <li><a
74 href="http://findbugs.sourceforge.net/bugDescriptions.html#SA_FIELD_SELF_COMPARISON">SA_FIELD_SELF_COMPARISON</a>
75 <li><a
76 href="http://findbugs.sourceforge.net/bugDescriptions.html#TQ_UNKNOWN_VALUE_USED_WHERE_ALWAYS_STRICTLY_REQUIRED">TQ_UNKNOWN_VALUE_USED_WHERE_ALWAYS_STRICTLY_REQUIRED</a>
77 </li>
78 </ul>
79 <li>Fixed false negatives for the following bug patterns (45
80 occurrences in findbugsTestCases):
81 <ul>
82 <li><a
83 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC_UNCONFIRMED_CAST">BC_UNCONFIRMED_CAST</a>
84 <li><a
85 href="http://findbugs.sourceforge.net/bugDescriptions.html#DM_NUMBER_CTOR">DM_NUMBER_CTOR</a>
86 <li><a
87 href="http://findbugs.sourceforge.net/bugDescriptions.html#EC_ARRAY_AND_NONARRAY">EC_ARRAY_AND_NONARRAY</a>
88 <li><a
89 href="http://findbugs.sourceforge.net/bugDescriptions.html#EC_INCOMPATIBLE_ARRAY_COMPARE">EC_INCOMPATIBLE_ARRAY_COMPARE</a>
90 <li><a
91 href="http://findbugs.sourceforge.net/bugDescriptions.html#EC_UNRELATED_TYPES">EC_UNRELATED_TYPES</a>
92 <li><a
93 href="http://findbugs.sourceforge.net/bugDescriptions.html#GC_UNRELATED_TYPES">GC_UNRELATED_TYPES</a>
94 <li><a
95 href="http://findbugs.sourceforge.net/bugDescriptions.html#IS_FIELD_NOT_GUARDED">IS_FIELD_NOT_GUARDED</a>
96 <li><a
97 href="http://findbugs.sourceforge.net/bugDescriptions.html#IT_NO_SUCH_ELEMENT">IT_NO_SUCH_ELEMENT</a>
98 <li><a
99 href="http://findbugs.sourceforge.net/bugDescriptions.html#JCIP_FIELD_ISNT_FINAL_IN_IMMUTABLE_CLASS">JCIP_FIELD_ISNT_FINAL_IN_IMMUTABLE_CLASS</a>
100 <li><a
101 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_NULL_ON_SOME_PATH">NP_NULL_ON_SOME_PATH</a>
102 <li><a
103 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_NONNULL_PARAM_VIOLATION">NP_NONNULL_PARAM_VIOLATION</a>
104 <li><a
105 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE">NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE</a>
106 <li><a
107 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE">NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE</a>
108 <li><a
109 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_STORE_INTO_NONNULL_FIELD">NP_STORE_INTO_NONNULL_FIELD</a>
110 <li><a
111 href="http://findbugs.sourceforge.net/bugDescriptions.html#RE_POSSIBLE_UNINTENDED_PATTERN">RE_POSSIBLE_UNINTENDED_PATTERN</a>
112 <li><a
113 href="http://findbugs.sourceforge.net/bugDescriptions.html#SA_FIELD_SELF_COMPARISON">SA_FIELD_SELF_COMPARISON</a>
114 </ul>
115 </ul>
116 <h1>FindBugs Change Log, Version 2.0.2</h1>
117
118 <ul>
119 <li>Fix false positions for <a
120 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR">NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR</a>
121 - fixing <a
122 href="https://sourceforge.net/tracker/?func=detail&aid=3547559&group_id=96405&atid=614693">Bug3547559</a>,
123 <a
124 href="https://sourceforge.net/tracker/?func=detail&aid=3555408&group_id=96405&atid=614693">Bug3555408</a>,
125 <a
126 href="https://sourceforge.net/tracker/?func=detail&aid=3580266&group_id=96405&atid=614693">Bug3580266</a>
127 and <a
128 href="https://sourceforge.net/tracker/?func=detail&aid=3587164&group_id=96405&atid=614693">Bug3587164</a>.
129
130
131 </li>
132 <li>Fix false positives for <a
133 href="http://findbugs.sourceforge.net/bugDescriptions.html#SF_SWITCH_NO_DEFAULT">SF_SWITCH_NO_DEFAULT</a>
134 <li>Inline access methods for private fields,
10 <table width="100%">
11 <tr>
12
13 @HTML_SIDEBAR@
14
15 <td align="left" valign="top">
16
17
18 <h1>FindBugs Change Log, Version @VERSION@</h1>
19 <ul>
20 <li>New Bug patterns:
21 <ul>
22 <li>
23 <a
24 href="http://findbugs.sourceforge.net/bugDescriptions.html#BSHIFT_WRONG_ADD_PRIORITY">BSHIFT_WRONG_ADD_PRIORITY</a>,
25 <li>
26 <a
27 href="http://findbugs.sourceforge.net/bugDescriptions.html#CO_COMPARETO_INCORRECT_FLOATING">CO_COMPARETO_INCORRECT_FLOATING</a>,
28 <li>
29 <a
30 href="http://findbugs.sourceforge.net/bugDescriptions.html#DC_PARTIALLY_CONSTRUCTED">DC_PARTIALLY_CONSTRUCTED</a>,
31 <li>
32 <a
33 href="http://findbugs.sourceforge.net/bugDescriptions.html#DM_BOXED_PRIMITIVE_FOR_COMPARE">DM_BOXED_PRIMITIVE_FOR_COMPARE</a>,
34 <li>
35 <a
36 href="http://findbugs.sourceforge.net/bugDescriptions.html#DM_INVALID_MIN_MAX">DM_INVALID_MIN_MAX</a>,
37 <li>
38 <a
39 href="http://findbugs.sourceforge.net/bugDescriptions.html#ME_MUTABLE_ENUM_FIELD">ME_MUTABLE_ENUM_FIELD</a>,
40 <li>
41 <a
42 href="http://findbugs.sourceforge.net/bugDescriptions.html#ME_ENUM_FIELD_SETTER">ME_ENUM_FIELD_SETTER</a>,
43 <li>
44 <a
45 href="http://findbugs.sourceforge.net/bugDescriptions.html#MS_MUTABLE_COLLECTION">MS_MUTABLE_COLLECTION</a>,
46 <li>
47 <a
48 href="http://findbugs.sourceforge.net/bugDescriptions.html#MS_MUTABLE_COLLECTION_PKGPROTECT">MS_MUTABLE_COLLECTION_PKGPROTECT</a>,
49 <li>
50 <a
51 href="http://findbugs.sourceforge.net/bugDescriptions.html#RANGE_ARRAY_INDEX">RANGE_ARRAY_INDEX</a>,
52 <li>
53 <a
54 href="http://findbugs.sourceforge.net/bugDescriptions.html#RANGE_ARRAY_OFFSET">RANGE_ARRAY_OFFSET</a>,
55 <li>
56 <a
57 href="http://findbugs.sourceforge.net/bugDescriptions.html#RANGE_ARRAY_LENGTH">RANGE_ARRAY_LENGTH</a>,
58 <li>
59 <a
60 href="http://findbugs.sourceforge.net/bugDescriptions.html#RANGE_STRING_INDEX">RANGE_STRING_INDEX</a>,
61 <li>
62 <a
63 href="http://findbugs.sourceforge.net/bugDescriptions.html#RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT">RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT</a>,
64 <li>
65 <a
66 href="http://findbugs.sourceforge.net/bugDescriptions.html#UC_USELESS_CONDITION">UC_USELESS_CONDITION</a>,
67 <li>
68 <a
69 href="http://findbugs.sourceforge.net/bugDescriptions.html#UC_USELESS_CONDITION_TYPE">UC_USELESS_CONDITION_TYPE</a>,
70 <li>
71 <a
72 href="http://findbugs.sourceforge.net/bugDescriptions.html#UC_USELESS_OBJECT">UC_USELESS_OBJECT</a>,
73 <li>
74 <a
75 href="http://findbugs.sourceforge.net/bugDescriptions.html#UC_USELESS_OBJECT_STACK">UC_USELESS_OBJECT_STACK</a>,
76 <li>
77 <a
78 href="http://findbugs.sourceforge.net/bugDescriptions.html#UC_USELESS_VOID_METHOD">UC_USELESS_VOID_METHOD</a>
79 </ul>
80 </li>
81 <li>Improved Bug patterns:
82 <ul>
83 <li>
84 <a
85 href="http://findbugs.sourceforge.net/bugDescriptions.html#INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE">INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE</a>,
86 <li>
87 <a
88 href="http://findbugs.sourceforge.net/bugDescriptions.html#RpC_REPEATED_CONDITIONAL_TEST">RpC_REPEATED_CONDITIONAL_TEST</a>,
89 <li>
90 <a
91 href="http://findbugs.sourceforge.net/bugDescriptions.html#WMI_WRONG_MAP_ITERATOR">WMI_WRONG_MAP_ITERATOR</a>,
92 <li>
93 <a
94 href="http://findbugs.sourceforge.net/bugDescriptions.html#DMI_HARDCODED_ABSOLUTE_FILENAME">DMI_HARDCODED_ABSOLUTE_FILENAME</a>,
95 <li>
96 <a
97 href="http://findbugs.sourceforge.net/bugDescriptions.html#DMI_EMPTY_DB_PASSWORD">DMI_EMPTY_DB_PASSWORD</a>,
98 <li>
99 <a
100 href="http://findbugs.sourceforge.net/bugDescriptions.html#DMI_CONSTANT_DB_PASSWORD">DMI_CONSTANT_DB_PASSWORD</a>,
101 <li>
102 <a
103 href="http://findbugs.sourceforge.net/bugDescriptions.html#PT_ABSOLUTE_PATH_TRAVERSAL">PT_ABSOLUTE_PATH_TRAVERSAL</a>,
104 <li>
105 <a
106 href="http://findbugs.sourceforge.net/bugDescriptions.html#PT_RELATIVE_PATH_TRAVERSAL">PT_RELATIVE_PATH_TRAVERSAL</a>,
107 <li>
108 <a
109 href="http://findbugs.sourceforge.net/bugDescriptions.html#IA_AMBIGUOUS_INVOCATION_OF_INHERITED_OR_OUTER_METHOD">IA_AMBIGUOUS_INVOCATION_OF_INHERITED_OR_OUTER_METHOD</a>,
110 <li>
111 <a
112 href="http://findbugs.sourceforge.net/bugDescriptions.html#SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE">SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE</a>,
113 <li>
114 <a
115 href="http://findbugs.sourceforge.net/bugDescriptions.html#SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING">SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING</a>
116 </ul>
117 </li>
118 <li> Bug patterns under evaluation:
119 <ul>
120 <li>
121 <a
122 href="http://findbugs.sourceforge.net/bugDescriptions.html#CAA_COVARIANT_ARRAY_FIELD">CAA_COVARIANT_ARRAY_FIELD</a>,
123 <li>
124 <a
125 href="http://findbugs.sourceforge.net/bugDescriptions.html#CAA_COVARIANT_ARRAY_RETURN">CAA_COVARIANT_ARRAY_RETURN</a>,
126 <li>
127 <a
128 href="http://findbugs.sourceforge.net/bugDescriptions.html#CAA_COVARIANT_ARRAY_LOCAL">CAA_COVARIANT_ARRAY_LOCAL</a>,
129 <li>
130 <a
131 href="http://findbugs.sourceforge.net/bugDescriptions.html#CAA_COVARIANT_ARRAY_ELEMENT_STORE">CAA_COVARIANT_ARRAY_ELEMENT_STORE</a>,
132 <li>
133 <a
134 href="http://findbugs.sourceforge.net/bugDescriptions.html#IIL_PREPARE_STATEMENT_IN_LOOP">IIL_PREPARE_STATEMENT_IN_LOOP</a>,
135 <li>
136 <a
137 href="http://findbugs.sourceforge.net/bugDescriptions.html#IIL_PATTERN_COMPILE_IN_LOOP">IIL_PATTERN_COMPILE_IN_LOOP</a>,
138 <li>
139 <a
140 href="http://findbugs.sourceforge.net/bugDescriptions.html#IIL_PATTERN_COMPILE_IN_LOOP_INDIRECT">IIL_PATTERN_COMPILE_IN_LOOP_INDIRECT</a>,
141 <li>
142 <a
143 href="http://findbugs.sourceforge.net/bugDescriptions.html#IIL_ELEMENTS_GET_LENGTH_IN_LOOP">IIL_ELEMENTS_GET_LENGTH_IN_LOOP</a>,
144 </ul>
145
146 </li>
147
148 <!--
149 <li>Fixed false positives for the following bug patterns (XXX occurrences in findbugsTestCases):
150 <ul>
151 <li><a
152 href="http://findbugs.sourceforge.net/bugDescriptions.html#XXX">XXX</a>
153 </ul>
154 </li>
155
156 <li>Fixed false negatives for the following bug patterns (XXX occurrences in findbugsTestCases):
157 <ul>
158 <li><a
159 href="http://findbugs.sourceforge.net/bugDescriptions.html#XXX">XXX</a>
160 </ul>
161 </li>
162 -->
163
164 <li>Various bug fixes, also many patches from community. Thanks for your contributions!
165 </li>
166 </ul>
167
168 <h1>FindBugs Change Log, Version 3.0.0</h1>
169 <ul>
170 <li>FindBugs supports Java 8 now (both as runtime and target platform).
171 <li>FindBugs requires minimum Java 7 as runtime environment!
172 <li>FindBugs uses ASM 5 now which means that some 3rd party detectors based on FindBugs 2.x/ASM 3 has to be upgraded.
173 See details in <a href="http://download.forge.objectweb.org/asm/asm4-guide.pdf#chapter.5">ASM documentation</a>.
174 <li>New Bug patterns:
175 <a
176 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_OPTIONAL_RETURN_NULL">NP_OPTIONAL_RETURN_NULL</a>,
177 <a
178 href="http://findbugs.sourceforge.net/bugDescriptions.html#IIO_INEFFICIENT_INDEX_OF">IIO_INEFFICIENT_INDEX_OF</a>,
179 <a
180 href="http://findbugs.sourceforge.net/bugDescriptions.html#IIO_INEFFICIENT_LAST_INDEX_OF">IIO_INEFFICIENT_LAST_INDEX_OF</a>,
181 <a
182 href="http://findbugs.sourceforge.net/bugDescriptions.html#CNT_ROUGH_CONSTANT_VALUE">CNT_ROUGH_CONSTANT_VALUE</a>
183 </li>
184 <li>New "Source" filter which can be used to filter out classes generated from other languages:
185 <pre>
186 &lt;?xml version="1.0" encoding="UTF-8"?&gt;
187 &lt;FindBugsFilter&gt;
188 &lt;Match&gt;
189 &lt;Source name="~.*\.groovy" /&gt;
190 &lt;/Match&gt;
191 &lt;/FindBugsFilter&gt;
192 </pre>
193 </li>
194 <li>New "-auxclasspathFromFile" and "-analyzeFromFile" command line options.
195 </li>
196 <li>New "nested" ant task attribute.
197 </li>
198
199
200 <!--
201 <li>Fixed false positives for the following bug patterns (XXX occurrences in findbugsTestCases):
202 <ul>
203 <li><a
204 href="http://findbugs.sourceforge.net/bugDescriptions.html#XXX">XXX</a>
205 </ul>
206 </li>
207
208 <li>Fixed false negatives for the following bug patterns (XXX occurrences in findbugsTestCases):
209 <ul>
210 <li><a
211 href="http://findbugs.sourceforge.net/bugDescriptions.html#XXX">XXX</a>
212 </ul>
213 </li>
214 -->
215
216 <li>Various bug fixes, also many patches from community. Thanks for your contributions!
217 </li>
218 </ul>
219
220
221 <h1>FindBugs Change Log, Version 2.0.3</h1>
222 <ul>
223 <li>New Bug patterns: <a
224 href="http://findbugs.sourceforge.net/bugDescriptions.html#DM_BOXED_PRIMITIVE_FOR_PARSING">DM_BOXED_PRIMITIVE_FOR_PARSING</a>,
225 <a
226 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_METHOD_RETURN_RELAXING_ANNOTATION">NP_METHOD_RETURN_RELAXING_ANNOTATION</a>,
227 and
228 <a
229 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_METHOD_PARAMETER_TIGHTENS_ANNOTATION">NP_METHOD_PARAMETER_TIGHTENS_ANNOTATION</a>
230 </li>
231 <li>Add the ability in the GUI to save the currently viewable/filtered bugs to HTML output.
232 <li>When dataflow does't terminate, make sure we continue with
233 analysis.
234
235 <li>Fix some problems that resulting in dataflow analysis not
236 terminating
237
238 <li>Get parameter annotations from default parameters
239 annotations applied to the method.
240 <li>Add subversion change number to eclipse plugin qualifier.
241
242 <li>Disabled detector for <a
243 href="http://findbugs.sourceforge.net/bugDescriptions.html#AM_CREATES_EMPTY_JAR_FILE_ENTRY">AM_CREATES_EMPTY_JAR_FILE_ENTRY</a>;
244 it complaints inappropriately about code that creates directory
245 entries.
246
247 <li>Add warnings about incompatible types passed to
248 org.testng.Assert.assertEquals</li>
249 <li>Add logic that understands more of the Google Guava APIs.
250 <li>Disable type qualifier validator execution within Eclipse plugin;
251 too many problems with class loading and security manager (see #1154 Random obscure Eclipse failures)
252 <li>Consistently check both access flags and attributes to see if something is synthetic. Compiler is
253 inconsistent about where synthetic elements are marked.
254
255 <li>Fixed false positives for the following bug patterns (17
256 occurrences in findbugsTestCases):
257 <ul>
258 <li><a
259 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC">BC</a>
260 <li><a
261 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC_IMPOSSIBLE_INSTANCEOF">BC_IMPOSSIBLE_INSTANCEOF</a>
262 <li><a
263 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC_UNCONFIRMED_CAST">BC_UNCONFIRMED_CAST</a>
264 <li><a
265 href="http://findbugs.sourceforge.net/bugDescriptions.html#EC_UNRELATED_TYPES">EC_UNRELATED_TYPES</a>
266 <li><a
267 href="http://findbugs.sourceforge.net/bugDescriptions.html#INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE">INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE</a>
268 <li><a
269 href="http://findbugs.sourceforge.net/bugDescriptions.html#IS2_INCONSISTENT_SYNC">IS2_INCONSISTENT_SYNC</a>
270 <li><a
271 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS">NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS</a>
272 <li><a
273 href="http://findbugs.sourceforge.net/bugDescriptions.html#OBL_UNSATISFIED_OBLIGATION">OBL_UNSATISFIED_OBLIGATION</a>
274 <li><a
275 href="http://findbugs.sourceforge.net/bugDescriptions.html#RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE">RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE</a>
276 <li><a
277 href="http://findbugs.sourceforge.net/bugDescriptions.html#SA_FIELD_SELF_COMPARISON">SA_FIELD_SELF_COMPARISON</a>
278 <li><a
279 href="http://findbugs.sourceforge.net/bugDescriptions.html#TQ_UNKNOWN_VALUE_USED_WHERE_ALWAYS_STRICTLY_REQUIRED">TQ_UNKNOWN_VALUE_USED_WHERE_ALWAYS_STRICTLY_REQUIRED</a>
280 </li>
281 </ul>
282 <li>Fixed false negatives for the following bug patterns (45
283 occurrences in findbugsTestCases):
284 <ul>
285 <li><a
286 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC_UNCONFIRMED_CAST">BC_UNCONFIRMED_CAST</a>
287 <li><a
288 href="http://findbugs.sourceforge.net/bugDescriptions.html#DM_NUMBER_CTOR">DM_NUMBER_CTOR</a>
289 <li><a
290 href="http://findbugs.sourceforge.net/bugDescriptions.html#EC_ARRAY_AND_NONARRAY">EC_ARRAY_AND_NONARRAY</a>
291 <li><a
292 href="http://findbugs.sourceforge.net/bugDescriptions.html#EC_INCOMPATIBLE_ARRAY_COMPARE">EC_INCOMPATIBLE_ARRAY_COMPARE</a>
293 <li><a
294 href="http://findbugs.sourceforge.net/bugDescriptions.html#EC_UNRELATED_TYPES">EC_UNRELATED_TYPES</a>
295 <li><a
296 href="http://findbugs.sourceforge.net/bugDescriptions.html#GC_UNRELATED_TYPES">GC_UNRELATED_TYPES</a>
297 <li><a
298 href="http://findbugs.sourceforge.net/bugDescriptions.html#IS_FIELD_NOT_GUARDED">IS_FIELD_NOT_GUARDED</a>
299 <li><a
300 href="http://findbugs.sourceforge.net/bugDescriptions.html#IT_NO_SUCH_ELEMENT">IT_NO_SUCH_ELEMENT</a>
301 <li><a
302 href="http://findbugs.sourceforge.net/bugDescriptions.html#JCIP_FIELD_ISNT_FINAL_IN_IMMUTABLE_CLASS">JCIP_FIELD_ISNT_FINAL_IN_IMMUTABLE_CLASS</a>
303 <li><a
304 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_NULL_ON_SOME_PATH">NP_NULL_ON_SOME_PATH</a>
305 <li><a
306 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_NONNULL_PARAM_VIOLATION">NP_NONNULL_PARAM_VIOLATION</a>
307 <li><a
308 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE">NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE</a>
309 <li><a
310 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE">NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE</a>
311 <li><a
312 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_STORE_INTO_NONNULL_FIELD">NP_STORE_INTO_NONNULL_FIELD</a>
313 <li><a
314 href="http://findbugs.sourceforge.net/bugDescriptions.html#RE_POSSIBLE_UNINTENDED_PATTERN">RE_POSSIBLE_UNINTENDED_PATTERN</a>
315 <li><a
316 href="http://findbugs.sourceforge.net/bugDescriptions.html#SA_FIELD_SELF_COMPARISON">SA_FIELD_SELF_COMPARISON</a>
317 </ul>
318 </ul>
319 <h1>FindBugs Change Log, Version 2.0.2</h1>
320
321 <ul>
322 <li>Fix false positions for <a
323 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR">NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR</a>
324 - fixing <a
325 href="https://sourceforge.net/tracker/?func=detail&aid=3547559&group_id=96405&atid=614693">Bug3547559</a>,
326 <a
327 href="https://sourceforge.net/tracker/?func=detail&aid=3555408&group_id=96405&atid=614693">Bug3555408</a>,
328 <a
329 href="https://sourceforge.net/tracker/?func=detail&aid=3580266&group_id=96405&atid=614693">Bug3580266</a>
330 and <a
331 href="https://sourceforge.net/tracker/?func=detail&aid=3587164&group_id=96405&atid=614693">Bug3587164</a>.
332
333
334 </li>
335 <li>Fix false positives for <a
336 href="http://findbugs.sourceforge.net/bugDescriptions.html#SF_SWITCH_NO_DEFAULT">SF_SWITCH_NO_DEFAULT</a>
337 <li>Inline access methods for private fields,
135338 fixing false positive in <a
136339 href="https://sourceforge.net/tracker/?func=detail&aid=3484713&group_id=96405&atid=614693">Bug3484713</a>.
137
340
138341 <li>Type qualifier annotations, including nullness
139 annotations, are now ignored on vararg parameters (including
140 default and inherited annotations), awaiting JSR308.
141 <li>Defined new bug pattern to give better explanations of
142 issues involving strict type qualifiers <a
143 href="http://findbugs.sourceforge.net/bugDescriptions.html#TQ_UNKNOWN_VALUE_USED_WHERE_ALWAYS_STRICTLY_REQUIRED">TQ_UNKNOWN_VALUE_USED_WHERE_ALWAYS_STRICTLY_REQUIRED</a>
144 <li>Adjusted analysis of type qualifiers, now giving warnings
145 where a computed value is used in a place where a value with a
146 strict type qualifier is required.
147 <li>Complain about missing classes only if they are
148 encountered while analyzing application classes; ignore missing
149 classes that are encounted while analyzing classes loaded from the
150 auxclasspath. Fix for <a
151 href="https://sourceforge.net/tracker/?func=detail&aid=3588379&group_id=96405&atid=614693">Bug3588379</a>
152 <li>Fixed false positive null pointer warning coming from
153 synthetic bridge methods, fixing <a
154 href="https://sourceforge.net/tracker/?func=detail&aid=3589328&group_id=96405&atid=614693">Bug3589328</a>
155 <li>In general, suppress warnings in synthetic methods.
156 <li>Fix some false positives involving <a
157 href="http://findbugs.sourceforge.net/bugDescriptions.html#GC_UNRELATED_TYPES">GC_UNRELATED_TYPES</a>
158 on classes that extend generic collection classes.
159
160 </li>
161 <li>Combine multiple identical warnings about
342 annotations, are now ignored on vararg parameters (including
343 default and inherited annotations), awaiting JSR308.
344 <li>Defined new bug pattern to give better explanations of
345 issues involving strict type qualifiers <a
346 href="http://findbugs.sourceforge.net/bugDescriptions.html#TQ_UNKNOWN_VALUE_USED_WHERE_ALWAYS_STRICTLY_REQUIRED">TQ_UNKNOWN_VALUE_USED_WHERE_ALWAYS_STRICTLY_REQUIRED</a>
347 <li>Adjusted analysis of type qualifiers, now giving warnings
348 where a computed value is used in a place where a value with a
349 strict type qualifier is required.
350 <li>Complain about missing classes only if they are
351 encountered while analyzing application classes; ignore missing
352 classes that are encounted while analyzing classes loaded from the
353 auxclasspath. Fix for <a
354 href="https://sourceforge.net/tracker/?func=detail&aid=3588379&group_id=96405&atid=614693">Bug3588379</a>
355 <li>Fixed false positive null pointer warning coming from
356 synthetic bridge methods, fixing <a
357 href="https://sourceforge.net/tracker/?func=detail&aid=3589328&group_id=96405&atid=614693">Bug3589328</a>
358 <li>In general, suppress warnings in synthetic methods.
359 <li>Fix some false positives involving <a
360 href="http://findbugs.sourceforge.net/bugDescriptions.html#GC_UNRELATED_TYPES">GC_UNRELATED_TYPES</a>
361 on classes that extend generic collection classes.
362
363 </li>
364 <li>Combine multiple identical warnings about
162365 <a
163366 href="http://findbugs.sourceforge.net/bugDescriptions.html#DM_DEFAULT_ENCODING">DM_DEFAULT_ENCODING</a>
164367 that occur in the same method,
165368 simplifying issue triage.
166
167 <li>Changes by Andrey Loskutov
168 <ul>
169 <li>fixed job scheduling errors in 3.8/4.2 Eclipse <a
170 href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=393748">bug
171 report</a>
172 <li>more realistic progress bar updates for jobs
173 <li>added nullness annotations for some common Eclipse API
174 methods known to usually return null values
175 <li>Added support for org.eclipse.jdt.annotation.Nullable,
176 NonNull and NonNullByDefault annotations (introduced with
177 Eclipse 3.8/4.2)</li>
178 </ul>
179 <li>Documentation improvements
180 <li><a href="http://code.google.com/p/findbugs/source/list">lots
181 of other small changes</a>
182 </ul>
183 <h1>FindBugs Change Log, Version 2.0.1</h1>
184
185 <ul>
186 <li>New bug patterns; in some cases, bugs previous reported as
187 other bug patterns are reported as instances of these new bug
188 patterns in order to make it easier for developers to understand
189 the bug reports
190 <ul>
191 <li><a
192 href="http://findbugs.sourceforge.net/bugDescriptions.html#PT_ABSOLUTE_PATH_TRAVERSAL">PT_ABSOLUTE_PATH_TRAVERSAL</a></li>
193 <li><a
194 href="http://findbugs.sourceforge.net/bugDescriptions.html#PT_RELATIVE_PATH_TRAVERSAL">PT_RELATIVE_PATH_TRAVERSAL</a></li>
195 <li><a
196 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR">NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR</a></li>
197 <li><a
198 href="http://findbugs.sourceforge.net/bugDescriptions.html#MS_SHOULD_BE_REFACTORED_TO_BE_FINAL">MS_SHOULD_BE_REFACTORED_TO_BE_FINAL</a></li>
199 <li><a
200 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC_UNCONFIRMED_CAST_OF_RETURN_VALUE">BC_UNCONFIRMED_CAST_OF_RETURN_VALUE</a></li>
201 <li><a
202 href="http://findbugs.sourceforge.net/bugDescriptions.html#PT_ABSOLUTE_PATH_TRAVERSAL">PT_ABSOLUTE_PATH_TRAVERSAL</a></li>
203 <li><a
204 href="http://findbugs.sourceforge.net/bugDescriptions.html#TQ_COMPARING_VALUES_WITH_INCOMPATIBLE_TYPE_QUALIFIERS">TQ_COMPARING_VALUES_WITH_INCOMPATIBLE_TYPE_QUALIFIERS</a></li>
205 </ul>
206 </li>
207
208 <li>Changes to fix false negatives for the following bug
209 patterns: <a
210 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC_UNCONFIRMED_CAST">BC_UNCONFIRMED_CAST</a>,
211 <a
212 href="http://findbugs.sourceforge.net/bugDescriptions.html#EC_BAD_ARRAY_COMPARE">EC_BAD_ARRAY_COMPARE</a>,
213 <a
214 href="http://findbugs.sourceforge.net/bugDescriptions.html#EQ_UNUSUAL">EQ_UNUSUAL</a>,
215 <a
216 href="http://findbugs.sourceforge.net/bugDescriptions.html#GC_UNRELATED_TYPES">GC_UNRELATED_TYPES</a>,
217 and <a
218 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE">NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE</a>.
219 </li>
220
221 <li>Changes to fix false positions for the following bug
222 patterns: <a
223 href="http://findbugs.sourceforge.net/bugDescriptions.html#DMI_DOH">DMI_DOH</a>,
224 <a
225 href="http://findbugs.sourceforge.net/bugDescriptions.html#EC_UNRELATED_TYPES">EC_UNRELATED_TYPES</a>,
226 and <a
227 href="http://findbugs.sourceforge.net/bugDescriptions.html#SE_BAD_FIELD">SE_BAD_FIELD</a>.
228 </li>
229 </ul>
230
231 <h1>FindBugs Change Log, Version 2.0.0</h1>
232
233 <h2>Changes since version 1.3.8</h2>
234 <ul>
235 <li>New bug patterns; in some cases, bugs previous reported as
236 other bug patterns are reported as instances of these new bug
237 patterns in order to make it easier for developers to understand
238 the bug reports
239 <ul>
240 <li><a
241 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC_IMPOSSIBLE_DOWNCAST ">BC_IMPOSSIBLE_DOWNCAST
242 </a></li>
243 <li><a
244 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY ">BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY
245 </a></li>
246 <li><a
247 href="http://findbugs.sourceforge.net/bugDescriptions.html#EC_INCOMPATIBLE_ARRAY_COMPARE ">EC_INCOMPATIBLE_ARRAY_COMPARE
248 </a></li>
249 <li><a
250 href="http://findbugs.sourceforge.net/bugDescriptions.html#JLM_JSR166_UTILCONCURRENT_MONITORENTER ">JLM_JSR166_UTILCONCURRENT_MONITORENTER
251 </a></li>
252 <li><a
253 href="http://findbugs.sourceforge.net/bugDescriptions.html#LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE ">LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE
254 </a></li>
255 <li><a
256 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_CLOSING_NULL ">NP_CLOSING_NULL
257 </a></li>
258 <li><a
259 href="http://findbugs.sourceforge.net/bugDescriptions.html#RC_REF_COMPARISON_BAD_PRACTICE ">RC_REF_COMPARISON_BAD_PRACTICE
260 </a></li>
261 <li><a
262 href="http://findbugs.sourceforge.net/bugDescriptions.html#RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN ">RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN
263 </a></li>
264 <li><a
265 href="http://findbugs.sourceforge.net/bugDescriptions.html#RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORED ">RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORED
266 </a></li>
267 <li><a
268 href="http://findbugs.sourceforge.net/bugDescriptions.html#SIC_THREADLOCAL_DEADLY_EMBRACE ">SIC_THREADLOCAL_DEADLY_EMBRACE
269 </a></li>
270 <li><a
271 href="http://findbugs.sourceforge.net/bugDescriptions.html#UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR ">UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR
272 </a></li>
273 <li><a
274 href="http://findbugs.sourceforge.net/bugDescriptions.html#VA_FORMAT_STRING_EXPECTED_MESSAGE_FORMAT_SUPPLIED ">VA_FORMAT_STRING_EXPECTED_MESSAGE_FORMAT_SUPPLIED
275 </a></li>
276 </ul>
277 </li>
278 <li>Providing a bug rank (1-20), and the ability to filter by
279 bug rank. Eventually, it will be possible to specify your own
280 rules for ranking bugs, but the procedure for doing so hasn't been
281 specified yet.</li>
282 <li>Fixed about <a
283 href="https://sourceforge.net/search/index.php?group_id=96405&search_summary=1&search_details=1&type_of_search=artifact&group_artifact_id%5B%5D=614693&open_date_start=2009-03-16&open_date_end=2009-08-20&form_submit=Search">45
284 bugs filed</a> through SourceForge
285 </li>
286 <li>Various reclassifications and priority tweaks</li>
287 <li>Added more bug annotations to a variety of bug reports.
288 This provides more context for understanding bug reports (e.g., if
289 the value in question was is the return value of a method, the
290 method is described as the source of the value in a bug
291 annotation). This also provide more accurate tracking of issues
292 across versions of the code being analyzed, but has the downside
293 that when comparing results from FindBugs 1.3.8 and FindBugs 1.3.9
294 on the same version of code being analyzed, FindBugs may think
295 that mistakenly believe that the issue reported by 1.3.8 was fixed
296 and a new issue was introduced that was reported by FindBugs
297 1.3.9. While annoying, it would be unusual for more than a dozen
298 issues per million lines of codes to be mistracked.</li>
299 <li>Lots of internal changes moving towards FindBugs 2.0, but
300 these features are undocumented, not yet officially supported, and
301 subject to radical changes before FindBugs 2.0 is released.</li>
302 </ul>
303
304 <p>Changes since version 1.3.8</p>
305 <ul>
306 <li>New bug patterns; in some cases, bugs previous reported as
307 other bug patterns are reported as instances of these new bug
308 patterns in order to make it easier for developers to understand
309 the bug reports
310 <ul>
311 <li><a
312 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC_IMPOSSIBLE_DOWNCAST ">BC_IMPOSSIBLE_DOWNCAST
313 </a>
314 <li><a
315 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY ">BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY
316 </a>
317 <li><a
318 href="http://findbugs.sourceforge.net/bugDescriptions.html#EC_INCOMPATIBLE_ARRAY_COMPARE ">EC_INCOMPATIBLE_ARRAY_COMPARE
319 </a>
320 <li><a
321 href="http://findbugs.sourceforge.net/bugDescriptions.html#JLM_JSR166_UTILCONCURRENT_MONITORENTER ">JLM_JSR166_UTILCONCURRENT_MONITORENTER
322 </a>
323 <li><a
324 href="http://findbugs.sourceforge.net/bugDescriptions.html#LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE ">LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE
325 </a>
326 <li><a
327 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_CLOSING_NULL ">NP_CLOSING_NULL
328 </a>
329 <li><a
330 href="http://findbugs.sourceforge.net/bugDescriptions.html#RC_REF_COMPARISON_BAD_PRACTICE ">RC_REF_COMPARISON_BAD_PRACTICE
331 </a>
332 <li><a
333 href="http://findbugs.sourceforge.net/bugDescriptions.html#RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN ">RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN
334 </a>
335 <li><a
336 href="http://findbugs.sourceforge.net/bugDescriptions.html#RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORED ">RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORED
337 </a>
338 <li><a
339 href="http://findbugs.sourceforge.net/bugDescriptions.html#SIC_THREADLOCAL_DEADLY_EMBRACE ">SIC_THREADLOCAL_DEADLY_EMBRACE
340 </a>
341 <li><a
342 href="http://findbugs.sourceforge.net/bugDescriptions.html#UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR ">UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR
343 </a>
344 <li><a
345 href="http://findbugs.sourceforge.net/bugDescriptions.html#VA_FORMAT_STRING_EXPECTED_MESSAGE_FORMAT_SUPPLIED ">VA_FORMAT_STRING_EXPECTED_MESSAGE_FORMAT_SUPPLIED
346 </a>
347 </ul>
348 </li>
349 <li>Providing a bug rank (1-20), and the ability to filter by
350 bug rank. Eventually, it will be possible to specify your own
351 rules for ranking bugs, but the procedure for doing so hasn't been
352 specified yet.</li>
353 <li>Fixed about <a
354 href="https://sourceforge.net/search/index.php?group_id=96405&search_summary=1&search_details=1&type_of_search=artifact&group_artifact_id%5B%5D=614693&open_date_start=2009-03-16&open_date_end=2009-08-20&form_submit=Search">45
355 bugs filed</a> through SourceForge
356 </li>
357 <li>Various reclassifications and priority tweaks</li>
358 <li>Added more bug annotations to a variety of bug reports.
359 This provides more context for understanding bug reports (e.g., if
360 the value in question was is the return value of a method, the
361 method is described as the source of the value in a bug
362 annotation). This also provide more accurate tracking of issues
363 across versions of the code being analyzed, but has the downside
364 that when comparing results from FindBugs 1.3.8 and FindBugs 1.3.9
365 on the same version of code being analyzed, FindBugs may think
366 that mistakenly believe that the issue reported by 1.3.8 was fixed
367 and a new issue was introduced that was reported by FindBugs
368 1.3.9. While annoying, it would be unusual for more than a dozen
369 issues per million lines of codes to be mistracked.</li>
370 <li>Lots of internal changes moving towards FindBugs 2.0, but
371 these features are undocumented, not yet officially supported, and
372 subject to radical changes before FindBugs 2.0 is released.</li>
373 </ul>
374
375 <p>Changes since version 1.3.7</p>
376 <ul>
377 <li>Primarily another small bugfix release.</li>
378 <li>FindBugs base:
379 <ul>
380 <li>New Reports:
381 <ul>
382 <li>SF_SWITCH_NO_DEFAULT: missing default case in switch
383 statement.</li>
384 <li>SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH_TO_THROW:
385 value ignored when switch fallthrough leads to thrown
386 exception.</li>
387 <li>INT_VACUOUS_BIT_OPERATION: bit operations that don't
388 do any meaningful work.</li>
389 <li>FB_UNEXPECTED_WARNING: warning generated that
390 conflicts with @NoWarning FindBugs annotation.</li>
391 <li>FB_MISSING_EXPECTED_WARNING: warning not generated
392 despite presence of @ExpectedWarning FindBugs annotation.</li>
393 <li>NOISE category: intended for use in data mining
394 experiments.
395 <ul>
396 <li>NOISE_NULL_DEREFERENCE: fake null point dereference
397 warning.</li>
398 <li>NOISE_METHOD_CALL: fake method call warning.</li>
399 <li>NOISE_FIELD_REFERENCE: fake field dereference
400 warning.</li>
401 <li>NOISE_OPERATION: fake operation warning.</li>
402 </ul>
403 </li>
404 </ul>
405 </li>
406 <li>Other:
407 <ul>
408 <li>Garvin Leclaire has created a new Apache Maven
409 repository for FindBugs at <a
410 href="http://code.google.com/p/findbugs/">the Google Code
411 FindBugs SVN repository</a>. (Thanks Garvin!)
412 </li>
413 </ul>
414 </li>
415 <li>Fixes:
416 <ul>
417 <li>[ 2317842 ] Highlighting broken in Windows</li>
418 <li>[ 2515908 ] check for oddness should track sign of
419 argument</li>
420 <li>[ 2487936 ] &quot;L B GC&quot; false pos cast from
421 Map.Entry.getKey() to Map.get()</li>
422 <li>[ 2528264 ] Ant tasks not compatible with Ant 1.7.1</li>
423 <li>[ 2539590 ] SF_SWITCH_FALLTHROUGH wrong message
424 reported</li>
425 <li>[ 2020066 ] Bug history displayed in fancy-hist.xsl is
426 incorrect</li>
427 <li>[ 2545098 ] Invalid character in analysis results file</li>
428 <li>[ 2492673 ] Plugin sites should specify &quot;requires
429 Eclipse 3.3 or newer&quot;</li>
430 <li>[ 2588044 ] a tiny typing error</li>
431 <li>[ 2589048 ] Documentation for convertXmlToText
432 insufficient</li>
433 <li>[ 2638739 ] NullPointerException when building</li>
434 </ul>
435 </li>
436 <li>Patches:
437 <ul>
438 <li>[ 2538184 ] Make BugCollection implement
439 Iterable&lt;BugInstance&gt; (thanks to Tomas Pollak)</li>
440 <li>[ 2249771 ] Add Maven2 Findbugs plugin link to the
441 Links page (thanks to Garvin Leclaire)</li>
442 <li>[ 2609526 ] Japanese manual update (thanks to K.
443 Hashimoto)</li>
444 <li>[ 2119482 ] CheckBcel checks for nonexistent classes
445 (thanks to Jerry James)</li>
446 </ul>
447 </li>
448 </ul>
449 </li>
450 <li>FindBugs Eclipse plugin:
451 <ul>
452 <li>Major feature enhancements (thanks to Andrey Loskutov).
453 See <a href="http://andrei.gmxhome.de/findbugs/index.html">this
454 overview</a> for more information.
455 </li>
456 <li>Major test improvements (thanks to Tomas Pollak).</li>
457 <li>Fixes:
458 <ul>
459 <li>[ 2532365 ] Compiler warning</li>
460 <li>[ 2522989 ] Fix filter files selection</li>
461 <li>[ 2504068 ] NullPointerException</li>
462 <li>[ 2640849 ] NPE in Eclipse plugin 1.3.7 and Eclipse
463 3.5 M5</li>
464 </ul>
465 </li>
466 <li>Patches:
467 <ul>
468 <li>[ 2143140 ] Unchecked conversion fixes for Eclipse
469 plugin (thanks to Jerry James)
470 </ul>
471 </li>
472 </ul>
473 </li>
474 </ul>
475
476 <p>Changes since version 1.3.6</p>
477 <ul>
478 <li>Overall, a small bugfix release.
479 <li>New detection of accidental vacuous/useless calls to
480 EasyMock methods, and of generic signatures that proclaim the use
481 of unhashable classes in ways that require that they be hashed.
482 <li>Eliminate some false positives where we were warning about
483 a useless call (e.g., comparing two incompatible types for
484 equality), but the only thing the code was doing with the result
485 was passing it to assertFalse.
486 <li>Japanese localization and manual by K.Hashimoto. (Thanks!)
487
488 <li>Added -exclude and -outputDir command line options to
489 rejarForAnalysis
490 <li>Extended -adjustPriorities option to FindBugs analysis
491 textui so that you can modify the priorities of individual bug
492 patterns as well as visitors, and also completely suppress
493 individual bug patterns or visitors.
494 <ul>
495 <li>e.g., -adjustPriority
496 MS_SHOULD_BE_FINAL=suppress,MS_PKGPROTECT=suppress,EI_EXPOSE_REP=suppress,EI_EXPOSE_REP2=suppress,PZLA_PREFER_ZERO_LENGTH_ARRAYS=raise
497
498 </ul>
499 </ul>
500
501
502 <p>Changes since version 1.3.5</p>
503 <ul>
504 <li>Added fairly exhaustive static analysis of uses of format
505 strings, checking for missing or extra arguements, invalid format
506 specifiers, or mismatched format specifiers and arguments (e.g,
507 passing a String value for a %d format specifier). The logic for
508 doing so is derived from Sun's java.util.Formatter class, and
509 available separately from FindBugs as part of the <a
510 href="https://jformatstring.dev.java.net/">jFormatString</a>
511 project.
512 <li>More tuning of the unsatisfied obligation detector. Since
513 this detector is still rather noisy and an unfinished research
514 project, I've moved the generated issues to a new category:
515 EXPERIMENTAL.
516 <li>Added check for <a
517 href="http://findbugs.sourceforge.net/bugDescriptions.html#BIT_ADD_OF_SIGNED_BYTE">BIT_ADD_OF_SIGNED_BYTE</a>;
518 similar to <a
519 href="http://findbugs.sourceforge.net/bugDescriptions.html#BIT_IOR_OF_SIGNED_BYTE">BIT_IOR_OF_SIGNED_BYTE</a>,
520 except that addition is being used to combine shifted signed
521 bytes.
522 <li>Changed detection of EI_EXPOSE_REP2, so we only report it
523 if the value stored is guaranteed to be the same value that was
524 passed in as a parameter.
525 <li>Added <a
526 href="http://findbugs.sourceforge.net/bugDescriptions.html#EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS">EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS</a>,
527 a warning when an equals method checks to see if an operand is an
528 instance of a class not compatible with itself. For example, if
529 the Foo class checks to see if the argument is an instance of
530 String. This is either a questionable design decision or a coding
531 mistake.
532 <li>Added <a
533 href="http://findbugs.sourceforge.net/bugDescriptions.html#DMI_INVOKING_HASHCODE_ON_ARRAY">DMI_INVOKING_HASHCODE_ON_ARRAY</a>,
534 which checks for invoking <code>hashCode()</code> on an array,
535 which returns a hash code that ignores the contents of the array.
536
537 <li>Added checks for using <code>x.removeAll(x)</code> to
538 rather than <code>x.clear()</code> to clear an array.
539 <li>Add checks for calls such as <code>x.contains(x)</code>, <code>x.remove(x)</code>
540 and <code>x.containsAll(x)</code>.
541 <li>Improvements to Eclipse plugin (thanks to Andrey
542 Loskutov):
543 <ul>
544 <li>Report separate markers for each occurrence of an issue
545 that appears multiple times in a method
546 <li>fine tuning for reported markers: add only one marker
547 for fields, add marker on right position
548 <li>link bugs selected in bug explorer view to the opened
549 editor and vice versa
550 <li>select bugs selected in editor ruler in the opened bug
551 explorer view
552 <li>consistent abbreviations used in both bug explorer and
553 bug details view
554 <li>added "Expand All" button to the bug explorer view
555 <li>added "Go Into/Go Up" buttons to the bug explorer view
556 <li>added "Copy to clipboard" menu/functionality to the
557 details view list widget
558 <li>fix for CNF exception if loading the backup solution for
559 broken browser widget
560 </ul>
561 </ul>
562
563
564
565 <p>Changes since version 1.3.4</p>
566 <ul>
567 <li>Analysis about 15% faster
568 <li><a
569 href="http://sourceforge.net/tracker/?atid=614693&group_id=96405&func=browse&status=closed">38
570 bugs closed</a></li>
571 <li>New defect warnings:
572 <ul>
573 <li>calls to methods that always throw
574 UnsupportedOperationException (DMI_UNSUPPORTED_METHOD)
575 <li>repeated conditional tests (e.g., <code>if (x
576 &lt; 0 || x &lt; 0) ...</code>) (RpC_REPEATED_CONDITIONAL_TEST)
577 <li>Complete rewrite of detector for format string problems.
578 More accurate, finds more problems, generates more descriptive
579 reports, several different bug pattern
580 (VA_FORMAT_STRING_EXTRA_ARGUMENTS_PASSED,
581 VA_FORMAT_STRING_ILLEGAL, VA_FORMAT_STRING_MISSING_ARGUMENT,
582 VA_FORMAT_STRING_BAD_ARGUMENT,
583 VA_FORMAT_STRING_NO_PREVIOUS_ARGUMENT)
584 <li>Fairly complete implementation of JSR-305 custom type
585 qualifier analysis (no support for custom validators yet).
586 (TQ_MAYBE_SOURCE_VALUE_REACHES_NEVER_SINK
587 TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_ALWAYS_SINK
588 TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_NEVER_SINK)
589 <li>New detector for unsatisfied obligations such forgetting
590 to close a file (OBL_UNSATISFIED_OBLIGATION).
591 <li>Warning when a parameter is marked as nullable, but is
592 always dereferenced.
593 (NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE)
594 <lI>Separate warning for dereference the result of readLine
595 (NP_DEREFERENCE_OF_READLINE_VALUE)
596 </ul>
597 <li>When XML is generated with messages, the project stats now
598 include &lt;FileStat&gt; elements. For each source file, this
599 gives the path for the file, the total number of warnings for that
600 file, and a bugHash for the file. While the instanceHash for a bug
601 is intended to be version invariant (ignoring line numbers, etc),
602 the bugHash for a file is intended to reflect all the information
603 about the warnings in that file. The intended use case is that if
604 the bugHash for a file is the same in two analysis runs, then <em>nothing</em>
605 has changed about any of the warnings reported for that file
606 between the two analysis runs.
607 <li>More merging of similar issues within a method. For
608 example, if the result of readLine() is dereferences multiple
609 times within a method, it will be reported as a single warning
610 with occurrences at multiple source lines.
611 </ul>
612 <p>Changes since version 1.3.3</p>
613
614 <ul>
615 <li>FindBugs base
616 <ul>
617 <li>New Reports:
618 <ul>
619 <li>EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC: equals method
620 overrides equals in superclass and may not be symmetric</li>
621 <li>EQ_ALWAYS_TRUE: equals method always returns true</li>
622 <li>EQ_ALWAYS_FALSE: equals method always returns false</li>
623 <li>EQ_COMPARING_CLASS_NAMES: equals method compares class
624 names rather than class objects</li>
625 <li>EQ_UNUSUAL: Unusual equals method</li>
626 <li>EQ_GETCLASS_AND_CLASS_CONSTANT: equals method fails
627 for subtypes</li>
628 <li>SE_READ_RESOLVE_IS_STATIC: The readResolve method must
629 not be declared as a static method.</li>
630 <li>SE_PRIVATE_READ_RESOLVE_NOT_INHERITED: private
631 readResolve method not inherited by subclasses</li>
632 <li>MSF_MUTABLE_SERVLET_FIELD: Mutable servlet field</li>
633 <li>XSS_REQUEST_PARAMETER_TO_SEND_ERROR: Servlet reflected
634 cross site scripting vulnerability</li>
635 <li>SKIPPED_CLASS_TOO_BIG: Class too big for analysis</li>
636 </ul>
637 </li>
638 <li>Other:
639 <ul>
640 <li>Value-number analysis now more space-efficient</li>
641 <li>Enhancements to reduce memory overhead when analyzing
642 very large classes</li>
643 <li>Now skips very large classes that would otherwise take
644 too much time and memory to analyze</li>
645 <li>Infrastructure for tracking effectively-constant/
646 effectively-final fields</li>
647 <li>Added more cweids</li>
648 <li>Enhanced taint tracking for taint-based detectors</li>
649 <li>Ignore doomed calls to equals if result is used as an
650 argument to assertFalse</li>
651 <li>EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC handles compareTo</li>
652 <li>Priority tweak for ICAST_INTEGER_MULTIPLY_CAST_TO_LONG
653 (only low priority if multiplying by 1000)</li>
654 <li>Improved tracking of fields across method calls</li>
655 </ul>
656 </li>
657 <li>Fixes:
658 <ul>
659 <li>[ 1941450 ] DLS_DEAD_LOCAL_STORE not reported</li>
660 <li>[ 1953323 ] Omitted break statement in
661 SynchronizeAndNullCheckField</li>
662 <li>[ 1942620 ] Source Directories selection dialog
663 interface confusion (partial)</li>
664 <li>[ 1948275 ] Unhelpful "Load of known null"</li>
665 <li>[ 1933922 ] MWM error in findbugs</li>
666 <li>[ 1934772 ] 1.3.3 appears to rely on JDK 1.6, JNLP
667 still specifies 1.5</li>
668 <li>[ 1933945 ] -loadbugs doesn't work</li>
669 <li>Fixed problems for class names starting with '$'</li>
670 <li>Fixed bugs and incomplete handling of annotations in
671 VersionInsensitiveBugComparator</li>
672 </ul>
673 </li>
674 <li>Patches:
675 <ul>
676 <li>[ 1955106 ] Javadoc fixes</li>
677 <li>[ 1951930 ] Superfluous import statements (thanks to
678 Jerry James)</li>
679 <li>[ 1951907 ] Missing @Deprecated annotations (thanks to
680 Jerry James)</li>
681 <li>[ 1951876 ] Infonode Docking Windows compile fix
682 (thanks to Jerry James)</li>
683 <li>[ 1936055 ] bugfix for findbugs.de.comment not working
684 (thanks to Peter Fokkinga)
685 </ul>
686 </li>
687 </ul>
688 <li>FindBugs BlueJ plugin
689 <ul>
690 <li>Updated to use FindBugs 1.3.4 (first new release since
691 1.1.3)</li>
692 </ul>
693 </li>
694 </ul>
695
696 <p>Changes since version 1.3.2</p>
697
698 <ul>
699 <li>FindBugs base
700 <ul>
701 <li>New Detectors:
702 <ul>
703 <li>FieldItemSummary: Produces summary information for
704 what is stored into fields</li>
705 <li>SynchronizeOnClassLiteralNotGetClass: Look for code
706 that synchronizes on the results of getClass rather than on
707 class literals</li>
708 <li>SynchronizingOnContentsOfFieldToProtectField: This
709 detector looks for code that seems to be synchronizing on a
710 field in order to guard updates of that field</li>
711 </ul>
712 </li>
713 <li>New BugCode:
714 <ul>
715 <li>HRS: HTTP Response splitting vulnerability</li>
716 <li>WL: Possible locking on wrong object</li>
717 </ul>
718 </li>
719 <li>New Reports:
720 <ul>
721 <li>DMI_CONSTANT_DB_PASSWORD: This code creates a database
722 connect using a hard coded, constant password</li>
723 <li>HRS_REQUEST_PARAMETER_TO_COOKIE: HTTP cookie formed
724 from untrusted input</li>
725 <li>HRS_REQUEST_PARAMETER_TO_HTTP_HEADER: HTTP parameter
726 directly written to HTTP header output</li>
727 <li>CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE: Class defines
728 clone() but doesn't implement Cloneable</li>
729 <li>DL_SYNCHRONIZATION_ON_BOXED_PRIMITIVE: Synchronization
730 on boxed primitive could lead to deadlock</li>
731 <li>DL_SYNCHRONIZATION_ON_BOOLEAN: Synchronization on
732 Boolean could lead to deadlock</li>
733 <li>ML_SYNC_ON_FIELD_TO_GUARD_CHANGING_THAT_FIELD:
734 Synchronization on field in futile attempt to guard that field
735 </li>
736 <li>DLS_DEAD_LOCAL_STORE_IN_RETURN: Useless assignment in
737 return statement</li>
738 <li>WL_USING_GETCLASS_RATHER_THAN_CLASS_LITERAL:
739 Synchronization on getClass rather than class literal</li>
740 </ul>
741 </li>
742 <li>Other:
743 <ul>
744 <li>Many enhancements to cross-site scripting detector and
745 its documentation</li>
746 <li>Enhanced switch fall through handling</li>
747 <li>Enhanced unread field handling (look for IF_ACMPEQ and
748 IF_ACMPNE)</li>
749 <li>Clarified documentation for @Nullable in manual</li>
750 <li>Fewer DeadLocalStore false positives</li>
751 <li>Fewer UnreadField false positives</li>
752 <li>Fewer StaticCalendarDetector false positives</li>
753 <li>Performance fix for slow file system IO e.g. Clearcase
754 repositories (thanks, Andrei!)</li>
755 <li>Other, general performance enhancements (thanks,
756 Andrei!)</li>
757 <li>Enhancements for using FindBugs scripts with MKS on
758 Windows (thanks, Kelly O'Hair!)</li>
759 <li>Noted in the manual that jsr305.jar must be present
760 for annotations to compile</li>
761 <li>Added and fine-tuned default-nullness annotations</li>
762 <li>More CWE IDs added</li>
763 <li>Check and warning for unexpected BCEL version in
764 classpath</li>
765 </ul>
766 </li>
767 <li>Fixes:
768 <ul>
769 <li>Bug fix to handling of local variable tables in BCEL</li>
770 <li>Refined documentation for
771 MTIA_SUSPECT_STRUTS_INSTANCE_FIELD</li>
772 <li>[ 1927295 ] NPE when called on project root</li>
773 <li>[ 1926405 ] Incorrect dead store warning</li>
774 <li>[ 1926409 ] Incorrect redundant nullcheck warning</li>
775 <li>[ 1926389 ] Wrong line number printed/highlighted in
776 bug</li>
777 <li>[ 1927040 ] typo in bug description</li>
778 <li>[ 1926263 ] Minor glitch in HTML output</li>
779 <li>[ 1926240 ] Minor error in standard options in manual</li>
780 <li>[ 1926236 ] Minor bug in installation section of
781 manual</li>
782 <li>[ 1925539 ] ZIP is default file system code base</li>
783 <li>[ 1894701 ] Livelock / memory leak in
784 ObjectTypeFactory (thanks, Andrei!)</li>
785 <li>[ 1867491 ] Doesn't reload annotations after code
786 changes in IDE (thanks, Andrei!)</li>
787 <li>[ 1921399 ] -project option not supported</li>
788 <li>[ 1913834 ] "Dead" store to variable with method call</li>
789 <li>[ 1917352 ] H B se:...field in serializable class</li>
790 <li>[ 1911617 ] CloneIdiom relies on
791 getNameConstantOperand for INSTANCEOF</li>
792 <li>[ 1911620 ] False +: DLS predecrement before return</li>
793 <li>[ 1871376 ] False negative: non-serializable Map field</li>
794 <li>[ 1871051 ] non standard clone() method</li>
795 <li>[ 1908854 ] Error in TestASM</li>
796 <li>[ 1907539 ] 22 minor errors in bug checker
797 documentation</li>
798 <li>[ 1897323 ] EJB implementation class false positives</li>
799 <li>[ 1899648 ] Crash on startup on Vista with Java
800 1.6.0_04</li>
801 </ul>
802 </li>
803 </ul>
804 </li>
805 <li>FindBugs Eclipse plugin (change log by Andrey Loskutov)
806 <ul>
807 <li>new feature: export basic FindBugs numbers for projects
808 via File-&gt;Export-&gt;Java-&gt;BugCounts (Andrey Loskutov)</li>
809 <li>new feature: jobs for different projects will be run in
810 parallel per default if running on a multi-core PC
811 ("fb.allowParallelBuild" system property not used anymore)
812 (Andrey Loskutov)</li>
813 <li>fixed performance slowdown in the multi-threaded build,
814 caused by workspace operation locks during assigning marker
815 attributes (Andrey Loskutov)</li>
816 </ul>
817 </li>
818 </ul>
819
820 <p>Changes since version 1.3.1</p>
821
822 <ul>
823 <li>FindBugs base
824 <ul>
825 <li>New Bug Category:
826 <ul>
827 <li>SECURITY (Abbrev: S), A use of untrusted input in a
828 way that could create a remotely exploitable security
829 vulnerability</li>
830 </ul>
831 </li>
832 <li>New Detectors:
833 <ul>
834 <li>CrossSiteScripting: This detector looks for
835 obvious/blatant cases of cross site scripting vulnerabilities</li>
836 </ul>
837 </li>
838 <li>New BugCode:
839 <ul>
840 <li>XSS: Cross site scripting</li>
841 </ul>
842 </li>
843 <li>New Reports:
844 <ul>
845 <li>XSS_REQUEST_PARAMETER_TO_SERVLET_WRITER: HTTP
846 parameter directly written to Servlet output, giving XSS
847 vulnerability</li>
848 <li>XSS_REQUEST_PARAMETER_TO_JSP_WRITER: HTTP parameter
849 directly written to JSP output, giving XSS vulnerability</li>
850 <li>EQ_OTHER_USE_OBJECT: equals() method defined that
851 doesn't override Object.equals(Object)</li>
852 <li>EQ_OTHER_NO_OBJECT: equals() method inherits rather
853 than overrides equals(Object)</li>
854 <li>NP_NULL_ON_SOME_PATH_MIGHT_BE_INFEASIBLE: Possible
855 null pointer dereference on path that might be infeasible</li>
856 </ul>
857 </li>
858 <li>Other:
859 <ul>
860 <li>Added -noClassOk command-line parameter to
861 command-line and ant interfaces; when -noClassOk is specified
862 and no classfiles are given, FindBugs will print a warning
863 message and output a well- formed file with no warnings</li>
864 <li>Fewer false positives for null pointer bugs</li>
865 <li>Suppress dead-local-store false positives in .jsp code</li>
866 <li>Type fixes in warning messages</li>
867 <li>Better warning message for NP_NULL_ON_SOME_PATH</li>
868 <li>"WMI" bug code description renamed from "Wrong Map
869 Iterator" to "Inefficient Map Iterator"</li>
870 </ul>
871 </li>
872 <li>Fixes:
873 <ul>
874 <li>[ 1893048 ] FindBugs confused by a findbugs.xml file</li>
875 <li>[ 1878528 ] XSL xforms don't support history features</li>
876 <li>[ 1876584 ] two default.xsl flaws</li>
877 <li>[ 1874856 ] Format string bug detector doesn't handle
878 special operators</li>
879 <li>[ 1872645 ] computeBugHistory -
880 java.lang.IllegalArgumentException</li>
881 <li>[ 1872237 ] Ant task fails when no .class files</li>
882 <li>[ 1868670 ] Filters: include AND exclude don't allowed</li>
883 <li>[ 1868666 ] check-for-oddness reported, but array
884 length can never be negative</li>
885 <li>[ 1866108 ] SetBugDatabaseInfoTask strips dir from
886 output filename</li>
887 <li>[ 1866021 ] MineBugHistoryTask strips dir of output
888 filename</li>
889 <li>[ 1865265 ] code doesn't handle
890 StringBuffer.append([CII) right</li>
891 <li>[ 1864793 ] Warning when casting a null reference
892 compared to a String</li>
893 <li>[ 1863376 ] Typo in manual chap 8: Filter Files</li>
894 <li>[ 1862705 ] Transient fields that default to null</li>
895 <li>[ 1842545 ] DLS on catch variable (with priority
896 tweaking)</li>
897 <li>[ 1816258 ] false positive BC_IMPOSSIBLE_CAST</li>
898 <li>[ 1551732 ] Get erroneous DLS with while loop</li>
899 </ul>
900 </li>
901 </ul>
902 </li>
903 <li>FindBugs Eclipse plugin (change log by Andrey Loskutov)
904 <ul>
905 <li>new feature: added Bug explorer view (replacing Bug tree
906 view), based on Common Navigator framework (Andrey Loskutov)</li>
907 <li>bug 1873860 fixed: empty projects are no longer shown in
908 Bug tree view (Andrey Loskutov)</li>
909 <li>new feature: bug counts decorators for projects, folders
910 and files (has to be activated via Preferences -&gt; general
911 -&gt; appearance -&gt; label decorations)(Andrey Loskutov)</li>
912 <li>patch 1746499: better icons (Alessandro Nistico)</li>
913 <li>patch 1893685: Find bug actions on change sets bug
914 (Alessandro Nistico)</li>
915 <li>fixed bug 1855384: Bug configuration is broken in
916 Eclipse (Andrey Loskutov)</li>
917 <li>refactored FindBugs properties page (Andrey Loskutov)</li>
918 <li>refactored FindBugs worker/builder/run action (Andrey
919 Loskutov)</li>
920 <li>FB detects now only bugs from classes on project's
921 classpath (no double work on duplicated class files) (Andrey
922 Loskutov)</li>
923 <li>fixed bug introduced by the bad patch for 1867951: FB
924 cannot be executed incrementally on a folder of file (Andrey
925 Loskutov)</li>
926 <li>fixed job rule: now jobs for different projects may run
927 in parallel if running on a multi-core PC and
928 "fb.allowParallelBuild" system property is set to true (Andrey
929 Loskutov)</li>
930 <li>fixed FB auto-build not started if .fbprefs or
931 .classpath was changed (Andrey Loskutov)</li>
932 <li>fixed not reporting bugs on secondary types (classes
933 defined in java files with different name) (Andrey Loskutov)</li>
934 </ul>
935 </li>
936 </ul>
937
938 <p>Changes since version 1.3.0</p>
939 <ul>
940 <li>New Reports
941 <ul>
942 <li>VA_FORMAT_STRING_ARG_MISMATCH: A format-string method
943 with a variable number of arguments is called, but the number of
944 arguments passed does not match with the number of %
945 placeholders in the format string. This is probably not what the
946 author intended.
947 <li>IO_APPENDING_TO_OBJECT_OUTPUT_STREAM: This code opens a
948 file in append mode and that wraps the result in an object
949 output stream. This won't allow you to append to an existing
950 object output stream stored in a file. If you want to be able to
951 append to an object output stream, you need to keep the object
952 output stream open. The only situation in which opening a file
953 in append mode and the writing an object output stream could
954 work is if on reading the file you plan to open it in random
955 access mode and seek to the byte offset where the append
956 started.
957 <li>NP_BOOLEAN_RETURN_NULL: A method that returns either
958 Boolean.TRUE, Boolean.FALSE or null is an accident waiting to
959 happen. This method can be invoked as though it returned a value
960 of type boolean, and the compiler will insert automatic unboxing
961 of the Boolean value. If a null value is returned, this will
962 result in a NullPointerException.
963 </ul>
964 </li>
965 <li>Changes to Existing Reports
966 <ul>
967 <li>RV_DONT_JUST_NULL_CHECK_READLINE: CORRECTNESS -&gt;
968 STYLE</li>
969 <li>DMI_INVOKING_TOSTRING_ON_ARRAY: Long description
970 mentions array name whenever possible</li>
971 </ul>
972 </li>
973 <li>Fixes:
974 <ul>
975 <li>Updated manual to mention that Java 1.5 is now a
976 requirement for running FindBugs
977 <li>Applied patch 1840206 fixing issue "Ant task does not
978 work when presetdef is used" - thanks to phejl
979 <li>Applied patch 1778690 fixing issue "Ant task: tolerate
980 but complain about invalid auxClasspath" - thanks to David
981 Schmidt
982 <li>Applied patch 1852125 adding a Chinese-language GUI
983 bundle props file - thanks to fifi
984 <li>Applied patch 1845903 adding ability to load XML results
985 with the Eclipse plugin - thanks to Alex Mont
986 <li>Fixed issue 1844671 - "FP for "reversed" null check in
987 catch for stream close"
988 <li>Fixed issue 1836050 - "-onlyAnalyze broken"
989 <li>Fixed issue 1853011 - "Typo: Field names should start
990 with aN lower case letter"
991 <li>Fixed issue 1844181 - "JNLP file does not contain all
992 necessary JARs"
993 <li>Fixed issue 1840245 - "xxxException class does not
994 derive from Exception"
995 <li>Fixed issue 1840277 - "[M D EC] Typo in bug
996 documentation"
997 <li>Fixed issue 1782447 - "OutOfMemoryError if i activate
998 Findbugs on my project"
999 <li>Fixed issue 1830576 - "[regression] keySet/entrySet
1000 false positive"
1001 </ul>
1002 </li>
1003 <li>Other:
1004 <ul>
1005 <li>New bug code: "IO" (for
1006 IO_APPENDING_TO_OBJECT_OUTPUT_STREAM)</li>
1007 <li>Added "-onlyMostRecent" option for computeBugHistory
1008 script/ant task
1009 <li>More explicit language in
1010 RV_RETURN_VALUE_IGNORED_BAD_PRACTICE messages
1011 <li>Modified ResourceValueAnalysis to correctly identify
1012 null == X or null != X as a null check (for issue 1844671)
1013 <li>Modified DMI_HARDCODED_ABSOLUTE_FILENAME logic in
1014 DumbMethodInvocations to ignore files from /etc or /dev and
1015 increase priority of files from /home
1016 <li>Better bug details for infinite loop warnings
1017 <li>Modified unread-fields detector to reduce false
1018 positives from reflective fields
1019 <li>build.xml "classes" target now builds all sources in one
1020 step
1021 </ul>
1022 </li>
1023 </ul>
1024
1025 <p>Changes since version 1.2.1</p>
1026 <ul>
1027 <li>New Detectors and Reports
1028 <ul>
1029 <li>SynchronizationOnSharedBuiltinConstant
1030 <ul>
1031 <li>DL_SYNCHRONIZATION_ON_SHARED_CONSTANT: The code
1032 synchronizes on a shared primitive constant, such as an
1033 interned String. Such constants are interned and shared across
1034 all other classes loaded by the JVM. Thus, this could be
1035 locking on something that other code might also be locking.
1036 This could result in very strange and hard to diagnose
1037 blocking and deadlock behavior. See <a
1038 href="http://www.javalobby.org/java/forums/t96352.html">http://www.javalobby.org/java/forums/t96352.html</a>
1039 and <a href="http://jira.codehaus.org/browse/JETTY-352">http://jira.codehaus.org/browse/JETTY-352</a>.
1040
1041 </ul>
1042 </li>
1043 <li>OverridingEqualsNotSymmetrical
1044 <ul>
1045 <li>EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC: Looks for equals
1046 methods that override equals methods in a superclass where the
1047 equivalence relationship might not be symmetrical.
1048 </ul>
1049 </li>
1050 <li>CheckTypeQualifiers
1051 <ul>
1052 <li>TQ_ALWAYS_VALUE_USED_WHERE_NEVER_REQUIRED: A value
1053 specified as carrying a type qualifier annotation is consumed
1054 in a location or locations requiring that the value not carry
1055 that annotation. More precisely, a value annotated with a type
1056 qualifier specifying when=ALWAYS is guaranteed to reach a use
1057 or uses where the same type qualifier specifies when=NEVER.</li>
1058 <li>TQ_NEVER_VALUE_USED_WHERE_ALWAYS_REQUIRED: A value
1059 specified as not carrying a type qualifier annotation is
1060 guaranteed to be consumed in a location or locations requiring
1061 that the value does carry that annotation. More precisely, a
1062 value annotated with a type qualifier specifying when=NEVER is
1063 guaranteed to reach a use or uses where the same type
1064 qualifier specifies when=ALWAYS.</li>
1065 <li>TQ_MAYBE_SOURCE_VALUE_REACHES_ALWAYS_SINK: A value
1066 that might not carry a type qualifier annotation reaches a use
1067 which requires that annotation.</li>
1068 <li>TQ_MAYBE_SOURCE_VALUE_REACHES_NEVER_SINK: A value
1069 which might carry a type qualifier annotation reaches a use
1070 which forbids values carrying that annotation.</li>
1071 </ul>
1072 </li>
1073 </ul>
1074 </li>
1075 <li>New Reports (existing detectors)
1076 <ul>
1077 <li>FindHEmismatch
1078 <ul>
1079 <li>EQ_DOESNT_OVERRIDE_EQUALS: This class extends a class
1080 that defines an equals method and adds fields, but doesn't
1081 define an equals method itself. Thus, equality on instances of
1082 this class will ignore the identity of the subclass and the
1083 added fields. Be sure this is what is intended, and that you
1084 don't need to override the equals method. Even if you don't
1085 need to override the equals method, consider overriding it
1086 anyway to document the fact that the equals method for the
1087 subclass just return the result of invoking super.equals(o).</li>
1088 </ul>
1089 </li>
1090 <li>Naming
1091 <ul>
1092 <li>NM_WRONG_PACKAGE, NM_WRONG_PACKAGE_INTENTIONAL: The
1093 method in the subclass doesn't override a similar method in a
1094 superclass because the type of a parameter doesn't exactly
1095 match the type of the corresponding parameter in the
1096 superclass.</li>
1097 <li>NM_SAME_SIMPLE_NAME_AS_SUPERCLASS: This class has a
1098 simple name that is identical to that of its superclass,
1099 except that its superclass is in a different package (e.g., <code>alpha.Foo</code>
1100 extends <code>beta.Foo</code>). This can be exceptionally
1101 confusing, create lots of situations in which you have to look
1102 at import statements to resolve references and creates many
1103 opportunities to accidently define methods that do not
1104 override methods in their superclasses.
1105 </li>
1106 <li>NM_SAME_SIMPLE_NAME_AS_INTERFACE: This class/interface
1107 has a simple name that is identical to that of an
1108 implemented/extended interface, except that the interface is
1109 in a different package (e.g., <code>alpha.Foo</code> extends <code>beta.Foo</code>).
1110 This can be exceptionally confusing, create lots of situations
1111 in which you have to look at import statements to resolve
1112 references and creates many opportunities to accidently define
1113 methods that do not override methods in their superclasses.
1114 </li>
1115 </ul>
1116 <li>FindRefComparison
1117 <ul>
1118 <li>EC_UNRELATED_TYPES_USING_POINTER_EQUALITY: This method
1119 uses using pointer equality to compare two references that
1120 seem to be of different types. The result of this comparison
1121 will always be false at runtime.</li>
1122 </ul>
1123 </li>
1124 <li>IncompatMask
1125 <ul>
1126 <li>BIT_SIGNED_CHECK, BIT_SIGNED_CHECK_HIGH_BIT: This
1127 method compares an expression such as <tt>((event.detail
1128 &amp; SWT.SELECTED) &gt; 0)</tt>. Using bit arithmetic and then
1129 comparing with the greater than operator can lead to
1130 unexpected results (of course depending on the value of
1131 SWT.SELECTED). If SWT.SELECTED is a negative number, this is a
1132 candidate for a bug. Even when SWT.SELECTED is not negative,
1133 it seems good practice to use '!= 0' instead of '&gt; 0'.
1134 </li>
1135 </ul>
1136 </li>
1137 <li>LazyInit
1138 <ul>
1139 <li>LI_LAZY_INIT_UPDATE_STATIC: This method contains an
1140 unsynchronized lazy initialization of a static field. After
1141 the field is set, the object stored into that location is
1142 further accessed. The setting of the field is visible to other
1143 threads as soon as it is set. If the further accesses in the
1144 method that set the field serve to initialize the object, then
1145 you have a <em>very serious</em> multithreading bug, unless
1146 something else prevents any other thread from accessing the
1147 stored object until it is fully initialized.
1148 </li>
1149 </ul>
1150 </li>
1151 <li>FindDeadLocalStores
1152 <ul>
1153 <li>DLS_DEAD_STORE_OF_CLASS_LITERAL: This instruction
1154 assigns a class literal to a variable and then never uses it.
1155 <a href="//java.sun.com/j2se/1.5.0/compatibility.html#literal">The
1156 behavior of this differs in Java 1.4 and in Java 5.</a> In Java
1157 1.4 and earlier, a reference to <code>Foo.class</code> would
1158 force the static initializer for <code>Foo</code> to be
1159 executed, if it has not been executed already. In Java 5 and
1160 later, it does not. See Sun's <a
1161 href="//java.sun.com/j2se/1.5.0/compatibility.html#literal">article
1162 on Java SE compatibility</a> for more details and examples, and
1163 suggestions on how to force class initialization in Java 5.
1164 </li>
1165 </ul>
1166 </li>
1167 <li>MethodReturnCheck
1168 <ul>
1169 <li>RV_RETURN_VALUE_IGNORED_BAD_PRACTICE: This method
1170 returns a value that is not checked. The return value should
1171 be checked since it can indication an unusual or unexpected
1172 function execution. For example, the <code>File.delete()</code>
1173 method returns false if the file could not be successfully
1174 deleted (rather than throwing an Exception). If you don't
1175 check the result, you won't notice if the method invocation
1176 signals unexpected behavior by returning an atypical return
1177 value.
1178 </li>
1179 <li>RV_EXCEPTION_NOT_THROWN: This code creates an
1180 exception (or error) object, but doesn't do anything with it.
1181 </li>
1182 </ul>
1183 </li>
1184 </ul>
1185 </li>
1186 <li>Changes to Existing Reports
1187 <ul>
1188 <li>NS_NON_SHORT_CIRCUIT: BAD_PRACTICE -&gt; STYLE</li>
1189 <li>NS_DANGEROUS_NON_SHORT_CIRCUIT: CORRECTNESS -&gt; STYLE</li>
1190 <li>RC_REF_COMPARISON: CORRECTNESS -&gt; BAD_PRACTICE</li>
1191 </ul>
1192 </li>
1193 <li>GUI Changes
1194 <ul>
1195 <li>Added importing and exporting of bug filters</li>
1196 <li>Better handling of failed analysis runs</li>
1197 <li>Added "-look" parameter for selecting look-and-feel</li>
1198 <li>Fixed incorrect package filtering</li>
1199 <li>Fixed issue where "synchronized" was not
1200 syntax-highlighted</li>
1201 </ul>
1202 </li>
1203 <li>Ant-task Changes
1204 <ul>
1205 <li>Refactored common ant-task code to AbstractFindBugsTask</li>
1206 <li>Added tasks for computeBugHistory, convertXmlToText,
1207 filterBugs, mineBugHistory, setBugDatabaseInfo</li>
1208 </ul>
1209 </li>
1210 <li>Manual
1211 <ul>
1212 <li>Updates to GUI section, including new screenshots</li>
1213 <li>Added description of rejarForAnalysis</li>
1214 <li>Revamp of data-mining section</li>
1215 </ul>
1216 </li>
1217 <li>Other Major
1218 <ul>
1219 <li>Internal restructuring for lower memory overhead</li>
1220 </ul>
1221 </li>
1222 <li>Other Minor
1223 <ul>
1224 <li>Fixed typo: was STCAL_STATIC_SIMPLE_DATA_FORMAT_INSTANCE
1225 now STCAL_STATIC_SIMPLE_DATE_FORMAT_INSTANCE</li>
1226 <li>-outputFile parameter became -output</li>
1227 <li>More sensitivity and specificity inLazyInit detector</li>
1228 <li>More sensitivity and specificity in Naming detector</li>
1229 <li>More sensitivity and specificity in UnreadFields
1230 detector</li>
1231 <li>More sensitivity in FindNullDeref detector</li>
1232 <li>More sensitivity in FindBadCast2 detector</li>
1233 <li>More specificity in FindReturnRef detector</li>
1234 <li>Many other tweaks and bug fixes</li>
1235 </ul>
1236 </li>
1237 </ul>
1238
1239 <p>Changes since version 1.2.0</p>
1240 <ul>
1241 <li>Bug fixes:
1242 <ul>
1243 <li><a
1244 href="http://fisheye2.cenqua.com/changelog/findbugs/?cs=8219">Fix</a>
1245 <a
1246 href="http://sourceforge.net/tracker/index.php?func=detail&aid=1726946&group_id=96405&atid=614693">bug</a>
1247 with detectors that were requested to be disabled but were
1248 enabled due to requirements of other detectors.</li>
1249 <li>Fix bugs in incremental analysis within Eclipse plugin</li>
1250 <li>Fix some analysis errors</li>
1251 <li>Fix some threading bugs in GUI2</li>
1252 <li>Report version as version when it was compiled, not when
1253 it was run</li>
1254 <li>Copy analysis time stamp when filtering or transforming
1255 analysis files.</li>
1256 </ul>
1257 <li>Enabled StaticCalendarDetector</li>
1258 <li>Reworked GUI2 to use standard FindBugs filters
1259 <ul>
1260 <li>Allow a suppression filter to be stored in a project and
1261 persisted to the XML representation of a project.</li>
1262 </ul>
1263 </li>
1264
1265 <li>Move away from old GUI2 save format (a directory
1266 containing an xml file and another file containing serialized
1267 filters).</li>
1268 <li>Support/recommend use of two new file extensions/formats:
1269 <dl>
1270 <dt>.fba - FindBugs Analysis File</dt>
1271 <dd>Exactly the same as an existing bug collection file
1272 stored in XML format, but using a distinct file extension to
1273 make it easier to figure out which xml files contain FindBugs
1274 results.</dd>
1275 <dt>.fbp - FindBugs Project File</dt>
1276 <dd>Contains just the information needed to run FindBugs and
1277 display the results (e.g., the files to be analyzed, the
1278 auxiliary class path and the location of source files)
1279 </dl>
1280 </li>
1281 </ul>
1282 <p>Changes since version 1.1.3</p>
1283 <ul>
1284 <li>Added -xml:withAbridgedMessages option to generate xml
1285 containing shorter messages. The messages will be shorted by doing
1286 things like eliding package names, and leaving off the source line
1287 from the LongMessage. These messages are appropriate if being used
1288 in a context where the non-message components of the bug
1289 annotations will be used to provide more information (e.g.,
1290 clicking on the message for a MethodAnnotation will display the
1291 source for the method).
1292 <ul>
1293 <li>FindBugsDisplayFeatures.setAbridgedMessages(true) can be
1294 used to generate abridged messages when FindBugs is being
1295 accessed directly (not via generated XML) from a GUI or IDE.</li>
1296 </ul>
1297 <li>In null pointer analysis, try to be better about always
1298 showing two locations: where it is known null and where it is
1299 dereferenced.
1300 <li>Interprocedural analysis of which methods return nonnull
1301 values
1302 <li>Use method calls to select order in which classes are
1303 analyzed, and order in which methods are analyzed, to improve
1304 interprocedural analysis results.
1305 <li>Significant improvements in memory footprint, memory
1306 allocation and CPU utilization (20-30% reduction in all three)
1307 <li>Added a project name, to provide better descriptions in
1308 the HTML output.
1309 <li>Added new bug pattern: Casting to char, or bit masking
1310 with nonnegative value, and then checking to see if the result is
1311 negative.
1312 <li>Stopped reporting transient fields of classes not marked
1313 as serializable. Transient is used by other persistence
1314 frameworks.
1315 <li>Improvements to detector for SQL injection (Thanks to <a
1316 href="http://www.clock.org/~matt">Matt Hargett</a> for his
1317 contributions
1318 <li>Changed open/save options in GUI2 to not distinguish
1319 between FindBugs projects and saved FindBugs analysis results.
1320 <li>Improvements to detection of serious non-short-circuit
1321 evaluation.
1322 <li>Updated Japanese localization (thanks to Ruimo Uno)
1323 <li>Eclipse plugin changes:
1324 <ul>
1325 <li>Created Bug User Annotations and Bug Tree Views
1326 <li>Use different icons for different bug priorities
1327 <li>Provide more information in Bug Details view
1328 </ul>
1329 </ul>
1330
1331 <p>Changes since version 1.1.2:</p>
1332 <ul>
1333 <li>Fixed broken Ant task
1334 <li>Added running ant task to smoke test
1335 <li>Added validating xml and html output to smoke test
1336 <li>Fixed some (but not all) issues with html output
1337 validation
1338 <li>Added check for x.equals(x) and x.compareTo(x)
1339 <li>Various bug fixes
1340 </ul>
1341 <p>Changes since version 1.1.1:</p>
1342 <ul>
1343 <li>Added check for infinite iterative loops</li>
1344 <li>Added check for use of incompatible types in a collection
1345 (e.g., checking to see if a Set&lt;String&gt; contains a
1346 StringBuffer).</li>
1347 <li>Added check for invocations of equals or hashCode on a
1348 URL, which, <a
1349 href="http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html">surprising
1350 many people</a>, requires DNS resolution.
1351 </li>
1352 <li>Added check for classes that define compareTo but not
1353 equals; such classes can exhibit some anomalous behavior (e.g.,
1354 they are treated differently by PriorityQueues in Java 5 and Java
1355 6).</li>
1356 <li>Added a check for useless self operations (e.g., x &lt; x
1357 or x ^ x).</li>
1358 <li>Fixed a data race that could cause the GUI to fail on
1359 startup</li>
1360 <li>Partial internationalization of the new GUI</li>
1361 <li>Fix bug in "Redo analysis" option of new GUI</li>
1362 <li>Tuning to reduce false positives</li>
1363 <li>Fixed a bug in null pointer analysis that was generating
1364 false positive null pointer warnings on exception paths. Fixing
1365 this bug eliminates about 1/4 of the warnings on null pointer
1366 exceptions on exception paths.</li>
1367 <li>Fixed a bug in the processing of phi nodes for fields in
1368 the null pointer analysis</li>
1369 <li>Applied contributed patch that provides more quick fixes
1370 in Eclipse plugin.</li>
1371 <li>Fixed a number of bugs in the Eclipse auto update sites,
1372 and in the way date qualifiers were being used in the Eclipse
1373 plugin. You may need to manually disable your existing version of
1374 the plugin and download the 1.1.2 from the update site to get the
1375 automatic update function working correctly. The Eclipse update
1376 sites are described at <a
1377 href="http://findbugs.cs.umd.edu/eclipse/">http://findbugs.cs.umd.edu/eclipse/</a>.
1378
1379 </li>
1380 <li>Fixed progress bar in Eclipse plugin</li>
1381 <li>A number of other bug fixes.</li>
1382 </ul>
1383
1384 <p>Changes since version 1.1.0:</p>
1385 <ul>
1386 <li>less scanning of classes not on the analysis path (This
1387 was causing some performance problems.)</li>
1388 <li>no unread field warnings for fields annotated with
1389 javax.persistent or javax.ejb3</li>
1390 <li>Eclipse plugin
1391 <ul>
1392 <li>bug annotation info displayed in Bug Details tab</li>
1393 <li>.fbwarnings data file now stored in .metadata (not in
1394 the project itself)</li>
1395 </ul>
1396 </li>
1397 <li>new SE_BAD_FIELD_INNER_CLASS pattern</li>
1398 <li>updates to Japanese translation (ruimo)</li>
1399 <li>fix some internal slashed/dotted path confusion</li>
1400 <li>other minor improvements</li>
1401 </ul>
1402
1403 <p>Changes since version 1.0.0:</p>
1404
1405 <ul>
1406 <li>Overall, the change from FindBugs 1.0.0 to FindBugs 1.1.0
1407 has been a big change. We've done a lot of work in a lot of areas,
1408 and aren't even going to try to enumerate all the changes.</li>
1409 <li>We spent a lot of time reviewing the results generated by
1410 FindBugs for open source and commercial code bases, and made a
1411 number of changes, small and large, to minimize the number of
1412 false positives. Our primary focus for this was warnings reported
1413 as high and medium priority correctness warnings. Our internal
1414 evaluation is that we produce very few high/medium priority
1415 correctness warnings where the analysis is actually wrong, and
1416 that more than 75% of the high/medium priority correctness
1417 warnings correspond to real coding defects that need addressing in
1418 the source code. The remaining 25% are largely cases such as a
1419 branch or statement that if taken would lead to an error, but in
1420 fact is a dead branch or statement that can never be taken. Such
1421 coding is confusing and hard to maintain, so it should arguably be
1422 fixed, but it is unlikely to actually result in an error during
1423 execution. Thus, some might classify those warnings as false
1424 positives.</li>
1425 <li>We've substantially improved the analysis for errors that
1426 could result in null pointer dereferences. Overall, our experience
1427 has been that these changes have roughly doubled the number of
1428 null pointer errors we detect, without increasing the number of
1429 false positives (in fact, our false positive rate has gone down).
1430 The improvements are due to four factors:
1431 <ul>
1432 <li>By default, we now do some interprocedural analysis to
1433 determine methods that unconditionally dereference their
1434 parameters.</li>
1435 <li>FindBugs also comes with a model of which JDK methods
1436 unconditionally dereference their parameters.</li>
1437 <li>We do limited tracking of fields, so that we can detect
1438 null values stored in fields that lead to exceptions.</li>
1439 <li>We implemented a new analysis technique to find
1440 guaranteed dereferences. Consider the following example: <pre>public int f(Object x, boolean b) {
369
370 <li>Changes by Andrey Loskutov
371 <ul>
372 <li>fixed job scheduling errors in 3.8/4.2 Eclipse <a
373 href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=393748">bug
374 report</a>
375 <li>more realistic progress bar updates for jobs
376 <li>added nullness annotations for some common Eclipse API
377 methods known to usually return null values
378 <li>Added support for org.eclipse.jdt.annotation.Nullable,
379 NonNull and NonNullByDefault annotations (introduced with
380 Eclipse 3.8/4.2)</li>
381 </ul>
382 <li>Documentation improvements
383 <li><a href="http://code.google.com/p/findbugs/source/list">lots
384 of other small changes</a>
385 </ul>
386 <h1>FindBugs Change Log, Version 2.0.1</h1>
387
388 <ul>
389 <li>New bug patterns; in some cases, bugs previous reported as
390 other bug patterns are reported as instances of these new bug
391 patterns in order to make it easier for developers to understand
392 the bug reports
393 <ul>
394 <li><a
395 href="http://findbugs.sourceforge.net/bugDescriptions.html#PT_ABSOLUTE_PATH_TRAVERSAL">PT_ABSOLUTE_PATH_TRAVERSAL</a></li>
396 <li><a
397 href="http://findbugs.sourceforge.net/bugDescriptions.html#PT_RELATIVE_PATH_TRAVERSAL">PT_RELATIVE_PATH_TRAVERSAL</a></li>
398 <li><a
399 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR">NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR</a></li>
400 <li><a
401 href="http://findbugs.sourceforge.net/bugDescriptions.html#MS_SHOULD_BE_REFACTORED_TO_BE_FINAL">MS_SHOULD_BE_REFACTORED_TO_BE_FINAL</a></li>
402 <li><a
403 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC_UNCONFIRMED_CAST_OF_RETURN_VALUE">BC_UNCONFIRMED_CAST_OF_RETURN_VALUE</a></li>
404 <li><a
405 href="http://findbugs.sourceforge.net/bugDescriptions.html#PT_ABSOLUTE_PATH_TRAVERSAL">PT_ABSOLUTE_PATH_TRAVERSAL</a></li>
406 <li><a
407 href="http://findbugs.sourceforge.net/bugDescriptions.html#TQ_COMPARING_VALUES_WITH_INCOMPATIBLE_TYPE_QUALIFIERS">TQ_COMPARING_VALUES_WITH_INCOMPATIBLE_TYPE_QUALIFIERS</a></li>
408 </ul>
409 </li>
410
411 <li>Changes to fix false negatives for the following bug
412 patterns: <a
413 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC_UNCONFIRMED_CAST">BC_UNCONFIRMED_CAST</a>,
414 <a
415 href="http://findbugs.sourceforge.net/bugDescriptions.html#EC_BAD_ARRAY_COMPARE">EC_BAD_ARRAY_COMPARE</a>,
416 <a
417 href="http://findbugs.sourceforge.net/bugDescriptions.html#EQ_UNUSUAL">EQ_UNUSUAL</a>,
418 <a
419 href="http://findbugs.sourceforge.net/bugDescriptions.html#GC_UNRELATED_TYPES">GC_UNRELATED_TYPES</a>,
420 and <a
421 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE">NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE</a>.
422 </li>
423
424 <li>Changes to fix false positions for the following bug
425 patterns: <a
426 href="http://findbugs.sourceforge.net/bugDescriptions.html#DMI_DOH">DMI_DOH</a>,
427 <a
428 href="http://findbugs.sourceforge.net/bugDescriptions.html#EC_UNRELATED_TYPES">EC_UNRELATED_TYPES</a>,
429 and <a
430 href="http://findbugs.sourceforge.net/bugDescriptions.html#SE_BAD_FIELD">SE_BAD_FIELD</a>.
431 </li>
432 </ul>
433
434 <h1>FindBugs Change Log, Version 2.0.0</h1>
435
436 <h2>Changes since version 1.3.8</h2>
437 <ul>
438 <li>New bug patterns; in some cases, bugs previous reported as
439 other bug patterns are reported as instances of these new bug
440 patterns in order to make it easier for developers to understand
441 the bug reports
442 <ul>
443 <li><a
444 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC_IMPOSSIBLE_DOWNCAST ">BC_IMPOSSIBLE_DOWNCAST
445 </a></li>
446 <li><a
447 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY ">BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY
448 </a></li>
449 <li><a
450 href="http://findbugs.sourceforge.net/bugDescriptions.html#EC_INCOMPATIBLE_ARRAY_COMPARE ">EC_INCOMPATIBLE_ARRAY_COMPARE
451 </a></li>
452 <li><a
453 href="http://findbugs.sourceforge.net/bugDescriptions.html#JLM_JSR166_UTILCONCURRENT_MONITORENTER ">JLM_JSR166_UTILCONCURRENT_MONITORENTER
454 </a></li>
455 <li><a
456 href="http://findbugs.sourceforge.net/bugDescriptions.html#LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE ">LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE
457 </a></li>
458 <li><a
459 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_CLOSING_NULL ">NP_CLOSING_NULL
460 </a></li>
461 <li><a
462 href="http://findbugs.sourceforge.net/bugDescriptions.html#RC_REF_COMPARISON_BAD_PRACTICE ">RC_REF_COMPARISON_BAD_PRACTICE
463 </a></li>
464 <li><a
465 href="http://findbugs.sourceforge.net/bugDescriptions.html#RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN ">RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN
466 </a></li>
467 <li><a
468 href="http://findbugs.sourceforge.net/bugDescriptions.html#RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORED ">RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORED
469 </a></li>
470 <li><a
471 href="http://findbugs.sourceforge.net/bugDescriptions.html#SIC_THREADLOCAL_DEADLY_EMBRACE ">SIC_THREADLOCAL_DEADLY_EMBRACE
472 </a></li>
473 <li><a
474 href="http://findbugs.sourceforge.net/bugDescriptions.html#UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR ">UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR
475 </a></li>
476 <li><a
477 href="http://findbugs.sourceforge.net/bugDescriptions.html#VA_FORMAT_STRING_EXPECTED_MESSAGE_FORMAT_SUPPLIED ">VA_FORMAT_STRING_EXPECTED_MESSAGE_FORMAT_SUPPLIED
478 </a></li>
479 </ul>
480 </li>
481 <li>Providing a bug rank (1-20), and the ability to filter by
482 bug rank. Eventually, it will be possible to specify your own
483 rules for ranking bugs, but the procedure for doing so hasn't been
484 specified yet.</li>
485 <li>Fixed about <a
486 href="https://sourceforge.net/search/index.php?group_id=96405&search_summary=1&search_details=1&type_of_search=artifact&group_artifact_id%5B%5D=614693&open_date_start=2009-03-16&open_date_end=2009-08-20&form_submit=Search">45
487 bugs filed</a> through SourceForge
488 </li>
489 <li>Various reclassifications and priority tweaks</li>
490 <li>Added more bug annotations to a variety of bug reports.
491 This provides more context for understanding bug reports (e.g., if
492 the value in question was is the return value of a method, the
493 method is described as the source of the value in a bug
494 annotation). This also provide more accurate tracking of issues
495 across versions of the code being analyzed, but has the downside
496 that when comparing results from FindBugs 1.3.8 and FindBugs 1.3.9
497 on the same version of code being analyzed, FindBugs may think
498 that mistakenly believe that the issue reported by 1.3.8 was fixed
499 and a new issue was introduced that was reported by FindBugs
500 1.3.9. While annoying, it would be unusual for more than a dozen
501 issues per million lines of codes to be mistracked.</li>
502 <li>Lots of internal changes moving towards FindBugs 2.0, but
503 these features are undocumented, not yet officially supported, and
504 subject to radical changes before FindBugs 2.0 is released.</li>
505 </ul>
506
507 <p>Changes since version 1.3.8</p>
508 <ul>
509 <li>New bug patterns; in some cases, bugs previous reported as
510 other bug patterns are reported as instances of these new bug
511 patterns in order to make it easier for developers to understand
512 the bug reports
513 <ul>
514 <li><a
515 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC_IMPOSSIBLE_DOWNCAST ">BC_IMPOSSIBLE_DOWNCAST
516 </a>
517 <li><a
518 href="http://findbugs.sourceforge.net/bugDescriptions.html#BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY ">BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY
519 </a>
520 <li><a
521 href="http://findbugs.sourceforge.net/bugDescriptions.html#EC_INCOMPATIBLE_ARRAY_COMPARE ">EC_INCOMPATIBLE_ARRAY_COMPARE
522 </a>
523 <li><a
524 href="http://findbugs.sourceforge.net/bugDescriptions.html#JLM_JSR166_UTILCONCURRENT_MONITORENTER ">JLM_JSR166_UTILCONCURRENT_MONITORENTER
525 </a>
526 <li><a
527 href="http://findbugs.sourceforge.net/bugDescriptions.html#LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE ">LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE
528 </a>
529 <li><a
530 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_CLOSING_NULL ">NP_CLOSING_NULL
531 </a>
532 <li><a
533 href="http://findbugs.sourceforge.net/bugDescriptions.html#RC_REF_COMPARISON_BAD_PRACTICE ">RC_REF_COMPARISON_BAD_PRACTICE
534 </a>
535 <li><a
536 href="http://findbugs.sourceforge.net/bugDescriptions.html#RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN ">RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN
537 </a>
538 <li><a
539 href="http://findbugs.sourceforge.net/bugDescriptions.html#RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORED ">RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORED
540 </a>
541 <li><a
542 href="http://findbugs.sourceforge.net/bugDescriptions.html#SIC_THREADLOCAL_DEADLY_EMBRACE ">SIC_THREADLOCAL_DEADLY_EMBRACE
543 </a>
544 <li><a
545 href="http://findbugs.sourceforge.net/bugDescriptions.html#UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR ">UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR
546 </a>
547 <li><a
548 href="http://findbugs.sourceforge.net/bugDescriptions.html#VA_FORMAT_STRING_EXPECTED_MESSAGE_FORMAT_SUPPLIED ">VA_FORMAT_STRING_EXPECTED_MESSAGE_FORMAT_SUPPLIED
549 </a>
550 </ul>
551 </li>
552 <li>Providing a bug rank (1-20), and the ability to filter by
553 bug rank. Eventually, it will be possible to specify your own
554 rules for ranking bugs, but the procedure for doing so hasn't been
555 specified yet.</li>
556 <li>Fixed about <a
557 href="https://sourceforge.net/search/index.php?group_id=96405&search_summary=1&search_details=1&type_of_search=artifact&group_artifact_id%5B%5D=614693&open_date_start=2009-03-16&open_date_end=2009-08-20&form_submit=Search">45
558 bugs filed</a> through SourceForge
559 </li>
560 <li>Various reclassifications and priority tweaks</li>
561 <li>Added more bug annotations to a variety of bug reports.
562 This provides more context for understanding bug reports (e.g., if
563 the value in question was is the return value of a method, the
564 method is described as the source of the value in a bug
565 annotation). This also provide more accurate tracking of issues
566 across versions of the code being analyzed, but has the downside
567 that when comparing results from FindBugs 1.3.8 and FindBugs 1.3.9
568 on the same version of code being analyzed, FindBugs may think
569 that mistakenly believe that the issue reported by 1.3.8 was fixed
570 and a new issue was introduced that was reported by FindBugs
571 1.3.9. While annoying, it would be unusual for more than a dozen
572 issues per million lines of codes to be mistracked.</li>
573 <li>Lots of internal changes moving towards FindBugs 2.0, but
574 these features are undocumented, not yet officially supported, and
575 subject to radical changes before FindBugs 2.0 is released.</li>
576 </ul>
577
578 <p>Changes since version 1.3.7</p>
579 <ul>
580 <li>Primarily another small bugfix release.</li>
581 <li>FindBugs base:
582 <ul>
583 <li>New Reports:
584 <ul>
585 <li>SF_SWITCH_NO_DEFAULT: missing default case in switch
586 statement.</li>
587 <li>SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH_TO_THROW:
588 value ignored when switch fallthrough leads to thrown
589 exception.</li>
590 <li>INT_VACUOUS_BIT_OPERATION: bit operations that don't
591 do any meaningful work.</li>
592 <li>FB_UNEXPECTED_WARNING: warning generated that
593 conflicts with @NoWarning FindBugs annotation.</li>
594 <li>FB_MISSING_EXPECTED_WARNING: warning not generated
595 despite presence of @ExpectedWarning FindBugs annotation.</li>
596 <li>NOISE category: intended for use in data mining
597 experiments.
598 <ul>
599 <li>NOISE_NULL_DEREFERENCE: fake null point dereference
600 warning.</li>
601 <li>NOISE_METHOD_CALL: fake method call warning.</li>
602 <li>NOISE_FIELD_REFERENCE: fake field dereference
603 warning.</li>
604 <li>NOISE_OPERATION: fake operation warning.</li>
605 </ul>
606 </li>
607 </ul>
608 </li>
609 <li>Other:
610 <ul>
611 <li>Garvin Leclaire has created a new Apache Maven
612 repository for FindBugs at <a
613 href="http://code.google.com/p/findbugs/">the Google Code
614 FindBugs SVN repository</a>. (Thanks Garvin!)
615 </li>
616 </ul>
617 </li>
618 <li>Fixes:
619 <ul>
620 <li>[ 2317842 ] Highlighting broken in Windows</li>
621 <li>[ 2515908 ] check for oddness should track sign of
622 argument</li>
623 <li>[ 2487936 ] &quot;L B GC&quot; false pos cast from
624 Map.Entry.getKey() to Map.get()</li>
625 <li>[ 2528264 ] Ant tasks not compatible with Ant 1.7.1</li>
626 <li>[ 2539590 ] SF_SWITCH_FALLTHROUGH wrong message
627 reported</li>
628 <li>[ 2020066 ] Bug history displayed in fancy-hist.xsl is
629 incorrect</li>
630 <li>[ 2545098 ] Invalid character in analysis results file</li>
631 <li>[ 2492673 ] Plugin sites should specify &quot;requires
632 Eclipse 3.3 or newer&quot;</li>
633 <li>[ 2588044 ] a tiny typing error</li>
634 <li>[ 2589048 ] Documentation for convertXmlToText
635 insufficient</li>
636 <li>[ 2638739 ] NullPointerException when building</li>
637 </ul>
638 </li>
639 <li>Patches:
640 <ul>
641 <li>[ 2538184 ] Make BugCollection implement
642 Iterable&lt;BugInstance&gt; (thanks to Tomas Pollak)</li>
643 <li>[ 2249771 ] Add Maven2 Findbugs plugin link to the
644 Links page (thanks to Garvin Leclaire)</li>
645 <li>[ 2609526 ] Japanese manual update (thanks to K.
646 Hashimoto)</li>
647 <li>[ 2119482 ] CheckBcel checks for nonexistent classes
648 (thanks to Jerry James)</li>
649 </ul>
650 </li>
651 </ul>
652 </li>
653 <li>FindBugs Eclipse plugin:
654 <ul>
655 <li>Major feature enhancements (thanks to Andrey Loskutov).
656 See <a href="http://andrei.gmxhome.de/findbugs/index.html">this
657 overview</a> for more information.
658 </li>
659 <li>Major test improvements (thanks to Tomas Pollak).</li>
660 <li>Fixes:
661 <ul>
662 <li>[ 2532365 ] Compiler warning</li>
663 <li>[ 2522989 ] Fix filter files selection</li>
664 <li>[ 2504068 ] NullPointerException</li>
665 <li>[ 2640849 ] NPE in Eclipse plugin 1.3.7 and Eclipse
666 3.5 M5</li>
667 </ul>
668 </li>
669 <li>Patches:
670 <ul>
671 <li>[ 2143140 ] Unchecked conversion fixes for Eclipse
672 plugin (thanks to Jerry James)
673 </ul>
674 </li>
675 </ul>
676 </li>
677 </ul>
678
679 <p>Changes since version 1.3.6</p>
680 <ul>
681 <li>Overall, a small bugfix release.
682 <li>New detection of accidental vacuous/useless calls to
683 EasyMock methods, and of generic signatures that proclaim the use
684 of unhashable classes in ways that require that they be hashed.
685 <li>Eliminate some false positives where we were warning about
686 a useless call (e.g., comparing two incompatible types for
687 equality), but the only thing the code was doing with the result
688 was passing it to assertFalse.
689 <li>Japanese localization and manual by K.Hashimoto. (Thanks!)
690
691 <li>Added -exclude and -outputDir command line options to
692 rejarForAnalysis
693 <li>Extended -adjustPriorities option to FindBugs analysis
694 textui so that you can modify the priorities of individual bug
695 patterns as well as visitors, and also completely suppress
696 individual bug patterns or visitors.
697 <ul>
698 <li>e.g., -adjustPriority
699 MS_SHOULD_BE_FINAL=suppress,MS_PKGPROTECT=suppress,EI_EXPOSE_REP=suppress,EI_EXPOSE_REP2=suppress,PZLA_PREFER_ZERO_LENGTH_ARRAYS=raise
700
701 </ul>
702 </ul>
703
704
705 <p>Changes since version 1.3.5</p>
706 <ul>
707 <li>Added fairly exhaustive static analysis of uses of format
708 strings, checking for missing or extra arguements, invalid format
709 specifiers, or mismatched format specifiers and arguments (e.g,
710 passing a String value for a %d format specifier). The logic for
711 doing so is derived from Sun's java.util.Formatter class, and
712 available separately from FindBugs as part of the <a
713 href="https://jformatstring.dev.java.net/">jFormatString</a>
714 project.
715 <li>More tuning of the unsatisfied obligation detector. Since
716 this detector is still rather noisy and an unfinished research
717 project, I've moved the generated issues to a new category:
718 EXPERIMENTAL.
719 <li>Added check for <a
720 href="http://findbugs.sourceforge.net/bugDescriptions.html#BIT_ADD_OF_SIGNED_BYTE">BIT_ADD_OF_SIGNED_BYTE</a>;
721 similar to <a
722 href="http://findbugs.sourceforge.net/bugDescriptions.html#BIT_IOR_OF_SIGNED_BYTE">BIT_IOR_OF_SIGNED_BYTE</a>,
723 except that addition is being used to combine shifted signed
724 bytes.
725 <li>Changed detection of EI_EXPOSE_REP2, so we only report it
726 if the value stored is guaranteed to be the same value that was
727 passed in as a parameter.
728 <li>Added <a
729 href="http://findbugs.sourceforge.net/bugDescriptions.html#EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS">EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS</a>,
730 a warning when an equals method checks to see if an operand is an
731 instance of a class not compatible with itself. For example, if
732 the Foo class checks to see if the argument is an instance of
733 String. This is either a questionable design decision or a coding
734 mistake.
735 <li>Added <a
736 href="http://findbugs.sourceforge.net/bugDescriptions.html#DMI_INVOKING_HASHCODE_ON_ARRAY">DMI_INVOKING_HASHCODE_ON_ARRAY</a>,
737 which checks for invoking <code>hashCode()</code> on an array,
738 which returns a hash code that ignores the contents of the array.
739
740 <li>Added checks for using <code>x.removeAll(x)</code> to
741 rather than <code>x.clear()</code> to clear an array.
742 <li>Add checks for calls such as <code>x.contains(x)</code>, <code>x.remove(x)</code>
743 and <code>x.containsAll(x)</code>.
744 <li>Improvements to Eclipse plugin (thanks to Andrey
745 Loskutov):
746 <ul>
747 <li>Report separate markers for each occurrence of an issue
748 that appears multiple times in a method
749 <li>fine tuning for reported markers: add only one marker
750 for fields, add marker on right position
751 <li>link bugs selected in bug explorer view to the opened
752 editor and vice versa
753 <li>select bugs selected in editor ruler in the opened bug
754 explorer view
755 <li>consistent abbreviations used in both bug explorer and
756 bug details view
757 <li>added "Expand All" button to the bug explorer view
758 <li>added "Go Into/Go Up" buttons to the bug explorer view
759 <li>added "Copy to clipboard" menu/functionality to the
760 details view list widget
761 <li>fix for CNF exception if loading the backup solution for
762 broken browser widget
763 </ul>
764 </ul>
765
766
767
768 <p>Changes since version 1.3.4</p>
769 <ul>
770 <li>Analysis about 15% faster
771 <li><a
772 href="http://sourceforge.net/tracker/?atid=614693&group_id=96405&func=browse&status=closed">38
773 bugs closed</a></li>
774 <li>New defect warnings:
775 <ul>
776 <li>calls to methods that always throw
777 UnsupportedOperationException (DMI_UNSUPPORTED_METHOD)
778 <li>repeated conditional tests (e.g., <code>if (x
779 &lt; 0 || x &lt; 0) ...</code>) (RpC_REPEATED_CONDITIONAL_TEST)
780 <li>Complete rewrite of detector for format string problems.
781 More accurate, finds more problems, generates more descriptive
782 reports, several different bug pattern
783 (VA_FORMAT_STRING_EXTRA_ARGUMENTS_PASSED,
784 VA_FORMAT_STRING_ILLEGAL, VA_FORMAT_STRING_MISSING_ARGUMENT,
785 VA_FORMAT_STRING_BAD_ARGUMENT,
786 VA_FORMAT_STRING_NO_PREVIOUS_ARGUMENT)
787 <li>Fairly complete implementation of JSR-305 custom type
788 qualifier analysis (no support for custom validators yet).
789 (TQ_MAYBE_SOURCE_VALUE_REACHES_NEVER_SINK
790 TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_ALWAYS_SINK
791 TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_NEVER_SINK)
792 <li>New detector for unsatisfied obligations such forgetting
793 to close a file (OBL_UNSATISFIED_OBLIGATION).
794 <li>Warning when a parameter is marked as nullable, but is
795 always dereferenced.
796 (NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE)
797 <lI>Separate warning for dereference the result of readLine
798 (NP_DEREFERENCE_OF_READLINE_VALUE)
799 </ul>
800 <li>When XML is generated with messages, the project stats now
801 include &lt;FileStat&gt; elements. For each source file, this
802 gives the path for the file, the total number of warnings for that
803 file, and a bugHash for the file. While the instanceHash for a bug
804 is intended to be version invariant (ignoring line numbers, etc),
805 the bugHash for a file is intended to reflect all the information
806 about the warnings in that file. The intended use case is that if
807 the bugHash for a file is the same in two analysis runs, then <em>nothing</em>
808 has changed about any of the warnings reported for that file
809 between the two analysis runs.
810 <li>More merging of similar issues within a method. For
811 example, if the result of readLine() is dereferences multiple
812 times within a method, it will be reported as a single warning
813 with occurrences at multiple source lines.
814 </ul>
815 <p>Changes since version 1.3.3</p>
816
817 <ul>
818 <li>FindBugs base
819 <ul>
820 <li>New Reports:
821 <ul>
822 <li>EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC: equals method
823 overrides equals in superclass and may not be symmetric</li>
824 <li>EQ_ALWAYS_TRUE: equals method always returns true</li>
825 <li>EQ_ALWAYS_FALSE: equals method always returns false</li>
826 <li>EQ_COMPARING_CLASS_NAMES: equals method compares class
827 names rather than class objects</li>
828 <li>EQ_UNUSUAL: Unusual equals method</li>
829 <li>EQ_GETCLASS_AND_CLASS_CONSTANT: equals method fails
830 for subtypes</li>
831 <li>SE_READ_RESOLVE_IS_STATIC: The readResolve method must
832 not be declared as a static method.</li>
833 <li>SE_PRIVATE_READ_RESOLVE_NOT_INHERITED: private
834 readResolve method not inherited by subclasses</li>
835 <li>MSF_MUTABLE_SERVLET_FIELD: Mutable servlet field</li>
836 <li>XSS_REQUEST_PARAMETER_TO_SEND_ERROR: Servlet reflected
837 cross site scripting vulnerability</li>
838 <li>SKIPPED_CLASS_TOO_BIG: Class too big for analysis</li>
839 </ul>
840 </li>
841 <li>Other:
842 <ul>
843 <li>Value-number analysis now more space-efficient</li>
844 <li>Enhancements to reduce memory overhead when analyzing
845 very large classes</li>
846 <li>Now skips very large classes that would otherwise take
847 too much time and memory to analyze</li>
848 <li>Infrastructure for tracking effectively-constant/
849 effectively-final fields</li>
850 <li>Added more cweids</li>
851 <li>Enhanced taint tracking for taint-based detectors</li>
852 <li>Ignore doomed calls to equals if result is used as an
853 argument to assertFalse</li>
854 <li>EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC handles compareTo</li>
855 <li>Priority tweak for ICAST_INTEGER_MULTIPLY_CAST_TO_LONG
856 (only low priority if multiplying by 1000)</li>
857 <li>Improved tracking of fields across method calls</li>
858 </ul>
859 </li>
860 <li>Fixes:
861 <ul>
862 <li>[ 1941450 ] DLS_DEAD_LOCAL_STORE not reported</li>
863 <li>[ 1953323 ] Omitted break statement in
864 SynchronizeAndNullCheckField</li>
865 <li>[ 1942620 ] Source Directories selection dialog
866 interface confusion (partial)</li>
867 <li>[ 1948275 ] Unhelpful "Load of known null"</li>
868 <li>[ 1933922 ] MWM error in findbugs</li>
869 <li>[ 1934772 ] 1.3.3 appears to rely on JDK 1.6, JNLP
870 still specifies 1.5</li>
871 <li>[ 1933945 ] -loadbugs doesn't work</li>
872 <li>Fixed problems for class names starting with '$'</li>
873 <li>Fixed bugs and incomplete handling of annotations in
874 VersionInsensitiveBugComparator</li>
875 </ul>
876 </li>
877 <li>Patches:
878 <ul>
879 <li>[ 1955106 ] Javadoc fixes</li>
880 <li>[ 1951930 ] Superfluous import statements (thanks to
881 Jerry James)</li>
882 <li>[ 1951907 ] Missing @Deprecated annotations (thanks to
883 Jerry James)</li>
884 <li>[ 1951876 ] Infonode Docking Windows compile fix
885 (thanks to Jerry James)</li>
886 <li>[ 1936055 ] bugfix for findbugs.de.comment not working
887 (thanks to Peter Fokkinga)
888 </ul>
889 </li>
890 </ul>
891 <li>FindBugs BlueJ plugin
892 <ul>
893 <li>Updated to use FindBugs 1.3.4 (first new release since
894 1.1.3)</li>
895 </ul>
896 </li>
897 </ul>
898
899 <p>Changes since version 1.3.2</p>
900
901 <ul>
902 <li>FindBugs base
903 <ul>
904 <li>New Detectors:
905 <ul>
906 <li>FieldItemSummary: Produces summary information for
907 what is stored into fields</li>
908 <li>SynchronizeOnClassLiteralNotGetClass: Look for code
909 that synchronizes on the results of getClass rather than on
910 class literals</li>
911 <li>SynchronizingOnContentsOfFieldToProtectField: This
912 detector looks for code that seems to be synchronizing on a
913 field in order to guard updates of that field</li>
914 </ul>
915 </li>
916 <li>New BugCode:
917 <ul>
918 <li>HRS: HTTP Response splitting vulnerability</li>
919 <li>WL: Possible locking on wrong object</li>
920 </ul>
921 </li>
922 <li>New Reports:
923 <ul>
924 <li>DMI_CONSTANT_DB_PASSWORD: This code creates a database
925 connect using a hard coded, constant password</li>
926 <li>HRS_REQUEST_PARAMETER_TO_COOKIE: HTTP cookie formed
927 from untrusted input</li>
928 <li>HRS_REQUEST_PARAMETER_TO_HTTP_HEADER: HTTP parameter
929 directly written to HTTP header output</li>
930 <li>CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE: Class defines
931 clone() but doesn't implement Cloneable</li>
932 <li>DL_SYNCHRONIZATION_ON_BOXED_PRIMITIVE: Synchronization
933 on boxed primitive could lead to deadlock</li>
934 <li>DL_SYNCHRONIZATION_ON_BOOLEAN: Synchronization on
935 Boolean could lead to deadlock</li>
936 <li>ML_SYNC_ON_FIELD_TO_GUARD_CHANGING_THAT_FIELD:
937 Synchronization on field in futile attempt to guard that field
938 </li>
939 <li>DLS_DEAD_LOCAL_STORE_IN_RETURN: Useless assignment in
940 return statement</li>
941 <li>WL_USING_GETCLASS_RATHER_THAN_CLASS_LITERAL:
942 Synchronization on getClass rather than class literal</li>
943 </ul>
944 </li>
945 <li>Other:
946 <ul>
947 <li>Many enhancements to cross-site scripting detector and
948 its documentation</li>
949 <li>Enhanced switch fall through handling</li>
950 <li>Enhanced unread field handling (look for IF_ACMPEQ and
951 IF_ACMPNE)</li>
952 <li>Clarified documentation for @Nullable in manual</li>
953 <li>Fewer DeadLocalStore false positives</li>
954 <li>Fewer UnreadField false positives</li>
955 <li>Fewer StaticCalendarDetector false positives</li>
956 <li>Performance fix for slow file system IO e.g. Clearcase
957 repositories (thanks, Andrei!)</li>
958 <li>Other, general performance enhancements (thanks,
959 Andrei!)</li>
960 <li>Enhancements for using FindBugs scripts with MKS on
961 Windows (thanks, Kelly O'Hair!)</li>
962 <li>Noted in the manual that jsr305.jar must be present
963 for annotations to compile</li>
964 <li>Added and fine-tuned default-nullness annotations</li>
965 <li>More CWE IDs added</li>
966 <li>Check and warning for unexpected BCEL version in
967 classpath</li>
968 </ul>
969 </li>
970 <li>Fixes:
971 <ul>
972 <li>Bug fix to handling of local variable tables in BCEL</li>
973 <li>Refined documentation for
974 MTIA_SUSPECT_STRUTS_INSTANCE_FIELD</li>
975 <li>[ 1927295 ] NPE when called on project root</li>
976 <li>[ 1926405 ] Incorrect dead store warning</li>
977 <li>[ 1926409 ] Incorrect redundant nullcheck warning</li>
978 <li>[ 1926389 ] Wrong line number printed/highlighted in
979 bug</li>
980 <li>[ 1927040 ] typo in bug description</li>
981 <li>[ 1926263 ] Minor glitch in HTML output</li>
982 <li>[ 1926240 ] Minor error in standard options in manual</li>
983 <li>[ 1926236 ] Minor bug in installation section of
984 manual</li>
985 <li>[ 1925539 ] ZIP is default file system code base</li>
986 <li>[ 1894701 ] Livelock / memory leak in
987 ObjectTypeFactory (thanks, Andrei!)</li>
988 <li>[ 1867491 ] Doesn't reload annotations after code
989 changes in IDE (thanks, Andrei!)</li>
990 <li>[ 1921399 ] -project option not supported</li>
991 <li>[ 1913834 ] "Dead" store to variable with method call</li>
992 <li>[ 1917352 ] H B se:...field in serializable class</li>
993 <li>[ 1911617 ] CloneIdiom relies on
994 getNameConstantOperand for INSTANCEOF</li>
995 <li>[ 1911620 ] False +: DLS predecrement before return</li>
996 <li>[ 1871376 ] False negative: non-serializable Map field</li>
997 <li>[ 1871051 ] non standard clone() method</li>
998 <li>[ 1908854 ] Error in TestASM</li>
999 <li>[ 1907539 ] 22 minor errors in bug checker
1000 documentation</li>
1001 <li>[ 1897323 ] EJB implementation class false positives</li>
1002 <li>[ 1899648 ] Crash on startup on Vista with Java
1003 1.6.0_04</li>
1004 </ul>
1005 </li>
1006 </ul>
1007 </li>
1008 <li>FindBugs Eclipse plugin (change log by Andrey Loskutov)
1009 <ul>
1010 <li>new feature: export basic FindBugs numbers for projects
1011 via File-&gt;Export-&gt;Java-&gt;BugCounts (Andrey Loskutov)</li>
1012 <li>new feature: jobs for different projects will be run in
1013 parallel per default if running on a multi-core PC
1014 ("fb.allowParallelBuild" system property not used anymore)
1015 (Andrey Loskutov)</li>
1016 <li>fixed performance slowdown in the multi-threaded build,
1017 caused by workspace operation locks during assigning marker
1018 attributes (Andrey Loskutov)</li>
1019 </ul>
1020 </li>
1021 </ul>
1022
1023 <p>Changes since version 1.3.1</p>
1024
1025 <ul>
1026 <li>FindBugs base
1027 <ul>
1028 <li>New Bug Category:
1029 <ul>
1030 <li>SECURITY (Abbrev: S), A use of untrusted input in a
1031 way that could create a remotely exploitable security
1032 vulnerability</li>
1033 </ul>
1034 </li>
1035 <li>New Detectors:
1036 <ul>
1037 <li>CrossSiteScripting: This detector looks for
1038 obvious/blatant cases of cross site scripting vulnerabilities</li>
1039 </ul>
1040 </li>
1041 <li>New BugCode:
1042 <ul>
1043 <li>XSS: Cross site scripting</li>
1044 </ul>
1045 </li>
1046 <li>New Reports:
1047 <ul>
1048 <li>XSS_REQUEST_PARAMETER_TO_SERVLET_WRITER: HTTP
1049 parameter directly written to Servlet output, giving XSS
1050 vulnerability</li>
1051 <li>XSS_REQUEST_PARAMETER_TO_JSP_WRITER: HTTP parameter
1052 directly written to JSP output, giving XSS vulnerability</li>
1053 <li>EQ_OTHER_USE_OBJECT: equals() method defined that
1054 doesn't override Object.equals(Object)</li>
1055 <li>EQ_OTHER_NO_OBJECT: equals() method inherits rather
1056 than overrides equals(Object)</li>
1057 <li>NP_NULL_ON_SOME_PATH_MIGHT_BE_INFEASIBLE: Possible
1058 null pointer dereference on path that might be infeasible</li>
1059 </ul>
1060 </li>
1061 <li>Other:
1062 <ul>
1063 <li>Added -noClassOk command-line parameter to
1064 command-line and ant interfaces; when -noClassOk is specified
1065 and no classfiles are given, FindBugs will print a warning
1066 message and output a well- formed file with no warnings</li>
1067 <li>Fewer false positives for null pointer bugs</li>
1068 <li>Suppress dead-local-store false positives in .jsp code</li>
1069 <li>Type fixes in warning messages</li>
1070 <li>Better warning message for NP_NULL_ON_SOME_PATH</li>
1071 <li>"WMI" bug code description renamed from "Wrong Map
1072 Iterator" to "Inefficient Map Iterator"</li>
1073 </ul>
1074 </li>
1075 <li>Fixes:
1076 <ul>
1077 <li>[ 1893048 ] FindBugs confused by a findbugs.xml file</li>
1078 <li>[ 1878528 ] XSL xforms don't support history features</li>
1079 <li>[ 1876584 ] two default.xsl flaws</li>
1080 <li>[ 1874856 ] Format string bug detector doesn't handle
1081 special operators</li>
1082 <li>[ 1872645 ] computeBugHistory -
1083 java.lang.IllegalArgumentException</li>
1084 <li>[ 1872237 ] Ant task fails when no .class files</li>
1085 <li>[ 1868670 ] Filters: include AND exclude don't allowed</li>
1086 <li>[ 1868666 ] check-for-oddness reported, but array
1087 length can never be negative</li>
1088 <li>[ 1866108 ] SetBugDatabaseInfoTask strips dir from
1089 output filename</li>
1090 <li>[ 1866021 ] MineBugHistoryTask strips dir of output
1091 filename</li>
1092 <li>[ 1865265 ] code doesn't handle
1093 StringBuffer.append([CII) right</li>
1094 <li>[ 1864793 ] Warning when casting a null reference
1095 compared to a String</li>
1096 <li>[ 1863376 ] Typo in manual chap 8: Filter Files</li>
1097 <li>[ 1862705 ] Transient fields that default to null</li>
1098 <li>[ 1842545 ] DLS on catch variable (with priority
1099 tweaking)</li>
1100 <li>[ 1816258 ] false positive BC_IMPOSSIBLE_CAST</li>
1101 <li>[ 1551732 ] Get erroneous DLS with while loop</li>
1102 </ul>
1103 </li>
1104 </ul>
1105 </li>
1106 <li>FindBugs Eclipse plugin (change log by Andrey Loskutov)
1107 <ul>
1108 <li>new feature: added Bug explorer view (replacing Bug tree
1109 view), based on Common Navigator framework (Andrey Loskutov)</li>
1110 <li>bug 1873860 fixed: empty projects are no longer shown in
1111 Bug tree view (Andrey Loskutov)</li>
1112 <li>new feature: bug counts decorators for projects, folders
1113 and files (has to be activated via Preferences -&gt; general
1114 -&gt; appearance -&gt; label decorations)(Andrey Loskutov)</li>
1115 <li>patch 1746499: better icons (Alessandro Nistico)</li>
1116 <li>patch 1893685: Find bug actions on change sets bug
1117 (Alessandro Nistico)</li>
1118 <li>fixed bug 1855384: Bug configuration is broken in
1119 Eclipse (Andrey Loskutov)</li>
1120 <li>refactored FindBugs properties page (Andrey Loskutov)</li>
1121 <li>refactored FindBugs worker/builder/run action (Andrey
1122 Loskutov)</li>
1123 <li>FB detects now only bugs from classes on project's
1124 classpath (no double work on duplicated class files) (Andrey
1125 Loskutov)</li>
1126 <li>fixed bug introduced by the bad patch for 1867951: FB
1127 cannot be executed incrementally on a folder of file (Andrey
1128 Loskutov)</li>
1129 <li>fixed job rule: now jobs for different projects may run
1130 in parallel if running on a multi-core PC and
1131 "fb.allowParallelBuild" system property is set to true (Andrey
1132 Loskutov)</li>
1133 <li>fixed FB auto-build not started if .fbprefs or
1134 .classpath was changed (Andrey Loskutov)</li>
1135 <li>fixed not reporting bugs on secondary types (classes
1136 defined in java files with different name) (Andrey Loskutov)</li>
1137 </ul>
1138 </li>
1139 </ul>
1140
1141 <p>Changes since version 1.3.0</p>
1142 <ul>
1143 <li>New Reports
1144 <ul>
1145 <li>VA_FORMAT_STRING_ARG_MISMATCH: A format-string method
1146 with a variable number of arguments is called, but the number of
1147 arguments passed does not match with the number of %
1148 placeholders in the format string. This is probably not what the
1149 author intended.
1150 <li>IO_APPENDING_TO_OBJECT_OUTPUT_STREAM: This code opens a
1151 file in append mode and that wraps the result in an object
1152 output stream. This won't allow you to append to an existing
1153 object output stream stored in a file. If you want to be able to
1154 append to an object output stream, you need to keep the object
1155 output stream open. The only situation in which opening a file
1156 in append mode and the writing an object output stream could
1157 work is if on reading the file you plan to open it in random
1158 access mode and seek to the byte offset where the append
1159 started.
1160 <li>NP_BOOLEAN_RETURN_NULL: A method that returns either
1161 Boolean.TRUE, Boolean.FALSE or null is an accident waiting to
1162 happen. This method can be invoked as though it returned a value
1163 of type boolean, and the compiler will insert automatic unboxing
1164 of the Boolean value. If a null value is returned, this will
1165 result in a NullPointerException.
1166 </ul>
1167 </li>
1168 <li>Changes to Existing Reports
1169 <ul>
1170 <li>RV_DONT_JUST_NULL_CHECK_READLINE: CORRECTNESS -&gt;
1171 STYLE</li>
1172 <li>DMI_INVOKING_TOSTRING_ON_ARRAY: Long description
1173 mentions array name whenever possible</li>
1174 </ul>
1175 </li>
1176 <li>Fixes:
1177 <ul>
1178 <li>Updated manual to mention that Java 1.5 is now a
1179 requirement for running FindBugs
1180 <li>Applied patch 1840206 fixing issue "Ant task does not
1181 work when presetdef is used" - thanks to phejl
1182 <li>Applied patch 1778690 fixing issue "Ant task: tolerate
1183 but complain about invalid auxClasspath" - thanks to David
1184 Schmidt
1185 <li>Applied patch 1852125 adding a Chinese-language GUI
1186 bundle props file - thanks to fifi
1187 <li>Applied patch 1845903 adding ability to load XML results
1188 with the Eclipse plugin - thanks to Alex Mont
1189 <li>Fixed issue 1844671 - "FP for "reversed" null check in
1190 catch for stream close"
1191 <li>Fixed issue 1836050 - "-onlyAnalyze broken"
1192 <li>Fixed issue 1853011 - "Typo: Field names should start
1193 with aN lower case letter"
1194 <li>Fixed issue 1844181 - "JNLP file does not contain all
1195 necessary JARs"
1196 <li>Fixed issue 1840245 - "xxxException class does not
1197 derive from Exception"
1198 <li>Fixed issue 1840277 - "[M D EC] Typo in bug
1199 documentation"
1200 <li>Fixed issue 1782447 - "OutOfMemoryError if i activate
1201 Findbugs on my project"
1202 <li>Fixed issue 1830576 - "[regression] keySet/entrySet
1203 false positive"
1204 </ul>
1205 </li>
1206 <li>Other:
1207 <ul>
1208 <li>New bug code: "IO" (for
1209 IO_APPENDING_TO_OBJECT_OUTPUT_STREAM)</li>
1210 <li>Added "-onlyMostRecent" option for computeBugHistory
1211 script/ant task
1212 <li>More explicit language in
1213 RV_RETURN_VALUE_IGNORED_BAD_PRACTICE messages
1214 <li>Modified ResourceValueAnalysis to correctly identify
1215 null == X or null != X as a null check (for issue 1844671)
1216 <li>Modified DMI_HARDCODED_ABSOLUTE_FILENAME logic in
1217 DumbMethodInvocations to ignore files from /etc or /dev and
1218 increase priority of files from /home
1219 <li>Better bug details for infinite loop warnings
1220 <li>Modified unread-fields detector to reduce false
1221 positives from reflective fields
1222 <li>build.xml "classes" target now builds all sources in one
1223 step
1224 </ul>
1225 </li>
1226 </ul>
1227
1228 <p>Changes since version 1.2.1</p>
1229 <ul>
1230 <li>New Detectors and Reports
1231 <ul>
1232 <li>SynchronizationOnSharedBuiltinConstant
1233 <ul>
1234 <li>DL_SYNCHRONIZATION_ON_SHARED_CONSTANT: The code
1235 synchronizes on a shared primitive constant, such as an
1236 interned String. Such constants are interned and shared across
1237 all other classes loaded by the JVM. Thus, this could be
1238 locking on something that other code might also be locking.
1239 This could result in very strange and hard to diagnose
1240 blocking and deadlock behavior. See <a
1241 href="http://www.javalobby.org/java/forums/t96352.html">http://www.javalobby.org/java/forums/t96352.html</a>
1242 and <a href="http://jira.codehaus.org/browse/JETTY-352">http://jira.codehaus.org/browse/JETTY-352</a>.
1243
1244 </ul>
1245 </li>
1246 <li>OverridingEqualsNotSymmetrical
1247 <ul>
1248 <li>EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC: Looks for equals
1249 methods that override equals methods in a superclass where the
1250 equivalence relationship might not be symmetrical.
1251 </ul>
1252 </li>
1253 <li>CheckTypeQualifiers
1254 <ul>
1255 <li>TQ_ALWAYS_VALUE_USED_WHERE_NEVER_REQUIRED: A value
1256 specified as carrying a type qualifier annotation is consumed
1257 in a location or locations requiring that the value not carry
1258 that annotation. More precisely, a value annotated with a type
1259 qualifier specifying when=ALWAYS is guaranteed to reach a use
1260 or uses where the same type qualifier specifies when=NEVER.</li>
1261 <li>TQ_NEVER_VALUE_USED_WHERE_ALWAYS_REQUIRED: A value
1262 specified as not carrying a type qualifier annotation is
1263 guaranteed to be consumed in a location or locations requiring
1264 that the value does carry that annotation. More precisely, a
1265 value annotated with a type qualifier specifying when=NEVER is
1266 guaranteed to reach a use or uses where the same type
1267 qualifier specifies when=ALWAYS.</li>
1268 <li>TQ_MAYBE_SOURCE_VALUE_REACHES_ALWAYS_SINK: A value
1269 that might not carry a type qualifier annotation reaches a use
1270 which requires that annotation.</li>
1271 <li>TQ_MAYBE_SOURCE_VALUE_REACHES_NEVER_SINK: A value
1272 which might carry a type qualifier annotation reaches a use
1273 which forbids values carrying that annotation.</li>
1274 </ul>
1275 </li>
1276 </ul>
1277 </li>
1278 <li>New Reports (existing detectors)
1279 <ul>
1280 <li>FindHEmismatch
1281 <ul>
1282 <li>EQ_DOESNT_OVERRIDE_EQUALS: This class extends a class
1283 that defines an equals method and adds fields, but doesn't
1284 define an equals method itself. Thus, equality on instances of
1285 this class will ignore the identity of the subclass and the
1286 added fields. Be sure this is what is intended, and that you
1287 don't need to override the equals method. Even if you don't
1288 need to override the equals method, consider overriding it
1289 anyway to document the fact that the equals method for the
1290 subclass just return the result of invoking super.equals(o).</li>
1291 </ul>
1292 </li>
1293 <li>Naming
1294 <ul>
1295 <li>NM_WRONG_PACKAGE, NM_WRONG_PACKAGE_INTENTIONAL: The
1296 method in the subclass doesn't override a similar method in a
1297 superclass because the type of a parameter doesn't exactly
1298 match the type of the corresponding parameter in the
1299 superclass.</li>
1300 <li>NM_SAME_SIMPLE_NAME_AS_SUPERCLASS: This class has a
1301 simple name that is identical to that of its superclass,
1302 except that its superclass is in a different package (e.g., <code>alpha.Foo</code>
1303 extends <code>beta.Foo</code>). This can be exceptionally
1304 confusing, create lots of situations in which you have to look
1305 at import statements to resolve references and creates many
1306 opportunities to accidently define methods that do not
1307 override methods in their superclasses.
1308 </li>
1309 <li>NM_SAME_SIMPLE_NAME_AS_INTERFACE: This class/interface
1310 has a simple name that is identical to that of an
1311 implemented/extended interface, except that the interface is
1312 in a different package (e.g., <code>alpha.Foo</code> extends <code>beta.Foo</code>).
1313 This can be exceptionally confusing, create lots of situations
1314 in which you have to look at import statements to resolve
1315 references and creates many opportunities to accidently define
1316 methods that do not override methods in their superclasses.
1317 </li>
1318 </ul>
1319 <li>FindRefComparison
1320 <ul>
1321 <li>EC_UNRELATED_TYPES_USING_POINTER_EQUALITY: This method
1322 uses using pointer equality to compare two references that
1323 seem to be of different types. The result of this comparison
1324 will always be false at runtime.</li>
1325 </ul>
1326 </li>
1327 <li>IncompatMask
1328 <ul>
1329 <li>BIT_SIGNED_CHECK, BIT_SIGNED_CHECK_HIGH_BIT: This
1330 method compares an expression such as <tt>((event.detail
1331 &amp; SWT.SELECTED) &gt; 0)</tt>. Using bit arithmetic and then
1332 comparing with the greater than operator can lead to
1333 unexpected results (of course depending on the value of
1334 SWT.SELECTED). If SWT.SELECTED is a negative number, this is a
1335 candidate for a bug. Even when SWT.SELECTED is not negative,
1336 it seems good practice to use '!= 0' instead of '&gt; 0'.
1337 </li>
1338 </ul>
1339 </li>
1340 <li>LazyInit
1341 <ul>
1342 <li>LI_LAZY_INIT_UPDATE_STATIC: This method contains an
1343 unsynchronized lazy initialization of a static field. After
1344 the field is set, the object stored into that location is
1345 further accessed. The setting of the field is visible to other
1346 threads as soon as it is set. If the further accesses in the
1347 method that set the field serve to initialize the object, then
1348 you have a <em>very serious</em> multithreading bug, unless
1349 something else prevents any other thread from accessing the
1350 stored object until it is fully initialized.
1351 </li>
1352 </ul>
1353 </li>
1354 <li>FindDeadLocalStores
1355 <ul>
1356 <li>DLS_DEAD_STORE_OF_CLASS_LITERAL: This instruction
1357 assigns a class literal to a variable and then never uses it.
1358 <a href="//java.sun.com/j2se/1.5.0/compatibility.html#literal">The
1359 behavior of this differs in Java 1.4 and in Java 5.</a> In Java
1360 1.4 and earlier, a reference to <code>Foo.class</code> would
1361 force the static initializer for <code>Foo</code> to be
1362 executed, if it has not been executed already. In Java 5 and
1363 later, it does not. See Sun's <a
1364 href="//java.sun.com/j2se/1.5.0/compatibility.html#literal">article
1365 on Java SE compatibility</a> for more details and examples, and
1366 suggestions on how to force class initialization in Java 5.
1367 </li>
1368 </ul>
1369 </li>
1370 <li>MethodReturnCheck
1371 <ul>
1372 <li>RV_RETURN_VALUE_IGNORED_BAD_PRACTICE: This method
1373 returns a value that is not checked. The return value should
1374 be checked since it can indication an unusual or unexpected
1375 function execution. For example, the <code>File.delete()</code>
1376 method returns false if the file could not be successfully
1377 deleted (rather than throwing an Exception). If you don't
1378 check the result, you won't notice if the method invocation
1379 signals unexpected behavior by returning an atypical return
1380 value.
1381 </li>
1382 <li>RV_EXCEPTION_NOT_THROWN: This code creates an
1383 exception (or error) object, but doesn't do anything with it.
1384 </li>
1385 </ul>
1386 </li>
1387 </ul>
1388 </li>
1389 <li>Changes to Existing Reports
1390 <ul>
1391 <li>NS_NON_SHORT_CIRCUIT: BAD_PRACTICE -&gt; STYLE</li>
1392 <li>NS_DANGEROUS_NON_SHORT_CIRCUIT: CORRECTNESS -&gt; STYLE</li>
1393 <li>RC_REF_COMPARISON: CORRECTNESS -&gt; BAD_PRACTICE</li>
1394 </ul>
1395 </li>
1396 <li>GUI Changes
1397 <ul>
1398 <li>Added importing and exporting of bug filters</li>
1399 <li>Better handling of failed analysis runs</li>
1400 <li>Added "-look" parameter for selecting look-and-feel</li>
1401 <li>Fixed incorrect package filtering</li>
1402 <li>Fixed issue where "synchronized" was not
1403 syntax-highlighted</li>
1404 </ul>
1405 </li>
1406 <li>Ant-task Changes
1407 <ul>
1408 <li>Refactored common ant-task code to AbstractFindBugsTask</li>
1409 <li>Added tasks for computeBugHistory, convertXmlToText,
1410 filterBugs, mineBugHistory, setBugDatabaseInfo</li>
1411 </ul>
1412 </li>
1413 <li>Manual
1414 <ul>
1415 <li>Updates to GUI section, including new screenshots</li>
1416 <li>Added description of rejarForAnalysis</li>
1417 <li>Revamp of data-mining section</li>
1418 </ul>
1419 </li>
1420 <li>Other Major
1421 <ul>
1422 <li>Internal restructuring for lower memory overhead</li>
1423 </ul>
1424 </li>
1425 <li>Other Minor
1426 <ul>
1427 <li>Fixed typo: was STCAL_STATIC_SIMPLE_DATA_FORMAT_INSTANCE
1428 now STCAL_STATIC_SIMPLE_DATE_FORMAT_INSTANCE</li>
1429 <li>-outputFile parameter became -output</li>
1430 <li>More sensitivity and specificity inLazyInit detector</li>
1431 <li>More sensitivity and specificity in Naming detector</li>
1432 <li>More sensitivity and specificity in UnreadFields
1433 detector</li>
1434 <li>More sensitivity in FindNullDeref detector</li>
1435 <li>More sensitivity in FindBadCast2 detector</li>
1436 <li>More specificity in FindReturnRef detector</li>
1437 <li>Many other tweaks and bug fixes</li>
1438 </ul>
1439 </li>
1440 </ul>
1441
1442 <p>Changes since version 1.2.0</p>
1443 <ul>
1444 <li>Bug fixes:
1445 <ul>
1446 <li><a
1447 href="http://fisheye2.cenqua.com/changelog/findbugs/?cs=8219">Fix</a>
1448 <a
1449 href="http://sourceforge.net/tracker/index.php?func=detail&aid=1726946&group_id=96405&atid=614693">bug</a>
1450 with detectors that were requested to be disabled but were
1451 enabled due to requirements of other detectors.</li>
1452 <li>Fix bugs in incremental analysis within Eclipse plugin</li>
1453 <li>Fix some analysis errors</li>
1454 <li>Fix some threading bugs in GUI2</li>
1455 <li>Report version as version when it was compiled, not when
1456 it was run</li>
1457 <li>Copy analysis time stamp when filtering or transforming
1458 analysis files.</li>
1459 </ul>
1460 <li>Enabled StaticCalendarDetector</li>
1461 <li>Reworked GUI2 to use standard FindBugs filters
1462 <ul>
1463 <li>Allow a suppression filter to be stored in a project and
1464 persisted to the XML representation of a project.</li>
1465 </ul>
1466 </li>
1467
1468 <li>Move away from old GUI2 save format (a directory
1469 containing an xml file and another file containing serialized
1470 filters).</li>
1471 <li>Support/recommend use of two new file extensions/formats:
1472 <dl>
1473 <dt>.fba - FindBugs Analysis File</dt>
1474 <dd>Exactly the same as an existing bug collection file
1475 stored in XML format, but using a distinct file extension to
1476 make it easier to figure out which xml files contain FindBugs
1477 results.</dd>
1478 <dt>.fbp - FindBugs Project File</dt>
1479 <dd>Contains just the information needed to run FindBugs and
1480 display the results (e.g., the files to be analyzed, the
1481 auxiliary class path and the location of source files)
1482 </dl>
1483 </li>
1484 </ul>
1485 <p>Changes since version 1.1.3</p>
1486 <ul>
1487 <li>Added -xml:withAbridgedMessages option to generate xml
1488 containing shorter messages. The messages will be shorted by doing
1489 things like eliding package names, and leaving off the source line
1490 from the LongMessage. These messages are appropriate if being used
1491 in a context where the non-message components of the bug
1492 annotations will be used to provide more information (e.g.,
1493 clicking on the message for a MethodAnnotation will display the
1494 source for the method).
1495 <ul>
1496 <li>FindBugsDisplayFeatures.setAbridgedMessages(true) can be
1497 used to generate abridged messages when FindBugs is being
1498 accessed directly (not via generated XML) from a GUI or IDE.</li>
1499 </ul>
1500 <li>In null pointer analysis, try to be better about always
1501 showing two locations: where it is known null and where it is
1502 dereferenced.
1503 <li>Interprocedural analysis of which methods return nonnull
1504 values
1505 <li>Use method calls to select order in which classes are
1506 analyzed, and order in which methods are analyzed, to improve
1507 interprocedural analysis results.
1508 <li>Significant improvements in memory footprint, memory
1509 allocation and CPU utilization (20-30% reduction in all three)
1510 <li>Added a project name, to provide better descriptions in
1511 the HTML output.
1512 <li>Added new bug pattern: Casting to char, or bit masking
1513 with nonnegative value, and then checking to see if the result is
1514 negative.
1515 <li>Stopped reporting transient fields of classes not marked
1516 as serializable. Transient is used by other persistence
1517 frameworks.
1518 <li>Improvements to detector for SQL injection (Thanks to <a
1519 href="http://www.clock.org/~matt">Matt Hargett</a> for his
1520 contributions
1521 <li>Changed open/save options in GUI2 to not distinguish
1522 between FindBugs projects and saved FindBugs analysis results.
1523 <li>Improvements to detection of serious non-short-circuit
1524 evaluation.
1525 <li>Updated Japanese localization (thanks to Ruimo Uno)
1526 <li>Eclipse plugin changes:
1527 <ul>
1528 <li>Created Bug User Annotations and Bug Tree Views
1529 <li>Use different icons for different bug priorities
1530 <li>Provide more information in Bug Details view
1531 </ul>
1532 </ul>
1533
1534 <p>Changes since version 1.1.2:</p>
1535 <ul>
1536 <li>Fixed broken Ant task
1537 <li>Added running ant task to smoke test
1538 <li>Added validating xml and html output to smoke test
1539 <li>Fixed some (but not all) issues with html output
1540 validation
1541 <li>Added check for x.equals(x) and x.compareTo(x)
1542 <li>Various bug fixes
1543 </ul>
1544 <p>Changes since version 1.1.1:</p>
1545 <ul>
1546 <li>Added check for infinite iterative loops</li>
1547 <li>Added check for use of incompatible types in a collection
1548 (e.g., checking to see if a Set&lt;String&gt; contains a
1549 StringBuffer).</li>
1550 <li>Added check for invocations of equals or hashCode on a
1551 URL, which, <a
1552 href="http://michaelscharf.blogspot.com/2006/11/javaneturlequals-and-hashcode-make.html">surprising
1553 many people</a>, requires DNS resolution.
1554 </li>
1555 <li>Added check for classes that define compareTo but not
1556 equals; such classes can exhibit some anomalous behavior (e.g.,
1557 they are treated differently by PriorityQueues in Java 5 and Java
1558 6).</li>
1559 <li>Added a check for useless self operations (e.g., x &lt; x
1560 or x ^ x).</li>
1561 <li>Fixed a data race that could cause the GUI to fail on
1562 startup</li>
1563 <li>Partial internationalization of the new GUI</li>
1564 <li>Fix bug in "Redo analysis" option of new GUI</li>
1565 <li>Tuning to reduce false positives</li>
1566 <li>Fixed a bug in null pointer analysis that was generating
1567 false positive null pointer warnings on exception paths. Fixing
1568 this bug eliminates about 1/4 of the warnings on null pointer
1569 exceptions on exception paths.</li>
1570 <li>Fixed a bug in the processing of phi nodes for fields in
1571 the null pointer analysis</li>
1572 <li>Applied contributed patch that provides more quick fixes
1573 in Eclipse plugin.</li>
1574 <li>Fixed a number of bugs in the Eclipse auto update sites,
1575 and in the way date qualifiers were being used in the Eclipse
1576 plugin. You may need to manually disable your existing version of
1577 the plugin and download the 1.1.2 from the update site to get the
1578 automatic update function working correctly. The Eclipse update
1579 sites are described at <a
1580 href="http://findbugs.cs.umd.edu/eclipse/">http://findbugs.cs.umd.edu/eclipse/</a>.
1581
1582 </li>
1583 <li>Fixed progress bar in Eclipse plugin</li>
1584 <li>A number of other bug fixes.</li>
1585 </ul>
1586
1587 <p>Changes since version 1.1.0:</p>
1588 <ul>
1589 <li>less scanning of classes not on the analysis path (This
1590 was causing some performance problems.)</li>
1591 <li>no unread field warnings for fields annotated with
1592 javax.persistent or javax.ejb3</li>
1593 <li>Eclipse plugin
1594 <ul>
1595 <li>bug annotation info displayed in Bug Details tab</li>
1596 <li>.fbwarnings data file now stored in .metadata (not in
1597 the project itself)</li>
1598 </ul>
1599 </li>
1600 <li>new SE_BAD_FIELD_INNER_CLASS pattern</li>
1601 <li>updates to Japanese translation (ruimo)</li>
1602 <li>fix some internal slashed/dotted path confusion</li>
1603 <li>other minor improvements</li>
1604 </ul>
1605
1606 <p>Changes since version 1.0.0:</p>
1607
1608 <ul>
1609 <li>Overall, the change from FindBugs 1.0.0 to FindBugs 1.1.0
1610 has been a big change. We've done a lot of work in a lot of areas,
1611 and aren't even going to try to enumerate all the changes.</li>
1612 <li>We spent a lot of time reviewing the results generated by
1613 FindBugs for open source and commercial code bases, and made a
1614 number of changes, small and large, to minimize the number of
1615 false positives. Our primary focus for this was warnings reported
1616 as high and medium priority correctness warnings. Our internal
1617 evaluation is that we produce very few high/medium priority
1618 correctness warnings where the analysis is actually wrong, and
1619 that more than 75% of the high/medium priority correctness
1620 warnings correspond to real coding defects that need addressing in
1621 the source code. The remaining 25% are largely cases such as a
1622 branch or statement that if taken would lead to an error, but in
1623 fact is a dead branch or statement that can never be taken. Such
1624 coding is confusing and hard to maintain, so it should arguably be
1625 fixed, but it is unlikely to actually result in an error during
1626 execution. Thus, some might classify those warnings as false
1627 positives.</li>
1628 <li>We've substantially improved the analysis for errors that
1629 could result in null pointer dereferences. Overall, our experience
1630 has been that these changes have roughly doubled the number of
1631 null pointer errors we detect, without increasing the number of
1632 false positives (in fact, our false positive rate has gone down).
1633 The improvements are due to four factors:
1634 <ul>
1635 <li>By default, we now do some interprocedural analysis to
1636 determine methods that unconditionally dereference their
1637 parameters.</li>
1638 <li>FindBugs also comes with a model of which JDK methods
1639 unconditionally dereference their parameters.</li>
1640 <li>We do limited tracking of fields, so that we can detect
1641 null values stored in fields that lead to exceptions.</li>
1642 <li>We implemented a new analysis technique to find
1643 guaranteed dereferences. Consider the following example: <pre>public int f(Object x, boolean b) {
14411644 int result = 0;
14421645 if (x == null) result++;
14431646 else result--;
14541657 }
14551658 </pre>
14561659
1457 <p>
1458 FindBugs 1.0 used forward dataflow analysis to determine
1459 whether each value is definitely null, null on a simple path,
1460 possible null on a complex path, or definitely nonnull. Thus,
1461 at the statement where
1462 <code> result </code>
1463 is decremented, we know that
1464 <code> x </code>
1465 is definitely null, and at the point before
1466 <code> if (b) </code>
1467 , we know that
1468 <code> x </code>
1469 is null on a simple path. If
1470 <code> x </code>
1471 were to be dereferenced here, we would generate a warning,
1472 because if the else branch of the
1473 <code> if (x == null) </code>
1474 were ever taken, a null pointer exception would result.
1475 </p>
1476
1477 <p>
1478 However, in both the then and else branches of the
1479 <code> if (b) </code>
1480 statement,
1481 <code> x </code>
1482 is only null on a complex path that may be infeasible. It might
1483 be that the program logic is such that if
1484 <code> x </code>
1485 is null, then
1486 <code> b </code>
1487 is never true, so generating a warning about the dereference in
1488 the then clause might be a false positive. We could try to
1489 analyze the program to determine whether it is possible for
1490 <code> x </code>
1491 to be null and
1492 <code> b </code>
1493 to be true, but that can be a hard analysis problem.
1494 </p>
1495
1496 <p>
1497 However,
1498 <code> x </code>
1499 is dereferenced in both the then <em>and</em> else branches of
1500 the
1501 <code> if (b) </code>
1502 statement. So at the point immediately before
1503 <code> if (b) </code>
1504 , we know that
1505 <code> x </code>
1506 is null on a simple path <em>and</em> that
1507 <code> x </code>
1508 is guaranteed to be dereferenced on all paths from this point
1509 forward. FindBugs 1.1 performs a backwards data flow analysis
1510 to determine the values that are guaranteed to be dereferenced,
1511 and will generate a warning in this case.
1512 </p>
1513 </li>
1514 </ul>
1515 <p>
1516 The following screen shot of our new GUI shows an example of this
1517 analysis, as well as showing off our new GUI and points out a
1518 limitation of our current plugins for Eclipse and NetBeans. The
1519 screen shot shows a null pointer bug in HelpDisplay.java. The
1520 test for
1521 <code> href!=null </code>
1522 on line 78 suggests that
1523 <code> href </code>
1524 could be null. If it is, then
1525 <code> href </code>
1526 will be dereferenced on either line 87 or on line 90, generating
1527 a NPE. Note that our analysis here also understands that passing
1528 <code> href </code>
1529 to
1530 <code> URLEncoder.encode </code>
1531 will deference it, and thus treats line 87 as a dereference, even
1532 though
1533 <code> href </code>
1534 is not actually dereferenced at that line. Within our new GUI,
1535 all of these locations are highlighted and listed in the summary
1536 panel. In the original GUI (and in HTML output) we list all of
1537 the locations, but only the primary location is highlighted by
1538 the original GUI. In the Eclipse and NetBeans plugins, only the
1539 primary location is displayed; fixing this is on our todo list
1540 (contributions welcome).
1541 </p>
1542 <p>
1543 <img src="guaranteedDereference.png" alt="">
1544
1545
1546 </p>
1547
1548 </li>
1549 <li>Preliminary support for detectors using the frameworks
1550 other than BCEL, such as the <a href="http://asm.objectweb.org/">ASM</a>
1551 bytecode framework. You may experiment with writing ASM-based
1552 detectors, but beware the API may still change (which could
1553 possibly also affect BCEL-based detectors). In general, we've
1554 started trying to move away from a deep dependence on BCEL, but
1555 that change is only partially complete. Probably best to just
1556 avoid this until we complete more work on this. This change is
1557 only visible to FindBugs plugin developers, and shouldn't be
1558 visible to FindBugs users.
1559 </li>
1560 <li>
1561 <p>Bug categories (CORRECTNESS, MT_CORRECTNESS, etc.) are no
1562 longer hard-coded, but rather defined in xml files associated
1563 with plugins, including the core plugin which defines the
1564 standard categories. Third-party plugins can define their own
1565 categories.</p>
1566 </li>
1567 <li>
1568 <p>Several bug patterns have been moved from CORRECTNESS and
1569 STYLE into a new category, BAD_PRACTICE. The English localization
1570 of STYLE has changed from "Style" to "Dodgy."</p>
1571 <p>In general, we've worked very hard to limit CORRECTNESS
1572 bugs to be real programming errors and sins of commission. We
1573 have reclassified as BAD_PRACTICE a number of bad design
1574 practices that result in overly fragile code, such as defining an
1575 equals method that doesn't accept null or defining class with a
1576 equals method that inherits hashCode from class Object.</p>
1577 <p>In general, our guidelines for deciding whether a bug
1578 should be classified as CORRECTNESS, BAD_PRACTICE or STYLE are:</p>
1579 <dl>
1580 <dt>CORRECTNESS</dt>
1581 <dd>A problem that we can recognize with high confidence and
1582 is an issue that we believe almost all developers would want to
1583 examine and address. We recommend that software teams review all
1584 high and medium priority warnings in their entire code base.</dd>
1585 <dt>BAD_PRACTICE</dt>
1586 <dd>A problem that we can recognize with high confidence and
1587 represents a clear violation of recommended and standard coding
1588 practice. We believe each software team should decide which bad
1589 practices identified by FindBugs it wants to prohibit in the
1590 team's coding standard, and take action to remedy violations of
1591 those coding standards.</dd>
1592 <dt>STYLE</dt>
1593 <dd>These are places where something strange or dodgy is
1594 going on, such as a dead store to a local variable. Typically,
1595 less than half of these represent actionable programming
1596 defects. Reviewing these warnings in any code under active
1597 development is probably a good idea, but reviewing all such
1598 warnings in your entire code base might be appropriate only in
1599 some situations. Individual or team programming styles can
1600 substantially influence the effectiveness of each of these
1601 warnings (e.g., you might have a coding practice or style in
1602 your group that confuses one of the detectors into generating a
1603 lot of STYLE warnings); you will likely want to selectively
1604 suppress or report the STYLE warnings that are effective for
1605 your group.</dd>
1606 </dl>
1607 </li>
1608 <li>Released a preliminary version of a new GUI (known
1609 internally as GUI2 -- not very creative, huh?)</li>
1610 <li>Provided standard ways to mark user designations of bug
1611 warnings (e.g., as NOT_A_BUG or SHOULD_FIX). The internal logic
1612 now records this, it is represented in the XML file, and GUI2
1613 allows the designations to be applied (along with free-form user
1614 annotations about each warning). The user designations and
1615 annotations are not yet supported by the Eclipse plugin, but we
1616 clearly want to support it in Eclipse shortly.</li>
1617 <li>Added a check for a bad comparison with a signed byte with
1618 a value not in the range -128..127. For example: <pre>boolean find200(byte b[]) {
1660 <p>
1661 FindBugs 1.0 used forward dataflow analysis to determine
1662 whether each value is definitely null, null on a simple path,
1663 possible null on a complex path, or definitely nonnull. Thus,
1664 at the statement where
1665 <code> result </code>
1666 is decremented, we know that
1667 <code> x </code>
1668 is definitely null, and at the point before
1669 <code> if (b) </code>
1670 , we know that
1671 <code> x </code>
1672 is null on a simple path. If
1673 <code> x </code>
1674 were to be dereferenced here, we would generate a warning,
1675 because if the else branch of the
1676 <code> if (x == null) </code>
1677 were ever taken, a null pointer exception would result.
1678 </p>
1679
1680 <p>
1681 However, in both the then and else branches of the
1682 <code> if (b) </code>
1683 statement,
1684 <code> x </code>
1685 is only null on a complex path that may be infeasible. It might
1686 be that the program logic is such that if
1687 <code> x </code>
1688 is null, then
1689 <code> b </code>
1690 is never true, so generating a warning about the dereference in
1691 the then clause might be a false positive. We could try to
1692 analyze the program to determine whether it is possible for
1693 <code> x </code>
1694 to be null and
1695 <code> b </code>
1696 to be true, but that can be a hard analysis problem.
1697 </p>
1698
1699 <p>
1700 However,
1701 <code> x </code>
1702 is dereferenced in both the then <em>and</em> else branches of
1703 the
1704 <code> if (b) </code>
1705 statement. So at the point immediately before
1706 <code> if (b) </code>
1707 , we know that
1708 <code> x </code>
1709 is null on a simple path <em>and</em> that
1710 <code> x </code>
1711 is guaranteed to be dereferenced on all paths from this point
1712 forward. FindBugs 1.1 performs a backwards data flow analysis
1713 to determine the values that are guaranteed to be dereferenced,
1714 and will generate a warning in this case.
1715 </p>
1716 </li>
1717 </ul>
1718 <p>
1719 The following screen shot of our new GUI shows an example of this
1720 analysis, as well as showing off our new GUI and points out a
1721 limitation of our current plugins for Eclipse and NetBeans. The
1722 screen shot shows a null pointer bug in HelpDisplay.java. The
1723 test for
1724 <code> href!=null </code>
1725 on line 78 suggests that
1726 <code> href </code>
1727 could be null. If it is, then
1728 <code> href </code>
1729 will be dereferenced on either line 87 or on line 90, generating
1730 a NPE. Note that our analysis here also understands that passing
1731 <code> href </code>
1732 to
1733 <code> URLEncoder.encode </code>
1734 will deference it, and thus treats line 87 as a dereference, even
1735 though
1736 <code> href </code>
1737 is not actually dereferenced at that line. Within our new GUI,
1738 all of these locations are highlighted and listed in the summary
1739 panel. In the original GUI (and in HTML output) we list all of
1740 the locations, but only the primary location is highlighted by
1741 the original GUI. In the Eclipse and NetBeans plugins, only the
1742 primary location is displayed; fixing this is on our todo list
1743 (contributions welcome).
1744 </p>
1745 <p>
1746 <img src="guaranteedDereference.png" alt="">
1747
1748
1749 </p>
1750
1751 </li>
1752 <li>Preliminary support for detectors using the frameworks
1753 other than BCEL, such as the <a href="http://asm.objectweb.org/">ASM</a>
1754 bytecode framework. You may experiment with writing ASM-based
1755 detectors, but beware the API may still change (which could
1756 possibly also affect BCEL-based detectors). In general, we've
1757 started trying to move away from a deep dependence on BCEL, but
1758 that change is only partially complete. Probably best to just
1759 avoid this until we complete more work on this. This change is
1760 only visible to FindBugs plugin developers, and shouldn't be
1761 visible to FindBugs users.
1762 </li>
1763 <li>
1764 <p>Bug categories (CORRECTNESS, MT_CORRECTNESS, etc.) are no
1765 longer hard-coded, but rather defined in xml files associated
1766 with plugins, including the core plugin which defines the
1767 standard categories. Third-party plugins can define their own
1768 categories.</p>
1769 </li>
1770 <li>
1771 <p>Several bug patterns have been moved from CORRECTNESS and
1772 STYLE into a new category, BAD_PRACTICE. The English localization
1773 of STYLE has changed from "Style" to "Dodgy."</p>
1774 <p>In general, we've worked very hard to limit CORRECTNESS
1775 bugs to be real programming errors and sins of commission. We
1776 have reclassified as BAD_PRACTICE a number of bad design
1777 practices that result in overly fragile code, such as defining an
1778 equals method that doesn't accept null or defining class with a
1779 equals method that inherits hashCode from class Object.</p>
1780 <p>In general, our guidelines for deciding whether a bug
1781 should be classified as CORRECTNESS, BAD_PRACTICE or STYLE are:</p>
1782 <dl>
1783 <dt>CORRECTNESS</dt>
1784 <dd>A problem that we can recognize with high confidence and
1785 is an issue that we believe almost all developers would want to
1786 examine and address. We recommend that software teams review all
1787 high and medium priority warnings in their entire code base.</dd>
1788 <dt>BAD_PRACTICE</dt>
1789 <dd>A problem that we can recognize with high confidence and
1790 represents a clear violation of recommended and standard coding
1791 practice. We believe each software team should decide which bad
1792 practices identified by FindBugs it wants to prohibit in the
1793 team's coding standard, and take action to remedy violations of
1794 those coding standards.</dd>
1795 <dt>STYLE</dt>
1796 <dd>These are places where something strange or dodgy is
1797 going on, such as a dead store to a local variable. Typically,
1798 less than half of these represent actionable programming
1799 defects. Reviewing these warnings in any code under active
1800 development is probably a good idea, but reviewing all such
1801 warnings in your entire code base might be appropriate only in
1802 some situations. Individual or team programming styles can
1803 substantially influence the effectiveness of each of these
1804 warnings (e.g., you might have a coding practice or style in
1805 your group that confuses one of the detectors into generating a
1806 lot of STYLE warnings); you will likely want to selectively
1807 suppress or report the STYLE warnings that are effective for
1808 your group.</dd>
1809 </dl>
1810 </li>
1811 <li>Released a preliminary version of a new GUI (known
1812 internally as GUI2 -- not very creative, huh?)</li>
1813 <li>Provided standard ways to mark user designations of bug
1814 warnings (e.g., as NOT_A_BUG or SHOULD_FIX). The internal logic
1815 now records this, it is represented in the XML file, and GUI2
1816 allows the designations to be applied (along with free-form user
1817 annotations about each warning). The user designations and
1818 annotations are not yet supported by the Eclipse plugin, but we
1819 clearly want to support it in Eclipse shortly.</li>
1820 <li>Added a check for a bad comparison with a signed byte with
1821 a value not in the range -128..127. For example: <pre>boolean find200(byte b[]) {
16191822 for(int i = 0; i &lt; b.length; i++) if (b[i] == 200) return i;
16201823 return -1;
16211824 }
16221825 </pre>
1623 </li>
1624 <li>Added a checking for testing if a value is equal to
1625 Double.NaN (no value is equal to NaN, not even NaN).</li>
1626 <li>Added a check for using a class with an equals method but
1627 no hashCode method in a hashed data structure.</li>
1628 <li>Added check for uncallable method of an anonymous inner
1629 class. For example, in the following code, it is impossible to
1630 invoke the initalValue method (because the name is misspelled and
1631 as a result is doesn't override a method in ThreadLocal). <pre>private static ThreadLocal serialNum = new ThreadLocal() {
1826 </li>
1827 <li>Added a checking for testing if a value is equal to
1828 Double.NaN (no value is equal to NaN, not even NaN).</li>
1829 <li>Added a check for using a class with an equals method but
1830 no hashCode method in a hashed data structure.</li>
1831 <li>Added check for uncallable method of an anonymous inner
1832 class. For example, in the following code, it is impossible to
1833 invoke the initalValue method (because the name is misspelled and
1834 as a result is doesn't override a method in ThreadLocal). <pre>private static ThreadLocal serialNum = new ThreadLocal() {
16321835 protected synchronized Object initalValue() {
16331836 return new Integer(nextSerialNum++);
16341837 }
16351838 };
16361839 </pre>
1637 </li>
1638 <li>Added check for a dead local store caused by a switch
1639 statement fall through</li>
1640 <li>Added check for computing the absolute value of a random
1641 32 bit integer or of a hashcode. This is broken because <code>
1642 Math.abs(Integer.MIN_VALUE) == Integer.MIN_VALUE </code> , and thus
1643 result of calling Math.abs, which is expected to be nonnegative,
1644 will in fact be negative one time out of 2 <sup> 32 </sup> , which
1645 will invariably be the time your boss is demoing the software to
1646 your customers.
1647
1648 </li>
1649 <li>More careful resolution of inherited methods and fields.
1650 Some of the shortcuts we were taking in FindBugs 1.0.0 were
1651 leading to inaccurate results, and it was fairly easy to address
1652 this by making the analysis more accurate.</li>
1653 <li>Overall, analysis times are about 1.6 times longer in
1654 FindBugs 1.1.0 than in FindBugs 1.0.0. This is because we have
1655 enabled substantial additional analysis at the default effort
1656 level (the actual analysis engine is significantly faster than in
1657 FindBugs 1.0). On a recent AMD Athlon processor, analyzing
1658 JDK1.6.0 (about 1 million lines of code) requires about 15 minutes
1659 of wall clock time.</li>
1660 <li>Provided class and script (printClass) to print classfile
1661 in the human readable format produced by BCEL</li>
1662 <li>Provided -findSource option to setBugDatabaseInfo</li>
1663 </ul>
1664
1665
1666 <p>Changes since version 0.9.7:</p>
1667
1668 <ul>
1669 <li>fix ObjectTypeFactory bug that was suppressing some bugs</li>
1670 <li>opcode stack may determine definite zeros on some paths</li>
1671 <li>opcode stack can track some constant string concatenations
1672 (dbrosius)</li>
1673 <li>default effort performs iterative opcode analysis (but min
1674 effort does not)</li>
1675 <li>default heap size upped to 384m</li>
1676 <li>schema for XML output available: bugcollection.xsd</li>
1677 <li>fixed some internal confusion between dotted and slashed
1678 class names</li>
1679 <li>New detectors
1680 <ul>
1681 <li>CheckImmutableAnnotation.java: checks JCIP annotations</li>
1682 </ul>
1683 </li>
1684 <li>Updated detectors
1685 <ul>
1686 <li>BadRegEx.java: understands Pattern.LITERAL, warns about
1687 "."</li>
1688 <li>FindUnreleasedLock.java: fewer false positives</li>
1689 <li>DumbMethods.java: check for vacuous comparisons to
1690 MAX_INTEGER or MIN_INTEGER, fix bugs detecting
1691 DM_NEXTINT_VIA_NEXTDOUBLE</li>
1692 <li>FindPuzzlers.java: detect <tt>n%2==1</tt>, detect
1693 toString() on array types
1694 </li>
1695 <li>FindInconsistentSync2.java: detects IS_FIELD_NOT_GUARDED
1696 </li>
1697 <li>MethodReturnCheck.java: add check for discarded newly
1698 constructed values, increase priority of some ignored
1699 constructed exceptions, better handling of bytecode compiled by
1700 Eclipse</li>
1701 <li>FindEmptySynchronizedBlock.java: better handling of
1702 bytecode compiled by Eclipse</li>
1703 <li>DoInsideDoPrivileged.java: warn if call to setAccessible
1704 isn't in doPriviledged, don't report private methods</li>
1705 <li>LoadOfKnownNullValue.java: fix bug that was reporting
1706 false positives on <code> finally </code> blocks
1707 </li>
1708 <li>CheckReturnAnnotationDatabase.java: better checks for
1709 unstarted threads</li>
1710 <li>ConfusionBetweenInheritedAndOuterMethod.java: fewer
1711 false positives, fixed a package-handling bug</li>
1712 <li>BadResultSetAccess.java: separate bug pattern for
1713 PreparedStatements, <code> BRZA </code> category folded into <code>
1714 SQL </code> category
1715 </li>
1716 <li>FindDeadLocalStores.java, FindBadCast2.java,
1717 DumbMethods.java, RuntimeExceptionCapture.java: coalesce similar
1718 bugs within a method into a single bug instance with multiple
1719 source lines</li>
1720 </ul>
1721 </li>
1722 <li>Eclipse plugin
1723 <ul>
1724 <li>plugin ID changed from <tt>de.tobject.findbugs</tt> to <tt>edu.umd.cs.findbugs.plugin.eclipse</tt>
1725 </li>
1726 <li>support for findbugs eclipse auto-update site</li>
1727 </ul>
1728 </li>
1729 <li>Updated test case files
1730 <ul>
1731 <li>BadRegEx.java</li>
1732 <li>JSR166.java</li>
1733 <li>ConcurrentModificationBug.java</li>
1734 <li>DeadStore.java</li>
1735 <li>InstanceOf.java</li>
1736 <li>LoadKnownNull.java</li>
1737 <li>NeedsToCheckReturnValue.java</li>
1738 <li>BadResultSetAccessTest.java</li>
1739 <li>DeadStore.java</li>
1740 <li>TestNonNull2.java</li>
1741 <li>TestImmutable.java</li>
1742 <li>TestGuardedBy.java</li>
1743 <li>BadRandomInt.java</li>
1744 <li>six test cases added to new <code> TigerTraps </code>
1745 directory
1746 </li>
1747 </ul>
1748 </li>
1749 <li>fix bug that was generating duplicate uids</li>
1750 <li>fix bug with <code> -onlyAnalyze some.package.* </code> on
1751 jdk1.4
1752 </li>
1753 <li>fix regression bug in
1754 DismantleByteCode.getRefConstantOperand()</li>
1755 <li>fix some minor bugs with the Swing GUI</li>
1756 <li>reordered some bugInstances so that source line
1757 annotations come last</li>
1758 <li>removed references to unused java system properties</li>
1759 <li>French translation updates (David Cotton)</li>
1760 <li>Japanese translation updates (Hanai Shisei)</li>
1761 <li>content cleanup for findbugs.xml and messages.xml</li>
1762 <li>references to cvs hostname updated to
1763 findbugs.cvs.sourceforge.net</li>
1764 <li>documented xdoc output options, new
1765 mineBugHistory/computeBugHistory options</li>
1766 </ul>
1767
1768 <p>Changes since version 0.9.6:</p>
1769
1770 <ul>
1771 <li>performance improvements</li>
1772 <li>ObjectType instances are cached to reduce memory footprint
1773 </li>
1774 <li>for performance and memory reasons stateless detectors are
1775 no longer cloned, must clear their own state between .class files
1776 </li>
1777 <li>fixed bug in bytecode-set lookup for methods (was causing
1778 bad results for IS2, perhaps others)</li>
1779 <li>fix some OpcodeStack bugs with integer and long
1780 operations, perform iterative analysis when effort is <tt>max</tt>
1781 </li>
1782 <li>HTML output includes LongMessage text again (regression in
1783 0.95 - 0.96)</li>
1784 <li>New detectors
1785 <ul>
1786 <li>CalledMethods.java: builds a list of invoked methods for
1787 other detectors to consult (non-reporting)</li>
1788 <li>UncallableMethodOfAnonymousClass.java: detect anonymous
1789 inner classes that define methods that are probably intended to
1790 but do not override methods in a superclass.</li>
1791 </ul>
1792 </li>
1793 <li>Updated detectors
1794 <ul>
1795 <li>FindFieldSelfAssignment.java: recognize separate fields
1796 with the same name (one from superclass)</li>
1797 <li>FindLocalSelfAssignment2.java: handles backward branches
1798 better (Dave Brosius)</li>
1799 <li>FindBadCast2.java: BC_NULL_INSTANCEOF changed to
1800 NP_NULL_INSTANCEOF</li>
1801 <li>FindPuzzlers.java: eliminate false positive on setDate()
1802 (Dave Brosius)</li>
1803 </ul>
1804 </li>
1805 <li>Eclipse plugin
1806 <ul>
1807 <li>fix serious threading bug</li>
1808 <li>preferences for Filters and effort (Peter Hendriks)</li>
1809 <li>French localization (David Cotton)</li>
1810 <li>fix bug when reporting inner classes (Peter Friese)</li>
1811 </ul>
1812 </li>
1813 <li>Updated test case files
1814 <ul>
1815 <li>Mwn.java (Carl Burke/Dave Brosius)</li>
1816 <li>DumbMethodInvocations.java (Anto paul/Dave Brosius)</li>
1817 <!--sic-->
1818 </ul>
1819 </li>
1820 <li>XML output includes garbage collection duration</li>
1821 <li>French messages updated (David Cotton)</li>
1822 <li>Swing GUI shows file name after Load Bugs command</li>
1823 <li>Ant task to launch the findbugs frame (Mark McKay)</li>
1824 <li>miscellaneous code cleanup</li>
1825 </ul>
1826
1827 <p>Changes since version 0.9.5:</p>
1828
1829 <ul>
1830 <li>Updated detectors
1831 <ul>
1832 <li>FindNullDeref.java: respect NonNull and CheckForNull
1833 field annotations</li>
1834 <li>SerializableIdiom.java: detect non-private readObject
1835 and writeObject methods</li>
1836 <li>FindRefComparison.java: smarter array comparison
1837 detection</li>
1838 <li>IsNullValueAnalysis.java: detect <tt>null
1839 instanceof</tt>
1840 </li>
1841 <li>FindLocalSelfAssignment2.java: suppress some false
1842 positives (Dave Brosius)</li>
1843 <li>FindUnreleasedLock.java: don't waste time processing
1844 classes that don't refer to java.util.concurrent.locks</li>
1845 <li>MutableStaticFields.java: report the source line (Dave
1846 Brosius)</li>
1847 <li>SwitchFallthrough.java: better handling of System.exit()
1848 (Dave Brosius)</li>
1849 <li>MultithreadedInstanceAccess.java: better handling of
1850 Servlet.init() (Dave Brosius)</li>
1851 <li>ConfusionBetweenInheritedAndOuterMethod.java: now
1852 enabled</li>
1853 </ul>
1854 </li>
1855 <li>Eclipse plugin
1856 <ul>
1857 <li>background processing (Peter Friese)</li>
1858 <li>internationalization, Japanese localization (Takashi
1859 Okamoto)</li>
1860 </ul>
1861 </li>
1862 <li>findbugs <tt>-onlyAnalyze</tt> option now works on windows
1863 platforms
1864 </li>
1865 <li>mineBugHistory <tt>-noTabs</tt> option for better
1866 alignment of output columns
1867 </li>
1868 <li>filterBugs <tt>-fixed</tt> option (also: will now
1869 recognize the most recent version string)
1870 </li>
1871 <li>XML output includes running time and memory usage data</li>
1872 <li>miscellaneous minor corrections to the manual</li>
1873 <li>better bytecode analysis of the <tt>iinc</tt> instruction
1874 </li>
1875 <li>fix bug in null pointer analysis</li>
1876 <li>improved catch block heuristics</li>
1877 <li>some type analysis tweaks</li>
1878 <li>Bug priority changes
1879 <ul>
1880 <li>DumbMethodInvocations.java: decrease priority of
1881 hard-coded <tt>/tmp</tt> filenames
1882 </li>
1883 <li>ComparatorIdiom.java: decrease priority of
1884 non-serializable anonymous comparators</li>
1885 <li>FindSqlInjection.java: decrease priority of appending a
1886 constant or a static</li>
1887 </ul>
1888 </li>
1889 <li>Updated bug explanations
1890 <ul>
1891 <li>NM_VERY_CONFUSING (Dave Brosius)</li>
1892 </ul>
1893 </li>
1894 <li>Updated test case files
1895 <ul>
1896 <li>BadStoreOfNonSerializableObject.java</li>
1897 <li>BadRandomInt.java</li>
1898 <li>TestFieldAnnotations.java</li>
1899 <li>UseInitCause.java</li>
1900 <li>SqlInjection.java</li>
1901 <li>ArrayEquality.java</li>
1902 <li>BadIntegerOperations.java</li>
1903 <li>Pilhuhn.java</li>
1904 <li>InstanceOf.java</li>
1905 <li>SwitchFallthrough.java (Dave Brosius)</li>
1906 </ul>
1907 </li>
1908 <li>fix URL decoding bug when running under Java Web Start
1909 (Dave Brosius)</li>
1910 <li>distribution includes <tt>project.xml</tt> file for
1911 NetBeans
1912 </li>
1913 </ul>
1914
1915 <p>Changes since version 0.9.4:</p>
1916 <ul>
1917 <li>New detectors
1918 <ul>
1919 <li>VarArgsProblems.java</li>
1920 <li>FindSqlInjection.java: now enabled</li>
1921 <li>ComparatorIdiom.java: comparators usually implement
1922 serializable</li>
1923 <li>Naming.java: detect methods not overridden due to
1924 eponymously typed args from different packages</li>
1925 </ul>
1926 </li>
1927 <li>Updated detectors
1928 <ul>
1929 <li>SwitchFallthrough.java: surpress some false positives</li>
1930 <li>DuplicateBranches.java: surpress some false positives</li>
1931 <li>IteratorIdioms.java: surpress some false positives</li>
1932 <li>FindHEmismatch.java: surpress some false positives</li>
1933 <li>QuestionableBooleanAssignment.java: finds more cases of
1934 <tt>if (b=true)</tt> ilk
1935 </li>
1936 <li>DumbMethods.java: detect int remainder by 1, delayed gc
1937 errors</li>
1938 <li>SerializableIdiom.java: detect store of nonserializable
1939 object into field of serializable class</li>
1940 <li>FindNullDeref.java: fix potential exception</li>
1941 <li>IsNullValue.java: fix potential exception</li>
1942 <li>MultithreadedInstanceAccess.java: fix potential
1943 exception</li>
1944 <li>PreferZeroLengthArrays.java: flag the method, not the
1945 line</li>
1946 </ul>
1947 </li>
1948 <li>Remove some inadvertent dependencies on JDK 1.5</li>
1949 <li>Sort order should be more consistent</li>
1950 <li>XML output changes
1951 <ul>
1952 <li>Option to sort XML bug output</li>
1953 <li>Now contains instance IDs</li>
1954 <li>uid no longer missing (was causing problems with fancy
1955 HTML output)</li>
1956 <li>Typo fixed</li>
1957 </ul>
1958 </li>
1959 <li>Internal changes to track source files, <tt>-sourceInfo</tt>
1960 option
1961 </li>
1962 <li>Bug matching: first try exact bug pattern matching, option
1963 to compare priorities, option to disable package moves</li>
1964 <li>Architecture documentation in <tt>design/architecture</tt>
1965 </li>
1966 <li>Test cases move into their own CVS project</li>
1967 <li>Don't report warnings that occur outside the analyzed
1968 classes</li>
1969 <li>Fixes to the build.xml files</li>
1970 <li>Better handling of @CheckReturnValue and @CheckForNull
1971 annotations (also, some additional methods searched for check
1972 return value and check for null)</li>
1973 <li>Fixed some stream-closing bugs (one by <tt>z-fb-user</tt>/Dave
1974 Brosius)
1975 </li>
1976 <li>Bug priority changes
1977 <ul>
1978 <li>increase priority of ignoring return value of
1979 java.sql.Connection methods</li>
1980 <li>increase priority of comparing classes like Integer
1981 using <tt>==</tt>
1982 </li>
1983 <li>decrease priority of IT_NO_SUCH_ELEMENT if we see any
1984 call to <tt>next()</tt>
1985 </li>
1986 <li>tweak priority of NM_METHOD_CONSTRUCTOR_CONFUSION</li>
1987 <li>decrease priority of RV_RETURN_VALUE_IGNORED for an
1988 inherited annotation that doesn't return same type as class</li>
1989 </ul>
1990 </li>
1991 <li>Updated bug explanations
1992 <ul>
1993 <li>RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE</li>
1994 <li>DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED</li>
1995 <li>IMA_INEFFICIENT_MEMBER_ACCESS (Dave Brosius)</li>
1996 <li>some Japanese improvements to messages_ja.xml ( <tt>ruimo</tt>)
1997 </li>
1998 <li>some German improvements to findbugs_de.properties (Dave
1999 Brosius, <tt>dvholten</tt>)
2000 </li>
2001 </ul>
2002 </li>
2003 <li>Updated test case files
2004 <ul>
2005 <li>BadIntegerOperations.java</li>
2006 <li>SecondKaboom.java</li>
2007 <li>OpenDatabase.java (Dave Brosius)</li>
2008 <li>FindOpenStream.java (Dave Brosius)</li>
2009 <li>BadRandomInt.java</li>
2010 </ul>
2011 </li>
2012 <li>Source-lines info maintained for methods (handy for
2013 abstract and native methods)</li>
2014 <li>Remove surrounding opcodes from source line annotations</li>
2015 <li>Better error when can't read file</li>
2016 <li>Swing GUI: removed console pane from FindBugsFrame, fix
2017 missing classes bug</li>
2018 <li>Fixes to OpcodeStack.java</li>
2019 <li>Detectors may attach a custom value to an OpcodeStack.Item
2020 (Dave Brosius)</li>
2021 <li>Filter.java: ability to add text messages to XML output,
2022 fix bug with <tt>-withMessages</tt>
2023 </li>
2024 <li>SourceInfoMap supports ranges of source lines</li>
2025 <li>Ant task supports the <tt>timestampNow</tt> attribute
2026 </li>
2027 </ul>
2028
2029 <p>Changes since version 0.9.3:</p>
2030 <ul>
2031 <li>Substantial rework of datamining code</li>
2032 <li>Removed bogus warnings about await on things other than
2033 Condition not being in a loop</li>
2034 <li>Fixed bug in OpcodeStack handling of dup2 of long/double
2035 values</li>
2036 <li>Don't report array types as missing classes</li>
2037 <li>Adjustment of some warnings on ignored return values</li>
2038 <li>Added thread safety annotations from Java Concurrency in
2039 Practice (no detectors written for these yet)</li>
2040 <li>Added annotation for methods that, if overridden, should
2041 be invoked by overriding methods via a call to super</li>
2042 <li>Updated -html:fancy.xsl (Etienne Giraudy)</li>
2043 </ul>
2044
2045 <p>Note: there was no version 0.9.2</p>
2046
2047 <p>Changes since version 0.9.1:</p>
2048 <ul>
2049 <!-- New detectors -->
2050 <li>Embellish USM to find abstract methods that implement an
2051 interface method (Dave Brosius)</li>
2052 <li>New detector to find stores of literal booleans inside if
2053 or while expressions (Dave Brosius)</li>
2054 <li>New style detector to find final classes that declare
2055 protected fields (Dave Brosius)</li>
2056 <li>New detector to find subclass methods that simply forward,
2057 verbatim, to the super class (Dave Brosius)</li>
2058 <li>Detector to find instances where code is attempting to
2059 write an object out via an implementation of DataOutput, but the
2060 object is not guaranteed to be Serializable (Jon Christiansen,
2061 Bill Pugh)</li>
2062
2063 <!-- Feature enhancements -->
2064 <li>Large (35%) analysis speedup (Bill Pugh)</li>
2065 <li>Add line numbers to Swing GUI code panel (Dave Brosius)</li>
2066 <li>Added effort options to Swing GUI (Dave Brosius)</li>
2067 <li>Add ability to specify bugs file to open from command line
2068 for GUI version, through -loadbugs (Phillip Martin)</li>
2069 <li>New stylesheet for generating HTML: use option <tt>-html:plain.xsl</tt>
2070 (Chris Nappin)
2071 </li>
2072 <li>New stylesheet for generating HTML: use option <tt>-html:fancy.xsl</tt>
2073 (Etienne Giraudy)
2074 </li>
2075 <li>Updated Japanese bug message translations (Shisei Hanai)</li>
2076
2077 <!-- Bug fixes -->
2078 <li>XHTML compliance fixes for bug details (Etienne Giraudy)</li>
2079 <li>Various detector fixes (Shisei Hanai)</li>
2080 <li>Fixed bugs in the project preferences dialog int the
2081 Eclipse plugin (Takashi Okamoto, Thomas Einwaller)</li>
2082 <li>Lowered priority of analysis thread in Swing GUI (David
2083 Hovemeyer, suggested by Shisei Hanai and Jeffrey W. Badorek)</li>
2084 <li>Fixed EclipsePlugin to correctly pick up auxclasspath
2085 entries (Jon Christiansen)</li>
2086 </ul>
2087
2088 <p>Changes since version 0.9.0:</p>
2089 <ul>
2090 <li>Fixed dependence on JRE 1.5: all features should work on
2091 JRE 1.4 again</li>
2092 <li>Fixed -effort command line option handling for Swing GUI</li>
2093 <li>Fixed conserveSpace and workHard attributes int Ant task</li>
2094 <li>Added support for effort attribute in Ant task</li>
2095 </ul>
2096
2097 <p>Changes since version 0.8.8:</p>
2098 <ul>
2099 <!-- New detectors and bug patterns -->
2100 <li>XMLFactoryBypass detector to find direct allocation of xml
2101 class implementations (Dave Brosius)</li>
2102 <li>InefficientMemberAccess detector to find accesses to
2103 owning class private members (Dave Brosius)</li>
2104 <li>DuplicateBranches detector checks switch statements too
2105 (Dave Brosius)</li>
2106
2107 <!-- Feature enhancements -->
2108 <li>FindBugs available from findbugs.sourceforge.net as Java
2109 Web Start application (Dave Brosius)</li>
2110 <li>Updated Japanese bug message translations (Shisei Hanai)</li>
2111 <li>Improved bug detail message for covariant equals() (Shisei
2112 Hanai)</li>
2113 <li>Modeling of instanceof checks is now enabled by default,
2114 making the bad cast detector much more useful (Bill Pugh, David
2115 Hovemeyer)</li>
2116 <li>Support for detector ordering constraints in plugin
2117 descriptor (David Hovemeyer)</li>
2118 <li>Simpler option to control analysis effort: -effort: <i>value</i>,
2119 where <i>value</i> is one of <code> min </code> , <code>
2120 default </code> , or <code> max </code> (David Hovemeyer)
2121 </li>
2122 <li>Using -effort:max, FindNullDeref checks for null arguments
2123 passed to methods which dereference them unconditionally (David
2124 Hovemeyer)</li>
2125 <li>FindNullDeref checks @Null and @NonNull annotations for
2126 parameters and return values (David Hovemeyer)</li>
2127
2128 <!-- Bug fixes -->
2129 </ul>
2130
2131 <p>Changes since version 0.8.7:</p>
2132
2133 <ul>
2134 <!-- New detectors and bug patterns -->
2135 <li>New detector to find duplicate code in if/else statements
2136 (Dave Brosius)</li>
2137 <li>Look for calls to wait() on Condition objects (David
2138 Hovemeyer)</li>
2139 <li>Look for java.util.concurrent.Lock objects not released on
2140 every path out of method (David Hovemeyer)</li>
2141 <li>Look for calls to Thread.sleep() with a lock held (David
2142 Hovemeyer)</li>
2143 <li>More accurate detection of impossible casts (Bill Pugh,
2144 David Hovemeyer)</li>
2145
2146 <!-- Feature enhancements -->
2147 <li>Saved XML now contains project statistics (Jay Dunning)</li>
2148 <li>Filter files can select by bug pattern type and warning
2149 priority (David Hovemeyer)</li>
2150
2151 <!-- Bug fixes -->
2152 <li>Restored some files inadvertently omitted from previous
2153 release (Rohan Lloyd, David Hovemeyer)</li>
2154 <li>Make sure detectors requiring JDK 1.5 runtime classes are
2155 only executed if those classes are available (David Hovemeyer)</li>
2156 <li>Don't display analysis error dialog unless there is really
2157 an error (David Hovemeyer)</li>
2158 <li>Updated and expanded French translations of bug patterns
2159 and Swing GUI (Olivier Parent)</li>
2160 <li>Fixed invalid character encoding in German Swing GUI
2161 translation (Olivier Parent)</li>
2162 <li>Fix locale used for date format in project stats (K.
2163 Hashimoto)</li>
2164 <li>Fixed LongDescription elements in xml:withMessages output
2165 format (K. Hashimoto)</li>
2166 </ul>
2167
2168 <p>Changes since version 0.8.6:</p>
2169
2170 <ul>
2171 <!-- new detectors -->
2172 <li>Extend Naming detector to look for classes that are named
2173 XXXException but that are not Exceptions (Dave Brosius)</li>
2174 <li>New detector to find classes that expose semaphores in the
2175 public implementation through the 'this' reference. (Dave Brosius)
2176 </li>
2177 <li>New Style detector to find Struts Action/Servlet derived
2178 classes that reference instance member variable not in
2179 synchronized blocks. (Dave Brosius)</li>
2180 <li>New Style detector to find classes that declare
2181 implementation of interfaces that are already implemented by super
2182 classes (Dave Brosius)</li>
2183 <li>New Style detector to find circular dependencies between
2184 classes (Dave Brosius)</li>
2185 <li>New Style detector to find unnecessary math on constants
2186 (Dave Brosius)</li>
2187 <li>New detector to find equality comparisons using floating
2188 point math (Jay Dunning)</li>
2189 <li>New faster detector to find local self assignments (Bill
2190 Pugh)</li>
2191 <li>New detector to find infinite recursive loops (Bill Pugh)
2192 </li>
2193 <li>New detector to find for loops with an incorrect increment
2194 (Bill Pugh)</li>
2195 <li>New detector to find suspicious uses of
2196 BufferedReader.readLine() and String.indexOf() (Bill Pugh)</li>
2197 <li>New detector to find suspicious integer to double casts
2198 (David Hovemeyer, Bill Pugh)</li>
2199 <li>New detector to find invalid regular expression patterns
2200 (Bill Pugh)</li>
2201 <li>New detector to find Bloch/Gafter Java puzzlers (Bill
2202 Pugh)</li>
2203
2204 <!-- feature enhancements -->
2205 <li>New system property to suppress reporting of DLS based on
2206 local variable name (Glenn Boysko)</li>
2207 <li>Enhancements to configuration dialog in Eclipse plugin,
2208 allow for saving enabled detectors in Eclipse projects (Phil
2209 Crosby)</li>
2210 <li>Sortable columns in detector dialog (Dave Brosius)</li>
2211 <li>New tab in gui for showing bugs grouped by category (Dave
2212 Brosius)</li>
2213 <li>Improved German translation of Swing GUI (Thomas Kuehne)</li>
2214 <li>Improved source file reporting in Emacs output format (Len
2215 Trigg)</li>
2216 <li>Improvements to redundant null comparison detector (Bill
2217 Pugh)</li>
2218 <li>Localization of run analysis and analysis error dialogs in
2219 Swing GUI (K. Hashimoto)</li>
2220
2221 <!-- Bug fixes -->
2222 <li>Don't scan equals methods in FindHEMismatch if code is
2223 native (Greg Bentz)</li>
2224 <li>French translation fixes (David Cotton)</li>
2225 <li>Internationalization report fixes (K. Hashimoto)</li>
2226 <li>Japanese translations updates (SHISEI Hanai)</li>
2227 </ul>
2228
2229 <p>Changes since version 0.8.5:</p>
2230 <ul>
2231 <!-- new detectors -->
2232 <li>New detector to find catch blocks that may inadvertently
2233 catch runtime exceptions (Brian Goetz)</li>
2234 <li>New detector to find objects that are instantiated based
2235 on classes that only have static methods and fields, using the
2236 synthesized constructor (Dave Brosius)</li>
2237 <li>New detector to find calls to Thread.interrupted() in a
2238 non static context, and especially with non currentThread()
2239 threads (Dave Brosius)</li>
2240 <li>New detector to find calls to equals() methods that use
2241 Object's version. (Dave Brosius)</li>
2242 <li>New detector to find Applets that call methods in the
2243 constructor refering to the AppletStub (Dave Brosius)</li>
2244 <li>New detector to find some cases of infinite recursion
2245 (Bill Pugh)</li>
2246 <li>New detector to find dead stores to local variables (David
2247 Hovemeyer, Bill Pugh)</li>
2248 <li>Extend Dumb Method detector for toUpperCase(),
2249 toLowerCase() without a locale, new Integer(1).toString(), new
2250 XXX().getClass(), and new Thread() without a run implementation
2251 (Dave Brosius) <!-- feature enhancements -->
2252 </li>
2253 <li>Ant task supports "errorProperty" attribute, which sets an
2254 Ant property to "true" if an error occurs running FindBugs
2255 (Michael Tamm)</li>
2256 <li>Eclipse plugin allows filtering of warnings by bug
2257 category, priority (David Hovemeyer)</li>
2258 <li>Swing GUI allows filtering of warnings by bug category
2259 (David Hovemeyer)</li>
2260 <li>Ability to annotate methods using Java 1.5 annotations
2261 that suppress FindBugs warnings (Bill Pugh)</li>
2262 <li>New -adjustExperimental for lowering priority of
2263 BugPatterns that are experimental (Dave Brosius)</li>
2264 <li>Allow for command line options 'files' using the @ symbol
2265 (David Hovemeyer)</li>
2266 <li>New -adjustPriority command line option to for adjusting
2267 bug priorites (David Hovemeyer)</li>
2268 <li>Added an Edit menu (cut/copy/paste) to Swing GUI (Dave
2269 Brosius)</li>
2270 <li>French translation supplied (David Cotton) <!-- Bug fixes -->
2271 </li>
2272 </ul>
2273
2274 <p>Changes since version 0.8.4:</p>
2275 <ul>
2276 <!-- new detectors -->
2277 <li>New detector for volatile references to arrays (Bill Pugh)
2278 </li>
2279 <li>New detector to find instanceof usage where inheritance
2280 can be determined statically (Dave Brosius)</li>
2281 <li>New detector to find ResultSet.getXXX updateXXX calls
2282 using index 0 (Dave Brosius)</li>
2283 <li>New detector to find empty zip or jar entries (Bill Pugh)
2284
2285 <!-- feature enhancements -->
2286 </li>
2287 <li>HTML output generation using built-in XSLT stylesheet or
2288 user-defined stylesheet (David Hovemeyer)</li>
2289 <li>Allow URLs to be specified to analyze zip/jar files, local
2290 directories, and single classfiles (David Hovemeyer)</li>
2291 <li>New command line option -onlyAnalyze restricts analysis to
2292 selected classes and packages without reducing accuracy (David
2293 Hovemeyer)</li>
2294 <li>Allow Swing GUI to show source code in jar files on
2295 Windows systems (Dave Brosius) <!-- Bug fixes -->
2296 </li>
2297 <li>Fix the Switch Fall Thru detector (Dave Brosius, David
2298 Hovemeyer, Bill Pugh)</li>
2299 <li>MacOS GUI fixes (Rohan Lloyd)</li>
2300 <li>Fix false positive in BOA in case where method is
2301 correctly and 'incorrectly' overridden (Dave Brosius)</li>
2302 <li>Fixed memory blowup when analyzing methods which access a
2303 large number of fields (David Hovemeyer)</li>
2304 </ul>
2305
2306 <p>Changes since version 0.8.3:</p>
2307 <ul>
2308 <li>Initial and preliminary localization of the Swing
2309 GUI.&nbsp; Translations by:
2310 <ul>
2311 <li>German - Peter D. Stout, Holger Stenzhorn</li>
2312 <li>Finnish - Juha Knuutila</li>
2313 <li>Estonian - Tanel Lebedev</li>
2314 <li>Japanese - Hanai Shisei</li>
2315 </ul>
2316 </li>
2317 <li>Eliminated debug print statements inadvertently left
2318 enabled</li>
2319 <li>Reverted some changes in the open stream detector: this
2320 should fix some false positives that were introduced in the
2321 previous release</li>
2322 <li>Fixed a couple missing class reports</li>
2323 </ul>
2324
2325 <p>Changes since version 0.8.2:</p>
2326 <ul>
2327
2328 <!-- New detectors -->
2329 <li>New detector to find improperly overridden GUI Adapter
2330 classes (Dave Brosius)</li>
2331 <li>New detector to find improperly setup JUnit TestCases
2332 (Dave Brosius)</li>
2333 <li>New detector to find variables that mask class level
2334 fields (Dave Brosius)</li>
2335 <li>New detector to find comparisons of values computed with
2336 bitwise operators that always yield the same result (Tom Truscott)
2337 </li>
2338 <li>New detector to find unsafe getClass().getResource() calls
2339 (Bill Pugh)</li>
2340 <li>New detector to find GUI changes not in GUI thread but in
2341 static main (Bill Pugh)</li>
2342 <li>New detector to find calls to Collection.toArray() with
2343 zero-length array argument; it is more efficient to pass an array
2344 the size of the collection, which can be populated and returned as
2345 the result (Dave Brosius) <!-- Analysis improvements -->
2346 </li>
2347 <li>Better suppression of false warnings in various detectors
2348 (Bill Pugh, David Hovemeyer)</li>
2349 <li>Enhancement to ReadReturnShouldBeChecked detector for
2350 skip() (Dave Brosius)</li>
2351 <li>Enhancement to DumbMethods detector (Dave Brosius)</li>
2352 <li>Open stream detector does not report wrappers of streams
2353 passed as method parameters (David Hovemeyer) <!-- Feature enhancements -->
2354 </li>
2355 <li>Cancel confirmation dialog in Swing GUI (Pete Angstadt)</li>
2356 <li>Better relative path saving in Project file (Dave Brosius)
2357 </li>
2358 <li>Detector Priority in GUI is now saved in prefs file (Dave
2359 Brosius)</li>
2360 <li>Controls in GUI to reorder source and classpath entries,
2361 and ability to flip between Project details and bugs pages (Dave
2362 Brosius)</li>
2363 <li>In Swing GUI, analysis error dialog supports "Select All"
2364 and "Copy" operations for easy generation of error reports (Dave
2365 Brosius)</li>
2366 <li>Complete translation of bug descriptions and messages into
2367 Japanese (Hanai Shisei) <!-- Bug fixes -->
2368 </li>
2369 <li>Fixed bug in DroppedException detector (Dave Brosius) <!-- Development stuff -->
2370 </li>
2371 <li>The source distribution defaults to using JDK 1.5 javac to
2372 compile, but support for compiling with JSR-14 prototype is still
2373 supported</li>
2374 </ul>
2375
2376 <p>Changes since version 0.8.1:</p>
2377 <ul>
2378 <li>Fixed a critical ClassCastException bug (triggered if the
2379 -workHard option was used, and an exception type was merged with
2380 an array type during type inference)</li>
2381 </ul>
2382
2383 <p>Changes since version 0.8.0:</p>
2384 <ul>
2385 <li>Disabled SwitchFallthrough detector to work around
2386 NullPointerExceptions</li>
2387 <li>Added some additional false positive suppression
2388 heuristics</li>
2389 </ul>
2390
2391 <p>Also, two contributors to the 0.8.0 release were
2392 inadvertently left out of the credits:</p>
2393 <ul>
2394 <li>Pete Angstadt fixed several problems in the Swing GUI</li>
2395 <li>Francis Lalonde provided a task resource file for the
2396 FindBugs Ant task</li>
2397 </ul>
2398
2399 <p>Changes since version 0.7.4:</p>
2400 <ul>
2401 <li>New detector to look for uses of "+" operator to
2402 concatenate String objects in a loop (Dave Brosius)</li>
2403 <li>Reference comparison detector looks for places where the
2404 argument passed to the equals(Object) method isn't the same type
2405 as the receiver object</li>
2406 <li>Better suppression of false warnings in many detectors</li>
2407 <li>Many improvements to Eclipse plugin (Andrey Loskutov,
2408 Peter Friese)</li>
2409 <li>Fixed problem with building Eclipse plugin on Windows
2410 (Thomas Klaeger)</li>
2411 <li>Open stream detector looks for unclosed PreparedStatement
2412 objects (Thomas Klaeger, Rohan Lloyd)</li>
2413 <li>Fix for open stream detector: it wasn't detecting close()
2414 methods called through an invokeinterface instruction (Thomas
2415 Klaeger)</li>
2416 <li>Refactoring of visitor classes to enforce use of accessors
2417 for visited class features (Brian Goetz)</li>
2418 </ul>
2419
2420 <p>Changes since version 0.7.3:</p>
2421 <ul>
2422 <li>Experimental modification of open stream detector to look
2423 for non-escaping JDBC resources (connections and statements) that
2424 aren't closed on all paths out of method</li>
2425 <li>Eclipse plugin fixed so it compiles and runs on Eclipse
2426 2.1.x (Peter Friese)</li>
2427 <li>Option to Swing GUI and command line to generate project
2428 file using relative paths for archives, source directories, and
2429 aux classpath entries (Dave Brosius)</li>
2430 <li>Improvements to findbugs.bat script for launching FindBugs
2431 on Windows (Dave Brosius)</li>
2432 <li>Updated Japanese message translations (Hiroshi Okugawa)</li>
2433 <li>Uncalled private methods are now reported as low priority,
2434 unless they have the same name as another method in the class
2435 (which is more likely to indicate an actual bug)</li>
2436 <li>Added some missing data in the bug messages XML files</li>
2437 <li>Fixed some problems building from source on Windows
2438 systems</li>
2439 <li>Various minor bug fixes</li>
2440 </ul>
2441
2442 <p>Changes since version 0.7.2:</p>
2443 <ul>
2444 <li>Enhanced Eclipse plugin, which displays the detailed bug
2445 description in a view (Phil Crosby)</li>
2446 <li>Various tweaks to existing detectors to reduce false
2447 warnings</li>
2448 <li>New command line option <code> -workHard </code> enables
2449 pruning of infeasible or unlikely exception edges, which results
2450 in better accuracy in the open stream detector, at the expense of
2451 a 30%-100% slowdown
2452 </li>
2453 <li>New website and HTML documentation design</li>
2454 <li>Documentation includes an HTML document with descriptions
2455 of all bug patterns reported by FindBugs</li>
2456 <li>Web page has a link to a <a
2457 href="http://www.simeji.com/findbugs/doc/manual_ja/index.html">Japanese
2458 translation</a> of the FindBugs manual, contributed by Hiroshi
2459 Okugawa
2460 </li>
2461 <li>Changed the Inconsistent Synchronization detector so that
2462 fields synchronized 50% of the time (or more) are reported as
2463 medium priority bugs (previously they were reported as low)</li>
2464 <li>New detector to find code that catches
2465 IllegalMonitorStateException</li>
2466 <li>New detector to find private methods that are never called
2467 </li>
2468 <li>New detector to find suspicious uses of
2469 non-short-circuiting boolean operators ( <code> &amp; </code> and
2470 <code> | </code> , rather than <code> &amp;&amp; </code> and <code>
2471 || </code> )
2472 </li>
2473 </ul>
2474
2475 <p>Changes since version 0.7.1:</p>
2476 <ul>
2477 <li>Incorporated patched version of BCEL, which allows classes
2478 compiled with JDK 1.5.0 beta to be analyzed</li>
2479 <li>Fixed some bugs related to lookups of array classes</li>
2480 <li>Fixed bug that prevented GUI from loading XML result files
2481 when running under JDK 1.5.0 beta</li>
2482 <li>Added new experimental bug detector, LazyInit, which looks
2483 for potentially buggy lazy initializations of static fields</li>
2484 <li>Because of long filenames, switched to distributing the
2485 source archive as a zip file rather than a tar file</li>
2486 <li>The 0.7.1 source tarfile was botched - 0.7.2 has a valid
2487 source archive</li>
2488 <li>Fixed some problems in the Ant build script</li>
2489 <li>Fixed NullPointerException when checking Class-Path
2490 attribute for Jar files without manifests</li>
2491 <li>Generate version numbers for the core and UI Eclipse
2492 plugins using the Version class; all version numbers are now in a
2493 common location</li>
2494 </ul>
2495
2496 <p>Changes since version 0.7.0:</p>
2497 <ul>
2498 <li>Eclipse plugin (contributed by Peter Friese)</li>
2499 <li>Source package structure rearranged: all source (other
2500 than Eclipse plugin UI) is in the edu.umd.cs.findbugs package, or
2501 a subpackage</li>
2502 <li>Class-Path attributes of manifests of analyzed jar files
2503 are used to set the aux classpath automatically (Peter D. Stout)</li>
2504 <li>GUI starts in directory specified by user.home property
2505 (Peter D. Stout)</li>
2506 <li>Added -project option to GUI (Mikko T.)</li>
2507 <li>Added -look:{plastic,gtk,native} option to GUI, for
2508 setting look and feel (Mikko T.)</li>
2509 <li>Fixed DataflowAnalysisException in inconsistent
2510 synchronization detector</li>
2511 <li>Ant task supports failOnError parameter (Rohan Lloyd)</li>
2512 <li>Serializable class warnings are downgraded to low priority
2513 for GUI classes</li>
2514 <li>MWN detector will only report calls to wait(), notify(),
2515 and notifyAll() methods that have the correct signature</li>
2516 <li>FindBugs works with latest CVS version of BCEL</li>
2517 <li>Zip and Jar files may be added to the source path</li>
2518 <li>The GUI will automatically find source files residing in
2519 analyzed Zip or Jar files</li>
2520 </ul>
2521
2522 <p>Note that the version number jumped from 0.6.6 to 0.6.9;
2523 there were no 0.6.7 or 0.6.8 releases.</p>
2524 <p>Changes since version 0.6.9:</p>
2525 <ul>
2526 <li>Added -conserveSpace option to reduce memory use at the
2527 expense of analysis precision</li>
2528 <li>Bug fixes in findbugs.bat script: JAVA_HOME handling,
2529 autodetection of FINDBUGS_HOME, missing output with -textui</li>
2530 <li>Fixed NullPointerException when a missing class is
2531 encountered</li>
2532 </ul>
2533
2534 <p>Changes since version 0.6.6:</p>
2535 <ul>
2536 <li>The null pointer dereference detector is more powerful</li>
2537 <li>Significantly improved heuristics and bug fixes in
2538 inconsistent synchronization detector</li>
2539 <li>Improved heuristics in open stream and dropped exception
2540 detectors; fewer false positives should be reported</li>
2541 <li>Save HTML summary in XML results files, rather than
2542 recomputing; this makes loading results in GUI much faster</li>
2543 <li>Report at most one String comparison using == or != per
2544 method</li>
2545 <li>The findbugs.bat script on Windows autodetects
2546 FINDBUGS_HOME, and doesn't open a DOS window when launching the
2547 GUI (contributed by TJSB)</li>
2548 <li>Emacs reporting format (contributed by David Li)</li>
2549 <li>Various bug fixes</li>
2550 </ul>
2551
2552 <p>Changes since 0.6.5:</p>
2553 <ul>
2554 <li>Rewritten inconsistent synchronization detector; accuracy
2555 is significantly improved, and bug reports are prioritized</li>
2556 <li>New detector to find self assignment (x=x) of local
2557 variables (suggested by Jeff Martin)</li>
2558 <li>New detector to find calls to wait(), notify(), and
2559 notifyAll() on an object which is not obviously locked</li>
2560 <li>Open stream detector now reports Readers and Writers</li>
2561 <li>Fixed bug in finalizer idioms detector which caused
2562 spurious warnings about failure to call super.finalize() (reported
2563 by Jim Menard)</li>
2564 <li>Fixed bug where output stream was not closed using non-XML
2565 output (reported by Sigiswald Madou)</li>
2566 <li>Fixed corrupted HTML bug detail message (reported by
2567 Trevor Harmon)</li>
2568 </ul>
2569
2570 <p>Changes since version 0.6.4:</p>
2571 <ul>
2572 <li>For redundant comparison of reference values, fixed false
2573 positives resulting from duplication of code in finally blocks</li>
2574 <li>Fixed false positives resulting from wrapped byte array
2575 streams left open</li>
2576 <li>Fixed bug in Ant task preventing output file from working
2577 properly if a relative path was used</li>
2578 </ul>
2579
2580 <p>Changes since version 0.6.3:</p>
2581 <ul>
2582 <li>Fixed bug in Ant task where output would be corrupted, and
2583 added a <code> timeout </code> attribute
2584 </li>
2585 <li>Added -outputFile option to text UI, for explicitly
2586 specifying an output file</li>
2587 <li>GUI has a summary window, for statistics about overall bug
2588 densities (contributed by Mike Fagan)</li>
2589 <li>Find redundant comparisons of reference values</li>
2590 <li>More accurate detection of Strings compared with == and !=
2591 operators</li>
2592 <li>Detection of other reference types which should generally
2593 not be compared with == and != operators; Boolean, Integer, etc.</li>
2594 <li>Find non-transient non-serializable instance fields in
2595 Serializable classes</li>
2596 <li>Source code may be compiled with latest early access
2597 generics-enabled javac (version 2.2)</li>
2598 </ul>
2599
2600 <p>Changes since version 0.6.2:</p>
2601 <ul>
2602 <li>GUI supports filtering bugs by priority</li>
2603 <li>Ant task rewritten; supports all functionality offered by
2604 Text UI (contributed by Mike Fagan)</li>
2605 <li>Ant task is fully documented in the manual</li>
2606 <li>Classes in nested archives are analyzed; this allows full
2607 support for analyzing .ear and .war files (contributed by Mike
2608 Fagan)</li>
2609 <li>DepthFirstSearch changed to use non-recursive
2610 implementation; this should fix the StackOverflowErrors that
2611 several users reported</li>
2612 <li>Various minor bugfixes and improvements</li>
2613 </ul>
2614
2615 <p>Changes since version 0.6.1:</p>
2616 <ul>
2617 <li>New detector to look for useless control flow (suggested
2618 by Richard P. King and Mike Fagan)</li>
2619 <li>Look for places where return value of
2620 java.io.File.createNewFile() is ignored (suggested by Richard P.
2621 King)</li>
2622 <li>Fixed bug in resolution of source files (only the first
2623 source directory was searched)</li>
2624 <li>Fixed a NullPointerException in the bytecode pattern
2625 matching code</li>
2626 <li>Ant task supports project files (contributed by Mike
2627 Fagan)</li>
2628 <li>Unix findbugs script honors the <code> JAVA_HOME </code>
2629 environment variable (contributed by Pedro Morais)
2630 </li>
2631 <li>Allow .war and .ear files to be analyzed</li>
2632 </ul>
2633
2634 <p>Changes since version 0.6.0:</p>
2635 <ul>
2636 <li>New bug pattern detector which looks for places where a
2637 null pointer might be dereferenced</li>
2638 <li>New bug pattern detector which looks for IO streams that
2639 are opened, do not escape the method, and are not closed on all
2640 paths out of the method</li>
2641 <li>New bug pattern detector to find methods that can return
2642 null instead of a zero-length array</li>
2643 <li>New bug pattern detector to find places where the == or !=
2644 operators are used to compare String objects</li>
2645 <li>Command line interface can save bugs as XML</li>
2646 <li>GUI can save bugs to and load bugs from XML</li>
2647 <li>An "Annotations" window in the GUI allows the user to add
2648 textual annotations to bug reports; these annotations are
2649 preserved when bugs are saved as XML</li>
2650 <li>In this release, the Japanese bug summary translations by
2651 Germano Leichsenring are really included (they were inadvertently
2652 omitted in the previous release)</li>
2653 <li>Completely rewrote the control flow graph builder,
2654 hopefully for the last time</li>
2655 <li>Simplified implementation of control flow graphs, which
2656 should reduce memory use and possibly improve performance</li>
2657 <li>Improvements to command line interface (list bug
2658 priorities, filter by priority, specify aux classpath, specify
2659 project to analyze)</li>
2660 <li>Various bug fixes and enhancements</li>
2661 </ul>
2662
2663 <p>Changes since version 0.5.4</p>
2664 <ul>
2665 <li>Added an <a href="http://ant.apache.org/">Ant</a> task for
2666 FindBugs, contributed by Mike Fagan.
2667 </li>
2668 <li>Added a GUI dialog which allows individual bug pattern
2669 detectors to be enabled or disabled.&nbsp; Disabling certain slow
2670 detectors can greatly speed up analysis of large programs, at the
2671 expense of reducing the number of potential bugs found.</li>
2672 <li>Added a new detector for finding improperly ignored return
2673 values for methods such as <code> String.trim() </code> .&nbsp;
2674 Suggested by Andreas Mandel.
2675 </li>
2676 <li>Japanese translations of the bug summaries, contributed by
2677 Germano Leichsenring.</li>
2678 <li>Filtering of results is supported in command line
2679 interface. See the <a href="manual/index.html">FindBugs manual</a>
2680 for details.
2681 </li>
2682 <li>Added "byte code patterns", a general pattern matching
2683 infrastructure for bytecode instructions.&nbsp; This feature
2684 significantly reduces the complexity of implementing new bug
2685 pattern detectors.</li>
2686 <li>Enabled a new general dataflow analysis to track values in
2687 methods.</li>
2688 <li>Switched to new control-flow graph builder implementation.
2689 </li>
2690 </ul>
2691
2692 <p>Changes since version 0.5.3</p>
2693 <ul>
2694 <li>Fixed a bug in the script used to launch FindBugs on
2695 Windows platforms.</li>
2696 <li>Fixed crashes when analyzing class files without source
2697 line information.</li>
2698 <li>All major errors are reported using an error dialog; file
2699 not found errors are more informative.</li>
2700 <li>Minor GUI improvements.</li>
2701 </ul>
2702
2703 <p>Changes since version 0.5.2</p>
2704 <ul>
2705 <li>All of the source code and related files are in a single
2706 directory tree.</li>
2707 <li>Updated some of the detectors to produce source line
2708 information.</li>
2709 <li><a href="http://ant.apache.org/">Ant</a> build script and
2710 several GUI enhancements and fixes contributed by Mike Fagan.</li>
2711 <li>Converted to use a <a href="AddingDetectors.txt">plugin
2712 architecture</a> for loading bug detectors.
2713 </li>
2714 <li>Eliminated generics-related compiler warnings.</li>
2715 <li>More complete documentation has been added.</li>
2716 </ul>
2717
2718 <p>Changes since version 0.5.1:</p>
2719 <ul>
2720 <li>Fixed a large number of bugs in the BCEL Repository and
2721 FindBugs's use of the Repository.&nbsp; With these changes,
2722 FindBugs should <em>never</em> crash or otherwise misbehave
2723 because of Repository lookup failures.&nbsp; Because of these
2724 changes, you must use a modified version of <code> bcel.jar
2725 </code> with FindBugs.&nbsp; This jar file is included in the FindBugs
2726 0.5.2 binary release.&nbsp; A complete patch containing the <a
2727 href="http://faculty.ycp.edu/~dhovemey/bcel-30-April-2003.patch">modifications
2728 against the BCEL CVS main branch as of April 30, 2003</a> is also
2729 available.
2730 </li>
2731 <li>Implemented the "auxiliary classpath entry list".&nbsp;
2732 Aux classpath entries can be added to a project to provide classes
2733 that are referenced by the analyzed application, but should not
2734 themselves be analyzed.&nbsp; Having all referenced classes
2735 available allows FindBugs to produce more accurate results.</li>
2736 </ul>
2737
2738 <p>Changes since version 0.5.0:</p>
2739 <ul>
2740 <li>Many user interface bugs have been fixed.</li>
2741 <li>Upgraded to a recent CVS version of BCEL, with some bug
2742 fixes.&nbsp; This should prevent FindBugs from crashing when there
2743 is a failure to find a class on the classpath.</li>
2744 <li>Added support for Plastic look and feel from <a
2745 href="http://www.jgoodies.com/">jgoodies.com</a>.
2746 </li>
2747 <li>Major overhaul of infrastructure for doing dataflow
2748 analysis.</li>
2749 </ul> @HTML_FOOTER@
2750
2751 </td>
2752
2753 </tr>
2754 </table>
1840 </li>
1841 <li>Added check for a dead local store caused by a switch
1842 statement fall through</li>
1843 <li>Added check for computing the absolute value of a random
1844 32 bit integer or of a hashcode. This is broken because <code>
1845 Math.abs(Integer.MIN_VALUE) == Integer.MIN_VALUE </code> , and thus
1846 result of calling Math.abs, which is expected to be nonnegative,
1847 will in fact be negative one time out of 2 <sup> 32 </sup> , which
1848 will invariably be the time your boss is demoing the software to
1849 your customers.
1850
1851 </li>
1852 <li>More careful resolution of inherited methods and fields.
1853 Some of the shortcuts we were taking in FindBugs 1.0.0 were
1854 leading to inaccurate results, and it was fairly easy to address
1855 this by making the analysis more accurate.</li>
1856 <li>Overall, analysis times are about 1.6 times longer in
1857 FindBugs 1.1.0 than in FindBugs 1.0.0. This is because we have
1858 enabled substantial additional analysis at the default effort
1859 level (the actual analysis engine is significantly faster than in
1860 FindBugs 1.0). On a recent AMD Athlon processor, analyzing
1861 JDK1.6.0 (about 1 million lines of code) requires about 15 minutes
1862 of wall clock time.</li>
1863 <li>Provided class and script (printClass) to print classfile
1864 in the human readable format produced by BCEL</li>
1865 <li>Provided -findSource option to setBugDatabaseInfo</li>
1866 </ul>
1867
1868
1869 <p>Changes since version 0.9.7:</p>
1870
1871 <ul>
1872 <li>fix ObjectTypeFactory bug that was suppressing some bugs</li>
1873 <li>opcode stack may determine definite zeros on some paths</li>
1874 <li>opcode stack can track some constant string concatenations
1875 (dbrosius)</li>
1876 <li>default effort performs iterative opcode analysis (but min
1877 effort does not)</li>
1878 <li>default heap size upped to 384m</li>
1879 <li>schema for XML output available: bugcollection.xsd</li>
1880 <li>fixed some internal confusion between dotted and slashed
1881 class names</li>
1882 <li>New detectors
1883 <ul>
1884 <li>CheckImmutableAnnotation.java: checks JCIP annotations</li>
1885 </ul>
1886 </li>
1887 <li>Updated detectors
1888 <ul>
1889 <li>BadRegEx.java: understands Pattern.LITERAL, warns about
1890 "."</li>
1891 <li>FindUnreleasedLock.java: fewer false positives</li>
1892 <li>DumbMethods.java: check for vacuous comparisons to
1893 MAX_INTEGER or MIN_INTEGER, fix bugs detecting
1894 DM_NEXTINT_VIA_NEXTDOUBLE</li>
1895 <li>FindPuzzlers.java: detect <tt>n%2==1</tt>, detect
1896 toString() on array types
1897 </li>
1898 <li>FindInconsistentSync2.java: detects IS_FIELD_NOT_GUARDED
1899 </li>
1900 <li>MethodReturnCheck.java: add check for discarded newly
1901 constructed values, increase priority of some ignored
1902 constructed exceptions, better handling of bytecode compiled by
1903 Eclipse</li>
1904 <li>FindEmptySynchronizedBlock.java: better handling of
1905 bytecode compiled by Eclipse</li>
1906 <li>DoInsideDoPrivileged.java: warn if call to setAccessible
1907 isn't in doPriviledged, don't report private methods</li>
1908 <li>LoadOfKnownNullValue.java: fix bug that was reporting
1909 false positives on <code> finally </code> blocks
1910 </li>
1911 <li>CheckReturnAnnotationDatabase.java: better checks for
1912 unstarted threads</li>
1913 <li>ConfusionBetweenInheritedAndOuterMethod.java: fewer
1914 false positives, fixed a package-handling bug</li>
1915 <li>BadResultSetAccess.java: separate bug pattern for
1916 PreparedStatements, <code> BRZA </code> category folded into <code>
1917 SQL </code> category
1918 </li>
1919 <li>FindDeadLocalStores.java, FindBadCast2.java,
1920 DumbMethods.java, RuntimeExceptionCapture.java: coalesce similar
1921 bugs within a method into a single bug instance with multiple
1922 source lines</li>
1923 </ul>
1924 </li>
1925 <li>Eclipse plugin
1926 <ul>
1927 <li>plugin ID changed from <tt>de.tobject.findbugs</tt> to <tt>edu.umd.cs.findbugs.plugin.eclipse</tt>
1928 </li>
1929 <li>support for findbugs eclipse auto-update site</li>
1930 </ul>
1931 </li>
1932 <li>Updated test case files
1933 <ul>
1934 <li>BadRegEx.java</li>
1935 <li>JSR166.java</li>
1936 <li>ConcurrentModificationBug.java</li>
1937 <li>DeadStore.java</li>
1938 <li>InstanceOf.java</li>
1939 <li>LoadKnownNull.java</li>
1940 <li>NeedsToCheckReturnValue.java</li>
1941 <li>BadResultSetAccessTest.java</li>
1942 <li>DeadStore.java</li>
1943 <li>TestNonNull2.java</li>
1944 <li>TestImmutable.java</li>
1945 <li>TestGuardedBy.java</li>
1946 <li>BadRandomInt.java</li>
1947 <li>six test cases added to new <code> TigerTraps </code>
1948 directory
1949 </li>
1950 </ul>
1951 </li>
1952 <li>fix bug that was generating duplicate uids</li>
1953 <li>fix bug with <code> -onlyAnalyze some.package.* </code> on
1954 jdk1.4
1955 </li>
1956 <li>fix regression bug in
1957 DismantleByteCode.getRefConstantOperand()</li>
1958 <li>fix some minor bugs with the Swing GUI</li>
1959 <li>reordered some bugInstances so that source line
1960 annotations come last</li>
1961 <li>removed references to unused java system properties</li>
1962 <li>French translation updates (David Cotton)</li>
1963 <li>Japanese translation updates (Hanai Shisei)</li>
1964 <li>content cleanup for findbugs.xml and messages.xml</li>
1965 <li>references to cvs hostname updated to
1966 findbugs.cvs.sourceforge.net</li>
1967 <li>documented xdoc output options, new
1968 mineBugHistory/computeBugHistory options</li>
1969 </ul>
1970
1971 <p>Changes since version 0.9.6:</p>
1972
1973 <ul>
1974 <li>performance improvements</li>
1975 <li>ObjectType instances are cached to reduce memory footprint
1976 </li>
1977 <li>for performance and memory reasons stateless detectors are
1978 no longer cloned, must clear their own state between .class files
1979 </li>
1980 <li>fixed bug in bytecode-set lookup for methods (was causing
1981 bad results for IS2, perhaps others)</li>
1982 <li>fix some OpcodeStack bugs with integer and long
1983 operations, perform iterative analysis when effort is <tt>max</tt>
1984 </li>
1985 <li>HTML output includes LongMessage text again (regression in
1986 0.95 - 0.96)</li>
1987 <li>New detectors
1988 <ul>
1989 <li>CalledMethods.java: builds a list of invoked methods for
1990 other detectors to consult (non-reporting)</li>
1991 <li>UncallableMethodOfAnonymousClass.java: detect anonymous
1992 inner classes that define methods that are probably intended to
1993 but do not override methods in a superclass.</li>
1994 </ul>
1995 </li>
1996 <li>Updated detectors
1997 <ul>
1998 <li>FindFieldSelfAssignment.java: recognize separate fields
1999 with the same name (one from superclass)</li>
2000 <li>FindLocalSelfAssignment2.java: handles backward branches
2001 better (Dave Brosius)</li>
2002 <li>FindBadCast2.java: BC_NULL_INSTANCEOF changed to
2003 NP_NULL_INSTANCEOF</li>
2004 <li>FindPuzzlers.java: eliminate false positive on setDate()
2005 (Dave Brosius)</li>
2006 </ul>
2007 </li>
2008 <li>Eclipse plugin
2009 <ul>
2010 <li>fix serious threading bug</li>
2011 <li>preferences for Filters and effort (Peter Hendriks)</li>
2012 <li>French localization (David Cotton)</li>
2013 <li>fix bug when reporting inner classes (Peter Friese)</li>
2014 </ul>
2015 </li>
2016 <li>Updated test case files
2017 <ul>
2018 <li>Mwn.java (Carl Burke/Dave Brosius)</li>
2019 <li>DumbMethodInvocations.java (Anto paul/Dave Brosius)</li>
2020 <!--sic-->
2021 </ul>
2022 </li>
2023 <li>XML output includes garbage collection duration</li>
2024 <li>French messages updated (David Cotton)</li>
2025 <li>Swing GUI shows file name after Load Bugs command</li>
2026 <li>Ant task to launch the findbugs frame (Mark McKay)</li>
2027 <li>miscellaneous code cleanup</li>
2028 </ul>
2029
2030 <p>Changes since version 0.9.5:</p>
2031
2032 <ul>
2033 <li>Updated detectors
2034 <ul>
2035 <li>FindNullDeref.java: respect NonNull and CheckForNull
2036 field annotations</li>
2037 <li>SerializableIdiom.java: detect non-private readObject
2038 and writeObject methods</li>
2039 <li>FindRefComparison.java: smarter array comparison
2040 detection</li>
2041 <li>IsNullValueAnalysis.java: detect <tt>null
2042 instanceof</tt>
2043 </li>
2044 <li>FindLocalSelfAssignment2.java: suppress some false
2045 positives (Dave Brosius)</li>
2046 <li>FindUnreleasedLock.java: don't waste time processing
2047 classes that don't refer to java.util.concurrent.locks</li>
2048 <li>MutableStaticFields.java: report the source line (Dave
2049 Brosius)</li>
2050 <li>SwitchFallthrough.java: better handling of System.exit()
2051 (Dave Brosius)</li>
2052 <li>MultithreadedInstanceAccess.java: better handling of
2053 Servlet.init() (Dave Brosius)</li>
2054 <li>ConfusionBetweenInheritedAndOuterMethod.java: now
2055 enabled</li>
2056 </ul>
2057 </li>
2058 <li>Eclipse plugin
2059 <ul>
2060 <li>background processing (Peter Friese)</li>
2061 <li>internationalization, Japanese localization (Takashi
2062 Okamoto)</li>
2063 </ul>
2064 </li>
2065 <li>findbugs <tt>-onlyAnalyze</tt> option now works on windows
2066 platforms
2067 </li>
2068 <li>mineBugHistory <tt>-noTabs</tt> option for better
2069 alignment of output columns
2070 </li>
2071 <li>filterBugs <tt>-fixed</tt> option (also: will now
2072 recognize the most recent version string)
2073 </li>
2074 <li>XML output includes running time and memory usage data</li>
2075 <li>miscellaneous minor corrections to the manual</li>
2076 <li>better bytecode analysis of the <tt>iinc</tt> instruction
2077 </li>
2078 <li>fix bug in null pointer analysis</li>
2079 <li>improved catch block heuristics</li>
2080 <li>some type analysis tweaks</li>
2081 <li>Bug priority changes
2082 <ul>
2083 <li>DumbMethodInvocations.java: decrease priority of
2084 hard-coded <tt>/tmp</tt> filenames
2085 </li>
2086 <li>ComparatorIdiom.java: decrease priority of
2087 non-serializable anonymous comparators</li>
2088 <li>FindSqlInjection.java: decrease priority of appending a
2089 constant or a static</li>
2090 </ul>
2091 </li>
2092 <li>Updated bug explanations
2093 <ul>
2094 <li>NM_VERY_CONFUSING (Dave Brosius)</li>
2095 </ul>
2096 </li>
2097 <li>Updated test case files
2098 <ul>
2099 <li>BadStoreOfNonSerializableObject.java</li>
2100 <li>BadRandomInt.java</li>
2101 <li>TestFieldAnnotations.java</li>
2102 <li>UseInitCause.java</li>
2103 <li>SqlInjection.java</li>
2104 <li>ArrayEquality.java</li>
2105 <li>BadIntegerOperations.java</li>
2106 <li>Pilhuhn.java</li>
2107 <li>InstanceOf.java</li>
2108 <li>SwitchFallthrough.java (Dave Brosius)</li>
2109 </ul>
2110 </li>
2111 <li>fix URL decoding bug when running under Java Web Start
2112 (Dave Brosius)</li>
2113 <li>distribution includes <tt>project.xml</tt> file for
2114 NetBeans
2115 </li>
2116 </ul>
2117
2118 <p>Changes since version 0.9.4:</p>
2119 <ul>
2120 <li>New detectors
2121 <ul>
2122 <li>VarArgsProblems.java</li>
2123 <li>FindSqlInjection.java: now enabled</li>
2124 <li>ComparatorIdiom.java: comparators usually implement
2125 serializable</li>
2126 <li>Naming.java: detect methods not overridden due to
2127 eponymously typed args from different packages</li>
2128 </ul>
2129 </li>
2130 <li>Updated detectors
2131 <ul>
2132 <li>SwitchFallthrough.java: surpress some false positives</li>
2133 <li>DuplicateBranches.java: surpress some false positives</li>
2134 <li>IteratorIdioms.java: surpress some false positives</li>
2135 <li>FindHEmismatch.java: surpress some false positives</li>
2136 <li>QuestionableBooleanAssignment.java: finds more cases of
2137 <tt>if (b=true)</tt> ilk
2138 </li>
2139 <li>DumbMethods.java: detect int remainder by 1, delayed gc
2140 errors</li>
2141 <li>SerializableIdiom.java: detect store of nonserializable
2142 object into field of serializable class</li>
2143 <li>FindNullDeref.java: fix potential exception</li>
2144 <li>IsNullValue.java: fix potential exception</li>
2145 <li>MultithreadedInstanceAccess.java: fix potential
2146 exception</li>
2147 <li>PreferZeroLengthArrays.java: flag the method, not the
2148 line</li>
2149 </ul>
2150 </li>
2151 <li>Remove some inadvertent dependencies on JDK 1.5</li>
2152 <li>Sort order should be more consistent</li>
2153 <li>XML output changes
2154 <ul>
2155 <li>Option to sort XML bug output</li>
2156 <li>Now contains instance IDs</li>
2157 <li>uid no longer missing (was causing problems with fancy
2158 HTML output)</li>
2159 <li>Typo fixed</li>
2160 </ul>
2161 </li>
2162 <li>Internal changes to track source files, <tt>-sourceInfo</tt>
2163 option
2164 </li>
2165 <li>Bug matching: first try exact bug pattern matching, option
2166 to compare priorities, option to disable package moves</li>
2167 <li>Architecture documentation in <tt>design/architecture</tt>
2168 </li>
2169 <li>Test cases move into their own CVS project</li>
2170 <li>Don't report warnings that occur outside the analyzed
2171 classes</li>
2172 <li>Fixes to the build.xml files</li>
2173 <li>Better handling of @CheckReturnValue and @CheckForNull
2174 annotations (also, some additional methods searched for check
2175 return value and check for null)</li>
2176 <li>Fixed some stream-closing bugs (one by <tt>z-fb-user</tt>/Dave
2177 Brosius)
2178 </li>
2179 <li>Bug priority changes
2180 <ul>
2181 <li>increase priority of ignoring return value of
2182 java.sql.Connection methods</li>
2183 <li>increase priority of comparing classes like Integer
2184 using <tt>==</tt>
2185 </li>
2186 <li>decrease priority of IT_NO_SUCH_ELEMENT if we see any
2187 call to <tt>next()</tt>
2188 </li>
2189 <li>tweak priority of NM_METHOD_CONSTRUCTOR_CONFUSION</li>
2190 <li>decrease priority of RV_RETURN_VALUE_IGNORED for an
2191 inherited annotation that doesn't return same type as class</li>
2192 </ul>
2193 </li>
2194 <li>Updated bug explanations
2195 <ul>
2196 <li>RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE</li>
2197 <li>DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED</li>
2198 <li>IMA_INEFFICIENT_MEMBER_ACCESS (Dave Brosius)</li>
2199 <li>some Japanese improvements to messages_ja.xml ( <tt>ruimo</tt>)
2200 </li>
2201 <li>some German improvements to findbugs_de.properties (Dave
2202 Brosius, <tt>dvholten</tt>)
2203 </li>
2204 </ul>
2205 </li>
2206 <li>Updated test case files
2207 <ul>
2208 <li>BadIntegerOperations.java</li>
2209 <li>SecondKaboom.java</li>
2210 <li>OpenDatabase.java (Dave Brosius)</li>
2211 <li>FindOpenStream.java (Dave Brosius)</li>
2212 <li>BadRandomInt.java</li>
2213 </ul>
2214 </li>
2215 <li>Source-lines info maintained for methods (handy for
2216 abstract and native methods)</li>
2217 <li>Remove surrounding opcodes from source line annotations</li>
2218 <li>Better error when can't read file</li>
2219 <li>Swing GUI: removed console pane from FindBugsFrame, fix
2220 missing classes bug</li>
2221 <li>Fixes to OpcodeStack.java</li>
2222 <li>Detectors may attach a custom value to an OpcodeStack.Item
2223 (Dave Brosius)</li>
2224 <li>Filter.java: ability to add text messages to XML output,
2225 fix bug with <tt>-withMessages</tt>
2226 </li>
2227 <li>SourceInfoMap supports ranges of source lines</li>
2228 <li>Ant task supports the <tt>timestampNow</tt> attribute
2229 </li>
2230 </ul>
2231
2232 <p>Changes since version 0.9.3:</p>
2233 <ul>
2234 <li>Substantial rework of datamining code</li>
2235 <li>Removed bogus warnings about await on things other than
2236 Condition not being in a loop</li>
2237 <li>Fixed bug in OpcodeStack handling of dup2 of long/double
2238 values</li>
2239 <li>Don't report array types as missing classes</li>
2240 <li>Adjustment of some warnings on ignored return values</li>
2241 <li>Added thread safety annotations from Java Concurrency in
2242 Practice (no detectors written for these yet)</li>
2243 <li>Added annotation for methods that, if overridden, should
2244 be invoked by overriding methods via a call to super</li>
2245 <li>Updated -html:fancy.xsl (Etienne Giraudy)</li>
2246 </ul>
2247
2248 <p>Note: there was no version 0.9.2</p>
2249
2250 <p>Changes since version 0.9.1:</p>
2251 <ul>
2252 <!-- New detectors -->
2253 <li>Embellish USM to find abstract methods that implement an
2254 interface method (Dave Brosius)</li>
2255 <li>New detector to find stores of literal booleans inside if
2256 or while expressions (Dave Brosius)</li>
2257 <li>New style detector to find final classes that declare
2258 protected fields (Dave Brosius)</li>
2259 <li>New detector to find subclass methods that simply forward,
2260 verbatim, to the super class (Dave Brosius)</li>
2261 <li>Detector to find instances where code is attempting to
2262 write an object out via an implementation of DataOutput, but the
2263 object is not guaranteed to be Serializable (Jon Christiansen,
2264 Bill Pugh)</li>
2265
2266 <!-- Feature enhancements -->
2267 <li>Large (35%) analysis speedup (Bill Pugh)</li>
2268 <li>Add line numbers to Swing GUI code panel (Dave Brosius)</li>
2269 <li>Added effort options to Swing GUI (Dave Brosius)</li>
2270 <li>Add ability to specify bugs file to open from command line
2271 for GUI version, through -loadbugs (Phillip Martin)</li>
2272 <li>New stylesheet for generating HTML: use option <tt>-html:plain.xsl</tt>
2273 (Chris Nappin)
2274 </li>
2275 <li>New stylesheet for generating HTML: use option <tt>-html:fancy.xsl</tt>
2276 (Etienne Giraudy)
2277 </li>
2278 <li>Updated Japanese bug message translations (Shisei Hanai)</li>
2279
2280 <!-- Bug fixes -->
2281 <li>XHTML compliance fixes for bug details (Etienne Giraudy)</li>
2282 <li>Various detector fixes (Shisei Hanai)</li>
2283 <li>Fixed bugs in the project preferences dialog int the
2284 Eclipse plugin (Takashi Okamoto, Thomas Einwaller)</li>
2285 <li>Lowered priority of analysis thread in Swing GUI (David
2286 Hovemeyer, suggested by Shisei Hanai and Jeffrey W. Badorek)</li>
2287 <li>Fixed EclipsePlugin to correctly pick up auxclasspath
2288 entries (Jon Christiansen)</li>
2289 </ul>
2290
2291 <p>Changes since version 0.9.0:</p>
2292 <ul>
2293 <li>Fixed dependence on JRE 1.5: all features should work on
2294 JRE 1.4 again</li>
2295 <li>Fixed -effort command line option handling for Swing GUI</li>
2296 <li>Fixed conserveSpace and workHard attributes int Ant task</li>
2297 <li>Added support for effort attribute in Ant task</li>
2298 </ul>
2299
2300 <p>Changes since version 0.8.8:</p>
2301 <ul>
2302 <!-- New detectors and bug patterns -->
2303 <li>XMLFactoryBypass detector to find direct allocation of xml
2304 class implementations (Dave Brosius)</li>
2305 <li>InefficientMemberAccess detector to find accesses to
2306 owning class private members (Dave Brosius)</li>
2307 <li>DuplicateBranches detector checks switch statements too
2308 (Dave Brosius)</li>
2309
2310 <!-- Feature enhancements -->
2311 <li>FindBugs available from findbugs.sourceforge.net as Java
2312 Web Start application (Dave Brosius)</li>
2313 <li>Updated Japanese bug message translations (Shisei Hanai)</li>
2314 <li>Improved bug detail message for covariant equals() (Shisei
2315 Hanai)</li>
2316 <li>Modeling of instanceof checks is now enabled by default,
2317 making the bad cast detector much more useful (Bill Pugh, David
2318 Hovemeyer)</li>
2319 <li>Support for detector ordering constraints in plugin
2320 descriptor (David Hovemeyer)</li>
2321 <li>Simpler option to control analysis effort: -effort: <i>value</i>,
2322 where <i>value</i> is one of <code> min </code> , <code>
2323 default </code> , or <code> max </code> (David Hovemeyer)
2324 </li>
2325 <li>Using -effort:max, FindNullDeref checks for null arguments
2326 passed to methods which dereference them unconditionally (David
2327 Hovemeyer)</li>
2328 <li>FindNullDeref checks @Null and @NonNull annotations for
2329 parameters and return values (David Hovemeyer)</li>
2330
2331 <!-- Bug fixes -->
2332 </ul>
2333
2334 <p>Changes since version 0.8.7:</p>
2335
2336 <ul>
2337 <!-- New detectors and bug patterns -->
2338 <li>New detector to find duplicate code in if/else statements
2339 (Dave Brosius)</li>
2340 <li>Look for calls to wait() on Condition objects (David
2341 Hovemeyer)</li>
2342 <li>Look for java.util.concurrent.Lock objects not released on
2343 every path out of method (David Hovemeyer)</li>
2344 <li>Look for calls to Thread.sleep() with a lock held (David
2345 Hovemeyer)</li>
2346 <li>More accurate detection of impossible casts (Bill Pugh,
2347 David Hovemeyer)</li>
2348
2349 <!-- Feature enhancements -->
2350 <li>Saved XML now contains project statistics (Jay Dunning)</li>
2351 <li>Filter files can select by bug pattern type and warning
2352 priority (David Hovemeyer)</li>
2353
2354 <!-- Bug fixes -->
2355 <li>Restored some files inadvertently omitted from previous
2356 release (Rohan Lloyd, David Hovemeyer)</li>
2357 <li>Make sure detectors requiring JDK 1.5 runtime classes are
2358 only executed if those classes are available (David Hovemeyer)</li>
2359 <li>Don't display analysis error dialog unless there is really
2360 an error (David Hovemeyer)</li>
2361 <li>Updated and expanded French translations of bug patterns
2362 and Swing GUI (Olivier Parent)</li>
2363 <li>Fixed invalid character encoding in German Swing GUI
2364 translation (Olivier Parent)</li>
2365 <li>Fix locale used for date format in project stats (K.
2366 Hashimoto)</li>
2367 <li>Fixed LongDescription elements in xml:withMessages output
2368 format (K. Hashimoto)</li>
2369 </ul>
2370
2371 <p>Changes since version 0.8.6:</p>
2372
2373 <ul>
2374 <!-- new detectors -->
2375 <li>Extend Naming detector to look for classes that are named
2376 XXXException but that are not Exceptions (Dave Brosius)</li>
2377 <li>New detector to find classes that expose semaphores in the
2378 public implementation through the 'this' reference. (Dave Brosius)
2379 </li>
2380 <li>New Style detector to find Struts Action/Servlet derived
2381 classes that reference instance member variable not in
2382 synchronized blocks. (Dave Brosius)</li>
2383 <li>New Style detector to find classes that declare
2384 implementation of interfaces that are already implemented by super
2385 classes (Dave Brosius)</li>
2386 <li>New Style detector to find circular dependencies between
2387 classes (Dave Brosius)</li>
2388 <li>New Style detector to find unnecessary math on constants
2389 (Dave Brosius)</li>
2390 <li>New detector to find equality comparisons using floating
2391 point math (Jay Dunning)</li>
2392 <li>New faster detector to find local self assignments (Bill
2393 Pugh)</li>
2394 <li>New detector to find infinite recursive loops (Bill Pugh)
2395 </li>
2396 <li>New detector to find for loops with an incorrect increment
2397 (Bill Pugh)</li>
2398 <li>New detector to find suspicious uses of
2399 BufferedReader.readLine() and String.indexOf() (Bill Pugh)</li>
2400 <li>New detector to find suspicious integer to double casts
2401 (David Hovemeyer, Bill Pugh)</li>
2402 <li>New detector to find invalid regular expression patterns
2403 (Bill Pugh)</li>
2404 <li>New detector to find Bloch/Gafter Java puzzlers (Bill
2405 Pugh)</li>
2406
2407 <!-- feature enhancements -->
2408 <li>New system property to suppress reporting of DLS based on
2409 local variable name (Glenn Boysko)</li>
2410 <li>Enhancements to configuration dialog in Eclipse plugin,
2411 allow for saving enabled detectors in Eclipse projects (Phil
2412 Crosby)</li>
2413 <li>Sortable columns in detector dialog (Dave Brosius)</li>
2414 <li>New tab in gui for showing bugs grouped by category (Dave
2415 Brosius)</li>
2416 <li>Improved German translation of Swing GUI (Thomas Kuehne)</li>
2417 <li>Improved source file reporting in Emacs output format (Len
2418 Trigg)</li>
2419 <li>Improvements to redundant null comparison detector (Bill
2420 Pugh)</li>
2421 <li>Localization of run analysis and analysis error dialogs in
2422 Swing GUI (K. Hashimoto)</li>
2423
2424 <!-- Bug fixes -->
2425 <li>Don't scan equals methods in FindHEMismatch if code is
2426 native (Greg Bentz)</li>
2427 <li>French translation fixes (David Cotton)</li>
2428 <li>Internationalization report fixes (K. Hashimoto)</li>
2429 <li>Japanese translations updates (SHISEI Hanai)</li>
2430 </ul>
2431
2432 <p>Changes since version 0.8.5:</p>
2433 <ul>
2434 <!-- new detectors -->
2435 <li>New detector to find catch blocks that may inadvertently
2436 catch runtime exceptions (Brian Goetz)</li>
2437 <li>New detector to find objects that are instantiated based
2438 on classes that only have static methods and fields, using the
2439 synthesized constructor (Dave Brosius)</li>
2440 <li>New detector to find calls to Thread.interrupted() in a
2441 non static context, and especially with non currentThread()
2442 threads (Dave Brosius)</li>
2443 <li>New detector to find calls to equals() methods that use
2444 Object's version. (Dave Brosius)</li>
2445 <li>New detector to find Applets that call methods in the
2446 constructor refering to the AppletStub (Dave Brosius)</li>
2447 <li>New detector to find some cases of infinite recursion
2448 (Bill Pugh)</li>
2449 <li>New detector to find dead stores to local variables (David
2450 Hovemeyer, Bill Pugh)</li>
2451 <li>Extend Dumb Method detector for toUpperCase(),
2452 toLowerCase() without a locale, new Integer(1).toString(), new
2453 XXX().getClass(), and new Thread() without a run implementation
2454 (Dave Brosius) <!-- feature enhancements -->
2455 </li>
2456 <li>Ant task supports "errorProperty" attribute, which sets an
2457 Ant property to "true" if an error occurs running FindBugs
2458 (Michael Tamm)</li>
2459 <li>Eclipse plugin allows filtering of warnings by bug
2460 category, priority (David Hovemeyer)</li>
2461 <li>Swing GUI allows filtering of warnings by bug category
2462 (David Hovemeyer)</li>
2463 <li>Ability to annotate methods using Java 1.5 annotations
2464 that suppress FindBugs warnings (Bill Pugh)</li>
2465 <li>New -adjustExperimental for lowering priority of
2466 BugPatterns that are experimental (Dave Brosius)</li>
2467 <li>Allow for command line options 'files' using the @ symbol
2468 (David Hovemeyer)</li>
2469 <li>New -adjustPriority command line option to for adjusting
2470 bug priorites (David Hovemeyer)</li>
2471 <li>Added an Edit menu (cut/copy/paste) to Swing GUI (Dave
2472 Brosius)</li>
2473 <li>French translation supplied (David Cotton) <!-- Bug fixes -->
2474 </li>
2475 </ul>
2476
2477 <p>Changes since version 0.8.4:</p>
2478 <ul>
2479 <!-- new detectors -->
2480 <li>New detector for volatile references to arrays (Bill Pugh)
2481 </li>
2482 <li>New detector to find instanceof usage where inheritance
2483 can be determined statically (Dave Brosius)</li>
2484 <li>New detector to find ResultSet.getXXX updateXXX calls
2485 using index 0 (Dave Brosius)</li>
2486 <li>New detector to find empty zip or jar entries (Bill Pugh)
2487
2488 <!-- feature enhancements -->
2489 </li>
2490 <li>HTML output generation using built-in XSLT stylesheet or
2491 user-defined stylesheet (David Hovemeyer)</li>
2492 <li>Allow URLs to be specified to analyze zip/jar files, local
2493 directories, and single classfiles (David Hovemeyer)</li>
2494 <li>New command line option -onlyAnalyze restricts analysis to
2495 selected classes and packages without reducing accuracy (David
2496 Hovemeyer)</li>
2497 <li>Allow Swing GUI to show source code in jar files on
2498 Windows systems (Dave Brosius) <!-- Bug fixes -->
2499 </li>
2500 <li>Fix the Switch Fall Thru detector (Dave Brosius, David
2501 Hovemeyer, Bill Pugh)</li>
2502 <li>MacOS GUI fixes (Rohan Lloyd)</li>
2503 <li>Fix false positive in BOA in case where method is
2504 correctly and 'incorrectly' overridden (Dave Brosius)</li>
2505 <li>Fixed memory blowup when analyzing methods which access a
2506 large number of fields (David Hovemeyer)</li>
2507 </ul>
2508
2509 <p>Changes since version 0.8.3:</p>
2510 <ul>
2511 <li>Initial and preliminary localization of the Swing
2512 GUI.&nbsp; Translations by:
2513 <ul>
2514 <li>German - Peter D. Stout, Holger Stenzhorn</li>
2515 <li>Finnish - Juha Knuutila</li>
2516 <li>Estonian - Tanel Lebedev</li>
2517 <li>Japanese - Hanai Shisei</li>
2518 </ul>
2519 </li>
2520 <li>Eliminated debug print statements inadvertently left
2521 enabled</li>
2522 <li>Reverted some changes in the open stream detector: this
2523 should fix some false positives that were introduced in the
2524 previous release</li>
2525 <li>Fixed a couple missing class reports</li>
2526 </ul>
2527
2528 <p>Changes since version 0.8.2:</p>
2529 <ul>
2530
2531 <!-- New detectors -->
2532 <li>New detector to find improperly overridden GUI Adapter
2533 classes (Dave Brosius)</li>
2534 <li>New detector to find improperly setup JUnit TestCases
2535 (Dave Brosius)</li>
2536 <li>New detector to find variables that mask class level
2537 fields (Dave Brosius)</li>
2538 <li>New detector to find comparisons of values computed with
2539 bitwise operators that always yield the same result (Tom Truscott)
2540 </li>
2541 <li>New detector to find unsafe getClass().getResource() calls
2542 (Bill Pugh)</li>
2543 <li>New detector to find GUI changes not in GUI thread but in
2544 static main (Bill Pugh)</li>
2545 <li>New detector to find calls to Collection.toArray() with
2546 zero-length array argument; it is more efficient to pass an array
2547 the size of the collection, which can be populated and returned as
2548 the result (Dave Brosius) <!-- Analysis improvements -->
2549 </li>
2550 <li>Better suppression of false warnings in various detectors
2551 (Bill Pugh, David Hovemeyer)</li>
2552 <li>Enhancement to ReadReturnShouldBeChecked detector for
2553 skip() (Dave Brosius)</li>
2554 <li>Enhancement to DumbMethods detector (Dave Brosius)</li>
2555 <li>Open stream detector does not report wrappers of streams
2556 passed as method parameters (David Hovemeyer) <!-- Feature enhancements -->
2557 </li>
2558 <li>Cancel confirmation dialog in Swing GUI (Pete Angstadt)</li>
2559 <li>Better relative path saving in Project file (Dave Brosius)
2560 </li>
2561 <li>Detector Priority in GUI is now saved in prefs file (Dave
2562 Brosius)</li>
2563 <li>Controls in GUI to reorder source and classpath entries,
2564 and ability to flip between Project details and bugs pages (Dave
2565 Brosius)</li>
2566 <li>In Swing GUI, analysis error dialog supports "Select All"
2567 and "Copy" operations for easy generation of error reports (Dave
2568 Brosius)</li>
2569 <li>Complete translation of bug descriptions and messages into
2570 Japanese (Hanai Shisei) <!-- Bug fixes -->
2571 </li>
2572 <li>Fixed bug in DroppedException detector (Dave Brosius) <!-- Development stuff -->
2573 </li>
2574 <li>The source distribution defaults to using JDK 1.5 javac to
2575 compile, but support for compiling with JSR-14 prototype is still
2576 supported</li>
2577 </ul>
2578
2579 <p>Changes since version 0.8.1:</p>
2580 <ul>
2581 <li>Fixed a critical ClassCastException bug (triggered if the
2582 -workHard option was used, and an exception type was merged with
2583 an array type during type inference)</li>
2584 </ul>
2585
2586 <p>Changes since version 0.8.0:</p>
2587 <ul>
2588 <li>Disabled SwitchFallthrough detector to work around
2589 NullPointerExceptions</li>
2590 <li>Added some additional false positive suppression
2591 heuristics</li>
2592 </ul>
2593
2594 <p>Also, two contributors to the 0.8.0 release were
2595 inadvertently left out of the credits:</p>
2596 <ul>
2597 <li>Pete Angstadt fixed several problems in the Swing GUI</li>
2598 <li>Francis Lalonde provided a task resource file for the
2599 FindBugs Ant task</li>
2600 </ul>
2601
2602 <p>Changes since version 0.7.4:</p>
2603 <ul>
2604 <li>New detector to look for uses of "+" operator to
2605 concatenate String objects in a loop (Dave Brosius)</li>
2606 <li>Reference comparison detector looks for places where the
2607 argument passed to the equals(Object) method isn't the same type
2608 as the receiver object</li>
2609 <li>Better suppression of false warnings in many detectors</li>
2610 <li>Many improvements to Eclipse plugin (Andrey Loskutov,
2611 Peter Friese)</li>
2612 <li>Fixed problem with building Eclipse plugin on Windows
2613 (Thomas Klaeger)</li>
2614 <li>Open stream detector looks for unclosed PreparedStatement
2615 objects (Thomas Klaeger, Rohan Lloyd)</li>
2616 <li>Fix for open stream detector: it wasn't detecting close()
2617 methods called through an invokeinterface instruction (Thomas
2618 Klaeger)</li>
2619 <li>Refactoring of visitor classes to enforce use of accessors
2620 for visited class features (Brian Goetz)</li>
2621 </ul>
2622
2623 <p>Changes since version 0.7.3:</p>
2624 <ul>
2625 <li>Experimental modification of open stream detector to look
2626 for non-escaping JDBC resources (connections and statements) that
2627 aren't closed on all paths out of method</li>
2628 <li>Eclipse plugin fixed so it compiles and runs on Eclipse
2629 2.1.x (Peter Friese)</li>
2630 <li>Option to Swing GUI and command line to generate project
2631 file using relative paths for archives, source directories, and
2632 aux classpath entries (Dave Brosius)</li>
2633 <li>Improvements to findbugs.bat script for launching FindBugs
2634 on Windows (Dave Brosius)</li>
2635 <li>Updated Japanese message translations (Hiroshi Okugawa)</li>
2636 <li>Uncalled private methods are now reported as low priority,
2637 unless they have the same name as another method in the class
2638 (which is more likely to indicate an actual bug)</li>
2639 <li>Added some missing data in the bug messages XML files</li>
2640 <li>Fixed some problems building from source on Windows
2641 systems</li>
2642 <li>Various minor bug fixes</li>
2643 </ul>
2644
2645 <p>Changes since version 0.7.2:</p>
2646 <ul>
2647 <li>Enhanced Eclipse plugin, which displays the detailed bug
2648 description in a view (Phil Crosby)</li>
2649 <li>Various tweaks to existing detectors to reduce false
2650 warnings</li>
2651 <li>New command line option <code> -workHard </code> enables
2652 pruning of infeasible or unlikely exception edges, which results
2653 in better accuracy in the open stream detector, at the expense of
2654 a 30%-100% slowdown
2655 </li>
2656 <li>New website and HTML documentation design</li>
2657 <li>Documentation includes an HTML document with descriptions
2658 of all bug patterns reported by FindBugs</li>
2659 <li>Web page has a link to a <a
2660 href="http://www.simeji.com/findbugs/doc/manual_ja/index.html">Japanese
2661 translation</a> of the FindBugs manual, contributed by Hiroshi
2662 Okugawa
2663 </li>
2664 <li>Changed the Inconsistent Synchronization detector so that
2665 fields synchronized 50% of the time (or more) are reported as
2666 medium priority bugs (previously they were reported as low)</li>
2667 <li>New detector to find code that catches
2668 IllegalMonitorStateException</li>
2669 <li>New detector to find private methods that are never called
2670 </li>
2671 <li>New detector to find suspicious uses of
2672 non-short-circuiting boolean operators ( <code> &amp; </code> and
2673 <code> | </code> , rather than <code> &amp;&amp; </code> and <code>
2674 || </code> )
2675 </li>
2676 </ul>
2677
2678 <p>Changes since version 0.7.1:</p>
2679 <ul>
2680 <li>Incorporated patched version of BCEL, which allows classes
2681 compiled with JDK 1.5.0 beta to be analyzed</li>
2682 <li>Fixed some bugs related to lookups of array classes</li>
2683 <li>Fixed bug that prevented GUI from loading XML result files
2684 when running under JDK 1.5.0 beta</li>
2685 <li>Added new experimental bug detector, LazyInit, which looks
2686 for potentially buggy lazy initializations of static fields</li>
2687 <li>Because of long filenames, switched to distributing the
2688 source archive as a zip file rather than a tar file</li>
2689 <li>The 0.7.1 source tarfile was botched - 0.7.2 has a valid
2690 source archive</li>
2691 <li>Fixed some problems in the Ant build script</li>
2692 <li>Fixed NullPointerException when checking Class-Path
2693 attribute for Jar files without manifests</li>
2694 <li>Generate version numbers for the core and UI Eclipse
2695 plugins using the Version class; all version numbers are now in a
2696 common location</li>
2697 </ul>
2698
2699 <p>Changes since version 0.7.0:</p>
2700 <ul>
2701 <li>Eclipse plugin (contributed by Peter Friese)</li>
2702 <li>Source package structure rearranged: all source (other
2703 than Eclipse plugin UI) is in the edu.umd.cs.findbugs package, or
2704 a subpackage</li>
2705 <li>Class-Path attributes of manifests of analyzed jar files
2706 are used to set the aux classpath automatically (Peter D. Stout)</li>
2707 <li>GUI starts in directory specified by user.home property
2708 (Peter D. Stout)</li>
2709 <li>Added -project option to GUI (Mikko T.)</li>
2710 <li>Added -look:{plastic,gtk,native} option to GUI, for
2711 setting look and feel (Mikko T.)</li>
2712 <li>Fixed DataflowAnalysisException in inconsistent
2713 synchronization detector</li>
2714 <li>Ant task supports failOnError parameter (Rohan Lloyd)</li>
2715 <li>Serializable class warnings are downgraded to low priority
2716 for GUI classes</li>
2717 <li>MWN detector will only report calls to wait(), notify(),
2718 and notifyAll() methods that have the correct signature</li>
2719 <li>FindBugs works with latest CVS version of BCEL</li>
2720 <li>Zip and Jar files may be added to the source path</li>
2721 <li>The GUI will automatically find source files residing in
2722 analyzed Zip or Jar files</li>
2723 </ul>
2724
2725 <p>Note that the version number jumped from 0.6.6 to 0.6.9;
2726 there were no 0.6.7 or 0.6.8 releases.</p>
2727 <p>Changes since version 0.6.9:</p>
2728 <ul>
2729 <li>Added -conserveSpace option to reduce memory use at the
2730 expense of analysis precision</li>
2731 <li>Bug fixes in findbugs.bat script: JAVA_HOME handling,
2732 autodetection of FINDBUGS_HOME, missing output with -textui</li>
2733 <li>Fixed NullPointerException when a missing class is
2734 encountered</li>
2735 </ul>
2736
2737 <p>Changes since version 0.6.6:</p>
2738 <ul>
2739 <li>The null pointer dereference detector is more powerful</li>
2740 <li>Significantly improved heuristics and bug fixes in
2741 inconsistent synchronization detector</li>
2742 <li>Improved heuristics in open stream and dropped exception
2743 detectors; fewer false positives should be reported</li>
2744 <li>Save HTML summary in XML results files, rather than
2745 recomputing; this makes loading results in GUI much faster</li>
2746 <li>Report at most one String comparison using == or != per
2747 method</li>
2748 <li>The findbugs.bat script on Windows autodetects
2749 FINDBUGS_HOME, and doesn't open a DOS window when launching the
2750 GUI (contributed by TJSB)</li>
2751 <li>Emacs reporting format (contributed by David Li)</li>
2752 <li>Various bug fixes</li>
2753 </ul>
2754
2755 <p>Changes since 0.6.5:</p>
2756 <ul>
2757 <li>Rewritten inconsistent synchronization detector; accuracy
2758 is significantly improved, and bug reports are prioritized</li>
2759 <li>New detector to find self assignment (x=x) of local
2760 variables (suggested by Jeff Martin)</li>
2761 <li>New detector to find calls to wait(), notify(), and
2762 notifyAll() on an object which is not obviously locked</li>
2763 <li>Open stream detector now reports Readers and Writers</li>
2764 <li>Fixed bug in finalizer idioms detector which caused
2765 spurious warnings about failure to call super.finalize() (reported
2766 by Jim Menard)</li>
2767 <li>Fixed bug where output stream was not closed using non-XML
2768 output (reported by Sigiswald Madou)</li>
2769 <li>Fixed corrupted HTML bug detail message (reported by
2770 Trevor Harmon)</li>
2771 </ul>
2772
2773 <p>Changes since version 0.6.4:</p>
2774 <ul>
2775 <li>For redundant comparison of reference values, fixed false
2776 positives resulting from duplication of code in finally blocks</li>
2777 <li>Fixed false positives resulting from wrapped byte array
2778 streams left open</li>
2779 <li>Fixed bug in Ant task preventing output file from working
2780 properly if a relative path was used</li>
2781 </ul>
2782
2783 <p>Changes since version 0.6.3:</p>
2784 <ul>
2785 <li>Fixed bug in Ant task where output would be corrupted, and
2786 added a <code> timeout </code> attribute
2787 </li>
2788 <li>Added -outputFile option to text UI, for explicitly
2789 specifying an output file</li>
2790 <li>GUI has a summary window, for statistics about overall bug
2791 densities (contributed by Mike Fagan)</li>
2792 <li>Find redundant comparisons of reference values</li>
2793 <li>More accurate detection of Strings compared with == and !=
2794 operators</li>
2795 <li>Detection of other reference types which should generally
2796 not be compared with == and != operators; Boolean, Integer, etc.</li>
2797 <li>Find non-transient non-serializable instance fields in
2798 Serializable classes</li>
2799 <li>Source code may be compiled with latest early access
2800 generics-enabled javac (version 2.2)</li>
2801 </ul>
2802
2803 <p>Changes since version 0.6.2:</p>
2804 <ul>
2805 <li>GUI supports filtering bugs by priority</li>
2806 <li>Ant task rewritten; supports all functionality offered by
2807 Text UI (contributed by Mike Fagan)</li>
2808 <li>Ant task is fully documented in the manual</li>
2809 <li>Classes in nested archives are analyzed; this allows full
2810 support for analyzing .ear and .war files (contributed by Mike
2811 Fagan)</li>
2812 <li>DepthFirstSearch changed to use non-recursive
2813 implementation; this should fix the StackOverflowErrors that
2814 several users reported</li>
2815 <li>Various minor bugfixes and improvements</li>
2816 </ul>
2817
2818 <p>Changes since version 0.6.1:</p>
2819 <ul>
2820 <li>New detector to look for useless control flow (suggested
2821 by Richard P. King and Mike Fagan)</li>
2822 <li>Look for places where return value of
2823 java.io.File.createNewFile() is ignored (suggested by Richard P.
2824 King)</li>
2825 <li>Fixed bug in resolution of source files (only the first
2826 source directory was searched)</li>
2827 <li>Fixed a NullPointerException in the bytecode pattern
2828 matching code</li>
2829 <li>Ant task supports project files (contributed by Mike
2830 Fagan)</li>
2831 <li>Unix findbugs script honors the <code> JAVA_HOME </code>
2832 environment variable (contributed by Pedro Morais)
2833 </li>
2834 <li>Allow .war and .ear files to be analyzed</li>
2835 </ul>
2836
2837 <p>Changes since version 0.6.0:</p>
2838 <ul>
2839 <li>New bug pattern detector which looks for places where a
2840 null pointer might be dereferenced</li>
2841 <li>New bug pattern detector which looks for IO streams that
2842 are opened, do not escape the method, and are not closed on all
2843 paths out of the method</li>
2844 <li>New bug pattern detector to find methods that can return
2845 null instead of a zero-length array</li>
2846 <li>New bug pattern detector to find places where the == or !=
2847 operators are used to compare String objects</li>
2848 <li>Command line interface can save bugs as XML</li>
2849 <li>GUI can save bugs to and load bugs from XML</li>
2850 <li>An "Annotations" window in the GUI allows the user to add
2851 textual annotations to bug reports; these annotations are
2852 preserved when bugs are saved as XML</li>
2853 <li>In this release, the Japanese bug summary translations by
2854 Germano Leichsenring are really included (they were inadvertently
2855 omitted in the previous release)</li>
2856 <li>Completely rewrote the control flow graph builder,
2857 hopefully for the last time</li>
2858 <li>Simplified implementation of control flow graphs, which
2859 should reduce memory use and possibly improve performance</li>
2860 <li>Improvements to command line interface (list bug
2861 priorities, filter by priority, specify aux classpath, specify
2862 project to analyze)</li>
2863 <li>Various bug fixes and enhancements</li>
2864 </ul>
2865
2866 <p>Changes since version 0.5.4</p>
2867 <ul>
2868 <li>Added an <a href="http://ant.apache.org/">Ant</a> task for
2869 FindBugs, contributed by Mike Fagan.
2870 </li>
2871 <li>Added a GUI dialog which allows individual bug pattern
2872 detectors to be enabled or disabled.&nbsp; Disabling certain slow
2873 detectors can greatly speed up analysis of large programs, at the
2874 expense of reducing the number of potential bugs found.</li>
2875 <li>Added a new detector for finding improperly ignored return
2876 values for methods such as <code> String.trim() </code> .&nbsp;
2877 Suggested by Andreas Mandel.
2878 </li>
2879 <li>Japanese translations of the bug summaries, contributed by
2880 Germano Leichsenring.</li>
2881 <li>Filtering of results is supported in command line
2882 interface. See the <a href="manual/index.html">FindBugs manual</a>
2883 for details.
2884 </li>
2885 <li>Added "byte code patterns", a general pattern matching
2886 infrastructure for bytecode instructions.&nbsp; This feature
2887 significantly reduces the complexity of implementing new bug
2888 pattern detectors.</li>
2889 <li>Enabled a new general dataflow analysis to track values in
2890 methods.</li>
2891 <li>Switched to new control-flow graph builder implementation.
2892 </li>
2893 </ul>
2894
2895 <p>Changes since version 0.5.3</p>
2896 <ul>
2897 <li>Fixed a bug in the script used to launch FindBugs on
2898 Windows platforms.</li>
2899 <li>Fixed crashes when analyzing class files without source
2900 line information.</li>
2901 <li>All major errors are reported using an error dialog; file
2902 not found errors are more informative.</li>
2903 <li>Minor GUI improvements.</li>
2904 </ul>
2905
2906 <p>Changes since version 0.5.2</p>
2907 <ul>
2908 <li>All of the source code and related files are in a single
2909 directory tree.</li>
2910 <li>Updated some of the detectors to produce source line
2911 information.</li>
2912 <li><a href="http://ant.apache.org/">Ant</a> build script and
2913 several GUI enhancements and fixes contributed by Mike Fagan.</li>
2914 <li>Converted to use a <a href="AddingDetectors.txt">plugin
2915 architecture</a> for loading bug detectors.
2916 </li>
2917 <li>Eliminated generics-related compiler warnings.</li>
2918 <li>More complete documentation has been added.</li>
2919 </ul>
2920
2921 <p>Changes since version 0.5.1:</p>
2922 <ul>
2923 <li>Fixed a large number of bugs in the BCEL Repository and
2924 FindBugs's use of the Repository.&nbsp; With these changes,
2925 FindBugs should <em>never</em> crash or otherwise misbehave
2926 because of Repository lookup failures.&nbsp; Because of these
2927 changes, you must use a modified version of <code> bcel.jar
2928 </code> with FindBugs.&nbsp; This jar file is included in the FindBugs
2929 0.5.2 binary release.&nbsp; A complete patch containing the <a
2930 href="http://faculty.ycp.edu/~dhovemey/bcel-30-April-2003.patch">modifications
2931 against the BCEL CVS main branch as of April 30, 2003</a> is also
2932 available.
2933 </li>
2934 <li>Implemented the "auxiliary classpath entry list".&nbsp;
2935 Aux classpath entries can be added to a project to provide classes
2936 that are referenced by the analyzed application, but should not
2937 themselves be analyzed.&nbsp; Having all referenced classes
2938 available allows FindBugs to produce more accurate results.</li>
2939 </ul>
2940
2941 <p>Changes since version 0.5.0:</p>
2942 <ul>
2943 <li>Many user interface bugs have been fixed.</li>
2944 <li>Upgraded to a recent CVS version of BCEL, with some bug
2945 fixes.&nbsp; This should prevent FindBugs from crashing when there
2946 is a failure to find a class on the classpath.</li>
2947 <li>Added support for Plastic look and feel from <a
2948 href="http://www.jgoodies.com/">jgoodies.com</a>.
2949 </li>
2950 <li>Major overhaul of infrastructure for doing dataflow
2951 analysis.</li>
2952 </ul> @HTML_FOOTER@
2953
2954 </td>
2955
2956 </tr>
2957 </table>
27552958
27562959 </body>
27572960
3434
3535 <h2><a name="q1">Q1: I'm getting java.lang.UnsupportedClassVersionError when I try to run FindBugs</a></h2>
3636
37 <p> FindBugs requires JRE 1.5.0 or later to run.&nbsp; If you use an earlier version,
37 <p> FindBugs requires JRE 1.7.0 or later to run.&nbsp; If you use an earlier version,
3838 you will see an exception error message similar to the following:
3939 <pre>
4040 Exception in thread "main" java.lang.UnsupportedClassVersionError:
41 edu/umd/cs/findbugs/gui/FindBugsFrame (Unsupported major.minor version 48.0)
41 edu/umd/cs/findbugs/gui/FindBugsFrame (Unsupported major.minor version 51.0)
4242 </pre>
43 The solution is to upgrade to JRE 1.5.0 or later.
43 The solution is to upgrade to JRE 1.7.0 or later.
4444
4545 <h2><a name="q2">Q2: When I click the "Find Bugs!" button, I get a NoSuchMethodError or VerifyError</a></h2>
4646
100100 <h2><a name="q3">Q3: FindBugs is running out of memory, or is taking a long time to finish</a></h2>
101101
102102 <p> In general, FindBugs requires lots of memory and a relatively
103 fast CPU. For large applications, 512M or more of heap space may be
104 required. By default, FindBugs allocates 256M of heap space.
103 fast CPU. For large applications, 1024M or more of heap space may be
104 required. By default, FindBugs allocates 768M of heap space.
105105 You can increase this using the <code>-maxHeap <i>n</i></code> option,
106106 where <i>n</i> is the number of megabytes of heap space to allocate.
107107
1818
1919 <h2>Import FindBugs code as Eclipse projects</h2>
2020
21 <p>The preferred way to get the FindBugs source code and create the patch is to use Eclipse + SVN.
21 <p>The preferred way to get the FindBugs source code and create the patch is to use Eclipse + Git.
2222 You can easily import FindBugs code into Eclipse by following the steps described
23 here: <a href="http://code.google.com/p/findbugs/source/browse/trunk/eclipsePlugin/doc/building_findbugsplugin.txt">Import Eclipse projects</a>
24 .
23 here: <a href="https://code.google.com/p/findbugs/source/browse/eclipsePlugin/doc/building_findbugsplugin.txt">Import Eclipse projects</a>
24 .
2525 </p>
2626
2727 <h2>Preparing a patch</h2>
2828
29 <p> The best way to
30 send an enhancement is to create a patch against the latest code
31 in the FindBugs Subversion repository
32 at <a href="http://findbugs.googlecode.com/svn/trunk/">http://findbugs.googlecode.com/svn/trunk/</a>
33 (those people who have been given commit priviledges should use
34 <a href="https://findbugs.googlecode.com/svn/trunk/">http<b>s</b>://findbugs.googlecode.com/svn/trunk/</a>).
35 </p>
36
37 <p>To create a patch from Eclipse, please right click the [findbugs] or [findBugsEclipsePlugin] project
38 and choose [Team | Create Patch...] context menu.
29 <p> The best way to create a patch is to clone FindBugs Git repository here:
30 <a href="http://code.google.com/p/findbugs/source/clones">http://code.google.com/p/findbugs/source/clones</a>,
31 commit changes with descriptive commit messages (small commits are better) and
32 create a new <a href="https://sourceforge.net/p/findbugs/bugs/">bug</a>
33 or <a href="https://sourceforge.net/p/findbugs/feature-requests/">feature request</a>
34 with the "[patch]" prefix in the title and link to your repository in the description.
3935 </p>
4036
4137 <p> Please follow these guidelines when preparing your patch:</p>
4238 <ul>
4339 <li> <b>Use the same indentation style as the source file(s) you
44 are modifying</b>.&nbsp; In particular, please use tabs (not spaces)
45 to indent your code; one tab per indent level.
40 are modifying</b>. In particular, please use spaces
41 to indent your code; four spaces per indent level.
4642 <li> If at all possible, avoid making whitespace modifications.
47 <li> Small patches are appreciated.
43 <li> Small patches/changesets are appreciated.
44 <li> If you have lot of changes, try to group them by small commits with descriptive messages.
45 <li> If you have multiple patches, create <b>branch per patch</b>.
46 <li> All new files should contain proper license header (<a href="http://www.gnu.org/licenses/lgpl.html">Lesser GNU Public License</a>).
4847 <li> If you are submitting a new bug detector, please submit a small
4948 standalone source file that contains an instance of the
50 kind of bug the detector looks for.
49 kind of bug the detector looks for. The file should be placed at
50 <a href="https://code.google.com/p/findbugs/source/browse/#git%2FfindbugsTestCases%2Fsrc%2Fjava%2FsfBugsNew">findbugsTestCases/src/java/sfBugsNew</a>
51 package and named after the bug or feature you are addressing.
5152 </ul>
5253
5354 <p> Following these guidelines makes it much easier for us
5455 to incorporate new code.
5556
56 <h2>How to submit a patch</h2>
57 <h2>Where to submit a patch</h2>
5758
5859 <p> Patches may be submitted through the
59 <a href="http://sourceforge.net/tracker/?atid=614695&group_id=96405&func=browse">Patches</a> tracker on the
60 <a href="https://sourceforge.net/p/findbugs/bugs/">bug</a>
61 or <a href="https://sourceforge.net/p/findbugs/feature-requests/">feature request</a> trackers on the
6062 <a href="http://sourceforge.net/projects/findbugs/">sourceforge project page</a>.
6163
6264 @HTML_FOOTER@
4242 </li>
4343 <li>Eclipse plugin for FindBugs version @ECLIPSE_UI_VERSION@ (requires Eclipse 3.6 or later)
4444 <ul>
45 <li><a href="http://sourceforge.net/projects/findbugs/files/findbugs%20eclipse%20plugin/@VERSION_BASE@/edu.umd.cs.findbugs.plugin.eclipse_@ECLIPSE_UI_VERSION@-@FINDBUGS_SVN_REVISION@.zip/download">edu.umd.cs.findbugs.plugin.eclipse_@ECLIPSE_UI_VERSION@-@FINDBUGS_SVN_REVISION@.zip</a>
46 <li><a href="http://sourceforge.net/projects/findbugs/files/findbugs%20eclipse%20plugin/@VERSION_BASE@/eclipsePlugin-@ECLIPSE_UI_VERSION@-@FINDBUGS_SVN_REVISION@-source.zip/download">
47 eclipsePlugin-@ECLIPSE_UI_VERSION@-@FINDBUGS_SVN_REVISION@-source.zip/download</a>
45 <li><a href="http://sourceforge.net/projects/findbugs/files/findbugs%20eclipse%20plugin/@VERSION_BASE@/edu.umd.cs.findbugs.plugin.eclipse_@ECLIPSE_UI_VERSION@-@FINDBUGS_GIT_REVISION@.zip/download">edu.umd.cs.findbugs.plugin.eclipse_@ECLIPSE_UI_VERSION@-@FINDBUGS_GIT_REVISION@.zip</a>
46 <li><a href="http://sourceforge.net/projects/findbugs/files/findbugs%20eclipse%20plugin/@VERSION_BASE@/edu.umd.cs.findbugs.plugin.eclipse_@ECLIPSE_UI_VERSION@-@FINDBUGS_GIT_REVISION@-source.zip/download">edu.umd.cs.findbugs.plugin.eclipse_@ECLIPSE_UI_VERSION@-@FINDBUGS_GIT_REVISION@-source.zip</a>
4847 </ul>
4948 </li>
5049 </ul>
3737 <p>The current version of FindBugs is @VERSION@.</p>
3838
3939 <p>
40 FindBugs requires JRE (or JDK) 1.5.0 or later to run.&nbsp; However, it can analyze programs
41 compiled for any version of Java, from 1.0 to 1.7. Some classfiles compiled for Java 1.8 give
42 FindBugs problems, the next major release of FindBugs will handle Java 1.8 classfiles.
43
40 FindBugs requires JRE (or JDK) 1.7.0 or later to run.&nbsp; However, it can analyze programs
41 compiled for any version of Java, from 1.0 to 1.8.
42
4443 <p> The current version of FindBugs is @VERSION@,
4544
4645 released on @RELEASE_DATE@. <a href="reportingBugs.html">We are very interested in getting
4746 feedback on how to improve FindBugs</a>. File bug reports on <a
48 href="http://sourceforge.net/tracker/?func=browse&amp;group_id=96405&amp;atid=614693"> our
47 href="http://sourceforge.net/p/findbugs/_list/tickets?source=navbar"> our
4948 sourceforge bug tracker</a>
5049 </p>
51 <p>The current version of FindBugs may encounter errors when analyzing
52 Java 1.8 bytecode, due to changes in the classfile format. After FindBugs 2.0.3
53 is released, work will start on the next major release of FindBugs, which will
54 be able to analyze Java 1.8 (and will require Java 1.7 to compile and run).
55
50
5651
5752 <p>
5853 <a href="#changes">Changes</a> | <a href="#talks">Talks</a> | <a href="#papers">Papers </a> | <a
5954 href="#sponsors">Sponsors</a> | <a href="#support">Support</a>
6055 </p>
56
57 <h1>FindBugs 3.0.1 Release</h1>
58 <ul>
59 <li>A number of changes described in the <a href="Changes.html">changes document</a>, including new bug patterns:
60 <ul>
61 <li>
62 <a
63 href="http://findbugs.sourceforge.net/bugDescriptions.html#BSHIFT_WRONG_ADD_PRIORITY">BSHIFT_WRONG_ADD_PRIORITY</a>,
64 <li>
65 <a
66 href="http://findbugs.sourceforge.net/bugDescriptions.html#CO_COMPARETO_INCORRECT_FLOATING">CO_COMPARETO_INCORRECT_FLOATING</a>,
67 <li>
68 <a
69 href="http://findbugs.sourceforge.net/bugDescriptions.html#DC_PARTIALLY_CONSTRUCTED">DC_PARTIALLY_CONSTRUCTED</a>,
70 <li>
71 <a
72 href="http://findbugs.sourceforge.net/bugDescriptions.html#DM_BOXED_PRIMITIVE_FOR_COMPARE">DM_BOXED_PRIMITIVE_FOR_COMPARE</a>,
73 <li>
74 <a
75 href="http://findbugs.sourceforge.net/bugDescriptions.html#DM_INVALID_MIN_MAX">DM_INVALID_MIN_MAX</a>,
76 <li>
77 <a
78 href="http://findbugs.sourceforge.net/bugDescriptions.html#ME_MUTABLE_ENUM_FIELD">ME_MUTABLE_ENUM_FIELD</a>,
79 <li>
80 <a
81 href="http://findbugs.sourceforge.net/bugDescriptions.html#ME_ENUM_FIELD_SETTER">ME_ENUM_FIELD_SETTER</a>,
82 <li>
83
84 <a
85 href="http://findbugs.sourceforge.net/bugDescriptions.html#MS_MUTABLE_COLLECTION">MS_MUTABLE_COLLECTION</a>,
86 <li>
87 <a
88 href="http://findbugs.sourceforge.net/bugDescriptions.html#MS_MUTABLE_COLLECTION_PKGPROTECT">MS_MUTABLE_COLLECTION_PKGPROTECT</a>,
89 <li>
90 <a
91 href="http://findbugs.sourceforge.net/bugDescriptions.html#RANGE_ARRAY_INDEX">RANGE_ARRAY_INDEX</a>,
92 <li>
93 <a
94 href="http://findbugs.sourceforge.net/bugDescriptions.html#RANGE_ARRAY_OFFSET">RANGE_ARRAY_OFFSET</a>,
95 <li>
96 <a
97 href="http://findbugs.sourceforge.net/bugDescriptions.html#RANGE_ARRAY_LENGTH">RANGE_ARRAY_LENGTH</a>,
98 <li>
99 <a
100 href="http://findbugs.sourceforge.net/bugDescriptions.html#RANGE_STRING_INDEX">RANGE_STRING_INDEX</a>,
101 <li>
102 <a
103 href="http://findbugs.sourceforge.net/bugDescriptions.html#RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT">RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT</a>,
104 <li>
105 <a
106 href="http://findbugs.sourceforge.net/bugDescriptions.html#UC_USELESS_CONDITION">UC_USELESS_CONDITION</a>,
107 <li>
108 <a
109 href="http://findbugs.sourceforge.net/bugDescriptions.html#UC_USELESS_CONDITION_TYPE">UC_USELESS_CONDITION_TYPE</a>,
110 <li>
111 <a
112 href="http://findbugs.sourceforge.net/bugDescriptions.html#UC_USELESS_OBJECT">UC_USELESS_OBJECT</a>,
113 <li>
114 <a
115 href="http://findbugs.sourceforge.net/bugDescriptions.html#UC_USELESS_OBJECT_STACK">UC_USELESS_OBJECT_STACK</a>,
116 <li>
117 <a
118 href="http://findbugs.sourceforge.net/bugDescriptions.html#UC_USELESS_VOID_METHOD">UC_USELESS_VOID_METHOD</a>
119 </ul>
120 </li>
121 </ul>
122 <h1>FindBugs 3.0.0 Release</h1>
123
124 <ul>
125 <li>FindBugs supports Java 8 now (both as runtime and target platform).
126 <li>FindBugs requires minimum Java 7 as runtime environment!
127 <li>FindBugs uses ASM 5 now which means that some 3rd party detectors based on FindBugs 2.x/ASM 3 has to be upgraded.
128 See details in <a href="http://download.forge.objectweb.org/asm/asm4-guide.pdf#chapter.5">ASM documentation</a>.
129 <li>New Bug patterns:
130 <a
131 href="http://findbugs.sourceforge.net/bugDescriptions.html#NP_OPTIONAL_RETURN_NULL">NP_OPTIONAL_RETURN_NULL</a>,
132 <a
133 href="http://findbugs.sourceforge.net/bugDescriptions.html#IIO_INEFFICIENT_INDEX_OF">IIO_INEFFICIENT_INDEX_OF</a>,
134 <a
135 href="http://findbugs.sourceforge.net/bugDescriptions.html#IIO_INEFFICIENT_LAST_INDEX_OF">IIO_INEFFICIENT_LAST_INDEX_OF</a>
136 <a
137 href="http://findbugs.sourceforge.net/bugDescriptions.html#CNT_ROUGH_CONSTANT_VALUE">CNT_ROUGH_CONSTANT_VALUE</a>
138 </li>
139 <li>New "Source" filter which can be used to filter out classes generated from other languages:
140 <pre>
141 &lt;?xml version="1.0" encoding="UTF-8"?&gt;
142 &lt;FindBugsFilter&gt;
143 &lt;Match&gt;
144 &lt;Source name="~.*\.groovy" /&gt;
145 &lt;/Match&gt;
146 &lt;/FindBugsFilter&gt;
147 </pre>
148 </li>
149 <li>New "-auxclasspathFromFile" and "-analyzeFromFile" command line options.
150 </li>
151 <li>New "nested" ant task attribute.
152 </li>
153
154
155 <!--
156 <li>Fixed false positives for the following bug patterns (XXX occurrences in findbugsTestCases):
157 <ul>
158 <li><a
159 href="http://findbugs.sourceforge.net/bugDescriptions.html#XXX">XXX</a>
160 </ul>
161 </li>
162
163 <li>Fixed false negatives for the following bug patterns (XXX occurrences in findbugsTestCases):
164 <ul>
165 <li><a
166 href="http://findbugs.sourceforge.net/bugDescriptions.html#XXX">XXX</a>
167 </ul>
168 </li>
169 -->
170
171 <li>Various bug fixes, also many patches from community. Thanks for your contributions!
172 </li>
173 </ul>
174
175
61176
62177 <h1>FindBugs 2.0.3 Release</h1>
63178 <p>FindBugs 2.0.3 is intended to be a minor bug fix release over
64179 FindBugs 2.0.2. Although than some improvements to existing bug detectors
65 and analysis engines, and a few new bug patterns, and some
180 and analysis engines, and a few new bug patterns, and some
66181 important bug fixes to the Eclipse plugin, no significant changes
67182 should be observed. Consult the <a href="Changes.html">Change log</a>
68183 for more details.</p>
116231 </dd>
117232 <dt><a href="https://wiki.jenkins-ci.org/display/JENKINS">Jenkins</a></dt>
118233 <dd> <a href="https://wiki.jenkins-ci.org/display/JENKINS/FindBugs+Plugin">Jenkins FindBugs Plugin</a>
119
234
120235 <dt>
121236 <a href="http://wiki.hudson-ci.org/display/HUDSON/Home">Hudson</a>
122237 </dt>
132247
133248 </dd>
134249 </dl>
135
136
137 <h1>New</h1>
138 <ul>
139
140 <li>jFormatString library republished at
141 <a href="http://code.google.com/p/j-format-string">http://code.google.com/p/j-format-string</a>.
142 This is the library we use for compile time checking of format strings. It is separately published to
143
144 <li>We're releasing FindBugs 2.0.3.
145
146 Mostly small changes to address false positives, with one important fix to the Eclipse plugin
147 to fix a problem that had prevented the plugin from running in some versions of Eclipse.
148 Check the <a href="Changes.html">change log</a> for more details.
149
150 <li>We've released <a href="findbugs2.html">FindBugs 2.0</a>
151 </li>
152 <li>FindBugs communal cloud and Java web start links:. We have analyzed several large open
153 source projects, and provide Java web start links to allow you to view the results. We'd be
154 happy to work with projects to make the results available from a continuous build:
155 <p></p>
156 <ul>
157 <li><a href="http://findbugs.cs.umd.edu/cloud/jdk.jnlp">Sun's JDK 8</a></li>
158 <li><a href="http://findbugs.cs.umd.edu/cloud/eclipse.jnlp">Eclipse 3.8</a></li>
159 <li><a href="http://findbugs.cs.umd.edu/cloud/tomcat.jnlp">Apache Tomcat 7.0</a></li>
160 <li><a href="http://findbugs.cs.umd.edu/cloud/intellij.jnlp">IntelliJ IDEA</a></li>
161 <li><a href="http://findbugs.cs.umd.edu/cloud/jboss.jnlp">JBoss</a></li>
162 </ul>
163 </li>
164 </ul>
165
166
167250
168251 <h1>Experience with FindBugs</h1>
169252 <ul>
2121 <ul>
2222 <li> <a href="http://fb-contrib.sourceforge.net/">fb-contrib</a>: additional bug detectors for use with
2323 FindBugs. The lead FindBugs team does not vouch for the relevance, accuracy or wisdom of the warnings
24 generated by any third-party plugin.
24 generated by any third-party plugin.
25 <li> <a href="http://h3xstream.github.io/find-sec-bugs/">Find Security Bugs</a>: additional bug detectors for use with
26 FindBugs. The lead FindBugs team does not vouch for the relevance, accuracy or wisdom of the warnings
27 generated by any third-party plugin.
2528 <li> <a href="http://www.tobject.de/development/findbugs.html">FindBugs Eclipse plugin</a>.&nbsp;
2629 This is now included as part of FindBugs.
2730 <!--<li> <a href="http://maven-plugins.sourceforge.net/maven-findbugs-plugin/index.html">Maven FindBugs plugin</a>.&nbsp;-->
00 <?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
11 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
2 "file:../etc/docbook/docbookx.dtd" [
2 "../../etc/docbook/docbookx.dtd" [
33 <!ENTITY FindBugs "<application>FindBugs</application>">
44 <!ENTITY Ant "<application>Ant</application>">
55 <!ENTITY Saxon "<application>Saxon</application>">
938938 </varlistentry>
939939
940940 <varlistentry>
941 <term><command>-auxclasspathFromInput</command> </term>
942 <listitem>
943 <para>
944 Read the auxiliary classpath for analysis from standard input, each line adds new
945 entry to the auxiliary classpath for analysis.
946 </para>
947 </listitem>
948 </varlistentry>
949
950 <varlistentry>
951 <term><command>-auxclasspathFromFile</command> <replaceable>filepath</replaceable></term>
952 <listitem>
953 <para>
954 Read the auxiliary classpath for analysis from file, each line adds new
955 entry to the auxiliary classpath for analysis.
956 </para>
957 </listitem>
958 </varlistentry>
959
960 <varlistentry>
961 <term><command>-analyzeFromFile</command> <replaceable>filepath</replaceable></term>
962 <listitem>
963 <para>
964 Read the files to analyze from file, each line adds new
965 entry to the classpath for analysis.
966 </para>
967 </listitem>
968 </varlistentry>
969
970 <varlistentry>
941971 <term><command>-userPrefs</command> <replaceable>edu.umd.cs.findbugs.core.prefs</replaceable></term>
942972 <listitem>
943973 <para>
14151445 <term><literal>omitVisitors</literal></term>
14161446 <listitem>
14171447 <para>
1418 Optional attribute. It is like the <literal>visitors</literal> attribute,
1448 Optional attribute. It specifies a comma-separated list of bug detectors.
1449 It is like the <literal>visitors</literal> attribute,
14191450 except it specifies detectors which will <emphasis>not</emphasis> be run.
1451 </para>
1452 </listitem>
1453 </varlistentry>
1454
1455 <varlistentry>
1456 <term><literal>chooseVisitors</literal></term>
1457 <listitem>
1458 <para>
1459 Optional attribute. It specifies a comma-separated list of bug detectors
1460 prefixed with "+" or "-" to selectively enable/disable them.
14201461 </para>
14211462 </listitem>
14221463 </varlistentry>
15381579 </listitem>
15391580 </varlistentry>
15401581
1582 <varlistentry>
1583 <term><literal>nested</literal></term>
1584 <listitem>
1585 <para>
1586 Optional attribute which enables or disables scanning of nested jar and zip files found in
1587 the list of files and directories to be analyzed.
1588 By default, scanning of nested jar/zip files is enabled.
1589 </para>
1590 </listitem>
1591 </varlistentry>
1592
1593 <varlistentry>
1594 <term><literal>setExitCode</literal></term>
1595 <listitem>
1596 <para>
1597 Optional boolean attribute. Whether the exit code will be returned to
1598 the main ant job. Defaults to "true".
1599 </para>
1600 </listitem>
1601 </varlistentry>
1602
15411603 </variablelist>
15421604
15431605
15641626 The FindBugs Eclipse plugin allows &FindBugs; to be used within
15651627 the <ulink url="http://www.eclipse.org/">Eclipse</ulink> IDE.
15661628 The FindBugs Eclipse plugin was generously contributed by Peter Friese.
1567 Phil Crosby and Andrei Loskutov contributed major improvements
1629 Phil Crosby and Andrey Loskutov contributed major improvements
15681630 to the plugin.
15691631 </para>
15701632
16931755 <title>Extending the Eclipse Plugin (since 2.0.0)</title>
16941756 <para>
16951757 Eclipse plugin supports contribution of custom &FindBugs; detectors (see also
1696 <ulink url="http://code.google.com/p/findbugs/source/browse/trunk/findbugs/src/doc/AddingDetectors.txt">AddingDetectors.txt</ulink>
1758 <ulink url="http://code.google.com/p/findbugs/source/browse/findbugs/src/doc/AddingDetectors.txt">AddingDetectors.txt</ulink>
16971759 for more information). There are two ways to contribute custom plugins to the Eclipse:
16981760 </para>
16991761 <itemizedlist>
17041766 <guimenu>Window</guimenu>
17051767 <guimenuitem>Preferences</guimenuitem>
17061768 <guimenuitem>Java</guimenuitem>
1707 <guimenuitem>&FindBugs;</guimenuitem>
1769 <guimenuitem>FindBugs</guimenuitem>
17081770 <guimenuitem>Misc. Settings</guimenuitem>
17091771 <guimenuitem>Custom Detectors</guimenuitem>
17101772 </menuchoice>.
17261788
17271789 <para>
17281790 Please check the documentation of the
1729 <ulink url="http://code.google.com/p/findbugs/source/browse/trunk/eclipsePlugin/schema/detectorPlugins.exsd">
1791 <ulink url="http://code.google.com/p/findbugs/source/browse/eclipsePlugin/schema/detectorPlugins.exsd">
17301792 findBugsEclipsePlugin/schema/detectorPlugins.exsd</ulink>
17311793 extension point how to update the plugin.xml. Existing &FindBugs; detector plugins can
17321794 be easily "extended" to be full featured &FindBugs; AND Eclipse detector plugins.
17421804 Eclipse plugin. You can do this even for third-party detector packages.
17431805 Another major differentiator is the ability to extend the default FindBugs
17441806 classpath at runtime with required third party libraries (see
1745 <ulink url="http://code.google.com/p/findbugs/source/browse/trunk/findbugs/src/doc/AddingDetectors.txt">AddingDetectors.txt</ulink>
1807 <ulink url="http://code.google.com/p/findbugs/source/browse/findbugs/src/doc/AddingDetectors.txt">AddingDetectors.txt</ulink>
17461808 for more information).
17471809 </para>
17481810 </listitem>
18011863 <para>
18021864 Filter files may be used to include or exclude bug reports for particular classes
18031865 and methods. This chapter explains how to use filter files.
1804
1805 <note>
1806 <title>Planned Features</title>
1807 <para>
1808 Filters are currently only supported by the Command Line interface.
1809 Eventually, filter support will be added to the GUI.
1810 </para>
1811 </note>
18121866 </para>
18131867
18141868
19311985 <para>
19321986 This element matches warnings associated with a particular class. The
19331987 <literal>name</literal> attribute is used to specify the exact or regex match pattern
1934 for the class name.
1988 for the class name. The <literal>role</literal> attribute is the class role.
19351989 </para>
19361990
19371991 <para>
19512005 </varlistentry>
19522006
19532007 <varlistentry>
2008 <term><literal>&lt;Source&gt;</literal></term>
2009 <listitem>
2010 <para>
2011 This element matches warnings associated with a particular source file. The
2012 <literal>name</literal> attribute is used to specify the exact or regex match pattern
2013 for the source file name.
2014 </para>
2015 </listitem>
2016 </varlistentry>
2017
2018 <varlistentry>
19542019 <term><literal>&lt;Method&gt;</literal></term>
19552020
19562021 <listitem><para>This element specifies a method. The <literal>name</literal> is used to specify
19572022 the exact or regex match pattern for the method name.
19582023 The <literal>params</literal> attribute is a comma-separated list
19592024 of the types of the method's parameters. The <literal>returns</literal> attribute is
1960 the method's return type. In <literal>params</literal> and <literal>returns</literal>, class names
2025 the method's return type. The <literal>role</literal> attribute is
2026 the method role. In <literal>params</literal> and <literal>returns</literal>, class names
19612027 must be fully qualified. (E.g., "java.lang.String" instead of just
19622028 "String".) If one of the latter attributes is specified the other is required for creating a method signature.
19632029 Note that you can provide either <literal>name</literal> attribute or <literal>params</literal>
19722038 <listitem><para>This element specifies a field. The <literal>name</literal> attribute is is used to specify
19732039 the exact or regex match pattern for the field name. You can also filter fields according to their signature -
19742040 use <literal>type</literal> attribute to specify fully qualified type of the field. You can specify eiter or both
1975 of these attributes in order to perform name / signature based matches.
2041 of these attributes in order to perform name / signature based matches. The <literal>role</literal> attribute is
2042 the field role.
19762043 </para></listitem>
19772044 </varlistentry>
19782045
19952062 <term><literal>&lt;And&gt;</literal></term>
19962063 <listitem><para>
19972064 This element combines <literal>Match</literal> clauses which both must evaluate to true. I.e., you can put
1998 <literal>Bug</literal> and <literal>Priority</literal> elements in an <literal>And</literal> clause in order
1999 to match specific bugs with given priority only.
2065 <literal>Bug</literal> and <literal>Confidence</literal> elements in an <literal>And</literal> clause in order
2066 to match specific bugs with given confidence only.
20002067 </para></listitem>
20012068 </varlistentry>
20022069 <varlistentry>
20152082 <title>Java element name matching</title>
20162083
20172084 <para>
2018 If the <literal>name</literal> attribute of <literal>Class</literal>, <literal>Method</literal> or
2019 <literal>Field</literal> starts with the ~ character the rest of attribute content is interpreted as
2085 If the <literal>name</literal> attribute of <literal>Class</literal>, <literal>Source</literal>,
2086 <literal>Method</literal> or <literal>Field</literal> starts with the ~ character
2087 the rest of attribute content is interpreted as
20202088 a Java regular expression that is matched against the names of the Java element in question.
20212089 </para>
20222090
22542322 <Bug code="IJU" /> <!-- 'IJU' is the code for bugs related to JUnit test code -->
22552323 </Not>
22562324 </Match>
2325 ]]>
2326 </programlisting>
2327 </para>
2328
2329 <para>
2330 12. Full exclusion filter file to match all classes generated from Groovy source files.
2331
2332 <programlisting>
2333 <![CDATA[
2334 <?xml version="1.0" encoding="UTF-8"?>
2335 <FindBugsFilter>
2336 <Match>
2337 <Source name="~.*\.groovy" />
2338 </Match>
2339 </FindBugsFilter>
22572340 ]]>
22582341 </programlisting>
22592342 </para>
24602543 <varlistentry>
24612544 <term><command>edu.umd.cs.findbugs.annotations.CheckForNull</command></term>
24622545 <listitem>
2463 <command>[Target]</command> Field, Method, Parameter
2546 <para>
2547 <command>[Target]</command> Field, Method, Parameter
2548 </para>
24642549 </listitem>
24652550 <listitem>
24662551 <para>
24732558 <varlistentry>
24742559 <term><command>edu.umd.cs.findbugs.annotations.CheckReturnValue</command></term>
24752560 <listitem>
2561 <para>
24762562 <command>[Target]</command> Method, Constructor
2563 </para>
24772564 </listitem>
24782565 <listitem>
24792566 <variablelist>
25022589 <varlistentry>
25032590 <term><command>edu.umd.cs.findbugs.annotations.DefaultAnnotation</command></term>
25042591 <listitem>
2505 <command>[Target]</command> Type, Package
2592 <para>
2593 <command>[Target]</command> Type, Package
2594 </para>
25062595 </listitem>
25072596 <listitem>
25082597 <variablelist>
25352624 <varlistentry>
25362625 <term><command>edu.umd.cs.findbugs.annotations.DefaultAnnotationForFields</command></term>
25372626 <listitem>
2627 <para>
25382628 <command>[Target]</command> Type, Package
2629 </para>
25392630 </listitem>
25402631 <listitem>
25412632 <variablelist>
25642655 <varlistentry>
25652656 <term><command>edu.umd.cs.findbugs.annotations.DefaultAnnotationForMethods</command></term>
25662657 <listitem>
2658 <para>
25672659 <command>[Target]</command> Type, Package
2660 </para>
25682661 </listitem>
25692662 <listitem>
25702663 <variablelist>
25932686 <varlistentry>
25942687 <term><command>edu.umd.cs.findbugs.annotations.DefaultAnnotationForParameters</command></term>
25952688 <listitem>
2689 <para>
25962690 <command>[Target]</command> Type, Package
2691 </para>
25972692 </listitem>
25982693 <listitem>
25992694 <variablelist>
26222717 <varlistentry>
26232718 <term><command>edu.umd.cs.findbugs.annotations.NonNull</command></term>
26242719 <listitem>
2720 <para>
26252721 <command>[Target]</command> Field, Method, Parameter
2722 </para>
26262723 </listitem>
26272724 <listitem>
26282725 <para>
26352732 <varlistentry>
26362733 <term><command>edu.umd.cs.findbugs.annotations.Nullable</command></term>
26372734 <listitem>
2735 <para>
26382736 <command>[Target]</command> Field, Method, Parameter
2737 </para>
26392738 </listitem>
26402739 <listitem>
26412740 <para>
26452744 treat the annotated items as though they had no annotation.
26462745 </para>
26472746 <para>
2648 In pratice this annotation is useful only for overriding an overarching NonNull
2747 In practice this annotation is useful only for overriding an overarching NonNull
26492748 annotation.
26502749 </para>
26512750 </listitem>
26542753 <varlistentry>
26552754 <term><command>edu.umd.cs.findbugs.annotations.OverrideMustInvoke</command></term>
26562755 <listitem>
2756 <para>
26572757 <command>[Target]</command> Method
2758 </para>
26582759 </listitem>
26592760 <listitem>
26602761 <variablelist>
26922793 <varlistentry>
26932794 <term><command>edu.umd.cs.findbugs.annotations.SuppressWarnings</command></term>
26942795 <listitem>
2796 <para>
26952797 <command>[Target]</command> Type, Field, Method, Parameter, Constructor, Package
2798 </para>
26962799 </listitem>
26972800 <listitem>
26982801 <variablelist>
27272830 <varlistentry>
27282831 <term><command>edu.umd.cs.findbugs.annotations.UnknownNullness</command></term>
27292832 <listitem>
2833 <para>
27302834 <command>[Target]</command> Field, Method, Parameter
2835 </para>
27312836 </listitem>
27322837 <listitem>
27332838 <para>
27392844 <varlistentry>
27402845 <term><command>edu.umd.cs.findbugs.annotations.UnknownNullness</command></term>
27412846 <listitem>
2847 <para>
27422848 <command>[Target]</command> Field, Method, Parameter
2849 </para>
27432850 </listitem>
27442851 <listitem>
27452852 <para>
27522859 <para>
27532860 &FindBugs; also supports the following annotations:
27542861 <itemizedlist>
2755 <listitem>net.jcip.annotations.GuardedBy</listitem>
2756 <listitem>net.jcip.annotations.Immutable</listitem>
2757 <listitem>net.jcip.annotations.NotThreadSafe</listitem>
2758 <listitem>net.jcip.annotations.ThreadSafe</listitem>
2862 <listitem><para>net.jcip.annotations.GuardedBy</para></listitem>
2863 <listitem><para>net.jcip.annotations.Immutable</para></listitem>
2864 <listitem><para>net.jcip.annotations.NotThreadSafe</para></listitem>
2865 <listitem><para>net.jcip.annotations.ThreadSafe</para></listitem>
27592866 </itemizedlist>
27602867 </para>
27612868 <para>
37653872 <para>Thomas Klaeger contributed a number of bug fixes and
37663873 bug detector improvements.</para>
37673874
3768 <para>Andrei Loskutov made a number of improvements to the
3769 Eclipse plugin.</para>
3875 <para>Andrey Loskutov made a number of bug fixes and
3876 bug detector improvements. He is maintainer of the Eclipse plugin.</para>
37703877
37713878 <para>Brian Goetz contributed a major refactoring of the
37723879 visitor classes to improve readability and understandability.</para>
38513958 <para>Dieter von Holten (dvholten) contributed
38523959 some German improvements to findbugs_de.properties.</para>
38533960
3961 <para>Kevin Lubick contributed fixes and tests for multi-quick-fixes and customizable
3962 annotation colors for Eclipse plugin.</para>
3963
3964 <para>Tagir Valeev contributed several new bug detectors and improved existing ones.</para>
38543965
38553966 <para>If you have contributed to &FindBugs;, but aren't mentioned above,
38563967 please send email to <email>findbugs@cs.umd.edu</email> (and also accept
0 <?xml version="1.0" encoding="UTF-8"?>
1 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN" "http://findbugs.googlecode.com/svn/trunk/findbugs/etc/docbook/docbookx.dtd"
2 [
0 <?xml version="1.0" encoding="UTF-8" standalone="no"?>
1 <!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.2//EN"
2 "../../etc/docbook/docbookx.dtd" [
33 <!ENTITY FindBugs "<application>FindBugs</application>">
44 <!ENTITY Ant "<application>Ant</application>">
55 <!ENTITY Saxon "<application>Saxon</application>">
1212 <bookinfo>
1313 <title>&FindBugs;&trade; マニュアル</title>
1414
15 <authorgroup>
15 <authorgroup lang="en">
1616 <author>
1717 <firstname>David</firstname>
1818 <othername>H.</othername>
2525 </author>
2626 </authorgroup>
2727
28 <copyright>
29 <year>2003</year>
30 <year>2004</year>
31 <year>2005</year>
32 <year>2006</year>
33 <year>2008</year>
28 <copyright lang="en">
29 <year>2003 - 2012</year>
3430 <holder>University of Maryland</holder>
3531 </copyright>
3632
4137
4238 <edition>@VERSION@</edition>
4339
44 <pubdate>@RELEASE_DATE@</pubdate>
40 <pubdate>@RELEASE_DATE@ (翻訳 2014年7月)</pubdate>
4541
4642 </bookinfo>
4743
119115 </listitem>
120116 <listitem>
121117 <para>
122 <ulink url="http://java.sun.com/j2se/">JDK 1.5.0 ベータ またはそれ以降</ulink>
118 <ulink url="http://java.sun.com/j2se/">JDK 1.5.0 またはそれ以降</ulink>
123119 </para>
124120 </listitem>
125121 <listitem>
635631 </listitem>
636632 </varlistentry>
637633
634 <varlistentry>
635 <term><command>-auxclasspathFromInput</command> </term>
636 <listitem>
637 <para>分析時に使用する補助クラスパスを標準入力から読み込みます。標準入力の各行が分析時に使用する補助クラスパスに追加されます。</para>
638 </listitem>
639 </varlistentry>
640
641 <varlistentry>
642 <term><command>-auxclasspathFromFile</command> <replaceable>ファイルパス</replaceable></term>
643 <listitem>
644 <para>分析時に使用する補助クラスパスをファイルから読み込みます。ファイルの各行が分析時に使用する補助クラスパスに追加されます。</para>
645 </listitem>
646 </varlistentry>
647
648 <varlistentry>
649 <term><command>-analyzeFromFile</command> <replaceable>ファイルパス</replaceable></term>
650 <listitem>
651 <para>分析対象ファイルをファイルから読み込みます。ファイルの各行が分析対象クラスパスに追加されます。</para>
652 </listitem>
653 </varlistentry>
654
655 <varlistentry>
656 <term><command>-userPrefs</command> <replaceable>edu.umd.cs.findbugs.core.prefs</replaceable></term>
657 <listitem>
658 <para>ユーザー設定ファイルのパスを設定します。ユーザー設定ファイルの内容で、他のオプションの一部を上書きすることができます。
659 <literal>userPrefs</literal> を一番目の引数に指定した場合は、後続のオプションの方がユーザー設定ファイルの内容に優先します。
660 <literal>userPrefs</literal> を最後の引数に指定した場合は、ユーザー設定ファイルの内容の方が前に指定したオプションに優先します。
661 このオプションを設けた背景・理由は、 Eclipse プロジェクトの FindBugs の設定をコマンドライン実行で再利用するためです。</para>
662 </listitem>
663 </varlistentry>
664
638665 <!--
639666 <varlistentry>
640667 <term><command></command> <replaceable></replaceable></term>
667694 </mediaobject>
668695 </para>
669696
670 <para>「Class archives and directories to analyze」テキストフィールドの横にある 「Add」ボタンを押すと、バグを分析する java クラスを含んでいる Java アーカイブファイル (zip, jar, ear, or war file) を選択して指定できます。複数の アーカイブ/ディレクトリーを追加することが可能です。</para>
697 <para>「Classpath to analyze」の横にある 「Add」ボタンを押すと、バグを分析する java クラスを含んでいる Java アーカイブファイル (zip, jar, ear, or war file) を選択して指定できます。複数の アーカイブ/ディレクトリーを追加することが可能です。</para>
671698
672699 <para>また、分析を行う Java アーカイブのソースコードを含んだソースディレクトリーを指定することもできます。そうすると、バグの可能性があるソースコードの場所が、&FindBugs; 上でハイライトして表示されます。ソースディレクトリーは、Java パッケージ階層のルートディレクトリーを指定する必要があります。例えば、ユーザのアプリケーションが <varname>org.foobar.myapp</varname> パッケージの中にある場合は、 <filename class="directory">org</filename> ディレクトリーの親ディレクトリーをソースディレクトリーリストに指定する必要があります。</para>
673700
677704
678705 <sect1>
679706 <title>分析の実行</title>
680 <para>アーカイブ、ディレクトリーおよびソースディレクトリーの指定ができれば、「Finish」ボタンを押して Jar ファイルに含まれるクラスに対する分析を実行します。巨大なプロジェクトを古いコンピュータ上で実行すると、かなりの時間(数十分)がかかることに注意してください。大容量メモリである最近のコンピュータなら、大きなプログラムであっても数分程度で分析できます。</para>
707 <para>アーカイブ、ディレクトリーおよびソースディレクトリーの指定ができれば、「Analyze」ボタンを押して Jar ファイルに含まれるクラスに対する分析を実行します。巨大なプロジェクトを古いコンピュータ上で実行すると、かなりの時間(数十分)がかかることに注意してください。大容量メモリである最近のコンピュータなら、大きなプログラムであっても数分程度で分析できます。</para>
681708 </sect1>
682709
683710 <sect1>
802829 <sect1>
803830 <title>パラメーター</title>
804831
805 <para>このセクションでは、 &FindBugs; タスクを使用する際に、指定することができるパラメーターについて説明します。<variablelist> <varlistentry> <term><literal>class</literal></term>
806 <listitem>
807 <para>分析の対象となるクラス群を指定するためのネストされる要素です。<literal>class</literal> 要素には <literal>location</literal> 属性の指定が必須です。分析対象となるアーカイブファイル (jar, zip, 他)、ディレクトリーまたはクラスファイルの名前を記述します。1 つの <literal>findbugs</literal> 要素に対して、複数の <literal>class</literal> 子要素を指定することができます。</para>
832 <para>このセクションでは、 &FindBugs; タスクを使用する際に、指定することができるパラメーターについて説明します。
833
834 <variablelist>
835
836 <varlistentry>
837 <term><literal>class</literal></term>
838 <listitem>
839 <para>任意指定のネストされる要素です。分析の対象となるクラス群を指定します。
840 <literal>class</literal> 要素には <literal>location</literal> 属性の指定が必須です。
841 分析対象となるアーカイブファイル (jar, zip, 他)、ディレクトリーまたはクラスファイルの名前を記述します。
842 1 つの <literal>findbugs</literal> 要素に対して、複数の <literal>class</literal> 子要素を指定することができます。
843 </para>
844 <para><literal>class</literal> 要素の指定を置き換えるまたは追加する形で、 &FindBugs; タスクに1個以上の <literal>fileset</literal> 要素を記述することで
845 分析するファイル群を指定することができます。
846 例えば、 fileset において特定のディレクトリにある全ての jar ファイルを分析対象に指定することができます。
847 </para>
808848 </listitem>
809849 </varlistentry>
810850
839879 <varlistentry>
840880 <term><literal>reportLevel</literal></term>
841881 <listitem>
842 <para>任意指定の属性です。報告されるバグの優先度のしきい値を指定します。「low」に設定すると、すべてのバグが報告されます。「medium」 (デフォルト) に設定すると、優先度 (中)および優先度 (高)のバグが報告されます。「high」に設定すると、優先度 (高) のバグのみが報告されます。</para>
882 <para>
883 任意指定の属性です。報告される問題の信頼度・優先度のしきい値を指定します。
884 「low」に設定すると、バグ報告が信頼度により除外されることはありません。
885 「medium」 (デフォルト) に設定すると、信頼度(低)の問題が除外されます。
886 「high」に設定すると、信頼度(高) のバグのみが報告されます。
887 </para>
843888 </listitem>
844889 </varlistentry>
845890
908953 <varlistentry>
909954 <term><literal>omitVisitors</literal></term>
910955 <listitem>
911 <para>任意指定の属性です。<literal>visitors</literal> 属性と似ていますが、こちらは <emphasis>実行されない</emphasis> ディテクタを指定します。</para>
956 <para>
957 任意指定の属性です。
958 バグディテクタをコンマ区切りのリストで指定します。
959 <literal>visitors</literal> 属性と似ていますが、こちらは <emphasis>実行されない</emphasis> ディテクタを指定します。
960 </para>
961 </listitem>
962 </varlistentry>
963
964 <varlistentry>
965 <term><literal>chooseVisitors</literal></term>
966 <listitem>
967 <para>
968 任意指定の属性です。
969 頭に「+」または「-」を付け加えたバグディテクタをコンマ区切りのリストで指定します。
970 「+」を付け加えたバグディテクタは有効に、「-」を付け加えたバグディテクタは無効になります。
971 </para>
912972 </listitem>
913973 </varlistentry>
914974
9721032 <term><literal>warningsProperty</literal></term>
9731033 <listitem>
9741034 <para>任意指定の属性です。&FindBugs; が分析したプログラムにバグ報告が 1 件でもある場合に、「true」が設定されるプロパティーの名前を指定します。</para>
1035 </listitem>
1036 </varlistentry>
1037
1038 <varlistentry>
1039 <term><literal>userPreferencesFile</literal></term>
1040 <listitem>
1041 <para>
1042 任意指定の属性です。
1043 ユーザー設定ファイルのパスを設定します。ユーザー設定ファイルの内容で、他のオプションの一部を上書きすることができます。
1044 <literal>userPreferencesFile</literal> を一番目の引数に指定した場合は、後続のオプションの方がユーザー設定ファイルの内容に優先します。
1045 <literal>userPreferencesFile</literal> を最後の引数に指定した場合は、ユーザー設定ファイルの内容の方が前に指定したオプションに優先します。
1046 このオプションを設けた背景・理由は、 Eclipse プロジェクトの FindBugs の設定をコマンドライン実行で再利用するためです。
1047 </para>
1048 </listitem>
1049 </varlistentry>
1050
1051 <varlistentry>
1052 <term><literal>nested</literal></term>
1053 <listitem>
1054 <para>
1055 任意指定の属性です。分析対象のファイル・ディレクトリーリストにあるファイル内にネストされた jar および zip ファイルに対する分析を有効化・無効化します。
1056 デフォルトでは、ネストされた jar/zip の分析は有効です。
1057 </para>
9751058 </listitem>
9761059 </varlistentry>
9771060
9971080 <chapter id="eclipse">
9981081 <title>&FindBugs;&trade; Eclipse プラグインの使用方法</title>
9991082
1000 <para>FindBugs Eclipse プラグインを使用することによって、 &FindBugs; を <ulink url="http://www.eclipse.org/">Eclipse</ulink> IDE で使用することができるようになります。このFindBugs Eclipse プラグインは、 Peter Friese 氏の多大な貢献によるものです。Phil Crosby 氏 と Andrei Loskutov 氏は、プラグインの重要な改良に貢献しました。</para>
1083 <para>
1084 FindBugs Eclipse プラグインを使用することによって、 &FindBugs; を <ulink url="http://www.eclipse.org/">Eclipse</ulink> IDE で使用することができるようになります。
1085 このFindBugs Eclipse プラグインは、 Peter Friese 氏の多大な貢献によるものです。
1086 Phil Crosby 氏 と Andrey Loskutov 氏は、プラグインの重要な改良に貢献しました。
1087 </para>
10011088
10021089 <sect1>
10031090 <title>必要条件</title>
10191106 </listitem>
10201107 </varlistentry>
10211108
1022 <varlistentry><term><ulink url="http://findbugs.cs.umd.edu/eclipse-candidate/">http://findbugs.cs.umd.edu/eclips-candidate/</ulink></term>
1109 <varlistentry><term><ulink url="http://findbugs.cs.umd.edu/eclipse-candidate/">http://findbugs.cs.umd.edu/eclipse-candidate/</ulink></term>
10231110
10241111 <listitem>
10251112 <para>FindBugsの公式リリース物に加えて、公式リリース候補版を提供します。</para>
10631150 </sect1>
10641151
10651152 <sect1>
1066 <title>トラブルシューティング</title>
1067
1068 <para>&FindBugs; Eclipse プラグインは、まだ実験段階です。このセクションでは、プラグインに関する一般的な問題と (判明していれば) それらの問題の解決方法を記述します。</para>
1069
1153 <title>Eclipse プラグインの拡張方法 (2.0.0 以降)</title>
1154 <para>
1155 Eclipse プラグインでは、 独自の &FindBugs; ディテクタを追加することができます。
1156 (詳しく知りたい方は、
1157 <ulink url="http://code.google.com/p/findbugs/source/browse/findbugs/src/doc/AddingDetectors.txt">AddingDetectors.txt</ulink>
1158 もご覧ください)。
1159 独自のプラグインを Eclipse に追加するには以下の2通りの方法があります。
1160 </para>
10701161 <itemizedlist>
10711162 <listitem>
1072 <para>&FindBugs; 問題マーカーが (ソース画面および問題ビューに) 表示されない場合は、問題ビューのフィルター設定を変更してください。詳細情報は <ulink url="http://findbugs.sourceforge.net/FAQ.html#q7">http://findbugs.sourceforge.net/FAQ.html#q7</ulink> を参照してください。</para>
1163 <para>
1164 既存の標準 &FindBugs; ディテクタパッケージは以下で設定できます。
1165 <menuchoice>
1166 <guimenu>Window</guimenu>
1167 <guimenuitem>Preferences</guimenuitem>
1168 <guimenuitem>Java</guimenuitem>
1169 <guimenuitem>FindBugs</guimenuitem>
1170 <guimenuitem>Misc. Settings</guimenuitem>
1171 <guimenuitem>Custom Detectors</guimenuitem>
1172 </menuchoice>。
1173 ここで追加するプラグインライブラリの場所を指定するだけです。
1174 </para>
1175
1176 <para>
1177 この方法の利点は、既存のディテクタパッケージは「そのまま」で、サードパーティーディテクタの品質をすばやく検証できることです。
1178 欠点としては、この設定は Eclipse ワークスペース毎に適用する必要があり、この設定をチームメンバー間で共有することができないことです。
1179 </para>
1180 </listitem>
1181
1182 <listitem>
1183 <para>
1184 Eclipse 標準の拡張機構を使って独自のディテクタを追加することができます。
1185 </para>
1186
1187 <para>
1188 <ulink url="http://code.google.com/p/findbugs/source/browse/eclipsePlugin/schema/detectorPlugins.exsd">
1189 findBugsEclipsePlugin/schema/detectorPlugins.exsd</ulink> の documentation で plugin.xml の更新方法を確認してください。
1190 既存の &FindBugs; ディテクタプラグイン を簡単に「拡張」して強力な &FindBugs; Eclipse ディテクタプラグイン にすることができます。
1191 通常必要なのは、 META-INF/MANIFEST.MF および plugin.xml を jar に追加してかつ、 MANIFEST.MF がビルド時に上書きされないようにビルドスクリプトを更新するだけです。
1192 </para>
1193
1194 <para>
1195 この方法の利点は、 Eclipse インストールを共有しさえすれば、チームメンバー同士で正確に同じディテクタ設定を使用できることです。
1196 前提条件は現在使用しているディテクタパッケージを Eclipse プラグインに変換しておくことです。
1197 これはサードパーティのディテクタパッケージも同様です。
1198 もうひとつの主な差別化要因は、 FindBugs のデフォルト実行時クラスパス に当該サードパーティライブラリーを追加することが必要なことです(くわしくは
1199 <ulink url="http://code.google.com/p/findbugs/source/browse/findbugs/src/doc/AddingDetectors.txt">AddingDetectors.txt</ulink>
1200 をご覧ください)。
1201 </para>
1202 </listitem>
1203
1204 </itemizedlist>
1205
1206 </sect1>
1207
1208 <sect1>
1209 <title>トラブルシューティング</title>
1210
1211 <para>
1212 このセクションでは、プラグインに関する一般的な問題と (判明していれば) それらの問題の解決方法を記述します。
1213 </para>
1214
1215 <itemizedlist>
1216 <listitem>
1217 <para>
1218 Eclipse において &FindBugs; の分析開始後に OutOfMemory エラーダイアログが 出た場合は、
1219 JVM の使用メモリを増やしてください。すなわち、 eclipse.ini の末尾に以下の記述を追加してください。
1220 <programlisting>
1221 -vmargs
1222 -Xmx1000m
1223 </programlisting>
1224 重要:「-vmargs」で始まる設定引数は eclipse.ini ファイルの末尾に記述する必要があります。
1225 また、各行には引数を一つだけしか書くことができません!
1226 </para>
1227 </listitem>
1228 <listitem>
1229 <para>
1230 &FindBugs; 問題マーカーが (ソース画面および問題ビューに) 表示されない場合は、問題ビューのフィルター設定を変更してください。
1231 詳細情報は <ulink url="http://findbugs.sourceforge.net/FAQ.html#q7">http://findbugs.sourceforge.net/FAQ.html#q7</ulink> を参照してください。
1232 </para>
10731233 </listitem>
10741234
10751235 </itemizedlist>
11231283 </varlistentry>
11241284
11251285 <varlistentry>
1286 <term><literal>&lt;Confidence&gt;</literal></term>
1287 <listitem>
1288 <para>
1289 この要素は、特定のバグ信頼度をもつ警告を照合します。
1290 <literal>value</literal> 属性には、整数値を指定します。すなわち 1 は信頼度(高) 、また、 2 は信頼度(中) 、 3 は信頼度(低) を示します。
1291 &lt;Confidence&gt; は 2.0.0 リリースから &lt;Priority&gt; の後継として取って代わりました。
1292 </para>
1293 </listitem>
1294 </varlistentry>
1295
1296 <varlistentry>
11261297 <term><literal>&lt;Priority&gt;</literal></term>
11271298 <listitem>
1128 <para>この要素は、特定の優先度をもつ警告を照合します。<literal>value</literal> 属性には、整数値を指定します : 1 は優先度(高)、また、 2 は優先度(中) 、 3 は優先度(低) を示します。</para>
1299 <para>
1300 <literal>&lt;Confidence&gt;</literal> と同一です。下位互換性を保つため残されています。
1301 </para>
11291302 </listitem>
11301303 </varlistentry>
11311304
1305 <varlistentry>
1306 <term><literal>&lt;Rank&gt;</literal></term>
1307 <listitem>
1308 <para>
1309 この要素は、特定のバグランクをもつ警告を照合します。
1310 <literal>value</literal> 属性には、 1 から 20 の整数値を指定します。
1311 1 から 4 は最も恐ろしいバグ、 5 から 9 は恐ろしいバグ、 10 から 14 は厄介なバグ,
1312 そして 15 から 20 は不安なバグを示します。
1313 </para>
1314 </listitem>
1315 </varlistentry>
11321316
11331317 <varlistentry>
11341318 <term><literal>&lt;Package&gt;</literal></term>
11451329 <para>下位互換性を持たせたい場合は、この要素の代わりに <literal>Match</literal> 要素を使用してください。クラス名そのものの指定は <literal>class</literal> 属性を、クラス名を正規表現で指定する場合は <literal>classregex</literal> 属性をそれぞれ使用してください</para>
11461330
11471331 <para>もし <literal>Match</literal> 要素に <literal>Class</literal> 要素が無かったり、 <literal>class</literal> / <literal>classregex</literal> 属性が無かったりした場合は、すべてのクラスに適用されます。その場合、想定外に多くのバグ検索結果が一致してしまうことがあり得ます。その場合は、適当なメソッドやフィールドで絞り込んでください。</para>
1332 </listitem>
1333 </varlistentry>
1334
1335 <varlistentry>
1336 <term><literal>&lt;Source&gt;</literal></term>
1337 <listitem>
1338 <para>
1339 この要素は、特定のソースファイルに関連した警告を照合します。
1340 <literal>name</literal> 属性を使用して、照合するソースファイル名をソースファイル名そのものか、または、正規表現で指定します。
1341 </para>
11481342 </listitem>
11491343 </varlistentry>
11501344
11701364 <term><literal>&lt;Or&gt;</literal></term>
11711365 <listitem><para>この要素は、論理和として <literal>Match</literal> 条項を結合します。すなわち、2つの <literal>Method</literal> 要素を <literal>Or</literal> 条項に入れることで、どちらか一方のメソッドでマッチさせることができます。</para></listitem>
11721366 </varlistentry>
1367 <varlistentry>
1368 <term><literal>&lt;And&gt;</literal></term>
1369 <listitem><para>
1370 この要素は、論理積として <literal>Match</literal> 条項を結合します。
1371 すなわち、 <literal>Bug</literal> および <literal>Confidence</literal> 要素を <literal>And</literal> 条項内に指定することで
1372 特定の信頼度の特定のバグのみ照合することができます。
1373 </para></listitem>
1374 </varlistentry>
1375 <varlistentry>
1376 <term><literal>&lt;Not&gt;</literal></term>
1377 <listitem><para>
1378 この要素は、内包する子 <literal>Match</literal> を反転します。
1379 すなわち、 <literal>Bug</literal> 要素を <literal>Not</literal> 条項内に指定することで指定したバグ以外のすべてのバグと照合します。
1380 </para></listitem>
1381 </varlistentry>
11731382 </variablelist>
11741383
11751384 </sect1>
11771386 <sect1>
11781387 <title>Java 要素名マッチング</title>
11791388
1180 <para><literal>Class</literal> 、 <literal>Method</literal> または <literal>Field</literal> の <literal>name</literal> 属性が文字 ~ で始まっている場合は、属性値の残りの部分を Java の正規表現として解釈します。そうして、当該 Java 要素の名前に対しての照合が行われます。</para>
1389 <para>
1390 <literal>Class</literal> 、 <literal>Source</literal> 、 <literal>Method</literal> または <literal>Field</literal> の <literal>name</literal> 属性が文字 ~ で始まっている場合は、
1391 属性値の残りの部分を Java の正規表現として解釈します。
1392 そうして、当該 Java 要素の名前に対しての照合が行われます。
1393 </para>
11811394
11821395 <para>パターンの照合は要素の名前全体に対して行われることに注意してください。そのため、部分一致照合を行いたい場合はパターン文字列の前後に .* を付加して使用する必要があります。</para>
11831396
13361549 </Match>
13371550 ]]>
13381551 </programlisting>
1339
1552 </para>
1553
1554
1555 <para>
1556 11. Not フィルター演算子を使用してバグに一致させます。
1557 <programlisting>
1558 <![CDATA[
1559 <!-- テストクラスのバグは無視する。ただし、 JUnit テスト特有のバグは例外とする。 -->
1560 <!-- i.e. filter bug if ( classIsJUnitTest && ! bugIsRelatedToJUnit ) -->
1561 <Match>
1562 <!-- Match フィルタは論理和と同等 -->
1563
1564 <Class name="~.*\.*Test" />
1565 <!-- テストクラスは末尾に「Test」 -->
1566
1567 <Not>
1568 <Bug code="IJU" /> <!-- 「IJU」は JUnit テストコード関連のバグコード -->
1569 </Not>
1570 </Match>
1571 ]]>
1572 </programlisting>
1573 </para>
1574
1575 <para>
1576 12. 完全な除外フィルター。 Groovy ソースファイルから生成された全クラスを除外します。
1577
1578 <programlisting>
1579 <![CDATA[
1580 <?xml version="1.0" encoding="UTF-8"?>
1581 <FindBugsFilter>
1582 <Match>
1583 <Source name="~.*\.groovy" />
1584 </Match>
1585 </FindBugsFilter>
1586 ]]>
1587 </programlisting>
13401588 </para>
13411589
13421590 </sect1>
13791627 <Bug pattern="DLS_DEAD_LOCAL_STORE" />
13801628 <Priority value="2" />
13811629 </Match>
1630
1631 <!-- テストクラスのバグすべて。 JUnit 特有のバグは除く。 -->
1632 <Match>
1633 <Class name="~.*\.*Test" />
1634 <Not>
1635 <Bug code="IJU" />
1636 </Not>
1637 </Match>
1638
13821639 </FindBugsFilter>
13831640 ]]>
13841641 </programlisting>
14881745 <varlistentry>
14891746 <term><command>edu.umd.cs.findbugs.annotations.CheckForNull</command></term>
14901747 <listitem>
1491 <command>[Target]</command> Field, Method, Parameter
1748 <para>
1749 <command>[Target]</command> Field, Method, Parameter
1750 </para>
14921751 </listitem>
14931752 <listitem>
14941753 <para>アノテーションをつけた要素は、 null である可能性があります。したがって、当該要素を使用する際は null チェックをするべきです。このアノテーションをメソッドに適用すると、メソッドの戻り値に適用されます。</para>
14981757 <varlistentry>
14991758 <term><command>edu.umd.cs.findbugs.annotations.CheckReturnValue</command></term>
15001759 <listitem>
1760 <para>
15011761 <command>[Target]</command> Method, Constructor
1762 </para>
15021763 </listitem>
15031764 <listitem>
15041765 <variablelist>
15231784 <varlistentry>
15241785 <term><command>edu.umd.cs.findbugs.annotations.DefaultAnnotation</command></term>
15251786 <listitem>
1787 <para>
15261788 <command>[Target]</command> Type, Package
1789 </para>
15271790 </listitem>
15281791 <listitem>
15291792 <variablelist>
15421805 </listitem>
15431806 <listitem>
15441807 <para>
1545 Indicates that all members of the class or package should be annotated with the default
1546 value of the supplied annotation classes. This would be used for behavior annotations
1547 such as @NonNull, @CheckForNull, or @CheckReturnValue. In particular, you can use
1548 @DefaultAnnotation(NonNull.class) on a class or package, and then use @Nullable only
1549 on those parameters, methods or fields that you want to allow to be null.
1808 クラスまたはパッケージのすべてのメンバーが指定されたアノテーションクラスのデフォルト値でアノテートされることを指定することができます。
1809 この指定は @NonNull 、 @CheckForNull 、または @CheckReturnValueなどに使用する想定です。
1810 具体的には、 クラスまたはパッケージに @DefaultAnnotation(NonNull.class) を指定した上で、
1811 null を許容したいパラメーター、メソッドまたはフィールドにのみ @Nullable を指定するという使用方法があります。
15501812 </para>
15511813 </listitem>
15521814 </varlistentry>
15541816 <varlistentry>
15551817 <term><command>edu.umd.cs.findbugs.annotations.DefaultAnnotationForFields</command></term>
15561818 <listitem>
1819 <para>
15571820 <command>[Target]</command> Type, Package
1821 </para>
15581822 </listitem>
15591823 <listitem>
15601824 <variablelist>
15731837 </listitem>
15741838 <listitem>
15751839 <para>
1576 This is same as the DefaultAnnotation except it only applys to fields.
1840 フィールドのみに適用されること以外は DefaultAnnotation と同様です。
15771841 </para>
15781842 </listitem>
15791843 </varlistentry>
15811845 <varlistentry>
15821846 <term><command>edu.umd.cs.findbugs.annotations.DefaultAnnotationForMethods</command></term>
15831847 <listitem>
1848 <para>
15841849 <command>[Target]</command> Type, Package
1850 </para>
15851851 </listitem>
15861852 <listitem>
15871853 <variablelist>
16001866 </listitem>
16011867 <listitem>
16021868 <para>
1603 This is same as the DefaultAnnotation except it only applys to methods.
1869 メソッドのみに適用されること以外は DefaultAnnotation と同様です。
16041870 </para>
16051871 </listitem>
16061872 </varlistentry>
16081874 <varlistentry>
16091875 <term><command>edu.umd.cs.findbugs.annotations.DefaultAnnotationForParameters</command></term>
16101876 <listitem>
1877 <para>
16111878 <command>[Target]</command> Type, Package
1879 </para>
16121880 </listitem>
16131881 <listitem>
16141882 <variablelist>
16271895 </listitem>
16281896 <listitem>
16291897 <para>
1630 This is same as the DefaultAnnotation except it only applys to method parameters.
1898 パラメーターのみに適用されること以外は DefaultAnnotation と同様です。
16311899 </para>
16321900 </listitem>
16331901 </varlistentry>
16351903 <varlistentry>
16361904 <term><command>edu.umd.cs.findbugs.annotations.NonNull</command></term>
16371905 <listitem>
1906 <para>
16381907 <command>[Target]</command> Field, Method, Parameter
1908 </para>
16391909 </listitem>
16401910 <listitem>
16411911 <para>アノテーションをつけた要素は、 null であってはいけません。アノテーションをつけたフィールドは、構築完了後 null であってはいけません。アノテーションをつけたメソッドは、 null ではない値を戻り値としなければなりません。</para>
16451915 <varlistentry>
16461916 <term><command>edu.umd.cs.findbugs.annotations.Nullable</command></term>
16471917 <listitem>
1918 <para>
16481919 <command>[Target]</command> Field, Method, Parameter
1649 </listitem>
1650 <listitem>
1651 <para>アノテーションをつけた要素は、 null であってはいけません。In general, this means developers will have to read the documentation to determine when a null value is acceptable and whether it is neccessary to check for a null value. FindBugs will treat the annotated items as though they had no annotation.</para>
1920 </para>
1921 </listitem>
1922 <listitem>
16521923 <para>
1653 In pratice this annotation is useful only for overriding an overarching NonNull
1654 annotation.
1924 アノテーションをつけた要素は、 条件により null である可能性があります。
1925 一般に、開発者はドキュメントを読んで null 値を許容するかどうかまたは null 値をチェックするかどうかを決定する必要があります。
1926 FindBugs はこのアノテーションをつけた要素を、アノテーションがついていないものと同様に扱います。
16551927 </para>
1928 <para>
1929 実際には、このアノテーションは NonNull をつけた要素をオーバーライドする場合に有用です。
1930 </para>
16561931 </listitem>
16571932 </varlistentry>
16581933
16591934 <varlistentry>
16601935 <term><command>edu.umd.cs.findbugs.annotations.OverrideMustInvoke</command></term>
16611936 <listitem>
1937 <para>
16621938 <command>[Target]</command> Method
1939 </para>
16631940 </listitem>
16641941 <listitem>
16651942 <variablelist>
16671944 <term><command>[Parameter]</command></term>
16681945 <listitem>
16691946 <para>
1670 <command>value:</command>Specify when the super invocation should be
1671 performed (FIRST, ANYTIME, LAST). Default value:ANYTIME.
1947 <command>value:</command>super の呼び出し箇所を指定します (FIRST, ANYTIME, LAST)。 デフォルト値 :ANYTIME。
16721948 </para>
16731949 </listitem>
16741950 </varlistentry>
16761952 </listitem>
16771953 <listitem>
16781954 <para>
1679 Used to annotate a method that, if overridden, must (or should) be invoke super
1680 in the overriding method. Examples of such methods include finalize() and clone().
1681 The argument to the method indicates when the super invocation should occur:
1682 at any time, at the beginning of the overriding method, or at the end of the overriding method.
1683 (This anotation is not implmemented in FindBugs as of September 8, 2006).
1955 オーバーライドされた場合にオーバーライドメソッド内で super を呼び出すべきメソッドにアノテートします。
1956 例えば、 finalize() や clone() がそのようなメソッドに該当します。
1957 引数は、 super の呼び出し箇所を指定します。すなわち、随時、メソッドの最初またはメソッドの最後です。
16841958 </para>
16851959 </listitem>
16861960 </varlistentry>
16891963 <term><command>edu.umd.cs.findbugs.annotations.PossiblyNull</command></term>
16901964 <listitem>
16911965 <para>
1692 This annotation is deprecated. Use CheckForNull instead.
1966 非推奨です。 CheckForNull を使用してください。
16931967 </para>
16941968 </listitem>
16951969 </varlistentry>
16971971 <varlistentry>
16981972 <term><command>edu.umd.cs.findbugs.annotations.SuppressWarnings</command></term>
16991973 <listitem>
1974 <para>
17001975 <command>[Target]</command> Type, Field, Method, Parameter, Constructor, Package
1976 </para>
17011977 </listitem>
17021978 <listitem>
17031979 <variablelist>
17051981 <term><command>[Parameter]</command></term>
17061982 <listitem>
17071983 <para>
1708 <command>value:</command>The name of the warning. More than one name can be specified.
1984 <command>value:</command>警告の名称。複数の名称を指定することができます。
17091985 </para>
17101986 </listitem>
17111987 <listitem>
17121988 <para>
1713 <command>justification:</command>Reason why the warning should be ignored. デフォルト値 :&quot;&quot;。</para>
1989 <command>justification:</command>警告を無視する理由。 デフォルト値 :&quot;&quot;。</para>
17141990 </listitem>
17151991 </varlistentry>
17161992 </variablelist>
17312007 <varlistentry>
17322008 <term><command>edu.umd.cs.findbugs.annotations.UnknownNullness</command></term>
17332009 <listitem>
2010 <para>
17342011 <command>[Target]</command> Field, Method, Parameter
2012 </para>
17352013 </listitem>
17362014 <listitem>
17372015 <para>
17432021 <varlistentry>
17442022 <term><command>edu.umd.cs.findbugs.annotations.UnknownNullness</command></term>
17452023 <listitem>
2024 <para>
17462025 <command>[Target]</command> Field, Method, Parameter
2026 </para>
17472027 </listitem>
17482028 <listitem>
17492029 <para>
17542034 </variablelist>
17552035
17562036 <para>また、 &FindBugs; 次に示すアノテーションもサポートしています。 :<itemizedlist>
1757 <listitem>net.jcip.annotations.GuardedBy</listitem>
1758 <listitem>net.jcip.annotations.Immutable</listitem>
1759 <listitem>net.jcip.annotations.NotThreadSafe</listitem>
1760 <listitem>net.jcip.annotations.ThreadSafe</listitem>
2037 <listitem><para>net.jcip.annotations.GuardedBy</para></listitem>
2038 <listitem><para>net.jcip.annotations.Immutable</para></listitem>
2039 <listitem><para>net.jcip.annotations.NotThreadSafe</para></listitem>
2040 <listitem><para>net.jcip.annotations.ThreadSafe</para></listitem>
17612041 </itemizedlist>
17622042 </para>
17632043 <para><ulink url="http://jcip.net/">Java Concurrency in Practice</ulink> の <ulink url="http://jcip.net/annotations/doc/index.html"> API ドキュメント</ulink> を参照してください。</para>
20152295 <row><entry>-newCode[:truth]</entry> <entry>newCode=&quot;[true|false]&quot;</entry> <entry>新クラスの追加によってもたらされた警告のみ出力されます。</entry></row>
20162296 <row><entry>-removedCode[:truth]</entry> <entry>removedCode=&quot;[true|false]&quot;</entry> <entry>クラスの削除によって除去された警告のみ出力されます。</entry></row>
20172297 <row><entry>-priority &lt;level&gt;</entry> <entry>priority=&quot;&lt;level&gt;&quot;</entry> <entry>指定した優先度以上の優先度をもつ警告のみ出力されます。</entry></row>
2298 <row><entry>-maxRank &lt;rank&gt;</entry> <entry>rank="[1..20]"</entry> <entry>allow only warnings with this rank or lower</entry></row>
20182299 <row><entry>-class &lt;pattern&gt;</entry> <entry>class=&quot;&lt;class&gt;&quot;</entry> <entry>指定したパターンに一致する主クラスをもつ警告のみ出力されます。</entry></row>
20192300 <row><entry>-bugPattern &lt;pattern&gt;</entry> <entry>bugPattern=&quot;&lt;pattern&gt;&quot;</entry> <entry>指定したパターンに一致するバグ種別をもつ警告のみ出力されます。</entry></row>
20202301 <row><entry>-category &lt;category&gt;</entry> <entry>category=&quot;&lt;category&gt;&quot;</entry> <entry>指定した文字列で始まるカテゴリーの警告のみ出力されます。</entry></row>
25822863 <para>Thomas Klaeger contributed a number of bug fixes and
25832864 bug detector improvements.</para>
25842865
2585 <para>Andrei Loskutov made a number of improvements to the
2586 Eclipse plugin.</para>
2866 <para>Andrey Loskutov made a number of bug fixes and
2867 bug detector improvements. He is maintainer of the Eclipse plugin.</para>
25872868
25882869 <para>Brian Goetz contributed a major refactoring of the
25892870 visitor classes to improve readability and understandability.</para>
2525 <h2>Important Request</h2>
2626 <p> If you are seeing any significant performance regressions in FindBugs 2.0,
2727 I very much need your help. Please either email <a href="mailto:findbugs@cs.umd.edu">findbugs@cs.umd.edu</a>
28 or file <a href="http://sourceforge.net/tracker/?atid=614693&amp;group_id=96405&amp;func=browse">a
28 or file <a href="http://sourceforge.net/p/findbugs/_list/tickets?source=navbar">a
2929 bug report</a>.&nbsp;with the following information from the xml file for your project (from both the
3030 1.3.9 and 2.0.0 version if possible). Sending me your code or pointing me to a open source repository
3131 would be great, but I know that isn't feasible for a lot of projects. The information I'm requesting
3232 doesn't include any information about the code being analyzed other than the total size of the code
3333 being analyzed and the total number of issues found at the different confidence levels. The
3434 &lt;FindBugsSummary ... &gt; start tag. For example: <quote> <pre>
35 &lt;FindBugsSummary timestamp="Tue, 30 Dec 2008 21:29:52 -0500"
36 total_classes="206" referenced_classes="325" total_bugs="72" total_size="7654" num_packages="21"
37 vm_version="20.4-b02-402" cpu_seconds="62.52" clock_seconds="22.01"
38 peak_mbytes="112.21" alloc_mbytes="1683.38" gc_seconds="1.19"
35 &lt;FindBugsSummary timestamp="Tue, 30 Dec 2008 21:29:52 -0500"
36 total_classes="206" referenced_classes="325" total_bugs="72" total_size="7654" num_packages="21"
37 vm_version="20.4-b02-402" cpu_seconds="62.52" clock_seconds="22.01"
38 peak_mbytes="112.21" alloc_mbytes="1683.38" gc_seconds="1.19"
3939 priority_3="56" priority_2="14" priority_1="2"&gt;
4040 </pre> </quote> The &lt;FindBugsProfile&gt;...&lt;/FindBugsProfile&gt; element. For example: <quote>
4141 <pre>
4242 &lt;FindBugsProfile&gt;
43 &lt;ClassProfile name="edu.umd.cs.findbugs.detect.IncompatMask" totalMilliseconds="11"
44 invocations="206" avgMicrosecondsPerInvocation="55" maxMicrosecondsPerInvocation="475"
43 &lt;ClassProfile name="edu.umd.cs.findbugs.detect.IncompatMask" totalMilliseconds="11"
44 invocations="206" avgMicrosecondsPerInvocation="55" maxMicrosecondsPerInvocation="475"
4545 standardDeviationMircosecondsPerInvocation="75"/&gt;
46 &lt;ClassProfile name="edu.umd.cs.findbugs.detect.FindFinalizeInvocations" totalMilliseconds="11"
47 invocations="206" avgMicrosecondsPerInvocation="55" maxMicrosecondsPerInvocation="402"
46 &lt;ClassProfile name="edu.umd.cs.findbugs.detect.FindFinalizeInvocations" totalMilliseconds="11"
47 invocations="206" avgMicrosecondsPerInvocation="55" maxMicrosecondsPerInvocation="402"
4848 standardDeviationMircosecondsPerInvocation="69"/&gt;
49 &lt;ClassProfile name="edu.umd.cs.findbugs.classfile.engine.bcel.LockDataflowFactory" totalMilliseconds="11"
50 invocations="23" avgMicrosecondsPerInvocation="515" maxMicrosecondsPerInvocation="2637"
49 &lt;ClassProfile name="edu.umd.cs.findbugs.classfile.engine.bcel.LockDataflowFactory" totalMilliseconds="11"
50 invocations="23" avgMicrosecondsPerInvocation="515" maxMicrosecondsPerInvocation="2637"
5151 standardDeviationMircosecondsPerInvocation="639"/&gt;
5252 ...
5353 &lt;/FindBugsProfile&gt;
2222
2323 <p>
2424 Please report bugs using the
25 <a href="http://sourceforge.net/tracker/?atid=614693&group_id=96405&func=browse">Sourceforge
25 <a href="http://sourceforge.net/p/findbugs/_list/tickets?source=navbar">Sourceforge
2626 bugs tracker</a>.&nbsp; Note that you need to be logged in to sourceforge to
2727 use the bug tracker.
2828 </p>
2929
3030 <p>
31 If you cannot use the Sourceforge tracker, you can try sending
31 If you cannot use the Sourceforge tracker, you can try sending
3232 email to the <a href="http://www.cs.umd.edu/mailman/listinfo/findbugs-discuss"
3333 >findbugs-discuss mailing list</a>.&nbsp; You must be subscribed
3434 to the list to post a message.
3939 However, such emails are much less likely to be handled in a timely manner than
4040 posts to the tracker or mailing list.
4141 </p>
42
42
4343 <h2>False and Inaccurate Warnings</h2>
44
44
4545 <p>
46 Like most bug-detection tools based on static analysis, FindBugs
47 issues some warnings that do not correspond to real bugs.&nbsp;
48 While in general we would like to make the percentage of such warnings
49 small, we can never fully eliminate them.
46 Like most bug-detection tools based on static analysis, FindBugs
47 issues some warnings that do not correspond to real bugs.&nbsp;
48 While in general we would like to make the percentage of such warnings
49 small, we can never fully eliminate them.
5050 </p>
5151
5252 <h2>Information to include</h2>
3131 import javax.swing.AbstractButton;
3232 import javax.swing.JButton;
3333 import javax.swing.JFrame;
34 import javax.swing.SwingUtilities;
3435
3536 import edu.umd.cs.findbugs.L10N;
3637 import edu.umd.cs.findbugs.SystemProperties;
3738
3839 /**
3940 * Class to handle Strings annotated with embedded mnemonics
40 *
41 *
4142 * Note: Since the human interface guidelines for Mac OS X say never to use
4243 * mnemonics, this class behaves as if no mnemonics are set when run on Mac OS
4344 * X.
7980 /**
8081 * Return the appropriate mnemonic character for this string. If no mnemonic
8182 * should be displayed, KeyEvent.VK_UNDEFINED is returned.
82 *
83 *
8384 * @return the Mnemonic character, or VK_UNDEFINED if no mnemonic should be
8485 * set
8586 */
125126 public static void main(String[] args) {
126127 // Some basic tests
127128
128 JFrame frame = new JFrame();
129 final JFrame frame = new JFrame();
129130 frame.getContentPane().setLayout(new FlowLayout());
130131
131132 addButton(frame, "&File");
141142 addButton(frame, "Cat & Dog");
142143 addButton(frame, "Cat && Dog");
143144
144 frame.pack();
145 frame.setVisible(true);
145 SwingUtilities.invokeLater(new Runnable() {
146 @Override
147 public void run() {
148 frame.pack();
149 frame.setVisible(true);
150 }
151 });
152
146153 }
147154
148155 /**
149156 * Localise the given AbstractButton, setting the text and optionally
150157 * mnemonic Note that AbstractButton includes menus and menu items.
151 *
158 *
152159 * @param button
153160 * The button to localise
154161 * @param key
2424
2525 package edu.umd.cs.findbugs.gui2;
2626
27 import java.io.BufferedReader;
2827 import java.io.IOException;
29 import java.io.InputStream;
3028 import java.net.URL;
3129 import java.text.MessageFormat;
3230 import java.util.regex.Pattern;
3533 import javax.swing.event.HyperlinkEvent;
3634
3735 import edu.umd.cs.findbugs.Version;
38 import edu.umd.cs.findbugs.charsets.UTF8;
3936 import edu.umd.cs.findbugs.log.Logger;
4037 import edu.umd.cs.findbugs.util.LaunchBrowser;
4138
7572 /**
7673 * Process an HTML page to replace certain substitution patterns. Right now,
7774 * we just expand @VERSION@.
78 */
75 *
7976 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings("OS_OPEN_STREAM")
8077 private void processPage(javax.swing.JEditorPane pane, String fileName) throws IOException {
8178 InputStream in = null;
113110 } catch (IOException e) {
114111 }
115112 }
116 }
113 }*/
117114
118115 /**
119116 * This method is called from within the constructor to initialize the form.
144141
145142 aboutEditorPane.setEditable(false);
146143 aboutEditorPane.addHyperlinkListener(new javax.swing.event.HyperlinkListener() {
144 @Override
147145 public void hyperlinkUpdate(javax.swing.event.HyperlinkEvent evt) {
148146 editorPaneHyperlinkUpdate(evt);
149147 }
155153
156154 licenseEditorPane.setEditable(false);
157155 licenseEditorPane.addHyperlinkListener(new javax.swing.event.HyperlinkListener() {
156 @Override
158157 public void hyperlinkUpdate(javax.swing.event.HyperlinkEvent evt) {
159158 editorPaneHyperlinkUpdate(evt);
160159 }
166165
167166 acknowldgementsEditorPane.setEditable(false);
168167 acknowldgementsEditorPane.addHyperlinkListener(new javax.swing.event.HyperlinkListener() {
168 @Override
169169 public void hyperlinkUpdate(javax.swing.event.HyperlinkEvent evt) {
170170 editorPaneHyperlinkUpdate(evt);
171171 }
195195 okButton.setMnemonic('O');
196196 okButton.setText(edu.umd.cs.findbugs.L10N.getLocalString("dlg.ok_btn", "OK"));
197197 okButton.addActionListener(new java.awt.event.ActionListener() {
198 @Override
198199 public void actionPerformed(java.awt.event.ActionEvent evt) {
199200 okButtonActionPerformed(evt);
200201 }
256257 // End of variables declaration//GEN-END:variables
257258
258259 }
259
260 // vim:ts=4
0 package edu.umd.cs.findbugs.gui2;
1
2 import java.awt.Component;
3 import java.awt.GridBagConstraints;
4 import java.awt.GridBagLayout;
5 import java.awt.Insets;
6 import java.awt.event.ActionEvent;
7 import java.awt.event.ActionListener;
8 import java.io.InputStream;
9 import java.lang.reflect.InvocationTargetException;
10 import java.net.URL;
11 import java.util.ArrayList;
12 import java.util.List;
13 import java.util.concurrent.ExecutorService;
14
15 import javax.swing.DefaultComboBoxModel;
16 import javax.swing.JComboBox;
17 import javax.swing.JComponent;
18 import javax.swing.JLabel;
19 import javax.swing.JOptionPane;
20 import javax.swing.JPanel;
21 import javax.swing.JPasswordField;
22 import javax.swing.JTextField;
23 import javax.swing.MutableComboBoxModel;
24 import javax.swing.ProgressMonitor;
25 import javax.swing.ProgressMonitorInputStream;
26 import javax.swing.SwingUtilities;
27 import javax.swing.event.DocumentEvent;
28 import javax.swing.event.DocumentListener;
29 import javax.swing.text.JTextComponent;
30
31 import edu.umd.cs.findbugs.AWTEventQueueExecutor;
32 import edu.umd.cs.findbugs.IGuiCallback;
33 import edu.umd.cs.findbugs.util.LaunchBrowser;
34
35 public abstract class AbstractSwingGuiCallback implements IGuiCallback {
36 private final AWTEventQueueExecutor bugUpdateExecutor = new AWTEventQueueExecutor();
37
38 private final Component parent;
39
40 public AbstractSwingGuiCallback(Component parent) {
41 this.parent = parent;
42 }
43
44 public ExecutorService getBugUpdateExecutor() {
45 return bugUpdateExecutor;
46 }
47
48 public void showMessageDialogAndWait(final String message) throws InterruptedException {
49 if (SwingUtilities.isEventDispatchThread())
50 JOptionPane.showMessageDialog(parent, message);
51 else
52 try {
53 SwingUtilities.invokeAndWait(new Runnable() {
54 public void run() {
55 JOptionPane.showMessageDialog(parent, message);
56 }
57 });
58 } catch (InvocationTargetException e) {
59 throw new IllegalStateException(e);
60 }
61 }
62
63 public void showMessageDialog(final String message) {
64 if (SwingUtilities.isEventDispatchThread())
65 JOptionPane.showMessageDialog(parent, message);
66 else
67 SwingUtilities.invokeLater(new Runnable() {
68 public void run() {
69 JOptionPane.showMessageDialog(parent, message);
70 }
71 });
72 }
73
74 public int showConfirmDialog(String message, String title, String ok, String cancel) {
75 return JOptionPane.showOptionDialog(parent, message, title, JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE,
76 null, new Object[] { ok, cancel }, ok);
77 }
78
79 public InputStream getProgressMonitorInputStream(InputStream in, int length, String msg) {
80 ProgressMonitorInputStream pmin = new ProgressMonitorInputStream(parent, msg, in);
81 ProgressMonitor pm = pmin.getProgressMonitor();
82
83 if (length > 0)
84 pm.setMaximum(length);
85 return pmin;
86 }
87
88 public void displayNonmodelMessage(String title, String message) {
89 DisplayNonmodelMessage.displayNonmodelMessage(title, message, parent, true);
90 }
91
92 public String showQuestionDialog(String message, String title, String defaultValue) {
93 return (String) JOptionPane.showInputDialog(parent, message, title, JOptionPane.QUESTION_MESSAGE, null, null,
94 defaultValue);
95 }
96
97 public List<String> showForm(String message, String title, List<FormItem> items) {
98 int result = showFormDialog(message, title, items);
99 if (result != JOptionPane.OK_OPTION)
100 return null;
101 updateFormItemsFromGui(items);
102 List<String> results = new ArrayList<String>();
103 for (FormItem item : items) {
104 results.add(item.getCurrentValue());
105 }
106 return results;
107 }
108
109 public boolean showDocument(URL u) {
110 return LaunchBrowser.showDocument(u);
111 }
112
113 public boolean isHeadless() {
114 return false;
115 }
116
117 public void invokeInGUIThread(Runnable r) {
118 SwingUtilities.invokeLater(r);
119 }
120
121
122 private void updateFormItemsFromGui(List<FormItem> items) {
123 for (FormItem item : items) {
124 JComponent field = item.getField();
125 if (field instanceof JTextComponent) {
126 JTextComponent textComponent = (JTextComponent) field;
127 item.setCurrentValue(textComponent.getText());
128
129 } else if (field instanceof JComboBox) {
130 JComboBox box = (JComboBox) field;
131 String value = (String) box.getSelectedItem();
132 item.setCurrentValue(value);
133 }
134 item.updated();
135 }
136 updateComboBoxes(items);
137 }
138
139 private void updateComboBoxes(List<FormItem> items) {
140 for (FormItem item : items) {
141 JComponent field = item.getField();
142 if (field instanceof JComboBox) {
143 JComboBox box = (JComboBox) field;
144 List<String> newPossibleValues = item.getPossibleValues();
145 if (!boxModelIsSame(box, newPossibleValues)) {
146 MutableComboBoxModel mmodel = (MutableComboBoxModel) box.getModel();
147 replaceBoxModelValues(mmodel, newPossibleValues);
148 mmodel.setSelectedItem(item.getCurrentValue());
149 }
150 }
151 }
152 }
153
154 private void replaceBoxModelValues(MutableComboBoxModel mmodel, List<String> newPossibleValues) {
155 try {
156 while (mmodel.getSize() > 0)
157 mmodel.removeElementAt(0);
158 } catch (Exception e) {
159 // ignore weird index out of bounds exceptions
160 }
161 for (String value : newPossibleValues) {
162 mmodel.addElement(value);
163 }
164 }
165
166 private boolean boxModelIsSame(JComboBox box, List<String> newPossibleValues) {
167 boolean same = true;
168 if (box.getModel().getSize() != newPossibleValues.size())
169 same = false;
170 else
171 for (int i = 0; i < box.getModel().getSize(); i++) {
172 if (!box.getModel().getElementAt(i).equals(newPossibleValues.get(i))) {
173 same = false;
174 break;
175 }
176 }
177 return same;
178 }
179
180 private int showFormDialog(String message, String title, final List<FormItem> items) {
181 JPanel panel = new JPanel();
182 panel.setLayout(new GridBagLayout());
183 GridBagConstraints gbc = new GridBagConstraints();
184 gbc.fill = GridBagConstraints.BOTH;
185 gbc.weightx = 1;
186 gbc.weighty = 0;
187 gbc.gridwidth = 2;
188 gbc.gridy = 1;
189 gbc.insets = new Insets(5, 5, 5, 5);
190 panel.add(new JLabel(message), gbc);
191 gbc.gridwidth = 1;
192
193 for (FormItem item : items) {
194 item.setItems(items);
195 gbc.gridy++;
196 panel.add(new JLabel(item.getLabel()), gbc);
197 String defaultValue = item.getDefaultValue();
198 if (item.getPossibleValues() != null) {
199 JComboBox box = createComboBox(items, item);
200 panel.add(box, gbc);
201
202 } else {
203 JTextField field = createTextField(items, item);
204 panel.add(field, gbc);
205 }
206 }
207
208 return JOptionPane.showConfirmDialog(parent, panel, title, JOptionPane.OK_CANCEL_OPTION);
209 }
210
211 private JTextField createTextField(final List<FormItem> items, FormItem item) {
212 String defaultValue = item.getDefaultValue();
213 JTextField field = (item.isPassword() ? new JPasswordField() : new JTextField());
214 if (defaultValue != null) {
215 field.setText(defaultValue);
216 }
217 item.setField(field);
218 field.getDocument().addDocumentListener(new DocumentListener() {
219 public void insertUpdate(DocumentEvent e) {
220 changed();
221 }
222
223 public void removeUpdate(DocumentEvent e) {
224 changed();
225 }
226
227 public void changedUpdate(DocumentEvent e) {
228 changed();
229 }
230
231 private void changed() {
232 updateFormItemsFromGui(items);
233 }
234 });
235 return field;
236 }
237
238 private JComboBox createComboBox(final List<FormItem> items, FormItem item) {
239 DefaultComboBoxModel model = new DefaultComboBoxModel();
240 JComboBox box = new JComboBox(model);
241 item.setField(box);
242 for (String possibleValue : item.getPossibleValues()) {
243 model.addElement(possibleValue);
244 }
245 String defaultValue = item.getDefaultValue();
246 if (defaultValue == null)
247 model.setSelectedItem(model.getElementAt(0));
248 else
249 model.setSelectedItem(defaultValue);
250 box.addActionListener(new ActionListener() {
251 public void actionPerformed(ActionEvent e) {
252 updateFormItemsFromGui(items);
253 }
254 });
255 return box;
256 }
257 }
0 package edu.umd.cs.findbugs.gui2;
1
2 import java.awt.Component;
3 import java.awt.GridBagConstraints;
4 import java.awt.GridBagLayout;
5 import java.awt.Insets;
6 import java.awt.event.ActionEvent;
7 import java.awt.event.ActionListener;
8 import java.io.InputStream;
9 import java.lang.reflect.InvocationTargetException;
10 import java.net.URL;
11 import java.util.ArrayList;
12 import java.util.List;
13 import java.util.concurrent.ExecutorService;
14
15 import javax.swing.DefaultComboBoxModel;
16 import javax.swing.JComboBox;
17 import javax.swing.JComponent;
18 import javax.swing.JLabel;
19 import javax.swing.JOptionPane;
20 import javax.swing.JPanel;
21 import javax.swing.JPasswordField;
22 import javax.swing.JTextField;
23 import javax.swing.MutableComboBoxModel;
24 import javax.swing.ProgressMonitor;
25 import javax.swing.ProgressMonitorInputStream;
26 import javax.swing.SwingUtilities;
27 import javax.swing.event.DocumentEvent;
28 import javax.swing.event.DocumentListener;
29 import javax.swing.text.JTextComponent;
30
31 import edu.umd.cs.findbugs.AWTEventQueueExecutor;
32 import edu.umd.cs.findbugs.IGuiCallback;
33 import edu.umd.cs.findbugs.util.LaunchBrowser;
34
35 public abstract class AbstractSwingGuiCallback implements IGuiCallback {
36 private final AWTEventQueueExecutor bugUpdateExecutor = new AWTEventQueueExecutor();
37
38 private final Component parent;
39
40 public AbstractSwingGuiCallback(Component parent) {
41 this.parent = parent;
42 }
43
44 @Override
45 public ExecutorService getBugUpdateExecutor() {
46 return bugUpdateExecutor;
47 }
48
49 @Override
50 public void showMessageDialogAndWait(final String message) throws InterruptedException {
51 if (SwingUtilities.isEventDispatchThread()) {
52 JOptionPane.showMessageDialog(parent, message);
53 } else {
54 try {
55 SwingUtilities.invokeAndWait(new Runnable() {
56 @Override
57 public void run() {
58 JOptionPane.showMessageDialog(parent, message);
59 }
60 });
61 } catch (InvocationTargetException e) {
62 throw new IllegalStateException(e);
63 }
64 }
65 }
66
67 @Override
68 public void showMessageDialog(final String message) {
69 if (SwingUtilities.isEventDispatchThread()) {
70 JOptionPane.showMessageDialog(parent, message);
71 } else {
72 SwingUtilities.invokeLater(new Runnable() {
73 @Override
74 public void run() {
75 JOptionPane.showMessageDialog(parent, message);
76 }
77 });
78 }
79 }
80
81 @Override
82 public int showConfirmDialog(String message, String title, String ok, String cancel) {
83 return JOptionPane.showOptionDialog(parent, message, title, JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE,
84 null, new Object[] { ok, cancel }, ok);
85 }
86
87 @Override
88 public InputStream getProgressMonitorInputStream(InputStream in, int length, String msg) {
89 ProgressMonitorInputStream pmin = new ProgressMonitorInputStream(parent, msg, in);
90 ProgressMonitor pm = pmin.getProgressMonitor();
91
92 if (length > 0) {
93 pm.setMaximum(length);
94 }
95 return pmin;
96 }
97
98 @Override
99 public void displayNonmodelMessage(String title, String message) {
100 DisplayNonmodelMessage.displayNonmodelMessage(title, message, parent, true);
101 }
102
103 @Override
104 public String showQuestionDialog(String message, String title, String defaultValue) {
105 return (String) JOptionPane.showInputDialog(parent, message, title, JOptionPane.QUESTION_MESSAGE, null, null,
106 defaultValue);
107 }
108
109 @Override
110 public List<String> showForm(String message, String title, List<FormItem> items) {
111 int result = showFormDialog(message, title, items);
112 if (result != JOptionPane.OK_OPTION) {
113 return null;
114 }
115 updateFormItemsFromGui(items);
116 List<String> results = new ArrayList<String>();
117 for (FormItem item : items) {
118 results.add(item.getCurrentValue());
119 }
120 return results;
121 }
122
123 @Override
124 public boolean showDocument(URL u) {
125 return LaunchBrowser.showDocument(u);
126 }
127
128 @Override
129 public boolean isHeadless() {
130 return false;
131 }
132
133 @Override
134 public void invokeInGUIThread(Runnable r) {
135 SwingUtilities.invokeLater(r);
136 }
137
138
139 private void updateFormItemsFromGui(List<FormItem> items) {
140 for (FormItem item : items) {
141 JComponent field = item.getField();
142 if (field instanceof JTextComponent) {
143 JTextComponent textComponent = (JTextComponent) field;
144 item.setCurrentValue(textComponent.getText());
145
146 } else if (field instanceof JComboBox) {
147 @SuppressWarnings("unchecked")
148 JComboBox<String> box = (JComboBox<String>) field;
149 String value = (String) box.getSelectedItem();
150 item.setCurrentValue(value);
151 }
152 item.updated();
153 }
154 updateComboBoxes(items);
155 }
156
157 private void updateComboBoxes(List<FormItem> items) {
158 for (FormItem item : items) {
159 JComponent field = item.getField();
160 if (field instanceof JComboBox) {
161 @SuppressWarnings("unchecked")
162 JComboBox<String> box = (JComboBox<String>) field;
163 List<String> newPossibleValues = item.getPossibleValues();
164 if (!boxModelIsSame(box, newPossibleValues)) {
165 MutableComboBoxModel<String> mmodel = (MutableComboBoxModel<String>) box.getModel();
166 replaceBoxModelValues(mmodel, newPossibleValues);
167 mmodel.setSelectedItem(item.getCurrentValue());
168 }
169 }
170 }
171 }
172
173 private void replaceBoxModelValues(MutableComboBoxModel<String> mmodel, List<String> newPossibleValues) {
174 try {
175 while (mmodel.getSize() > 0) {
176 mmodel.removeElementAt(0);
177 }
178 } catch (Exception e) {
179 // ignore weird index out of bounds exceptions
180 }
181 for (String value : newPossibleValues) {
182 mmodel.addElement(value);
183 }
184 }
185
186 private boolean boxModelIsSame(JComboBox<String> box, List<String> newPossibleValues) {
187 boolean same = true;
188 if (box.getModel().getSize() != newPossibleValues.size()) {
189 same = false;
190 } else {
191 for (int i = 0; i < box.getModel().getSize(); i++) {
192 if (!box.getModel().getElementAt(i).equals(newPossibleValues.get(i))) {
193 same = false;
194 break;
195 }
196 }
197 }
198 return same;
199 }
200
201 private int showFormDialog(String message, String title, final List<FormItem> items) {
202 JPanel panel = new JPanel();
203 panel.setLayout(new GridBagLayout());
204 GridBagConstraints gbc = new GridBagConstraints();
205 gbc.fill = GridBagConstraints.BOTH;
206 gbc.weightx = 1;
207 gbc.weighty = 0;
208 gbc.gridwidth = 2;
209 gbc.gridy = 1;
210 gbc.insets = new Insets(5, 5, 5, 5);
211 panel.add(new JLabel(message), gbc);
212 gbc.gridwidth = 1;
213
214 for (FormItem item : items) {
215 item.setItems(items);
216 gbc.gridy++;
217 panel.add(new JLabel(item.getLabel()), gbc);
218 String defaultValue = item.getDefaultValue();
219 if (item.getPossibleValues() != null) {
220 JComboBox<?> box = createComboBox(items, item);
221 panel.add(box, gbc);
222
223 } else {
224 JTextField field = createTextField(items, item);
225 panel.add(field, gbc);
226 }
227 }
228
229 return JOptionPane.showConfirmDialog(parent, panel, title, JOptionPane.OK_CANCEL_OPTION);
230 }
231
232 private JTextField createTextField(final List<FormItem> items, FormItem item) {
233 String defaultValue = item.getDefaultValue();
234 JTextField field = (item.isPassword() ? new JPasswordField() : new JTextField());
235 if (defaultValue != null) {
236 field.setText(defaultValue);
237 }
238 item.setField(field);
239 field.getDocument().addDocumentListener(new DocumentListener() {
240 @Override
241 public void insertUpdate(DocumentEvent e) {
242 changed();
243 }
244
245 @Override
246 public void removeUpdate(DocumentEvent e) {
247 changed();
248 }
249
250 @Override
251 public void changedUpdate(DocumentEvent e) {
252 changed();
253 }
254
255 private void changed() {
256 updateFormItemsFromGui(items);
257 }
258 });
259 return field;
260 }
261
262 private JComboBox<String> createComboBox(final List<FormItem> items, FormItem item) {
263 DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>();
264 JComboBox<String> box = new JComboBox<>(model);
265 item.setField(box);
266 for (String possibleValue : item.getPossibleValues()) {
267 model.addElement(possibleValue);
268 }
269 String defaultValue = item.getDefaultValue();
270 if (defaultValue == null) {
271 model.setSelectedItem(model.getElementAt(0));
272 } else {
273 model.setSelectedItem(defaultValue);
274 }
275 box.addActionListener(new ActionListener() {
276 @Override
277 public void actionPerformed(ActionEvent e) {
278 updateFormItemsFromGui(items);
279 }
280 });
281 return box;
282 }
283 }
4949 private volatile boolean analysisFinished = false;
5050
5151 @Nonnull
52 private Project project;
53
54 private AnalysisCallback callback;
55
56 private AnalysisThread analysisThread = new AnalysisThread();
52 private final Project project;
53
54 private final AnalysisCallback callback;
55
56 private final AnalysisThread analysisThread = new AnalysisThread();
5757
5858 private int count;
5959
6060 private int goal;
6161
62 private JLabel statusLabel;
63
64 private JProgressBar progressBar;
65
66 private JButton cancelButton;
62 private final JLabel statusLabel;
63
64 private final JProgressBar progressBar;
65
66 private final JButton cancelButton;
6767
6868 public static void show(@Nonnull final Project project) {
6969 AnalysisCallback callback = new AnalysisCallback() {
70 @Override
7071 public void analysisFinished(BugCollection results) {
7172 MainFrame instance = MainFrame.getInstance();
7273 assert results.getProject() == project;
7980 results.reinitializeCloud();
8081 }
8182
83 @Override
8284 public void analysisInterrupted() {
8385 MainFrame instance = MainFrame.getInstance();
8486 instance.updateProjectAndBugCollection(null);
8688 }
8789 };
8890 show(project, callback, false);
89
90 }
91
91
92 }
93
9294 /**
93 *
95 *
9496 * @param project
9597 * The Project to analyze
9698 * @param callback
101103 * analysis is complete. If true, the constructor does not return
102104 * until the analysis is either finished or interrupted.
103105 */
104
106
105107 public static void show(@Nonnull
106 Project project, AnalysisCallback callback, boolean joinThread) {
108 Project project, AnalysisCallback callback, boolean joinThread) {
107109 AnalyzingDialog dialog = new AnalyzingDialog(project, callback, joinThread);
108110 MainFrame.getInstance().acquireDisplayWait();
109111 try {
110112 dialog.analysisThread.start();
111 if (joinThread)
113 if (joinThread) {
112114 try {
113115 dialog.analysisThread.join();
114116 } catch (InterruptedException e) {
115117 }
118 }
116119 } finally {
117 if (joinThread)
120 if (joinThread) {
118121 MainFrame.getInstance().releaseDisplayWait();
119 }
120 }
121
122
122 }
123 }
124 }
125
126
123127
124128 /**
125 *
129 *
126130 * @param project
127131 * The Project to analyze
128132 * @param callback
134138 * until the analysis is either finished or interrupted.
135139 */
136140 private AnalyzingDialog(@Nonnull Project project, AnalysisCallback callback, boolean joinThread) {
137 if (project == null)
141 if (project == null) {
138142 throw new NullPointerException("null project");
143 }
139144 this.project = project;
140145 this.callback = callback;
141146 statusLabel = new JLabel(" ");
143148 progressBar.setStringPainted(true);
144149 cancelButton = new JButton(edu.umd.cs.findbugs.L10N.getLocalString("dlg.cancel_btn", "Cancel"));
145150 cancelButton.addActionListener(new ActionListener() {
151 @Override
146152 public void actionPerformed(ActionEvent evt) {
147153 cancel();
148154 }
154160 cancel();
155161 }
156162 });
157
163
158164 SwingUtilities.invokeLater(new Runnable() {
165 @Override
159166 public void run() {
160167 setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
161168 add(statusLabel);
177184 }
178185 }
179186 });
180
187
181188 }
182189
183190 private void cancel() {
196203 private void incrementCount() {
197204 count++;
198205 SwingUtilities.invokeLater(new Runnable() {
206 @Override
199207 public void run() {
200208 progressBar.setString(count + "/" + goal);
201209 progressBar.setValue(count);
207215 this.count = count;
208216 this.goal = goal;
209217 SwingUtilities.invokeLater(new Runnable() {
218 @Override
210219 public void run() {
211220 progressBar.setString(count + "/" + goal);
212221 progressBar.setValue(count);
215224 });
216225 }
217226
227 @Override
218228 public void finishArchive() {
219229 incrementCount();
220230 }
221231
232 @Override
222233 public void finishClass() {
223234 incrementCount();
224235 }
225236
237 @Override
226238 public void finishPerClassAnalysis() {
227239 updateStage(edu.umd.cs.findbugs.L10N.getLocalString("progress.finishing_analysis", "Finishing analysis..."));
228240 }
229241
242 @Override
230243 public void reportNumberOfArchives(int numArchives) {
231244 updateStage(edu.umd.cs.findbugs.L10N.getLocalString("progress.scanning_archives", "Scanning archives..."));
232245 updateCount(0, numArchives);
234247
235248 int pass = 0;
236249
250 @Override
237251 public void startAnalysis(int numClasses) {
238252 pass++;
239253 String localString = edu.umd.cs.findbugs.L10N.getLocalString("progress.analyzing_classes", "Analyzing classes...");
251265
252266 @Override
253267 public void run() {
254 if (project == null)
268 if (project == null) {
255269 throw new NullPointerException("null project");
270 }
256271
257272 BugCollection data;
258273 try {
286301 SwingUtilities.invokeLater(new Runnable() {
287302 /*
288303 * (non-Javadoc)
289 *
304 *
290305 * @see java.lang.Runnable#run()
291306 */
307 @Override
292308 public void run() {
293309 AnalyzingDialog.this.setVisible(false);
294310 }
299315 SwingUtilities.invokeLater(new Runnable() {
300316 /*
301317 * (non-Javadoc)
302 *
318 *
303319 * @see java.lang.Runnable#run()
304320 */
321 @Override
305322 public void run() {
306323 JOptionPane.showMessageDialog(MainFrame.getInstance(), message, title, JOptionPane.ERROR_MESSAGE);
307324 }
313330
314331 /*
315332 * (non-Javadoc)
316 *
333 *
317334 * @see edu.umd.cs.findbugs.FindBugsProgress#predictPassCount(int[])
318335 */
336 @Override
319337 public void predictPassCount(int[] classesPerPass) {
320338 this.classesPerPass = classesPerPass;
321339
322340 }
323341
342 @Override
324343 public void startArchive(String name) {
325344 // noop
326345 }
00 /*
11 * FindBugs - Find Bugs in Java programs
22 * Copyright (C) 2003-2008 University of Maryland
3 *
3 *
44 * This library is free software; you can redistribute it and/or
55 * modify it under the terms of the GNU Lesser General Public
66 * License as published by the Free Software Foundation; either
77 * version 2.1 of the License, or (at your option) any later version.
8 *
8 *
99 * This library is distributed in the hope that it will be useful,
1010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1111 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1212 * Lesser General Public License for more details.
13 *
13 *
1414 * You should have received a copy of the GNU Lesser General Public
1515 * License along with this library; if not, write to the Free Software
1616 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2424
2525 /**
2626 * Updates filters in the current running FindBugs.
27 *
27 *
2828 * @author Graham Allan
2929 */
3030 public class ApplyNewFilter {
3838 this.preferencesFrame = preferencesFrame;
3939 this.filterActivityNotifier = filterActivityNotifier;
4040 }
41
41
4242 public void fromMatcher(Matcher matcher) {
4343 if (matcher != null) {
4444 suppressionFilter.addChild(matcher);
45
45
4646 preferencesFrame.updateFilterPanel();
4747 filterActivityNotifier.notifyListeners(FilterListener.Action.FILTERING, null);
4848 }
4949 }
50
50
5151 }
2828 * could be sent to the main bugset to return all the bugs it contains For
2929 * example, a single bugAspects could be <priority,high> or it could be
3030 * <priority,high>, <designation,must fix>,<class,fishpond>,<package,default>
31 *
31 *
3232 * In this implementation, <priority,high>,<designation,unclassified> is
3333 * different from <designation,unclassified>,<priority,high>. (I'm not talking
3434 * about the fact we use the .equals from ArrayList, I'm talking about what a
3939 * more like Swing's validate, only clearing data if the data is wrong. This
4040 * would save time after changing certain aspects of the tree. Just an idea, I
4141 * wouldn't suggest it unless its absolutely necessary. -Dan
42 *
43 *
42 *
43 *
4444 * @author All of us
4545 */
4646 public class BugAspects implements Iterable<BugAspects.SortableValue> {
6464
6565 @Override
6666 public String toString() {
67 if (lst.isEmpty())
67 if (lst.isEmpty()) {
6868 return edu.umd.cs.findbugs.L10N.getLocalString("tree.bugs", "Bugs") + " (" + count + ")";
69 else {
70 if (count == -1)
69 } else {
70 if (count == -1) {
7171 return last().value;
72 else
72 } else {
7373 return last().key.formatValue(last().value) + " (" + count + ")";
74 }
7475 }
7576 }
7677
7778 /**
7879 * This is how the numbers after the branches contain the number of bugs in
7980 * them, even if they aren't the final branch
80 *
81 *
8182 * @param count
8283 */
8384 public void setCount(int count) {
113114
114115 public StackedFilterMatcher getStackedFilterMatcher() {
115116 FilterMatcher[] filters = new FilterMatcher[lst.size()];
116 for (int i = 0; i < filters.length; i++)
117 for (int i = 0; i < filters.length; i++) {
117118 filters[i] = new FilterMatcher(lst.get(i));
119 }
118120 StackedFilterMatcher sfm = new StackedFilterMatcher(filters);
119121 return sfm;
120122 }
140142
141143 @Override
142144 public boolean equals(Object that) {
143 if (!(that instanceof SortableValue))
145 if (!(that instanceof SortableValue)) {
144146 return false;
147 }
145148 SortableValue thatStringPair = ((SortableValue) that);
146149 return this.key.equals(thatStringPair.key) && this.value.equals(thatStringPair.value);
147150 }
152155 }
153156 }
154157
158 @Override
155159 public Iterator<SortableValue> iterator() {
156160 return lst.iterator();
157161 }
3131
3232 public class BugLeafNode {
3333
34 private BugInstance bug;
34 private final BugInstance bug;
3535
3636 BugLeafNode(BugInstance b) {
3737 bug = b;
4343
4444 @Override
4545 public String toString() {
46 if (bug.isDead())
46 if (bug.isDead()) {
4747 return "\u2620 " + bug.getMessageWithoutPrefix();
48 }
4849 return bug.getMessageWithoutPrefix();
4950 }
5051
6970
7071 @Override
7172 public boolean equals(Object o) {
72 if (!(o instanceof BugLeafNode))
73 if (!(o instanceof BugLeafNode)) {
7374 return false;
74 else
75 } else {
7576 return bug == (((BugLeafNode) o).getBug());
77 }
7678 }
7779
7880 @Override
8183 }
8284
8385 public boolean matches(BugAspects aspects) {
84 if (aspects.size() == 0)
86 if (aspects.size() == 0) {
8587 return true;
88 }
8689 for (BugAspects.SortableValue strPair : aspects) {
87 if (!matches(strPair))
90 if (!matches(strPair)) {
8891 return false;
92 }
8993 }
9094
9195 return true;
1717 */
1818
1919 package edu.umd.cs.findbugs.gui2;
20
21 import static java.util.Objects.requireNonNull;
2022
2123 import java.awt.Dimension;
2224 import java.io.File;
8688 * @throws IOException
8789 */
8890 public static BugCollection doAnalysis(@Nonnull Project p, FindBugsProgress progressCallback) throws IOException,
89 InterruptedException {
91 InterruptedException {
9092 StringWriter stringWriter = new StringWriter();
9193 BugCollectionBugReporter pcb = new BugCollectionBugReporter(p, new PrintWriter(stringWriter, true));
92 pcb.setPriorityThreshold(Priorities.NORMAL_PRIORITY);
94 pcb.setPriorityThreshold(Priorities.LOW_PRIORITY);
9395 IFindBugsEngine fb = createEngine(p, pcb);
9496 fb.setUserPreferences(getUserPreferences());
9597 fb.setProgressCallback(progressCallback);
155157 return col;
156158 }
157159
158 /**
159 * @param col
160 */
161160 private static void initiateCommunication(SortedBugCollection col) {
162161 Cloud cloud = col.getCloud();
163 if (cloud != null)
164 cloud.initiateCommunication();
162 cloud.initiateCommunication();
165163 }
166164
167165 public static @CheckForNull
182180 initiateCommunication(col);
183181 addDeadBugMatcher(col);
184182
185 } catch (Exception e) {
183 } catch (Throwable e) {
186184 String msg = SystemProperties.getOSDependentProperty("findbugs.unableToLoadViaURL");
187 if (msg == null)
185 if (msg == null) {
188186 msg = e.getMessage();
189 else try {
190 msg = String.format(msg, url);
191 } catch (Exception e2) {
192 msg = e.getMessage();
187 } else {
188 try {
189 msg = String.format(msg, url);
190 } catch (Exception e2) {
191 msg = e.getMessage();
192 }
193193 }
194194 JOptionPane.showMessageDialog(mainFrame, "Could not read " + url + "\n" + msg);
195 if (SystemProperties.getBoolean("findbugs.failIfUnableToLoadViaURL"))
195 if (SystemProperties.getBoolean("findbugs.failIfUnableToLoadViaURL")) {
196196 System.exit(1);
197 }
197198 }
198199 MainFrame.getInstance().setProjectAndBugCollectionInSwingThread(project, col);
199200 return col;
203204 if (bugCollection == null || !bugCollection.hasDeadBugs()) {
204205 return;
205206 }
206
207
207208 Filter suppressionMatcher = bugCollection.getProject().getSuppressionFilter();
208 if (suppressionMatcher != null) {
209 suppressionMatcher.softAdd(LastVersionMatcher.DEAD_BUG_MATCHER);
210 }
209 suppressionMatcher.softAdd(LastVersionMatcher.DEAD_BUG_MATCHER);
211210 }
212211
213212 public static @CheckForNull
248247 // This is done by FBFileChooser.
249248 chooser.setMultiSelectionEnabled(true);
250249 chooser.setDialogTitle(edu.umd.cs.findbugs.L10N.getLocalString("dlg.choose_xmls_ttl", "Choose All XML's To Combine"));
251 if (chooser.showOpenDialog(MainFrame.getInstance()) == JFileChooser.CANCEL_OPTION)
250 if (chooser.showOpenDialog(MainFrame.getInstance()) == JFileChooser.CANCEL_OPTION) {
252251 return null;
252 }
253253
254254 SortedBugCollection conglomeration = new SortedBugCollection();
255255 conglomeration.readXML(chooser.getSelectedFiles()[0]);
259259 SortedBugCollection col = new SortedBugCollection();
260260 col.readXML(f);
261261 conglomeration = (SortedBugCollection) update.mergeCollections(conglomeration, col, false, false);// False
262 // means
263 // dont
264 // show
265 // dead
266 // bugs
262 // means
263 // dont
264 // show
265 // dead
266 // bugs
267267 }
268268
269269 return conglomeration;
286286 */
287287 public static @CheckForNull
288288 BugCollection doAnalysis(@Nonnull Project p) {
289 if (p == null)
290 throw new NullPointerException("null project");
289 requireNonNull(p, "null project");
291290
292291 RedoAnalysisCallback ac = new RedoAnalysisCallback();
293292
294293 AnalyzingDialog.show(p, ac, true);
295294
296 if (ac.finished)
295 if (ac.finished) {
297296 return ac.getBugCollection();
298 else
299 return null;
297 } else {
298 return null;
299 }
300300
301301 }
302302
309309 */
310310 public static @CheckForNull
311311 BugCollection redoAnalysisKeepComments(@Nonnull Project p) {
312 if (p == null)
313 throw new NullPointerException("null project");
314
312 requireNonNull(p, "null project");
313
315314 BugCollection current = MainFrame.getInstance().getBugCollection();
316
315
317316 Update update = new Update();
318317
319318 RedoAnalysisCallback ac = new RedoAnalysisCallback();
320319
321320 AnalyzingDialog.show(p, ac, true);
322321
323 if (!ac.finished)
324 return null;
325 if (current == null)
322 if (!ac.finished) {
323 return null;
324 }
325 if (current == null) {
326326 current = ac.getBugCollection();
327 else {
327 } else {
328328 current = update.mergeCollections(current, ac.getBugCollection(), true, false);
329329 if (current.hasDeadBugs()) {
330330 addDeadBugMatcher(current);
331331 }
332332 }
333 return current;
334
333 return current;
334
335335
336336 }
337337
346346
347347 volatile boolean finished;
348348
349 @Override
349350 public void analysisFinished(BugCollection b) {
350351 justAnalyzed = b;
351352 finished = true;
352353 }
353354
355 @Override
354356 public void analysisInterrupted() {
355357 finished = false;
356358 }
4242 int row, boolean hasFocus) {
4343 Component toReturn = super.getTreeCellRendererComponent(tree, node, selected, expanded, leaf, row, hasFocus);
4444
45 if (!(node instanceof BugLeafNode))
45 if (!(node instanceof BugLeafNode)) {
4646 return toReturn;
47 else {
47 } else {
4848 BugInstance bug = ((BugLeafNode) node).getBug();
4949 final Color c;
5050 switch (bug.getPriority()) {
5252 c = new Color(0.4f, 0.4f, 0.6f);
5353 break;
5454 case Priorities.NORMAL_PRIORITY:
55 if (bug.isDead())
55 if (bug.isDead()) {
5656 c = new Color(0.2f, 0.2f, 0.2f);
57 else
57 } else {
5858 c = new Color(255, 204, 0);
59 }
5960 break;
6061 case Priorities.HIGH_PRIORITY:
61 if (bug.isDead())
62 if (bug.isDead()) {
6263 c = new Color(.65f, 0.2f, 0.2f);
63 else
64 } else {
6465 c = new Color(.85f, 0, 0);
66 }
6567 break;
6668 case Priorities.EXP_PRIORITY:
6769 case Priorities.IGNORE_PRIORITY:
7173 }
7274 if (leaf) {
7375 Icon icon = new Icon() {
76 @Override
7477 public void paintIcon(Component comp, Graphics g, int x, int y) {
7578 Graphics2D g2 = (Graphics2D) g;
7679 g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
8083 g2.drawOval(2, 2, 12, 12);
8184 }
8285
86 @Override
8387 public int getIconWidth() {
8488 return 16;
8589 }
8690
91 @Override
8792 public int getIconHeight() {
8893 return 16;
8994 }
9095 };
91 ((BugRenderer) toReturn).setLeafIcon(icon);
96 ((BugRenderer) toReturn).setIcon(icon);
9297 }
9398 return toReturn;
9499 }
5959
6060 private ArrayList<BugLeafNode> mainList;
6161
62 private HashMap<SortableValue, BugSet> doneMap;
63
64 private HashMap<SortableValue, Boolean> doneContainsMap;
62 private final HashMap<SortableValue, BugSet> doneMap;
63
64 private final HashMap<SortableValue, Boolean> doneContainsMap;
6565
6666 private HashMap<Sortables, String[]> sortablesToStrings;
6767
107107
108108 BugSet(BugCollection bugCollection) {
109109 this(Collections.<BugLeafNode> emptyList());
110 for (Iterator<BugInstance> i = bugCollection.iterator(); i.hasNext();)
110 for (Iterator<BugInstance> i = bugCollection.iterator(); i.hasNext();) {
111111 mainList.add(new BugLeafNode(i.next()));
112 }
112113
113114 }
114115
151152
152153 String[] computeDistinctValues(Sortables key) {
153154
154 if (key == Sortables.DIVIDER)
155 if (key == Sortables.DIVIDER) {
155156 return EMPTY_STRING_ARRAY;
157 }
156158
157159 Collection<String> list = new HashSet<String>();
158160
159161 for (BugLeafNode p : mainList) {
160 if (suppress(p))
162 if (suppress(p)) {
161163 continue;
164 }
162165 BugInstance bug = p.getBug();
163166
164167 String value = key.getFrom(bug);
177180 */
178181 static int countFilteredBugs() {
179182 int result = 0;
180 for (BugLeafNode bug : getMainBugSet().mainList)
181 if (suppress(bug))
183 for (BugLeafNode bug : getMainBugSet().mainList) {
184 if (suppress(bug)) {
182185 result++;
186 }
187 }
183188
184189 return result;
185190 }
208213 * with all the Mutable Static bugs in the current set Note also: This query
209214 * will only be performed once, and then stored and reused if the same query
210215 * is used again.
211 *
212 * @param keyValuePair
213 * @return
214216 */
215217 BugSet query(SortableValue keyValuePair) {
216 if (doneMap.containsKey(keyValuePair))
218 if (doneMap.containsKey(keyValuePair)) {
217219 return doneMap.get(keyValuePair);
220 }
218221 ArrayList<BugLeafNode> bugs = new ArrayList<BugLeafNode>();
219222
220223 for (BugLeafNode b : mainList) {
221 if (b.matches(keyValuePair))
224 if (b.matches(keyValuePair)) {
222225 bugs.add(b);
226 }
223227 }
224228
225229 BugSet temp = new BugSet(bugs);
237241
238242 Comparator<BugLeafNode> comparator = new Comparator<BugLeafNode>() {
239243 int compare(int one, int two) {
240 if (one > two)
244 if (one > two) {
241245 return 1;
242 else if (one < two)
246 } else if (one < two) {
243247 return -1;
248 }
244249 return 0;
245250 }
246251
252 @Override
247253 public int compare(BugLeafNode one, BugLeafNode two) {
248 if (one == two)
254 if (one == two) {
249255 return 0;
256 }
250257 int result;
251258 for (Sortables i : order) {
252259 result = i.getBugLeafNodeComparator().compare(one, two);
253 if (result != 0)
260 if (result != 0) {
254261 return result;
262 }
255263 }
256264 BugInstance bugOne = one.getBug();
257265 BugInstance bugTwo = two.getBug();
258266 result = bugOne.getPrimaryClass().getClassName().compareTo(bugTwo.getPrimaryClass().getClassName());
259 if (result != 0)
267 if (result != 0) {
260268 return result;
269 }
261270 SourceLineAnnotation oneSource = bugOne.getPrimarySourceLineAnnotation();
262271 SourceLineAnnotation twoSource = bugTwo.getPrimarySourceLineAnnotation();
263272 result = oneSource.getClassName().compareTo(twoSource.getClassName());
264 if (result != 0)
273 if (result != 0) {
265274 return result;
275 }
266276 result = compare(oneSource.getStartLine(), twoSource.getStartLine());
267 if (result != 0)
277 if (result != 0) {
268278 return result;
279 }
269280 result = compare(oneSource.getEndLine(), twoSource.getEndLine());
270 if (result != 0)
281 if (result != 0) {
271282 return result;
283 }
272284 result = compare(oneSource.getStartBytecode(), twoSource.getStartBytecode());
273 if (result != 0)
285 if (result != 0) {
274286 return result;
287 }
275288 result = compare(oneSource.getEndBytecode(), twoSource.getEndBytecode());
276289 return result;
277290
278291 }
279292 };
280 Collections.sort(mainList, comparator);
281
282 if (SystemProperties.ASSERTIONS_ENABLED)
283 for(int i = 0; i < mainList.size(); i++) {
284 BugLeafNode nodeI = mainList.get(i);
285
286 for(int j = i+1; j < mainList.size(); j++) {
287 BugLeafNode nodeJ = mainList.get(j);
288 if (comparator.compare(nodeI, nodeJ) > 0)
289 throw new AssertionError(
290 String.format("bug list isn't consistently sorted (%d:%s) vs. (%d:%s)",
291 i, nodeI.getBug().getInstanceHash(), j, nodeJ.getBug().getInstanceHash()));
292 }}
293 ArrayList<BugLeafNode> copy = new ArrayList<BugLeafNode>(mainList);
294 Collections.sort(copy, comparator);
295 mainList = copy;
296
297 if (SystemProperties.ASSERTIONS_ENABLED) {
298 for(int i = 0; i < mainList.size(); i++) {
299 BugLeafNode nodeI = mainList.get(i);
300
301 for(int j = i+1; j < mainList.size(); j++) {
302 BugLeafNode nodeJ = mainList.get(j);
303 if (comparator.compare(nodeI, nodeJ) > 0) {
304 throw new AssertionError(
305 String.format("bug list isn't consistently sorted (%d:%s) vs. (%d:%s)",
306 i, nodeI.getBug().getInstanceHash(), j, nodeJ.getBug().getInstanceHash()));
307 }
308 }}
309 }
293310
294311
295312
303320 * @return true if a bug leaf from filterNoCache() matches the pair
304321 */
305322 public boolean contains(SortableValue keyValuePair) {
306 if (doneContainsMap.containsKey(keyValuePair))
323 if (doneContainsMap.containsKey(keyValuePair)) {
307324 return doneContainsMap.get(keyValuePair);
325 }
308326
309327 for (BugLeafNode p : filteredBugsCached().mainList) {
310328 if (p.matches(keyValuePair)) {
340358 return mainList.get(index);
341359 }
342360
361 @Override
343362 public Iterator<BugLeafNode> iterator() {
344363 return mainList.iterator();
345364 }
350369 this.mainList = new ArrayList<BugLeafNode>(filteredSet);
351370 doneMap = new HashMap<SortableValue, BugSet>();
352371 doneContainsMap = new HashMap<SortableValue, Boolean>();
353 if (cacheSortables)
372 if (cacheSortables) {
354373 cacheSortables();
374 }
355375 }
356376
357377 private BugSet filteredBugsNoCache() {
358378
359379 ArrayList<BugLeafNode> people = new ArrayList<BugLeafNode>();
360380 for (BugLeafNode p : mainList) {
361 if (!suppress(p))
381 if (!suppress(p)) {
362382 people.add(p);
383 }
363384 }
364385 return new BugSet(people, false);
365386 }
371392 }
372393
373394 private BugSet filteredBugsCached() {
374 if (cache == null)
395 if (cache == null) {
375396 cache = filteredBugsNoCache();
397 }
376398 return cache;
377399 }
378400
379401 public BugSet getBugsMatchingFilter(Matcher m) {
380402 ArrayList<BugLeafNode> people = new ArrayList<BugLeafNode>();
381403 for (BugLeafNode p : mainList) {
382 if (!(m.match(p.getBug())))
404 if (!(m.match(p.getBug()))) {
383405 people.add(p);
406 }
384407 }
385408 return new BugSet(people, false);
386409 }
8585 public class BugTreeModel implements TreeModel, TableColumnModelListener, TreeExpansionListener {
8686 private BugAspects root = new BugAspects();
8787
88 private SorterTableColumnModel st;
88 private final SorterTableColumnModel st;
8989
9090 private BugSet bugSet;
9191
114114 BugSet.setAsRootAndCache(this.bugSet);
115115 root.setCount(data.size());
116116 FilterActivity.addFilterListener(bugTreeFilterListener);
117 if (DEBUG)
117 if (DEBUG) {
118118 this.addTreeModelListener(new TreeModelListener() {
119119
120 @Override
120121 public void treeNodesChanged(TreeModelEvent arg0) {
121122 System.out.println("Tree nodes changed");
122123 System.out.println(" " + arg0.getTreePath());
123124 }
124125
126 @Override
125127 public void treeNodesInserted(TreeModelEvent arg0) {
126128 System.out.println("Tree nodes inserted");
127129 System.out.println(" " + arg0.getTreePath());
128130 }
129131
132 @Override
130133 public void treeNodesRemoved(TreeModelEvent arg0) {
131134 System.out.println("Tree nodes removed");
132135 System.out.println(" " + arg0.getTreePath());
133136 }
134137
138 @Override
135139 public void treeStructureChanged(TreeModelEvent arg0) {
136140 System.out.println("Tree structure changed");
137141 System.out.println(" " + arg0.getTreePath());
138142 }
139143 });
144 }
140145 }
141146
142147 public BugTreeModel(BugTreeModel other) {
158163 bugSet.clearCache();
159164 }
160165
166 @Override
161167 public Object getRoot() {
162168 return root;
163169 }
164170
171 @Override
165172 public Object getChild(Object o, int index) {
166173 int childCount = getChildCount(o);
167174 if (index < 0 || index >= childCount) {
205212 }
206213 }
207214
215 @Override
208216 public int getChildCount(Object o) {
209217
210 if (!(o instanceof BugAspects))
218 if (!(o instanceof BugAspects)) {
211219 return 0;
220 }
212221
213222 BugAspects a = (BugAspects) o;
214223
215 if (st.getOrderBeforeDivider().size() == 0 && a.size() == 0)// If its
216 // the root
217 // and we
218 // aren't
219 // sorting
220 // by
221 // anything
224 if (st.getOrderBeforeDivider().size() == 0 && a.size() == 0) {
225 // the root
226 // and we
227 // aren't
228 // sorting
229 // by
230 // anything
222231 return bugSet.size();
223
224 if ((a.size() == 0) || (a.last().key != st.getOrderBeforeDivider().get(st.getOrderBeforeDivider().size() - 1)))
232 }
233
234 if ((a.size() == 0) || (a.last().key != st.getOrderBeforeDivider().get(st.getOrderBeforeDivider().size() - 1))) {
225235 return enumsThatExist(a).size();
226 else
236 } else {
227237 return bugSet.query(a).size();
238 }
228239 }
229240
230241 /*
242253 }
243254
244255 Sortables key;
245 if (a.size() == 0)
256 if (a.size() == 0) {
246257 key = orderBeforeDivider.get(0);
247 else {
258 } else {
248259 Sortables lastKey = a.last().key;
249260 int index = orderBeforeDivider.indexOf(lastKey);
250 if (index + 1 < orderBeforeDivider.size())
261 if (index + 1 < orderBeforeDivider.size()) {
251262 key = orderBeforeDivider.get(index + 1);
252 else
263 } else {
253264 key = lastKey;
265 }
254266 }
255267
256268 String[] all = key.getAll(bugSet.query(a));
257269 ArrayList<SortableValue> result = new ArrayList<SortableValue>(all.length);
258 for (String i : all)
270 for (String i : all) {
259271 result.add(new SortableValue(key, i));
272 }
260273 return result;
261274
262275 }
263276
277 @Override
264278 public boolean isLeaf(Object o) {
265279 return (o instanceof BugLeafNode);
266280 }
267281
282 @Override
268283 public void valueForPathChanged(TreePath arg0, Object arg1) {
269284 }
270285
286 @Override
271287 public int getIndexOfChild(Object parent, Object child) {
272 if (parent == null || child == null || isLeaf(parent))
288 if (parent == null || child == null || isLeaf(parent)) {
273289 return -1;
290 }
274291
275292 if (isLeaf(child)) {
276293 return bugSet.query((BugAspects) parent).indexOf((BugLeafNode) child);
281298 }
282299 }
283300
301 @Override
284302 public void addTreeModelListener(TreeModelListener listener) {
285303 listeners.add(listener);
286304 }
287305
306 @Override
288307 public void removeTreeModelListener(TreeModelListener listener) {
289308 listeners.remove(listener);
290309 }
291310
311 @Override
292312 public void columnAdded(TableColumnModelEvent e) {
293313 sortsAddedOrRemoved = true;
294314 // rebuild();
295315 }
296316
317 @Override
297318 public void columnRemoved(TableColumnModelEvent e) {
298319 sortsAddedOrRemoved = true;
299320 // rebuild();
300321 }
301322
323 @Override
302324 public void columnMoved(final TableColumnModelEvent evt) {
303 if (evt.getFromIndex() == evt.getToIndex())
325 if (evt.getFromIndex() == evt.getToIndex()) {
304326 return;
327 }
305328 sortOrderChanged = true;
306329 // rebuild();
307330 }
322345 *
323346 */
324347 public void rebuild() {
325 if (TRACE)
348 if (TRACE) {
326349 System.out.println("rebuilding bug tree model");
350 }
327351
328352 NewFilterFromBug.closeAll();
329353
335359 // As of now, it should be impossible to interrupt a rebuilding thread,
336360 // in another version this may change, so this if statement check is
337361 // left in, even though it should always be true.
338 if (rebuildingThread == null)
362 if (rebuildingThread == null) {
339363 setOldSelectedBugs();
364 }
340365
341366 Debug.println("Please Wait called right before starting rebuild thread");
342367 mainFrame.acquireDisplayWait();
343368 rebuildingThread = edu.umd.cs.findbugs.util.Util.runInDameonThread(new Runnable() {
344369 BugTreeModel newModel;
345370
371 @Override
346372 public void run() {
347373 try {
348374 newModel = new BugTreeModel(BugTreeModel.this);
352378 } finally {
353379 rebuildingThread = null;
354380 SwingUtilities.invokeLater(new Runnable() {
381 @Override
355382 public void run() {
356383 if (newModel != null) {
357384 JTree newTree = new JTree(newModel);
369396 }
370397
371398 public void crawl(final ArrayList<BugAspects> path, final int depth) {
372 for (int i = 0; i < getChildCount(path.get(path.size() - 1)); i++)
399 for (int i = 0; i < getChildCount(path.get(path.size() - 1)); i++) {
373400 if (depth > 0) {
374401 ArrayList<BugAspects> newPath = new ArrayList<BugAspects>(path);
375402 newPath.add((BugAspects) getChild(path.get(path.size() - 1), i));
376403 crawl(newPath, depth - 1);
377404 } else {
378 for (TreeModelListener l : listeners)
405 for (TreeModelListener l : listeners) {
379406 l.treeStructureChanged(new TreeModelEvent(this, path.toArray()));
380 }
407 }
408 }
409 }
381410 }
382411
383412 void openPreviouslySelected(List<BugLeafNode> selected) {
387416 try {
388417 BugInstance bug = b.getBug();
389418 TreePath path = getPathToBug(bug);
390 if (path == null)
419 if (path == null) {
391420 continue;
421 }
392422 Debug.printf("Opening %s\n", path);
393423 mainFrame.getTree().expandPath(path.getParentPath());
394424 mainFrame.getTree().addSelectionPath(path);
408438
409439 public void crawlToOpen(TreePath path, ArrayList<BugLeafNode> bugLeafNodes, ArrayList<TreePath> treePaths) {
410440 for (int i = 0; i < getChildCount(path.getLastPathComponent()); i++) {
411 if (!isLeaf(getChild(path.getLastPathComponent(), i)))
441 if (!isLeaf(getChild(path.getLastPathComponent(), i))) {
412442 for (BugLeafNode p : bugLeafNodes) {
413443 if (p.matches((BugAspects) getChild(path.getLastPathComponent(), i))) {
414444 tree.expandPath(path);
416446 break;
417447 }
418448 }
419 else {
449 } else {
420450 for (BugLeafNode b : bugLeafNodes) {
421451 if (getChild(path.getLastPathComponent(), i).equals(b)) {
422452 tree.expandPath(path);
430460 public static boolean TRACE = false;
431461
432462 public void resetData()// FIXME: Does this need a setAsRootAndCache() on the
433 // new BugSet?
463 // new BugSet?
434464 {
435 if (TRACE)
465 if (TRACE) {
436466 System.out.println("Reseting data in bug tree model");
467 }
437468 bugSet = new BugSet(bugSet);
438469 }
439470
440471 FilterListener bugTreeFilterListener = new MyFilterListener();
441472
442473 class MyFilterListener implements FilterListener {
474 @Override
443475 public void clearCache() {
444 if (TRACE)
476 if (TRACE) {
445477 System.out.println("clearing cache in bug tree model");
478 }
446479 resetData();
447480 BugSet.setAsRootAndCache(bugSet);// FIXME: Should this be in
448 // resetData? Does this allow our
449 // main list to not be the same as
450 // the data in our tree?
481 // resetData? Does this allow our
482 // main list to not be the same as
483 // the data in our tree?
451484 root.setCount(bugSet.size());
452485
453486 rebuild();
454487 }
455488
456 }
489 }
457490
458491 void treeNodeChanged(TreePath path) {
459492 Debug.println("Tree Node Changed: " + path);
479512 // Create an array of BugAspects of lengths from one to the full
480513 // BugAspect list of the bugInstance
481514 BugAspects[] toBug = new BugAspects[order.size()];
482 for (int i = 0; i < order.size(); i++)
515 for (int i = 0; i < order.size(); i++) {
483516 toBug[i] = new BugAspects();
517 }
484518
485519 for (int x = 0; x < order.size(); x++) {
486520 for (int y = 0; y <= x; y++) {
495529
496530 if (index == -1) {
497531 if (MainFrame.GUI2_DEBUG)
532 {
498533 System.err.println("Node does not exist in the tree");// For
499 // example,
500 // not
501 // a
502 // bug
503 // bugs
504 // are
505 // filtered,
506 // they
507 // set
508 // a
509 // bug
510 // to
511 // be
512 // not
513 // a
514 // bug
515 // it
516 // filters
517 // out
534 }
535 // example,
536 // not
537 // a
538 // bug
539 // bugs
540 // are
541 // filtered,
542 // they
543 // set
544 // a
545 // bug
546 // to
547 // be
548 // not
549 // a
550 // bug
551 // it
552 // filters
553 // out
518554 return null;
519555 }
520556
523559 // Using a hashlist to store bugs in BugSet will make getIndexOfChild
524560 // Waaaaaay faster, thus making this O(1) (avg case)
525561 int index = getIndexOfChild(pathToBug.getLastPathComponent(), new BugLeafNode(b));
526 if (index == -1)
562 if (index == -1) {
527563 return null;
564 }
528565 pathToBug = pathToBug.pathByAddingChild(getChild(pathToBug.getLastPathComponent(), index));
529566 return pathToBug;
530567
545582 Debug.println("The BugTreeModel has been DELETED! This means there are no more references to it, and its finally off all of the stupid listener lists");
546583 }
547584
585 @Override
548586 public void columnMarginChanged(ChangeEvent arg0) {
549587 }
550588
589 @Override
551590 public void columnSelectionChanged(ListSelectionEvent arg0) {
552591 }
553592
593 @Override
554594 public void treeExpanded(TreeExpansionEvent event) {
555595 }
556596
597 @Override
557598 public void treeCollapsed(TreeExpansionEvent event) {
558599 }
559600
560601 private void setOldSelectedBugs() {
561602 selectedBugLeafNodes.clear();
562 if (tree.getSelectionPaths() != null) // Who the cussword wrote this API
563 // anyway?
564 for (TreePath path : tree.getSelectionPaths())
565 if (isLeaf(path.getLastPathComponent()))
603 if (tree.getSelectionPaths() != null) {
604 // anyway?
605 for (TreePath path : tree.getSelectionPaths()) {
606 if (isLeaf(path.getLastPathComponent())) {
566607 selectedBugLeafNodes.add((BugLeafNode) path.getLastPathComponent());
608 }
609 }
610 }
567611 }
568612
569613 ArrayList<BugLeafNode> getOldSelectedBugs() {
579623 }
580624
581625 public TreeModelEvent restructureBranch(ArrayList<String> stringsToBranch, boolean removing) throws BranchOperationException {
582 if (removing)
626 if (removing) {
583627 return branchOperations(stringsToBranch, TreeModification.REMOVERESTRUCTURE);
584 else
628 } else {
585629 return branchOperations(stringsToBranch, TreeModification.INSERTRESTRUCTURE);
630 }
586631 }
587632
588633 public TreeModelEvent insertBranch(ArrayList<String> stringsToBranch) throws BranchOperationException {
629674 throws BranchOperationException {
630675 TreeModelEvent event = null;
631676
632 if (whatToDo == TreeModification.REMOVE)
677 if (whatToDo == TreeModification.REMOVE) {
633678 Debug.println("Removing a branch......");
634 else if (whatToDo == TreeModification.INSERT)
679 } else if (whatToDo == TreeModification.INSERT) {
635680 Debug.println("Inserting a branch......");
636 else if (whatToDo == TreeModification.REMOVERESTRUCTURE)
681 } else if (whatToDo == TreeModification.REMOVERESTRUCTURE) {
637682 Debug.println("Restructuring from branch to remove......");
638 else if (whatToDo == TreeModification.INSERTRESTRUCTURE)
683 } else if (whatToDo == TreeModification.INSERTRESTRUCTURE) {
639684 Debug.println("Restructuring from branch to insert......");
685 }
640686 Debug.println(stringsToBranch);
641687
642688 if (whatToDo == TreeModification.INSERT || whatToDo == TreeModification.INSERTRESTRUCTURE) {
670716 // break;
671717 }
672718 }
673 if (pathToBranch.getParentPath() != null)
719 if (pathToBranch.getParentPath() != null) {
674720 while (getChildCount(pathToBranch.getParentPath().getLastPathComponent()) == 1) {
675 if (pathToBranch.getParentPath().getLastPathComponent().equals(root))
721 if (pathToBranch.getParentPath().getLastPathComponent().equals(root)) {
676722 break;
723 }
677724 pathToBranch = pathToBranch.getParentPath();
678725 }
726 }
679727 Debug.println(pathToBranch);
680728
681729 if (whatToDo == TreeModification.INSERT) {
695743 event = new TreeModelEvent(this, pathToBranch);
696744 }
697745
698 if (whatToDo == TreeModification.REMOVE || whatToDo == TreeModification.REMOVERESTRUCTURE)
746 if (whatToDo == TreeModification.REMOVE || whatToDo == TreeModification.REMOVERESTRUCTURE) {
699747 resetData();
748 }
700749
701750 return event;
702751 }
708757 }
709758 resetData();
710759 for (TreeModelListener l : listeners) {
711 if (whatToDo == TreeModification.REMOVE)
760 if (whatToDo == TreeModification.REMOVE) {
712761 l.treeNodesRemoved(event);
713 else if (whatToDo == TreeModification.INSERT) {
762 } else if (whatToDo == TreeModification.INSERT) {
714763 l.treeNodesInserted(event);
715764 l.treeStructureChanged(new TreeModelEvent(this, new TreePath(event.getPath()).pathByAddingChild(event
716765 .getChildren()[0])));
3232
3333 /**
3434 * A list of JCheckBoxes! How convenient!
35 *
35 *
3636 * Adapted from: http://www.devx.com/tips/Tip/5342
37 *
37 *
3838 * @author Trevor Harmon
3939 */
4040 @SuppressWarnings("serial")
41 public class CheckBoxList extends JList {
41 public class CheckBoxList<E> extends JList<E> {
4242 private static Border noFocusBorder = new EmptyBorder(1, 1, 1, 1);
4343
4444 public CheckBoxList() {
6060 setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
6161 }
6262
63 public CheckBoxList(Object[] list) {
63 public CheckBoxList(E[] list) {
6464 this();
6565 setListData(list);
6666 }
6969 public void setEnabled(boolean enabled) {
7070 super.setEnabled(enabled);
7171
72 for (int i = 0; i < getModel().getSize(); i++)
72 for (int i = 0; i < getModel().getSize(); i++) {
7373 ((JCheckBox) getModel().getElementAt(i)).setEnabled(enabled);
74 }
7475 }
7576
76 protected class CellRenderer implements ListCellRenderer {
77 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
77 protected class CellRenderer implements ListCellRenderer<E> {
78 @Override
79 public Component getListCellRendererComponent(JList<? extends E> list, E value, int index, boolean isSelected,
7880 boolean cellHasFocus) {
7981 JCheckBox checkbox = (JCheckBox) value;
8082 checkbox.setBackground(isSelected ? getSelectionBackground() : getBackground());
8688 checkbox.setBorder(isSelected ? UIManager.getBorder("List.focusCellHighlightBorder") : noFocusBorder);
8789 return checkbox;
8890 }
91
92
93
8994 }
9095 }
0 /*
1 * FindBugs - Find bugs in Java programs
2 * Copyright (C) 2010-2013 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.gui2;
20
21 import static edu.umd.cs.findbugs.util.Util.nullSafeEquals;
22
23 import java.awt.BorderLayout;
24 import java.awt.CardLayout;
25 import java.awt.Color;
26 import java.awt.Component;
27 import java.awt.Dimension;
28 import java.awt.Font;
29 import java.awt.GridBagConstraints;
30 import java.awt.GridBagLayout;
31 import java.awt.Insets;
32 import java.awt.Toolkit;
33 import java.awt.event.ActionEvent;
34 import java.awt.event.ActionListener;
35 import java.awt.event.FocusEvent;
36 import java.awt.event.FocusListener;
37 import java.awt.event.KeyAdapter;
38 import java.awt.event.KeyEvent;
39 import java.awt.event.MouseAdapter;
40 import java.awt.event.MouseEvent;
41 import java.text.DateFormat;
42 import java.text.MessageFormat;
43 import java.text.ParseException;
44 import java.util.ArrayList;
45 import java.util.Collection;
46 import java.util.Collections;
47 import java.util.Comparator;
48 import java.util.Date;
49 import java.util.HashSet;
50 import java.util.List;
51 import java.util.Set;
52 import java.util.concurrent.Executor;
53 import java.util.concurrent.Executors;
54 import java.util.concurrent.atomic.AtomicBoolean;
55 import java.util.concurrent.atomic.AtomicInteger;
56
57 import javax.swing.BorderFactory;
58 import javax.swing.DefaultListCellRenderer;
59 import javax.swing.JButton;
60 import javax.swing.JComponent;
61 import javax.swing.JLabel;
62 import javax.swing.JList;
63 import javax.swing.JOptionPane;
64 import javax.swing.JPanel;
65 import javax.swing.JScrollPane;
66 import javax.swing.JTextArea;
67 import javax.swing.border.EmptyBorder;
68 import javax.swing.border.EtchedBorder;
69 import javax.swing.event.DocumentEvent;
70 import javax.swing.event.DocumentListener;
71
72 import edu.umd.cs.findbugs.BugCollection;
73 import edu.umd.cs.findbugs.BugInstance;
74 import edu.umd.cs.findbugs.DetectorFactoryCollection;
75 import edu.umd.cs.findbugs.I18N;
76 import edu.umd.cs.findbugs.L10N;
77 import edu.umd.cs.findbugs.cloud.Cloud;
78 import edu.umd.cs.findbugs.cloud.Cloud.SigninState;
79 import edu.umd.cs.findbugs.cloud.Cloud.UserDesignation;
80 import edu.umd.cs.findbugs.cloud.CloudPlugin;
81 import edu.umd.cs.findbugs.util.Util;
82
83 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings({"SE_TRANSIENT_FIELD_NOT_RESTORED", "SE_BAD_FIELD", "SE_BAD_FIELD_STORE"})
84 public abstract class CloudCommentsPane extends JPanel {
85
86 private static final String MSG_REVIEW = L10N.getLocalString("dlg.cloud.add_review", "Click to add review...");
87 private static final String MSG_REVIEW_MULTI = L10N.getLocalString("dlg.cloud.add_review_multi",
88 "Click to add review to {0} bugs...");
89 private static final String MSG_OVERWRITE_REVIEW = L10N.getLocalString("dlg.cloud.ovwrt_review_multi",
90 "Click to overwrite {0} reviews...");
91
92 private JTextArea cloudReportPane;
93 protected JComponent cancelLink;
94 protected JComponent signInOutLink;
95 private JTextArea commentBox;
96 private JButton submitCommentButton;
97 private WideComboBox designationCombo;
98 private JPanel mainPanel;
99 private JScrollPane _cloudReportScrollPane;
100 protected JLabel titleLabel;
101 protected JTextArea cloudDetailsLabel;
102 private JPanel dumbPanelSignInOutLink;
103 private JLabel lastSavedLabel;
104 private JPanel cards;
105 private JButton bulkReviewButton;
106 private JLabel warningLabel;
107
108 protected BugCollection _bugCollection;
109 protected BugInstance _bugInstance;
110 private BugAspects _bugAspects;
111
112 private final Executor backgroundExecutor = Executors.newCachedThreadPool();
113
114 private final Cloud.CloudStatusListener _cloudStatusListener = new MyCloudStatusListener();
115 private Cloud lastCloud = null;
116 private Font plainCommentFont;
117 private String lastCommentText = null;
118 private Set<BugInstance> lastBugsEdited = Collections.emptySet();
119 private boolean clickedBulkReview = false;
120
121
122 private void addNotInCloudCard() {
123 final JPanel panel5 = new JPanel();
124 cards.add(panel5, "NOT_IN_CLOUD");
125 }
126
127 public CloudCommentsPane() {
128 $$$setupUI$$$();
129 addNotInCloudCard();
130 cloudReportPane.setBackground(this.getBackground());
131 cloudReportPane.setBorder(new EmptyBorder(0, 0, 0, 0));
132 _cloudReportScrollPane.setBorder(new EmptyBorder(0, 0, 0, 0));
133
134 // designationCombo.setPreferredSize(new Dimension(300, 20));
135 commentBox.addMouseListener(new MouseAdapter() {
136 @Override
137 public void mousePressed(MouseEvent e) {
138 commentBoxClicked();
139 }
140 });
141 commentBox.getDocument().addDocumentListener(new DocumentListener() {
142 public void insertUpdate(DocumentEvent e) {
143 changed();
144 }
145
146 public void removeUpdate(DocumentEvent e) {
147 changed();
148 }
149
150 public void changedUpdate(DocumentEvent e) {
151 changed();
152 }
153
154 private void changed() {
155 updateSaveButton();
156 }
157 });
158 commentBox.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
159
160 dumbPanelSignInOutLink.setPreferredSize(null);
161
162 setLayout(new BorderLayout());
163 add(mainPanel, BorderLayout.CENTER);
164
165 designationCombo.removeAllItems();
166 final List<String> userDesignationKeys = I18N.instance().getUserDesignationKeys(true);
167 for (final String designation : userDesignationKeys) {
168 designationCombo.addItem(I18N.instance().getUserDesignation(designation));
169 }
170 designationCombo.addItem(null);
171 designationCombo.setRenderer(new DefaultListCellRenderer() {
172 @Override
173 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
174 boolean cellHasFocus) {
175 Component real = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
176 if (value == null)
177 return real;
178 if (index == -1)
179 return real;
180 JPanel panel = new JPanel(new GridBagLayout());
181 panel.setBorder(new EmptyBorder(3, 3, 3, 3));
182 int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
183 GridBagConstraints gbc = new GridBagConstraints();
184 gbc.fill = GridBagConstraints.BOTH;
185 gbc.weightx = 1;
186 gbc.anchor = GridBagConstraints.WEST;
187 panel.add(real, gbc);
188
189 gbc.weightx = 0;
190 gbc.anchor = GridBagConstraints.EAST;
191 gbc.insets = new Insets(0, 10, 0, 0);
192 JLabel label = new JLabel(KeyEvent.getKeyModifiersText(mask) + "-" + (index + 1));
193 label.setForeground(Color.GRAY);
194 // Font font = label.getFont();
195 // label.setFont(font.deriveFont(font.getSize() - 2f));
196 panel.add(label, gbc);
197 panel.setBackground(real.getBackground());
198 return panel;
199 }
200 });
201 designationCombo.addActionListener(new ActionListener() {
202 public void actionPerformed(ActionEvent e) {
203 if (!updatingHeader) {
204 int selectedIndex = designationCombo.getSelectedIndex();
205 if (selectedIndex >= 0)
206 setDesignation(userDesignationKeys.get(selectedIndex));
207 }
208 }
209 });
210
211 // commentEntryPanel.setVisible(false);
212 submitCommentButton.addActionListener(new ActionListener() {
213 public void actionPerformed(final ActionEvent e) {
214 submitComment(CloudCommentsPane.this.getSelectedBugs());
215 }
216 });
217 cloudDetailsLabel.setBackground(null);
218 cloudDetailsLabel.setBorder(null);
219 plainCommentFont = commentBox.getFont().deriveFont(Font.PLAIN);
220 cloudReportPane.setFont(plainCommentFont);
221 // cloudReportPane.setEditorKit(new HTMLEditorKit());
222 // ((HTMLEditorKit) cloudReportPane.getDocument()).getStyleSheet().addRule("body { font-");
223
224 setDefaultComment(MSG_REVIEW);
225 commentBox.addFocusListener(new FocusListener() {
226 public void focusGained(FocusEvent e) {
227 commentBox.setForeground(null);
228 commentBox.setFont(plainCommentFont);
229 if (isDefaultComment(commentBox.getText())) {
230 resetCommentBoxFont();
231 setCommentText("");
232 }
233 }
234
235 public void focusLost(FocusEvent e) {
236 String text = commentBox.getText();
237 if (isDefaultComment(text)) {
238 refresh();
239 } else if (text.equals(lastCommentText)) {
240 if (text.trim().length() == 0)
241 refresh();
242 } else {
243 submitComment(CloudCommentsPane.this.getSelectedBugs());
244 resetCommentBoxFont();
245 }
246 }
247 });
248 commentBox.addKeyListener(new KeyAdapter() {
249 @Override
250 public void keyPressed(KeyEvent e) {
251 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
252 cancelClicked();
253 } else if (e.getKeyCode() == KeyEvent.VK_ENTER && (e.getModifiersEx() & KeyEvent.CTRL_DOWN_MASK) != 0) {
254 submitComment(CloudCommentsPane.this.getSelectedBugs());
255 }
256 }
257 });
258 submitCommentButton.setToolTipText("Submit review [Enter]");
259 cancelLink.setToolTipText("Cancel [Esc]");
260
261 bulkReviewButton.addActionListener(new ActionListener() {
262 public void actionPerformed(ActionEvent e) {
263 clickedBulkReview = true;
264 refresh();
265 }
266 });
267
268 setCanAddComments(false, false);
269 setLastSaved(0);
270
271 updateBugCommentsView();
272 }
273
274 private boolean isDefaultComment(String text) {
275 if (text.equals(MSG_REVIEW)) return true;
276 try {
277 new MessageFormat(MSG_REVIEW_MULTI).parse(text);
278 return true; // didn't throw an exception
279 } catch (ParseException e) {
280 }
281 try {
282 new MessageFormat(MSG_OVERWRITE_REVIEW).parse(text);
283 return true; // didn't throw an exception
284 } catch (ParseException e) {
285 }
286 return false;
287 }
288
289 private void updateSaveButton() {
290 boolean changed = commentWasChanged();
291 submitCommentButton.setEnabled(changed);
292 submitCommentButton.setText(changed
293 ? L10N.getLocalString("dlg.save_btn", "Save")
294 : L10N.getLocalString("dlg.saved_btn", "Saved"));
295 cancelLink.setEnabled(false/*changed*/);
296 }
297
298 private void setCommentText(String t) {
299 lastCommentText = t;
300 if (!commentBox.getText().equals(t))
301 commentBox.setText(t);
302 }
303
304 private void resetCommentBoxFont() {
305 commentBox.setFont(plainCommentFont);
306 commentBox.setForeground(null);
307 }
308
309 private void setDefaultComment(String defaultComment) {
310 setCommentText(defaultComment);
311 commentBox.setForeground(Color.DARK_GRAY);
312 commentBox.setFont(plainCommentFont.deriveFont(Font.ITALIC));
313 }
314
315 private void createUIComponents() {
316 setupLinksOrButtons();
317 }
318
319 protected abstract void setupLinksOrButtons();
320
321
322 private void applyToBugs(final BugAction bugAction) {
323 Executor executor = backgroundExecutor;
324
325 final AtomicInteger shownErrorMessages = new AtomicInteger(0);
326 for (final BugInstance bug : getSelectedBugs())
327 executor.execute(new Runnable() {
328 public void run() {
329 if (shownErrorMessages.get() > 5) {
330 // 5 errors? let's just stop trying.
331 return;
332 }
333 try {
334 bugAction.execute(bug);
335 } catch (Throwable e) {
336 if (shownErrorMessages.addAndGet(1) > 5) {
337 return;
338 }
339 JOptionPane.showMessageDialog(CloudCommentsPane.this,
340 "Error while submitting cloud reviews:\n"
341 + e.getClass().getSimpleName() + ": " + e.getMessage(),
342 "Review Submission Error", JOptionPane.ERROR_MESSAGE);
343 }
344 }
345 });
346 }
347
348 protected void signInOrOutClicked() {
349 if (_bugCollection != null) {
350 final Cloud cloud = _bugCollection.getCloud();
351 if (cloud.getPlugin().getId().equals("edu.umd.cs.findbugs.cloud.doNothingCloud")) {
352 changeClicked();
353 }
354 SigninState state = cloud.getSigninState();
355 if (state == SigninState.SIGNED_IN) {
356 backgroundExecutor.execute(new Runnable() {
357 public void run() {
358 cloud.signOut();
359 refresh();
360 }
361 });
362 refresh();
363 } else if (state.couldSignIn()) {
364 backgroundExecutor.execute(new Runnable() {
365 public void run() {
366 try {
367 cloud.signIn();
368 } catch (Exception e) {
369 _bugCollection
370 .getProject()
371 .getGuiCallback()
372 .showMessageDialog(
373 "The FindBugs Cloud could not be contacted at this time.\n\n"
374 + Util.getNetworkErrorMessage(e));
375 }
376 refresh();
377 }
378 });
379 refresh();
380
381 }
382 }
383 }
384
385 protected void commentBoxClicked() {
386 if (commentWasChanged())
387 return;
388 setCanAddComments(false, true);
389 CommentInfo commentInfo = new CommentInfo().invoke();
390 boolean sameText = commentInfo.isSameText();
391 String txt = commentInfo.getTxt();
392 if (!sameText)
393 txt = "";
394 if (txt == null || txt.trim().length() == 0)
395 txt = "";
396 resetCommentBoxFont();
397 boolean sameTextInBox = commentBox.getText().equals(txt);
398 setCommentText(txt);
399 int start = commentBox.getSelectionStart();
400 int end = commentBox.getSelectionEnd();
401 if (!commentBox.hasFocus() && (!sameTextInBox || start != 0 || end != txt.length())) {
402 commentBox.setSelectionStart(0);
403 commentBox.setSelectionEnd(txt.length());
404 }
405 updateSaveButton();
406 }
407
408 private boolean commentWasChanged() {
409 String text = commentBox.getText();
410 boolean b = !isDefaultComment(text);
411 // boolean b1 = text.trim().equals("");
412 boolean b3 = text.equals(lastCommentText);
413 return b && !b3;
414 }
415
416 public boolean canSetDesignations() {
417 List<BugInstance> bugs = getSelectedBugs();
418 if (bugs.isEmpty())
419 return true;
420 Cloud plugin = _bugCollection != null ? _bugCollection.getCloud() : null;
421 if (plugin == null)
422 return false;
423 for(BugInstance b : bugs)
424 if (plugin.canStoreUserAnnotation(b))
425 return true;
426 return false;
427 }
428
429 public void setDesignation(final String designationKey) {
430
431 // List<BugInstance> selectedBugs = getSelectedBugs();
432 // if (selectedBugs.size() > 1)
433 // if (!confirmAnnotation(selectedBugs))
434 // return;
435 final AtomicBoolean stop = new AtomicBoolean(false);
436 applyToBugs(new BugAction() {
437 public void execute(BugInstance bug) {
438 if (stop.get())
439 return;
440 String oldValue = bug.getUserDesignationKey();
441 String key = designationKey;
442 if (key.equals(oldValue))
443 return;
444 Cloud plugin = _bugCollection != null ? _bugCollection.getCloud() : null;
445 if (plugin != null && key.equals("I_WILL_FIX") && plugin.supportsClaims()) {
446 String claimedBy = plugin.claimedBy(bug);
447 if (claimedBy != null && !plugin.getUser().equals(claimedBy)) {
448 int result = JOptionPane.showConfirmDialog(null,
449 bug.getMessage() + "\n"
450 + claimedBy + " has already said they will fix this issue\n"
451 + "Do you want to also be listed as fixing this issue?\n"
452 + "If so, please coordinate with " + claimedBy,
453 "Issue already claimed", JOptionPane.YES_NO_CANCEL_OPTION);
454 if (result == JOptionPane.CANCEL_OPTION) {
455 stop.set(true);
456 return;
457 }
458 if (result != JOptionPane.YES_OPTION)
459 key = "MUST_FIX";
460 }
461 }
462 changeDesignationOfBugRightNow(bug, key);
463 refresh();
464 }
465 });
466 }
467
468 @SuppressWarnings({"UnusedDeclaration"})
469 private void submitComment(List<BugInstance> selectedBugs) {
470 String comment = commentBox.getText();
471 if (isDefaultComment(comment))
472 comment = "";
473 // if (selectedBugs.size() > 1)
474 // if (!confirmAnnotation(selectedBugs))
475 // return;
476 if (designationCombo.getSelectedItem() != null) {
477 final int index = designationCombo.getSelectedIndex();
478 final String choice;
479 if (index == -1) {
480 choice = UserDesignation.UNCLASSIFIED.name();
481 } else {
482 choice = I18N.instance().getUserDesignationKeys(true).get(index);
483 }
484 setDesignation(choice);
485 }
486 final String finalComment = comment;
487 applyToBugs(new BugAction() {
488 public void execute(BugInstance bug) {
489 bug.setAnnotationText(finalComment, _bugCollection);
490 refresh();
491 setLastSaved(System.currentTimeMillis());
492 }
493 });
494
495 refresh();
496
497 setCanAddComments(true, false);
498 commentBox.requestFocus();
499 }
500
501 private void setLastSaved(long date) {
502 if (date > 0)
503 lastSavedLabel.setText("saved " + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT)
504 .format(new Date(date)));
505 else
506 lastSavedLabel.setText("");
507 }
508
509 protected void cancelClicked() {
510 setDefaultComment(lastCommentText);
511 // commentEntryPanel.setVisible(false);
512 setCanAddComments(true, false);
513 }
514
515 private List<BugInstance> getSelectedBugs() {
516 if (_bugInstance != null)
517 return Collections.singletonList(_bugInstance);
518 if (_bugAspects != null) {
519 List<BugInstance> set = new ArrayList<BugInstance>();
520 for (BugLeafNode node : _bugAspects.getMatchingBugs(BugSet.getMainBugSet())) {
521 if (!BugSet.suppress(node))
522 set.add(node.getBug());
523 }
524 return set;
525 }
526 return Collections.emptyList();
527 }
528
529 private boolean hasSelectedBugs() {
530 return _bugInstance != null || _bugAspects != null;
531 }
532
533 protected void changeClicked() {
534 final List<CloudPlugin> plugins = new ArrayList<CloudPlugin>();
535 final List<String> descriptions = new ArrayList<String>();
536 List<CloudPlugin> clouds = new ArrayList<CloudPlugin>(DetectorFactoryCollection.instance().getRegisteredClouds().values());
537 Collections.sort(clouds, new Comparator<CloudPlugin>() {
538 public int compare(CloudPlugin o1, CloudPlugin o2) {
539 return o1.getDescription().compareToIgnoreCase(o2.getDescription());
540 }
541 });
542 for (final CloudPlugin plugin : clouds) {
543 final boolean disabled = isDisabled(plugin);
544 if (!disabled && !plugin.isHidden()) {
545 descriptions.add(plugin.getDescription());
546 plugins.add(plugin);
547 }
548 }
549 showCloudChooser(plugins, descriptions);
550 }
551
552 protected abstract boolean isDisabled(CloudPlugin plugin);
553
554 protected abstract void showCloudChooser(List<CloudPlugin> plugins, List<String> descriptions);
555
556 protected void changeCloud(String newCloudId) {
557 final String oldCloudId = _bugCollection.getCloud().getPlugin().getId();
558 if (!oldCloudId.equals(newCloudId)) {
559 _bugCollection.getProject().setCloudId(newCloudId);
560 MainFrame.getInstance().setProjectChanged(true);
561 backgroundExecutor.execute(new Runnable() {
562 public void run() {
563 _bugCollection.reinitializeCloud();
564 Cloud cloud = _bugCollection.getCloud();
565 if (cloud != null)
566 cloud.waitUntilIssueDataDownloaded();
567 updateCloudListeners(_bugCollection);
568 refresh();
569 }
570 });
571 refresh();
572 }
573 }
574
575 public void setBugCollection(BugCollection bugCollection) {
576 updateCloudListeners(bugCollection);
577 _bugCollection = bugCollection;
578 _bugInstance = null;
579 _bugAspects = null;
580 refresh();
581 }
582
583 public void setBugInstance(final BugInstance bugInstance) {
584 setBugs(bugInstance, null);
585 }
586
587 public void setBugAspects(BugAspects aspects) {
588 setBugs(null, aspects);
589 }
590
591 private void setBugs(BugInstance bugInstance, BugAspects bugAspects) {
592 if (_bugInstance == bugInstance && _bugAspects == bugAspects)
593 return;
594 if (!canNavigateAway())
595 return;
596
597 _bugInstance = bugInstance;
598 _bugAspects = bugAspects;
599 refresh();
600 }
601
602 public boolean canNavigateAway() {
603 if (commentWasChanged()) {
604 submitComment(getSelectedBugs());
605 return true;
606 } else {
607 return true;
608 }
609 }
610
611 protected void changeDesignationOfBugRightNow(final BugInstance bug, final String designationKey) {
612 String oldValue = bug.getUserDesignationKey();
613 if (designationKey.equals(oldValue))
614 return;
615 bug.setUserDesignationKey(designationKey, _bugCollection);
616 }
617
618 public void refresh() {
619 updateBugCommentsView();
620 }
621
622 public void updateCloud() {
623 updateCloudListeners(_bugCollection);
624 refresh();
625 }
626
627 private void updateCloudListeners(BugCollection newBugCollection) {
628 final Cloud newCloud = newBugCollection == null ? null : newBugCollection.getCloud();
629 if (_bugCollection != null) {
630 //noinspection ObjectEquality
631 if (lastCloud != newCloud) {
632 if (lastCloud != null) {
633 lastCloud.removeStatusListener(_cloudStatusListener);
634 }
635 }
636 }
637 if (lastCloud != newCloud && newCloud != null) {
638 lastCloud = newCloud;
639 newCloud.addStatusListener(_cloudStatusListener);
640 }
641 }
642
643
644 private boolean inCloud(Collection<BugInstance> bugs) {
645 final Cloud cloud = _bugCollection.getCloud();
646
647 for (BugInstance b : bugs)
648 if (cloud.isInCloud(b))
649 return true;
650 return false;
651
652 }
653
654 private void updateBugCommentsView() {
655
656 //TODO: fix cancel button
657 List<BugInstance> bugs = getSelectedBugs();
658 if (_bugCollection == null) {
659 signInOutLink.setVisible(false);
660 cloudDetailsLabel.setText("");
661 cloudReportPane.setText("");
662 titleLabel.setText("<html>Reviews");
663 return;
664 }
665 updateHeader();
666 final Cloud cloud = _bugCollection.getCloud();
667 final CloudPlugin plugin = cloud.getPlugin();
668 String details = plugin.getDetails();
669 cloudDetailsLabel.setText(details);
670
671 if (bugs.isEmpty()) {
672 setCanAddComments(false, false);
673 return;
674 }
675
676 String report;
677 long lastSaved = -1;
678 if (bugs.size() > 1) {
679 int totalReviews = 0;
680 int bugsWithReviews = 0;
681 for (BugInstance bug : bugs) {
682 long newTs = cloud.getUserTimestamp(bug);
683 if (bug.hasSomeUserAnnotation() && newTs > 0 && (lastSaved == -1 || lastSaved < newTs)) {
684 lastSaved = newTs;
685 }
686 int reviewers = cloud.getNumberReviewers(bug);
687 if (reviewers > 0)
688 bugsWithReviews++;
689 totalReviews += reviewers;
690 }
691 report = bugs.size() + " bug" + (bugs.size() == 1 ? "" : "s") + " selected\n";
692 report += bugsWithReviews + " reviewed bug" + (bugsWithReviews == 1 ? "" : "s")
693 + " / " + totalReviews + " total review" + (totalReviews == 1 ? "" : "s");
694 } else {
695 BugInstance bug = bugs.get(0);
696 if (bug.hasSomeUserAnnotation()) {
697 lastSaved = bug.getUserTimestamp();
698 }
699 report = cloud.getCloudReportWithoutMe(bug);
700 }
701 setLastSaved(lastSaved);
702 cloudReportPane.setText(report);
703 CommentInfo commentInfo = new CommentInfo().invoke();
704 boolean sameText = commentInfo.isSameText();
705 String txt = commentInfo.getTxt();
706 CardLayout cl = (CardLayout) (cards.getLayout());
707 HashSet<BugInstance> newBugSet = new HashSet<BugInstance>(bugs);
708 boolean sameBugs = newBugSet.equals(lastBugsEdited);
709 if (!sameBugs) {
710 lastBugsEdited = newBugSet;
711 clickedBulkReview = false;
712 }
713 if (!inCloud(bugs)) {
714 cl.show(cards, "NOT_IN_CLOUD");
715 } else if (bugs.size() > 1 && !clickedBulkReview) {
716 warningLabel.setText("<HTML>" + bugs.size() + " bugs are selected.<BR>Click to review them all at once.");
717 cl.show(cards, "WARNING");
718 } else {
719 cl.show(cards, "COMMENTS");
720 }
721 if (!sameText) {
722 txt = MessageFormat.format(MSG_OVERWRITE_REVIEW, bugs.size());
723 setDefaultComment(txt);
724 } else {
725 if (txt == null || txt.trim().length() == 0) {
726 txt = bugs.size() > 1 ? MessageFormat.format(MSG_REVIEW_MULTI, bugs.size()) : MSG_REVIEW;
727 setDefaultComment(txt);
728 } else {
729 resetCommentBoxFont();
730 setCommentText(txt);
731 }
732 }
733
734 setCanAddComments(cloud.canStoreUserAnnotation(bugs.get(0)), false);
735 updateSaveButton();
736 }
737
738 private boolean updatingHeader = false;
739
740 private void updateHeader() {
741 final Cloud cloud = _bugCollection.getCloud();
742 CloudPlugin plugin = cloud.getPlugin();
743 if (hasSelectedBugs()) {
744 CommentInfo commentInfo = new CommentInfo().invoke();
745 boolean sameDesignation = commentInfo.isSameDesignation();
746 String designation = commentInfo.getDesignation();
747 if (!sameDesignation)
748 designation = null;
749 updatingHeader = true;
750 designationCombo.setSelectedIndex(I18N.instance().getUserDesignationKeys(true).indexOf(designation));
751 updatingHeader = false;
752 setCanAddComments(true, true);
753 } else {
754 setCanAddComments(false, false);
755 }
756
757 final Cloud.SigninState state = cloud.getSigninState();
758 final String stateStr = state == Cloud.SigninState.NO_SIGNIN_REQUIRED ? "" : "" + state;
759 final String userStr = cloud.getUser() == null ? "" : cloud.getUser();
760 if (plugin.getId().equals("edu.umd.cs.findbugs.cloud.doNothingCloud"))
761 titleLabel.setText("<html><b>No cloud selected");
762 else
763 titleLabel.setText("<html><b>Reviews - " + cloud.getCloudName() + "</b>"
764 + "<br><font style='font-size: x-small;color:darkgray'>" + stateStr
765 + (userStr.length() > 0 ? " - " + userStr : ""));
766 switch (state) {
767 case NO_SIGNIN_REQUIRED:
768 case SIGNING_IN:
769 signInOutLink.setVisible(false);
770 break;
771 case SIGNED_IN:
772 setSignInOutText("sign out");
773 signInOutLink.setVisible(true);
774 break;
775 default:
776 if (state.couldSignIn()) {
777 setSignInOutText("sign in");
778 signInOutLink.setVisible(true);
779 }
780 break;
781 }
782 if (cloud.getPlugin().getId().equals("edu.umd.cs.findbugs.cloud.doNothingCloud")) {
783 setSignInOutText("enable cloud plugin...");
784 signInOutLink.setVisible(true);
785 }
786 }
787
788 private void setCanAddComments(boolean canClick, boolean canEnter) {
789 submitCommentButton.setEnabled(canClick || canEnter);
790 designationCombo.setEnabled(canClick || canEnter);
791 commentBox.setEnabled(canClick || canEnter);
792 }
793
794 protected abstract void setSignInOutText(String buttonText);
795
796 /**
797 * Method generated by IntelliJ IDEA GUI Designer
798 * >>> IMPORTANT!! <<<
799 * DO NOT edit this method OR call it in your code!
800 *
801 * @noinspection ALL
802 */
803 private void $$$setupUI$$$() {
804 createUIComponents();
805 mainPanel = new JPanel();
806 mainPanel.setLayout(new GridBagLayout());
807 mainPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3), null));
808 _cloudReportScrollPane = new JScrollPane();
809 GridBagConstraints gbc;
810 gbc = new GridBagConstraints();
811 gbc.gridx = 0;
812 gbc.gridy = 2;
813 gbc.gridwidth = 6;
814 gbc.weightx = 1.0;
815 gbc.weighty = 1.0;
816 gbc.fill = GridBagConstraints.BOTH;
817 gbc.insets = new Insets(5, 5, 5, 5);
818 mainPanel.add(_cloudReportScrollPane, gbc);
819 cloudReportPane = new JTextArea();
820 cloudReportPane.setEditable(false);
821 cloudReportPane.setLineWrap(true);
822 cloudReportPane.setText("<html>\r\n <head>\r\n \r\n </head>\r\n <body>\r\n </body>\r\n</html>\r\n");
823 cloudReportPane.setWrapStyleWord(true);
824 _cloudReportScrollPane.setViewportView(cloudReportPane);
825 final JPanel panel1 = new JPanel();
826 panel1.setLayout(new GridBagLayout());
827 panel1.setBackground(new Color(-3355444));
828 gbc = new GridBagConstraints();
829 gbc.gridx = 0;
830 gbc.gridy = 0;
831 gbc.gridwidth = 6;
832 gbc.weightx = 1.0;
833 gbc.fill = GridBagConstraints.BOTH;
834 mainPanel.add(panel1, gbc);
835 panel1.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(new Color(-16751002)), null));
836 titleLabel = new JLabel();
837 titleLabel.setFont(new Font(titleLabel.getFont().getName(), Font.BOLD, 14));
838 titleLabel.setForeground(new Color(-16777216));
839 titleLabel.setText("FindBugs Cloud - signed in");
840 gbc = new GridBagConstraints();
841 gbc.gridx = 0;
842 gbc.gridy = 0;
843 gbc.gridheight = 2;
844 gbc.weightx = 1.0;
845 gbc.anchor = GridBagConstraints.NORTHWEST;
846 panel1.add(titleLabel, gbc);
847 dumbPanelSignInOutLink = new JPanel();
848 dumbPanelSignInOutLink.setLayout(new GridBagLayout());
849 dumbPanelSignInOutLink.setOpaque(false);
850 dumbPanelSignInOutLink.setPreferredSize(new Dimension(50, 10));
851 gbc = new GridBagConstraints();
852 gbc.gridx = 1;
853 gbc.gridy = 0;
854 gbc.gridheight = 2;
855 gbc.fill = GridBagConstraints.BOTH;
856 panel1.add(dumbPanelSignInOutLink, gbc);
857 gbc = new GridBagConstraints();
858 gbc.gridx = 0;
859 gbc.gridy = 0;
860 gbc.anchor = GridBagConstraints.NORTHWEST;
861 dumbPanelSignInOutLink.add(signInOutLink, gbc);
862 final JPanel panel2 = new JPanel();
863 panel2.setLayout(new GridBagLayout());
864 panel2.setVisible(false);
865 gbc = new GridBagConstraints();
866 gbc.gridx = 0;
867 gbc.gridy = 1;
868 gbc.weightx = 1.0;
869 gbc.fill = GridBagConstraints.BOTH;
870 gbc.insets = new Insets(5, 5, 5, 5);
871 mainPanel.add(panel2, gbc);
872 cloudDetailsLabel = new JTextArea();
873 cloudDetailsLabel.setEditable(false);
874 cloudDetailsLabel.setFont(new Font(cloudDetailsLabel.getFont().getName(), Font.ITALIC, 10));
875 cloudDetailsLabel.setForeground(new Color(-10066330));
876 cloudDetailsLabel.setLineWrap(true);
877 cloudDetailsLabel.setMaximumSize(new Dimension(100, 50));
878 cloudDetailsLabel.setMinimumSize(new Dimension(50, 16));
879 cloudDetailsLabel.setOpaque(false);
880 cloudDetailsLabel.setPreferredSize(new Dimension(100, 31));
881 cloudDetailsLabel.setText("Comments are stored on the FindBugs Cloud at http://findbugs-cloud.appspot.com");
882 cloudDetailsLabel.setWrapStyleWord(true);
883 gbc = new GridBagConstraints();
884 gbc.gridx = 0;
885 gbc.gridy = 0;
886 gbc.weightx = 1.0;
887 gbc.weighty = 1.0;
888 gbc.fill = GridBagConstraints.BOTH;
889 panel2.add(cloudDetailsLabel, gbc);
890 final JPanel panel3 = new JPanel();
891 panel3.setLayout(new GridBagLayout());
892 gbc = new GridBagConstraints();
893 gbc.gridx = 1;
894 gbc.gridy = 3;
895 gbc.gridwidth = 5;
896 gbc.weightx = 1.0;
897 gbc.weighty = 1.0;
898 gbc.fill = GridBagConstraints.BOTH;
899 mainPanel.add(panel3, gbc);
900 cards = new JPanel();
901 cards.setLayout(new CardLayout(0, 0));
902 gbc = new GridBagConstraints();
903 gbc.gridx = 5;
904 gbc.gridy = 4;
905 gbc.weightx = 1.0;
906 gbc.weighty = 1.0;
907 gbc.fill = GridBagConstraints.BOTH;
908 mainPanel.add(cards, gbc);
909 final JPanel panel4 = new JPanel();
910 panel4.setLayout(new GridBagLayout());
911 cards.add(panel4, "COMMENTS");
912 designationCombo = new WideComboBox();
913 gbc = new GridBagConstraints();
914 gbc.gridx = 4;
915 gbc.gridy = 0;
916 gbc.gridwidth = 2;
917 gbc.anchor = GridBagConstraints.NORTHWEST;
918 gbc.insets = new Insets(5, 0, 0, 0);
919 panel4.add(designationCombo, gbc);
920 final JScrollPane scrollPane1 = new JScrollPane();
921 gbc = new GridBagConstraints();
922 gbc.gridx = 0;
923 gbc.gridy = 0;
924 gbc.gridwidth = 4;
925 gbc.gridheight = 4;
926 gbc.weightx = 1.0;
927 gbc.fill = GridBagConstraints.BOTH;
928 panel4.add(scrollPane1, gbc);
929 scrollPane1.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), null));
930 commentBox = new JTextArea();
931 commentBox.setLineWrap(true);
932 commentBox.setRows(5);
933 commentBox.setText(" ");
934 commentBox.setWrapStyleWord(true);
935 scrollPane1.setViewportView(commentBox);
936 submitCommentButton = new JButton();
937 submitCommentButton.setText("Save");
938 gbc = new GridBagConstraints();
939 gbc.gridx = 4;
940 gbc.gridy = 1;
941 gbc.insets = new Insets(5, 5, 5, 5);
942 panel4.add(submitCommentButton, gbc);
943 lastSavedLabel = new JLabel();
944 lastSavedLabel.setFont(new Font(lastSavedLabel.getFont().getName(), Font.ITALIC, 9));
945 lastSavedLabel.setText("saved at");
946 gbc = new GridBagConstraints();
947 gbc.gridx = 4;
948 gbc.gridy = 3;
949 gbc.gridwidth = 2;
950 panel4.add(lastSavedLabel, gbc);
951 gbc = new GridBagConstraints();
952 gbc.gridx = 5;
953 gbc.gridy = 1;
954 panel4.add(cancelLink, gbc);
955 final JPanel panel5 = new JPanel();
956 panel5.setLayout(new GridBagLayout());
957 cards.add(panel5, "WARNING");
958 warningLabel = new JLabel();
959 warningLabel.setHorizontalAlignment(0);
960 warningLabel.setHorizontalTextPosition(0);
961 warningLabel.setText("<HTML>Multiple bugs are selected.<BR>Click to review them all at once.");
962 gbc = new GridBagConstraints();
963 gbc.gridx = 0;
964 gbc.gridy = 0;
965 gbc.insets = new Insets(10, 10, 10, 10);
966 panel5.add(warningLabel, gbc);
967 bulkReviewButton = new JButton();
968 bulkReviewButton.setText("Bulk Review");
969 gbc = new GridBagConstraints();
970 gbc.gridx = 1;
971 gbc.gridy = 0;
972 gbc.fill = GridBagConstraints.HORIZONTAL;
973 gbc.insets = new Insets(10, 10, 10, 10);
974 panel5.add(bulkReviewButton, gbc);
975 }
976
977 /**
978 * @noinspection ALL
979 */
980 public JComponent $$$getRootComponent$$$() {
981 return mainPanel;
982 }
983
984 private class MyCloudStatusListener implements Cloud.CloudStatusListener {
985 public void handleIssueDataDownloadedEvent() {
986 refresh();
987 }
988
989
990 public void handleStateChange(final Cloud.SigninState oldState, final Cloud.SigninState state) {
991 updateHeader();
992 refresh();
993 }
994
995
996 }
997
998 private interface BugAction {
999 void execute(BugInstance bug);
1000 }
1001
1002 private class CommentInfo {
1003 private String txt;
1004 private boolean sameText;
1005 private String designation;
1006 private boolean sameDesignation;
1007
1008 public String getTxt() {
1009 return txt;
1010 }
1011
1012 public boolean isSameText() {
1013 return sameText;
1014 }
1015
1016 public String getDesignation() {
1017 return designation;
1018 }
1019
1020 public boolean isSameDesignation() {
1021 return sameDesignation;
1022 }
1023
1024 public CommentInfo invoke() {
1025 txt = null;
1026 sameText = true;
1027 designation = null;
1028 sameDesignation = true;
1029 for (BugInstance bug : getSelectedBugs()) {
1030 String newText = bug.getAnnotationText();
1031 if (txt == null)
1032 txt = newText;
1033 else {
1034 if (!nullSafeEquals(txt, newText))
1035 sameText = false;
1036 }
1037
1038 String newDesignation = bug.getUserDesignationKey();
1039 if (designation == null)
1040 designation = newDesignation;
1041 else {
1042 if (!nullSafeEquals(designation, newDesignation))
1043 sameDesignation = false;
1044 }
1045 }
1046 return this;
1047 }
1048 }
1049 }
0 /*
1 * FindBugs - Find bugs in Java programs
2 * Copyright (C) 2010-2013 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.gui2;
20
21 import static edu.umd.cs.findbugs.util.Util.nullSafeEquals;
22
23 import java.awt.BorderLayout;
24 import java.awt.CardLayout;
25 import java.awt.Color;
26 import java.awt.Component;
27 import java.awt.Dimension;
28 import java.awt.Font;
29 import java.awt.GridBagConstraints;
30 import java.awt.GridBagLayout;
31 import java.awt.Insets;
32 import java.awt.Toolkit;
33 import java.awt.event.ActionEvent;
34 import java.awt.event.ActionListener;
35 import java.awt.event.FocusEvent;
36 import java.awt.event.FocusListener;
37 import java.awt.event.InputEvent;
38 import java.awt.event.KeyAdapter;
39 import java.awt.event.KeyEvent;
40 import java.awt.event.MouseAdapter;
41 import java.awt.event.MouseEvent;
42 import java.text.DateFormat;
43 import java.text.MessageFormat;
44 import java.text.ParseException;
45 import java.util.ArrayList;
46 import java.util.Collection;
47 import java.util.Collections;
48 import java.util.Comparator;
49 import java.util.Date;
50 import java.util.HashSet;
51 import java.util.List;
52 import java.util.Set;
53 import java.util.concurrent.Executor;
54 import java.util.concurrent.Executors;
55 import java.util.concurrent.atomic.AtomicBoolean;
56 import java.util.concurrent.atomic.AtomicInteger;
57
58 import javax.swing.BorderFactory;
59 import javax.swing.DefaultListCellRenderer;
60 import javax.swing.JButton;
61 import javax.swing.JComponent;
62 import javax.swing.JLabel;
63 import javax.swing.JList;
64 import javax.swing.JOptionPane;
65 import javax.swing.JPanel;
66 import javax.swing.JScrollPane;
67 import javax.swing.JTextArea;
68 import javax.swing.border.EmptyBorder;
69 import javax.swing.border.EtchedBorder;
70 import javax.swing.event.DocumentEvent;
71 import javax.swing.event.DocumentListener;
72
73 import edu.umd.cs.findbugs.BugCollection;
74 import edu.umd.cs.findbugs.BugInstance;
75 import edu.umd.cs.findbugs.DetectorFactoryCollection;
76 import edu.umd.cs.findbugs.I18N;
77 import edu.umd.cs.findbugs.L10N;
78 import edu.umd.cs.findbugs.cloud.Cloud;
79 import edu.umd.cs.findbugs.cloud.Cloud.SigninState;
80 import edu.umd.cs.findbugs.cloud.Cloud.UserDesignation;
81 import edu.umd.cs.findbugs.cloud.CloudPlugin;
82 import edu.umd.cs.findbugs.util.Util;
83
84 @edu.umd.cs.findbugs.annotations.SuppressFBWarnings({"SE_TRANSIENT_FIELD_NOT_RESTORED", "SE_BAD_FIELD", "SE_BAD_FIELD_STORE"})
85 public abstract class CloudCommentsPane extends JPanel {
86
87 private static final String MSG_REVIEW = L10N.getLocalString("dlg.cloud.add_review", "Click to add review...");
88 private static final String MSG_REVIEW_MULTI = L10N.getLocalString("dlg.cloud.add_review_multi",
89 "Click to add review to {0} bugs...");
90 private static final String MSG_OVERWRITE_REVIEW = L10N.getLocalString("dlg.cloud.ovwrt_review_multi",
91 "Click to overwrite {0} reviews...");
92
93 private JTextArea cloudReportPane;
94 protected JComponent cancelLink;
95 protected JComponent signInOutLink;
96 private JTextArea commentBox;
97 private JButton submitCommentButton;
98 private WideComboBox<String> designationCombo;
99 private JPanel mainPanel;
100 private JScrollPane _cloudReportScrollPane;
101 protected JLabel titleLabel;
102 protected JTextArea cloudDetailsLabel;
103 private JPanel dumbPanelSignInOutLink;
104 private JLabel lastSavedLabel;
105 private JPanel cards;
106 private JButton bulkReviewButton;
107 private JLabel warningLabel;
108
109 protected BugCollection _bugCollection;
110 protected BugInstance _bugInstance;
111 private BugAspects _bugAspects;
112
113 private final Executor backgroundExecutor = Executors.newCachedThreadPool();
114
115 private final Cloud.CloudStatusListener _cloudStatusListener = new MyCloudStatusListener();
116 private Cloud lastCloud = null;
117 private Font plainCommentFont;
118 private String lastCommentText = null;
119 private Set<BugInstance> lastBugsEdited = Collections.emptySet();
120 private boolean clickedBulkReview = false;
121
122
123 private void addNotInCloudCard() {
124 final JPanel panel5 = new JPanel();
125 cards.add(panel5, "NOT_IN_CLOUD");
126 }
127
128 public CloudCommentsPane() {
129 $$$setupUI$$$();
130 addNotInCloudCard();
131 cloudReportPane.setBackground(this.getBackground());
132 cloudReportPane.setBorder(new EmptyBorder(0, 0, 0, 0));
133 _cloudReportScrollPane.setBorder(new EmptyBorder(0, 0, 0, 0));
134
135 // designationCombo.setPreferredSize(new Dimension(300, 20));
136 commentBox.addMouseListener(new MouseAdapter() {
137 @Override
138 public void mousePressed(MouseEvent e) {
139 commentBoxClicked();
140 }
141 });
142 commentBox.getDocument().addDocumentListener(new DocumentListener() {
143 @Override
144 public void insertUpdate(DocumentEvent e) {
145 changed();
146 }
147
148 @Override
149 public void removeUpdate(DocumentEvent e) {
150 changed();
151 }
152
153 @Override
154 public void changedUpdate(DocumentEvent e) {
155 changed();
156 }
157
158 private void changed() {
159 updateSaveButton();
160 }
161 });
162 commentBox.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
163
164 dumbPanelSignInOutLink.setPreferredSize(null);
165
166 setLayout(new BorderLayout());
167 add(mainPanel, BorderLayout.CENTER);
168
169 designationCombo.removeAllItems();
170 final List<String> userDesignationKeys = I18N.instance().getUserDesignationKeys(true);
171 for (final String designation : userDesignationKeys) {
172 designationCombo.addItem(I18N.instance().getUserDesignation(designation));
173 }
174 designationCombo.addItem(null);
175 designationCombo.setRenderer(new DefaultListCellRenderer() {
176 @Override
177 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
178 boolean cellHasFocus) {
179 Component real = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
180 if (value == null) {
181 return real;
182 }
183 if (index == -1) {
184 return real;
185 }
186 JPanel panel = new JPanel(new GridBagLayout());
187 panel.setBorder(new EmptyBorder(3, 3, 3, 3));
188 int mask = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
189 GridBagConstraints gbc = new GridBagConstraints();
190 gbc.fill = GridBagConstraints.BOTH;
191 gbc.weightx = 1;
192 gbc.anchor = GridBagConstraints.WEST;
193 panel.add(real, gbc);
194
195 gbc.weightx = 0;
196 gbc.anchor = GridBagConstraints.EAST;
197 gbc.insets = new Insets(0, 10, 0, 0);
198 JLabel label = new JLabel(KeyEvent.getKeyModifiersText(mask) + "-" + (index + 1));
199 label.setForeground(Color.GRAY);
200 // Font font = label.getFont();
201 // label.setFont(font.deriveFont(font.getSize() - 2f));
202 panel.add(label, gbc);
203 panel.setBackground(real.getBackground());
204 return panel;
205 }
206 });
207 designationCombo.addActionListener(new ActionListener() {
208 @Override
209 public void actionPerformed(ActionEvent e) {
210 if (!updatingHeader) {
211 int selectedIndex = designationCombo.getSelectedIndex();
212 if (selectedIndex >= 0) {
213 setDesignation(userDesignationKeys.get(selectedIndex));
214 }
215 }
216 }
217 });
218
219 // commentEntryPanel.setVisible(false);
220 submitCommentButton.addActionListener(new ActionListener() {
221 @Override
222 public void actionPerformed(final ActionEvent e) {
223 submitComment(CloudCommentsPane.this.getSelectedBugs());
224 }
225 });
226 cloudDetailsLabel.setBackground(null);
227 cloudDetailsLabel.setBorder(null);
228 plainCommentFont = commentBox.getFont().deriveFont(Font.PLAIN);
229 cloudReportPane.setFont(plainCommentFont);
230 // cloudReportPane.setEditorKit(new HTMLEditorKit());
231 // ((HTMLEditorKit) cloudReportPane.getDocument()).getStyleSheet().addRule("body { font-");
232
233 setDefaultComment(MSG_REVIEW);
234 commentBox.addFocusListener(new FocusListener() {
235 @Override
236 public void focusGained(FocusEvent e) {
237 commentBox.setForeground(null);
238 commentBox.setFont(plainCommentFont);
239 if (isDefaultComment(commentBox.getText())) {
240 resetCommentBoxFont();
241 setCommentText("");
242 }
243 }
244
245 @Override
246 public void focusLost(FocusEvent e) {
247 String text = commentBox.getText();
248 if (isDefaultComment(text)) {
249 refresh();
250 } else if (text.equals(lastCommentText)) {
251 if (text.trim().length() == 0) {
252 refresh();
253 }
254 } else {
255 submitComment(CloudCommentsPane.this.getSelectedBugs());
256 resetCommentBoxFont();
257 }
258 }
259 });
260 commentBox.addKeyListener(new KeyAdapter() {
261 @Override
262 public void keyPressed(KeyEvent e) {
263 if (e.getKeyCode() == KeyEvent.VK_ESCAPE) {
264 cancelClicked();
265 } else if (e.getKeyCode() == KeyEvent.VK_ENTER && (e.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0) {
266 submitComment(CloudCommentsPane.this.getSelectedBugs());
267 }
268 }
269 });
270 submitCommentButton.setToolTipText("Submit review [Enter]");
271 cancelLink.setToolTipText("Cancel [Esc]");
272
273 bulkReviewButton.addActionListener(new ActionListener() {
274 @Override
275 public void actionPerformed(ActionEvent e) {
276 clickedBulkReview = true;
277 refresh();
278 }
279 });
280
281 setCanAddComments(false, false);
282 setLastSaved(0);
283
284 updateBugCommentsView();
285 }
286
287 private boolean isDefaultComment(String text) {
288 if (text.equals(MSG_REVIEW)) {
289 return true;
290 }
291 try {
292 new MessageFormat(MSG_REVIEW_MULTI).parse(text);
293 return true; // didn't throw an exception
294 } catch (ParseException e) {
295 }
296 try {
297 new MessageFormat(MSG_OVERWRITE_REVIEW).parse(text);
298 return true; // didn't throw an exception
299 } catch (ParseException e) {
300 }
301 return false;
302 }
303
304 private void updateSaveButton() {
305 boolean changed = commentWasChanged();
306 submitCommentButton.setEnabled(changed);
307 submitCommentButton.setText(changed
308 ? L10N.getLocalString("dlg.save_btn", "Save")
309 : L10N.getLocalString("dlg.saved_btn", "Saved"));
310 cancelLink.setEnabled(false/*changed*/);
311 }
312
313 private void setCommentText(String t) {
314 lastCommentText = t;
315 if (!commentBox.getText().equals(t)) {
316 commentBox.setText(t);
317 }
318 }
319
320 private void resetCommentBoxFont() {
321 commentBox.setFont(plainCommentFont);
322 commentBox.setForeground(null);
323 }
324
325 private void setDefaultComment(String defaultComment) {
326 setCommentText(defaultComment);
327 commentBox.setForeground(Color.DARK_GRAY);
328 commentBox.setFont(plainCommentFont.deriveFont(Font.ITALIC));
329 }
330
331 private void createUIComponents() {
332 setupLinksOrButtons();
333 }
334
335 protected abstract void setupLinksOrButtons();
336
337
338 private void applyToBugs(final BugAction bugAction) {
339 Executor executor = backgroundExecutor;
340
341 final AtomicInteger shownErrorMessages = new AtomicInteger(0);
342 for (final BugInstance bug : getSelectedBugs()) {
343 executor.execute(new Runnable() {
344 @Override
345 public void run() {
346 if (shownErrorMessages.get() > 5) {
347 // 5 errors? let's just stop trying.
348 return;
349 }
350 try {
351 bugAction.execute(bug);
352 } catch (Throwable e) {
353 if (shownErrorMessages.addAndGet(1) > 5) {
354 return;
355 }
356 JOptionPane.showMessageDialog(CloudCommentsPane.this,
357 "Error while submitting cloud reviews:\n"
358 + e.getClass().getSimpleName() + ": " + e.getMessage(),
359 "Review Submission Error", JOptionPane.ERROR_MESSAGE);
360 }
361 }
362 });
363 }
364 }
365
366 protected void signInOrOutClicked() {
367 if (_bugCollection != null) {
368 final Cloud cloud = _bugCollection.getCloud();
369 if ("edu.umd.cs.findbugs.cloud.doNothingCloud".equals(cloud.getPlugin().getId())) {
370 changeClicked();
371 }
372 SigninState state = cloud.getSigninState();
373 if (state == SigninState.SIGNED_IN) {
374 backgroundExecutor.execute(new Runnable() {
375 @Override
376 public void run() {
377 cloud.signOut();
378 refresh();
379 }
380 });
381 refresh();
382 } else if (state.couldSignIn()) {
383 backgroundExecutor.execute(new Runnable() {
384 @Override
385 public void run() {
386 try {
387 cloud.signIn();
388 } catch (Exception e) {
389 _bugCollection
390 .getProject()
391 .getGuiCallback()
392 .showMessageDialog(
393 "The FindBugs Cloud could not be contacted at this time.\n\n"
394 + Util.getNetworkErrorMessage(e));
395 }
396 refresh();
397 }
398 });
399 refresh();
400
401 }
402 }
403 }
404
405 protected void commentBoxClicked() {
406 if (commentWasChanged()) {
407 return;
408 }
409 setCanAddComments(false, true);
410 CommentInfo commentInfo = new CommentInfo().invoke();
411 boolean sameText = commentInfo.isSameText();
412 String txt = commentInfo.getTxt();
413 if (!sameText) {
414 txt = "";
415 }
416 if (txt == null || txt.trim().length() == 0) {
417 txt = "";
418 }
419 resetCommentBoxFont();
420 boolean sameTextInBox = commentBox.getText().equals(txt);
421 setCommentText(txt);
422 int start = commentBox.getSelectionStart();
423 int end = commentBox.getSelectionEnd();
424 if (!commentBox.hasFocus() && (!sameTextInBox || start != 0 || end != txt.length())) {
425 commentBox.setSelectionStart(0);
426 commentBox.setSelectionEnd(txt.length());
427 }
428 updateSaveButton();
429 }
430
431 private boolean commentWasChanged() {
432 String text = commentBox.getText();
433 boolean b = !isDefaultComment(text);
434 // boolean b1 = text.trim().equals("");
435 boolean b3 = text.equals(lastCommentText);
436 return b && !b3;
437 }
438
439 public boolean canSetDesignations() {
440 List<BugInstance> bugs = getSelectedBugs();
441 if (bugs.isEmpty()) {
442 return true;
443 }
444 Cloud plugin = _bugCollection != null ? _bugCollection.getCloud() : null;
445 if (plugin == null) {
446 return false;
447 }
448 for(BugInstance b : bugs) {
449 if (plugin.canStoreUserAnnotation(b)) {
450 return true;
451 }
452 }
453 return false;
454 }
455
456 public void setDesignation(final String designationKey) {
457
458 // List<BugInstance> selectedBugs = getSelectedBugs();
459 // if (selectedBugs.size() > 1)
460 // if (!confirmAnnotation(selectedBugs))
461 // return;
462 final AtomicBoolean stop = new AtomicBoolean(false);
463 applyToBugs(new BugAction() {
464 @Override
465 public void execute(BugInstance bug) {
466 if (stop.get()) {
467 return;
468 }
469 String oldValue = bug.getUserDesignationKey();
470 String key = designationKey;
471 if (key.equals(oldValue)) {
472 return;
473 }
474 Cloud plugin = _bugCollection != null ? _bugCollection.getCloud() : null;
475 if (plugin != null && "I_WILL_FIX".equals(key) && plugin.supportsClaims()) {
476 String claimedBy = plugin.claimedBy(bug);
477 if (claimedBy != null && !plugin.getUser().equals(claimedBy)) {
478 int result = JOptionPane.showConfirmDialog(null,
479 bug.getMessage() + "\n"
480 + claimedBy + " has already said they will fix this issue\n"
481 + "Do you want to also be listed as fixing this issue?\n"
482 + "If so, please coordinate with " + claimedBy,
483 "Issue already claimed", JOptionPane.YES_NO_CANCEL_OPTION);
484 if (result == JOptionPane.CANCEL_OPTION) {
485 stop.set(true);
486 return;
487 }
488 if (result != JOptionPane.YES_OPTION) {
489 key = "MUST_FIX";
490 }
491 }
492 }
493 changeDesignationOfBugRightNow(bug, key);
494 refresh();
495 }
496 });
497 }
498
499 @SuppressWarnings({"UnusedDeclaration"})
500 private void submitComment(List<BugInstance> selectedBugs) {
501 String comment = commentBox.getText();
502 if (isDefaultComment(comment)) {
503 comment = "";
504 }
505 // if (selectedBugs.size() > 1)
506 // if (!confirmAnnotation(selectedBugs))
507 // return;
508 if (designationCombo.getSelectedItem() != null) {
509 final int index = designationCombo.getSelectedIndex();
510 final String choice;
511 if (index == -1) {
512 choice = UserDesignation.UNCLASSIFIED.name();
513 } else {
514 choice = I18N.instance().getUserDesignationKeys(true).get(index);
515 }
516 setDesignation(choice);
517 }
518 final String finalComment = comment;
519 applyToBugs(new BugAction() {
520 @Override
521 public void execute(BugInstance bug) {
522 bug.setAnnotationText(finalComment, _bugCollection);
523 refresh();
524 setLastSaved(System.currentTimeMillis());
525 }
526 });
527
528 refresh();
529
530 setCanAddComments(true, false);
531 commentBox.requestFocus();
532 }
533
534 private void setLastSaved(long date) {
535 if (date > 0) {
536 lastSavedLabel.setText("saved " + DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT)
537 .format(new Date(date)));
538 } else {
539 lastSavedLabel.setText("");
540 }
541 }
542
543 protected void cancelClicked() {
544 setDefaultComment(lastCommentText);
545 // commentEntryPanel.setVisible(false);
546 setCanAddComments(true, false);
547 }
548
549 private List<BugInstance> getSelectedBugs() {
550 if (_bugInstance != null) {
551 return Collections.singletonList(_bugInstance);
552 }
553 if (_bugAspects != null) {
554 List<BugInstance> set = new ArrayList<BugInstance>();
555 for (BugLeafNode node : _bugAspects.getMatchingBugs(BugSet.getMainBugSet())) {
556 if (!BugSet.suppress(node)) {
557 set.add(node.getBug());
558 }
559 }
560 return set;
561 }
562 return Collections.emptyList();
563 }
564
565 private boolean hasSelectedBugs() {
566 return _bugInstance != null || _bugAspects != null;
567 }
568
569 protected void changeClicked() {
570 final List<CloudPlugin> plugins = new ArrayList<CloudPlugin>();
571 final List<String> descriptions = new ArrayList<String>();
572 List<CloudPlugin> clouds = new ArrayList<CloudPlugin>(DetectorFactoryCollection.instance().getRegisteredClouds().values());
573 Collections.sort(clouds, new Comparator<CloudPlugin>() {
574 @Override
575 public int compare(CloudPlugin o1, CloudPlugin o2) {
576 return o1.getDescription().compareToIgnoreCase(o2.getDescription());
577 }
578 });
579 for (final CloudPlugin plugin : clouds) {
580 final boolean disabled = isDisabled(plugin);
581 if (!disabled && !plugin.isHidden()) {
582 descriptions.add(plugin.getDescription());
583 plugins.add(plugin);
584 }
585 }
586 showCloudChooser(plugins, descriptions);
587 }
588
589 protected abstract boolean isDisabled(CloudPlugin plugin);
590
591 protected abstract void showCloudChooser(List<CloudPlugin> plugins, List<String> descriptions);
592
593 protected void changeCloud(String newCloudId) {
594 final String oldCloudId = _bugCollection.getCloud().getPlugin().getId();
595 if (!oldCloudId.equals(newCloudId)) {
596 _bugCollection.getProject().setCloudId(newCloudId);
597 MainFrame.getInstance().setProjectChanged(true);
598 backgroundExecutor.execute(new Runnable() {
599 @Override
600 public void run() {
601 _bugCollection.reinitializeCloud();
602 Cloud cloud = _bugCollection.getCloud();
603 if (cloud != null) {
604 cloud.waitUntilIssueDataDownloaded();
605 }
606 updateCloudListeners(_bugCollection);
607 refresh();
608 }
609 });
610 refresh();
611 }
612 }
613
614 public void setBugCollection(BugCollection bugCollection) {
615 updateCloudListeners(bugCollection);
616 _bugCollection = bugCollection;
617 _bugInstance = null;
618 _bugAspects = null;
619 refresh();
620 }
621
622 public void setBugInstance(final BugInstance bugInstance) {
623 setBugs(bugInstance, null);
624 }
625
626 public void setBugAspects(BugAspects aspects) {
627 setBugs(null, aspects);
628 }
629
630 private void setBugs(BugInstance bugInstance, BugAspects bugAspects) {
631 if (_bugInstance == bugInstance && _bugAspects == bugAspects) {
632 return;
633 }
634 if (!canNavigateAway()) {
635 return;
636 }
637
638 _bugInstance = bugInstance;
639 _bugAspects = bugAspects;
640 refresh();
641 }
642
643 public boolean canNavigateAway() {
644 if (commentWasChanged()) {
645 submitComment(getSelectedBugs());
646 return true;
647 } else {
648 return true;
649 }
650 }
651
652 protected void changeDesignationOfBugRightNow(final BugInstance bug, final String designationKey) {
653 String oldValue = bug.getUserDesignationKey();
654 if (designationKey.equals(oldValue)) {
655 return;
656 }
657 bug.setUserDesignationKey(designationKey, _bugCollection);
658 }
659
660 public void refresh() {
661 updateBugCommentsView();
662 }
663
664 public void updateCloud() {
665 updateCloudListeners(_bugCollection);
666 refresh();
667 }
668
669 private void updateCloudListeners(BugCollection newBugCollection) {
670 final Cloud newCloud = newBugCollection == null ? null : newBugCollection.getCloud();
671 if (_bugCollection != null) {
672 //noinspection ObjectEquality
673 if (lastCloud != newCloud) {
674 if (lastCloud != null) {
675 lastCloud.removeStatusListener(_cloudStatusListener);
676 }
677 }
678 }
679 if (lastCloud != newCloud && newCloud != null) {
680 lastCloud = newCloud;
681 newCloud.addStatusListener(_cloudStatusListener);
682 }
683 }
684
685
686 private boolean inCloud(Collection<BugInstance> bugs) {
687 final Cloud cloud = _bugCollection.getCloud();
688
689 for (BugInstance b : bugs) {
690 if (cloud.isInCloud(b)) {
691 return true;
692 }
693 }
694 return false;
695
696 }
697
698 private void updateBugCommentsView() {
699
700 //TODO: fix cancel button
701 List<BugInstance> bugs = getSelectedBugs();
702 if (_bugCollection == null) {
703 signInOutLink.setVisible(false);
704 cloudDetailsLabel.setText("");
705 cloudReportPane.setText("");
706 titleLabel.setText("<html>Reviews");
707 return;
708 }
709 updateHeader();
710 final Cloud cloud = _bugCollection.getCloud();
711 final CloudPlugin plugin = cloud.getPlugin();
712 String details = plugin.getDetails();
713 cloudDetailsLabel.setText(details);
714
715 if (bugs.isEmpty()) {
716 setCanAddComments(false, false);
717 return;
718 }
719
720 String report;
721 long lastSaved = -1;
722 if (bugs.size() > 1) {
723 int totalReviews = 0;
724 int bugsWithReviews = 0;
725 for (BugInstance bug : bugs) {
726 long newTs = cloud.getUserTimestamp(bug);
727 if (bug.hasSomeUserAnnotation() && newTs > 0 && (lastSaved == -1 || lastSaved < newTs)) {
728 lastSaved = newTs;
729 }
730 int reviewers = cloud.getNumberReviewers(bug);
731 if (reviewers > 0) {
732 bugsWithReviews++;
733 }
734 totalReviews += reviewers;
735 }
736 report = bugs.size() + " bug" + (bugs.size() == 1 ? "" : "s") + " selected\n";
737 report += bugsWithReviews + " reviewed bug" + (bugsWithReviews == 1 ? "" : "s")
738 + " / " + totalReviews + " total review" + (totalReviews == 1 ? "" : "s");
739 } else {
740 BugInstance bug = bugs.get(0);
741 if (bug.hasSomeUserAnnotation()) {
742 lastSaved = bug.getUserTimestamp();
743 }
744 report = cloud.getCloudReportWithoutMe(bug);
745 }
746 setLastSaved(lastSaved);
747 cloudReportPane.setText(report);
748 CommentInfo commentInfo = new CommentInfo().invoke();
749 boolean sameText = commentInfo.isSameText();
750 String txt = commentInfo.getTxt();
751 CardLayout cl = (CardLayout) (cards.getLayout());
752 HashSet<BugInstance> newBugSet = new HashSet<BugInstance>(bugs);
753 boolean sameBugs = newBugSet.equals(lastBugsEdited);
754 if (!sameBugs) {
755 lastBugsEdited = newBugSet;
756 clickedBulkReview = false;
757 }
758 if (!inCloud(bugs)) {
759 cl.show(cards, "NOT_IN_CLOUD");
760 } else if (bugs.size() > 1 && !clickedBulkReview) {
761 warningLabel.setText("<HTML>" + bugs.size() + " bugs are selected.<BR>Click to review them all at once.");
762 cl.show(cards, "WARNING");
763 } else {
764 cl.show(cards, "COMMENTS");
765 }
766 if (!sameText) {
767 txt = MessageFormat.format(MSG_OVERWRITE_REVIEW, bugs.size());
768 setDefaultComment(txt);
769 } else {
770 if (txt == null || txt.trim().length() == 0) {
771 txt = bugs.size() > 1 ? MessageFormat.format(MSG_REVIEW_MULTI, bugs.size()) : MSG_REVIEW;
772 setDefaultComment(txt);
773 } else {
774 resetCommentBoxFont();
775 setCommentText(txt);
776 }
777 }
778
779 setCanAddComments(cloud.canStoreUserAnnotation(bugs.get(0)), false);
780 updateSaveButton();
781 }
782
783 private boolean updatingHeader = false;
784
785 private void updateHeader() {
786 final Cloud cloud = _bugCollection.getCloud();
787 CloudPlugin plugin = cloud.getPlugin();
788 if (hasSelectedBugs()) {
789 CommentInfo commentInfo = new CommentInfo().invoke();
790 boolean sameDesignation = commentInfo.isSameDesignation();
791 String designation = commentInfo.getDesignation();
792 if (!sameDesignation) {
793 designation = null;
794 }
795 updatingHeader = true;
796 designationCombo.setSelectedIndex(I18N.instance().getUserDesignationKeys(true).indexOf(designation));
797 updatingHeader = false;
798 setCanAddComments(true, true);
799 } else {
800 setCanAddComments(false, false);
801 }
802
803 final Cloud.SigninState state = cloud.getSigninState();
804 final String stateStr = state == Cloud.SigninState.NO_SIGNIN_REQUIRED ? "" : "" + state;
805 final String userStr = cloud.getUser() == null ? "" : cloud.getUser();
806 if ("edu.umd.cs.findbugs.cloud.doNothingCloud".equals(plugin.getId())) {
807 titleLabel.setText("<html><b>No cloud selected");
808 } else {
809 titleLabel.setText("<html><b>Reviews - " + cloud.getCloudName() + "</b>"
810 + "<br><font style='font-size: x-small;color:darkgray'>" + stateStr
811 + (userStr.length() > 0 ? " - " + userStr : ""));
812 }
813 switch (state) {
814 case NO_SIGNIN_REQUIRED:
815 case SIGNING_IN:
816 signInOutLink.setVisible(false);
817 break;
818 case SIGNED_IN:
819 setSignInOutText("sign out");
820 signInOutLink.setVisible(true);
821 break;
822 default:
823 if (state.couldSignIn()) {
824 setSignInOutText("sign in");
825 signInOutLink.setVisible(true);
826 }
827 break;
828 }
829 if ("edu.umd.cs.findbugs.cloud.doNothingCloud".equals(cloud.getPlugin().getId())) {
830 setSignInOutText("enable cloud plugin...");
831 signInOutLink.setVisible(true);
832 }
833 }
834
835 private void setCanAddComments(boolean canClick, boolean canEnter) {
836 submitCommentButton.setEnabled(canClick || canEnter);
837 designationCombo.setEnabled(canClick || canEnter);
838 commentBox.setEnabled(canClick || canEnter);
839 }
840
841 protected abstract void setSignInOutText(String buttonText);
842
843 /**
844 * Method generated by IntelliJ IDEA GUI Designer
845 * >>> IMPORTANT!! <<<
846 * DO NOT edit this method OR call it in your code!
847 *
848 * @noinspection ALL
849 */
850 private void $$$setupUI$$$() {
851 createUIComponents();
852 mainPanel = new JPanel();
853 mainPanel.setLayout(new GridBagLayout());
854 mainPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(3, 3, 3, 3), null));
855 _cloudReportScrollPane = new JScrollPane();
856 GridBagConstraints gbc;
857 gbc = new GridBagConstraints();
858 gbc.gridx = 0;
859 gbc.gridy = 2;
860 gbc.gridwidth = 6;
861 gbc.weightx = 1.0;
862 gbc.weighty = 1.0;
863 gbc.fill = GridBagConstraints.BOTH;
864 gbc.insets = new Insets(5, 5, 5, 5);
865 mainPanel.add(_cloudReportScrollPane, gbc);
866 cloudReportPane = new JTextArea();
867 cloudReportPane.setEditable(false);
868 cloudReportPane.setLineWrap(true);
869 cloudReportPane.setText("<html>\r\n <head>\r\n \r\n </head>\r\n <body>\r\n </body>\r\n</html>\r\n");
870 cloudReportPane.setWrapStyleWord(true);
871 _cloudReportScrollPane.setViewportView(cloudReportPane);
872 final JPanel panel1 = new JPanel();
873 panel1.setLayout(new GridBagLayout());
874 panel1.setBackground(new Color(-3355444));
875 gbc = new GridBagConstraints();
876 gbc.gridx = 0;
877 gbc.gridy = 0;
878 gbc.gridwidth = 6;
879 gbc.weightx = 1.0;
880 gbc.fill = GridBagConstraints.BOTH;
881 mainPanel.add(panel1, gbc);
882 panel1.setBorder(BorderFactory.createTitledBorder(BorderFactory.createLineBorder(new Color(-16751002)), null));
883 titleLabel = new JLabel();
884 titleLabel.setFont(new Font(titleLabel.getFont().getName(), Font.BOLD, 14));
885 titleLabel.setForeground(new Color(-16777216));
886 titleLabel.setText("FindBugs Cloud - signed in");
887 gbc = new GridBagConstraints();
888 gbc.gridx = 0;
889 gbc.gridy = 0;
890 gbc.gridheight = 2;
891 gbc.weightx = 1.0;
892 gbc.anchor = GridBagConstraints.NORTHWEST;
893 panel1.add(titleLabel, gbc);
894 dumbPanelSignInOutLink = new JPanel();
895 dumbPanelSignInOutLink.setLayout(new GridBagLayout());
896 dumbPanelSignInOutLink.setOpaque(false);
897 dumbPanelSignInOutLink.setPreferredSize(new Dimension(50, 10));
898 gbc = new GridBagConstraints();
899 gbc.gridx = 1;
900 gbc.gridy = 0;
901 gbc.gridheight = 2;
902 gbc.fill = GridBagConstraints.BOTH;
903 panel1.add(dumbPanelSignInOutLink, gbc);
904 gbc = new GridBagConstraints();
905 gbc.gridx = 0;
906 gbc.gridy = 0;
907 gbc.anchor = GridBagConstraints.NORTHWEST;
908 dumbPanelSignInOutLink.add(signInOutLink, gbc);
909 final JPanel panel2 = new JPanel();
910 panel2.setLayout(new GridBagLayout());
911 panel2.setVisible(false);
912 gbc = new GridBagConstraints();
913 gbc.gridx = 0;
914 gbc.gridy = 1;
915 gbc.weightx = 1.0;
916 gbc.fill = GridBagConstraints.BOTH;
917 gbc.insets = new Insets(5, 5, 5, 5);
918 mainPanel.add(panel2, gbc);
919 cloudDetailsLabel = new JTextArea();
920 cloudDetailsLabel.setEditable(false);
921 cloudDetailsLabel.setFont(new Font(cloudDetailsLabel.getFont().getName(), Font.ITALIC, 10));
922 cloudDetailsLabel.setForeground(new Color(-10066330));
923 cloudDetailsLabel.setLineWrap(true);
924 cloudDetailsLabel.setMaximumSize(new Dimension(100, 50));
925 cloudDetailsLabel.setMinimumSize(new Dimension(50, 16));
926 cloudDetailsLabel.setOpaque(false);
927 cloudDetailsLabel.setPreferredSize(new Dimension(100, 31));
928 cloudDetailsLabel.setText("Comments are stored on the FindBugs Cloud at http://findbugs-cloud.appspot.com");
929 cloudDetailsLabel.setWrapStyleWord(true);
930 gbc = new GridBagConstraints();
931 gbc.gridx = 0;
932 gbc.gridy = 0;
933 gbc.weightx = 1.0;
934 gbc.weighty = 1.0;
935 gbc.fill = GridBagConstraints.BOTH;
936 panel2.add(cloudDetailsLabel, gbc);
937 final JPanel panel3 = new JPanel();
938 panel3.setLayout(new GridBagLayout());
939 gbc = new GridBagConstraints();
940 gbc.gridx = 1;
941 gbc.gridy = 3;
942 gbc.gridwidth = 5;
943 gbc.weightx = 1.0;
944 gbc.weighty = 1.0;
945 gbc.fill = GridBagConstraints.BOTH;
946 mainPanel.add(panel3, gbc);
947 cards = new JPanel();
948 cards.setLayout(new CardLayout(0, 0));
949 gbc = new GridBagConstraints();
950 gbc.gridx = 5;
951 gbc.gridy = 4;
952 gbc.weightx = 1.0;
953 gbc.weighty = 1.0;
954 gbc.fill = GridBagConstraints.BOTH;
955 mainPanel.add(cards, gbc);
956 final JPanel panel4 = new JPanel();
957 panel4.setLayout(new GridBagLayout());
958 cards.add(panel4, "COMMENTS");
959 designationCombo = new WideComboBox<>();
960 gbc = new GridBagConstraints();
961 gbc.gridx = 4;
962 gbc.gridy = 0;
963 gbc.gridwidth = 2;
964 gbc.anchor = GridBagConstraints.NORTHWEST;
965 gbc.insets = new Insets(5, 0, 0, 0);
966 panel4.add(designationCombo, gbc);
967 final JScrollPane scrollPane1 = new JScrollPane();
968 gbc = new GridBagConstraints();
969 gbc.gridx = 0;
970 gbc.gridy = 0;
971 gbc.gridwidth = 4;
972 gbc.gridheight = 4;
973 gbc.weightx = 1.0;
974 gbc.fill = GridBagConstraints.BOTH;
975 panel4.add(scrollPane1, gbc);
976 scrollPane1.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), null));
977 commentBox = new JTextArea();
978 commentBox.setLineWrap(true);
979 commentBox.setRows(5);
980 commentBox.setText(" ");
981 commentBox.setWrapStyleWord(true);
982 scrollPane1.setViewportView(commentBox);
983 submitCommentButton = new JButton();
984 submitCommentButton.setText("Save");
985 gbc = new GridBagConstraints();
986 gbc.gridx = 4;
987 gbc.gridy = 1;
988 gbc.insets = new Insets(5, 5, 5, 5);
989 panel4.add(submitCommentButton, gbc);
990 lastSavedLabel = new JLabel();
991 lastSavedLabel.setFont(new Font(lastSavedLabel.getFont().getName(), Font.ITALIC, 9));
992 lastSavedLabel.setText("saved at");
993 gbc = new GridBagConstraints();
994 gbc.gridx = 4;
995 gbc.gridy = 3;
996 gbc.gridwidth = 2;
997 panel4.add(lastSavedLabel, gbc);
998 gbc = new GridBagConstraints();
999 gbc.gridx = 5;
1000 gbc.gridy = 1;
1001 panel4.add(cancelLink, gbc);
1002 final JPanel panel5 = new JPanel();
1003 panel5.setLayout(new GridBagLayout());
1004 cards.add(panel5, "WARNING");
1005 warningLabel = new JLabel();
1006 warningLabel.setHorizontalAlignment(0);
1007 warningLabel.setHorizontalTextPosition(0);
1008 warningLabel.setText("<HTML>Multiple bugs are selected.<BR>Click to review them all at once.");
1009 gbc = new GridBagConstraints();
1010 gbc.gridx = 0;
1011 gbc.gridy = 0;
1012 gbc.insets = new Insets(10, 10, 10, 10);
1013 panel5.add(warningLabel, gbc);
1014 bulkReviewButton = new JButton();
1015 bulkReviewButton.setText("Bulk Review");
1016 gbc = new GridBagConstraints();
1017 gbc.gridx = 1;
1018 gbc.gridy = 0;
1019 gbc.fill = GridBagConstraints.HORIZONTAL;
1020 gbc.insets = new Insets(10, 10, 10, 10);
1021 panel5.add(bulkReviewButton, gbc);
1022 }
1023
1024 /**
1025 * @noinspection ALL
1026 */
1027 public JComponent $$$getRootComponent$$$() {
1028 return mainPanel;
1029 }
1030
1031 private class MyCloudStatusListener implements Cloud.CloudStatusListener {
1032 @Override
1033 public void handleIssueDataDownloadedEvent() {
1034 refresh();
1035 }
1036
1037
1038 @Override
1039 public void handleStateChange(final Cloud.SigninState oldState, final Cloud.SigninState state) {
1040 updateHeader();
1041 refresh();
1042 }
1043
1044
1045 }
1046
1047 private interface BugAction {
1048 void execute(BugInstance bug);
1049 }
1050
1051 private class CommentInfo {
1052 private String txt;
1053 private boolean sameText;
1054 private String designation;
1055 private boolean sameDesignation;
1056
1057 public String getTxt() {
1058 return txt;
1059 }
1060
1061 public boolean isSameText() {
1062 return sameText;
1063 }
1064
1065 public String getDesignation() {
1066 return designation;
1067 }
1068
1069 public boolean isSameDesignation() {
1070 return sameDesignation;
1071 }
1072
1073 public CommentInfo invoke() {
1074 txt = null;
1075 sameText = true;
1076 designation = null;
1077 sameDesignation = true;
1078 for (BugInstance bug : getSelectedBugs()) {
1079 String newText = bug.getAnnotationText();
1080 if (txt == null) {
1081 txt = newText;
1082 } else {
1083 if (!nullSafeEquals(txt, newText)) {
1084 sameText = false;
1085 }
1086 }
1087
1088 String newDesignation = bug.getUserDesignationKey();
1089 if (designation == null) {
1090 designation = newDesignation;
1091 } else {
1092 if (!nullSafeEquals(designation, newDesignation)) {
1093 sameDesignation = false;
1094 }
1095 }
1096 }
1097 return this;
1098 }
1099 }
1100 }
6969 fileBug.setToolTipText("Click to file bug for this issue");
7070 fileBug.addActionListener(new ActionListener() {
7171
72 @Override
7273 public void actionPerformed(ActionEvent e) {
7374 if (frame.getCurrentSelectedBugLeaf() == null) {
7475 return;
7576 }
76 if (!canNavigateAway())
77 if (!canNavigateAway()) {
7778 return;
79 }
7880 BugInstance bug = frame.getCurrentSelectedBugLeaf().getBug();
7981 Cloud cloud1 = MainFrame.getInstance().getBugCollection().getCloud();
80 if (!cloud1.supportsBugLinks())
82 if (!cloud1.supportsBugLinks()) {
8183 return;
84 }
8285 try {
8386 URL u = cloud1.getBugLink(bug);
8487 if (u != null) {
117120 }
118121
119122 void updateCommentsFromLeafInformation(final BugLeafNode node) {
120 if (node == null)
123 if (node == null) {
121124 return;
125 }
122126 SwingUtilities.invokeLater(new Runnable() {
127 @Override
123128 public void run() {
124129 BugInstance bug = node.getBug();
125130 Cloud plugin = getCloud();
144149
145150 void updateCommentsFromNonLeafInformation(final BugAspects theAspects) {
146151 SwingUtilities.invokeLater(new Runnable() {
152 @Override
147153 public void run() {
148154 updateCommentsFromNonLeafInformationFromSwingThread(theAspects);
149155 }
167173 private @CheckForNull Cloud getCloud() {
168174 MainFrame instance = MainFrame.getInstance();
169175 BugCollection bugCollection = instance.getBugCollection();
170 if (bugCollection == null)
176 if (bugCollection == null) {
171177 return null;
178 }
172179 return bugCollection.getCloud();
173180 }
174181
3333 public class CompoundMatcher extends HashSet<Matcher> implements Matcher {
3434 private static final long serialVersionUID = -6167545252176658833L;
3535
36 @Override
3637 public boolean match(BugInstance bugInstance) {
37 for (Matcher i : this)
38 if (!i.match(bugInstance))
38 for (Matcher i : this) {
39 if (!i.match(bugInstance)) {
3940 return false;
41 }
42 }
4043 return true;
4144 }
4245
46 @Override
4347 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
4448 throw new UnsupportedOperationException();
4549 }
1818
1919 package edu.umd.cs.findbugs.gui2;
2020
21 import edu.umd.cs.findbugs.BugInstance;
22
2321 /**
2422 * @author Dan
2523 */
2624 public class DeadBugFilter extends FilterMatcher {
2725
28 /**
29 * @param filterBy
30 * @param value
31 * @param mode
32 */
3326 public DeadBugFilter(Sortables filterBy, String value, FilterWhere mode) {
3427 super(filterBy, value, mode);
3528 }
3932 return edu.umd.cs.findbugs.L10N.getLocalString("pref.dead_bugs", "Dead Bugs");
4033 }
4134
42 @Override
43 public boolean match(BugInstance bugInstance) {
44 return super.match(bugInstance);
45 }
4635 }
2121 /**
2222 * For debugging purposes only... Make sure DEBUG is set to false before you
2323 * release a new version.
24 *
24 *
2525 * @author Dan
26 *
26 *
2727 */
2828 public class Debug {
2929 public static void println(Object s) {
30 if (MainFrame.GUI2_DEBUG)
30 if (MainFrame.GUI2_DEBUG) {
3131 System.out.println(s);
32 }
3233 }
3334
3435 public static void printf(String format, Object... args) {
35 if (MainFrame.GUI2_DEBUG)
36 if (MainFrame.GUI2_DEBUG) {
3637 System.out.printf(format, args);
38 }
3739 }
3840
3941 public static void println(Exception e) {
40 if (MainFrame.GUI2_DEBUG)
42 if (MainFrame.GUI2_DEBUG) {
4143 e.printStackTrace();
44 }
4245 }
4346
4447 public static void main(String[] args) {
4949
5050 static Font sourceFont = new Font("Monospaced", Font.PLAIN, (int) Driver.getFontSize());
5151
52 static void setMessageFrame(JFrame messageFrame) {
53 DisplayNonmodelMessage.messageFrame = messageFrame;
54 }
55
5256 public static void displayNonmodelMessage(String title, String message, @CheckForNull Component centerOver, boolean onTop) {
5357 boolean positionWindow = false;
5458 if (messageFrame == null) {
5559 positionWindow = true;
56 messageFrame = new JFrame(title);
60 setMessageFrame(new JFrame(title));
5761
5862 messageTextArea = new JTextArea(40, 80);
5963 messageTextArea.setEditable(false);
7478 @Override
7579 public void windowClosed(WindowEvent e) {
7680 JFrame tmp = messageFrame;
77 messageFrame = null;
81 setMessageFrame(null);
7882 tmp.setVisible(false);
7983 tmp.dispose();
8084 }
8387 messageTextArea.setText(message);
8488 messageFrame.setTitle(title);
8589 messageFrame.pack();
86 if (positionWindow)
90 if (positionWindow) {
8791 messageFrame.setLocationRelativeTo(centerOver);
92 }
8893
8994 messageFrame.setVisible(true);
9095 messageFrame.toFront();
102107 }
103108 }
104109
110 /*
105111 static Runnable moveToFrontLater = new Runnable() {
112 @Override
106113 public void run() {
107114 sleep(5);
108115 SwingUtilities.invokeLater(moveToFront);
109116 }
110117 };
118 */
111119
112120 static Runnable clearAlwaysOnTopLater = new Runnable() {
121 @Override
113122 public void run() {
114123 sleep(5);
115124 SwingUtilities.invokeLater(clearAlwaysOnTop);
116125 }
117126 };
118127
128 /*
119129 static Runnable moveToFront = new Runnable() {
130 @Override
120131 public void run() {
121132 JFrame frame = messageFrame;
122 if (frame != null)
133 if (frame != null) {
123134 frame.toFront();
135 }
124136 }
125137 };
138 */
126139
127140 static Runnable clearAlwaysOnTop = new Runnable() {
141 @Override
128142 public void run() {
129143 JFrame frame = messageFrame;
130 if (frame != null)
144 if (frame != null) {
131145 frame.setAlwaysOnTop(false);
146 }
132147 }
133148 };
134149 }
5050 public static void main(String[] args) throws Exception {
5151 try {
5252
53 String name = "FindBugs GUI";
54 if (JavaWebStart.isRunningViaJavaWebstart())
55 name = "FindBugs webstart GUI";
56 Version.registerApplication(name, Version.RELEASE);
53 String name = "FindBugs GUI";
54 if (JavaWebStart.isRunningViaJavaWebstart()) {
55 name = "FindBugs webstart GUI";
56 }
57 Version.registerApplication(name, Version.RELEASE);
5758
58 if (SystemProperties.getProperty("os.name").startsWith("Mac")) {
59 System.setProperty("apple.laf.useScreenMenuBar", "true");
60 System.setProperty("com.apple.mrj.application.apple.menu.about.name", "FindBugs");
61 Debug.println("Mac OS detected");
62 }
63 splash = new SplashFrame();
64 splash.setVisible(true);
59 if (SystemProperties.getProperty("os.name").startsWith("Mac")) {
60 System.setProperty("apple.laf.useScreenMenuBar", "true");
61 System.setProperty("com.apple.mrj.application.apple.menu.about.name", "FindBugs");
62 Debug.println("Mac OS detected");
63 }
64 splash = new SplashFrame();
65 splash.setVisible(true);
6566
66 int numParsed = commandLine.parse(args, 0, 1, USAGE);
67 int numParsed = commandLine.parse(args, 0, 1, USAGE);
6768
68 //
69 // See if an argument filename was specified after the parsed
70 // options/switches.
71 //
72 if (numParsed < args.length) {
73 String arg = args[numParsed];
74 String argLowerCase = arg.toLowerCase(Locale.ENGLISH);
75 if (argLowerCase.endsWith(".fbp") || argLowerCase.endsWith(".fb")) {
76 // Project file specified
77 commandLine.loadProject(arg);
78 } else if (argLowerCase.endsWith(".xml") || argLowerCase.endsWith(".xml.gz") || argLowerCase.endsWith(".fba")) {
79 // Saved analysis results specified
80 commandLine.setSaveFile(new File(arg));
69 //
70 // See if an argument filename was specified after the parsed
71 // options/switches.
72 //
73 if (numParsed < args.length) {
74 String arg = args[numParsed];
75 String argLowerCase = arg.toLowerCase(Locale.ENGLISH);
76 if (argLowerCase.endsWith(".fbp") || argLowerCase.endsWith(".fb")) {
77 // Project file specified
78 commandLine.loadProject(arg);
79 } else if (argLowerCase.endsWith(".xml") || argLowerCase.endsWith(".xml.gz") || argLowerCase.endsWith(".fba")) {
80 // Saved analysis results specified
81 commandLine.setSaveFile(new File(arg));
82 } else {
83 System.out.println("Unknown argument: " + arg);
84 commandLine.printUsage(System.out);
85 System.exit(1);
86 }
87 }
88
89 if (commandLine.getDocking()) {
90 // make sure docking runtime support is available
91 try {
92 Class.forName("net.infonode.docking.DockingWindow");
93 Class.forName("edu.umd.cs.findbugs.gui2.DockLayout");
94 } catch (Exception e) {
95 commandLine.setDocking(false);
96 }
97 }
98
99 try {
100 GUISaveState.loadInstance();
101 } catch (RuntimeException e) {
102 GUISaveState.clear();
103 e.printStackTrace();
104 }
105
106 GUISaveState guiSavedPreferences = GUISaveState.getInstance();
107 if (commandLine.isFontSizeSpecified()) {
108 guiSavedPreferences.setFontSize(commandLine.getFontSize());
109 }
110
111 // System.setProperty("findbugs.home",".."+File.separator+"findbugs");
112
113 enablePlugins(guiSavedPreferences.getEnabledPlugins(), true);
114 enablePlugins(guiSavedPreferences.getDisabledPlugins(), false);
115
116 // The bug with serializable idiom detection has been fixed on the
117 // findbugs end.
118 // DetectorFactory
119 // serializableIdiomDetector=DetectorFactoryCollection.instance().getFactory("SerializableIdiom");
120 // System.out.println(serializableIdiomDetector.getFullName());
121 // UserPreferences.getUserPreferences().enableDetector(serializableIdiomDetector,false);
122
123 FindBugsLayoutManagerFactory factory;
124
125 if (isDocking()) {
126 factory = new FindBugsLayoutManagerFactory("edu.umd.cs.findbugs.gui2.DockLayout");
81127 } else {
82 System.out.println("Unknown argument: " + arg);
83 commandLine.printUsage(System.out);
84 System.exit(1);
128 factory = new FindBugsLayoutManagerFactory(SplitLayout.class.getName());
85129 }
86 }
87
88 if (commandLine.getDocking()) {
89 // make sure docking runtime support is available
90 try {
91 Class.forName("net.infonode.docking.DockingWindow");
92 Class.forName("edu.umd.cs.findbugs.gui2.DockLayout");
93 } catch (Exception e) {
94 commandLine.setDocking(false);
95 }
96 }
97
98 try {
99 GUISaveState.loadInstance();
100 } catch (RuntimeException e) {
101 GUISaveState.clear();
102 e.printStackTrace();
103 }
104
105 GUISaveState guiSavedPreferences = GUISaveState.getInstance();
106 if (commandLine.isFontSizeSpecified())
107 guiSavedPreferences.setFontSize(commandLine.getFontSize());
108
109 // System.setProperty("findbugs.home",".."+File.separator+"findbugs");
110
111 enablePlugins(guiSavedPreferences.getEnabledPlugins(), true);
112 enablePlugins(guiSavedPreferences.getDisabledPlugins(), false);
113
114 // The bug with serializable idiom detection has been fixed on the
115 // findbugs end.
116 // DetectorFactory
117 // serializableIdiomDetector=DetectorFactoryCollection.instance().getFactory("SerializableIdiom");
118 // System.out.println(serializableIdiomDetector.getFullName());
119 // UserPreferences.getUserPreferences().enableDetector(serializableIdiomDetector,false);
120
121 FindBugsLayoutManagerFactory factory;
122
123 if (isDocking())
124 factory = new FindBugsLayoutManagerFactory("edu.umd.cs.findbugs.gui2.DockLayout");
125 else
126 factory = new FindBugsLayoutManagerFactory(SplitLayout.class.getName());
127 MainFrame.makeInstance(factory);
130 MainFrame.makeInstance(factory);
128131
129132
130 splash.setVisible(false);
131 splash.dispose();
133 splash.setVisible(false);
134 splash.dispose();
132135 } catch (Throwable t) {
133136 JOptionPane.showMessageDialog(null, t.toString(), "Fatal Error during FindBugs startup", JOptionPane.ERROR_MESSAGE);
134137 t.printStackTrace(System.err);
140143 for (String pid : plugins) {
141144 Plugin plugin = Plugin.getByPluginId(pid);
142145 if (plugin != null) {
143 if (!enabled && plugin.cannotDisable())
146 if (!enabled && plugin.cannotDisable()) {
144147 JOptionPane.showMessageDialog(null,
145148 "Cannot disable plugin: " + plugin.getPluginId() + "\n" + plugin.getShortDescription(),
146149 "Cannot disable plugin", JOptionPane.ERROR_MESSAGE);
147 else
150 } else {
148151 plugin.setGloballyEnabled(enabled);
152 }
149153 }
150154 }
151155 }
152156
153157 public static void removeSplashScreen() {
154 if (splash == null)
158 if (splash == null) {
155159 return;
160 }
156161 splash.setVisible(false);
157162 splash.dispose();
158163
2727
2828 /**
2929 * All Dialogs are FBDialogs so font size will work.
30 *
30 *
3131 * @author Kristin
32 *
32 *
3333 */
3434 @SuppressWarnings("serial")
3535 public class FBDialog extends JDialog {
4848
4949 /**
5050 * Sets size of font
51 *
51 *
5252 * @param size
5353 */
5454 protected void setFontSize(float size) {
6161 * Helps above method, runs through all components recursively.
6262 */
6363 protected void setFontSizeHelper(Component[] comps, float size) {
64 if (comps.length <= 0)
64 if (comps.length <= 0) {
6565 return;
66 }
6667
6768 for (Component comp : comps) {
6869 comp.setFont(comp.getFont().deriveFont(size));
69 if (comp instanceof Container)
70 if (comp instanceof Container) {
7071 setFontSizeHelper(((Container) comp).getComponents(), size);
72 }
7173 }
7274 }
7375
3434
3535 /**
3636 * All FileChoosers are FBFileChoosers so font size will work
37 *
37 *
3838 * @author Kristin
39 *
39 *
4040 */
4141 public class FBFileChooser extends JFileChooser {
4242
4848
4949 /**
5050 * Sets size of font
51 *
51 *
5252 * @param size
5353 */
5454 protected void setFontSize(float size) {
6161 * Helps above method, runs through all components recursively.
6262 */
6363 protected void setFontSizeHelper(Component[] comps, float size) {
64 if (comps.length <= 0)
64 if (comps.length <= 0) {
6565 return;
66 }
6667
6768 for (Component comp : comps) {
6869 comp.setFont(comp.getFont().deriveFont(size));
69 if (comp instanceof Container)
70 if (comp instanceof Container) {
7071 setFontSizeHelper(((Container) comp).getComponents(), size);
72 }
7173 }
7274 }
7375
98100 public int showOpenDialog(Component parent) {
99101 assert java.awt.EventQueue.isDispatchThread();
100102 int x = super.showOpenDialog(parent);
101 if (SystemProperties.getProperty("os.name").startsWith("Mac"))
103 if (SystemProperties.getProperty("os.name").startsWith("Mac")) {
102104 workAroundJFileChooserBug();
105 }
103106
104107 GUISaveState.getInstance().setStarterDirectoryForLoadBugs(getCurrentDirectory());
105108
110113 public int showSaveDialog(Component parent) {
111114 assert java.awt.EventQueue.isDispatchThread();
112115 int x = super.showSaveDialog(parent);
113 if (SystemProperties.getProperty("os.name").startsWith("Mac"))
116 if (SystemProperties.getProperty("os.name").startsWith("Mac")) {
114117 workAroundJFileChooserBug();
118 }
115119
116120 GUISaveState.getInstance().setStarterDirectoryForLoadBugs(getCurrentDirectory());
117121
122126 public int showDialog(Component parent, String approveButtonText) {
123127 assert java.awt.EventQueue.isDispatchThread();
124128 int x = super.showDialog(parent, approveButtonText);
125 if (SystemProperties.getProperty("os.name").startsWith("Mac"))
129 if (SystemProperties.getProperty("os.name").startsWith("Mac")) {
126130 workAroundJFileChooserBug();
131 }
127132
128133 GUISaveState.getInstance().setStarterDirectoryForLoadBugs(getCurrentDirectory());
129134
130135 return x;
131136 }
132
137
133138 private void addHiddenFileCheckBox() {
134139 final JCheckBox showHiddenFileCheckBox = new JCheckBox("Show Hidden");
135140 JPanel accessory = new JPanel();
139144 setAccessory(accessory);
140145
141146 showHiddenFileCheckBox.addActionListener(new ActionListener() {
147 @Override
142148 public void actionPerformed(ActionEvent ae) {
143149 setFileHidingEnabled(!showHiddenFileCheckBox.isSelected());
144150 }
3030
3131 /**
3232 * Sets size of font
33 *
33 *
3434 * @param size
3535 */
3636 protected void setFontSize(float size) {
5656 protected void setFontSizeHelper(float size, Component... comps) {
5757 for (Component comp : comps) {
5858 comp.setFont(comp.getFont().deriveFont(size));
59 if (comp instanceof Container)
59 if (comp instanceof Container) {
6060 setFontSizeHelper(size, ((Container) comp).getComponents());
61 }
6162 }
6263 }
6364
4646 switch (whatsGoingOnCode) {
4747 case FILTERING:
4848 case UNFILTERING:
49 for (FilterListener i : currentListeners)
49 for (FilterListener i : currentListeners) {
5050 i.clearCache();
51 }
5152 break;
52
53
5354 }
5455 MainFrame.getInstance().updateStatusBar();
5556 }
56
57
5758 public static class FilterActivityNotifier {
5859 public void notifyListeners(FilterListener.Action whatsGoingOnCode, TreePath optionalPath) {
5960 FilterActivity.notifyListeners(whatsGoingOnCode, optionalPath);
4040 * @author pugh
4141 */
4242 public class FilterFactory {
43
43
4444 public static Matcher makeOrMatcher(Collection<SortableValue> sortables) {
4545 return makeMatcher(sortables, false);
4646 }
5353 if (originalMatcher instanceof NotMatcher) {
5454 return ((NotMatcher) originalMatcher).originalMatcher();
5555 }
56
56
5757 NotMatcher notMatcher = new NotMatcher();
5858 notMatcher.addChild(originalMatcher);
5959 return notMatcher;
6060 }
61
61
6262 private static Matcher makeMatcher(Collection<SortableValue> sortables, boolean andOr) {
6363 if (sortables.size() == 1) {
64 for (SortableValue s : sortables)
64 for (SortableValue s : sortables) {
6565 return makeMatcher(s);
66 }
6667 }
6768 edu.umd.cs.findbugs.filter.CompoundMatcher matcher;
68 if (andOr == true)
69 if (andOr == true) {
6970 matcher = new AndMatcher();
70 else
71 } else {
7172 matcher = new OrMatcher();
72 for (SortableValue s : sortables)
73 }
74 for (SortableValue s : sortables) {
7375 matcher.addChild(makeMatcher(s));
76 }
7477 return matcher;
7578 }
7679
7780 public static Matcher makeMatcher(Collection<Sortables> sortables, BugInstance bug) {
7881 if (sortables.size() == 1) {
79 for (Sortables s : sortables)
82 for (Sortables s : sortables) {
8083 return makeMatcher(s, bug);
84 }
8185 }
8286 AndMatcher matcher = new AndMatcher();
83 for (Sortables s : sortables)
87 for (Sortables s : sortables) {
8488 matcher.addChild(makeMatcher(s, bug));
89 }
8590 return matcher;
8691 }
8792
88
93
8994 public static boolean canFilter(Sortables s) {
9095 switch (s) {
9196 case BUGCODE:
103108 return false;
104109 }
105110 }
106
111
107112
108113 private static Matcher makeMatcher(Sortables s, BugInstance bug) {
109114 switch (s) {
123128 case PACKAGE:
124129 String p = Sortables.CLASS.getFrom(bug);
125130 int lastDot = p.lastIndexOf('.');
126 if (lastDot > 0)
131 if (lastDot > 0) {
127132 p = p.substring(0, lastDot);
133 }
128134 return new ClassMatcher("~" + p + "\\.[^.]+");
129135 case PRIORITY:
130136 return new PriorityMatcher(Integer.toString(bug.getPriority()));
131
137
132138 case TYPE:
133139 return new BugMatcher(null, s.getFrom(bug), null);
134140
135141 case BUG_RANK:
136142 return new RankMatcher(s.getFrom(bug));
137
143
138144 case DIVIDER:
139145 default:
140146 throw new IllegalArgumentException("Don't know how to make maker for " + s);
00 /*
11 * FindBugs - Find Bugs in Java programs
22 * Copyright (C) 2003-2008 University of Maryland
3 *
3 *
44 * This library is free software; you can redistribute it and/or
55 * modify it under the terms of the GNU Lesser General Public
66 * License as published by the Free Software Foundation; either
77 * version 2.1 of the License, or (at your option) any later version.
8 *
8 *
99 * This library is distributed in the hope that it will be useful,
1010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1111 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1212 * Lesser General Public License for more details.
13 *
13 *
1414 * You should have received a copy of the GNU Lesser General Public
1515 * License along with this library; if not, write to the Free Software
1616 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
3737
3838 /**
3939 * Creates a list of options on for filtering bugs based on the current bug selected.
40 *
40 *
4141 * Gives the option to invert the created filter by wrapping it in a {@link NotMatcher}.
42 *
42 *
4343 * @author Graham Allan (grundlefleck@gmail.com)
4444 */
4545 final class FilterFromBugPicker {
46
46
4747 private final HashMap<JCheckBox, Sortables> map = new HashMap<JCheckBox, Sortables>();
4848 private final BugInstance bug;
4949 private final List<Sortables> availableSortables;
5050 private final JPanel pickerPanel;
5151 private final JCheckBox notFilterCheck = new JCheckBox("Invert (i.e. filter bugs which do not match selected criteria).");
52
52
5353 public FilterFromBugPicker(BugInstance bug, List<Sortables> availableSortables) {
5454 this.bug = bug;
5555 this.availableSortables = availableSortables;
6060 JPanel panel = new JPanel();
6161 panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
6262 panel.setBorder(new EmptyBorder(new Insets(6, 6, 6, 6)));
63
63
6464 addFilterLikeCheckboxes(panel);
6565 addNotFilterOption(panel);
66
66
6767 return panel;
6868 }
69
69
7070 private void addFilterLikeCheckboxes(JPanel center) {
7171 for (Sortables sortable : availableSortables) {
7272 if (!FilterFactory.canFilter(sortable)) { continue; }
73
73
7474 JCheckBox checkBox = new JCheckBox(sortable.toString() + " is " + sortable.formatValue(sortable.getFrom(bug)));
75
75
7676 map.put(checkBox, sortable);
7777 center.add(checkBox);
7878 }
9797 Matcher matcher = null;
9898 if (!set.isEmpty()) {
9999 matcher = FilterFactory.makeMatcher(set, bug);
100
100
101101 if(notFilterCheck.isSelected()) {
102102 matcher = FilterFactory.invertMatcher(matcher);
103103 }
104104 }
105
105
106106 return matcher;
107107 }
108
108
109109 }
6060 }
6161
6262 public FilterMatcher(Sortables filterBy, String value, FilterWhere mode) // 0
63 // =
64 // exactly;
65 // 1
66 // =
67 // at
68 // or
69 // after;
70 // 2
71 // =
72 // at
73 // or
74 // before;
75 // 3
76 // =
77 // not
78 // at
63 // =
64 // exactly;
65 // 1
66 // =
67 // at
68 // or
69 // after;
70 // 2
71 // =
72 // at
73 // or
74 // before;
75 // 3
76 // =
77 // not
78 // at
7979 {
8080 this.filterBy = filterBy;
8181 this.value = value;
9393 public void setActive(boolean active) {
9494 if (active != this.active) {
9595 this.active = active;
96 if (active == true)
96 if (active == true) {
9797 FilterActivity.notifyListeners(FilterListener.Action.FILTERING, null);
98 else
98 } else {
9999 FilterActivity.notifyListeners(FilterListener.Action.UNFILTERING, null);
100 }
100101 }
101102 }
102103
104105 return active;
105106 }
106107
108 @Override
107109 public boolean match(BugInstance bugInstance) {
108 if (!active)
110 if (!active) {
109111 return true;
112 }
110113
111114 SortableStringComparator ssc = filterBy.getComparator();
112115 int compare = ssc.compare(filterBy.getFrom(bugInstance), value);
129132 switch (mode) {
130133 case FILTER_EXACTLY:
131134 return filterBy.toString() + " " + edu.umd.cs.findbugs.L10N.getLocalString("dlg.is", "is") + " "
132 + edu.umd.cs.findbugs.L10N.getLocalString("mode.equal_to", "equal to") + " " + filterBy.formatValue(value);
135 + edu.umd.cs.findbugs.L10N.getLocalString("mode.equal_to", "equal to") + " " + filterBy.formatValue(value);
133136 case FILTER_AT_OR_AFTER:
134137 return filterBy.toString() + " " + edu.umd.cs.findbugs.L10N.getLocalString("dlg.is", "is") + " "
135 + edu.umd.cs.findbugs.L10N.getLocalString("mode.at_or_after", "at or after") + " "
136 + filterBy.formatValue(value);
138 + edu.umd.cs.findbugs.L10N.getLocalString("mode.at_or_after", "at or after") + " "
139 + filterBy.formatValue(value);
137140 case FILTER_AT_OR_BEFORE:
138141 return filterBy.toString() + " " + edu.umd.cs.findbugs.L10N.getLocalString("dlg.is", "is") + " "
139 + edu.umd.cs.findbugs.L10N.getLocalString("mode.at_or_before", "at or before") + " "
140 + filterBy.formatValue(value);
142 + edu.umd.cs.findbugs.L10N.getLocalString("mode.at_or_before", "at or before") + " "
143 + filterBy.formatValue(value);
141144 case FILTER_ALL_BUT:
142145 return filterBy.toString() + " " + edu.umd.cs.findbugs.L10N.getLocalString("dlg.is", "is") + " "
143 + edu.umd.cs.findbugs.L10N.getLocalString("mode.not_equal_to", "not equal to") + " "
144 + filterBy.formatValue(value);
146 + edu.umd.cs.findbugs.L10N.getLocalString("mode.not_equal_to", "not equal to") + " "
147 + filterBy.formatValue(value);
145148 default:
146149 throw new RuntimeException();
147150 }
149152
150153 @Override
151154 public boolean equals(Object o) {
152 if (o == null)
155 if (o == null) {
153156 return false;
154 if (getClass() != o.getClass())
157 }
158 if (getClass() != o.getClass()) {
155159 return false;
160 }
156161
157 if (filterBy.equals(((FilterMatcher) o).filterBy) && value.equals(((FilterMatcher) o).value))
162 if (filterBy.equals(((FilterMatcher) o).filterBy) && value.equals(((FilterMatcher) o).value)) {
158163 return true;
164 }
159165 return false;
160166 }
161167
164170 return value.hashCode() + filterBy.hashCode();
165171 }
166172
173 @Override
167174 public int compareTo(FilterMatcher that) {
168 if (this.filterBy != that.filterBy)
175 if (this.filterBy != that.filterBy) {
169176 return (this.filterBy.ordinal() < that.filterBy.ordinal() ? -1 : 1);
177 }
170178
171179 return this.value.compareTo(that.value);
172180 }
173181
182 @Override
174183 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
175184 throw new UnsupportedOperationException();
176185 }
2727
2828 /**
2929 * Command line switches/options for GUI2.
30 *
30 *
3131 * @author David Hovemeyer
3232 */
3333 public class GUI2CommandLine extends FindBugsCommandLine {
5050
5151 @Override
5252 protected void handleOption(String option, String optionExtraPart) {
53 if (option.equals("-clear")) {
53 if ("-clear".equals(option)) {
5454 GUISaveState.clear();
5555 System.exit(0);
56 } else if (option.equals("-d") || option.equals("--nodock")) {
56 } else if ("-d".equals(option) || "--nodock".equals(option)) {
5757 docking = false;
58 } else if (option.equals("-look")) {
58 } else if ("-look".equals(option)) {
5959 String arg = optionExtraPart;
6060 String theme = null;
6161
62 if (arg.equals("plastic")) {
62 if ("plastic".equals(arg)) {
6363 // You can get the Plastic look and feel from jgoodies.com:
6464 // http://www.jgoodies.com/downloads/libraries.html
6565 // Just put "plastic.jar" in the lib directory, right next
6666 // to the other jar files.
6767 theme = "com.jgoodies.plaf.plastic.PlasticXPLookAndFeel";
68 } else if (arg.equals("gtk")) {
68 } else if ("gtk".equals(arg)) {
6969 theme = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel";
70 } else if (arg.equals("native")) {
70 } else if ("native".equals(arg)) {
7171 theme = UIManager.getSystemLookAndFeelClassName();
7272 } else {
7373 System.err.println("Style '" + arg + "' not supported");
8787
8888 @Override
8989 protected void handleOptionWithArgument(String option, String argument) throws IOException {
90 if (option.equals("-f")) {
90 if ("-f".equals(option)) {
9191 try {
9292 fontSize = Float.parseFloat(argument);
9393 fontSizeSpecified = true;
9494 } catch (NumberFormatException e) {
9595 // ignore
9696 }
97 } else if (option.equals("-priority")) {
97 } else if ("-priority".equals(option)) {
9898 try {
9999 priority = Integer.parseInt(argument);
100100 } catch (NumberFormatException e) {
101101 // ignore
102102 }
103 } else if (option.equals("-loadBugs") || option.equals("-loadbugs")) {
103 } else if ("-loadBugs".equals(option) || "-loadbugs".equals(option)) {
104104 saveFile = new File(argument);
105105 if (!saveFile.exists()) {
106106 System.err.println("Bugs file \"" + argument + "\" could not be found");
6969 private static final int MAXNUMRECENTPROJECTS = 5;
7070
7171 private static final Sortables[] DEFAULT_COLUMN_HEADERS = new Sortables[] { Sortables.CATEGORY, Sortables.BUGCODE,
72 Sortables.TYPE, Sortables.DIVIDER, Sortables.BUG_RANK, Sortables.FIRST_SEEN, Sortables.DESIGNATION };
72 Sortables.TYPE, Sortables.DIVIDER, Sortables.BUG_RANK, Sortables.FIRST_SEEN, Sortables.DESIGNATION };
7373
7474 private static final String[] RECENTPROJECTKEYS = new String[MAXNUMRECENTPROJECTS];
7575
9292
9393
9494 static {
95 for (int x = 0; x < RECENTPROJECTKEYS.length; x++)
95 for (int x = 0; x < RECENTPROJECTKEYS.length; x++) {
9696 RECENTPROJECTKEYS[x] = "Project" + x;
97 for (int x = 0; x < COMMENTKEYS.length; x++)
97 }
98 for (int x = 0; x < COMMENTKEYS.length; x++) {
9899 COMMENTKEYS[x] = "Comment" + x;
100 }
99101 }
100102
101103 private int splitMain;
134136
135137 private List<String> enabledPlugins = new ArrayList<String>();
136138 private List<String> disabledPlugins = new ArrayList<String>();
137 private LinkedHashSet<URI> customPlugins = new LinkedHashSet<URI>();
139 private final LinkedHashSet<URI> customPlugins = new LinkedHashSet<URI>();
138140
139141 private static String[] generateSorterKeys(int numSorters) {
140142 String[] result = new String[numSorters];
145147 }
146148
147149 public static synchronized GUISaveState getInstance() {
148 if (instance == null)
150 if (instance == null) {
149151 instance = new GUISaveState();
152 }
150153 return instance;
151154 }
152155
182185 Sortables s = Sortables.getSortableByPrettyName(p.get(sortKeys[x], "*none*"));
183186
184187 if (s == null) {
185 if (MainFrame.GUI2_DEBUG)
188 if (MainFrame.GUI2_DEBUG) {
186189 System.err.println("Sort order was corrupted, using default sort order");
190 }
187191 newInstance.useDefault = true;
188192 break;
189193 }
196200 sortColumns.addAll(missingSortColumns);
197201 newInstance.sortColumns = sortColumns.toArray(new Sortables[sortColumns.size()]);
198202 }
199 } else
203 } else {
200204 newInstance.useDefault = true;
205 }
201206
202207 newInstance.dockingLayout = p.getByteArray(DOCKINGLAYOUT, new byte[0]);
203208
205210 Rectangle r = new Rectangle(0, 0, 800, 650);
206211 if (boundsString != null) {
207212 String[] a = boundsString.split(",", 4);
208 if (a.length > 0)
213 if (a.length > 0) {
209214 try {
210215 r.x = Math.max(0, Integer.parseInt(a[0]));
211216 } catch (NumberFormatException nfe) {
212217 assert true;
213218 }
214 if (a.length > 1)
219 }
220 if (a.length > 1) {
215221 try {
216222 r.y = Math.max(0, Integer.parseInt(a[1]));
217223 } catch (NumberFormatException nfe) {
218224 assert true;
219225 }
220 if (a.length > 2)
226 }
227 if (a.length > 2) {
221228 try {
222229 r.width = Math.max(40, Integer.parseInt(a[2]));
223230 } catch (NumberFormatException nfe) {
224231 assert true;
225232 }
226 if (a.length > 3)
233 }
234 if (a.length > 3) {
227235 try {
228236 r.height = Math.max(40, Integer.parseInt(a[3]));
229237 } catch (NumberFormatException nfe) {
230238 assert true;
231239 }
240 }
232241 }
233242 newInstance.frameBounds = r;
234243 newInstance.extendedWindowState = p.getInt(EXTENDED_WINDOW_STATE, Frame.NORMAL);
240249 newInstance.packagePrefixSegments = p.getInt(PACKAGE_PREFIX_SEGEMENTS, 3);
241250
242251 String plugins = p.get(CUSTOM_PLUGINS, "");
243 if (plugins.length() > 0)
252 if (plugins.length() > 0) {
244253 for (String s : plugins.split(" ")) {
245 try {
246 URI u = new URI(s);
247 Plugin.addCustomPlugin(u);
248 newInstance.customPlugins.add(u);
249 } catch (PluginException e) {
250 assert true;
251 } catch (URISyntaxException e) {
252 assert true;
254 try {
255 URI u = new URI(s);
256 Plugin.addCustomPlugin(u);
257 newInstance.customPlugins.add(u);
258 } catch (PluginException e) {
259 assert true;
260 } catch (URISyntaxException e) {
261 assert true;
262 }
253263 }
254264 }
255265
319329 * @param f
320330 */
321331 public void addRecentFile(File f) {
322 if (null != f)
332 if (null != f) {
323333 recentFiles.add(f);
334 }
324335 }
325336
326337 /**
340351 public void fileNotFound(File f) {
341352 if (!recentFiles.contains(f)) {
342353 throw new IllegalStateException("Well no wonder it wasn't found, its not in the list.");
343 } else
354 } else {
344355 recentFiles.remove(f);
356 }
345357
346358 }
347359
549561 try {
550562 return customPlugins.add(u.toURI());
551563 } catch (URISyntaxException e) {
552 throw new IllegalArgumentException("Error converting to uri: " + u, e);
564 throw new IllegalArgumentException("Error converting to uri: " + u, e);
553565 }
554566 }
555567 public List<String> getDisabledPlugins() {
565577 }
566578
567579 SorterTableColumnModel getStarterTable() {
568 if (starterTable != null)
580 if (starterTable != null) {
569581 return starterTable;
570
571 if (useDefault || sortColumns == null)
582 }
583
584 if (useDefault || sortColumns == null) {
572585 starterTable = new SorterTableColumnModel(GUISaveState.DEFAULT_COLUMN_HEADERS);
573 else
586 } else {
574587 starterTable = new SorterTableColumnModel(sortColumns);
588 }
575589
576590 return starterTable;
577591 }
176176 private final PluginUpdateDialog pluginUpdateDialog = new PluginUpdateDialog();
177177
178178 public static void makeInstance(FindBugsLayoutManagerFactory factory) {
179 if (instance != null)
179 if (instance != null) {
180180 throw new IllegalStateException();
181 }
181182 instance = new MainFrame(factory);
182183 instance.initializeGUI();
183184 }
184185
185186 public static MainFrame getInstance() {
186 if (instance == null)
187 if (instance == null) {
187188 throw new IllegalStateException();
189 }
188190 return instance;
189191 }
190192
214216 System.err.println("acquiring display wait, count " + waitCount);
215217 Thread.dumpStack();
216218 }
217 if (waitCount == 1)
219 if (waitCount == 1) {
218220 mainFrameTree.showCard(BugCard.WAITCARD, new Cursor(Cursor.WAIT_CURSOR), this);
221 }
219222 }
220223 }
221224
224227 public void releaseDisplayWait() {
225228 synchronized (waitLock) {
226229 if (waitCount <= 0) {
227 if (previousDecrementToZero != null)
230 if (previousDecrementToZero != null) {
228231 throw new IllegalStateException("Can't decrease wait count; already zero", previousDecrementToZero);
229 else
232 } else {
230233 throw new IllegalStateException("Can't decrease wait count; never incremented");
234 }
231235 }
232236 waitCount--;
233237 if (GUI2_DEBUG) {
270274 * b.
271275 */
272276 public void setProjectChanged(boolean b) {
273 if (curProject == null)
277 if (curProject == null) {
274278 return;
275
276 if (projectChanged == b)
279 }
280
281 if (projectChanged == b) {
277282 return;
283 }
278284
279285 projectChanged = b;
280286 mainFrameMenu.setSaveMenu(this);
286292 /**
287293 * Show an error dialog.
288294 */
295 @Override
289296 public void error(String message) {
290297 JOptionPane.showMessageDialog(this, message, "Error", JOptionPane.ERROR_MESSAGE);
291298 }
293300 /**
294301 * Write a message to stdout.
295302 */
303 @Override
296304 public void writeToLog(String message) {
297 if (GUI2_DEBUG)
305 if (GUI2_DEBUG) {
298306 System.out.println(message);
307 }
299308 }
300309
301310 public int showConfirmDialog(String message, String title, int optionType) {
341350 + edu.umd.cs.findbugs.L10N.getLocalString("statusbar.bugs_hidden", "bugs hidden (see view menu)");
342351 }
343352 msg = updateCloudSigninStatus(msg);
344 if (errorMsg != null && errorMsg.length() > 0)
353 if (errorMsg != null && errorMsg.length() > 0) {
345354 msg = join(msg, errorMsg);
355 }
346356
347357 mainFrameTree.setWaitStatusLabelText(msg); // should not be the URL
348 if (msg.length() == 0)
358 if (msg.length() == 0) {
349359 msg = "http://findbugs.sourceforge.net";
360 }
350361 statusBarLabel.setText(msg);
351362 }
352363
353364 private String updateCloudSigninStatus(String msg) {
354365 if (getBugCollection() != null) {
355366 Cloud cloud = getBugCollection().getCloud();
356 if (cloud != null) {
357 String pluginMsg = cloud.getStatusMsg();
358 if (pluginMsg != null && pluginMsg.length() > 1)
359 msg = join(msg, pluginMsg);
367 String pluginMsg = cloud.getStatusMsg();
368 if (pluginMsg != null && pluginMsg.length() > 1) {
369 msg = join(msg, pluginMsg);
360370 }
361371 }
362372 return msg;
367377 * the exit menuItem or by clicking on the window's system menu.
368378 */
369379 void callOnClose() {
370 if (!canNavigateAway())
380 if (!canNavigateAway()) {
371381 return;
382 }
372383
373384 if (projectChanged && !SystemProperties.getBoolean("findbugs.skipSaveChangesWarning")) {
374385 Object[] options = {
380391 edu.umd.cs.findbugs.L10N.getLocalString("msg.confirm_save_txt", "Do you want to save?"),
381392 JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
382393
383 if (value == 2 || value == JOptionPane.CLOSED_OPTION)
394 if (value == 2 || value == JOptionPane.CLOSED_OPTION) {
384395 return;
385 else if (value == 0) {
396 } else if (value == 0) {
386397
387398 if (saveFile == null) {
388 if (!mainFrameLoadSaveHelper.saveAs())
399 if (!mainFrameLoadSaveHelper.saveAs()) {
389400 return;
390 } else
401 }
402 } else {
391403 mainFrameLoadSaveHelper.save();
404 }
392405 }
393406 }
394407
399412 guiSaveState.save();
400413 if (this.bugCollection != null) {
401414 Cloud cloud = this.bugCollection.getCloud();
402 if (cloud != null)
403 cloud.shutdown();
415 cloud.shutdown();
404416 }
405417 System.exit(0);
406418 }
462474 @SwingThread
463475 private void setProjectAndBugCollection(@CheckForNull Project project, @CheckForNull BugCollection bugCollection) {
464476 if (GUI2_DEBUG) {
465 if (bugCollection == null)
477 if (bugCollection == null) {
466478 System.out.println("Setting bug collection to null");
467 else
479 } else {
468480 System.out.println("Setting bug collection; contains " + bugCollection.getCollection().size() + " bugs");
481 }
469482
470483 }
471484 if (bugCollection != null && bugCollection.getProject() != project) {
478491
479492 if (this.bugCollection != bugCollection && this.bugCollection != null) {
480493 Cloud plugin = this.bugCollection.getCloud();
481 if (plugin != null) {
482 plugin.removeListener(userAnnotationListener);
483 plugin.removeStatusListener(cloudStatusListener);
484 plugin.shutdown();
485 }
494 plugin.removeListener(userAnnotationListener);
495 plugin.removeStatusListener(cloudStatusListener);
496 plugin.shutdown();
486497 }
487498 // setRebuilding(false);
488499 setProject(project);
493504 displayer.clearCache();
494505 if (bugCollection != null) {
495506 Cloud plugin = bugCollection.getCloud();
496 if (plugin != null) {
497 plugin.addListener(userAnnotationListener);
498 plugin.addStatusListener(cloudStatusListener);
499 }
507 plugin.addListener(userAnnotationListener);
508 plugin.addStatusListener(cloudStatusListener);
500509 }
501510 mainFrameTree.updateBugTree();
502511 setProjectChanged(false);
503512 Runnable runnable = new Runnable() {
513 @Override
504514 public void run() {
505515 PreferencesFrame.getInstance().updateFilterPanel();
506516 mainFrameMenu.getReconfigMenuItem().setEnabled(true);
518528 updateTitle();
519529 }
520530 };
521 if (SwingUtilities.isEventDispatchThread())
531 if (SwingUtilities.isEventDispatchThread()) {
522532 runnable.run();
523 else
533 } else {
524534 SwingUtilities.invokeLater(runnable);
535 }
525536 } finally {
526537 releaseDisplayWait();
527538 }
544555 setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
545556 }
546557
547 @SuppressWarnings({ "SimplifiableIfStatement" })
558 // @SuppressWarnings({ "SimplifiableIfStatement" })
548559 boolean shouldDisplayIssue(BugInstance b) {
549560 Project project = getProject();
550561 Filter suppressionFilter = project.getSuppressionFilter();
551 if (null == getBugCollection() || suppressionFilter.match(b))
562 if (null == getBugCollection() || suppressionFilter.match(b)) {
552563 return false;
564 }
553565 return viewFilter.show(b);
554566 }
555567
556568 // ============================= menu actions
557569 // ===============================
558570
571 @SuppressWarnings("unused")
559572 public void createNewProjectFromMenuItem() {
560 if (!canNavigateAway())
573 if (!canNavigateAway()) {
561574 return;
575 }
562576 new NewProjectWizard();
563577
564578 newProject = true;
588602 }
589603
590604 void preferences() {
591 if (!canNavigateAway())
605 if (!canNavigateAway()) {
592606 return;
607 }
593608 PreferencesFrame.getInstance().setLocationRelativeTo(this);
594609 PreferencesFrame.getInstance().setVisible(true);
595610 }
607622 }
608623
609624 void redoAnalysis() {
610 if (!canNavigateAway())
625 if (!canNavigateAway()) {
611626 return;
627 }
612628
613629 /// QQQ-TODO: new RuntimeException("Redo analysis called").printStackTrace();
614630 acquireDisplayWait();
615631 edu.umd.cs.findbugs.util.Util.runInDameonThread(
616 new Runnable() {
617 public void run() {
618 try {
619 updateDesignationDisplay();
620 Project project = getProject();
621 BugCollection bc = BugLoader.redoAnalysisKeepComments(project);
622 updateProjectAndBugCollection(bc);
623 setProjectAndBugCollectionInSwingThread(project, bc);
624 } finally {
625 releaseDisplayWait();
626 }
627 }
628 });
632 new Runnable() {
633 @Override
634 public void run() {
635 try {
636 updateDesignationDisplay();
637 Project project = getProject();
638 BugCollection bc = BugLoader.redoAnalysisKeepComments(project);
639 updateProjectAndBugCollection(bc);
640 setProjectAndBugCollectionInSwingThread(project, bc);
641 } finally {
642 releaseDisplayWait();
643 }
644 }
645 });
629646 }
630647
631648 // ================================== misc junk 2
653670
654671 void clearSourcePane() {
655672 SwingUtilities.invokeLater(new Runnable() {
673 @Override
656674 public void run() {
657675 mainFrameComponentFactory.setSourceTab("", null);
658676 sourceCodeTextPane.setDocument(SourceCodeDisplay.SOURCE_NOT_RELEVANT);
705723
706724 private String getActionWithoutSavingMsg(String action) {
707725 String msg = edu.umd.cs.findbugs.L10N.getLocalString("msg.you_are_" + action + "_without_saving_txt", null);
708 if (msg != null)
726 if (msg != null) {
709727 return msg;
728 }
710729 return edu.umd.cs.findbugs.L10N.getLocalString("msg.you_are_" + action + "_txt", "You are " + action) + " "
711 + edu.umd.cs.findbugs.L10N.getLocalString("msg.without_saving_txt", "without saving. Do you want to save?");
730 + edu.umd.cs.findbugs.L10N.getLocalString("msg.without_saving_txt", "without saving. Do you want to save?");
712731 }
713732
714733 public void updateBugTree() {
726745 public void updateTitle() {
727746 Project project = getProject();
728747 String name = project.getProjectName();
729 if ((name == null || name.trim().equals("")) && saveFile != null)
748 if ((name == null || "".equals(name.trim())) && saveFile != null) {
730749 name = saveFile.getAbsolutePath();
731 if (name == null)
750 }
751 if (name == null) {
732752 name = "";//Project.UNNAMED_PROJECT;
753 }
733754 String oldTitle = this.getTitle();
734 String newTitle = TITLE_START_TXT + (name.trim().equals("") ? "" : " - " + name);
735 if (oldTitle.equals(newTitle))
755 String newTitle = TITLE_START_TXT + ("".equals(name.trim()) ? "" : " - " + name);
756 if (oldTitle.equals(newTitle)) {
736757 return;
758 }
737759 this.setTitle(newTitle);
738760 }
739761
740 @SuppressWarnings({ "SimplifiableIfStatement" })
762 // @SuppressWarnings({ "SimplifiableIfStatement" })
741763 private boolean shouldDisplayIssueIgnoringPackagePrefixes(BugInstance b) {
742764 Project project = getProject();
743765 Filter suppressionFilter = project.getSuppressionFilter();
744 if (null == getBugCollection() || suppressionFilter.match(b))
766 if (null == getBugCollection() || suppressionFilter.match(b)) {
745767 return false;
768 }
746769 return viewFilter.showIgnoringPackagePrefixes(b);
747770 }
748771
750773 TreeSet<String> projects = new TreeSet<String>(String.CASE_INSENSITIVE_ORDER);
751774 Multiset<String> count = new Multiset<String>();
752775 int total = 0;
753 for (BugInstance b : getBugCollection().getCollection())
776 for (BugInstance b : getBugCollection().getCollection()) {
754777 if (shouldDisplayIssueIgnoringPackagePrefixes(b)) {
755778 TreeSet<String> projectsForThisBug = projectPackagePrefixes.getProjects(b.getPrimaryClass().getClassName());
756779 projects.addAll(projectsForThisBug);
757780 count.addAll(projectsForThisBug);
758781 total++;
759782 }
783 }
760784 if (projects.size() == 0) {
761785 JOptionPane.showMessageDialog(this, "No issues in current view");
762786 return;
771795 ProjectSelector choice = (ProjectSelector) JOptionPane.showInputDialog(null,
772796 "Choose a project to set appropriate package prefix(es)", "Select package prefixes by package",
773797 JOptionPane.QUESTION_MESSAGE, null, selectors.toArray(), everything);
774 if (choice == null)
798 if (choice == null) {
775799 return;
800 }
776801
777802 mainFrameTree.setFieldForPackagesToDisplayText(choice.filter);
778803 viewFilter.setPackagesToDisplay(choice.filter);
781806 }
782807
783808 private static String join(String s1, String s2) {
784 if (s1 == null || s1.length() == 0)
809 if (s1 == null || s1.length() == 0) {
785810 return s2;
786 if (s2 == null || s2.length() == 0)
811 }
812 if (s2 == null || s2.length() == 0) {
787813 return s1;
814 }
788815 return s1 + "; " + s2;
789816 }
790817
792819 final BugInstance bug = node.getBug();
793820
794821 SwingUtilities.invokeLater(new Runnable() {
822 @Override
795823 public void run() {
796824 summaryTopPanel.removeAll();
797825
798826 summaryTopPanel.add(mainFrameComponentFactory.bugSummaryComponent(bug.getAbridgedMessage(), bug));
799827
800 for (BugAnnotation b : bug.getAnnotationsForMessage(true))
828 for (BugAnnotation b : bug.getAnnotationsForMessage(true)) {
801829 summaryTopPanel.add(mainFrameComponentFactory.bugSummaryComponent(b, bug));
830 }
802831
803832
804833 BugPattern bugPattern = bug.getBugPattern();
805834 String detailText =
806835 bugPattern.getDetailText()
807836 +"<br><p> <b>Bug kind and pattern: " +
808 bugPattern.getAbbrev() + " - " + bugPattern.getType();
837 bugPattern.getAbbrev() + " - " + bugPattern.getType();
809838 String txt = bugPattern.getDetailHTML(detailText);
810839 summaryHtmlArea.setText(txt);
811840
813842 summaryTopPanel.revalidate();
814843
815844 SwingUtilities.invokeLater(new Runnable() {
845 @Override
816846 public void run() {
817847 summaryHtmlScrollPane.getVerticalScrollBar().setValue(
818848 summaryHtmlScrollPane.getVerticalScrollBar().getMinimum());
841871 case 2:
842872 targetLineNum = displayer.findPrevious(targetString);
843873 break;
844 }
845 if (targetLineNum != -1)
874 default:
875 break;
876 }
877 if (targetLineNum != -1) {
846878 displayer.foundItem(targetLineNum);
879 }
847880 }
848881
849882 public boolean canNavigateAway() {
866899 }
867900
868901 public void setSaveType(SaveType saveType) {
869 if (GUI2_DEBUG && this.saveType != saveType)
902 if (GUI2_DEBUG && this.saveType != saveType) {
870903 System.out.println("Changing save type from " + this.saveType + " to " + saveType);
904 }
871905 this.saveType = saveType;
872906 }
873907
877911
878912 private Iterable<BugInstance> getDisplayedBugs() {
879913 return new Iterable<BugInstance>() {
914 @Override
880915 public Iterator<BugInstance> iterator() {
881916 return new ShownBugsIterator();
882917 }
10221057 JMenuItem toggleItem = new JMenuItem(text);
10231058
10241059 toggleItem.addActionListener(new ActionListener() {
1060 @Override
10251061 public void actionPerformed(ActionEvent arg0) {
10261062 Cloud cloud = getBugCollection().getCloud();
10271063 if (cloud instanceof DoNothingCloud) {
10281064 JOptionPane.showMessageDialog(MainFrame.this, "No cloud selected; enable and select optional Bug Collection XML Pseudo-Cloud plugin to store designations in XML");
1029 } else if (comments.canSetDesignations())
1065 } else if (comments.canSetDesignations()) {
10301066 comments.setDesignation(key);
1031 else {
1067 } else {
10321068 JOptionPane.showMessageDialog(MainFrame.this, "The currently selected cloud cannot store these designations");
10331069 }
10341070 }
10671103
10681104 BugInstance next;
10691105
1106 @Override
10701107 public boolean hasNext() {
10711108 if (!nextKnown) {
10721109 nextKnown = true;
10731110 while (base.hasNext()) {
10741111 next = base.next();
1075 if (shouldDisplayIssue(next))
1112 if (shouldDisplayIssue(next)) {
10761113 return true;
1114 }
10771115 }
10781116 next = null;
10791117 return false;
10811119 return next != null;
10821120 }
10831121
1122 @Override
10841123 public BugInstance next() {
1085 if (!hasNext())
1124 if (!hasNext()) {
10861125 throw new NoSuchElementException();
1126 }
10871127 BugInstance result = next;
10881128 next = null;
10891129 nextKnown = false;
10901130 return result;
10911131 }
10921132
1133 @Override
10931134 public void remove() {
10941135 throw new UnsupportedOperationException();
10951136 }
11001141 super(MainFrame.this);
11011142 }
11021143
1144 @Override
11031145 public void registerCloud(Project project, BugCollection collection, Cloud plugin) {
1104 // assert collection.getCloud() == plugin
1105 // : collection.getCloud().getCloudName() + " vs " + plugin.getCloudName();
1146 // assert collection.getCloud() == plugin
1147 // : collection.getCloud().getCloudName() + " vs " + plugin.getCloudName();
11061148 if (MainFrame.this.bugCollection == collection) {
11071149 plugin.addListener(userAnnotationListener);
11081150 plugin.addStatusListener(cloudStatusListener);
11091151 }
11101152 }
11111153
1154 @Override
11121155 public void unregisterCloud(Project project, BugCollection collection, Cloud plugin) {
11131156 assert collection.getCloud() == plugin;
11141157 if (MainFrame.this.bugCollection == collection) {
11171160 }
11181161 }
11191162
1163 @Override
11201164 public void setErrorMessage(String errorMsg) {
11211165 MainFrame.this.errorMsg = errorMsg;
11221166 SwingUtilities.invokeLater(new Runnable() {
1167 @Override
11231168 public void run() {
11241169 updateStatusBar();
11251170 }
11281173 }
11291174
11301175 private class MyCloudListener implements CloudListener {
1176 @Override
11311177 public void issueUpdated(BugInstance bug) {
1132 if (mainFrameTree.getCurrentSelectedBugLeaf() != null && mainFrameTree.getCurrentSelectedBugLeaf().getBug() == bug)
1178 if (mainFrameTree.getCurrentSelectedBugLeaf() != null && mainFrameTree.getCurrentSelectedBugLeaf().getBug() == bug) {
11331179 comments.updateCommentsFromLeafInformation(mainFrameTree.getCurrentSelectedBugLeaf());
1134 }
1135
1180 }
1181 }
1182
1183 @Override
11361184 public void statusUpdated() {
11371185 SwingUtilities.invokeLater(updateStatusBarRunner);
11381186 }
11391187
1188 @Override
11401189 public void taskStarted(Cloud.CloudTask task) {
11411190 }
11421191 }
11431192
11441193 private class MyCloudStatusListener implements Cloud.CloudStatusListener {
1194 @Override
11451195 public void handleIssueDataDownloadedEvent() {
11461196 mainFrameTree.rebuildBugTreeIfSortablesDependOnCloud();
11471197 }
11481198
1199 @Override
11491200 public void handleStateChange(SigninState oldState, SigninState state) {
11501201 Cloud cloud = MainFrame.this.bugCollection.getCloudLazily();
1151 if (cloud != null && cloud.isInitialized())
1152 mainFrameTree.rebuildBugTreeIfSortablesDependOnCloud();
1202 if (cloud != null && cloud.isInitialized()) {
1203 mainFrameTree.rebuildBugTreeIfSortablesDependOnCloud();
1204 }
11531205 }
11541206 }
11551207
11561208 private class StatusBarUpdater implements Runnable {
1209 @Override
11571210 public void run() {
11581211 updateStatusBar();
11591212 }
107107 mainFrame.setSummaryTopPanel(new JPanel());
108108 mainFrame.getSummaryTopPanel().setLayout(new GridLayout(0, 1));
109109 mainFrame.getSummaryTopPanel().setBorder(BorderFactory.createEmptyBorder(2, 4, 2, 4));
110 // mainFrame.getSummaryTopPanel().setMinimumSize(new Dimension(fontSize * 50, fontSize * 5));
110 // mainFrame.getSummaryTopPanel().setMinimumSize(new Dimension(fontSize * 50, fontSize * 5));
111111
112112 JPanel summaryTopOuter = new JPanel(new BorderLayout());
113113 summaryTopOuter.add(mainFrame.getSummaryTopPanel(), BorderLayout.NORTH);
115115 mainFrame.getSummaryHtmlArea().setContentType("text/html");
116116 mainFrame.getSummaryHtmlArea().setEditable(false);
117117 mainFrame.getSummaryHtmlArea().addHyperlinkListener(new HyperlinkListener() {
118 @Override
118119 public void hyperlinkUpdate(HyperlinkEvent evt) {
119120 AboutDialog.editorPaneHyperlinkUpdate(evt);
120121 }
178179 panel.add(sourceCodeScrollPane, BorderLayout.CENTER);
179180
180181 panel.revalidate();
181 if (MainFrame.GUI2_DEBUG)
182 if (MainFrame.GUI2_DEBUG) {
182183 System.out.println("Created source code panel");
184 }
183185 return panel;
184186 }
185187
200202 thePanel.add(mainFrame.getSourceSearchTextField());
201203 // add the buttons
202204 mainFrame.getFindButton().addActionListener(new ActionListener() {
205 @Override
203206 public void actionPerformed(ActionEvent evt) {
204207 mainFrame.searchSource(0);
205208 }
210213 gridbag.setConstraints(mainFrame.getFindButton(), c);
211214 thePanel.add(mainFrame.getFindButton());
212215 mainFrame.getFindNextButton().addActionListener(new ActionListener() {
216 @Override
213217 public void actionPerformed(ActionEvent evt) {
214218 mainFrame.searchSource(1);
215219 }
220224 gridbag.setConstraints(mainFrame.getFindNextButton(), c);
221225 thePanel.add(mainFrame.getFindNextButton());
222226 mainFrame.getFindPreviousButton().addActionListener(new ActionListener() {
227 @Override
223228 public void actionPerformed(ActionEvent evt) {
224229 mainFrame.searchSource(2);
225230 }
242247 URL u = null;
243248 if (bug != null) {
244249 Cloud plugin = mainFrame.getBugCollection().getCloud();
245 if (plugin.supportsSourceLinks())
250 if (plugin.supportsSourceLinks()) {
246251 u = plugin.getSourceLink(bug);
247 }
248 if (u != null)
252 }
253 }
254 if (u != null) {
249255 addLink(label, u);
250 else
256 } else {
251257 removeLink(label);
258 }
252259
253260 }
254261 mainFrame.getGuiLayout().setSourceTitle(title);
263270 @Override
264271 public void mouseClicked(MouseEvent e) {
265272 URL u = sourceLink;
266 if (u != null)
273 if (u != null) {
267274 LaunchBrowser.showDocument(u);
275 }
268276
269277 }
270278 });
271279 }
272280 component.setCursor(new Cursor(Cursor.HAND_CURSOR));
273281 Cloud plugin = mainFrame.getBugCollection().getCloud();
274 if (plugin != null)
282 if (plugin != null) {
275283 component.setToolTipText(plugin.getSourceLinkToolTip(null));
284 }
276285
277286 }
278287
304313 String srcStr = "";
305314 int start = link.getStartLine();
306315 int end = link.getEndLine();
307 if (start < 0 && end < 0)
316 if (start < 0 && end < 0) {
308317 srcStr = sourceCodeLabel;
309 else if (start == end)
318 } else if (start == end) {
310319 srcStr = " [" + summaryLine + " " + start + "]";
311 else if (start < end)
320 } else if (start < end) {
312321 srcStr = " [" + summaryLines + " " + start + " - " + end + "]";
322 }
313323
314324 label.setToolTipText(clickToGoToText + " " + srcStr);
315325
324334 if (link != null && sourceCodeExists(link)) {
325335 int start = link.getStartLine();
326336 int end = link.getEndLine();
327 if (start < 0 && end < 0)
337 if (start < 0 && end < 0) {
328338 srcStr = sourceCodeLabel;
329 else if (start == end)
339 } else if (start == end) {
330340 srcStr = " [" + summaryLine + " " + start + "]";
331 else if (start < end)
341 } else if (start < end) {
332342 srcStr = " [" + summaryLines + " " + start + " - " + end + "]";
333
334 if (!srcStr.equals("")) {
343 }
344
345 if (!"".equals(srcStr)) {
335346 label.setToolTipText(clickToGoToText + " " + srcStr);
336347 label.addMouseListener(new BugSummaryMouseListener(bug, label, link));
337348 }
338349 }
339350 String noteText;
340 if (note == bug.getPrimaryMethod() || note == bug.getPrimaryField())
351 if (note == bug.getPrimaryMethod() || note == bug.getPrimaryField()) {
341352 noteText = note.toString();
342 else
353 } else {
343354 noteText = note.toString(primaryClass);
344 if (!srcStr.equals(sourceCodeLabel))
355 }
356 if (!srcStr.equals(sourceCodeLabel)) {
345357 label.setText(noteText + srcStr);
346 else
358 } else {
347359 label.setText(noteText);
360 }
348361 } else {
349362 label.setText(value.toString(primaryClass));
350363 }
358371 * return a JLabel with the annotation's toString(). If that annotation is a
359372 * SourceLineAnnotation or has a SourceLineAnnotation connected to it and
360373 * the source file is available will attach a listener to the label.
361 *
362 * @return
363374 */
364375 public Component bugSummaryComponent(String str, BugInstance bug) {
365376 JLabel label = new JLabel();
370381 label.setText(str);
371382
372383 SourceLineAnnotation link = bug.getPrimarySourceLineAnnotation();
373 if (link != null)
384 if (link != null) {
374385 label.addMouseListener(new BugSummaryMouseListener(bug, label, link));
386 }
375387
376388 return label;
377389 }
388400 }
389401
390402 private static class InitializeGUI implements Runnable {
391 private MainFrame mainFrame;
403 private final MainFrame mainFrame;
392404
393405 public InitializeGUI(final MainFrame mainFrame) {
394406 this.mainFrame = mainFrame;
395407 }
396408
409 @Override
397410 public void run() {
398411 mainFrame.setTitle("FindBugs");
399412 // noinspection ConstantConditions
413426 // (as in sourceforge bug 1899648). In an attempt to recover
414427 // gracefully, this code reverts to the cross-platform look-
415428 // and-feel and attempts again to initialize the layout.
416 if (!UIManager.getLookAndFeel().getName().equals("Metal")) {
429 if (!"Metal".equals(UIManager.getLookAndFeel().getName())) {
417430 System.err.println("Exception caught initializing GUI; reverting to CrossPlatformLookAndFeel");
418431 try {
419432 UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
430443 mainFrame.mainFrameTree.setBranchPopupMenu(mainFrame.mainFrameTree.createBranchPopUpMenu());
431444 mainFrame.updateStatusBar();
432445 Rectangle bounds = GUISaveState.getInstance().getFrameBounds();
433 if (bounds != null)
446 if (bounds != null) {
434447 mainFrame.setBounds(bounds);
448 }
435449
436450 mainFrame.setExtendedState(GUISaveState.getInstance().getExtendedWindowState());
437451 Toolkit.getDefaultToolkit().setDynamicLayout(true);
481495 // a system without the EAWT
482496 // because OSXAdapter extends ApplicationAdapter in its def
483497 System.err
484 .println("This version of Mac OS X does not support the Apple EAWT. Application Menu handling has been disabled ("
485 + e + ")");
498 .println("This version of Mac OS X does not support the Apple EAWT. Application Menu handling has been disabled ("
499 + e + ")");
486500 } catch (ClassNotFoundException e) {
487501 // This shouldn't be reached; if there's a problem with the
488502 // OSXAdapter we should get the
489503 // above NoClassDefFoundError first.
490504 System.err
491 .println("This version of Mac OS X does not support the Apple EAWT. Application Menu handling has been disabled ("
492 + e + ")");
505 .println("This version of Mac OS X does not support the Apple EAWT. Application Menu handling has been disabled ("
506 + e + ")");
493507 } catch (Exception e) {
494508 System.err.println("Exception while loading the OSXAdapter: " + e);
495509 e.printStackTrace();
6161
6262 int value = filterOpenFileChooser.showOpenDialog(mainFrame);
6363
64 if (value != JFileChooser.APPROVE_OPTION)
64 if (value != JFileChooser.APPROVE_OPTION) {
6565 return;
66 }
6667
6768 f = filterOpenFileChooser.getSelectedFile();
6869
8182 }
8283 mainFrame.setProjectChanged(true);
8384 Filter suppressionFilter = mainFrame.getProject().getSuppressionFilter();
84
85
8586 for (Matcher m : filter.getChildren()) {
8687 suppressionFilter.addChild(m);
8788 }
88
89
8990 PreferencesFrame.getInstance().updateFilterPanel();
9091 }
9192
9596 * This method is for when the user wants to open a file.
9697 */
9798 void open() {
98 if (!mainFrame.canNavigateAway())
99 if (!mainFrame.canNavigateAway()) {
99100 return;
100
101 if (askToSave()) return;
101 }
102
103 if (askToSave()) {
104 return;
105 }
102106
103107 boolean loading = true;
104108 SaveType fileType;
105109 tryAgain: while (loading) {
106110 int value = saveOpenFileChooser.showOpenDialog(mainFrame);
107 if (value != JFileChooser.APPROVE_OPTION)
111 if (value != JFileChooser.APPROVE_OPTION) {
108112 return;
113 }
109114
110115 loading = false;
111116 fileType = convertFilterToType(saveOpenFileChooser.getFileFilter());
161166 if (mainFrame.isProjectChanged()) {
162167 int response = JOptionPane.showConfirmDialog(mainFrame, L10N.getLocalString("dlg.save_current_changes",
163168 "The current project has been changed, Save current changes?"), L10N.getLocalString("dlg.save_changes",
164 "Save Changes?"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
169 "Save Changes?"), JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
165170
166171 if (response == JOptionPane.YES_OPTION) {
167 if (mainFrame.getSaveFile() != null)
172 if (mainFrame.getSaveFile() != null) {
168173 save();
169 else
174 } else {
170175 saveAs();
176 }
171177 } else if (response == JOptionPane.CANCEL_OPTION)
178 {
172179 return true;
173 // IF no, do nothing.
180 // IF no, do nothing.
181 }
174182 }
175183 return false;
176184 }
202210
203211 int value = filterOpenFileChooser.showSaveDialog(mainFrame);
204212
205 if (value != JFileChooser.APPROVE_OPTION)
213 if (value != JFileChooser.APPROVE_OPTION) {
206214 return false;
215 }
207216
208217 f = filterOpenFileChooser.getSelectedFile();
209218
214223 L10N.getLocalString("dlg.warning_ttl", "Warning!"), JOptionPane.OK_CANCEL_OPTION,
215224 JOptionPane.WARNING_MESSAGE);
216225
217 if (response == JOptionPane.OK_OPTION)
226 if (response == JOptionPane.OK_OPTION) {
218227 retry = false;
228 }
219229 if (response == JOptionPane.CANCEL_OPTION) {
220230 retry = true;
221231 continue;
237247 }
238248
239249 boolean saveAs() {
240 if (!mainFrame.canNavigateAway())
250 if (!mainFrame.canNavigateAway()) {
241251 return false;
252 }
242253
243254 saveOpenFileChooser.setDialogTitle(L10N.getLocalString("dlg.saveas_ttl", "Save as..."));
244255
256267
257268 int value = saveOpenFileChooser.showSaveDialog(mainFrame);
258269
259 if (value != JFileChooser.APPROVE_OPTION)
270 if (value != JFileChooser.APPROVE_OPTION) {
260271 return false;
272 }
261273
262274 fileType = convertFilterToType(saveOpenFileChooser.getFileFilter());
263275 if (fileType == SaveType.NOT_KNOWN) {
291303 L10N.getLocalString("dlg.warning_ttl", "Warning!"), JOptionPane.OK_CANCEL_OPTION,
292304 JOptionPane.WARNING_MESSAGE);
293305 break;case XML_ANALYSIS:
294 response = JOptionPane.showConfirmDialog(saveOpenFileChooser,
295 L10N.getLocalString("dlg.analysis_exists_lbl", "This analysis already exists.\nReplace it?"),
296 L10N.getLocalString("dlg.warning_ttl", "Warning!"), JOptionPane.OK_CANCEL_OPTION,
297 JOptionPane.WARNING_MESSAGE);
298 break;
299 case FBP_FILE:
300 response = JOptionPane.showConfirmDialog(saveOpenFileChooser,
301 L10N.getLocalString("FB Project File already exists",
302 "This FB project file already exists.\nDo you want to replace it?"), L10N.getLocalString(
303 "dlg.warning_ttl", "Warning!"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
304 break;
305 case FBA_FILE:
306 response = JOptionPane.showConfirmDialog(saveOpenFileChooser, L10N.getLocalString(
307 "FB Analysis File already exists",
308 "This FB analysis file already exists.\nDo you want to replace it?"), L10N.getLocalString(
309 "dlg.warning_ttl", "Warning!"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
310 break;
311 default:
312 assert false;
313 }
314
315 if (response == JOptionPane.OK_OPTION)
306 response = JOptionPane.showConfirmDialog(saveOpenFileChooser,
307 L10N.getLocalString("dlg.analysis_exists_lbl", "This analysis already exists.\nReplace it?"),
308 L10N.getLocalString("dlg.warning_ttl", "Warning!"), JOptionPane.OK_CANCEL_OPTION,
309 JOptionPane.WARNING_MESSAGE);
310 break;
311 case FBP_FILE:
312 response = JOptionPane.showConfirmDialog(saveOpenFileChooser,
313 L10N.getLocalString("FB Project File already exists",
314 "This FB project file already exists.\nDo you want to replace it?"), L10N.getLocalString(
315 "dlg.warning_ttl", "Warning!"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
316 break;
317 case FBA_FILE:
318 response = JOptionPane.showConfirmDialog(saveOpenFileChooser, L10N.getLocalString(
319 "FB Analysis File already exists",
320 "This FB analysis file already exists.\nDo you want to replace it?"), L10N.getLocalString(
321 "dlg.warning_ttl", "Warning!"), JOptionPane.OK_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE);
322 break;
323 default:
324 assert false;
325 }
326
327 if (response == JOptionPane.OK_OPTION) {
316328 retry = false;
329 }
317330 if (response == JOptionPane.CANCEL_OPTION) {
318331 retry = true;
319332 continue;
362375 }
363376
364377 SaveType convertFilterToType(FileFilter f) {
365 if (f instanceof FindBugsFileFilter)
378 if (f instanceof FindBugsFileFilter) {
366379 return ((FindBugsFileFilter) f).getSaveType();
380 }
367381
368382 return SaveType.NOT_KNOWN;
369383 }
376390 File convertFile(File f, SaveType fileType) {
377391 // Checks that it has the correct file extension, makes a new file if it
378392 // doesn't.
379 if (!f.getName().endsWith(fileType.getFileExtension()))
393 if (!f.getName().endsWith(fileType.getFileExtension())) {
380394 f = new File(f.getAbsolutePath() + fileType.getFileExtension());
395 }
381396
382397 return f;
383398 }
384399
385400 void save() {
386 if (!mainFrame.canNavigateAway())
401 if (!mainFrame.canNavigateAway()) {
387402 return;
403 }
388404 File sFile = mainFrame.getSaveFile();
389405 assert sFile != null;
390406
417433 }
418434
419435 SaveReturn saveFBPFile(File saveFile2) {
420 if (!mainFrame.canNavigateAway())
436 if (!mainFrame.canNavigateAway()) {
421437 return SaveReturn.SAVE_ERROR;
438 }
422439 try {
423440 mainFrame.getProject().writeXML(saveFile2, mainFrame.getBugCollection());
424441 } catch (IOException e) {
434451 SaveReturn printHtml(final File f) {
435452
436453 Future<Object> waiter = mainFrame.getBackgroundExecutor().submit(new Callable<Object>() {
454 @Override
437455 public Object call() throws Exception {
438456 HTMLBugReporter reporter = new HTMLBugReporter( mainFrame.getProject(), "default.xsl");
439457 reporter.setIsRelaxed(true);
440458 reporter.setOutputStream(UTF8.printStream(new FileOutputStream(f)));
441459 for(BugInstance bug : mainFrame.getBugCollection().getCollection()) {
442460 try {
443 if (mainFrame.getViewFilter().show(bug)) {
444 reporter.reportBug(bug);
445 }
461 if (mainFrame.getViewFilter().show(bug)) {
462 reporter.reportBug(bug);
463 }
446464 } catch (Exception e) {
447465 e.printStackTrace();
448466 }
461479
462480 return SaveReturn.SAVE_SUCCESSFUL;
463481 }
464
482
465483 /**
466484 * Save current analysis as file passed in. Return SAVE_SUCCESSFUL if save
467485 * successful. Method doesn't do much. This method is more if need to do
471489 SaveReturn saveAnalysis(final File f) {
472490
473491 Future<Object> waiter = mainFrame.getBackgroundExecutor().submit(new Callable<Object>() {
492 @Override
474493 public Object call() throws Exception {
475494 BugSaver.saveBugs(f, mainFrame.getBugCollection(), mainFrame.getProject());
476495 return null;
500519 }
501520
502521 void closeProject() {
503 if (askToSave())
522 if (askToSave()) {
504523 return;
524 }
505525
506526 closeProjectInternal();
507527 }
508
528
509529 private void closeProjectInternal() {
510530 // This creates a new filters and suppressions so don't use the
511531 // previoues one.
520540 void loadAnalysis(final File file) {
521541
522542 Runnable runnable = new Runnable() {
543 @Override
523544 public void run() {
524545 mainFrame.acquireDisplayWait();
525546 try {
528549 project.setCurrentWorkingDirectory(file.getParentFile());
529550 BugLoader.loadBugs(mainFrame, project, file);
530551 project.getSourceFinder(); // force source finder to be
531 // initialized
552 // initialized
532553 mainFrame.updateBugTree();
533554 } finally {
534555 mainFrame.releaseDisplayWait();
535556 }
536557 }
537558 };
538 if (EventQueue.isDispatchThread())
559 if (EventQueue.isDispatchThread()) {
539560 new Thread(runnable, "Analysis loading thread").start();
540 else
561 } else {
541562 runnable.run();
563 }
542564 }
543565
544566 void loadAnalysis(final URL url) {
545567
546568 Runnable runnable = new Runnable() {
569 @Override
547570 public void run() {
548571 mainFrame.acquireDisplayWait();
549572 try {
558581 }
559582 }
560583 };
561 if (EventQueue.isDispatchThread())
584 if (EventQueue.isDispatchThread()) {
562585 new Thread(runnable, "Analysis loading thread").start();
563 else
586 } else {
564587 runnable.run();
588 }
565589 }
566590
567591 void loadProjectFromFile(final File f) {
568592
569593 Runnable runnable = new Runnable() {
594 @Override
570595 public void run() {
571596 final Project project = BugLoader.loadProject(mainFrame, f);
572597 final BugCollection bc = project == null ? null : BugLoader.doAnalysis(project);
574599 mainFrame.setProjectAndBugCollectionInSwingThread(project, bc);
575600 }
576601 };
577 if (EventQueue.isDispatchThread())
602 if (EventQueue.isDispatchThread()) {
578603 new Thread(runnable).start();
579 else
604 } else {
580605 runnable.run();
606 }
581607 }
582608
583609 void mergeAnalysis() {
584 if (!mainFrame.canNavigateAway())
610 if (!mainFrame.canNavigateAway()) {
585611 return;
612 }
586613
587614 mainFrame.acquireDisplayWait();
588615 try {
00 package edu.umd.cs.findbugs.gui2;
11
2 import static edu.umd.cs.findbugs.gui2.MainFrameHelper.attachAcceleratorKey;
3 import static edu.umd.cs.findbugs.gui2.MainFrameHelper.newJMenu;
4 import static edu.umd.cs.findbugs.gui2.MainFrameHelper.newJMenuItem;
2 import static edu.umd.cs.findbugs.gui2.MainFrameHelper.*;
53
64 import java.awt.Cursor;
75 import java.awt.event.ActionEvent;
1513 import java.lang.reflect.Method;
1614 import java.util.ArrayList;
1715 import java.util.Collection;
16 import java.util.Locale;
1817
1918 import javax.swing.ActionMap;
2019 import javax.swing.ButtonGroup;
3635 import edu.umd.cs.findbugs.cloud.Cloud;
3736 import edu.umd.cs.findbugs.filter.Filter;
3837 import edu.umd.cs.findbugs.filter.Matcher;
38 import edu.umd.cs.findbugs.gui.AnnotatedString;
3939 import edu.umd.cs.findbugs.gui2.FilterListener.Action;
4040 import edu.umd.cs.findbugs.updates.UpdateChecker;
4141
6868 }
6969
7070 JMenuItem createRecentItem(final File f, final SaveType localSaveType) {
71 if (MainFrame.GUI2_DEBUG)
71 if (MainFrame.GUI2_DEBUG) {
7272 System.out.println("createRecentItem(" + f + ", " + localSaveType + ")");
73 }
7374 String name = f.getName();
7475
7576 final JMenuItem item = new JMenuItem(name);
7677 item.addActionListener(new ActionListener() {
78 @Override
7779 public void actionPerformed(ActionEvent e) {
7880 try {
7981 mainFrame.setCursor(new Cursor(Cursor.WAIT_CURSOR));
8587 return;
8688 }
8789 GUISaveState.getInstance().fileReused(f);// Move to front in
88 // GUISaveState, so
89 // it will be last
90 // thing to be
91 // removed from the
92 // list
90 // GUISaveState, so
91 // it will be last
92 // thing to be
93 // removed from the
94 // list
9395
9496 recentMenuCache.addRecentFile(f);
9597
96 if (!f.exists())
98 if (!f.exists()) {
9799 throw new IllegalStateException("User used a recent projects menu item that didn't exist.");
100 }
98101
99102 // Moved this outside of the thread, and above the line
100103 // saveFile=f.getParentFile()
104107 if (mainFrame.getCurProject() != null && mainFrame.isProjectChanged()) {
105108 int response = JOptionPane.showConfirmDialog(mainFrame, L10N.getLocalString("dlg.save_current_changes",
106109 "The current project has been changed, Save current changes?"), L10N.getLocalString(
107 "dlg.save_changes", "Save Changes?"), JOptionPane.YES_NO_CANCEL_OPTION,
108 JOptionPane.WARNING_MESSAGE);
110 "dlg.save_changes", "Save Changes?"), JOptionPane.YES_NO_CANCEL_OPTION,
111 JOptionPane.WARNING_MESSAGE);
109112
110113 if (response == JOptionPane.YES_OPTION) {
111 if (mainFrame.getSaveFile() != null)
114 if (mainFrame.getSaveFile() != null) {
112115 mainFrame.getMainFrameLoadSaveHelper().save();
113 else
116 } else {
114117 mainFrame.getMainFrameLoadSaveHelper().saveAs();
118 }
115119 } else if (response == JOptionPane.CANCEL_OPTION)
120 {
116121 return;
117 // IF no, do nothing.
122 // IF no, do nothing.
123 }
118124 }
119125
120126 SaveType st = SaveType.forFile(f);
149155
150156 /**
151157 * Creates the MainFrame's menu bar.
152 *
158 *
153159 * @return the menu bar for the MainFrame
154160 */
155161 JMenuBar createMainMenuBar() {
173179 if (!MainFrame.MAC_OS_X) {
174180 exitMenuItem = newJMenuItem("menu.exit", "Exit", KeyEvent.VK_X);
175181 exitMenuItem.addActionListener(new ActionListener() {
182 @Override
176183 public void actionPerformed(ActionEvent evt) {
177184 mainFrame.callOnClose();
178185 }
187194 attachAcceleratorKey(newProjectMenuItem, KeyEvent.VK_N);
188195
189196 newProjectMenuItem.addActionListener(new ActionListener() {
197 @Override
190198 public void actionPerformed(ActionEvent evt) {
191199 mainFrame.createNewProjectFromMenuItem();
192200 }
196204 reconfigMenuItem.setEnabled(false);
197205 attachAcceleratorKey(reconfigMenuItem, KeyEvent.VK_F);
198206 reconfigMenuItem.addActionListener(new ActionListener() {
199 public void actionPerformed(ActionEvent evt) {
200 if (!mainFrame.canNavigateAway())
207 @Override
208 public void actionPerformed(ActionEvent evt) {
209 if (!mainFrame.canNavigateAway()) {
201210 return;
211 }
202212 new NewProjectWizard(mainFrame.getCurProject());
203213 }
204214 });
207217
208218 mergeMenuItem.setEnabled(true);
209219 mergeMenuItem.addActionListener(new ActionListener() {
220 @Override
210221 public void actionPerformed(ActionEvent evt) {
211222 mainFrame.getMainFrameLoadSaveHelper().mergeAnalysis();
212223 }
218229 redoAnalysis.setEnabled(false);
219230 attachAcceleratorKey(redoAnalysis, KeyEvent.VK_R);
220231 redoAnalysis.addActionListener(new ActionListener() {
232 @Override
221233 public void actionPerformed(ActionEvent evt) {
222234 mainFrame.redoAnalysis();
223235 }
225237 }
226238 closeProjectItem = newJMenuItem("menu.closeProject", "Close Project");
227239 closeProjectItem.addActionListener(new ActionListener() {
240 @Override
228241 public void actionPerformed(ActionEvent e) {
229242 mainFrame.getMainFrameLoadSaveHelper().closeProject();
230243 mainFrame.clearBugCollection();
236249 openMenuItem.setEnabled(true);
237250 attachAcceleratorKey(openMenuItem, KeyEvent.VK_O);
238251 openMenuItem.addActionListener(new ActionListener() {
252 @Override
239253 public void actionPerformed(ActionEvent evt) {
240254 mainFrame.getMainFrameLoadSaveHelper().open();
241255 }
242256 });
243257
244258 saveAsMenuItem.addActionListener(new ActionListener() {
259 @Override
245260 public void actionPerformed(ActionEvent evt) {
246261 mainFrame.getMainFrameLoadSaveHelper().saveAs();
247262 }
248263 });
249264 exportFilter.addActionListener(new ActionListener() {
265 @Override
250266 public void actionPerformed(ActionEvent evt) {
251267 mainFrame.getMainFrameLoadSaveHelper().exportFilter();
252268 }
253269 });
254270 importFilter.addActionListener(new ActionListener() {
271 @Override
255272 public void actionPerformed(ActionEvent evt) {
256273 mainFrame.getMainFrameLoadSaveHelper().importFilter();
257274 }
259276 saveMenuItem.setEnabled(false);
260277 attachAcceleratorKey(saveMenuItem, KeyEvent.VK_S);
261278 saveMenuItem.addActionListener(new ActionListener() {
279 @Override
262280 public void actionPerformed(ActionEvent evt) {
263281 mainFrame.getMainFrameLoadSaveHelper().save();
264282 }
265283 });
266284
267 if (!FindBugs.isNoAnalysis())
285 if (!FindBugs.isNoAnalysis()) {
268286 fileMenu.add(newProjectMenuItem);
287 }
269288
270289 fileMenu.add(openMenuItem);
271290 fileMenu.add(recentMenu);
305324 attachAcceleratorKey(pasteMenuItem, KeyEvent.VK_V);
306325
307326 preferencesMenuItem.addActionListener(new ActionListener() {
327 @Override
308328 public void actionPerformed(ActionEvent evt) {
309329 mainFrame.preferences();
310330 }
311331 });
312332
313333 groupByMenuItem.addActionListener(new ActionListener() {
314 public void actionPerformed(ActionEvent evt) {
315 if (!mainFrame.canNavigateAway())
334 @Override
335 public void actionPerformed(ActionEvent evt) {
336 if (!mainFrame.canNavigateAway()) {
316337 return;
338 }
317339 SorterDialog.getInstance().setLocationRelativeTo(mainFrame);
318340 SorterDialog.getInstance().setVisible(true);
319341 }
321343
322344 attachAcceleratorKey(goToLineMenuItem, KeyEvent.VK_L);
323345 goToLineMenuItem.addActionListener(new ActionListener() {
346 @Override
324347 public void actionPerformed(ActionEvent evt) {
325348 mainFrame.getGuiLayout().makeSourceVisible();
326349 try {
347370
348371 menuBar.add(editMenu);
349372
350 if (windowMenu != null)
373 if (windowMenu != null) {
351374 menuBar.add(windowMenu);
375 }
352376
353377 viewMenu = newJMenu("menu.view_menu", "View");
354378 setViewMenu();
383407 helpMenu.add(aboutItem);
384408
385409 aboutItem.addActionListener(new ActionListener() {
410 @Override
386411 public void actionPerformed(ActionEvent evt) {
387412 mainFrame.about();
388413 }
392417 UpdateChecker checker = DetectorFactoryCollection.instance().getUpdateChecker();
393418 boolean disabled = checker.updateChecksGloballyDisabled();
394419 updateItem.setEnabled(!disabled);
395 if (disabled)
420 if (disabled) {
396421 updateItem.setToolTipText("Update checks disabled by plugin: "
397422 + checker.getPluginThatDisabledUpdateChecks());
423 }
398424 helpMenu.add(updateItem);
399425
400426 updateItem.addActionListener(new ActionListener() {
427 @Override
401428 public void actionPerformed(ActionEvent evt) {
402429 DetectorFactoryCollection.instance().checkForUpdates(true);
403430 }
416443 if (cloud != null && cloud.supportsCloudSummaries()) {
417444 JMenuItem cloudReport = new JMenuItem("Cloud summary");
418445 cloudReport.addActionListener(new ActionListener() {
446 @Override
419447 public void actionPerformed(ActionEvent e) {
420448 mainFrame.displayCloudReport();
421449
426454 if (mainFrame.getProjectPackagePrefixes().size() > 0 && mainFrame.getBugCollection() != null) {
427455 JMenuItem selectPackagePrefixMenu = new JMenuItem("Select class search strings by project...");
428456 selectPackagePrefixMenu.addActionListener(new ActionListener() {
457 @Override
429458 public void actionPerformed(ActionEvent e) {
430459 mainFrame.selectPackagePrefixByProject();
431460
434463 viewMenu.add(selectPackagePrefixMenu);
435464
436465 }
437 if (viewMenu.getItemCount() > 0)
466 if (viewMenu.getItemCount() > 0) {
438467 viewMenu.addSeparator();
468 }
439469
440470 ButtonGroup rankButtonGroup = new ButtonGroup();
441471 for (final ViewFilter.RankFilter r : ViewFilter.RankFilter.values()) {
442472 JRadioButtonMenuItem rbMenuItem = new JRadioButtonMenuItem(r.toString());
473 AnnotatedString.localiseButton(rbMenuItem, "menu.rankFilter_"+r.name().toLowerCase(Locale.ENGLISH), r.toString(), true);
443474 rankButtonGroup.add(rbMenuItem);
444 if (r == ViewFilter.RankFilter.ALL)
475 if (r == ViewFilter.RankFilter.ALL) {
445476 rbMenuItem.setSelected(true);
477 }
446478 rbMenuItem.addActionListener(new ActionListener() {
447479
480 @Override
448481 public void actionPerformed(ActionEvent e) {
449482 mainFrame.getViewFilter().setRank(r);
450483 mainFrame.resetViewCache();
455488
456489 viewMenu.addSeparator();
457490
491 ButtonGroup priorityButtonGroup = new ButtonGroup();
492 for (final ViewFilter.PriorityFilter r : ViewFilter.PriorityFilter.values()) {
493 JRadioButtonMenuItem rbMenuItem = new JRadioButtonMenuItem(r.toString());
494 AnnotatedString.localiseButton(rbMenuItem, "menu.priorityFilter_"+r.name().toLowerCase(Locale.ENGLISH), r.toString(), true);
495 priorityButtonGroup.add(rbMenuItem);
496 if (r == ViewFilter.PriorityFilter.ALL_BUGS) {
497 rbMenuItem.setSelected(true);
498 }
499 rbMenuItem.addActionListener(new ActionListener() {
500
501 @Override
502 public void actionPerformed(ActionEvent e) {
503 mainFrame.getViewFilter().setPriority(r);
504 mainFrame.resetViewCache();
505 }
506 });
507 viewMenu.add(rbMenuItem);
508 }
509
510 viewMenu.addSeparator();
511
458512 if (cloud != null && cloud.getMode() == Cloud.Mode.COMMUNAL) {
459513 ButtonGroup overallClassificationButtonGroup = new ButtonGroup();
460514 for (final ViewFilter.OverallClassificationFilter r : ViewFilter.OverallClassificationFilter.values()) {
461 if (!r.supported(cloud))
515 if (!r.supported(cloud)) {
462516 continue;
517 }
463518 JRadioButtonMenuItem rbMenuItem = new JRadioButtonMenuItem(r.toString());
519 AnnotatedString.localiseButton(rbMenuItem, "menu.classificatonFilter_"+r.name().toLowerCase(Locale.ENGLISH), r.toString(), true);
464520 overallClassificationButtonGroup.add(rbMenuItem);
465 if (r == ViewFilter.OverallClassificationFilter.ALL)
521 if (r == ViewFilter.OverallClassificationFilter.ALL) {
466522 rbMenuItem.setSelected(true);
523 }
467524 rbMenuItem.addActionListener(new ActionListener() {
468525
526 @Override
469527 public void actionPerformed(ActionEvent e) {
470528 mainFrame.getViewFilter().setClassification(r);
471529 mainFrame.resetViewCache();
478536
479537 ButtonGroup evalButtonGroup = new ButtonGroup();
480538 for (final ViewFilter.CloudFilter r : ViewFilter.CloudFilter.values()) {
481 if (cloud != null && !r.supported(cloud))
539 if (cloud != null && !r.supported(cloud)) {
482540 continue;
541 }
483542 JRadioButtonMenuItem rbMenuItem = new JRadioButtonMenuItem(r.toString());
543 AnnotatedString.localiseButton(rbMenuItem, "menu.cloudFilter_"+r.name().toLowerCase(Locale.ENGLISH), r.toString(), true);
484544 evalButtonGroup.add(rbMenuItem);
485 if (r == ViewFilter.CloudFilter.ALL)
545 if (r == ViewFilter.CloudFilter.ALL) {
486546 rbMenuItem.setSelected(true);
547 }
487548 rbMenuItem.addActionListener(new ActionListener() {
488549
550 @Override
489551 public void actionPerformed(ActionEvent e) {
490552 mainFrame.getViewFilter().setEvaluation(r);
491553 mainFrame.resetViewCache();
497559 ButtonGroup ageButtonGroup = new ButtonGroup();
498560 for (final ViewFilter.FirstSeenFilter r : ViewFilter.FirstSeenFilter.values()) {
499561 JRadioButtonMenuItem rbMenuItem = new JRadioButtonMenuItem(r.toString());
562 AnnotatedString.localiseButton(rbMenuItem, "menu.firstSeenFilter_"+r.name().toLowerCase(Locale.ENGLISH), r.toString(), true);
500563 ageButtonGroup.add(rbMenuItem);
501 if (r == ViewFilter.FirstSeenFilter.ALL)
564 if (r == ViewFilter.FirstSeenFilter.ALL) {
502565 rbMenuItem.setSelected(true);
566 }
503567 rbMenuItem.addActionListener(new ActionListener() {
504568
569 @Override
505570 public void actionPerformed(ActionEvent e) {
506571 mainFrame.getViewFilter().setFirstSeen(r);
507572 mainFrame.resetViewCache();
513578 final Filter suppressionFilter = MainFrame.getInstance().getProject().getSuppressionFilter();
514579 Collection<Matcher> filters = suppressionFilter.getChildren();
515580 JMenuItem filterMenu = new JMenuItem(filters.isEmpty() ? "Add Filters..." : "Filters...");
516
581
517582 filterMenu.addActionListener(new ActionListener() {
518
583
584 @Override
519585 public void actionPerformed(ActionEvent e) {
520586 PreferencesFrame preferenceFrame = PreferencesFrame.getInstance();
521587 preferenceFrame.showFilterPane();
522588 preferenceFrame.setLocationRelativeTo(mainFrame);
523589 preferenceFrame.setVisible(true);
524
525
590
591
526592 }
527593 });
528594 viewMenu.add(filterMenu);
529 for(final Matcher m : filters) {
595 for(final Matcher m : filters) {
530596 JCheckBoxMenuItem f = new JCheckBoxMenuItem(m.toString(), suppressionFilter.isEnabled(m));
531597 viewMenu.add(f);
532598 f.addItemListener(new ItemListener() {
533
599
600 @Override
534601 public void itemStateChanged(ItemEvent e) {
535602 boolean enabled = e.getStateChange() == ItemEvent.SELECTED;
536603 suppressionFilter.setEnabled(m, enabled);
537604 FilterActivity.notifyListeners(enabled ? Action.FILTERING : Action.UNFILTERING, null);
538
539 }
540 });
541
542
605
606 }
607 });
608
609
543610 }
544611
545612
548615 /**
549616 * This checks if the xmlFile is in the GUISaveState. If not adds it. Then
550617 * adds the file to the recentMenuCache.
551 *
618 *
552619 * @param xmlFile
553620 */
554621 /*
634701 super(L10N.getLocalString("txt.cut", "Cut"));
635702 }
636703
704 @Override
637705 public void actionPerformed(ActionEvent evt) {
638706 JTextComponent text = getTextComponent(evt);
639707
640 if (text == null)
708 if (text == null) {
641709 return;
710 }
642711
643712 text.cut();
644713 }
662731 super(L10N.getLocalString("txt.copy", "Copy"));
663732 }
664733
734 @Override
665735 public void actionPerformed(ActionEvent evt) {
666736 JTextComponent text = getTextComponent(evt);
667737
668 if (text == null)
738 if (text == null) {
669739 return;
740 }
670741
671742 text.copy();
672743 }
678749 super(L10N.getLocalString("txt.paste", "Paste"));
679750 }
680751
752 @Override
681753 public void actionPerformed(ActionEvent evt) {
682754 JTextComponent text = getTextComponent(evt);
683755
684 if (text == null)
756 if (text == null) {
685757 return;
758 }
686759
687760 text.paste();
688761 }
8484
8585 public void newTree(final JTree newTree, final BugTreeModel newModel) {
8686 SwingUtilities.invokeLater(new Runnable() {
87 @Override
8788 public void run() {
8889 tree = newTree;
8990 tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
120121 public Sortables[] getAvailableSortables() {
121122 Sortables[] sortables;
122123 ArrayList<Sortables> a = new ArrayList<Sortables>(Sortables.values().length);
123 for (Sortables s : Sortables.values())
124 if (s.isAvailable(mainFrame))
124 for (Sortables s : Sortables.values()) {
125 if (s.isAvailable(mainFrame)) {
125126 a.add(s);
127 }
128 }
126129 sortables = new Sortables[a.size()];
127130 a.toArray(sortables);
128131 return sortables;
130133
131134 /**
132135 * Returns the SorterTableColumnModel of the MainFrame.
133 *
134 * @return
135136 */
136137 SorterTableColumnModel getSorter() {
137138 return sorter;
172173
173174 private void warnUserOfFilters() {
174175 JOptionPane
175 .showMessageDialog(
176 mainFrame,
177 edu.umd.cs.findbugs.L10N
178 .getLocalString("dlg.everything_is_filtered",
179 "All bugs in this project appear to be filtered out. \nYou may wish to check your filter settings in the preferences menu."),
176 .showMessageDialog(
177 mainFrame,
178 edu.umd.cs.findbugs.L10N
179 .getLocalString("dlg.everything_is_filtered",
180 "All bugs in this project appear to be filtered out. \nYou may wish to check your filter settings in the preferences menu."),
180181 "Warning", JOptionPane.WARNING_MESSAGE);
181182 }
182183
183184 /**
184185 * Creates popup menu for bugs on tree.
185 *
186 * @return
187186 */
188187 JPopupMenu createBugPopupMenu() {
189188 JPopupMenu popupMenu = new JPopupMenu();
191190 JMenuItem filterMenuItem = MainFrameHelper.newJMenuItem("menu.filterBugsLikeThis", "Filter bugs like this");
192191
193192 filterMenuItem.addActionListener(new ActionListener() {
193 @Override
194194 public void actionPerformed(ActionEvent evt) {
195195 if (!mainFrame.canNavigateAway()) { return; }
196
197 new NewFilterFromBug(new FilterFromBugPicker(currentSelectedBugLeaf.getBug(),
198 Arrays.asList(mainFrame.getAvailableSortables())),
199 new ApplyNewFilter(mainFrame.getProject().getSuppressionFilter(),
200 PreferencesFrame.getInstance(),
201 new FilterActivityNotifier()));
196
197 new NewFilterFromBug(new FilterFromBugPicker(currentSelectedBugLeaf.getBug(),
198 Arrays.asList(mainFrame.getAvailableSortables())),
199 new ApplyNewFilter(mainFrame.getProject().getSuppressionFilter(),
200 PreferencesFrame.getInstance(),
201 new FilterActivityNotifier()));
202202
203203 mainFrame.setProjectChanged(true);
204204 mainFrame.getTree().setSelectionRow(0); // Selects the top of the Jtree so the CommentsArea syncs up.
225225 /**
226226 * Creates the branch pop up menu that ask if the user wants to hide all the
227227 * bugs in that branch.
228 *
229 * @return
230228 */
231229 JPopupMenu createBranchPopUpMenu() {
232230 JPopupMenu popupMenu = new JPopupMenu();
234232 JMenuItem filterMenuItem = MainFrameHelper.newJMenuItem("menu.filterTheseBugs", "Filter these bugs");
235233
236234 filterMenuItem.addActionListener(new ActionListener() {
235 @Override
237236 public void actionPerformed(ActionEvent evt) {
238237 // TODO This code does a smarter version of filtering that is
239238 // only possible for branches, and does so correctly
244243 // benefit of using the smarter deletion method.
245244
246245 try {
247 if (!mainFrame.canNavigateAway())
246 if (!mainFrame.canNavigateAway()) {
248247 return;
248 }
249249 int startCount;
250250 TreePath path = MainFrame.getInstance().getTree().getSelectionPath();
251251 TreePath deletePath = path;
254254 while (count == startCount) {
255255 deletePath = deletePath.getParentPath();
256256 if (deletePath.getParentPath() == null)// We are at the
257 // top of the
258 // tree, don't
259 // let this be
260 // removed,
261 // rebuild tree
262 // from root.
257 // top of the
258 // tree, don't
259 // let this be
260 // removed,
261 // rebuild tree
262 // from root.
263263 {
264264 Matcher m = mainFrame.getCurrentSelectedBugAspects().getMatcher();
265265 Filter suppressionFilter = MainFrame.getInstance().getProject().getSuppressionFilter();
292292 mainFrame.setProjectChanged(true);
293293
294294 MainFrame.getInstance().getTree().setSelectionRow(0);// Selects
295 // the
296 // top
297 // of
298 // the
299 // Jtree
300 // so
301 // the
302 // CommentsArea
303 // syncs
304 // up.
295 // the
296 // top
297 // of
298 // the
299 // Jtree
300 // so
301 // the
302 // CommentsArea
303 // syncs
304 // up.
305305 } catch (RuntimeException e) {
306306 MainFrame.getInstance().showMessageDialog("Unable to create filter: " + e.getMessage());
307307 }
328328 ActionListener treeActionAdapter(ActionMap map, String actionName) {
329329 final Action selectPrevious = map.get(actionName);
330330 return new ActionListener() {
331 @Override
331332 public void actionPerformed(ActionEvent e) {
332333 e.setSource(tree);
333334 selectPrevious.actionPerformed(e);
342343 int i = 0;
343344 while (true) {
344345 int rows = jTree.getRowCount();
345 if (i >= rows || rows >= max)
346 if (i >= rows || rows >= max) {
346347 break;
348 }
347349 jTree.expandRow(i++);
348350 }
349351 }
356358 for (int i = 0; i < rows; i++) {
357359 TreePath treePath = jTree.getPathForRow(i);
358360 Object lastPathComponent = treePath.getLastPathComponent();
359 if (lastPathComponent instanceof BugLeafNode)
361 if (lastPathComponent instanceof BugLeafNode) {
360362 return true;
363 }
361364 }
362365 return false;
363366 }
365368 @SwingThread
366369 void expandToFirstLeaf(int max) {
367370 Debug.println("expand to first leaf");
368 if (leavesShown())
371 if (leavesShown()) {
369372 return;
373 }
370374 JTree jTree = getTree();
371375 int i = 0;
372376 while (true) {
373377 int rows = jTree.getRowCount();
374 if (i >= rows || rows >= max)
378 if (i >= rows || rows >= max) {
375379 break;
380 }
376381 TreePath treePath = jTree.getPathForRow(i);
377382 Object lastPathComponent = treePath.getLastPathComponent();
378 if (lastPathComponent instanceof BugLeafNode)
383 if (lastPathComponent instanceof BugLeafNode) {
379384 return;
385 }
380386 jTree.expandRow(i++);
381387 }
382388 }
383389
384390 void setupTreeListeners() {
385391 // noinspection ConstantIfStatement
386 if (false)
392 if (false) {
387393 tree.addTreeExpansionListener(new MyTreeExpansionListener());
394 }
388395 tree.addTreeSelectionListener(new MyTreeSelectionListener());
389396
390397 tree.addMouseListener(new TreeMouseListener());
412419 @Override
413420 public void mouseClicked(MouseEvent e) {
414421 Debug.println("tableheader.getReorderingAllowed() = " + getTableheader().getReorderingAllowed());
415 if (!getTableheader().getReorderingAllowed())
422 if (!getTableheader().getReorderingAllowed()) {
416423 return;
417 if (e.getClickCount() == 2)
424 }
425 if (e.getClickCount() == 2) {
418426 SorterDialog.getInstance().setVisible(true);
427 }
419428 }
420429
421430 @Override
422431 public void mouseReleased(MouseEvent arg0) {
423 if (!getTableheader().getReorderingAllowed())
432 if (!getTableheader().getReorderingAllowed()) {
424433 return;
434 }
425435 BugTreeModel bt = (BugTreeModel) (getTree().getModel());
426436 bt.checkSorter();
427437 }
450460 textFieldForPackagesToDisplay = new JTextField();
451461 ActionListener filterAction = new ActionListener() {
452462
463 @Override
453464 public void actionPerformed(ActionEvent e) {
454465 try {
455466 String text = textFieldForPackagesToDisplay.getText();
531542
532543 void showCard(final MainFrame.BugCard card, final Cursor cursor, final Window window) {
533544 Runnable doRun = new Runnable() {
545 @Override
534546 public void run() {
535547 mainFrame.enableRecentMenu(card == MainFrame.BugCard.TREECARD);
536548 getTableheader().setReorderingAllowed(card == MainFrame.BugCard.TREECARD);
538550 window.setCursor(cursor);
539551 CardLayout layout = (CardLayout) cardPanel.getLayout();
540552 layout.show(cardPanel, card.name());
541 if (card == MainFrame.BugCard.TREECARD)
553 if (card == MainFrame.BugCard.TREECARD) {
542554 SorterDialog.getInstance().thaw();
543 else
555 } else {
544556 SorterDialog.getInstance().freeze();
557 }
545558 }
546559 };
547 if (SwingUtilities.isEventDispatchThread())
560 if (SwingUtilities.isEventDispatchThread()) {
548561 doRun.run();
549 else
562 } else {
550563 SwingUtilities.invokeLater(doRun);
564 }
551565 }
552566
553567 private JPanel makeNavigationPanel(String packageSelectorLabel, JComponent packageSelector, JComponent treeHeader,
595609 }
596610
597611 private class TreeMouseListener implements MouseListener {
612 @Override
598613 public void mouseClicked(MouseEvent e) {
599614 TreePath path = tree.getPathForLocation(e.getX(), e.getY());
600615
601 if (path == null)
616 if (path == null) {
602617 return;
618 }
619
620 if(currentSelectedBugLeaf == path.getLastPathComponent()) {
621 // sync mainFrame if user just clicks on the same bug
622 mainFrame.syncBugInformation();
623 }
603624
604625 if ((e.getButton() == MouseEvent.BUTTON3) || (e.getButton() == MouseEvent.BUTTON1 && e.isControlDown())) {
605626
608629 bugPopupMenu.show(tree, e.getX(), e.getY());
609630 } else {
610631 tree.setSelectionPath(path);
611 if (!(path.getParentPath() == null))// If the path's parent
612 // path is null, the
613 // root was selected,
614 // dont allow them to
615 // filter out the root.
632 if (!(path.getParentPath() == null)) {
633 // path is null, the
634 // root was selected,
635 // dont allow them to
636 // filter out the root.
616637 branchPopupMenu.show(tree, e.getX(), e.getY());
617 }
618 }
619 }
620
638 }
639 }
640 }
641 }
642
643 @Override
621644 public void mousePressed(MouseEvent arg0) {
622645 }
623646
647 @Override
624648 public void mouseReleased(MouseEvent arg0) {
625649 }
626650
651 @Override
627652 public void mouseEntered(MouseEvent arg0) {
628653 }
629654
655 @Override
630656 public void mouseExited(MouseEvent arg0) {
631657 }
632658 }
633659
634660 private class MyTreeSelectionListener implements TreeSelectionListener {
635661 private volatile boolean ignoreSelection = false;
662 @Override
636663 public void valueChanged(TreeSelectionEvent selectionEvent) {
637 if (ignoreSelection)
664 if (ignoreSelection) {
638665 return;
666 }
639667
640668 TreePath path = selectionEvent.getNewLeadSelectionPath();
641669 if (path != null) {
671699
672700 private class MyTreeExpansionListener implements TreeExpansionListener {
673701
702 @Override
674703 public void treeExpanded(TreeExpansionEvent event) {
675704 System.out.println("Tree expanded");
676705 TreePath path = event.getPath();
682711 final TreePath p = path.pathByAddingChild(o);
683712 SwingUtilities.invokeLater(new Runnable() {
684713
714 @Override
685715 public void run() {
686716 try {
687717 System.out.println("auto expanding " + p);
697727 }
698728 }
699729
730 @Override
700731 public void treeCollapsed(TreeExpansionEvent event) {
701732 // do nothing
702733 }
4343 import edu.umd.cs.findbugs.gui2.BugAspects.SortableValue;
4444
4545 /**
46 *
46 *
4747 * Lets you choose your new filter, shouldn't let you choose filters that
4848 * wouldn't filter anything out including filters that you already have
49 *
49 *
5050 */
5151 @SuppressWarnings("serial")
5252 public class NewFilterFrame extends FBDialog {
5353
54 private JList list = new JList();
54 private final JList<String> list = new JList<>();
5555
5656 private static NewFilterFrame instance = null;
5757
9797 }
9898 }
9999
100 final JComboBox comboBox = new JComboBox(valuesWithoutDivider);
101 comboBox.setRenderer(new ListCellRenderer() {
100 final JComboBox<Sortables> comboBox = new JComboBox<>(valuesWithoutDivider);
101 comboBox.setRenderer(new ListCellRenderer<Sortables>() {
102102 final Color SELECTED_BACKGROUND = new Color(183, 184, 204);
103103
104 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
104 @Override
105 public Component getListCellRendererComponent(JList<? extends Sortables> list, Sortables value, int index, boolean isSelected,
105106 boolean cellHasFocus) {
106107 JLabel result = new JLabel();
107108 result.setFont(result.getFont().deriveFont(Driver.getFontSize()));
108109 result.setOpaque(true);
109110 result.setText(value.toString().toLowerCase());
110 if (isSelected)
111 if (isSelected) {
111112 result.setBackground(SELECTED_BACKGROUND);
113 }
112114 return result;
113115 }
114116 });
115117 comboBox.addActionListener(new ActionListener() {
118 @Override
116119 public void actionPerformed(ActionEvent evt) {
117120 Sortables filterBy = (Sortables) comboBox.getSelectedItem();
118 String[] listData = filterBy.getAllSorted();
119 for (int i = 0; i < listData.length; i++)
120 listData[i] = filterBy.formatValue(listData[i]);
121 String[] rawValues = filterBy.getAllSorted();
122 String[] listData = new String[rawValues.length];
123 for (int i = 0; i < listData.length; i++) {
124 listData[i] = filterBy.formatValue(rawValues[i]);
125 }
121126 list.setListData(listData);
122127 }
123128 });
127132 String[] filterModes = { edu.umd.cs.findbugs.L10N.getLocalString("mode.equal_to", "equal to"),
128133 edu.umd.cs.findbugs.L10N.getLocalString("mode.at_or_after", "at or after"),
129134 edu.umd.cs.findbugs.L10N.getLocalString("mode.at_or_before", "at or before") };
130 final JComboBox filterModeComboBox = new JComboBox(filterModes);
135 final JComboBox<String> filterModeComboBox = new JComboBox<>(filterModes);
131136 north.add(filterModeComboBox);
132137 north.add(new JLabel(":"));
133138 north.add(Box.createHorizontalGlue());
134139 JPanel south = new JPanel();
135140 JButton okButton = new JButton(edu.umd.cs.findbugs.L10N.getLocalString("dlg.ok_btn", "OK"));
136141 okButton.addActionListener(new ActionListener() {
142 @Override
137143 public void actionPerformed(ActionEvent evt) {
138144 Sortables key = (Sortables) comboBox.getSelectedItem();
139145 String[] values = key.getAllSorted();
157163 });
158164 JButton cancelButton = new JButton(edu.umd.cs.findbugs.L10N.getLocalString("dlg.cancel_btn", "Cancel"));
159165 cancelButton.addActionListener(new ActionListener() {
166 @Override
160167 public void actionPerformed(ActionEvent evt) {
161168 close();
162169 }
178185 // ProjectSettings.getInstance().addFilter(fm);
179186 // }
180187 FilterMatcher[] newFilters = new FilterMatcher[list.getSelectedIndices().length];
181 for (int i = 0; i < newFilters.length; i++)
188 for (int i = 0; i < newFilters.length; i++) {
182189 newFilters[i] = new FilterMatcher(key, values[list.getSelectedIndices()[i]]);
190 }
183191 ProjectSettings.getInstance().addFilters(newFilters);
184192 PreferencesFrame.getInstance().updateFilterPanel();
185193 close();
3939 public class NewFilterFromBug extends FBDialog {
4040
4141 private static final List<NewFilterFromBug> listOfAllFrames = new ArrayList<NewFilterFromBug>();
42
42
4343 public NewFilterFromBug(final FilterFromBugPicker filterFromBugPicker, final ApplyNewFilter applyNewFilter) {
4444 this.setModal(true);
4545 listOfAllFrames.add(this);
5252
5353 JPanel south = new JPanel();
5454 JButton okButton = new JButton(edu.umd.cs.findbugs.L10N.getLocalString("dlg.ok_btn", "OK"));
55
55
5656 okButton.addActionListener(new ActionListener() {
57 @Override
5758 public void actionPerformed(ActionEvent evt) {
5859 Matcher matcherFromSelection = filterFromBugPicker.makeMatcherFromSelection();
5960 applyNewFilter.fromMatcher(matcherFromSelection);
6263 });
6364 JButton cancelButton = new JButton(edu.umd.cs.findbugs.L10N.getLocalString("dlg.cancel_btn", "Cancel"));
6465 cancelButton.addActionListener(new ActionListener() {
66 @Override
6567 public void actionPerformed(ActionEvent evt) {
6668 closeDialog();
6769 }
7274 pack();
7375 setVisible(true);
7476 }
75
77
7678 private final void closeDialog() {
7779 NewFilterFromBug.this.dispose();
7880 }
4949 import javax.swing.JScrollPane;
5050 import javax.swing.JSeparator;
5151 import javax.swing.JTextField;
52 import javax.swing.ListCellRenderer;
5253 import javax.swing.SwingUtilities;
5354 import javax.swing.border.EmptyBorder;
5455 import javax.swing.filechooser.FileFilter;
9697 }
9798 };
9899
99 private final JList analyzeList = new JList();
100
101 private final DefaultListModel analyzeModel = new DefaultListModel();
100 private final JList<String> analyzeList = new JList<String>();
101
102 private final DefaultListModel<String> analyzeModel = new DefaultListModel<String>();
102103
103104 private final JTextField projectName = new JTextField();
104105
105 private final JList auxList = new JList();
106
107 private final DefaultListModel auxModel = new DefaultListModel();
108
109 private final JList sourceList = new JList();
110
111 private final DefaultListModel sourceModel = new DefaultListModel();
106 private final JList<String> auxList = new JList<String>();
107
108 private final DefaultListModel<String> auxModel = new DefaultListModel<String>();
109
110 private final JList<String> sourceList = new JList<String>();
111
112 private final DefaultListModel<String> sourceModel = new DefaultListModel<String>();
112113
113114 private final JButton finishButton = new JButton();
114115
115116 private final JButton cancelButton = new JButton(edu.umd.cs.findbugs.L10N.getLocalString("dlg.cancel_btn", "Cancel"));
116117
117 private final JComboBox cloudSelector = new JComboBox();
118 private final JComboBox<CloudPlugin> cloudSelector = new JComboBox<CloudPlugin>();
118119
119120 private final JComponent[] wizardComponents = new JComponent[4];
120121
130131 setBackground(list.getSelectionBackground());
131132 setForeground(list.getSelectionForeground());
132133 if (-1 < index) {
133 if (plugin == null)
134 if (plugin == null) {
134135 list.setToolTipText("No cloud plugin specified by project");
135 else
136 } else {
136137 list.setToolTipText(plugin.getDetails());
138 }
137139 }
138140 } else {
139141 setBackground(list.getBackground());
163165 }
164166 boolean temp = false;
165167
166 if (curProject == null)
168 if (curProject == null) {
167169 setTitle(edu.umd.cs.findbugs.L10N.getLocalString("dlg.new_item", "New Project"));
168 else {
170 } else {
169171 setTitle(edu.umd.cs.findbugs.L10N.getLocalString("dlg.reconfig", "Reconfigure"));
170172 temp = true;
171173 }
197199 cloudPanel.add(cloudSelector, BorderLayout.CENTER);
198200
199201 wizardComponents[3] = cloudPanel;
200 cloudSelector.setRenderer(new CloudComboBoxRenderer());
202 @SuppressWarnings("unchecked")
203 ListCellRenderer<CloudPlugin> aRenderer = new CloudComboBoxRenderer();
204 cloudSelector.setRenderer(aRenderer);
201205 cloudSelector.addItem(null);
202206 String cloudId = project.getCloudId();
203207
204208 for (CloudPlugin c : DetectorFactoryCollection.instance().getRegisteredClouds().values()) {
205209 String fbid = c.getFindbugsPluginId();
206210 Plugin plugin = Plugin.getByPluginId(fbid);
207 if (plugin == null)
211 if (plugin == null) {
208212 continue;
213 }
209214 Boolean fbPluginStatus = project.getPluginStatus(plugin);
210 if ((!c.isHidden() || c.getId().equals(cloudId)) && !Boolean.FALSE.equals(fbPluginStatus))
215 if ((!c.isHidden() || c.getId().equals(cloudId)) && !Boolean.FALSE.equals(fbPluginStatus)) {
211216 cloudSelector.addItem(c);
217 }
212218 }
213219
214220 if (cloudId != null) {
232238 boolean keepGoing = false;
233239
234240 private boolean displayWarningAndAskIfWeShouldContinue(String msg, String title) {
235 if (keepGoing)
241 if (keepGoing) {
236242 return true;
243 }
237244 boolean result = JOptionPane.showConfirmDialog(NewProjectWizard.this, msg, title, JOptionPane.OK_CANCEL_OPTION,
238245 JOptionPane.WARNING_MESSAGE) == JOptionPane.OK_OPTION;
239 if (result)
246 if (result) {
240247 keepGoing = true;
248 }
241249 return result;
242250
243251 }
244252
253 @Override
245254 public void actionPerformed(ActionEvent evt) {
246255
247 if (displayWarnings())
256 if (displayWarnings()) {
248257 return;
258 }
249259 Project p;
250260 String oldCloudId = null;
251261 p = project;
255265
256266
257267 // Now that p is cleared, we can add in all the correct files.
258 for (int i = 0; i < analyzeModel.getSize(); i++)
259 p.addFile((String) analyzeModel.get(i));
260 for (int i = 0; i < auxModel.getSize(); i++)
261 p.addAuxClasspathEntry((String) auxModel.get(i));
262 for (int i = 0; i < sourceModel.getSize(); i++)
263 p.addSourceDir((String) sourceModel.get(i));
268 for (int i = 0; i < analyzeModel.getSize(); i++) {
269 p.addFile(analyzeModel.get(i));
270 }
271 for (int i = 0; i < auxModel.getSize(); i++) {
272 p.addAuxClasspathEntry(auxModel.get(i));
273 }
274 for (int i = 0; i < sourceModel.getSize(); i++) {
275 p.addSourceDir(sourceModel.get(i));
276 }
264277 p.setProjectName(projectName.getText());
265278 CloudPlugin cloudPlugin = (CloudPlugin) cloudSelector.getSelectedItem();
266279 String newCloudId;
279292 || JOptionPane.showConfirmDialog(NewProjectWizard.this, edu.umd.cs.findbugs.L10N
280293 .getLocalString("dlg.project_settings_changed_lbl",
281294 "Project settings have been changed. Perform a new analysis with the changed files?"),
282 edu.umd.cs.findbugs.L10N.getLocalString("dlg.redo_analysis_question_lbl", "Redo analysis?"),
283 JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)) {
295 edu.umd.cs.findbugs.L10N.getLocalString("dlg.redo_analysis_question_lbl", "Redo analysis?"),
296 JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION)) {
284297 AnalyzingDialog.show(p);
285298 } else if (!Util.nullSafeEquals(newCloudId, oldCloudId)) {
286299 BugCollection bugs = mainFrame.getBugCollection();
296309 mainFrame.getComments().updateCommentsFromLeafInformation(mainFrame.getCurrentSelectedBugLeaf());
297310
298311 }
299 if (reconfig)
312 if (reconfig) {
300313 mainFrame.setProjectChanged(true);
314 }
301315
302316 String name = p.getProjectName();
303317 if (name == null) {
313327
314328 private boolean displayWarnings() {
315329 for (int i = 0; i < analyzeModel.getSize(); i++) {
316 File temp = new File((String) analyzeModel.get(i));
330 File temp = new File(analyzeModel.get(i));
317331 if (!temp.exists() && directoryOrArchive.accept(temp)) {
318332 if (!displayWarningAndAskIfWeShouldContinue(
319333 temp.getName() + " " + edu.umd.cs.findbugs.L10N.getLocalString("dlg.invalid_txt", " is invalid."),
320 edu.umd.cs.findbugs.L10N.getLocalString("dlg.error_ttl", "Can't locate file")))
334 edu.umd.cs.findbugs.L10N.getLocalString("dlg.error_ttl", "Can't locate file"))) {
321335 return true;
336 }
322337
323338 }
324339 }
325340
326341 for (int i = 0; i < sourceModel.getSize(); i++) {
327 File temp = new File((String) sourceModel.get(i));
342 File temp = new File(sourceModel.get(i));
328343 if (!temp.exists() && directoryOrArchive.accept(temp)) {
329344 if (!displayWarningAndAskIfWeShouldContinue(
330345 temp.getName() + " " + edu.umd.cs.findbugs.L10N.getLocalString("dlg.invalid_txt", " is invalid."),
331 edu.umd.cs.findbugs.L10N.getLocalString("dlg.error_ttl", "Can't locate file")))
346 edu.umd.cs.findbugs.L10N.getLocalString("dlg.error_ttl", "Can't locate file"))) {
332347 return true;
348 }
333349 }
334350 }
335351 for (int i = 0; i < auxModel.getSize(); i++) {
336 File temp = new File((String) auxModel.get(i));
352 File temp = new File(auxModel.get(i));
337353 if (!temp.exists() && directoryOrArchive.accept(temp)) {
338354 if (!displayWarningAndAskIfWeShouldContinue(
339355 temp.getName() + " " + edu.umd.cs.findbugs.L10N.getLocalString("dlg.invalid_txt", " is invalid."),
340 edu.umd.cs.findbugs.L10N.getLocalString("dlg.error_ttl", "Can't locate file")))
356 edu.umd.cs.findbugs.L10N.getLocalString("dlg.error_ttl", "Can't locate file"))) {
341357 return true;
358 }
342359 }
343360 }
344361 return false;
345362 }
346363 });
347 if (curProject == null)
364 if (curProject == null) {
348365 finishButton.setText(edu.umd.cs.findbugs.L10N.getLocalString("dlg.analyze_btn", "Analyze"));
349 else
366 } else {
350367 finishButton.setText(edu.umd.cs.findbugs.L10N.getLocalString("dlg.ok_btn", "OK"));
368 }
351369 cancelButton.addActionListener(new ActionListener() {
370 @Override
352371 public void actionPerformed(ActionEvent evt) {
353372 dispose();
354373 }
360379 south.add(buttons, BorderLayout.EAST);
361380
362381 if (curProject != null) {
363 for (String i : curProject.getFileList())
382 for (String i : curProject.getFileList()) {
364383 analyzeModel.addElement(i);
384 }
365385 // If the project had no classes in it, disable the finish button
366386 // until classes are added.
367387 // if (curProject.getFileList().size()==0)
368388 // this.finishButton.setEnabled(false);
369 for (String i : curProject.getAuxClasspathEntryList())
389 for (String i : curProject.getAuxClasspathEntryList()) {
370390 auxModel.addElement(i);
371 for (String i : curProject.getSourceDirList())
391 }
392 for (String i : curProject.getSourceDirList()) {
372393 sourceModel.addElement(i);
394 }
373395 projectName.setText(curProject.getProjectName());
374396 projectName.addKeyListener(new KeyAdapter() {
375397 @Override
399421 // First clear p's old files, otherwise we can't remove a file
400422 // once an analysis has been performed on it
401423 int numOldFiles = p.getFileCount();
402 for (int x = 0; x < numOldFiles; x++)
424 for (int x = 0; x < numOldFiles; x++) {
403425 p.removeFile(0);
426 }
404427
405428 int numOldAuxFiles = p.getNumAuxClasspathEntries();
406 for (int x = 0; x < numOldAuxFiles; x++)
429 for (int x = 0; x < numOldAuxFiles; x++) {
407430 p.removeAuxClasspathEntry(0);
431 }
408432
409433 int numOldSrc = p.getNumSourceDirs();
410 for (int x = 0; x < numOldSrc; x++)
434 for (int x = 0; x < numOldSrc; x++) {
411435 p.removeSourceDir(0);
436 }
412437 }
413438
414439 private JComponent createTextFieldPanel(String label, JTextField textField) {
421446 return myPanel;
422447 }
423448
424 private JPanel createFilePanel(final String label, final JList list, final DefaultListModel listModel,
425 final int fileSelectionMode, final FileFilter filter, final String dialogTitle,
426 boolean wizard, final String helpUrl) {
449 private JPanel createFilePanel(final String label, final JList<String> list, final DefaultListModel<String> listModel,
450 final int fileSelectionMode, final FileFilter filter, final String dialogTitle,
451 boolean wizard, final String helpUrl) {
427452 JPanel myPanel = new JPanel(new GridBagLayout());
428453 GridBagConstraints gbc = new GridBagConstraints();
429454 gbc.gridx = 0;
449474 button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
450475 button.setContentAreaFilled(false);
451476 button.addActionListener(new ActionListener() {
477 @Override
452478 public void actionPerformed(ActionEvent e) {
453479 try {
454480 LaunchBrowser.showDocument(new URL(helpUrl));
490516 final NewProjectWizard thisGUI = this;
491517 myPanel.add(wizardButton, gbc);
492518 wizardButton.addActionListener(new ActionListener() {
519 @Override
493520 public void actionPerformed(ActionEvent evt) {
494521 final Project tempProject = new Project();
495 for (int i = 0; i < analyzeModel.getSize(); i++)
496 tempProject.addFile((String) analyzeModel.get(i));
497 for (int i = 0; i < auxModel.getSize(); i++)
498 tempProject.addAuxClasspathEntry((String) auxModel.get(i));
522 for (int i = 0; i < analyzeModel.getSize(); i++) {
523 tempProject.addFile(analyzeModel.get(i));
524 }
525 for (int i = 0; i < auxModel.getSize(); i++) {
526 tempProject.addAuxClasspathEntry(auxModel.get(i));
527 }
499528
500529 java.awt.EventQueue.invokeLater(new Runnable() {
530 @Override
501531 public void run() {
502532 final SourceDirectoryWizard dialog = new SourceDirectoryWizard(new javax.swing.JFrame(), true,
503533 tempProject, thisGUI);
504534 dialog.addWindowListener(new java.awt.event.WindowAdapter() {
505535 @Override
506536 public void windowClosing(java.awt.event.WindowEvent e) {
507 if (dialog.discover != null && dialog.discover.isAlive())
537 if (dialog.discover != null && dialog.discover.isAlive()) {
508538 dialog.discover.interrupt();
539 }
509540 }
510541 });
511542 dialog.setVisible(true);
518549 myPanel.add(Box.createGlue(), gbc);
519550 myPanel.setBorder(border);
520551 addButton.addActionListener(new ActionListener() {
552 @Override
521553 public void actionPerformed(ActionEvent evt) {
522554 chooser.setFileSelectionMode(fileSelectionMode);
523555 chooser.setMultiSelectionEnabled(true);
540572 // If this is the primary class directories add button, set
541573 // it to enable the finish button of the main dialog
542574 if (label.equals(edu.umd.cs.findbugs.L10N.getLocalString("dlg.class_jars_dirs_lbl",
543 "Class archives and directories to analyze:")))
575 "Class archives and directories to analyze:"))) {
544576 finishButton.setEnabled(true);
577 }
545578 }
546579 }
547580 });
548581 removeButton.addActionListener(new ActionListener() {
582 @Override
549583 public void actionPerformed(ActionEvent evt) {
550 if (list.getSelectedValues().length > 0)
584 if (list.getSelectedValues().length > 0) {
551585 projectChanged = true;
586 }
552587 for (Object i : list.getSelectedValues())
588 {
553589 listModel.removeElement(i);
554 // If this is the primary class directories remove button, set
555 // it to disable finish when there are no class files being
556 // analyzed
557 // if (listModel.size()==0 &&
558 // label.equals(edu.umd.cs.findbugs.L10N.getLocalString("dlg.class_jars_dirs_lbl",
559 // "Class archives and directories to analyze:")))
560 // finishButton.setEnabled(false);
590 // If this is the primary class directories remove button, set
591 // it to disable finish when there are no class files being
592 // analyzed
593 // if (listModel.size()==0 &&
594 // label.equals(edu.umd.cs.findbugs.L10N.getLocalString("dlg.class_jars_dirs_lbl",
595 // "Class archives and directories to analyze:")))
596 // finishButton.setEnabled(false);
597 }
561598 }
562599 });
563600 return myPanel;
572609 */
573610 private void loadAllPanels(final JPanel mainPanel) {
574611 SwingUtilities.invokeLater(new Runnable() {
612 @Override
575613 public void run() {
576614 int numPanels = wizardComponents.length;
577 for (int i = 0; i < numPanels; i++)
615 for (int i = 0; i < numPanels; i++) {
578616 mainPanel.remove(wizardComponents[i]);
579 for (int i = 0; i < numPanels; i++)
617 }
618 for (int i = 0; i < numPanels; i++) {
580619 mainPanel.add(wizardComponents[i]);
620 }
581621 validate();
582622 repaint();
583623 }
596636
597637 int width = super.getWidth();
598638
599 if (width < 600)
639 if (width < 600) {
600640 width = 600;
641 }
601642 setSize(new Dimension(width, 500));
602643 setLocationRelativeTo(MainFrame.getInstance());
603644 }
605646 /**
606647 * @param foundModel
607648 */
608 public void setSourceDirecs(DefaultListModel foundModel) {
649 public void setSourceDirecs(DefaultListModel<String> foundModel) {
609650 for (int i = 0; i < foundModel.size(); i++) {
610651 this.sourceModel.addElement(foundModel.getElementAt(i));
611652 }
5656 // selected, and we get a ReOpenApplication event when user
5757 // switches back to Findbugs.
5858 javax.swing.SwingUtilities.invokeLater(new Runnable() {
59 @Override
5960 public void run() {
6061 mainApp.about();
6162 }
99100 // that needs to be called at runtime, and it can easily be done using
100101 // reflection (see MyApp.java)
101102 public static void registerMacOSXApplication(MainFrame inApp) {
102 if (mainApp != null)
103 if (mainApp != null) {
103104 throw new IllegalStateException("application already set");
105 }
104106
105107 mainApp = inApp;
106108
0 package edu.umd.cs.findbugs.gui2;
1
2 import java.awt.Cursor;
3 import java.awt.Font;
4 import java.awt.GridBagConstraints;
5 import java.awt.GridBagLayout;
6 import java.awt.Insets;
7 import java.awt.event.ActionEvent;
8 import java.awt.event.ActionListener;
9 import java.io.Serializable;
10 import java.net.MalformedURLException;
11 import java.net.URL;
12 import java.text.MessageFormat;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.Comparator;
17 import java.util.List;
18 import java.util.logging.Level;
19 import java.util.logging.Logger;
20
21 import javax.swing.JButton;
22 import javax.swing.JLabel;
23 import javax.swing.JOptionPane;
24 import javax.swing.JPanel;
25 import javax.swing.JTextPane;
26 import javax.swing.SwingUtilities;
27
28 import edu.umd.cs.findbugs.DetectorFactoryCollection;
29 import edu.umd.cs.findbugs.updates.PluginUpdateListener;
30 import edu.umd.cs.findbugs.updates.UpdateChecker;
31 import edu.umd.cs.findbugs.util.LaunchBrowser;
32
33 public class PluginUpdateDialog implements Serializable {
34 private static final Logger LOGGER = Logger.getLogger(PluginUpdateDialog.class.getName());
35
36 private static final int SOFTWARE_UPDATE_DIALOG_DELAY_MS = 5000;
37
38 public void showUpdateDialog(Collection<UpdateChecker.PluginUpdate> updates, boolean force) {
39 List<UpdateChecker.PluginUpdate> sortedUpdates = new ArrayList<UpdateChecker.PluginUpdate>();
40 UpdateChecker.PluginUpdate core = sortUpdates(updates, sortedUpdates);
41
42 if (DetectorFactoryCollection.instance().getUpdateChecker()
43 .updatesHaveBeenSeenBefore(sortedUpdates) && !force)
44 return;
45
46 String headline;
47 if (core != null && updates.size() >= 2)
48 headline = "FindBugs and some plugins have updates";
49 else if (updates.isEmpty())
50 headline = "FindBugs and all plugins are up to date!";
51 else if (core == null)
52 headline = "Some FindBugs plugins have updates";
53 else
54 headline = null;
55
56 final JPanel comp = new JPanel(new GridBagLayout());
57 GridBagConstraints gbc = new GridBagConstraints();
58 gbc.insets = new Insets(5, 5, 5, 5);
59 gbc.gridwidth = 3;
60 gbc.fill = GridBagConstraints.BOTH;
61 if (headline != null) {
62 JLabel headlineLabel = new JLabel(headline);
63 headlineLabel.setFont(headlineLabel.getFont().deriveFont(Font.BOLD, 24));
64 comp.add(headlineLabel, gbc);
65 }
66 if (!updates.isEmpty()) {
67 int i = 1;
68 for (final UpdateChecker.PluginUpdate update : sortedUpdates) {
69 gbc.gridy = ++i;
70 gbc.gridx = 1;
71 gbc.fill = GridBagConstraints.BOTH;
72 gbc.gridwidth = 1;
73 gbc.weightx = 1;
74 JLabel label = createPluginLabel(update);
75 comp.add(label, gbc);
76 gbc.weightx = 0;
77 gbc.gridx = 2;
78 String url = update.getUrl();
79 if (url != null && url.length() > 0) {
80 JButton button = createPluginUpdateButton(comp, update);
81 comp.add(button, gbc);
82 }
83 String msg = update.getMessage();
84 if (msg != null && msg.length() > 0) {
85 gbc.gridx = 1;
86 gbc.gridwidth = 3;
87 gbc.weightx = 1;
88 gbc.fill = GridBagConstraints.BOTH;
89 gbc.gridy = ++i;
90 JTextPane msgpane = createMessagePane(msg);
91 comp.add(msgpane, gbc);
92 }
93 }
94 }
95 JOptionPane.showMessageDialog(null, comp, "Software Updates", JOptionPane.INFORMATION_MESSAGE);
96 }
97
98 private JTextPane createMessagePane(String msg) {
99 JTextPane msgpane = new JTextPane();
100 msgpane.setEditable(false);
101 msgpane.setFocusable(false);
102 msgpane.setText(msg);
103 return msgpane;
104 }
105
106 private JLabel createPluginLabel(UpdateChecker.PluginUpdate update) {
107 String name;
108 if (update.getPlugin().isCorePlugin())
109 name = "FindBugs";
110 else
111 name = update.getPlugin().getShortDescription();
112 JLabel label = new JLabel(MessageFormat.format(
113 "<html><b>{0} {2}</b> is available<br><i><small>(currently installed: {1})",
114 name, update.getPlugin().getVersion(), update.getVersion()));
115 label.setFont(label.getFont().deriveFont(Font.PLAIN, label.getFont().getSize() + 4));
116 return label;
117 }
118
119
120 public PluginUpdateListener createListener() {
121 return new MyPluginUpdateListener();
122 }
123
124
125
126 private JButton createPluginUpdateButton(final JPanel comp, final UpdateChecker.PluginUpdate update) {
127 JButton button = new JButton("<html><u><font color=#0000ff>More info...");
128 button.setBorderPainted(false);
129 button.setOpaque(false);
130 button.setContentAreaFilled(false);
131 button.setBackground(comp.getBackground());
132 button.setToolTipText(update.getUrl());
133 button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
134 button.addActionListener(new ActionListener() {
135 public void actionPerformed(ActionEvent e) {
136 boolean failed;
137 String url = update.getUrl();
138 try {
139 failed = url == null || !LaunchBrowser.showDocument(new URL(url));
140 } catch (MalformedURLException e1) {
141 failed = true;
142 }
143 if (failed)
144 JOptionPane.showMessageDialog(comp, "Could not open URL " + url);
145 }
146 });
147 return button;
148 }
149
150 private UpdateChecker.PluginUpdate sortUpdates(Collection<UpdateChecker.PluginUpdate> updates,
151 List<UpdateChecker.PluginUpdate> sorted) {
152 UpdateChecker.PluginUpdate core = null;
153 for (UpdateChecker.PluginUpdate update : updates) {
154 if (update.getPlugin().isCorePlugin())
155 core = update;
156 else
157 sorted.add(update);
158 }
159 // sort by name
160 Collections.sort(sorted, new Comparator<UpdateChecker.PluginUpdate>() {
161 public int compare(UpdateChecker.PluginUpdate o1, UpdateChecker.PluginUpdate o2) {
162 return o1.getPlugin().getShortDescription().compareTo(o2.getPlugin().getShortDescription());
163 }
164 });
165 // place core plugin first, if present
166 if (core != null)
167 sorted.add(0, core);
168 return core;
169 }
170
171 private class MyPluginUpdateListener implements PluginUpdateListener {
172 public void pluginUpdateCheckComplete(final Collection<UpdateChecker.PluginUpdate> updates, final boolean force) {
173 if (updates.isEmpty() && !force)
174 return;
175
176 if (force)
177 showUpdateDialogInSwingThread(updates, force);
178 else
179 // wait 5 seconds before showing dialog
180 edu.umd.cs.findbugs.util.Util.runInDameonThread(new Runnable() {
181 public void run() {
182 try {
183 Thread.sleep(SOFTWARE_UPDATE_DIALOG_DELAY_MS);
184 showUpdateDialogInSwingThread(updates, force);
185 } catch (InterruptedException e) {
186 LOGGER.log(Level.FINE, "Software update dialog thread interrupted", e);
187 }
188 }
189 }, "Software Update Dialog");
190 }
191
192 private void showUpdateDialogInSwingThread(final Collection<UpdateChecker.PluginUpdate> updates, final boolean force) {
193 SwingUtilities.invokeLater(new Runnable() {
194 public void run() {
195 showUpdateDialog(updates, force);
196 }
197 });
198 }
199 }
0 package edu.umd.cs.findbugs.gui2;
1
2 import java.awt.Cursor;
3 import java.awt.Font;
4 import java.awt.GridBagConstraints;
5 import java.awt.GridBagLayout;
6 import java.awt.Insets;
7 import java.awt.event.ActionEvent;
8 import java.awt.event.ActionListener;
9 import java.io.Serializable;
10 import java.net.MalformedURLException;
11 import java.net.URL;
12 import java.text.MessageFormat;
13 import java.util.ArrayList;
14 import java.util.Collection;
15 import java.util.Collections;
16 import java.util.Comparator;
17 import java.util.List;
18 import java.util.logging.Level;
19 import java.util.logging.Logger;
20
21 import javax.swing.JButton;
22 import javax.swing.JLabel;
23 import javax.swing.JOptionPane;
24 import javax.swing.JPanel;
25 import javax.swing.JTextPane;
26 import javax.swing.SwingUtilities;
27
28 import edu.umd.cs.findbugs.DetectorFactoryCollection;
29 import edu.umd.cs.findbugs.updates.PluginUpdateListener;
30 import edu.umd.cs.findbugs.updates.UpdateChecker;
31 import edu.umd.cs.findbugs.util.LaunchBrowser;
32
33 public class PluginUpdateDialog implements Serializable {
34 private static final Logger LOGGER = Logger.getLogger(PluginUpdateDialog.class.getName());
35
36 private static final int SOFTWARE_UPDATE_DIALOG_DELAY_MS = 5000;
37
38 public void showUpdateDialog(Collection<UpdateChecker.PluginUpdate> updates, boolean force) {
39 List<UpdateChecker.PluginUpdate> sortedUpdates = new ArrayList<UpdateChecker.PluginUpdate>();
40 UpdateChecker.PluginUpdate core = sortUpdates(updates, sortedUpdates);
41
42 if (DetectorFactoryCollection.instance().getUpdateChecker()
43 .updatesHaveBeenSeenBefore(sortedUpdates) && !force) {
44 return;
45 }
46
47 String headline;
48 if (core != null && updates.size() >= 2) {
49 headline = "FindBugs and some plugins have updates";
50 } else if (updates.isEmpty()) {
51 headline = "FindBugs and all plugins are up to date!";
52 } else if (core == null) {
53 headline = "Some FindBugs plugins have updates";
54 } else {
55 headline = null;
56 }
57
58 final JPanel comp = new JPanel(new GridBagLayout());
59 GridBagConstraints gbc = new GridBagConstraints();
60 gbc.insets = new Insets(5, 5, 5, 5);
61 gbc.gridwidth = 3;
62 gbc.fill = GridBagConstraints.BOTH;
63 if (headline != null) {
64 JLabel headlineLabel = new JLabel(headline);
65 headlineLabel.setFont(headlineLabel.getFont().deriveFont(Font.BOLD, 24));
66 comp.add(headlineLabel, gbc);
67 }
68 if (!updates.isEmpty()) {
69 int i = 1;
70 for (final UpdateChecker.PluginUpdate update : sortedUpdates) {
71 gbc.gridy = ++i;
72 gbc.gridx = 1;
73 gbc.fill = GridBagConstraints.BOTH;
74 gbc.gridwidth = 1;
75 gbc.weightx = 1;
76 JLabel label = createPluginLabel(update);
77 comp.add(label, gbc);
78 gbc.weightx = 0;
79 gbc.gridx = 2;
80 String url = update.getUrl();
81 if (url != null && url.length() > 0) {
82 JButton button = createPluginUpdateButton(comp, update);
83 comp.add(button, gbc);
84 }
85 String msg = update.getMessage();
86 if (msg != null && msg.length() > 0) {
87 gbc.gridx = 1;
88 gbc.gridwidth = 3;
89 gbc.weightx = 1;
90 gbc.fill = GridBagConstraints.BOTH;
91 gbc.gridy = ++i;
92 JTextPane msgpane = createMessagePane(msg);
93 comp.add(msgpane, gbc);
94 }
95 }
96 }
97 JOptionPane.showMessageDialog(null, comp, "Software Updates", JOptionPane.INFORMATION_MESSAGE);
98 }
99
100 private JTextPane createMessagePane(String msg) {
101 JTextPane msgpane = new JTextPane();
102 msgpane.setEditable(false);
103 msgpane.setFocusable(false);
104 msgpane.setText(msg);
105 return msgpane;
106 }
107
108 private JLabel createPluginLabel(UpdateChecker.PluginUpdate update) {
109 String name;
110 if (update.getPlugin().isCorePlugin()) {
111 name = "FindBugs";
112 } else {
113 name = update.getPlugin().getShortDescription();
114 }
115 JLabel label = new JLabel(MessageFormat.format(
116 "<html><b>{0} {2}</b> is available<br><i><small>(currently installed: {1})",
117 name, update.getPlugin().getVersion(), update.getVersion()));
118 label.setFont(label.getFont().deriveFont(Font.PLAIN, label.getFont().getSize() + 4));
119 return label;
120 }
121
122
123 public PluginUpdateListener createListener() {
124 return new MyPluginUpdateListener();
125 }
126
127
128
129 private JButton createPluginUpdateButton(final JPanel comp, final UpdateChecker.PluginUpdate update) {
130 JButton button = new JButton("<html><u><font color=#0000ff>More info...");
131 button.setBorderPainted(false);
132 button.setOpaque(false);
133 button.setContentAreaFilled(false);
134 button.setBackground(comp.getBackground());
135 button.setToolTipText(update.getUrl());
136 button.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
137 button.addActionListener(new ActionListener() {
138 @Override
139 public void actionPerformed(ActionEvent e) {
140 boolean failed;
141 String url = update.getUrl();
142 try {
143 failed = url == null || !LaunchBrowser.showDocument(new URL(url));
144 } catch (MalformedURLException e1) {
145 failed = true;
146 }
147 if (failed) {
148 JOptionPane.showMessageDialog(comp, "Could not open URL " + url);
149 }
150 }
151 });
152 return button;
153 }
154
155 private UpdateChecker.PluginUpdate sortUpdates(Collection<UpdateChecker.PluginUpdate> updates,
156 List<UpdateChecker.PluginUpdate> sorted) {
157 UpdateChecker.PluginUpdate core = null;
158 for (UpdateChecker.PluginUpdate update : updates) {
159 if (update.getPlugin().isCorePlugin()) {
160 core = update;
161 } else {
162 sorted.add(update);
163 }
164 }
165 // sort by name
166 Collections.sort(sorted, new Comparator<UpdateChecker.PluginUpdate>() {
167 @Override
168 public int compare(UpdateChecker.PluginUpdate o1, UpdateChecker.PluginUpdate o2) {
169 return o1.getPlugin().getShortDescription().compareTo(o2.getPlugin().getShortDescription());
170 }
171 });
172 // place core plugin first, if present
173 if (core != null) {
174 sorted.add(0, core);
175 }
176 return core;
177 }
178
179 private class MyPluginUpdateListener implements PluginUpdateListener {
180 @Override
181 public void pluginUpdateCheckComplete(final Collection<UpdateChecker.PluginUpdate> updates, final boolean force) {
182 if (updates.isEmpty() && !force) {
183 return;
184 }
185
186 if (force) {
187 showUpdateDialogInSwingThread(updates, force);
188 } else {
189 // wait 5 seconds before showing dialog
190 edu.umd.cs.findbugs.util.Util.runInDameonThread(new Runnable() {
191 @Override
192 public void run() {
193 try {
194 Thread.sleep(SOFTWARE_UPDATE_DIALOG_DELAY_MS);
195 showUpdateDialogInSwingThread(updates, force);
196 } catch (InterruptedException e) {
197 LOGGER.log(Level.FINE, "Software update dialog thread interrupted", e);
198 }
199 }
200 }, "Software Update Dialog");
201 }
202 }
203
204 private void showUpdateDialogInSwingThread(final Collection<UpdateChecker.PluginUpdate> updates, final boolean force) {
205 SwingUtilities.invokeLater(new Runnable() {
206 @Override
207 public void run() {
208 showUpdateDialog(updates, force);
209 }
210 });
211 }
212 }
200213 }
4747 import java.util.HashMap;
4848 import java.util.List;
4949 import java.util.Map;
50 import java.util.Objects;
5051 import java.util.logging.Level;
5152 import java.util.logging.Logger;
5253
7778
7879 import edu.umd.cs.findbugs.BugCollection;
7980 import edu.umd.cs.findbugs.Plugin;
81 import edu.umd.cs.findbugs.PluginException;
8082 import edu.umd.cs.findbugs.Project;
8183 import edu.umd.cs.findbugs.cloud.CloudPlugin;
8284 import edu.umd.cs.findbugs.filter.Filter;
9597
9698 private static PreferencesFrame instance;
9799
98 private final CheckBoxList filterCheckBoxList = new CheckBoxList();
100 private final CheckBoxList<MatchBox> filterCheckBoxList = new CheckBoxList<>();
99101
100102 // Variables for Properties tab.
101103 private JTextField tabTextField;
117119 }
118120
119121 public static PreferencesFrame getInstance() {
120 if (instance == null)
122 if (instance == null) {
121123 instance = new PreferencesFrame();
124 }
122125
123126 return instance;
124127 }
126129 private boolean pluginsAdded = false;
127130 private final JPanel filterPane;
128131 private final JTabbedPane mainTabPane;
129
132
130133 public void showFilterPane() {
131134 mainTabPane.setSelectedComponent(filterPane);
132
135
133136 }
134137 private PreferencesFrame() {
135138 setTitle(edu.umd.cs.findbugs.L10N.getLocalString("dlg.fil_sup_ttl", "Preferences"));
156159 bottom.setLayout(new BoxLayout(bottom, BoxLayout.X_AXIS));
157160 bottom.add(Box.createHorizontalGlue());
158161 bottom.add(new JButton(new AbstractAction(edu.umd.cs.findbugs.L10N.getLocalString("pref.close", "Close")) {
162 @Override
159163 public void actionPerformed(ActionEvent evt) {
160164 handleWindowClose();
161165 PreferencesFrame.this.setVisible(false);
188192
189193 private void handleWindowClose() {
190194 TreeModel bt = (MainFrame.getInstance().getTree().getModel());
191 if (bt instanceof BugTreeModel)
195 if (bt instanceof BugTreeModel) {
192196 ((BugTreeModel) bt).checkSorter();
197 }
193198 Project project = getCurrentProject();
194199
195200 boolean changed = pluginsAdded;
202207 if (project != null) {
203208 Boolean newSetting = enabled.project;
204209 Boolean existingSetting = project.getPluginStatus(plugin);
205 if (existingSetting != newSetting && (newSetting == null || !newSetting.equals(existingSetting))) {
210 boolean sameSettings = Objects.equals(existingSetting, newSetting);
211 if (!sameSettings) {
206212 project.setPluginStatusTrinary(plugin.getPluginId(), newSetting);
207213 changed = true;
208214 }
209215 }
210 if (enabled.global)
216 if (enabled.global) {
211217 enabledPlugins.add(plugin.getPluginId());
212 else
218 } else {
213219 disabledPlugins.add(plugin.getPluginId());
220 }
214221 if (plugin.isGloballyEnabled() != enabled.global) {
215222 plugin.setGloballyEnabled(enabled.global);
216223 changed = true;
247254
248255 south.add(addButton);
249256 addButton.addActionListener(new ActionListener() {
257 @Override
250258 public void actionPerformed(ActionEvent e) {
251259 JFileChooser chooser = new JFileChooser();
252260 chooser.addChoosableFileFilter(new FileFilter() {
256264 return "FindBugs Plugin (*.jar)";
257265 }
258266
259 @SuppressWarnings({"RedundantIfStatement"})
260267 @Override
261268 public boolean accept(File f) {
262 if (f.isDirectory())
269 if (f.isDirectory()) {
263270 return true;
264 if (!f.canRead())
271 }
272 if (!f.canRead()) {
265273 return false;
266 if (f.getName().endsWith(".jar"))
274 }
275 if (f.getName().endsWith(".jar")) {
267276 return true;
277 }
268278 return false;
269279 }
270280 });
288298 pluginsAdded = true;
289299 rebuildPluginCheckboxes();
290300
291 } catch (Exception e1) {
301 } catch (PluginException | MalformedURLException e1) {
292302 LOGGER.log(Level.WARNING, "Could not load " + f.getPath(), e1);
293303 JOptionPane.showMessageDialog(PreferencesFrame.this, "Could not load " + f.getPath()
294304 + "\n\n"
307317 EnabledSettings isEnabled(@CheckForNull Project project, Plugin plugin) {
308318 return new EnabledSettings(plugin.isGloballyEnabled(), project == null ? null : project.getPluginStatus(plugin));
309319 }
310
320
311321 private void rebuildPluginCheckboxes() {
312322 Project currentProject = getCurrentProject();
313323 pluginPanelCenter.removeAll();
316326 g.fill = GridBagConstraints.NONE;
317327 g.insets = new Insets(5,5,5,5);
318328 g.gridy = 0;
319 // g.anchor = GridBagConstraints.WEST;
320 // g.gridx = 1;
321 // pluginPanelCenter.add(new JLabel("Global Setting"), g);
329 // g.anchor = GridBagConstraints.WEST;
330 // g.gridx = 1;
331 // pluginPanelCenter.add(new JLabel("Global Setting"), g);
322332 g.anchor = GridBagConstraints.CENTER;
323333 g.gridx = 2;
324334 pluginPanelCenter.add(new JLabel("Project Setting"), g);
326336 Collection<Plugin> plugins = Plugin.getAllPlugins();
327337 int added = 0;
328338 for (final Plugin plugin : plugins) {
329 if (plugin.isCorePlugin())
339 if (plugin.isCorePlugin()) {
330340 continue;
341 }
331342 String text = plugin.getShortDescription();
332343 String id = plugin.getPluginId();
333 if (text == null)
344 if (text == null) {
334345 text = id;
346 }
335347 final URL url = plugin.getPluginLoader().getURL();
336348 String pluginUrlStr = url.toExternalForm();
337349 if ("file".equals(url.getProtocol())) {
348360 final JCheckBox checkGlobal = new JCheckBox(text, enabled.global);
349361 final boolean cannotDisable = plugin.isEnabledByDefault() && plugin.cannotDisable();
350362 if (cannotDisable) {
351 if (!enabled.global)
363 if (!enabled.global) {
352364 throw new IllegalStateException(
353365 plugin.getPluginId() + " is enabled by default and cannot be disabled, but is disabled");
366 }
354367 checkGlobal.setEnabled(false);
355368 } else {
356369 checkGlobal.addMouseListener(new MouseAdapter() {
357
358 @Override
359 public void mousePressed(MouseEvent e) {
360 if (SwingUtilities.isRightMouseButton(e)) {
361 JPopupMenu menu = new JPopupMenu();
362 JMenuItem item = new JMenuItem("Uninstall " + plugin.getShortDescription() + "...");
363 item.addActionListener(new UninstallClickListener(plugin, url));
364 menu.add(item);
365 menu.show(checkGlobal, e.getX(), e.getY());
366 }
367 }
368 });
370
371 @Override
372 public void mousePressed(MouseEvent e) {
373 if (SwingUtilities.isRightMouseButton(e)) {
374 JPopupMenu menu = new JPopupMenu();
375 JMenuItem item = new JMenuItem("Uninstall " + plugin.getShortDescription() + "...");
376 item.addActionListener(new UninstallClickListener(plugin, url));
377 menu.add(item);
378 menu.show(checkGlobal, e.getX(), e.getY());
379 }
380 }
381 });
369382 checkGlobal.addActionListener(new ActionListener() {
383 @Override
370384 public void actionPerformed(ActionEvent e) {
371385 pluginEnabledStatus.get(plugin).global = checkGlobal.isSelected();
372386 }
374388 }
375389 checkGlobal.setVerticalTextPosition(SwingConstants.TOP);
376390 String longText = plugin.getDetailedDescription();
377 if (longText != null)
391 if (longText != null) {
378392 checkGlobal.setToolTipText("<html>" + longText + "</html>");
393 }
379394 pluginEnabledStatus.put(plugin, enabled);
380
395
381396 GridBagConstraints gbc = new GridBagConstraints();
382397 gbc.fill = GridBagConstraints.BOTH;
383398 gbc.weightx = 1;
388403 pluginPanelCenter.add(checkGlobal, gbc);
389404
390405 if (currentProject != null && !cannotDisable) {
391 final JComboBox combo = new WideComboBox(new Object[]{"DEFAULT", "DISABLED", "ENABLED"});
392 if (enabled.project == null) combo.setSelectedIndex(0);
393 else combo.setSelectedIndex(enabled.project ? 2 : 1);
406 final JComboBox<String> combo = new WideComboBox<>(new String[]{"DEFAULT", "DISABLED", "ENABLED"});
407 if (enabled.project == null) {
408 combo.setSelectedIndex(0);
409 } else {
410 combo.setSelectedIndex(enabled.project ? 2 : 1);
411 }
394412 combo.setRenderer(new DefaultListCellRenderer() {
395413 @Override
396414 public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
397415 if (index == -1) {
398 if (value.equals("DEFAULT")) value = "Default";
399 if (value.equals("DISABLED")) value = "Disabled";
400 if (value.equals("ENABLED")) value = "Enabled";
416 if (value.equals("DEFAULT")) {
417 value = "Default";
418 }
419 if (value.equals("DISABLED")) {
420 value = "Disabled";
421 }
422 if (value.equals("ENABLED")) {
423 value = "Enabled";
424 }
401425 } else {
402 if (value.equals("DEFAULT")) value = "Default (use global setting)";
403 if (value.equals("DISABLED")) value = "Disabled for this project";
404 if (value.equals("ENABLED")) value = "Enabled for this project";
426 if (value.equals("DEFAULT")) {
427 value = "Default (use global setting)";
428 }
429 if (value.equals("DISABLED")) {
430 value = "Disabled for this project";
431 }
432 if (value.equals("ENABLED")) {
433 value = "Enabled for this project";
434 }
405435 }
406436 return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
407437 }
408438 });
409439 combo.addActionListener(new ActionListener() {
440 @Override
410441 public void actionPerformed(ActionEvent e) {
411442 Boolean[] array = { null, false, true };
412443 int i = combo.getSelectedIndex();
413444 if (i < 0 || i > 2)
445 {
414446 return; // ??
447 }
415448 pluginEnabledStatus.get(plugin).project = array[i];
416449 }
417450 });
445478
446479 private void addField(JPanel p, GridBagConstraints c, int y, String lbl, JComponent field) {
447480 c.gridy = y;
448 JLabel l = new JLabel(lbl, JLabel.TRAILING);
481 JLabel l = new JLabel(lbl, SwingConstants.TRAILING);
449482 l.setLabelFor(field);
450483 c.anchor = GridBagConstraints.LINE_END;
451484 c.gridx = 0;
459492 JPanel mainPanel = new JPanel(new GridBagLayout());
460493 GridBagConstraints c = new GridBagConstraints();
461494 c.ipadx = c.ipady = 5;
462
495
463496
464497 float currFS = Driver.getFontSize();
465498
466499 tabTextField = new JTextField(Integer.toString(GUISaveState.getInstance().getTabSize()));
467500 tabTextField.setPreferredSize(new Dimension((int) (currFS * 4), (int) (currFS * 2)));
468501 addField(mainPanel, c, 0, "Tab size", tabTextField);
469
502
470503 fontTextField = new JTextField(Float.toString(GUISaveState.getInstance().getFontSize()));
471504 fontTextField.setPreferredSize(new Dimension((int) (currFS * 6), (int) (currFS * 2)));
472505 addField(mainPanel, c, 1, "Font size", fontTextField);
479512
480513 JPanel bottomPanel = new JPanel();
481514 bottomPanel.add(new JButton(new AbstractAction("Apply") {
515 @Override
482516 public void actionPerformed(ActionEvent evt) {
483517 changeTabSize();
484518 changeFontSize();
487521 }));
488522
489523 bottomPanel.add(new JButton(new AbstractAction("Reset") {
524 @Override
490525 public void actionPerformed(ActionEvent evt) {
491526 resetPropertiesPane();
492527 }
501536
502537 public PluginWithDescription(String description, CloudPlugin plugin) {
503538 this.description = description;
504 this.plugin = plugin;
539 // this.plugin = plugin;
505540 }
506541 final String description;
507 final CloudPlugin plugin;
542 // final CloudPlugin plugin;
543
508544 @Override
509545 public String toString() {
510546 return description;
511547 }
512
548
513549 }
514550 private void changeTabSize() {
515551 int tabSize;
518554 tabSize = Integer.decode(tabTextField.getText());
519555 } catch (NumberFormatException exc) {
520556 JOptionPane.showMessageDialog(instance, "Error in tab size field.", "Tab Size Error",
521 JOptionPane.INFORMATION_MESSAGE);
557 JOptionPane.INFORMATION_MESSAGE);
522558 return;
523559 }
524560
631667 gbc.weighty = 0;
632668 filterPanel.add(addButton, gbc);
633669 addButton.addActionListener(new ActionListener() {
670 @Override
634671 public void actionPerformed(ActionEvent evt) {
635672 NewFilterFrame.open();
636673 }
640677 gbc.insets = new Insets(5, 0, 0, 0);
641678 filterPanel.add(removeButton, gbc);
642679 removeButton.addActionListener(new ActionListener() {
680 @Override
643681 public void actionPerformed(ActionEvent evt) {
644682 Object[] selected = filterCheckBoxList.getSelectedValues();
645 if (selected.length == 0)
683 if (selected.length == 0) {
646684 return;
685 }
647686 for (Object o : selected) {
648687 MatchBox box = (MatchBox) o;
649688 MainFrame.getInstance().getProject().getSuppressionFilter().removeChild(box.getMatcher());
658697 gbc.insets = new Insets(5, 0, 0, 0);
659698 filterPanel.add(removeAllButton, gbc);
660699 removeAllButton.addActionListener(new ActionListener() {
700 @Override
661701 public void actionPerformed(ActionEvent evt) {
662702 boolean needsRebuild = false;
663703 Filter suppressionFilter = MainFrame.getInstance().getProject().getSuppressionFilter();
664 if (!suppressionFilter.isEmpty())
704 if (!suppressionFilter.isEmpty()) {
665705 needsRebuild = true;
706 }
666707 suppressionFilter.clear();
667708
668 if (needsRebuild)// TODO This will rebuild even if all the
669 // filters being cleared were disabled
709 if (needsRebuild) {
710 // filters being cleared were disabled
670711 FilterActivity.notifyListeners(FilterListener.Action.UNFILTERING, null);
712 }
671713 MainFrame.getInstance().setProjectChanged(true);
672714 updateFilterPanel();
673715 }
700742 for (final Matcher m : f.getChildren()) {
701743 MatchBox box = new MatchBox(m.toString(), m);
702744 box.addItemListener(new ItemListener() {
745 @Override
703746 public void itemStateChanged(ItemEvent evt) {
704747 boolean isSelected = ((JCheckBox) evt.getSource()).isSelected();
705748 boolean wasSelected = f.isEnabled(m);
706 if (isSelected == wasSelected)
749 if (isSelected == wasSelected) {
707750 return;
751 }
708752 f.setEnabled(m, isSelected);
709753 updateFilters(isSelected);
710754
733777 this.url = url;
734778 }
735779
780 @Override
736781 public void actionPerformed(ActionEvent e) {
737782 int result = JOptionPane.showOptionDialog(PreferencesFrame.this,
738783 "Are you sure you want to uninstall " + plugin.getShortDescription() + "?" +
739784 "\n\nNo files will be deleted from your computer.", "",
740 JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null,
741 new Object[]{"Uninstall", "Cancel"}, "Cancel");
785 JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE, null,
786 new Object[]{"Uninstall", "Cancel"}, "Cancel");
742787 if (result == 0) {
743788 if (!GUISaveState.getInstance().removeCustomPlugin(url)) {
744789 if ("file".equals(url.getProtocol())) {
756801 "The plugin could not be uninstalled automatically.\n\n" +
757802 "You can try to delete this plugin manually: \n"
758803 + path + "\n\n(This path has been copied to your clipboard)",
759 "Error", JOptionPane.ERROR_MESSAGE);
804 "Error", JOptionPane.ERROR_MESSAGE);
760805 } else {
761806 JOptionPane.showMessageDialog(PreferencesFrame.this,
762807 "This plugin is not actually in the list of plugins...\n" +
764809 + url.toExternalForm()
765810 + "\n\nPlugin URL's:\n" +
766811 GUISaveState.getInstance().getCustomPlugins(),
767 "Error", JOptionPane.ERROR_MESSAGE);
812 "Error", JOptionPane.ERROR_MESSAGE);
768813 }
769814 } else {
770815 JOptionPane.showMessageDialog(PreferencesFrame.this,
5454 }
5555
5656 public static synchronized ProjectSettings getInstance() {
57 if (instance == null)
57 if (instance == null) {
5858 instance = new ProjectSettings();
59 }
5960 return instance;
6061 }
6162
6263 /**
6364 * The list of all defined filters
6465 */
65 private ArrayList<FilterMatcher> filters;
66 private final ArrayList<FilterMatcher> filters;
6667
6768 /**
6869 * The CompoundMatcher enveloping all enabled matchers.
6970 */
70 private CompoundMatcher allMatchers;
71 private final CompoundMatcher allMatchers;
7172
7273 /**
7374 * Max number of previous comments stored.
8081 PreferencesFrame.getInstance().updateFilterPanel();
8182
8283 } catch (ClassNotFoundException e) {
83 if (MainFrame.GUI2_DEBUG)
84 if (MainFrame.GUI2_DEBUG) {
8485 System.err.println("Error in deserializing Settings:");
86 }
8587 Debug.println(e);
8688 } catch (IOException e) {
87 if (MainFrame.GUI2_DEBUG)
89 if (MainFrame.GUI2_DEBUG) {
8890 System.err.println("IO error in deserializing Settings:");
91 }
8992 Debug.println(e);
9093 instance = newInstance();
9194 } finally {
101104 try {
102105 new ObjectOutputStream(out).writeObject(this);
103106 } catch (IOException e) {
104 if (MainFrame.GUI2_DEBUG)
107 if (MainFrame.GUI2_DEBUG) {
105108 System.err.println("Error serializing Settings:");
109 }
106110 Debug.println(e);
107111 } finally {
108112 try {
117121 public void addFilter(FilterMatcher filter) {
118122 filters.add(filter);
119123 allMatchers.add(filter);
120 if (!(filter instanceof StackedFilterMatcher))
124 if (!(filter instanceof StackedFilterMatcher)) {
121125 FilterActivity.notifyListeners(FilterListener.Action.FILTERING, null);
122 else {
126 } else {
123127 StackedFilterMatcher theSame = (StackedFilterMatcher) filter;
124128 FilterMatcher[] filtersInStack = theSame.getFilters();
125129 ArrayList<Sortables> order = MainFrame.getInstance().getSorter().getOrder();
142146 ArrayList<String> finalPath = new ArrayList<String>();
143147 for (int x = 0; x < almostPath.size(); x++) {
144148 Sortables s = almostPathSortables.get(x);
145 if (MainFrame.getInstance().getSorter().getOrderBeforeDivider().contains(s))
149 if (MainFrame.getInstance().getSorter().getOrderBeforeDivider().contains(s)) {
146150 finalPath.add(almostPath.get(x));
151 }
147152 }
148153 BugTreeModel model = (MainFrame.getInstance().getBugTreeModel());
149154 try {
162167 }
163168
164169 public void addFilters(FilterMatcher[] newFilters) {
165 for (FilterMatcher i : newFilters)
170 for (FilterMatcher i : newFilters) {
166171 if (!filters.contains(i)) {
167172 filters.add(i);
168173 allMatchers.add(i);
172177 // FIXME Do I need to do this for allMatchers too? Or are the
173178 // filters all the same, with both just holding references?
174179 }
180 }
175181 FilterActivity.notifyListeners(FilterListener.Action.FILTERING, null);
176182 PreferencesFrame.getInstance().updateFilterPanel();
177183 MainFrame.getInstance().updateStatusBar();
198204
199205 /**
200206 * Sets the maximum number of previous comments stored.
201 *
207 *
202208 * @param num
203209 */
204210 public void setMaxSizeOfPreviousComments(int num) {
3636 * This is the properties dialog of the GUI. It allows the user to set the size
3737 * of the tabs and font size. If the user changes the font size they will be
3838 * told to restart the computer before the new size takes affect.
39 *
39 *
4040 * @author Kristin Stephens
4141 */
4242 public class PropertiesDialog extends FBDialog {
4343 private static PropertiesDialog instance;
4444
45 private JTextField tabTextField;
45 private final JTextField tabTextField;
4646
47 private JTextField fontTextField;
47 private final JTextField fontTextField;
4848
4949 public static PropertiesDialog getInstance() {
50 if (instance == null)
50 if (instance == null) {
5151 instance = new PropertiesDialog();
52 }
5253 return instance;
5354 }
5455
6869
6970 JPanel bottomPanel = new JPanel();
7071 bottomPanel.add(new JButton(new AbstractAction("Apply") {
72 @Override
7173 public void actionPerformed(ActionEvent evt) {
7274 if (Integer.decode(tabTextField.getText()).intValue() != GUISaveState.getInstance().getTabSize()) {
7375 GUISaveState.getInstance().setTabSize(Integer.decode(tabTextField.getText()).intValue());
7476 MainFrame.getInstance().getSourceCodeDisplayer().clearCache();
7577 MainFrame.getInstance().syncBugInformation(); // This causes
76 // the GUI to
77 // redisplay
78 // the current
79 // code
78 // the GUI to
79 // redisplay
80 // the current
81 // code
8082 }
8183
8284 if (Float.parseFloat(fontTextField.getText()) != GUISaveState.getInstance().getFontSize()) {
8991 }));
9092
9193 bottomPanel.add(new JButton(new AbstractAction("Reset") {
94 @Override
9295 public void actionPerformed(ActionEvent evt) {
9396 tabTextField.setText(Integer.toString(GUISaveState.getInstance().getTabSize()));
9497 fontTextField.setText(Float.toString(GUISaveState.getInstance().getFontSize()));
104107 addWindowListener(new WindowAdapter() {
105108 @Override
106109 public void windowDeactivated(WindowEvent e) {
107 if (Integer.decode(tabTextField.getText()).intValue() != GUISaveState.getInstance().getTabSize())
110 if (Integer.decode(tabTextField.getText()).intValue() != GUISaveState.getInstance().getTabSize()) {
108111 tabTextField.setText(Integer.toString(GUISaveState.getInstance().getTabSize()));
112 }
109113
110114 if (Float.parseFloat(fontTextField.getText()) != GUISaveState.getInstance().getFontSize()) {
111115 fontTextField.setText(Float.toString(GUISaveState.getInstance().getFontSize()));
5151 }
5252
5353 LimitedArrayList<File> recentFiles;// Originally called recentProjects
54 // before merge two lists into one.
54 // before merge two lists into one.
5555
5656 JMenu recentMenu;
5757
7171 for (File f : recentFiles) {
7272 Debug.println(f);
7373 if (!f.exists()) {
74 if (MainFrame.GUI2_DEBUG)
74 if (MainFrame.GUI2_DEBUG) {
7575 System.err.println("a recent project was not found, removing it from menu");
76 }
7677 continue;
7778 }
7879
8384
8485 /**
8586 * Adds a file to the list of recent files used.
86 *
87 *
8788 * @param f
8889 */
8990 public void addRecentFile(final File f) {
90 if (f != null)
91 if (f != null) {
9192 recentFiles.add(f);
93 }
9294
9395 makeRecentMenu();
9496 }
3030 return FindBugsAnalysisFileFilter.INSTANCE;
3131 case HTML_OUTPUT:
3232 return FindBugsHtmlFileFilter.INSTANCE;
33 case FBP_FILE:
33 case FBP_FILE:
3434 return FindBugsFBPFileFilter.INSTANCE;
3535 case FBA_FILE:
3636 return FindBugsFBAFileFilter.INSTANCE;
4141
4242 public boolean isValid(File f) {
4343
44 if (f.isDirectory())
44 if (f.isDirectory()) {
4545 return false;
46 }
4647 FindBugsFileFilter filter = getFilter();
4748 return filter.accept(f);
4849 }
6667 public static SaveType forFile(File f) {
6768 String extension = Util.getFileExtension(f);
6869
69 if (extension.equals("html") || extension.equals("htm"))
70 if ("html".equals(extension) || "htm".equals(extension)) {
7071 return HTML_OUTPUT;
71 if (extension.equals("fba"))
72 }
73 if ("fba".equals(extension)) {
7274 return FBA_FILE;
73 if (extension.equals("fbp"))
75 }
76 if ("fbp".equals(extension)) {
7477 return FBP_FILE;
75 if (extension.equals("xml"))
78 }
79 if ("xml".equals(extension)) {
7680 return XML_ANALYSIS;
77 if (extension.equals("html"))
81 }
82 if ("html".equals(extension)) {
7883 return XML_ANALYSIS;
79 if (f.getName().toLowerCase().endsWith("xml.gz"))
84 }
85 if (f.getName().toLowerCase().endsWith("xml.gz")) {
8086 return XML_ANALYSIS;
87 }
8188 return NOT_KNOWN;
8289 }
8390 }
3131 mySortable = theSortable;
3232 }
3333
34 @Override
3435 public int compare(String one, String two) {
3536 return mySortable.compare(one, two);
3637 }
4343 * in BugInstances This is the preferred way for getting the information out of
4444 * a BugInstance and formatting it for display It also has the comparators for
4545 * the different types of data
46 *
46 *
4747 * @author Reuven
4848 */
4949
9595 public String formatValue(String value) {
9696 int seqNum = Integer.parseInt(value);
9797 BugCollection bugCollection = MainFrame.getInstance().getBugCollection();
98 if (bugCollection == null)
98 if (bugCollection == null) {
9999 return "--";
100 }
100101 AppVersion appVersion = bugCollection.getAppVersionFromSequenceNumber(seqNum);
101102 if (appVersion != null) {
102103 String timestamp = new Timestamp(appVersion.getTimestamp()).toString();
103104 return appVersion.getReleaseName() + " (" + timestamp.substring(0, timestamp.indexOf(' ')) + ")";
104 } else
105 } else {
105106 return "#" + seqNum;
107 }
106108 }
107109
108110 @Override
132134 @Override
133135 public String formatValue(String value) {
134136 // System.out.println("Formatting last version value");
135 if (value.equals("-1"))
137 if ("-1".equals(value)) {
136138 return "";
139 }
137140 int seqNum = Integer.parseInt(value);
138141 BugCollection bugCollection = MainFrame.getInstance().getBugCollection();
139 if (bugCollection == null)
142 if (bugCollection == null) {
140143 return "--";
144 }
141145 AppVersion appVersion = bugCollection.getAppVersionFromSequenceNumber(seqNum);
142146 if (appVersion != null) {
143147 String timestamp = new Timestamp(appVersion.getTimestamp()).toString();
144148 return appVersion.getReleaseName() + " (" + timestamp.substring(0, timestamp.indexOf(' ')) + ")";
145 }
146 else return "#" + seqNum;
147 }
148
149 @Override
150 public int compare(String one, String two) {
151 if (one.equals(two))
149 } else {
150 return "#" + seqNum;
151 }
152 }
153
154 @Override
155 public int compare(String one, String two) {
156 if (one.equals(two)) {
152157 return 0;
158 }
153159
154160 // Numerical (except that -1 is last)
155161 int first = Integer.parseInt(one);
156162 int second = Integer.parseInt(two);
157 if (first == second)
163 if (first == second) {
158164 return 0;
159 if (first < 0)
165 }
166 if (first < 0) {
160167 return 1;
161 if (second < 0)
168 }
169 if (second < 0) {
162170 return -1;
163 if (first < second)
171 }
172 if (first < second) {
164173 return -1;
174 }
165175 return 1;
166176 }
167177
168178 @Override
169179 public boolean isAvailable(MainFrame mainframe) {
170180 BugCollection bugCollection = mainframe.getBugCollection();
171 if (bugCollection == null)
181 if (bugCollection == null) {
172182 return true;
183 }
173184 return bugCollection.getCurrentAppVersion().getSequenceNumber() > 0;
174185
175186 }
184195
185196 @Override
186197 public String formatValue(String value) {
187 if (value.equals(String.valueOf(Priorities.HIGH_PRIORITY)))
198 if (value.equals(String.valueOf(Priorities.HIGH_PRIORITY))) {
188199 return edu.umd.cs.findbugs.L10N.getLocalString("sort.priority_high", "High");
189 if (value.equals(String.valueOf(Priorities.NORMAL_PRIORITY)))
200 }
201 if (value.equals(String.valueOf(Priorities.NORMAL_PRIORITY))) {
190202 return edu.umd.cs.findbugs.L10N.getLocalString("sort.priority_normal", "Normal");
191 if (value.equals(String.valueOf(Priorities.LOW_PRIORITY)))
203 }
204 if (value.equals(String.valueOf(Priorities.LOW_PRIORITY))) {
192205 return edu.umd.cs.findbugs.L10N.getLocalString("sort.priority_low", "Low");
193 if (value.equals(String.valueOf(Priorities.EXP_PRIORITY)))
206 }
207 if (value.equals(String.valueOf(Priorities.EXP_PRIORITY))) {
194208 return edu.umd.cs.findbugs.L10N.getLocalString("sort.priority_experimental", "Experimental");
209 }
195210 return edu.umd.cs.findbugs.L10N.getLocalString("sort.priority_ignore", "Ignore"); // This
196 // probably
197 // shouldn't
198 // ever
199 // happen,
200 // but
201 // what
202 // the
203 // hell,
204 // let's
205 // be
206 // complete
211 // probably
212 // shouldn't
213 // ever
214 // happen,
215 // but
216 // what
217 // the
218 // hell,
219 // let's
220 // be
221 // complete
207222
208223 }
209224
225240 // compare the numbers after the dollar signs.
226241 try {
227242 if (one.contains("$") && two.contains("$")
228 && one.substring(0, one.lastIndexOf("$")).equals(two.substring(0, two.lastIndexOf("$"))))
229 return Integer.valueOf(one.substring(one.lastIndexOf("$"))).compareTo(
230 Integer.valueOf(two.substring(two.lastIndexOf("$"))));
243 && one.substring(0, one.lastIndexOf('$')).equals(two.substring(0, two.lastIndexOf('$')))) {
244 return Integer.valueOf(one.substring(one.lastIndexOf('$'))).compareTo(
245 Integer.valueOf(two.substring(two.lastIndexOf('$'))));
246 }
231247 } catch (NumberFormatException e) {
232248 } // Somebody's playing silly buggers with dollar signs, just do it
233 // lexicographically
249 // lexicographically
234250
235251 // Otherwise, lexicographicalify it
236252 return one.compareTo(two);
244260
245261 @Override
246262 public String formatValue(String value) {
247 if (value.equals(""))
263 if ("".equals(value)) {
248264 return "(Default)";
265 }
249266 return value;
250267 }
251268 },
254271 public String getFrom(BugInstance bug) {
255272 int count = GUISaveState.getInstance().getPackagePrefixSegments();
256273
257 if (count < 1)
274 if (count < 1) {
258275 count = 1;
276 }
259277 String packageName = bug.getPrimarySourceLineAnnotation().getPackageName();
260278 return ClassName.extractPackagePrefix(packageName, count);
261279 }
283301 String catOne = one;
284302 String catTwo = two;
285303 int compare = catOne.compareTo(catTwo);
286 if (compare == 0)
304 if (compare == 0) {
287305 return 0;
288 if (catOne.equals("CORRECTNESS"))
306 }
307 if ("CORRECTNESS".equals(catOne)) {
289308 return -1;
290 if (catTwo.equals("CORRECTNESS"))
309 }
310 if ("CORRECTNESS".equals(catTwo)) {
291311 return 1;
312 }
292313 return compare;
293314
294315 }
302323
303324 /**
304325 * value is the key of the designations.
305 *
326 *
306327 * @param value
307328 * @return
308329 */
313334
314335 @Override
315336 public String[] getAllSorted() {// FIXME I think we always want user to
316 // see all possible designations, not
317 // just the ones he has set in his
318 // project, Agreement? -Dan
337 // see all possible designations, not
338 // just the ones he has set in his
339 // project, Agreement? -Dan
319340 List<String> sortedDesignations = I18N.instance().getUserDesignationKeys(true);
320341 return sortedDesignations.toArray(new String[sortedDesignations.size()]);
321342 }
364385 @Override
365386 public boolean isAvailable(MainFrame mf) {
366387 BugCollection bugCollection = mf.getBugCollection();
367 if (bugCollection == null || bugCollection.getCloud() == null)
388 if (bugCollection == null) {
368389 return false;
390 }
369391 return bugCollection.getCloud().getMode() == Mode.COMMUNAL;
370392
371393 }
375397 String[] values;
376398 {
377399 values = new String[40];
378 for (int i = 0; i < values.length; i++)
400 for (int i = 0; i < values.length; i++) {
379401 values[i] = String.format("%2d", i);
402 }
380403 }
381404
382405 @Override
402425 BugFilingStatus status = cloud.getBugLinkStatus(bug);
403426 if (status == BugFilingStatus.VIEW_BUG) {
404427 String bugStatus = cloud.getBugStatus(bug);
405 if (bugStatus != null)
428 if (bugStatus != null) {
406429 return bugStatus;
430 }
407431 }
408432
409433 return CONSENSUS.getFrom(bug);
417441 @Override
418442 public boolean isAvailable(MainFrame mf) {
419443 BugCollection bugCollection = mf.getBugCollection();
420 if (bugCollection == null || bugCollection.getCloud() == null)
444 if (bugCollection == null) {
421445 return false;
446 }
422447 boolean a = bugCollection.getCloud().supportsBugLinks() && bugCollection.getCloud().getMode() == Mode.COMMUNAL;
423448 return a;
424449
431456 public String getFrom(BugInstance bug) {
432457 ProjectPackagePrefixes p = MainFrame.getInstance().getProjectPackagePrefixes();
433458 Collection<String> projects = p.getProjects(bug.getPrimaryClass().getClassName());
434 if (projects.size() == 0)
459 if (projects.size() == 0) {
435460 return "unclassified";
461 }
436462 String result = projects.toString();
437463
438464 return result.substring(1, result.length() - 1);
472498 Sortables(String prettyName) {
473499 this.prettyName = prettyName;
474500 this.bugLeafNodeComparator = new Comparator<BugLeafNode>() {
501 @Override
475502 public int compare(BugLeafNode one, BugLeafNode two) {
476503 return Sortables.this.compare(Sortables.this.getFrom(one.getBug()), Sortables.this.getFrom(two.getBug()));
477504 }
497524 return value;
498525 }
499526
527 @Override
500528 public int compare(String one, String two) {
501529 // Lexicographical by default
502530 return one.compareTo(two);
512540 return values;
513541 }
514542
515 private SortableStringComparator comparator = new SortableStringComparator(this);
543 private final SortableStringComparator comparator = new SortableStringComparator(this);
516544
517545 public SortableStringComparator getComparator() {
518546 return comparator;
530558
531559 public static Sortables getSortableByPrettyName(String name) {
532560 for (Sortables s : values()) {
533 if (s.prettyName.equals(name))
561 if (s.prettyName.equals(name)) {
534562 return s;
563 }
535564 }
536565 return null;
537566 }
4242 /**
4343 * This is the window that pops up when the user double clicks on the sorting
4444 * table Its also available from the menu if they remove all Sortables.
45 *
45 *
4646 * The user can choose what Sortables he wants to sort by, sort them into the
4747 * order he wants to see and then click apply to move his choices onto the
4848 * sorting table
49 *
49 *
5050 * @author Dan
51 *
51 *
5252 */
5353 public class SorterDialog extends FBDialog {
5454
5555 private JTableHeader preview;
5656
57 private ArrayList<SortableCheckBox> checkBoxSortList = new ArrayList<SortableCheckBox>();
57 private final ArrayList<SortableCheckBox> checkBoxSortList = new ArrayList<SortableCheckBox>();
5858
5959 JButton sortApply;
6060
8888 super(s == Sortables.DIVIDER ? edu.umd.cs.findbugs.L10N.getLocalString("sort.divider", "[divider]") : s.toString());
8989 this.sortable = s;
9090 addChangeListener(new ChangeListener() {
91 @Override
9192 public void stateChanged(ChangeEvent e) {
9293 ((SorterTableColumnModel) preview.getColumnModel()).setVisible(sortable, isSelected());
9394 }
99100 /**
100101 * Creates JPanel with checkboxes of different things to sort by. List is:
101102 * priority, class, package, category, bugcode, status, and type.
102 *
103 * @return
104103 */
105104 private JPanel createSorterPane() {
106105 JPanel insidePanel = new JPanel();
109108 Sortables[] sortables = MainFrame.getInstance().getAvailableSortables();
110109 preview.setColumnModel(new SorterTableColumnModel(sortables));
111110
112 for (Sortables s : sortables)
111 for (Sortables s : sortables) {
113112 if (s != Sortables.DIVIDER) {
114113 checkBoxSortList.add(new SortableCheckBox(s));
115114 }
115 }
116116
117117 setSorterCheckBoxes();
118118
121121 gbc.gridx = 1;
122122 gbc.insets = new Insets(2,5,2,5);
123123 insidePanel.add(new JLabel("<html><h2>1. Choose bug properties"), gbc);
124 insidePanel.add(new CheckBoxList(checkBoxSortList.toArray(new JCheckBox[checkBoxSortList.size()])), gbc);
124 insidePanel.add(new CheckBoxList<>(checkBoxSortList.toArray(new JCheckBox[checkBoxSortList.size()])), gbc);
125125
126126 JTable t = new JTable(new DefaultTableModel(0, sortables.length));
127127 t.setTableHeader(preview);
131131
132132 sortApply = new JButton(edu.umd.cs.findbugs.L10N.getLocalString("dlg.apply_btn", "Apply"));
133133 sortApply.addActionListener(new ActionListener() {
134 @Override
134135 public void actionPerformed(ActionEvent e) {
135136 MainFrame.getInstance().getSorter().createFrom((SorterTableColumnModel) preview.getColumnModel());
136137 ((BugTreeModel) MainFrame.getInstance().getTree().getModel()).checkSorter();
166167
167168 SorterTableColumnModel sorter = MainFrame.getInstance().getSorter();
168169
169 for (SortableCheckBox c : checkBoxSortList)
170 for (SortableCheckBox c : checkBoxSortList) {
170171 c.setSelected(sorter.isShown(c.sortable));
172 }
171173 }
172174
173175 void freeze() {
4545 import javax.swing.table.TableColumnModel;
4646 import javax.swing.tree.TreeModel;
4747
48 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
49
4850 /**
4951 * Handles the sorting order and informs the treeModel when changes are
5052 * necessary
51 *
53 *
5254 * @author Dan
53 *
55 *
5456 */
5557 public class SorterTableColumnModel implements TableColumnModel {
5658
5759 private ArrayList<Sortables> order = new ArrayList<Sortables>();
5860
59 private Set<Sortables> shown = new HashSet<Sortables>();
60
61 private ArrayList<TableColumn> columnList = new ArrayList<TableColumn>();
61 private final Set<Sortables> shown = new HashSet<Sortables>();
62
63 private final ArrayList<TableColumn> columnList = new ArrayList<TableColumn>();
6264
6365 private DefaultListSelectionModel dlsm;
6466
65 private ArrayList<TableColumnModelListener> watchers = new ArrayList<TableColumnModelListener>();
67 private final ArrayList<TableColumnModelListener> watchers = new ArrayList<TableColumnModelListener>();
6668
6769 private boolean frozen = false;
6870
6971 public boolean isShown(Sortables s) {
7072 return shown.contains(s);
7173 }
72
74
7375 @Override
7476 public String toString() {
7577 return order.toString();
7678 }
7779
78 static boolean shownError = false;
79
80 static boolean shownError;
81
82 @SuppressFBWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD")
8083 public void check() {
81 if (order.size() == shown.size() && order.containsAll(shown))
84 if (order.size() == shown.size() && order.containsAll(shown)) {
8285 return;
83 if (shownError)
86 }
87 if (shownError) {
8488 return;
89 }
8590 shownError = true;
8691 MainFrame.getInstance().error("Incompatible order and shown for SorterTable: " + order + " vs. " + shown);
8792 shown.clear();
9297 MainFrame mainFrame = MainFrame.getInstance();
9398 int x = 0;
9499 for (Sortables c : columnHeaders) {
95 if (!c.isAvailable(mainFrame))
100 if (!c.isAvailable(mainFrame)) {
96101 continue;
102 }
97103 shown.add(c);
98104
99105 TableColumn tc = makeTableColumn(x, c);
106112 check();
107113 }
108114
109 /**
110 * @param x
111 * @param c
112 * @return
113 */
114115 private TableColumn makeTableColumn(int x, Sortables c) {
115116 TableColumn tc = new TableColumn(x);
116117 FBTableCellRenderer temp = new FBTableCellRenderer();
123124 }
124125
125126 public void createFrom(SorterTableColumnModel other) {
126 if (this.getOrder().equals(other.getOrder()))
127 if (this.getOrder().equals(other.getOrder())) {
127128 return;
129 }
128130 columnList.clear();
129131 for (int x = 0; x < order.size(); x++) {
130 for (TableColumnModelListener l : watchers)
132 for (TableColumnModelListener l : watchers) {
131133 l.columnRemoved(new TableColumnModelEvent(this, x, x));
134 }
132135 }
133136
134137 // First, empty showOrder
136139 MainFrame mainFrame = MainFrame.getInstance();
137140 int x = 0;
138141 for (Sortables c : other.order) {
139 if (!c.isAvailable(mainFrame))
142 if (!c.isAvailable(mainFrame)) {
140143 continue;
144 }
141145
142146 shown.add(c);
143147 TableColumn tc = makeTableColumn(x, c);
144148 columnList.add(tc);
145 for (TableColumnModelListener l : watchers)
149 for (TableColumnModelListener l : watchers) {
146150 l.columnAdded(new TableColumnModelEvent(this, x, x));
151 }
147152 x++;
148153 }
149154 dlsm = new DefaultListSelectionModel();
159164
160165 static class FBTableCellRenderer implements TableCellRenderer {
161166
162 private TableCellRenderer defaultRenderer = new JTableHeader().getDefaultRenderer();
163
167 private final TableCellRenderer defaultRenderer = new JTableHeader().getDefaultRenderer();
168
169 @Override
164170 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row,
165171 int column) {
166172
187193 }
188194 }
189195
196 @Override
190197 public void addColumn(TableColumn arg0) {
191198 throw new UnsupportedOperationException("Can't change sorter table columns using addColumn");
192199 }
193200
201 @Override
194202 public void removeColumn(TableColumn arg0) {
195203 throw new UnsupportedOperationException("Can't change sorter table columns using removeColumn");
196204 }
212220 for (TableColumnModelListener l : watchers) {
213221 l.columnAdded(new TableColumnModelEvent(this, columnList.size() - 1, columnList.size() - 1));
214222 }
215 } else if (!on) {
223 } else {
216224 shown.remove(s);
217225 for (int x = 0; x < columnList.size(); x++) {
218226 columnList.get(x).setModelIndex(x);
227235 }
228236
229237 orderUpdate();
230 for (TableColumnModelListener l : watchers)
238 for (TableColumnModelListener l : watchers) {
231239 l.columnRemoved(new TableColumnModelEvent(this, counter, counter));
232
240 }
233241 }
234242 }
235243 }
236244
237245 }
238246
247 @Override
239248 public void moveColumn(int fromIndex, int toIndex) {
240249
241 if (!MainFrame.getInstance().canNavigateAway())
250 if (!MainFrame.getInstance().canNavigateAway()) {
242251 return;
252 }
243253 MainFrame.getInstance().updateDesignationDisplay();
244254 TableColumn from = columnList.get(fromIndex);
245255 TableColumn to = columnList.get(toIndex);
257267 }
258268 }
259269
270 @Override
260271 public void setColumnMargin(int arg0) {
261272 throw new UnsupportedOperationException("NoBah");
262273 }
263274
275 @Override
264276 public int getColumnCount() {
265277 return columnList.size();
266278 }
267279
280 @Override
268281 public Enumeration<TableColumn> getColumns() {
269282 return Collections.<TableColumn> enumeration(columnList);
270283 }
271284
285 @Override
272286 public int getColumnIndex(Object columnIdentifier) {
273287
274 if (columnIdentifier == null)
288 if (columnIdentifier == null) {
275289 throw new IllegalArgumentException("Dont send null to getColumnIndex, null shouldn't be in the sorting table.");
290 }
276291
277292 for (int x = 0; x < columnList.size(); x++) {
278 if (columnList.get(x).getIdentifier().equals(columnIdentifier))
293 if (columnList.get(x).getIdentifier().equals(columnIdentifier)) {
279294 return x;
295 }
280296 }
281297
282298 throw new IllegalArgumentException();
283299 }
284300
301 @Override
285302 public TableColumn getColumn(int x) {
286303 return columnList.get(x);
287304 }
288305
306 @Override
289307 public int getColumnMargin() {
290308 return 0;
291309 }
292310
311 @Override
293312 public int getColumnIndexAtX(int XPosition) {
294313
295314 for (TableColumn tc : columnList) {
296315 XPosition -= tc.getWidth();
297 if (XPosition < 0)
316 if (XPosition < 0) {
298317 return tc.getModelIndex();
318 }
299319 }
300320 return -1;
301321 }
302322
323 @Override
303324 public int getTotalColumnWidth() {
304325 int total = 0;
305326 for (TableColumn tc : columnList) {
308329 return total;
309330 }
310331
332 @Override
311333 public void setColumnSelectionAllowed(boolean arg0) {
312334 throw new UnsupportedOperationException("BAH");// BAH
313335 }
314336
337 @Override
315338 public boolean getColumnSelectionAllowed() {
316339 return true;
317340 }
318341
342 @Override
319343 public int[] getSelectedColumns() {
320344 int index = dlsm.getMinSelectionIndex();
321 if (index == -1)
345 if (index == -1) {
322346 return new int[] {};
347 }
323348 return new int[] { index };
324349 }
325350
351 @Override
326352 public int getSelectedColumnCount() {
327353
328 if (dlsm.getMinSelectionIndex() == -1)
354 if (dlsm.getMinSelectionIndex() == -1) {
329355 return 0;
356 }
330357 return 1;
331358 }
332359
360 @Override
333361 public void setSelectionModel(ListSelectionModel arg0) {
334362 throw new UnsupportedOperationException("No... NO NO NO NO");
335363 }
336364
365 @Override
337366 public ListSelectionModel getSelectionModel() {
338367 return dlsm;
339368 }
340369
370 @Override
341371 public void addColumnModelListener(TableColumnModelListener listener) {
342372 watchers.add(listener);
343373 }
344374
375 @Override
345376 public void removeColumnModelListener(TableColumnModelListener listener) {
346377 watchers.remove(listener);
347378 }
355386 }
356387
357388 List<Sortables> getOrderBeforeDivider() {
358 if (!order.contains(Sortables.DIVIDER))
389 if (!order.contains(Sortables.DIVIDER)) {
359390 return order;
391 }
360392
361393 return order.subList(0, order.indexOf(Sortables.DIVIDER));
362394 }
363395
364396 List<Sortables> getOrderAfterDivider() {
365 if (!order.contains(Sortables.DIVIDER) || order.indexOf(Sortables.DIVIDER) == order.size() - 1)
397 if (!order.contains(Sortables.DIVIDER) || order.indexOf(Sortables.DIVIDER) == order.size() - 1) {
366398 return new ArrayList<Sortables>();
399 }
367400
368401 return order.subList(order.indexOf(Sortables.DIVIDER) + 1, order.size());
369402 }
372405 // order.clear();
373406 if (!frozen) {
374407 order = new ArrayList<Sortables>();
375 for (TableColumn c : columnList)
408 for (TableColumn c : columnList) {
376409 order.add((Sortables) c.getIdentifier());
410 }
377411 }
378412 check();
379413 }
7878 }
7979
8080 final BlockingQueue<DisplayMe> queue = new LinkedBlockingQueue<DisplayMe>();
81
81
8282 public void displaySource(BugInstance bug, SourceLineAnnotation source) {
8383 queue.add(new DisplayMe(bug, source));
8484 }
9494 String fullFileName = sourceFile.getFullFileName();
9595 SoftReference<JavaSourceDocument> resultReference = map.get(fullFileName);
9696 JavaSourceDocument result = null;
97 if (resultReference != null)
97 if (resultReference != null) {
9898 result = resultReference.get();
99 if (result != null)
99 }
100 if (result != null) {
100101 return result;
102 }
101103 try {
102104 InputStream in = sourceFile.getInputStream();
103105 result = new JavaSourceDocument(source.getClassName(), SourceCharset.bufferedReader(in), sourceFile);
114116 }
115117 }
116118
119 @Override
117120 public void run() {
118121 while (true) {
119122 DisplayMe display;
126129 }
127130 BugInstance myBug = display.bug;
128131 SourceLineAnnotation mySourceLine = display.source;
129
132
130133 if (myBug == null || mySourceLine == null) {
131134 frame.clearSourcePane();
132135 continue;
142145 BugAnnotation annotation = i.next();
143146 if (annotation instanceof SourceLineAnnotation) {
144147 SourceLineAnnotation sourceAnnotation = (SourceLineAnnotation) annotation;
145 if (sourceAnnotation == mySourceLine)
148 if (sourceAnnotation == mySourceLine) {
146149 continue;
147 if (sourceAnnotation.getDescription().equals(primaryKind))
150 }
151 if (sourceAnnotation.getDescription().equals(primaryKind)) {
148152 highlight(src, sourceAnnotation, MAIN_HIGHLIGHT_MORE);
149 else
153 } else {
150154 highlight(src, sourceAnnotation, ALTERNATIVE_HIGHLIGHT);
155 }
151156 }
152157 }
153158 highlight(src, mySourceLine, MAIN_HIGHLIGHT);
172177 this.myBug = myBug;
173178 }
174179
180 @Override
175181 public void run() {
176182 frame.getSourceCodeTextPane().setEditorKit(src.getEditorKit());
177183 StyledDocument document = src.getDocument();
178184 frame.getSourceCodeTextPane().setDocument(document);
179185 String sourceFile = mySourceLine.getSourceFile();
180 if (sourceFile == null || sourceFile.equals("<Unknown>")) {
186 if (sourceFile == null || "<Unknown>".equals(sourceFile)) {
181187 sourceFile = mySourceLine.getSimpleClassName();
182188 }
183189 int startLine = mySourceLine.getStartLine();
196202 // show(frame.getSourceCodeTextPane(),
197203 // document, sourceAnnotation);
198204 int otherLine = sourceAnnotation.getStartLine();
199 if (otherLine > originLine)
205 if (otherLine > originLine) {
200206 otherLine = sourceAnnotation.getEndLine();
207 }
201208 otherLines.add(otherLine);
202209 }
203210 }
204211 }
205
206 if (startLine >= 0 && endLine >= 0)
212
213 if (startLine >= 0 && endLine >= 0) {
207214 frame.getSourceCodeTextPane().scrollLinesToVisible(startLine, endLine, otherLines);
215 }
208216 }
209217 }
210218
215223 private void highlight(JavaSourceDocument src, SourceLineAnnotation sourceAnnotation, Color color) {
216224
217225 int startLine = sourceAnnotation.getStartLine();
218 if (startLine == -1)
226 if (startLine == -1) {
219227 return;
228 }
220229 String sourceFile = sourceAnnotation.getSourcePath();
221230 String sourceFile2 = src.getSourceFile().getFullFileName();
222231 if (!java.io.File.separator.equals(String.valueOf(SourceLineAnnotation.CANONICAL_PACKAGE_SEPARATOR))) {
223232 sourceFile2 = sourceFile2.replace(java.io.File.separatorChar, SourceLineAnnotation.CANONICAL_PACKAGE_SEPARATOR);
224233 }
225 if (!sourceFile2.endsWith(sourceFile))
234 if (!sourceFile2.endsWith(sourceFile)) {
226235 return;
236 }
227237 src.getHighlightInformation().setHighlight(startLine, sourceAnnotation.getEndLine(), color);
228238 }
229239
235245 }
236246
237247 private int search(JavaSourceDocument document, String target, int start, Boolean backwards) {
238 if (document == null)
239 return -1;
248 if (document == null) {
249 return -1;
250 }
240251
241252 String docContent = null;
242253 try {
243254 StyledDocument document2 = document.getDocument();
244 if (document2 == null)
255 if (document2 == null) {
245256 return -1;
257 }
246258 docContent = document2.getText(0, document2.getLength());
247259 } catch (BadLocationException ble) {
248260 System.out.println("Bad location exception");
249261 } catch (NullPointerException npe) {
250262 return -1;
251263 }
252 if (docContent == null)
253 return -1;
264 if (docContent == null) {
265 return -1;
266 }
254267 int targetLen = target.length();
255268 int sourceLen = docContent.length();
256 if (targetLen > sourceLen)
257 return -1;
258 else if (backwards) {
259 for (int i = start; i >= 0; i--)
260 if (docContent.substring(i, i + targetLen).equals(target))
269 if (targetLen > sourceLen) {
270 return -1;
271 } else if (backwards) {
272 for (int i = start; i >= 0; i--) {
273 if (docContent.substring(i, i + targetLen).equals(target)) {
261274 return i;
262 for (int i = (sourceLen - targetLen); i > start; i--)
263 if (docContent.substring(i, i + targetLen).equals(target))
275 }
276 }
277 for (int i = (sourceLen - targetLen); i > start; i--) {
278 if (docContent.substring(i, i + targetLen).equals(target)) {
264279 return i;
280 }
281 }
265282 return -1;
266283 } else {
267 for (int i = start; i <= (sourceLen - targetLen); i++)
268 if (docContent.substring(i, i + targetLen).equals(target))
284 for (int i = start; i <= (sourceLen - targetLen); i++) {
285 if (docContent.substring(i, i + targetLen).equals(target)) {
269286 return i;
270 for (int i = 0; i < start; i++)
271 if (docContent.substring(i, i + targetLen).equals(target))
287 }
288 }
289 for (int i = 0; i < start; i++) {
290 if (docContent.substring(i, i + targetLen).equals(target)) {
272291 return i;
292 }
293 }
273294 return -1;
274295 }
275296 }
276297
277298 private int charToLineNum(int charNum) {
278 if (charNum == -1)
279 return -1;
299 if (charNum == -1) {
300 return -1;
301 }
280302 try {
281303 for (int i = 1; true; i++) {
282 if (frame.getSourceCodeTextPane().getLineOffset(i) > charNum)
304 if (frame.getSourceCodeTextPane().getLineOffset(i) > charNum) {
283305 return i - 1;
284 else if (frame.getSourceCodeTextPane().getLineOffset(i) == -1)
306 } else if (frame.getSourceCodeTextPane().getLineOffset(i) == -1) {
285307 return -1;
308 }
286309 }
287310 } catch (BadLocationException ble) {
288311 return -1;
4040 /**
4141 * Wizard dialog to automatically find and configure source directories for a
4242 * project.
43 *
43 *
4444 * @author David Hovemeyer
4545 */
4646 public class SourceDirectoryWizard extends javax.swing.JDialog {
5151
5252 /**
5353 * Creates new form SourceDirectoryWizard
54 *
54 *
5555 * @param parentGUI
5656 */
5757 public SourceDirectoryWizard(java.awt.Frame parent, boolean modal, Project project, NewProjectWizard parentGUI) {
7272 // desc="Generated Code">//GEN-BEGIN:initComponents
7373 private void initComponents() {
7474
75 foundModel = new DefaultListModel();
76 progressModel = new DefaultListModel();
75 foundModel = new DefaultListModel<String>();
76 progressModel = new DefaultListModel<String>();
7777 contentPanel = new javax.swing.JPanel();
7878 secondPanel = new javax.swing.JPanel();
7979 jScrollPane1 = new javax.swing.JScrollPane();
8080 jScrollPane2 = new javax.swing.JScrollPane();
81 jList1 = new javax.swing.JList();
82 jList2 = new javax.swing.JList();
81 jList1 = new javax.swing.JList<>();
82 jList2 = new javax.swing.JList<>();
8383 jLabel1 = new javax.swing.JLabel();
8484 jLabel2 = new javax.swing.JLabel();
8585 jLabel3 = new javax.swing.JLabel();
158158 firstPanel.add(browseButton);
159159
160160 browseButton.addActionListener(new ActionListener() {
161 @Override
161162 public void actionPerformed(ActionEvent evt) {
162163 chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
163164 chooser.setMultiSelectionEnabled(true);
169170 for (File selectedFile : selectedFiles) {
170171 sourceRootBox.setText(selectedFile.getAbsolutePath());
171172 }
172 nextButton.setEnabled(!sourceRootBox.getText().equals(""));
173 nextButton.setEnabled(!"".equals(sourceRootBox.getText()));
173174 }
174175 }
175176 });
198199
199200 previousButton.setText("<< Previous");
200201 previousButton.addActionListener(new java.awt.event.ActionListener() {
202 @Override
201203 public void actionPerformed(java.awt.event.ActionEvent evt) {
202204 previousButtonActionPerformed(evt);
203205 }
207209
208210 nextButton.setText("Next >>");
209211 nextButton.addActionListener(new java.awt.event.ActionListener() {
212 @Override
210213 public void actionPerformed(java.awt.event.ActionEvent evt) {
211214 nextButtonActionPerformed(evt);
212
215
213216 }
214217 });
215218 getContentPane().add(nextButton);
217220
218221 finshButton.setText("Finish");
219222 finshButton.addActionListener(new java.awt.event.ActionListener() {
223 @Override
220224 public void actionPerformed(java.awt.event.ActionEvent evt) {
221225 finshButtonActionPerformed(evt);
222226 }
246250 @Override
247251 public void run() {
248252 IErrorLogger errorLogger = new IErrorLogger() {
253 @Override
249254 public void reportMissingClass(ClassNotFoundException ex) {
250255 String className = ClassNotFoundExceptionParser.getMissingClassName(ex);
251256 if (className != null) {
255260 }
256261 }
257262
263 @Override
258264 public void reportMissingClass(ClassDescriptor classDescriptor) {
259265 logError("Missing class: " + classDescriptor.toDottedClassName());
260266 }
261267
268 @Override
262269 public void logError(String message) {
263270 System.err.println("Error: " + message);
264271 }
265272
273 @Override
266274 public void logError(String message, Throwable e) {
267275 logError(message + ": " + e.getMessage());
268276 }
269277
278 @Override
270279 public void reportSkippedAnalysis(MethodDescriptor method) {
271280 logError("Skipped analysis of method " + method.toString());
272281 }
274283
275284 DiscoverSourceDirectories.Progress progress = new DiscoverSourceDirectories.Progress() {
276285
286 @Override
277287 public void startRecursiveDirectorySearch() {
278288 progressModel.addElement("Scanning directories...");
279289 }
280290
291 @Override
281292 public void doneRecursiveDirectorySearch() {
282293 }
283294
295 @Override
284296 public void startScanningArchives(int numArchivesToScan) {
285297 progressModel.addElement("Scanning " + numArchivesToScan + " archives..");
286298 }
287299
300 @Override
288301 public void doneScanningArchives() {
289302 }
290303
304 @Override
291305 public void startScanningClasses(int numClassesToScan) {
292306 progressModel.addElement("Scanning " + numClassesToScan + " classes...");
293307 }
294308
309 @Override
295310 public void finishClass() {
296311 }
297312
313 @Override
298314 public void doneScanningClasses() {
299315 }
300316
317 @Override
301318 public void finishArchive() {
302319 }
303320
321 @Override
304322 public void startArchive(String name) {
305323 }
306324 };
341359 if (parentGUI != null) {
342360 parentGUI.setSourceDirecs(foundModel);
343361 }
344 if (discover != null && discover.isAlive())
362 if (discover != null && discover.isAlive()) {
345363 discover.stop();
364 }
346365 dispose();
347366 }// GEN-LAST:event_finshButtonActionPerformed
348367
352371 */
353372 public static void main(String args[]) {
354373 java.awt.EventQueue.invokeLater(new Runnable() {
374 @Override
355375 public void run() {
356376 final SourceDirectoryWizard dialog = new SourceDirectoryWizard(new javax.swing.JFrame(), true, new Project(),
357377 null);
362382
363383 private JFileChooser chooser;
364384
365 private Project project;
366
367 private NewProjectWizard parentGUI;
368
369 private DefaultListModel foundModel;
370
371 private DefaultListModel progressModel;
372
373 private JList jList2;
385 private final Project project;
386
387 private final NewProjectWizard parentGUI;
388
389 private DefaultListModel<String> foundModel;
390
391 private DefaultListModel<String> progressModel;
392
393 private JList<String> jList2;
374394
375395 public Thread discover;
376396
399419
400420 private javax.swing.JLabel jLabel3;
401421
402 private javax.swing.JList jList1;
422 private javax.swing.JList<String> jList1;
403423
404424 private javax.swing.JScrollPane jScrollPane1;
405425
427447 }
428448 this.step = step;
429449 previousButton.setEnabled(step != MIN_STEP);
430 nextButton.setEnabled(step != MAX_STEP && !sourceRootBox.getText().equals(""));
450 nextButton.setEnabled(step != MAX_STEP && !"".equals(sourceRootBox.getText()));
431451
432452 CardLayout cards = (CardLayout) contentPanel.getLayout();
433453 cards.show(contentPanel, "card" + step);
8686 @Override
8787 public void setVisible(boolean b) {
8888 super.setVisible(b);
89 if (!b)
89 if (!b) {
9090 animator.interrupt();
91 }
9192 }
9293
9394 private static class Viewer extends JPanel {
109110
110111 int ypos = 0;
111112
112 int farRight;
113 // int farRight;
113114
114115 public Viewer(Image i1, Image i2, Image i1r, Image i2r) {
115116 image = i1;
131132 System.exit(1);
132133 }
133134 animator = new Thread(new Runnable() {
135 @Override
134136 public void run() {
135137 int deltaX = 1;
136138
137139 while (true) {
138 if (Thread.currentThread().isInterrupted())
140 if (Thread.currentThread().isInterrupted()) {
139141 return;
142 }
140143
141144 callCount++;
142145 if (callCount == 10) {
172175 animator.start();
173176 }
174177
175 @Override
176 public void setPreferredSize(Dimension d) {
177 super.setPreferredSize(d);
178 }
179
180178 private Image imageToDraw() {
181179 if (swap) {
182 if (!reverse)
180 if (!reverse) {
183181 return image;
182 }
184183 return imageR;
185184 } else {
186 if (!reverse)
185 if (!reverse) {
187186 return image2;
187 }
188188 return image2R;
189189 }
190190 }
6565
6666 /*
6767 * (non-Javadoc)
68 *
68 *
6969 * @see edu.umd.cs.findbugs.gui2.FindBugsLayoutManager#createWindowMenu()
7070 */
71 @Override
7172 public JMenu createWindowMenu() {
7273 return null;
7374 }
7475
76 @Override
7577 public void resetCommentsInputPane() {
7678 if (topLeftSPane != null) {
7779 int position = topLeftSPane.getDividerLocation();
8284
8385 /*
8486 * (non-Javadoc)
85 *
87 *
8688 * @see edu.umd.cs.findbugs.gui2.FindBugsLayoutManager#initialize()
8789 */
90 @Override
8891 public void initialize() {
8992
9093 Font buttonFont = viewSource.getFont();
153156
154157 /*
155158 * (non-Javadoc)
156 *
159 *
157160 * @see edu.umd.cs.findbugs.gui2.FindBugsLayoutManager#makeCommentsVisible()
158161 */
162 @Override
159163 public void makeCommentsVisible() {
160164
161165 }
162166
163167 /*
164168 * (non-Javadoc)
165 *
169 *
166170 * @see edu.umd.cs.findbugs.gui2.FindBugsLayoutManager#makeSourceVisible()
167171 */
172 @Override
168173 public void makeSourceVisible() {
169174
170175 }
171176
172177 /*
173178 * (non-Javadoc)
174 *
179 *
175180 * @see edu.umd.cs.findbugs.gui2.FindBugsLayoutManager#saveState()
176181 */
182 @Override
177183 public void saveState() {
178184 GUISaveState.getInstance().setSplitTreeComments(topLeftSPane.getDividerLocation());
179185 GUISaveState.getInstance().setSplitTop(topSPane.getDividerLocation());
183189
184190 /*
185191 * (non-Javadoc)
186 *
192 *
187193 * @see
188194 * edu.umd.cs.findbugs.gui2.FindBugsLayoutManager#setSourceTitle(java.lang
189195 * .String)
190196 */
197 @Override
191198 public void setSourceTitle(String title) {
192199 sourceTitle.setText(title);
193200
195202
196203 /*
197204 * (non-Javadoc)
198 *
205 *
199206 * @see
200207 * edu.umd.cs.findbugs.gui2.FindBugsLayoutManager#getSourceTitleComponent()
201208 */
209 @Override
202210 public JComponent getSourceViewComponent() {
203211 return viewSource;
204212 }
99 * A 1.4 file that provides utility methods for creating form- or grid-style
1010 * layouts with SpringLayout. These utilities are used by several programs, such
1111 * as SpringBox and SpringCompactGrid.
12 *
12 *
1313 * From the Swing tutorial.
14 *
14 *
1515 */
1616 public class SpringUtilities {
1717 /**
3131 * <code>parent</code> in a grid. Each component is as big as the maximum
3232 * preferred width and height of the components. The parent is made just big
3333 * enough to fit them all.
34 *
34 *
3535 * @param rows
3636 * number of rows
3737 * @param cols
5050 try {
5151 layout = (SpringLayout) parent.getLayout();
5252 } catch (ClassCastException exc) {
53 if (MainFrame.GUI2_DEBUG)
53 if (MainFrame.GUI2_DEBUG) {
5454 System.err.println("The first argument to makeGrid must use SpringLayout.");
55 }
5556 return;
5657 }
5758
124125 * the maximum preferred width of the components in that column; height is
125126 * similarly determined for each row. The parent is made just big enough to
126127 * fit them all.
127 *
128 *
128129 * @param rows
129130 * number of rows
130131 * @param cols
143144 try {
144145 layout = (SpringLayout) parent.getLayout();
145146 } catch (ClassCastException exc) {
146 if (MainFrame.GUI2_DEBUG)
147 if (MainFrame.GUI2_DEBUG) {
147148 System.err.println("The first argument to makeCompactGrid must use SpringLayout.");
149 }
148150 return;
149151 }
150152
3535 public class StackedFilterMatcher extends FilterMatcher {
3636 private static final long serialVersionUID = 3958267780332359162L;
3737
38 private FilterMatcher[] filters;
38 private final FilterMatcher[] filters;
3939
4040 @Override
4141 Sortables getFilterBy() {
6464 BugTreeModel.TreeModification whatToDo;
6565
6666 if (active != this.active) {
67 if (active == false)
67 if (active == false) {
6868 this.active = active;
69 }
6970
7071 StackedFilterMatcher theSame = this;
7172 FilterMatcher[] filtersInStack = theSame.getFilters();
7273 ArrayList<Sortables> order = MainFrame.getInstance().getSorter().getOrder();
7374 int sizeToCheck = filtersInStack.length;
74 if (order.contains(Sortables.DIVIDER))
75 if (order.contains(Sortables.DIVIDER)) {
7576 if (order.indexOf(Sortables.DIVIDER) < filtersInStack.length) {
7677 sizeToCheck++;
7778 }
79 }
7880 List<Sortables> sortablesToCheck = order.subList(0, Math.min(sizeToCheck, order.size()));
7981 Debug.println("Size to check" + sizeToCheck + " checking list" + sortablesToCheck);
8082 Debug.println("checking filters");
9294 ArrayList<String> finalPath = new ArrayList<String>();
9395 for (int x = 0; x < almostPath.size(); x++) {
9496 Sortables s = almostPathSortables.get(x);
95 if (MainFrame.getInstance().getSorter().getOrderBeforeDivider().contains(s))
97 if (MainFrame.getInstance().getSorter().getOrderBeforeDivider().contains(s)) {
9698 finalPath.add(almostPath.get(x));
99 }
97100 }
98101 try {
99102 if (finalPath.size() == filtersInStack.length) {
106109 }
107110 } else {
108111 event = (MainFrame.getInstance().getBugTreeModel()).restructureBranch(finalPath, active);// if
109 // active
110 // is
111 // true,
112 // this
113 // removes,
114 // if
115 // active
116 // if
117 // false,
118 // it
119 // inserts
120 if (active)
112 // active
113 // is
114 // true,
115 // this
116 // removes,
117 // if
118 // active
119 // if
120 // false,
121 // it
122 // inserts
123 if (active) {
121124 whatToDo = BugTreeModel.TreeModification.REMOVERESTRUCTURE;
122 else
125 } else {
123126 whatToDo = BugTreeModel.TreeModification.INSERTRESTRUCTURE;
124 }
125
126 if (active == true)
127 }
128 }
129
130 if (active == true) {
127131 this.active = active;
132 }
128133 (MainFrame.getInstance().getBugTreeModel()).sendEvent(event, whatToDo);
129134 } catch (BranchOperationException e) {
130135 // Another filter already filters out the branch this filter
142147
143148 @Override
144149 public boolean match(BugInstance bugInstance) {
145 if (!isActive())
150 if (!isActive()) {
146151 return true;
147
148 for (FilterMatcher i : filters)
149 if (i.match(bugInstance))
152 }
153
154 for (FilterMatcher i : filters) {
155 if (i.match(bugInstance)) {
150156 return true;
157 }
158 }
151159
152160 return false;
153161 }
156164 public String toString() {
157165 // return "StackedFilterMatcher: " + Arrays.toString(filters);
158166 StringBuilder result = new StringBuilder();
159 for (int i = 0; i < filters.length - 1; i++)
167 for (int i = 0; i < filters.length - 1; i++) {
160168 result.append(filters[i].toString() + (i == filters.length - 2 ? " " : ", "));
161 if (filters.length > 1)
169 }
170 if (filters.length > 1) {
162171 result.append("and ");
163 if (filters.length > 0)
172 }
173 if (filters.length > 0) {
164174 result.append(filters[filters.length - 1]);
175 }
165176 return result.toString();
166177 }
167178
168179 @Override
169180 public boolean equals(Object o) {
170 if (o == null || !(o instanceof StackedFilterMatcher))
181 if (o == null || !(o instanceof StackedFilterMatcher)) {
171182 return false;
183 }
172184
173185 FilterMatcher[] mine = new FilterMatcher[filters.length];
174186 System.arraycopy(this.filters, 0, mine, 0, mine.length);
184196 @Override
185197 public int hashCode() {
186198 int hash = 0;
187 for (FilterMatcher f : filters)
199 for (FilterMatcher f : filters) {
188200 hash += f.hashCode();
201 }
189202 return hash;
190203 }
191204
3232 public class SuppressionMatcher extends ArrayList<BugInstance> implements Matcher {
3333 private static final long serialVersionUID = -689204051024507484L;
3434
35 @Override
3536 public boolean match(BugInstance bugInstance) {
3637 return (!contains(bugInstance));
3738 }
3839
3940 @Override
4041 public boolean add(BugInstance bugInstance) {
41 if (contains(bugInstance))
42 if (contains(bugInstance)) {
4243 return false;
44 }
4345 return super.add(bugInstance);
4446 }
4547
48 @Override
4649 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
4750 throw new UnsupportedOperationException();
4851 }
4141 boolean show(MainFrame mf, BugInstance b);
4242 }
4343
44 enum PriorityFilter implements ViewFilterEnum {
45 HIGH_PRIORITY(1, "High priority only"), NORMAL_PRIORITY(2, "High and normal priority"), ALL_BUGS(10, "All bug priorities");
46
47 final int maxPriority;
48 final String displayName;
49
50 private PriorityFilter(int maxPriority, String displayName) {
51 this.maxPriority = maxPriority;
52 this.displayName = displayName;
53 }
54
55 @Override
56 public boolean show(MainFrame mf, BugInstance b) {
57 return b.getPriority() <= maxPriority;
58 }
59
60 @Override
61 public String toString() {
62 return displayName;
63 }
64 }
65
4466 enum RankFilter implements ViewFilterEnum {
4567 SCARIEST(4, "Scariest"), SCARY(9, "Scary"), TROUBLING(14, "Troubling"), ALL(Integer.MAX_VALUE, "All bug ranks");
4668 final int maxRank;
5274 this.displayName = displayName;
5375 }
5476
77 @Override
5578 public boolean show(MainFrame mf, BugInstance b) {
5679 int rank = BugRanker.findRank(b);
5780 return rank <= maxRank;
5982
6083 @Override
6184 public String toString() {
62 if (maxRank < Integer.MAX_VALUE)
85 if (maxRank < Integer.MAX_VALUE) {
6386 return displayName + " (Ranks 1 - " + maxRank + ")";
87 }
6488 return displayName;
6589 }
6690
7195 @Override
7296 boolean show(Cloud cloud, BugInstance b) {
7397 double score = cloud.getClassificationScore(b);
74 if (score <= UserDesignation.MOSTLY_HARMLESS.score())
98 if (score <= UserDesignation.MOSTLY_HARMLESS.score()) {
7599 return false;
100 }
76101
77102 score = cloud.getPortionObsoleteClassifications(b);
78 if (score >= 0.5)
103 if (score >= 0.5) {
79104 return false;
80
81 return true;
105 }
106
107 return true;
82108 }
83109 },
84110 SHOULD_FIX("Overall classification is should fix") {
105131 UNCERTAIN("Overall classification is uncertain") {
106132 @Override
107133 boolean show(Cloud cloud, BugInstance b) {
108 if (SHOULD_FIX.show(cloud, b) || DONT_FIX.show(cloud, b) || OBSOLETE.show(cloud, b))
134 if (SHOULD_FIX.show(cloud, b) || DONT_FIX.show(cloud, b) || OBSOLETE.show(cloud, b)) {
109135 return false;
110 if (cloud.getNumberReviewers(b) >= 2)
136 }
137 if (cloud.getNumberReviewers(b) >= 2) {
111138 return true;
139 }
112140 return false;
113141 }
114142 },
115 HIGH_VARIANCE("Controversial") {
143 HIGH_VARIANCE("Controversial") {
116144 @Override
117145 boolean show(Cloud cloud, BugInstance b) {
118146 double variance = cloud.getClassificationDisagreement(b);
121149
122150 },
123151 ALL("All issues") {
124
152
125153 @Override
126154 public boolean show(MainFrame mf, BugInstance b) {
127 return true;
155 return true;
128156 }
129157
130158 @Override
145173 return true;
146174 }
147175
176 @Override
148177 public boolean show(MainFrame mf, BugInstance b) {
149178 Cloud c = mf.getBugCollection().getCloud();
150179 return c.isInCloud(b) && show(c, b);
266295
267296 @Override
268297 public boolean show(MainFrame mf, BugInstance b) {
269 return true;
298 return true;
270299 }
271300 };
272301
282311 return true;
283312 }
284313
314 @Override
285315 public boolean show(MainFrame mf, BugInstance b) {
286316 Cloud c = mf.getBugCollection().getCloud();
287317 return c.isInCloud(b) && show(c, b);
315345 this.displayName = displayName;
316346 }
317347
348 @Override
318349 public boolean show(MainFrame mf, BugInstance b) {
319350 Cloud cloud = mf.getBugCollection().getCloud();
320 if (!cloud.isInCloud(b))
351 if (!cloud.isInCloud(b)) {
321352 return false;
353 }
322354 long firstSeen = cloud.getFirstSeen(b);
323355 long time = System.currentTimeMillis() - firstSeen;
324356 long days = TimeUnit.SECONDS.convert(time, TimeUnit.MILLISECONDS) / 3600 / 24;
335367
336368 RankFilter rank = RankFilter.ALL;
337369
370 PriorityFilter priority = PriorityFilter.ALL_BUGS;
371
338372 CloudFilter eval = CloudFilter.ALL;
339373
340374 OverallClassificationFilter classificationFilter = OverallClassificationFilter.ALL;
342376 FirstSeenFilter firstSeen = FirstSeenFilter.ALL;
343377
344378 String[] classSearchStrings;
379
345380
346381 static final Pattern legalClassSearchString = Pattern.compile("[\\p{javaLowerCase}\\p{javaUpperCase}0-9.$/_]*");
347382
348383 void setPackagesToDisplay(String value) {
349384 value = value.replace('/', '.').trim();
350 if (value.length() == 0)
385 if (value.length() == 0) {
351386 classSearchStrings = new String[0];
352 else {
387 } else {
353388 String[] parts = value.split("[ ,:]+");
354 for (String p : parts)
355 if (!legalClassSearchString.matcher(p).matches())
389 for (String p : parts) {
390 if (!legalClassSearchString.matcher(p).matches()) {
356391 throw new IllegalArgumentException("Classname filter must be legal Java identifier: " + p);
392 }
393 }
357394
358395 classSearchStrings = parts;
359396 }
370407
371408 }
372409
410 public PriorityFilter getPriority() {
411 return priority;
412 }
413
414 public void setPriority(PriorityFilter priority) {
415 this.priority = priority;
416 FilterActivity.notifyListeners(FilterListener.Action.FILTERING, null);
417
418 }
419
373420 public CloudFilter getEvaluation() {
374421 return eval;
375422 }
376423
377424 public void setEvaluation(CloudFilter eval) {
378 if (this.eval == eval)
425 if (this.eval == eval) {
379426 return;
427 }
380428 this.eval = eval;
381429 FilterActivity.notifyListeners(FilterListener.Action.FILTERING, null);
382430
383431 }
384432
385433 public void setClassification(OverallClassificationFilter classificationFilter) {
386 if (this.classificationFilter == classificationFilter)
434 if (this.classificationFilter == classificationFilter) {
387435 return;
436 }
388437 this.classificationFilter = classificationFilter;
389438 FilterActivity.notifyListeners(FilterListener.Action.FILTERING, null);
390439
409458 return false;
410459 }
411460 if (!rank.show(mf, b)) {
461 return false;
462 }
463 if (!priority.show(mf, b)) {
412464 return false;
413465 }
414466 if (!eval.show(mf, b)) {
0 package edu.umd.cs.findbugs.gui2;
1
2 import java.awt.Dimension;
3 import java.util.Vector;
4
5 import javax.swing.ComboBoxModel;
6 import javax.swing.JComboBox;
7
8 public class WideComboBox extends JComboBox {
9
10 public WideComboBox() {
11 }
12
13 public WideComboBox(final Object items[]) {
14 super(items);
15 }
16
17 public WideComboBox(Vector items) {
18 super(items);
19 }
20
21 public WideComboBox(ComboBoxModel aModel) {
22 super(aModel);
23 }
24
25 private boolean layingOut = false;
26
27 public void doLayout() {
28 try {
29 layingOut = true;
30 super.doLayout();
31 } finally {
32 layingOut = false;
33 }
34 }
35
36 public Dimension getSize() {
37 Dimension dim = super.getSize();
38 if (!layingOut) {
39 dim.width = Math.max(dim.width, 300);
40 dim.height = Math.max(dim.height, 500);
41 }
42 return dim;
43 }
44 }
0 package edu.umd.cs.findbugs.gui2;
1
2 import java.awt.Dimension;
3 import java.util.Vector;
4
5 import javax.swing.ComboBoxModel;
6 import javax.swing.JComboBox;
7
8 public class WideComboBox<E> extends JComboBox<E> {
9
10 public WideComboBox() {
11 }
12
13 public WideComboBox(final E items[]) {
14 super(items);
15 }
16
17 public WideComboBox(Vector<E> items) {
18 super(items);
19 }
20
21 public WideComboBox(ComboBoxModel<E> aModel) {
22 super(aModel);
23 }
24
25 private boolean layingOut = false;
26
27 @Override
28 public void doLayout() {
29 try {
30 layingOut = true;
31 super.doLayout();
32 } finally {
33 layingOut = false;
34 }
35 }
36
37 @Override
38 public Dimension getSize() {
39 Dimension dim = super.getSize();
40 if (!layingOut) {
41 dim.width = Math.max(dim.width, 300);
42 dim.height = Math.max(dim.height, 500);
43 }
44 return dim;
45 }
46 }
5757 throw new UnsupportedOperationException();
5858 }
5959
60 @Override
6061 public char current() {
6162 return text.current();
6263 }
6364
65 @Override
6466 public char first() {
6567 throw new UnsupportedOperationException();
6668 }
6769
70 @Override
6871 public int getBeginIndex() {
6972 throw new UnsupportedOperationException();
7073 }
7174
75 @Override
7276 public int getEndIndex() {
7377 throw new UnsupportedOperationException();
7478 }
7579
80 @Override
7681 public int getIndex() {
7782 return docPos;
7883 }
7984
85 @Override
8086 public char last() {
8187 throw new UnsupportedOperationException();
8288 }
8490 /**
8591 * Increments the iterator's index by one and returns the character at the
8692 * new index.
87 *
93 *
8894 * @return the character at the new position, or DONE if the new position is
8995 * off the end
9096 */
97 @Override
9198 public char next() {
9299 ++docPos;
93100 if (docPos < segmentEnd || segmentEnd >= doc.getLength()) {
102109 return text.current();
103110 }
104111
112 @Override
105113 public char previous() {
106114 throw new UnsupportedOperationException();
107115 }
108116
117 @Override
109118 public char setIndex(int position) {
110119 throw new UnsupportedOperationException();
111120 }
3737 }
3838
3939 public void setHighlight(int start, int end, Color color) {
40 for (int i = start; i <= end; i++)
40 for (int i = start; i <= end; i++) {
4141 map.put(i, color);
42 }
4243 }
4344
4445 public void setHighlight(Integer line, Color color) {
5051 }
5152
5253 public void updateFoundLineNum(Integer line) {
53 if (foundLineNum != -1)
54 if (foundLineNum != -1) {
5455 unsetHighlight(foundLineNum);
56 }
5557 foundLineNum = line;
5658 }
5759
4949 "synchronized", "this", "throw", "throws", "transient", "true", "try", "void", "volatile", "while" };
5050 int max = 0;
5151 for (String s : keywordList) {
52 if (max < s.length())
52 if (max < s.length()) {
5353 max = s.length();
54 }
5455 KEYWORDS.add(s);
5556 }
5657 MAX_KEYWORD_LENGTH = max;
9596 boolean couldBeKeyword = Character.isLowerCase(c);
9697 while (true) {
9798 c = iterator.current();
98 if (!Character.isJavaIdentifierPart(c))
99 if (!Character.isJavaIdentifierPart(c)) {
99100 break;
101 }
100102 buf.append(c);
101103 if (couldBeKeyword) {
102 if (!Character.isLowerCase(c) || buf.length() > MAX_KEYWORD_LENGTH)
104 if (!Character.isLowerCase(c) || buf.length() > MAX_KEYWORD_LENGTH) {
103105 couldBeKeyword = false;
106 }
104107 }
105108 c = iterator.next();
106109 }
107110 kind = NORMAL_TEXT;
108111 if (couldBeKeyword) {
109 if (KEYWORDS.contains(buf.toString()))
112 if (KEYWORDS.contains(buf.toString())) {
110113 kind = KEYWORD;
114 }
111115 }
112116 buf.setLength(0);
113117 } else if (c == '/') {
115119 if (c2 == '/') {
116120 while (true) {
117121 c2 = iterator.next();
118 if (c2 == '\n' || c2 == '\r' || c2 == CharacterIterator.DONE)
122 if (c2 == '\n' || c2 == '\r' || c2 == CharacterIterator.DONE) {
119123 break;
124 }
120125 }
121126 kind = COMMENT;
122127 return kind;
126131 if (c2 == '*') {
127132 do {
128133 c2 = iterator.next();
129 if (c2 == '/')
134 if (c2 == '/') {
130135 break scanComment;
136 }
131137 } while (c2 == '*');
132138 }
133139 }
134 kind = JAVADOC;
135 return kind;
140 kind = JAVADOC;
141 return kind;
136142 }
137143 } else if (c == '"') {
138144 kind = QUOTE;
140146 while (c2 != '"' && c2 != '\n' && c2 != '\r' && c2 != CharacterIterator.DONE) {
141147 if (c2 == '\\') {
142148 c2 = iterator.next();
143 if (c2 == '\n' || c2 == '\r')
149 if (c2 == '\n' || c2 == '\r') {
144150 break;
151 }
145152 }
146153 c2 = iterator.next();
147154 }
151158 kind = QUOTE; // or NORMAL_TEXT ?
152159 char c2 = iterator.current();
153160 if (c2 == '\\')
161 {
154162 c2 = iterator.next(); // advance past the escape char
163 }
155164 if (c2 != '\n' && c2 != '\r' && c2 != CharacterIterator.DONE)
165 {
156166 c2 = iterator.next(); // advance past the content char
167 }
157168 if (c2 != '\n' && c2 != '\r' && c2 != CharacterIterator.DONE)
169 {
158170 iterator.next(); // advance past closing char
171 }
159172
160 } else
173 } else {
161174 kind = NORMAL_TEXT;
175 }
162176 // System.out.println(kind + " " + startPosition + "-" +
163177 // iterator.getIndex());
164178 return kind;
2929 import javax.swing.text.BadLocationException;
3030 import javax.swing.text.DefaultStyledDocument;
3131 import javax.swing.text.Document;
32 import javax.swing.text.Element;
3332 import javax.swing.text.SimpleAttributeSet;
3433 import javax.swing.text.StyleConstants;
35 import javax.swing.text.StyleContext;
3634 import javax.swing.text.StyledDocument;
3735 import javax.swing.text.TabSet;
3836 import javax.swing.text.TabStop;
7270
7371 final NumberedEditorKit dek = new NumberedEditorKit(highlights);
7472
75 final StyleContext styleContext = new StyleContext();
73 // final StyleContext styleContext = new StyleContext();
7674
77 final Element root;
75 // final Element root;
7876
7977 final DefaultStyledDocument doc;
8078
114112 }
115113 in.close();
116114 doc.putProperty(Document.TitleProperty, title);
117 root = doc.getDefaultRootElement();
115 // root = doc.getDefaultRootElement();
118116 Toolkit toolkit = Toolkit.getDefaultToolkit();
119117 FontMetrics fontMetrics = toolkit.getFontMetrics(sourceFont);
120118 TabStop[] tabs = new TabStop[50];
121119 float width = fontMetrics.stringWidth(" ");
122120
123121 int tabSize = GUISaveState.getInstance().getTabSize();
124 for (int i = 0; i < tabs.length; i++)
122 for (int i = 0; i < tabs.length; i++) {
125123 tabs[i] = new TabStop(width * (tabSize + tabSize * i));
124 }
126125 TAB_SET = new TabSet(tabs);
127126 StyleConstants.setTabSet(commentAttributes, TAB_SET);
128127 StyleConstants.setTabSet(javadocAttributes, TAB_SET);
145144 switch (kind) {
146145 case JavaScanner.COMMENT:
147146 doc.setCharacterAttributes(parser.getStartPosition(), parser.getLength(), commentAttributes, true);
147 break;
148148
149 break;
150149 case JavaScanner.KEYWORD:
151150 doc.setCharacterAttributes(parser.getStartPosition(), parser.getLength(), keywordsAttributes, true);
151 break;
152152
153 break;
154153 case JavaScanner.JAVADOC:
155154 doc.setCharacterAttributes(parser.getStartPosition(), parser.getLength(), javadocAttributes, true);
155 break;
156156
157 break;
158157 case JavaScanner.QUOTE:
159158 doc.setCharacterAttributes(parser.getStartPosition(), parser.getLength(), quotesAttributes, true);
159 break;
160160
161 default:
161162 break;
162163 }
163164
165166
166167 }
167168
168 private static final long serialVersionUID = 0L;
169 // private static final long serialVersionUID = 0L;
169170
170171 public static final JavaSourceDocument UNKNOWNSOURCE;
171172 static {
5252 */
5353 private int parentHeight() {
5454 Container parent = getParent();
55 if (parent != null)
55 if (parent != null) {
5656 return parent.getHeight();
57 }
5758 return getHeight(); // entire pane height, may be huge
5859 }
5960
6566 Document d = getDocument();
6667 try {
6768 Element element = d.getDefaultRootElement().getElement(line - 1);
68 if (element == null)
69 if (element == null) {
6970 throw new BadLocationException("line " + line + " does not exist", -line);
71 }
7072 return element.getStartOffset();
7173 } catch (ArrayIndexOutOfBoundsException aioobe) {
7274 BadLocationException ble = new BadLocationException("line " + line + " does not exist", -line);
8890 final Rectangle r = new Rectangle(0, y - margin, 4, 2 * margin);
8991
9092 SwingUtilities.invokeLater(new Runnable() {
93 @Override
9194 public void run() {
9295 scrollRectToVisible(r);
9396 }
99102 int y = lineToY(line);
100103 scrollYToVisibleImpl(y, margin);
101104 } catch (BadLocationException ble) {
102 if (MainFrame.GUI2_DEBUG)
105 if (MainFrame.GUI2_DEBUG) {
103106 ble.printStackTrace();
107 }
104108 }
105109 }
106110
110114 */
111115 public void scrollLineToVisible(int line, int margin) {
112116 int maxMargin = (parentHeight() - 20) / 2;
113 if (margin > maxMargin)
117 if (margin > maxMargin) {
114118 margin = Math.max(0, maxMargin);
119 }
115120 scrollLineToVisibleImpl(line, margin);
116121 }
117122
130135 try {
131136 startY = lineToY(startLine);
132137 } catch (BadLocationException ble) {
133 if (MainFrame.GUI2_DEBUG)
138 if (MainFrame.GUI2_DEBUG) {
134139 ble.printStackTrace();
140 }
135141 return; // give up
136142 }
137143 try {
160166 int y = pq.remove();
161167 int lo = Math.min(startY, y);
162168 int hi = Math.max(endY, y);
163 if (hi - lo > max)
169 if (hi - lo > max) {
164170 break;
165 else {
171 } else {
166172 startY = lo;
167173 endY = hi;
168174 }
169175 }
170176 }
171
177
172178 if (endY - startY > max) {
173179 endY = startY + max;
174180 }
188194 * the origin. This sounds backwards, but this way closer values get a
189195 * higher priority in the priority queue.
190196 */
197 @Override
191198 public int compare(Integer a, Integer b) {
192199 return Math.abs(b - origin) - Math.abs(a - origin);
193200 }
100100
101101 Element element = this.getElement();
102102 Integer result = elementLineNumberCache.get(element);
103 if (result != null)
103 if (result != null) {
104104 return result;
105 }
105106 Element parent = element.getParentElement();
106107 int count = parent.getElementCount();
107108 for (int i = 0; i < count; i++) {
108109 elementLineNumberCache.put(parent.getElement(i), i + 1);
109110 }
110111 result = elementLineNumberCache.get(element);
111 if (result != null)
112 if (result != null) {
112113 return result;
114 }
113115 return -1;
114116
115117 }
3434 this.highlight = highlight;
3535 }
3636
37 @Override
3738 public View create(Element elem) {
3839 String kind = elem.getName();
3940 // System.out.println("Kind: " + kind);
40 if (kind != null)
41 if (kind.equals(AbstractDocument.ContentElementName)) {
41 if (kind != null) {
42 if (AbstractDocument.ContentElementName.equals(kind)) {
4243 return new LabelView(elem);
43 } else if (kind.equals(AbstractDocument.ParagraphElementName)) {
44 } else if (AbstractDocument.ParagraphElementName.equals(kind)) {
4445 return new NumberedParagraphView(elem, highlight);
45 } else if (kind.equals(AbstractDocument.SectionElementName)) {
46 } else if (AbstractDocument.SectionElementName.equals(kind)) {
4647 return new NoWrapBoxView(elem, View.Y_AXIS);
47 } else if (kind.equals(StyleConstants.ComponentElementName)) {
48 } else if (StyleConstants.ComponentElementName.equals(kind)) {
4849 return new ComponentView(elem);
49 } else if (kind.equals(StyleConstants.IconElementName)) {
50 } else if (StyleConstants.IconElementName.equals(kind)) {
5051 return new IconView(elem);
5152 }
53 }
5254 // default to text display
5355 return new LabelView(elem);
5456 }
0 package edu.umd.cs.findbugs;
1
2 import java.lang.reflect.InvocationTargetException;
3 import java.util.Collections;
4 import java.util.List;
5 import java.util.concurrent.AbstractExecutorService;
6 import java.util.concurrent.TimeUnit;
7
8 import javax.swing.SwingUtilities;
9
10 public class AWTEventQueueExecutor extends AbstractExecutorService {
11 public void shutdown() {
12 }
13
14 public List<Runnable> shutdownNow() {
15 return Collections.emptyList();
16 }
17
18 public boolean isShutdown() {
19 return true;
20 }
21
22 public boolean isTerminated() {
23 return true;
24 }
25
26 public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
27 return true;
28 }
29
30 public void execute(Runnable command) {
31 if (SwingUtilities.isEventDispatchThread()) {
32 command.run();
33 return;
34 }
35 try {
36 SwingUtilities.invokeAndWait(command);
37 } catch (InterruptedException e) {
38 throw new IllegalStateException(e);
39 } catch (InvocationTargetException e) {
40 throw new IllegalStateException(e);
41 }
42 }
43 }
0 package edu.umd.cs.findbugs;
1
2 import java.lang.reflect.InvocationTargetException;
3 import java.util.Collections;
4 import java.util.List;
5 import java.util.concurrent.AbstractExecutorService;
6 import java.util.concurrent.TimeUnit;
7
8 import javax.swing.SwingUtilities;
9
10 public class AWTEventQueueExecutor extends AbstractExecutorService {
11 @Override
12 public void shutdown() {
13 }
14
15 @Override
16 public List<Runnable> shutdownNow() {
17 return Collections.emptyList();
18 }
19
20 @Override
21 public boolean isShutdown() {
22 return true;
23 }
24
25 @Override
26 public boolean isTerminated() {
27 return true;
28 }
29
30 @Override
31 public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
32 return true;
33 }
34
35 @Override
36 public void execute(Runnable command) {
37 if (SwingUtilities.isEventDispatchThread()) {
38 command.run();
39 return;
40 }
41 try {
42 SwingUtilities.invokeAndWait(command);
43 } catch (InterruptedException e) {
44 throw new IllegalStateException(e);
45 } catch (InvocationTargetException e) {
46 throw new IllegalStateException(e);
47 }
48 }
49 }
138138 rankThreshold = BugRanker.VISIBLE_RANK_MAX;
139139 }
140140
141 @Override
141142 public void setErrorVerbosity(int level) {
142143 this.verbosityLevel = level;
143144 }
144145
146 @Override
145147 public void setPriorityThreshold(int threshold) {
146148 this.priorityThreshold = threshold;
147149 }
149151 public void setRankThreshold(int threshold) {
150152 this.rankThreshold = threshold;
151153 }
152
154
153155 public void setIsRelaxed(boolean relaxed) {
154156 this.relaxed = relaxed;
155157 this.relaxedSet = true;
166168 return relaxed;
167169 }
168170 // Subclasses must override doReportBug(), not this method.
171 @Override
169172 public final void reportBug(@Nonnull BugInstance bugInstance) {
170173 if (isRelaxed()) {
171174 doReportBug(bugInstance);
188191 doReportBug(bugInstance);
189192 } else {
190193 if (DEBUG) {
191 if (priority <= priorityThreshold)
194 if (priority <= priorityThreshold) {
192195 System.out.println("AbstractBugReporter: Filtering due to priorityThreshold " + priority + " > "
193196 + priorityThreshold);
194 else
197 } else {
195198 System.out.println("AbstractBugReporter: Filtering due to rankThreshold " + bugRank + " > " + rankThreshold);
199 }
196200 }
197201 }
198202 }
218222 return null;
219223 }
220224
225 @Override
221226 public void reportMissingClass(ClassNotFoundException ex) {
222227 if (DEBUG_MISSING_CLASSES) {
223228 System.out.println("Missing class: " + ex.toString());
248253 return false;
249254 }
250255
251 if (message.equals("")) {
256 if ("".equals(message)) {
252257 // Subtypes2 throws ClassNotFoundExceptions with no message in
253258 // some cases. Ignore them (the missing classes will already
254259 // have been reported).
259264 // we ignore all "package-info" issues
260265 return false;
261266 }
262 if (message.equals("java.lang.Synthetic"))
267 if ("java.lang.Synthetic".equals(message)) {
263268 return false;
269 }
264270 return true;
265271 }
266272
271277 * edu.umd.cs.findbugs.classfile.IErrorLogger#reportMissingClass(edu.umd
272278 * .cs.findbugs.classfile.ClassDescriptor)
273279 */
280 @Override
274281 public void reportMissingClass(ClassDescriptor classDescriptor) {
275282 if (DEBUG_MISSING_CLASSES) {
276283 System.out.println("Missing class: " + classDescriptor);
299306 *
300307 * @param method
301308 */
309 @Override
302310 public void reportSkippedAnalysis(MethodDescriptor method) {
303311 // TODO: log this
304312 }
305313
314 @Override
306315 public void logError(String message) {
307316 if (verbosityLevel == SILENT) {
308317 return;
328337 return missingClassMessageList;
329338 }
330339
340 @Override
331341 public void logError(String message, Throwable e) {
332342
333343 if (e instanceof MethodUnprofitableException) {
360370 }
361371 }
362372
373 @Override
363374 public void reportQueuedErrors() {
364375 // Report unique errors in order of their sequence
365376 Error[] errorList = errorSet.toArray(new Error[errorSet.size()]);
366377 Arrays.sort(errorList, new Comparator<Error>() {
378 @Override
367379 public int compare(Error o1, Error o2) {
368380 return o1.getSequence() - o2.getSequence();
369381 }
377389 }
378390 }
379391
392 @Override
380393 public void addObserver(BugReporterObserver observer) {
381394 observerList.add(observer);
382395 }
383396
397 @Override
384398 public ProjectStats getProjectStats() {
385399 return projectStats;
386400 }
423437 public abstract void reportMissingClass(String string);
424438 }
425439
426 // vim:ts=4
3131 public class AddAnnotation {
3232 private static final Logger LOGGER = Logger.getLogger(AddAnnotation.class.getName());
3333
34 private BugCollection bugCollection;
34 private final BugCollection bugCollection;
3535
36 private String annotation;
36 private final String annotation;
3737
3838 public AddAnnotation(BugCollection bugCollection, String annotation) {
3939 this.bugCollection = bugCollection;
5656 public void execute() {
5757 for (BugInstance bugInstance : bugCollection) {
5858 // Don't add the annotation if it is already present
59 if (bugInstance.annotationTextContainsWord(this.annotation))
59 if (bugInstance.annotationTextContainsWord(this.annotation)) {
6060 continue;
61 }
6162
6263 String annotation = bugInstance.getAnnotationText();
6364 StringBuilder buf = new StringBuilder();
64 if (!annotation.equals("")) {
65 if (!"".equals(annotation)) {
6566 buf.append(annotation);
6667 buf.append('\n');
6768 }
8788 }
8889 }
8990
90 // vim:ts=3
154154 Element root = document.getRootElement();
155155 for (String bugType : bugTypeSet) {
156156 BugPattern bugPattern = DetectorFactoryCollection.instance().lookupBugPattern(bugType);
157 if (bugPattern == null)
157 if (bugPattern == null) {
158158 continue;
159 }
159160 Element details = root.addElement("BugPattern");
160161 details.addAttribute("type", bugType).addAttribute("abbrev", bugPattern.getAbbrev())
161 .addAttribute("category", bugPattern.getCategory());
162 .addAttribute("category", bugPattern.getCategory());
162163 details.addElement("ShortDescription").addText(bugPattern.getShortDescription());
163164 details.addElement("Details").addCDATA(bugPattern.getDetailText());
164165 }
193194 }
194195 }
195196
196 // vim:ts=4
3232 /**
3333 * An implementation of org.apache.bcel.util.Repository that uses the
3434 * AnalysisCache as its backing store.
35 *
35 *
3636 * @author David Hovemeyer
3737 */
3838 public class AnalysisCacheToRepositoryAdapter implements Repository {
4444
4545 /*
4646 * (non-Javadoc)
47 *
47 *
4848 * @see org.apache.bcel.util.Repository#clear()
4949 */
50 @Override
5051 public void clear() {
5152 throw new UnsupportedOperationException();
5253 }
5354
5455 /*
5556 * (non-Javadoc)
56 *
57 *
5758 * @see org.apache.bcel.util.Repository#findClass(java.lang.String)
5859 */
60 @Override
5961 public JavaClass findClass(String className) {
6062 @SlashedClassName
6163 String slashedClassName = ClassName.toSlashedClassName(className);
6567
6668 /*
6769 * (non-Javadoc)
68 *
70 *
6971 * @see org.apache.bcel.util.Repository#getClassPath()
7072 */
73 @Override
7174 public ClassPath getClassPath() {
7275 throw new UnsupportedOperationException();
7376 }
7477
7578 /*
7679 * (non-Javadoc)
77 *
80 *
7881 * @see org.apache.bcel.util.Repository#loadClass(java.lang.String)
7982 */
83 @Override
8084 public JavaClass loadClass(String className) throws ClassNotFoundException {
81 if (className.length() == 0)
85 if (className.length() == 0) {
8286 throw new IllegalArgumentException("Request to load empty class");
87 }
8388 className = ClassName.toSlashedClassName(className);
8489 ClassDescriptor classDescriptor = DescriptorFactory.instance().getClassDescriptor(className);
8590 try {
9196
9297 /*
9398 * (non-Javadoc)
94 *
99 *
95100 * @see org.apache.bcel.util.Repository#loadClass(java.lang.Class)
96101 */
97 public JavaClass loadClass(Class cls) throws ClassNotFoundException {
102 @Override
103 public JavaClass loadClass(Class<?> cls) throws ClassNotFoundException {
98104 return loadClass(cls.getName());
99105 }
100106
101107 /*
102108 * (non-Javadoc)
103 *
109 *
104110 * @see
105111 * org.apache.bcel.util.Repository#removeClass(org.apache.bcel.classfile
106112 * .JavaClass)
107113 */
114 @Override
108115 public void removeClass(JavaClass arg0) {
109116 throw new UnsupportedOperationException();
110117 }
111118
112119 /*
113120 * (non-Javadoc)
114 *
121 *
115122 * @see
116123 * org.apache.bcel.util.Repository#storeClass(org.apache.bcel.classfile.
117124 * JavaClass)
118125 */
126 @Override
119127 public void storeClass(JavaClass cls) {
120128 throw new UnsupportedOperationException();
121129 }
44
55 /**
66 * Object recording a recoverable error that occurred during analysis.
7 *
7 *
88 * @author David Hovemeyer
99 */
1010 public class AnalysisError {
2020
2121 private final Throwable exception;
2222
23
23
2424 @Override
2525 public int hashCode() {
2626 final int prime = 31;
3535
3636 @Override
3737 public boolean equals(Object obj) {
38 if (this == obj)
38 if (this == obj) {
3939 return true;
40 if (obj == null)
40 }
41 if (obj == null) {
4142 return false;
42 if (!(obj instanceof AnalysisError))
43 }
44 if (!(obj instanceof AnalysisError)) {
4345 return false;
46 }
4447 AnalysisError other = (AnalysisError) obj;
4548 if (exceptionMessage == null) {
46 if (other.exceptionMessage != null)
49 if (other.exceptionMessage != null) {
4750 return false;
48 } else if (!exceptionMessage.equals(other.exceptionMessage))
51 }
52 } else if (!exceptionMessage.equals(other.exceptionMessage)) {
4953 return false;
54 }
5055 if (message == null) {
51 if (other.message != null)
56 if (other.message != null) {
5257 return false;
53 } else if (!message.equals(other.message))
58 }
59 } else if (!message.equals(other.message)) {
5460 return false;
61 }
5562 if (nestedExceptionMessage == null) {
56 if (other.nestedExceptionMessage != null)
63 if (other.nestedExceptionMessage != null) {
5764 return false;
58 } else if (!nestedExceptionMessage.equals(other.nestedExceptionMessage))
65 }
66 } else if (!nestedExceptionMessage.equals(other.nestedExceptionMessage)) {
5967 return false;
60 if (!Arrays.equals(nestedStackTrace, other.nestedStackTrace))
68 }
69 if (!Arrays.equals(nestedStackTrace, other.nestedStackTrace)) {
6170 return false;
62 if (!Arrays.equals(stackTrace, other.stackTrace))
71 }
72 if (!Arrays.equals(stackTrace, other.stackTrace)) {
6373 return false;
74 }
6475 return true;
6576 }
6677
6778 /**
6879 * Constructor.
69 *
80 *
7081 * @param message
7182 * message describing the error
7283 */
7687
7788 /**
7889 * Constructor.
79 *
90 *
8091 * @param message
8192 * message describing the error
8293 * @param exception
97108 }
98109 }
99110
100 /**
101 * @param exception
102 * @return
103 */
104111 private String[] getStackTraceAsStringArray(Throwable exception) {
105112 StackTraceElement[] exceptionStackTrace = exception.getStackTrace();
106113 ArrayList<String> arr = new ArrayList<String>();
113120
114121 /**
115122 * Set the message describing the error.
116 *
123 *
117124 * @param message
118125 * message describing the error
119126 */
131138 /**
132139 * Set the exception message. This is the value returned by calling
133140 * toString() on the original exception object.
134 *
141 *
135142 * @param exceptionMessage
136143 * the exception message
137144 */
158165 /**
159166 * Set the stack trace elements. These are the strings returned by calling
160167 * toString() on each StackTraceElement in the original exception.
161 *
168 *
162169 * @param stackTraceList
163170 * the stack trace elements
164171 */
4040 try {
4141 remote = AnalysisContext.lookupSystemClass("java.rmi.Remote");
4242 } catch (ClassNotFoundException e) {
43 if (storedException == null)
43 if (storedException == null) {
4444 storedException = e;
45 }
46 }
47
45 }
46 }
47 }
48
49 /*
4850 private static boolean containsConcreteClasses(Set<JavaClass> s) {
4951 for (JavaClass c : s)
5052 if (!c.isInterface() && !c.isAbstract())
5153 return true;
5254 return false;
5355 }
56 */
5457
5558 public static double isDeepSerializable(String refSig) throws ClassNotFoundException {
56 if (storedException != null)
59 if (storedException != null) {
5760 throw storedException;
58
59 if (isPrimitiveComponentClass(refSig))
60 return 1.0;
61 }
62
63 if (isPrimitiveComponentClass(refSig)) {
64 return 1.0;
65 }
6166
6267 String refName = getComponentClass(refSig);
63 if (refName.equals("java.lang.Object"))
68 if ("java.lang.Object".equals(refName)) {
6469 return 0.99;
70 }
6571
6672 JavaClass refJavaClass = Repository.lookupClass(refName);
6773 return isDeepSerializable(refJavaClass);
6874 }
6975
7076 public static double isDeepRemote(String refSig) {
71 if (remote == null)
77 if (remote == null) {
7278 return 0.1;
79 }
7380
7481 String refName = getComponentClass(refSig);
75 if (refName.equals("java.lang.Object"))
82 if ("java.lang.Object".equals(refName)) {
7683 return 0.99;
84 }
7785
7886 JavaClass refJavaClass;
7987 try {
100108 }
101109
102110 public static String getComponentClass(String refSig) {
103 while (refSig.charAt(0) == '[')
111 while (refSig.charAt(0) == '[') {
104112 refSig = refSig.substring(1);
113 }
105114
106115 // TODO: This method now returns primitive type signatures, is this ok?
107 if (refSig.charAt(0) == 'L')
116 if (refSig.charAt(0) == 'L') {
108117 return refSig.substring(1, refSig.length() - 1).replace('/', '.');
118 }
109119 return refSig;
110120 }
111121
112122 public static double isDeepSerializable(JavaClass x) throws ClassNotFoundException {
113 if (storedException != null)
123 if (storedException != null) {
114124 throw storedException;
125 }
115126
116127 double result = deepInstanceOf(x, serializable);
117 if (result >= 0.9)
118 return result;
128 if (result >= 0.9) {
129 return result;
130 }
119131 result = Math.max(result, deepInstanceOf(x, collection));
120 if (result >= 0.9)
121 return result;
132 if (result >= 0.9) {
133 return result;
134 }
122135 result = Math.max(result, deepInstanceOf(x, map));
123 if (result >= 0.9)
124 return result;
136 if (result >= 0.9) {
137 return result;
138 }
125139 result = Math.max(result, 0.5 * deepInstanceOf(x, comparator));
126 if (result >= 0.9)
127 return result;
140 if (result >= 0.9) {
141 return result;
142 }
128143 return result;
129144 }
130145
132147 * Given two JavaClasses, try to estimate the probability that an reference
133148 * of type x is also an instance of type y. Will return 0 only if it is
134149 * impossible and 1 only if it is guaranteed.
135 *
150 *
136151 * @param x
137152 * Known type of object
138153 * @param y
149164 * Given two JavaClasses, try to estimate the probability that an reference
150165 * of type x is also an instance of type y. Will return 0 only if it is
151166 * impossible and 1 only if it is guaranteed.
152 *
167 *
153168 * @param x
154169 * Known type of object
155170 * @param y
158173 */
159174 public static double deepInstanceOf(JavaClass x, JavaClass y) throws ClassNotFoundException {
160175
161 if (x.equals(y))
162 return 1.0;
163 if (y.getClassName().equals("java.lang.Object"))
164 return 1.0;
176 if (x.equals(y)) {
177 return 1.0;
178 }
179 if ("java.lang.Object".equals(y.getClassName())) {
180 return 1.0;
181 }
165182 Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
166183 ClassDescriptor xDesc = DescriptorFactory.createClassDescriptor(x);
167184 ClassDescriptor yDesc = DescriptorFactory.createClassDescriptor(y);
168185
169186 boolean xIsSubtypeOfY = Repository.instanceOf(x, y);
170 if (xIsSubtypeOfY)
171 return 1.0;
187 if (xIsSubtypeOfY) {
188 return 1.0;
189 }
172190 boolean yIsSubtypeOfX = Repository.instanceOf(y, x);
173191 if (!yIsSubtypeOfX) {
174 if (x.isFinal() || y.isFinal())
192 if (x.isFinal() || y.isFinal()) {
175193 return 0.0;
176 if (!x.isInterface() && !y.isInterface())
194 }
195 if (!x.isInterface() && !y.isInterface()) {
177196 return 0.0;
197 }
178198 }
179199
180200 Set<ClassDescriptor> transitiveCommonSubtypes = subtypes2.getTransitiveCommonSubtypes(xDesc, yDesc);
188208 continue;
189209 }
190210 if (!cx.isAbstract() && !cx.isInterface()) {
191 if (x.isAbstract() || x.isInterface())
211 if (x.isAbstract() || x.isInterface()) {
192212 return 0.2;
213 }
193214 return 0.1;
194215 }
195216 }
200221 Set<ClassDescriptor> xButNotY = new HashSet<ClassDescriptor>(subtypes2.getSubtypes(xDesc));
201222 xButNotY.removeAll(transitiveCommonSubtypes);
202223 for (ClassDescriptor c : xButNotY) {
203
224
204225 try {
205226 XClass cx = Global.getAnalysisCache().getClassAnalysis(XClass.class, c);
206 if (!cx.isAbstract() && !cx.isInterface())
227 if (!cx.isAbstract() && !cx.isInterface()) {
207228 return 0.7;
229 }
208230 } catch (CheckedAnalysisException e) {
209231 continue;
210232 }
211
233
212234 }
213235 return 0.99;
214236 }
3131 * application. Timestamp is when FindBugs was run (according to
3232 * System.currentTimeMillis()), and the release name is available if the user
3333 * provided it.
34 *
34 *
3535 * @author David Hovemeyer
3636 */
3737 public class AppVersion implements XMLWriteable, Cloneable {
4040 */
4141 public static final String ELEMENT_NAME = "AppVersion";
4242
43 private long sequence;
43 private final long sequence;
4444
4545 private long timestamp;
4646
7070
7171 /*
7272 * (non-Javadoc)
73 *
73 *
7474 * @see java.lang.Object#clone()
7575 */
7676
9494 * @return Returns the timestamp.
9595 */
9696 public long getTimestamp() {
97 if (timestamp <= 0)
97 if (timestamp <= 0) {
9898 return System.currentTimeMillis();
99 }
99100 return timestamp;
100101 }
101102
126127
127128 /*
128129 * (non-Javadoc)
129 *
130 *
130131 * @see
131132 * edu.umd.cs.findbugs.xml.XMLWriteable#writeXML(edu.umd.cs.findbugs.xml
132133 * .XMLOutput)
133134 */
135 @Override
134136 public void writeXML(XMLOutput xmlOutput) throws IOException {
135137 xmlOutput.openCloseTag(
136138 ELEMENT_NAME,
137139 new XMLAttributeList().addAttribute("sequence", String.valueOf(sequence))
138 .addAttribute("timestamp", String.valueOf(timestamp)).addAttribute("release", releaseName)
139 .addAttribute("codeSize", String.valueOf(codeSize))
140 .addAttribute("numClasses", String.valueOf(numClasses)));
140 .addAttribute("timestamp", String.valueOf(timestamp)).addAttribute("release", releaseName)
141 .addAttribute("codeSize", String.valueOf(codeSize))
142 .addAttribute("numClasses", String.valueOf(numClasses)));
141143 }
142144
143145 /*
144146 * (non-Javadoc)
145 *
147 *
146148 * @see java.lang.Object#toString()
147149 */
148150
5252
5353 private BugInstance lastBug;
5454 private SourceLineAnnotation lastSourceLine;
55
5556 static class Data {
57
5658 public Data(int priority, SourceLineAnnotation primarySource) {
5759 this.priority = priority;
5860 this.primarySource = primarySource;
8587 public void forgetLastBug() {
8688 Data d = map.get(lastBug);
8789 if (d != null) {
88
8990 d.allSource.remove(lastSourceLine);
9091 if (d.allSource.isEmpty()) {
9192 map.remove(lastBug);
9596 lastBug = null;
9697 lastSourceLine = null;
9798 }
99
98100 /**
99101 * Accumulate a warning at given source location.
100102 *
104106 * the source location
105107 */
106108 public void accumulateBug(BugInstance bug, SourceLineAnnotation sourceLine) {
107 if (sourceLine == null)
109 if (sourceLine == null) {
108110 throw new NullPointerException("Missing source line");
111 }
109112 int priority = bug.getPriority();
110 if (!performAccumulation)
113 if (!performAccumulation) {
111114 bug.addSourceLine(sourceLine);
112 else
115 } else {
113116 bug.setPriority(Priorities.NORMAL_PRIORITY);
117 }
114118
115119 lastBug = bug;
116120 lastSourceLine = sourceLine;
119123 String hash = bug.getInstanceHash();
120124 BugInstance conflictingBug = hashes.get(hash);
121125 if (conflictingBug != null) {
122 if (conflictingBug.getPriority() <= priority)
126 if (conflictingBug.getPriority() <= priority) {
123127 return;
128 }
124129 map.remove(conflictingBug);
125130 }
126131 d = new Data(priority, sourceLine);
127132 map.put(bug, d);
128133 hashes.put(hash, bug);
129134 } else if (d.priority > priority) {
130 if (d.priority >= Priorities.LOW_PRIORITY)
135 if (d.priority >= Priorities.LOW_PRIORITY) {
136 reportBug(bug, d);
131137 d.allSource.clear();
138 }
132139 d.priority = priority;
133140 d.primarySource = sourceLine;
134 } else if (priority >= Priorities.LOW_PRIORITY && priority > d.priority)
141 } else if (priority >= Priorities.LOW_PRIORITY && priority > d.priority) {
142 bug.setPriority(priority);
143 reporter.reportBug(bug);
135144 return;
145 }
136146 d.allSource.add(sourceLine);
137147 }
138148
167177 for (Map.Entry<BugInstance, Data> e : map.entrySet()) {
168178 BugInstance bug = e.getKey();
169179 Data d = e.getValue();
170 bug.setPriority(d.priority);
171 bug.addSourceLine(d.primarySource);
172 HashSet<Integer> lines = new HashSet<Integer>();
173 lines.add(d.primarySource.getStartLine());
174 d.allSource.remove(d.primarySource);
175 for (SourceLineAnnotation source : d.allSource) if (lines.add(source.getStartLine())) {
180 reportBug(bug, d);
181 }
182 clearBugs();
183 }
184
185 public void reportBug(BugInstance bug, Data d) {
186 bug.setPriority(d.priority);
187 bug.addSourceLine(d.primarySource);
188 HashSet<Integer> lines = new HashSet<Integer>();
189 lines.add(d.primarySource.getStartLine());
190 d.allSource.remove(d.primarySource);
191 for (SourceLineAnnotation source : d.allSource) {
192 if (lines.add(source.getStartLine())) {
176193 bug.addSourceLine(source);
177194 bug.describe(SourceLineAnnotation.ROLE_ANOTHER_INSTANCE);
178 } else if (false && SystemProperties.ASSERTIONS_ENABLED) {
195 } /* else if (false && SystemProperties.ASSERTIONS_ENABLED) {
179196 AnalysisContext.logError("Skipping duplicated source warning for " + bug.getInstanceHash() + " " + bug.getMessage());
180 }
181 reporter.reportBug(bug);
182 }
183 clearBugs();
197 }*/
198 }
199 reporter.reportBug(bug);
184200 }
185201
186202 /**
2222
2323 /**
2424 * An object providing context information about a particular BugInstance.
25 *
25 *
2626 * @author David Hovemeyer
2727 * @see BugInstance
2828 */
3636
3737 /**
3838 * Accept a BugAnnotationVisitor.
39 *
39 *
4040 * @param visitor
4141 * the visitor to accept
4242 */
4646 * Format the annotation as a String. The given key specifies additional
4747 * information about how the annotation should be formatted. If the key is
4848 * empty, then the "default" format will be used.
49 *
49 *
5050 * @param key
5151 * how the annotation should be formatted
5252 * @param primaryClass
7070 /**
7171 * Is this annotation used to compute instance hashes or match bug instances
7272 * across versions
73 *
73 *
7474 * @return true if significant
7575 */
7676 public boolean isSignificant();
7878 public String toString(ClassAnnotation primaryClass);
7979 }
8080
81 // vim:ts=4
2424
2525 /**
2626 * Utility methods for BugAnnotation classes.
27 *
27 *
2828 * @author David Hovemeyer
2929 */
3030 public abstract class BugAnnotationUtil {
3131 /**
3232 * Write a BugAnnotation as XML.
33 *
33 *
3434 * @param xmlOutput
3535 * the XMLOutput
3636 * @param elementName
4747 XMLAttributeList attributeList, boolean addMessages) throws IOException {
4848
4949 SourceLineAnnotation src = null;
50 if (annotation instanceof BugAnnotationWithSourceLines)
50 if (annotation instanceof BugAnnotationWithSourceLines) {
5151 src = ((BugAnnotationWithSourceLines) annotation).getSourceLines();
52 }
5253
5354 if (addMessages || src != null) {
5455 xmlOutput.openTag(elementName, attributeList);
55 if (src != null)
56 if (src != null) {
5657 src.writeXML(xmlOutput, addMessages, false);
58 }
5759 if (addMessages) {
5860 xmlOutput.openTag(BugAnnotation.MESSAGE_TAG);
5961 xmlOutput.writeText(annotation.toString());
3636 public void visitSourceLineAnnotation(SourceLineAnnotation sourceLineAnnotation);
3737 }
3838
39 // vim:ts=4
6262 /**
6363 * Get the SourceLineAnnotation describing the source lines where the method
6464 * is defined.
65 *
65 *
6666 * @return the SourceLineAnnotation, or null if there is no source
6767 * information for this package element
6868 */
7070 return sourceLines;
7171 }
7272
73 @Override
7374 public String toString(ClassAnnotation primaryClass) {
7475 return toString();
7576 }
3030 private String abbrev;
3131
3232 private String detailText;
33
33
3434 private boolean hidden;
3535
3636 /**
3737 * Constructor.
38 *
38 *
3939 * @param category
4040 * the category
4141 * @param shortDescription
5555
5656 /**
5757 * Constructor.
58 *
58 *
5959 * @param category
6060 * the category
6161 * @param shortDescription
6767
6868 /**
6969 * Constructor.
70 *
70 *
7171 * @param category
7272 * the category
73 * @param shortDescription
74 * short (a word or three) description of the bug species
7573 */
7674 public BugCategory(String category) {
7775 this(category, null, null, null);
113111 public void setShortDescription(String shortDescription) {
114112 this.shortDescription = shortDescription;
115113 }
116
114
117115 /**
118116 * Set the abbreviation (typically a single capital letter)
119117 */
129127 this.detailText = detailText;
130128 }
131129
130 @Override
132131 public int compareTo(BugCategory other) {
133132 return category.compareTo(other.category);
134133 }
140139
141140 @Override
142141 public boolean equals(Object o) {
143 if (!(o instanceof BugCategory))
142 if (!(o instanceof BugCategory)) {
144143 return false;
144 }
145145 BugCategory other = (BugCategory) o;
146146 return category.equals(other.category);
147147 }
2222 * A BugCode is an abbreviation that is shared among some number of BugPatterns.
2323 * For example, the code "HE" is shared by all of the BugPatterns that represent
2424 * hashcode/equals violations.
25 *
25 *
2626 * @author David Hovemeyer
2727 * @see BugPattern
2828 */
3535
3636 /**
3737 * Constructor.
38 *
38 *
3939 * @param abbrev
4040 * the abbreviation for the bug code
4141 * @param description
8383 return cweid;
8484 }
8585
86 @Override
8687 public int compareTo(BugCode o) {
8788 return abbrev.compareTo(o.abbrev);
8889 }
4747 static final String ERRORS_ELEMENT_NAME = "Errors";
4848
4949 static final String ANALYSIS_ERROR_ELEMENT_NAME = "AnalysisError"; // 0.8.6
50 // and
51 // earlier
50 // and
51 // earlier
5252
5353 static final String ERROR_ELEMENT_NAME = "Error"; // 0.8.7 and later
5454
5555 static final String ERROR_MESSAGE_ELEMENT_NAME = "ErrorMessage"; // 0.8.7
56 // and
57 // later
56 // and
57 // later
5858
5959 static final String ERROR_EXCEPTION_ELEMENT_NAME = "Exception"; // 0.8.7 and
60 // later
60 // later
6161
6262 static final String ERROR_STACK_TRACE_ELEMENT_NAME = "StackTrace"; // 0.8.7
63 // and
64 // later
63 // and
64 // later
6565
6666 static final String MISSING_CLASS_ELEMENT_NAME = "MissingClass";
6767
7070 static final String APP_CLASS_ELEMENT_NAME = "AppClass";
7171
7272 static final String CLASS_HASHES_ELEMENT_NAME = "ClassHashes"; // 0.9.2 and
73 // later
73 // later
7474
7575 static final String HISTORY_ELEMENT_NAME = "History"; // 0.9.2 and later
7676
329329 /**
330330 * Return an Iterator over all the BugInstance objects in the BugCollection.
331331 */
332 @Override
332333 public Iterator<BugInstance> iterator();
333334
334335 /**
4949 return project;
5050 }
5151
52 @Override
5253 public @Nonnull
5354 BugCollection getBugCollection() {
5455 return bugCollection;
5556 }
5657
58 @Override
5759 public void observeClass(ClassDescriptor classDescriptor) {
5860 }
5961
9092
9193 @Override
9294 public void doReportBug(BugInstance bugInstance) {
93 if (VERIFY_INTEGRITY)
95 if (VERIFY_INTEGRITY) {
9496 checkBugInstance(bugInstance);
95 if (bugCollection.add(bugInstance))
97 }
98 if (bugCollection.add(bugInstance)) {
9699 notifyObservers(bugInstance);
100 }
97101 }
98102
99103 /*
111115 *
112116 * @see edu.umd.cs.findbugs.BugReporter#finish()
113117 */
118 @Override
114119 public void finish() {
115 bugCollection.bugsPopulated();
116 if (writer != null)
120 bugCollection.bugsPopulated();
121 if (writer != null) {
117122 writer.flush();
123 }
118124 }
119125
120126 /**
137143
138144 }
139145
140 // vim:ts=4
5151 @Override
5252 public String toString() {
5353 String result = designation;
54 if (user != null)
54 if (user != null) {
5555 result += " by " + user;
56 if (annotationText != null && annotationText.length() > 0)
56 }
57 if (annotationText != null && annotationText.length() > 0) {
5758 result += " : " + annotationText;
59 }
5860 return result;
5961 }
6062
6870 setDirty(false);
6971 }
7072 public void setDirty(boolean dirty) {
71 if (this.dirty == dirty)
73 if (this.dirty == dirty) {
7274 return;
75 }
7376 this.dirty = dirty;
74 // if (dirty) {
75 // System.out.println("Setting dirty bit");
76 // new RuntimeException("Setting dirty bit").printStackTrace(System.out);
77 // } else {
78 // System.out.println("Clearing dirty bit");
79 // new RuntimeException("Clearing dirty bit").printStackTrace(System.out);
80 // }
77 // if (dirty) {
78 // System.out.println("Setting dirty bit");
79 // new RuntimeException("Setting dirty bit").printStackTrace(System.out);
80 // } else {
81 // System.out.println("Clearing dirty bit");
82 // new RuntimeException("Clearing dirty bit").printStackTrace(System.out);
83 // }
8184 }
8285
8386 private @javax.annotation.CheckForNull
143146 assert false;
144147 designationKey = null;
145148 }
146 if (designation.equals(designationKey))
149 if (designation.equals(designationKey)) {
147150 return;
151 }
148152 setDirty(true);
149153 timestamp = System.currentTimeMillis();
150154 designation = (designationKey != null ? designationKey : UNCLASSIFIED);
166170 public void setTimestamp(long ts) {
167171 if (timestamp != ts) {
168172 timestamp = ts;
169 if (false && !hasAnnotationText() && !hasDesignationKey())
173 if (false && !hasAnnotationText() && !hasDesignationKey()) {
170174 new RuntimeException("Setting timestamp on bug designation without annotation or designation").printStackTrace(System.out);
175 }
171176 setDirty(true);
172177 }
173178 }
183188
184189 @Nonnull
185190 public String getNonnullAnnotationText() {
186 if (annotationText == null)
191 if (annotationText == null) {
187192 return "";
193 }
188194 return annotationText;
189195 }
190196
191197 public void setAnnotationText(String s) {
192 if (s.equals(annotationText))
198 if (s.equals(annotationText)) {
193199 return;
200 }
194201 setDirty(true);
195202 annotationText = s;
196203 timestamp = System.currentTimeMillis();
197204 }
198205
206 @Override
199207 public void writeXML(XMLOutput xmlOutput) throws IOException {
200208 XMLAttributeList attributeList = new XMLAttributeList();
201209 // all three of these xml attributes are optional
202 if (hasDesignationKey())
210 if (hasDesignationKey()) {
203211 attributeList.addAttribute("designation", designation);
204 if (user != null && !"".equals(user))
212 }
213 if (user != null && !"".equals(user)) {
205214 attributeList.addAttribute("user", user);
206 if (dirty)
215 }
216 if (dirty) {
207217 attributeList.addAttribute("needsSync", "true");
208 if (timestamp > 0)
218 }
219 if (timestamp > 0) {
209220 attributeList.addAttribute("timestamp", String.valueOf(timestamp));
221 }
210222
211223 if ((annotationText != null && !"".equals(annotationText))) {
212224 xmlOutput.openTag("UserAnnotation", attributeList);
222234 * other
223235 */
224236 public void merge(@CheckForNull BugDesignation other) {
225 if (other == null)
237 if (other == null) {
226238 return;
239 }
227240 boolean changed = false;
228241 if ((annotationText == null || annotationText.length() == 0) && other.annotationText != null
229242 && other.annotationText.length() > 0) {
237250 changed = true;
238251 }
239252 if (!changed)
253 {
240254 return; // if no changes don't even try to copy user or timestamp
255 }
241256
242257 if ((user == null || user.length() == 0) && other.user != null && other.user.length() > 0) {
243258 user = other.user;
250265 @Override
251266 public int hashCode() {
252267 int hash = (int) this.timestamp;
253 if (user != null)
268 if (user != null) {
254269 hash += user.hashCode();
270 }
255271 hash += designation.hashCode();
256 if (annotationText != null)
272 if (annotationText != null) {
257273 hash += annotationText.hashCode();
274 }
258275 return hash;
259276 }
260277
261278 @Override
262279 public boolean equals(Object o) {
263 if (!(o instanceof BugDesignation))
280 if (!(o instanceof BugDesignation)) {
264281 return false;
282 }
265283 return this.compareTo((BugDesignation) o) == 0;
266284 }
267285
286 @Override
268287 public int compareTo(BugDesignation o) {
269 if (this == o)
288 if (this == o) {
270289 return 0;
290 }
271291 int result = Util.compare(o.timestamp, this.timestamp);
272 if (result != 0)
292 if (result != 0) {
273293 return result;
294 }
274295
275296 result = Util.nullSafeCompareTo(this.user, o.user);
276 if (result != 0)
297 if (result != 0) {
277298 return result;
299 }
278300
279301 result = this.designation.compareTo(o.designation);
280 if (result != 0)
302 if (result != 0) {
281303 return result;
304 }
282305 result = Util.nullSafeCompareTo(this.annotationText, o.annotationText);
283 if (result != 0)
306 if (result != 0) {
284307 return result;
308 }
285309
286310 return 0;
287311
1818
1919 package edu.umd.cs.findbugs;
2020
21 import static java.util.Objects.requireNonNull;
22
2123 import java.io.IOException;
2224 import java.io.Serializable;
2325 import java.io.UnsupportedEncodingException;
2729 import java.util.ArrayList;
2830 import java.util.Collection;
2931 import java.util.Collections;
32 import java.util.Comparator;
3033 import java.util.Date;
3134 import java.util.HashSet;
3235 import java.util.IdentityHashMap;
112115 * @see BugAnnotation
113116 */
114117 public class BugInstance implements Comparable<BugInstance>, XMLWriteable, Serializable, Cloneable {
118
115119 private static final long serialVersionUID = 1L;
116120
117121 private final String type;
138142 @CheckForNull
139143 private DetectorFactory detectorFactory;
140144
141 private final AtomicReference<XmlProps> xmlProps = new AtomicReference<XmlProps>();
145 private final AtomicReference<XmlProps> xmlProps;
142146
143147 /*
144148 * The following fields are used for tracking Bug instances across multiple
145149 * versions of software. They are meaningless in a BugCollection for just
146150 * one version of software.
147151 */
148 private long firstVersion = 0;
149
150 private long lastVersion = -1;
152 private long firstVersion;
153
154 private long lastVersion;
151155
152156 private boolean introducedByChangeOfExistingClass;
153157
159163 */
160164 private static final int INVALID_HASH_CODE = 0;
161165
166 private static final String ELEMENT_NAME = "BugInstance";
167
162168 /**
163169 * This value is used to indicate whether BugInstances should be
164170 * reprioritized very low, when the BugPattern is marked as experimental
165171 */
166 private static boolean adjustExperimental = false;
172 private static boolean adjustExperimental;
167173
168174 private static Set<String> missingBugTypes = Collections.synchronizedSet(new HashSet<String>());
169
170 public static DateFormat firstSeenXMLFormat() {
171 return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.ENGLISH);
172 }
173
174 private boolean isFakeBugType(String type) {
175 return "MISSING".equals(type) || "FOUND".equals(type);
176 }
177175
178176 public static class NoSuchBugPattern extends IllegalArgumentException {
179177 public final String type;
182180 this.type = type;
183181 }
184182 }
183
185184 /**
186185 * Constructor.
187186 *
193192 public BugInstance(String type, int priority) {
194193 this.type = type.intern();
195194 this.priority = priority;
195 lastVersion = -1;
196 xmlProps = new AtomicReference<XmlProps>();
196197 annotationList = new ArrayList<BugAnnotation>(4);
197198 cachedHashCode = INVALID_HASH_CODE;
198199
202203 String msg = "Can't find definition of bug type " + type;
203204 AnalysisContext.logError(msg, new NoSuchBugPattern(type));
204205 }
205 } else
206 } else {
206207 this.priority += p.getPriorityAdjustment();
207 if (adjustExperimental && isExperimental())
208 }
209 if (adjustExperimental && isExperimental()) {
208210 this.priority = Priorities.EXP_PRIORITY;
211 }
209212 boundPriority();
210213 }
214
215 public static DateFormat firstSeenXMLFormat() {
216 return DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT, Locale.ENGLISH);
217 }
218
219 /*
220 private boolean isFakeBugType(String type) {
221 return "MISSING".equals(type) || "FOUND".equals(type);
222 }
223 */
211224
212225 private void boundPriority() {
213226 priority = boundedPriority(priority);
216229 @Override
217230 public Object clone() {
218231 BugInstance dup;
219
220232 try {
221233 dup = (BugInstance) super.clone();
222234
266278 this.priority += factory.getPriorityAdjustment();
267279 boundPriority();
268280 BugPattern bugPattern = getBugPattern();
269 if (SystemProperties.ASSERTIONS_ENABLED && !bugPattern.getCategory().equals("EXPERIMENTAL")
281 if (SystemProperties.ASSERTIONS_ENABLED && !"EXPERIMENTAL".equals(bugPattern.getCategory())
270282 && !factory.getReportedBugPatterns().contains(bugPattern)) {
271283 AnalysisContext.logError(factory.getShortName() + " doesn't note that it reports "
272 + bugPattern + " in category " + bugPattern.getCategory());
284 + bugPattern + " in category " + bugPattern.getCategory());
273285 }
274286 }
275287 }
316328 /**
317329 * Get the BugPattern.
318330 */
319 public @Nonnull
320 BugPattern getBugPattern() {
331 public @Nonnull BugPattern getBugPattern() {
321332 BugPattern result = DetectorFactoryCollection.instance().lookupBugPattern(getType());
322 if (result != null)
333 if (result != null) {
323334 return result;
335 }
324336 AnalysisContext.logError("Unable to find description of bug pattern " + getType());
325337 result = DetectorFactoryCollection.instance().lookupBugPattern("UNKNOWN");
326 if (result != null)
338 if (result != null) {
327339 return result;
340 }
328341 return BugPattern.REALLY_UNKNOWN;
329342 }
330343
372385 // first, get the priority
373386 int value = this.getPriority();
374387 String priorityString;
375 if (value == Priorities.HIGH_PRIORITY)
388 if (value == Priorities.HIGH_PRIORITY) {
376389 priorityString = edu.umd.cs.findbugs.L10N.getLocalString("sort.priority_high", "High");
377 else if (value == Priorities.NORMAL_PRIORITY)
390 } else if (value == Priorities.NORMAL_PRIORITY) {
378391 priorityString = edu.umd.cs.findbugs.L10N.getLocalString("sort.priority_normal", "Medium");
379 else if (value == Priorities.LOW_PRIORITY)
392 } else if (value == Priorities.LOW_PRIORITY) {
380393 priorityString = edu.umd.cs.findbugs.L10N.getLocalString("sort.priority_low", "Low");
381 else if (value == Priorities.EXP_PRIORITY)
394 } else if (value == Priorities.EXP_PRIORITY) {
382395 priorityString = edu.umd.cs.findbugs.L10N.getLocalString("sort.priority_experimental", "Experimental");
383 else
396 }
397 else {
384398 priorityString = edu.umd.cs.findbugs.L10N.getLocalString("sort.priority_ignore", "Ignore"); // This
385 // probably
386 // shouldn't
387 // ever
388 // happen,
389 // but
390 // what
391 // the
392 // hell,
393 // let's
394 // be
395 // complete
399 }
400 // probably shouldn't ever happen, but what the hell, let's be complete
396401 return priorityString;
397402 }
398403
440445 }
441446
442447 /**
448 * Get the primary type annotation, which indicates where the bug occurs.
449 */
450 @CheckForNull
451 public TypeAnnotation getPrimaryType() {
452 return findPrimaryAnnotationOfType(TypeAnnotation.class);
453 }
454
455 /**
443456 * Get the primary method annotation, which indicates where the bug occurs.
444457 */
458 @CheckForNull
445459 public MethodAnnotation getPrimaryMethod() {
446460 return findPrimaryAnnotationOfType(MethodAnnotation.class);
447461 }
449463 /**
450464 * Get the primary method annotation, which indicates where the bug occurs.
451465 */
466 @CheckForNull
452467 public FieldAnnotation getPrimaryField() {
453468 return findPrimaryAnnotationOfType(FieldAnnotation.class);
454469 }
456471 @Nonnull
457472 public BugInstance lowerPriorityIfDeprecated() {
458473 MethodAnnotation m = getPrimaryMethod();
459 if (m != null && XFactory.createXMethod(m).isDeprecated())
474 if (m != null && XFactory.createXMethod(m).isDeprecated()) {
460475 lowerPriority();
476 }
461477 FieldAnnotation f = getPrimaryField();
462 if (f != null && XFactory.createXField(f).isDeprecated())
478 if (f != null && XFactory.createXField(f).isDeprecated()) {
463479 lowerPriority();
480 }
464481 return this;
465482 }
466483
473490 * @return the first matching BugAnnotation of the given type, or null if
474491 * there is no such BugAnnotation
475492 */
493 @CheckForNull
476494 private <T extends BugAnnotation> T findPrimaryAnnotationOfType(Class<T> cls) {
477495 T firstMatch = null;
478496 for (Iterator<BugAnnotation> i = annotationIterator(); i.hasNext();) {
479497 BugAnnotation annotation = i.next();
480498 if (cls.isAssignableFrom(annotation.getClass())) {
481 if (annotation.getDescription().endsWith("DEFAULT"))
499 if (annotation.getDescription().endsWith("DEFAULT")) {
482500 return cls.cast(annotation);
483 else if (firstMatch == null)
501 } else if (firstMatch == null) {
484502 firstMatch = cls.cast(annotation);
503 }
485504 }
486505 }
487506 return firstMatch;
488507 }
489508
490509 public LocalVariableAnnotation getPrimaryLocalVariableAnnotation() {
491 for (BugAnnotation annotation : annotationList)
492 if (annotation instanceof LocalVariableAnnotation)
510 for (BugAnnotation annotation : annotationList) {
511 if (annotation instanceof LocalVariableAnnotation) {
493512 return (LocalVariableAnnotation) annotation;
513 }
514 }
494515 return null;
495516 }
496517
500521 *
501522 * @return the source line annotation
502523 */
524 @Nonnull
503525 public SourceLineAnnotation getPrimarySourceLineAnnotation() {
504526 // Highest priority: return the first top level source line annotation
505527 for (BugAnnotation annotation : annotationList) {
506528 if (annotation instanceof SourceLineAnnotation
507 && annotation.getDescription().equals(SourceLineAnnotation.DEFAULT_ROLE)
508 && !((SourceLineAnnotation) annotation).isUnknown())
509
529 && SourceLineAnnotation.DEFAULT_ROLE.equals(annotation.getDescription())
530 && !((SourceLineAnnotation) annotation).isUnknown()) {
510531 return (SourceLineAnnotation) annotation;
532 }
511533 }
512534
513535 for (BugAnnotation annotation : annotationList) {
514 if (annotation instanceof SourceLineAnnotation && !((SourceLineAnnotation) annotation).isUnknown())
515
536 if (annotation instanceof SourceLineAnnotation && !((SourceLineAnnotation) annotation).isUnknown()) {
516537 return (SourceLineAnnotation) annotation;
538 }
517539 }
518540 // Next: Try primary method, primary field, primary class
519541 SourceLineAnnotation srcLine;
520 if ((srcLine = inspectPackageMemberSourceLines(getPrimaryMethod())) != null)
542 if ((srcLine = inspectPackageMemberSourceLines(getPrimaryMethod())) != null) {
521543 return srcLine;
522 if ((srcLine = inspectPackageMemberSourceLines(getPrimaryField())) != null)
544 }
545 if ((srcLine = inspectPackageMemberSourceLines(getPrimaryField())) != null) {
523546 return srcLine;
524 if ((srcLine = inspectPackageMemberSourceLines(getPrimaryClass())) != null)
547 }
548 if ((srcLine = inspectPackageMemberSourceLines(getPrimaryClass())) != null) {
525549 return srcLine;
550 }
526551
527552 // Last resort: throw exception
528553 throw new IllegalStateException("BugInstance for " + getType()
529554 + " must contain at least one class, method, or field annotation");
530555 }
531556
557 public Collection<? extends SourceLineAnnotation> getAnotherInstanceSourceLineAnnotations() {
558 // Highest priority: return the first top level source line annotation
559 Collection<SourceLineAnnotation> result = new ArrayList<SourceLineAnnotation>();
560 for (BugAnnotation annotation : annotationList) {
561 if (annotation instanceof SourceLineAnnotation
562 && SourceLineAnnotation.ROLE_ANOTHER_INSTANCE.equals(annotation.getDescription())
563 && !((SourceLineAnnotation) annotation).isUnknown()) {
564 result.add((SourceLineAnnotation) annotation);
565 }
566 }
567
568 return result;
569 }
570
571
532572 public String getInstanceKey() {
533573 String newValue = getInstanceKeyNew();
534574 return newValue;
536576
537577 private String getInstanceKeyNew() {
538578 StringBuilder buf = new StringBuilder(type);
539 for (BugAnnotation annotation : annotationList)
579 for (BugAnnotation annotation : annotationList) {
540580 if (annotation.isSignificant() || annotation instanceof IntAnnotation
541581 || annotation instanceof LocalVariableAnnotation) {
542582 buf.append(":");
543583 buf.append(annotation.format("hash", null));
544584 }
585 }
545586
546587 return buf.toString();
547588 }
575616
576617 /** Get the first bug annotation with the specified class and role; return null if no
577618 * such annotation exists;
578 * @param role
579 * @return
580619 */
581620 public @CheckForNull <A extends BugAnnotation> A getAnnotationWithRole(Class<A> c, String role) {
582621 for(BugAnnotation a : annotationList) {
583 if (c.isInstance(a) && Util.nullSafeEquals(role, a.getDescription()))
622 if (c.isInstance(a) && Util.nullSafeEquals(role, a.getDescription())) {
584623 return c.cast(a);
624 }
585625 }
586626 return null;
587
588627 }
589628
590629 /**
623662 @Deprecated
624663 @Nullable
625664 public BugDesignation getUserDesignation() {
626 if (userDesignation == null)
665 if (userDesignation == null) {
627666 return null;
628 if (!userDesignation.hasAnnotationText() && !userDesignation.hasDesignationKey())
667 }
668 if (!userDesignation.hasAnnotationText() && !userDesignation.hasDesignationKey()) {
629669 return null;
670 }
630671 return userDesignation;
631672 }
632673
640681 @Deprecated
641682 @Nonnull
642683 public BugDesignation getNonnullUserDesignation() {
643 if (userDesignation == null)
684 if (userDesignation == null) {
644685 userDesignation = new BugDesignation();
686 }
645687 return userDesignation;
646688 }
647689
659701 */
660702 @Nonnull
661703 public String getUserDesignationKey() {
662 if (userDesignation == null)
704 if (userDesignation == null) {
663705 return BugDesignation.UNCLASSIFIED;
706 }
664707 return userDesignation.getDesignationKey();
665708 }
666709
667 public @CheckForNull
668 String getUserName() {
669 if (userDesignation == null)
710 public @CheckForNull String getUserName() {
711 if (userDesignation == null) {
670712 return null;
713 }
671714 return userDesignation.getUser();
672715 }
673716
674717 public long getUserTimestamp() {
675 if (userDesignation == null)
718 if (userDesignation == null) {
676719 return Long.MAX_VALUE;
720 }
677721 return userDesignation.getTimestamp();
678722 }
679723
681725 return I18N.instance().getUserDesignationKeys(true).indexOf(getUserDesignationKey());
682726 }
683727
684 /**
685 * @param key
686 * @param bugCollection
687 * TODO
688 */
689728 public void setUserDesignationKey(String key, @CheckForNull BugCollection bugCollection) {
690729 BugDesignation userDesignation = key.length() > 0 ? getNonnullUserDesignation() : getUserDesignation();
691 if (userDesignation == null)
730 if (userDesignation == null) {
692731 return;
693 if (userDesignation.getDesignationKey().equals(key))
732 }
733 if (userDesignation.getDesignationKey().equals(key)) {
694734 return;
735 }
695736 userDesignation.setDesignationKey(key);
696737 Cloud plugin = bugCollection != null ? bugCollection.getCloud() : null;
697 if (plugin != null)
738 if (plugin != null) {
698739 plugin.storeUserAnnotation(this);
699 }
700
701 /**
702 * s
703 *
704 * @param index
705 * @param bugCollection
706 * TODO
707 */
740 }
741 }
742
708743 public void setUserDesignationKeyIndex(int index, @CheckForNull BugCollection bugCollection) {
709744 setUserDesignationKey(I18N.instance().getUserDesignationKey(index), bugCollection);
710745 }
714749 *
715750 * @param annotationText
716751 * the user annotation text
717 * @param bugCollection
718 * TODO
719752 */
720753 public void setAnnotationText(String annotationText, @CheckForNull BugCollection bugCollection) {
721754 BugDesignation u = annotationText.length() > 0 ? getNonnullUserDesignation() : getUserDesignation();
722 if (u == null)
755 if (u == null) {
723756 return;
757 }
724758 String existingText = u.getNonnullAnnotationText();
725 if (existingText.equals(annotationText))
759 if (existingText.equals(annotationText)) {
726760 return;
761 }
727762 u.setAnnotationText(annotationText);
728763 Cloud plugin = bugCollection != null ? bugCollection.getCloud() : null;
729 if (plugin != null)
764 if (plugin != null) {
730765 plugin.storeUserAnnotation(this);
766 }
731767 }
732768
733769 /**
738774 @Nonnull
739775 public String getAnnotationText() {
740776 BugDesignation userDesignation = this.userDesignation;
741 if (userDesignation == null)
777 if (userDesignation == null) {
742778 return "";
779 }
743780 String s = userDesignation.getAnnotationText();
744 if (s == null)
781 if (s == null) {
745782 return "";
783 }
746784 return s;
747785 }
748786
758796
759797 public void setUserAnnotationDirty(boolean dirty) {
760798 BugDesignation userDesignation = getUserDesignation();
761 if (userDesignation == null)
799 if (userDesignation == null) {
762800 return;
801 }
763802 userDesignation.setDirty(dirty);
764803 }
765804
766805 public boolean isUserAnnotationDirty() {
767806 BugDesignation userDesignation = getUserDesignation();
768 if (userDesignation == null)
807 if (userDesignation == null) {
769808 return false;
809 }
770810 return userDesignation.isDirty();
771811 }
812
772813 /**
773814 * Determine whether or not the annotation text contains the given word.
774815 *
800841
801842 public XmlProps getXmlProps() {
802843 XmlProps props = xmlProps.get();
803 if (props != null)
844 if (props != null) {
804845 return props;
846 }
805847
806848 props = new XmlProps();
807 while (xmlProps.get() == null)
849 while (xmlProps.get() == null) {
808850 xmlProps.compareAndSet(null, props);
851 }
809852 return xmlProps.get();
810853 }
811854
812855 public boolean hasSomeUserAnnotation() {
813 return !getAnnotationText().equals("")
856 return !"".equals(getAnnotationText())
814857 || !getUserDesignationKey().equals(BugDesignation.UNCLASSIFIED);
815858 }
816859
825868
826869 private boolean removed;
827870
828 /*
829 * (non-Javadoc)
830 *
831 * @see java.util.Iterator#hasNext()
832 */
871 @Override
833872 public boolean hasNext() {
834873 return findNext() != null;
835874 }
836875
837 /*
838 * (non-Javadoc)
839 *
840 * @see java.util.Iterator#next()
841 */
876 @Override
842877 public BugProperty next() {
843878 BugProperty next = findNext();
844 if (next == null)
879 if (next == null) {
845880 throw new NoSuchElementException();
881 }
846882 prev = cur;
847883 cur = next;
848884 removed = false;
849885 return cur;
850886 }
851887
852 /*
853 * (non-Javadoc)
854 *
855 * @see java.util.Iterator#remove()
856 */
888 @Override
857889 public void remove() {
858 if (cur == null || removed)
890 if (cur == null || removed) {
859891 throw new IllegalStateException();
892 }
860893 if (prev == null) {
861894 propertyListHead = cur.getNext();
862895 } else {
871904 private BugProperty findNext() {
872905 return cur == null ? propertyListHead : cur.getNext();
873906 }
874
875907 }
876908
877909 /**
945977 BugProperty prop = propertyListHead;
946978
947979 while (prop != null) {
948 if (prop.getName().equals(name))
980 if (prop.getName().equals(name)) {
949981 break;
982 }
950983 prop = prop.getNext();
951984 }
952985
966999 BugProperty prop = propertyListHead;
9671000
9681001 while (prop != null) {
969 if (prop.getName().equals(name))
1002 if (prop.getName().equals(name)) {
9701003 break;
1004 }
9711005 prev = prop;
9721006 prop = prop.getNext();
9731007 }
10521086 addClass(visitor);
10531087 XMethod m = visitor.getXMethod();
10541088 addMethod(visitor);
1055 if (m.isSynthetic())
1089 if (m.isSynthetic()) {
10561090 foundInSyntheticMethod();
1091 }
10571092 return this;
10581093 }
10591094
10601095 public void foundInSyntheticMethod() {
1061 if (annotationList.size() != 2)
1096 if (annotationList.size() != 2) {
10621097 return;
1098 }
10631099 priority+=2;
10641100 setProperty("FOUND_IN_SYNTHETIC_METHOD", "true");
1065 if (SystemProperties.ASSERTIONS_ENABLED && AnalysisContext.analyzingApplicationClass() && priority <= 3)
1101 if (SystemProperties.ASSERTIONS_ENABLED && AnalysisContext.analyzingApplicationClass() && priority <= 3) {
10661102 AnalysisContext.logError("Adding error " + getBugPattern().getType() + " to synthetic method " + getPrimaryMethod());
1103 }
10671104 }
10681105
10691106 /**
10931130 public BugInstance addClassAndMethod(MethodGen methodGen, String sourceFile) {
10941131 addClass(methodGen.getClassName());
10951132 addMethod(methodGen, sourceFile);
1096 if (BCELUtil.isSynthetic(methodGen))
1133 if (BCELUtil.isSynthetic(methodGen)) {
10971134 foundInSyntheticMethod();
1135 }
10981136 return this;
10991137 }
11001138
11111149 public BugInstance addClassAndMethod(JavaClass javaClass, Method method) {
11121150 addClass(javaClass.getClassName());
11131151 addMethod(javaClass, method);
1114
1115 if (BCELUtil.isSynthetic(method))
1152
1153 if (BCELUtil.isSynthetic(method)) {
11161154 foundInSyntheticMethod();
1155 }
11171156 return this;
11181157 }
11191158
12831322 Set<XMethod> targets = Hierarchy2.resolveVirtualMethodCallTargets(expectedClass, "equals", "(Ljava/lang/Object;)Z",
12841323 false, false);
12851324 addEqualsMethodUsed(targets);
1286
12871325 } catch (ClassNotFoundException e) {
12881326 AnalysisContext.reportMissingClass(e);
12891327 }
1290
12911328 return this;
12921329 }
12931330
12941331 @Nonnull
12951332 public BugInstance addEqualsMethodUsed(@CheckForNull Collection<XMethod> equalsMethods) {
1296 if (equalsMethods == null)
1333 if (equalsMethods == null) {
12971334 return this;
1335 }
12981336 if (equalsMethods.size() < 5) {
12991337 for (XMethod m : equalsMethods) {
13001338 addMethod(m).describe(MethodAnnotation.METHOD_EQUALS_USED);
13021340 } else {
13031341 addMethod(equalsMethods.iterator().next()).describe(MethodAnnotation.METHOD_EQUALS_USED);
13041342 }
1305
13061343 return this;
13071344 }
13081345
14041441 */
14051442 @Nonnull
14061443 public BugInstance addOptionalField(@CheckForNull XField xfield) {
1407 if (xfield == null)
1444 if (xfield == null) {
14081445 return this;
1446 }
14091447 return addField(xfield.getClassName(), xfield.getName(), xfield.getSignature(), xfield.isStatic());
14101448 }
14111449
14831521 public BugInstance addOptionalLocalVariable(DismantleBytecode dbc, OpcodeStack.Item item) {
14841522 int register = item.getRegisterNumber();
14851523
1486 if (register >= 0)
1524 if (register >= 0) {
14871525 this.add(LocalVariableAnnotation.getLocalVariableAnnotation(dbc.getMethod(), register, dbc.getPC() - 1, dbc.getPC()));
1526 }
14881527 return this;
14891528 }
14901529
16251664 public BugInstance addCalledMethod(XMethod m) {
16261665 return addMethod(m).describe(MethodAnnotation.METHOD_CALLED);
16271666 }
1667
16281668 /**
16291669 * Add a method annotation.
16301670 *
16961736 /**
16971737 * Add a MethodAnnotation from an MethodDescriptor.
16981738 *
1699 * @param xmethod
1700 * the XMethod
1739 * @param method
1740 * the method
17011741 * @return this object
17021742 */
17031743 @Nonnull
17051745 addMethod(MethodAnnotation.fromMethodDescriptor(method));
17061746 return this;
17071747 }
1748
17081749 /**
17091750 * Add a method annotation. If this is the first method annotation added, it
17101751 * becomes the primary method annotation.
17381779 return this;
17391780 }
17401781
1741 /*
1782 /**
17421783 * Add an annotation about a parameter
17431784 *
17441785 * @param index parameter index, starting from 0
18121853 public BugInstance addSourceLine(BytecodeScanningDetector visitor, int pc) {
18131854 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(visitor.getClassContext(),
18141855 visitor, pc);
1815 if (sourceLineAnnotation != null)
1856 if (sourceLineAnnotation != null) {
18161857 add(sourceLineAnnotation);
1858 }
18171859 return this;
18181860 }
18191861
18341876 @Nonnull
18351877 public BugInstance addSourceLine(ClassContext classContext, PreorderVisitor visitor, int pc) {
18361878 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(classContext, visitor, pc);
1837 if (sourceLineAnnotation != null)
1879 if (sourceLineAnnotation != null) {
18381880 add(sourceLineAnnotation);
1881 }
18391882 return this;
18401883 }
18411884
18591902 @Nonnull InstructionHandle handle) {
18601903 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen,
18611904 sourceFile, handle);
1862 if (sourceLineAnnotation != null)
1905 if (sourceLineAnnotation != null) {
18631906 add(sourceLineAnnotation);
1907 }
18641908 return this;
18651909 }
18661910
18901934 }
18911935 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstructionRange(classContext, methodGen,
18921936 sourceFile, start, end);
1893 if (sourceLineAnnotation != null)
1937 if (sourceLineAnnotation != null) {
18941938 add(sourceLineAnnotation);
1939 }
18951940 return this;
18961941 }
18971942
19481993 public BugInstance addSourceLine(ClassContext classContext, Method method, InstructionHandle handle) {
19491994 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(classContext, method,
19501995 handle.getPosition());
1951 if (sourceLineAnnotation != null)
1996 if (sourceLineAnnotation != null) {
19521997 add(sourceLineAnnotation);
1998 }
19531999 return this;
19542000 }
19552001
19712017 public BugInstance addSourceLineRange(BytecodeScanningDetector visitor, int startPC, int endPC) {
19722018 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstructionRange(visitor.getClassContext(),
19732019 visitor, startPC, endPC);
1974 if (sourceLineAnnotation != null)
1975 add(sourceLineAnnotation);
2020 requireNonNull(sourceLineAnnotation);
2021 add(sourceLineAnnotation);
19762022 return this;
19772023 }
19782024
19962042 public BugInstance addSourceLineRange(ClassContext classContext, PreorderVisitor visitor, int startPC, int endPC) {
19972043 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstructionRange(classContext, visitor,
19982044 startPC, endPC);
1999 if (sourceLineAnnotation != null)
2000 add(sourceLineAnnotation);
2045 requireNonNull(sourceLineAnnotation);
2046 add(sourceLineAnnotation);
20012047 return this;
20022048 }
20032049
20142060 @Nonnull
20152061 public BugInstance addSourceLine(BytecodeScanningDetector visitor) {
20162062 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(visitor);
2017 if (sourceLineAnnotation != null)
2063 if (sourceLineAnnotation != null) {
20182064 add(sourceLineAnnotation);
2065 }
20192066 return this;
20202067 }
20212068
20322079 @Nonnull
20332080 public BugInstance addUnknownSourceLine(String className, String sourceFile) {
20342081 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.createUnknown(className, sourceFile);
2035 if (sourceLineAnnotation != null)
2082 if (sourceLineAnnotation != null) {
20362083 add(sourceLineAnnotation);
2084 }
20372085 return this;
20382086 }
20392087
21432191 * ----------------------------------------------------------------------
21442192 */
21452193
2194 @Override
21462195 public void writeXML(XMLOutput xmlOutput) throws IOException {
21472196 writeXML(xmlOutput, null, false);
21482197 }
21512200 BugPattern pattern = getBugPattern();
21522201
21532202 int cweid = pattern.getCWEid();
2154 if (cweid != 0)
2203 if (cweid != 0) {
21552204 return cweid;
2205 }
21562206 BugCode bugCode = pattern.getBugCode();
21572207 return bugCode.getCWEid();
21582208 }
21592209 public void writeXML(XMLOutput xmlOutput, BugCollection bugCollection, boolean addMessages) throws IOException {
21602210 XMLAttributeList attributeList = new XMLAttributeList().addAttribute("type", type).addAttribute("priority",
21612211 String.valueOf(priority));
2212
2213 // Always add the rank attribute.
2214 attributeList.addAttribute("rank", Integer.toString(getBugRank()));
21622215
21632216 BugPattern pattern = getBugPattern();
21642217
21762229 attributeList.addAttribute("instanceHash", getInstanceHash());
21772230 attributeList.addAttribute("instanceOccurrenceNum", Integer.toString(getInstanceOccurrenceNum()));
21782231 attributeList.addAttribute("instanceOccurrenceMax", Integer.toString(getInstanceOccurrenceMax()));
2179 attributeList.addAttribute("rank", Integer.toString(getBugRank()));
2180
2232
21812233 int cweid = getCWEid();
2182 if (cweid != 0)
2234 if (cweid != 0) {
21832235 attributeList.addAttribute("cweid", Integer.toString(cweid));
2184
2185
2236 }
21862237 } else if (oldInstanceHash != null && !isInstanceHashConsistent()) {
21872238 attributeList.addAttribute("oldInstanceHash", oldInstanceHash);
21882239 }
2189 if (firstVersion > 0)
2240 if (firstVersion > 0) {
21902241 attributeList.addAttribute("first", Long.toString(firstVersion));
2191 if (lastVersion >= 0)
2242 }
2243 if (lastVersion >= 0) {
21922244 attributeList.addAttribute("last", Long.toString(lastVersion));
2193 if (introducedByChangeOfExistingClass)
2245 }
2246 if (introducedByChangeOfExistingClass) {
21942247 attributeList.addAttribute("introducedByChange", "true");
2195 if (removedByChangeOfPersistingClass)
2248 }
2249 if (removedByChangeOfPersistingClass) {
21962250 attributeList.addAttribute("removedByChange", "true");
2251 }
21972252
21982253 if (bugCollection != null) {
21992254 Cloud cloud = bugCollection.getCloudLazily();
22082263 if (reviews > 0) {
22092264 attributeList.addAttribute("reviews", Integer.toString(reviews));
22102265
2211 if (consensus != UserDesignation.UNCLASSIFIED)
2266 if (consensus != UserDesignation.UNCLASSIFIED) {
22122267 attributeList.addAttribute("consensus", consensus.toString());
2268 }
22132269
22142270 }
22152271 if (addMessages) {
22162272 int ageInDays = ageInDays(bugCollection, firstSeen);
22172273 attributeList.addAttribute("ageInDays", Integer.toString(ageInDays));
22182274 if (reviews > 0 && consensus != UserDesignation.UNCLASSIFIED) {
2219 if (consensus.score() < 0)
2275 if (consensus.score() < 0) {
22202276 attributeList.addAttribute("notAProblem", "true");
2221 if (consensus.score() > 0)
2277 }
2278 if (consensus.score() > 0) {
22222279 attributeList.addAttribute("shouldFix", "true");
2280 }
22232281 }
22242282
22252283 }
22262284 } else if (hasXmlProps()) {
22272285 XmlProps props = getXmlProps();
22282286
2229 if (props.firstSeen != null)
2287 if (props.firstSeen != null) {
22302288 attributeList.addOptionalAttribute("firstSeen", firstSeenXMLFormat().format(props.firstSeen));
2289 }
22312290 if (props.reviewCount > 0) {
2232 if (props.consensus != null)
2291 if (props.consensus != null) {
22332292 attributeList.addOptionalAttribute("consensus", props.consensus);
2293 }
22342294 attributeList.addAttribute("reviews", Integer.toString(props.reviewCount));
22352295 }
2236 if (!props.isInCloud())
2296 if (!props.isInCloud()) {
22372297 attributeList.addAttribute("isInCloud", "false");
2298 }
22382299 if (addMessages) {
22392300 UserDesignation consesus = UserDesignation.valueOf(props.consensus);
2240 if (consesus.shouldFix())
2301 if (consesus.shouldFix()) {
22412302 attributeList.addAttribute("shouldFix", "true");
2242 else if (consesus.notAProblem())
2303 } else if (consesus.notAProblem()) {
22432304 attributeList.addAttribute("notAProblem", "true");
2305 }
22442306
22452307 if (props.firstSeen != null) {
22462308 int ageInDays = ageInDays(bugCollection, props.firstSeen.getTime());
22652327 xmlOutput.closeTag("ShortMessage");
22662328
22672329 xmlOutput.openTag("LongMessage");
2268 if (FindBugsDisplayFeatures.isAbridgedMessages())
2330 if (FindBugsDisplayFeatures.isAbridgedMessages()) {
22692331 xmlOutput.writeText(this.getAbridgedMessage());
2270 else
2332 } else {
22712333 xmlOutput.writeText(this.getMessageWithoutPrefix());
2334 }
22722335 xmlOutput.closeTag("LongMessage");
22732336 }
22742337
22862349
22872350 boolean foundSourceAnnotation = false;
22882351 for (BugAnnotation annotation : annotationList) {
2289 if (annotation instanceof SourceLineAnnotation)
2352 if (annotation instanceof SourceLineAnnotation) {
22902353 foundSourceAnnotation = true;
2354 }
22912355 annotation.writeXML(xmlOutput, addMessages, primaryAnnotations.containsKey(annotation));
22922356 }
22932357 if (!foundSourceAnnotation && addMessages) {
22942358 SourceLineAnnotation synth = getPrimarySourceLineAnnotation();
2295 if (synth != null) {
2296 synth.setSynthetic(true);
2297 synth.writeXML(xmlOutput, addMessages, false);
2298 }
2359 synth.setSynthetic(true);
2360 synth.writeXML(xmlOutput, addMessages, false);
22992361 }
23002362
23012363 if (propertyListHead != null) {
2302 BugProperty prop = propertyListHead;
2303 while (prop != null) {
2364 List<BugProperty> props = new ArrayList<>();
2365 for(BugProperty prop = propertyListHead; prop != null; prop = prop.getNext()) {
2366 props.add(prop);
2367 }
2368 Collections.sort(props, new Comparator<BugProperty>() {
2369 @Override
2370 public int compare(BugProperty o1, BugProperty o2) {
2371 return o1.getName().compareTo(o2.getName());
2372 }
2373 });
2374 for(BugProperty prop : props) {
23042375 prop.writeXML(xmlOutput);
2305 prop = prop.getNext();
23062376 }
23072377 }
23082378
23092379 xmlOutput.closeTag(ELEMENT_NAME);
23102380 }
23112381
2312 /**
2313 * @param bugCollection
2314 * @param firstSeen
2315 * @return
2316 */
23172382 private int ageInDays(BugCollection bugCollection, long firstSeen) {
23182383 long age = bugCollection.getAnalysisTimestamp() - firstSeen;
2319 if (age < 0)
2384 if (age < 0) {
23202385 age = 0;
2386 }
23212387 int ageInDays = (int) (age / 1000 / 3600 / 24);
23222388 return ageInDays;
23232389 }
2324
2325 private static final String ELEMENT_NAME = "BugInstance";
23262390
23272391 /*
23282392 * ----------------------------------------------------------------------
23312395 */
23322396
23332397 public BugInstance addOptionalAnnotation(@CheckForNull BugAnnotation annotation) {
2334 if (annotation == null)
2398 if (annotation == null) {
23352399 return this;
2400 }
23362401 return add(annotation);
23372402 }
23382403
23392404 public BugInstance addOptionalAnnotation(@CheckForNull BugAnnotation annotation, String role) {
2340 if (annotation == null)
2405 if (annotation == null) {
23412406 return this;
2407 }
23422408 return add(annotation).describe(role);
23432409 }
23442410
23452411 public BugInstance add(@Nonnull BugAnnotation annotation) {
2346 if (annotation == null)
2347 throw new IllegalArgumentException("Missing BugAnnotation!");
2412 requireNonNull(annotation, "Missing BugAnnotation!");
23482413
23492414 // Add to list
23502415 annotationList.add(annotation);
23902455 BugAnnotation a0 = getSomeSource(classContext, method, location, stack, depth);
23912456 return a0;
23922457 } catch (UnreachableCodeException e) {
2393 if (SystemProperties.ASSERTIONS_ENABLED)
2458 if (SystemProperties.ASSERTIONS_ENABLED) {
23942459 AnalysisContext.logError(e.getMessage(), e);
2460 }
23952461 return null;
23962462 }
23972463 }
23982464
23992465 public static @CheckForNull
24002466 BugAnnotation getSomeSource(ClassContext classContext, Method method, Location location, OpcodeStack stack, int stackPos) {
2467 if (stack.isTop()) {
2468 return null;
2469 }
24012470 int pc = location.getHandle().getPosition();
24022471
24032472 try {
24042473 BugAnnotation result = ValueNumberSourceInfo.getFromValueNumber(classContext, method, location, stackPos);
2405 if (result != null)
2474 if (result != null) {
24062475 return result;
2476 }
24072477 } catch (DataflowAnalysisException e) {
24082478 AnalysisContext.logError("Couldn't find value source", e);
24092479 } catch (CFGBuilderException e) {
24172487 public static @javax.annotation.CheckForNull
24182488 BugAnnotation getValueSource(OpcodeStack.Item item, Method method, int pc) {
24192489 LocalVariableAnnotation lv = LocalVariableAnnotation.getLocalVariableAnnotation(method, item, pc);
2420 if (lv != null && lv.isNamed())
2490 if (lv != null && lv.isNamed()) {
24212491 return lv;
2492 }
24222493
24232494 BugAnnotation a = getFieldOrMethodValueSource(item);
2424 if (a != null)
2495 if (a != null) {
24252496 return a;
2497 }
24262498 Object c = item.getConstant();
24272499 if (c instanceof String) {
24282500 a = new StringAnnotation((String) c);
24292501 a.setDescription(StringAnnotation.STRING_CONSTANT_ROLE);
24302502 return a;
24312503 }
2432 if (c instanceof Integer) {
2504 if (c instanceof Integer && !item.isArray()) {
24332505 a = new IntAnnotation((Integer) c);
24342506 a.setDescription(IntAnnotation.INT_VALUE);
24352507 return a;
24392511 }
24402512
24412513 public BugInstance addValueSource(@CheckForNull OpcodeStack.Item item, DismantleBytecode dbc) {
2442 if (item != null)
2514 if (item != null) {
24432515 addValueSource(item, dbc.getMethod(), dbc.getPC());
2516 }
24442517 return this;
24452518 }
24462519
24492522 return this;
24502523 }
24512524
2452 /**
2453 * @param item
2454 */
24552525 public BugInstance addFieldOrMethodValueSource(OpcodeStack.Item item) {
24562526 addOptionalAnnotation(getFieldOrMethodValueSource(item));
24572527 return this;
24592529
24602530 public BugInstance addOptionalUniqueAnnotations(BugAnnotation... annotations) {
24612531 HashSet<BugAnnotation> added = new HashSet<BugAnnotation>();
2462 for (BugAnnotation a : annotations)
2463 if (a != null && added.add(a))
2532 for (BugAnnotation a : annotations) {
2533 if (a != null && added.add(a)) {
24642534 add(a);
2465 return this;
2466 }
2467
2535 }
2536 }
2537 return this;
2538 }
2539
24682540 public boolean tryAddingOptionalUniqueAnnotations(BugAnnotation... annotations) {
24692541 HashSet<BugAnnotation> added = new HashSet<BugAnnotation>();
2470 for (BugAnnotation a : annotations)
2471 if (a != null && added.add(a))
2542 for (BugAnnotation a : annotations) {
2543 if (a != null && added.add(a)) {
24722544 add(a);
2545 }
2546 }
24732547 return !added.isEmpty();
24742548 }
2475
24762549
24772550 public BugInstance addOptionalUniqueAnnotationsWithFallback(BugAnnotation fallback, BugAnnotation... annotations) {
24782551 HashSet<BugAnnotation> added = new HashSet<BugAnnotation>();
2479 for (BugAnnotation a : annotations)
2480 if (a != null && added.add(a))
2552 for (BugAnnotation a : annotations) {
2553 if (a != null && added.add(a)) {
24812554 add(a);
2482 if (added.isEmpty())
2555 }
2556 }
2557 if (added.isEmpty()) {
24832558 add(fallback);
2559 }
24842560 return this;
24852561 }
24862562
24872563 public static @CheckForNull
24882564 BugAnnotation getFieldOrMethodValueSource(@CheckForNull OpcodeStack.Item item) {
2489 if (item == null)
2565 if (item == null) {
24902566 return null;
2567 }
24912568 XField xField = item.getXField();
24922569 if (xField != null) {
24932570 FieldAnnotation a = FieldAnnotation.fromXField(xField);
25242601 if (cachedHashCode == INVALID_HASH_CODE) {
25252602 int hashcode = type.hashCode() + priority;
25262603 Iterator<BugAnnotation> i = annotationIterator();
2527 while (i.hasNext())
2604 while (i.hasNext()) {
25282605 hashcode += i.next().hashCode();
2529 if (hashcode == INVALID_HASH_CODE)
2606 }
2607 if (hashcode == INVALID_HASH_CODE) {
25302608 hashcode = INVALID_HASH_CODE + 1;
2609 }
25312610 cachedHashCode = hashcode;
25322611 }
25332612
25362615
25372616 @Override
25382617 public boolean equals(Object o) {
2539 if (this == o)
2618 if (this == o) {
25402619 return true;
2541 if (!(o instanceof BugInstance))
2620 }
2621 if (!(o instanceof BugInstance)) {
25422622 return false;
2623 }
25432624 BugInstance other = (BugInstance) o;
2544 if (!type.equals(other.type) || priority != other.priority)
2625 if (!type.equals(other.type) || priority != other.priority) {
25452626 return false;
2546 if (annotationList.size() != other.annotationList.size())
2627 }
2628 if (annotationList.size() != other.annotationList.size()) {
25472629 return false;
2630 }
25482631 int numAnnotations = annotationList.size();
25492632 for (int i = 0; i < numAnnotations; ++i) {
25502633 BugAnnotation lhs = annotationList.get(i);
25512634 BugAnnotation rhs = other.annotationList.get(i);
2552 if (!lhs.equals(rhs))
2635 if (!lhs.equals(rhs)) {
25532636 return false;
2637 }
25542638 }
25552639
25562640 return true;
25572641 }
25582642
2643 @Override
25592644 public int compareTo(BugInstance other) {
25602645 int cmp;
25612646 cmp = type.compareTo(other.type);
2562 if (cmp != 0)
2647 if (cmp != 0) {
25632648 return cmp;
2649 }
25642650 cmp = priority - other.priority;
2565 if (cmp != 0)
2651 if (cmp != 0) {
25662652 return cmp;
2653 }
25672654
25682655 // Compare BugAnnotations lexicographically
25692656 int pfxLen = Math.min(annotationList.size(), other.annotationList.size());
25712658 BugAnnotation lhs = annotationList.get(i);
25722659 BugAnnotation rhs = other.annotationList.get(i);
25732660 cmp = lhs.compareTo(rhs);
2574 if (cmp != 0)
2661 if (cmp != 0) {
25752662 return cmp;
2663 }
25762664 }
25772665
25782666 // All elements in prefix were the same,
25802668 return annotationList.size() - other.annotationList.size();
25812669 }
25822670
2583 /**
2584 * @param firstVersion
2585 * The firstVersion to set.
2586 */
25872671 public void setFirstVersion(long firstVersion) {
2588 if (lastVersion >= 0 && firstVersion > lastVersion)
2672 if (lastVersion >= 0 && firstVersion > lastVersion) {
25892673 throw new IllegalArgumentException(firstVersion + ".." + lastVersion);
2674 }
25902675 this.firstVersion = firstVersion;
2591
25922676 }
25932677
25942678 public void clearHistory() {
25982682 setRemovedByChangeOfPersistingClass(false);
25992683 }
26002684
2601 /**
2602 * @return Returns the firstVersion.
2603 */
26042685 public long getFirstVersion() {
26052686 return firstVersion;
26062687 }
26192700 this.introducedByChangeOfExistingClass = from.introducedByChangeOfExistingClass;
26202701 }
26212702
2622 /**
2623 * @param lastVersion
2624 * The lastVersion to set.
2625 */
26262703 public void setLastVersion(long lastVersion) {
2627 if (lastVersion >= 0 && firstVersion > lastVersion)
2704 if (lastVersion >= 0 && firstVersion > lastVersion) {
26282705 throw new IllegalArgumentException(firstVersion + ".." + lastVersion);
2706 }
26292707 this.lastVersion = lastVersion;
26302708 }
26312709
26332711 public void setLive() {
26342712 this.lastVersion = -1;
26352713 }
2636 /**
2637 * @return Returns the lastVersion.
2638 */
2714
26392715 public long getLastVersion() {
26402716 return lastVersion;
26412717 }
26442720 return lastVersion != -1;
26452721 }
26462722
2647 /**
2648 * @param introducedByChangeOfExistingClass
2649 * The introducedByChangeOfExistingClass to set.
2650 */
26512723 public void setIntroducedByChangeOfExistingClass(boolean introducedByChangeOfExistingClass) {
26522724 this.introducedByChangeOfExistingClass = introducedByChangeOfExistingClass;
26532725 }
26542726
2655 /**
2656 * @return Returns the introducedByChangeOfExistingClass.
2657 */
26582727 public boolean isIntroducedByChangeOfExistingClass() {
26592728 return introducedByChangeOfExistingClass;
26602729 }
26612730
2662 /**
2663 * @param removedByChangeOfPersistingClass
2664 * The removedByChangeOfPersistingClass to set.
2665 */
26662731 public void setRemovedByChangeOfPersistingClass(boolean removedByChangeOfPersistingClass) {
26672732 this.removedByChangeOfPersistingClass = removedByChangeOfPersistingClass;
26682733 }
26692734
2670 /**
2671 * @return Returns the removedByChangeOfPersistingClass.
2672 */
26732735 public boolean isRemovedByChangeOfPersistingClass() {
26742736 return removedByChangeOfPersistingClass;
26752737 }
26762738
2677 /**
2678 * @param instanceHash
2679 * The instanceHash to set.
2680 */
26812739 public void setInstanceHash(String instanceHash) {
26822740 this.instanceHash = instanceHash;
26832741 }
26842742
2685 /**
2686 * @param oldInstanceHash
2687 * The oldInstanceHash to set.
2688 */
26892743 public void setOldInstanceHash(String oldInstanceHash) {
26902744 this.oldInstanceHash = oldInstanceHash;
26912745 }
26922746
2693 /**
2694 * @return Returns the instanceHash.
2695 */
2696
26972747 public String getInstanceHash() {
26982748 String hash = instanceHash;
2699 if (hash != null)
2749 if (hash != null) {
27002750 return hash;
2751 }
27012752
27022753 MessageDigest digest = Util.getMD5Digest();
27032754 String key = getInstanceKey();
27162767 return oldInstanceHash == null || getInstanceHash().equals(oldInstanceHash);
27172768 }
27182769
2719 /**
2720 * @param instanceOccurrenceNum
2721 * The instanceOccurrenceNum to set.
2722 */
27232770 public void setInstanceOccurrenceNum(int instanceOccurrenceNum) {
27242771 this.instanceOccurrenceNum = instanceOccurrenceNum;
27252772 }
27262773
2727 /**
2728 * @return Returns the instanceOccurrenceNum.
2729 */
27302774 public int getInstanceOccurrenceNum() {
27312775 return instanceOccurrenceNum;
27322776 }
27332777
2734 /**
2735 * @param instanceOccurrenceMax
2736 * The instanceOccurrenceMax to set.
2737 */
27382778 public void setInstanceOccurrenceMax(int instanceOccurrenceMax) {
27392779 this.instanceOccurrenceMax = instanceOccurrenceMax;
27402780 }
27412781
2742 /**
2743 * @return Returns the instanceOccurrenceMax.
2744 */
27452782 public int getInstanceOccurrenceMax() {
27462783 return instanceOccurrenceMax;
27472784 }
27522789 }
27532790
27542791 private void optionalAdd(Collection<BugAnnotation> c, BugAnnotation a) {
2755 if (a != null)
2792 if (a != null) {
27562793 c.add(a);
2794 }
27572795 }
27582796
27592797 public List<BugAnnotation> getAnnotationsForMessage(boolean showContext) {
27722810 optionalAdd(primaryAnnotations, primeField);
27732811 optionalAdd(primaryAnnotations, primeClass);
27742812
2775 if (primarySourceLineAnnotation != null
2776 && (showContext || !primarySourceLineAnnotation.getDescription().equals(SourceLineAnnotation.DEFAULT_ROLE)))
2813 if ((showContext || !SourceLineAnnotation.DEFAULT_ROLE.equals(primarySourceLineAnnotation.getDescription()))) {
27772814 result.add(primarySourceLineAnnotation);
2778
2779 if (primeMethod != null && (showContext || !primeMethod.getDescription().equals(MethodAnnotation.DEFAULT_ROLE)))
2815 }
2816
2817 if (primeMethod != null && (showContext || !MethodAnnotation.DEFAULT_ROLE.equals(primeMethod.getDescription()))) {
27802818 result.add(primeMethod);
2819 }
27812820
27822821 optionalAdd(result, primeField);
27832822
27842823 String fieldClass = "";
27852824 String methodClass = "";
2786 if (primeField != null)
2825 if (primeField != null) {
27872826 fieldClass = primeField.getClassName();
2788 if (primeMethod != null)
2827 }
2828 if (primeMethod != null) {
27892829 methodClass = primeMethod.getClassName();
2830 }
27902831 if (showContext && (primaryAnnotations.size() < 2)
27912832 || (!(primeClass.getClassName().equals(fieldClass) || primeClass.getClassName().equals(methodClass)))) {
27922833 optionalAdd(result, primeClass);
27932834 }
27942835
27952836 for (BugAnnotation b : getAnnotations()) {
2796 if (primaryAnnotations.contains(b))
2837 if (primaryAnnotations.contains(b)) {
27972838 continue;
2798 if (b instanceof LocalVariableAnnotation && !((LocalVariableAnnotation) b).isNamed())
2839 }
2840 if (b instanceof LocalVariableAnnotation && !((LocalVariableAnnotation) b).isNamed()) {
27992841 continue;
2800 if (b instanceof SourceLineAnnotation && ((SourceLineAnnotation) b).isUnknown())
2842 }
2843 if (b instanceof SourceLineAnnotation && ((SourceLineAnnotation) b).isUnknown()) {
28012844 continue;
2845 }
28022846 result.add(b);
28032847 }
28042848 return result;
28522896 }
28532897 }
28542898 }
2855
2856 // vim:ts=4
4545 final private String longDescription;
4646
4747 final private String detailText;
48
49 final private String url;
4850
4951 final int cweid;
5052
7072 * by {@link FindBugsMessageFormat} to format BugAnnotations
7173 * @param detailText
7274 * HTML text containing a full description of the bug species
75 * @param bugsUrl
76 * URL of web-page containing bug descriptions or null if there's no such page.
7377 */
7478 public BugPattern(String type, String abbrev, String category, boolean experimental, String shortDescription,
75 String longDescription, String detailText) {
76 this(type, abbrev, category, experimental, shortDescription, longDescription, detailText, 0);
77 }
78
79 /**
80 * Constructor.
81 *
82 * @param type
83 * the type (species) of BugInstance
84 * @param abbrev
85 * the abbreviation or "bug code"; see {@link BugCode}
86 * @param category
87 * the category
88 * @param experimental
89 * true if the bug pattern is experimental
90 * @param shortDescription
91 * short one-line description of the bug species
92 * @param longDescription
93 * longer one-line description; may contain placeholders for use
94 * by {@link FindBugsMessageFormat} to format BugAnnotations
95 * @param detailText
96 * HTML text containing a full description of the bug species
97 */
98 public BugPattern(String type, String abbrev, String category, boolean experimental, String shortDescription,
99 String longDescription, String detailText, int cweid) {
79 String longDescription, String detailText, String bugsUrl, int cweid) {
10080
10181 this.type = type;
10282 this.abbrev = abbrev.intern();
10686 this.longDescription = longDescription;
10787 this.detailText = detailText;
10888 this.cweid = cweid;
89 this.url = bugsUrl;
10990 }
11091
11192 static final BugPattern REALLY_UNKNOWN = new BugPattern("REALLY_UNKNOWN", "TEST", "CORRECTNESS", false,
11394 "<p>A warning was recorded, but findbugs can't find the description of this bug pattern "
11495 + "and so can't describe it. This should occur only in cases of a bug in FindBugs or its configuration, "
11596 + "or perhaps if an analysis was generated using a plugin, but that plugin is not currently loaded. "
116 + "</p>");
97 + "</p>", null, 0);
11798
11899 /**
119100 * Get the BugPattern
134115 */
135116 public @Nonnull BugCode getBugCode() {
136117 return DetectorFactoryCollection.instance().getBugCode(abbrev);
137
118
138119 }
139120 /**
140121 * Get the BugCategory
154135 public String getCategoryAbbrev() {
155136 String categoryAbbrev = null;
156137 BugCategory bcat = DetectorFactoryCollection.instance().getBugCategory(getCategory());
157 if (bcat != null)
138 if (bcat != null) {
158139 categoryAbbrev = bcat.getAbbrev();
159 if (categoryAbbrev == null)
140 }
141 if (categoryAbbrev == null) {
160142 categoryAbbrev = TextUIBugReporter.OTHER_CATEGORY_ABBREV;
143 }
161144 return categoryAbbrev;
162145 }
163146
207190 public String getDetailHTML() {
208191 return getDetailHTML(getDetailText());
209192 }
210
193
211194 public String getDetailHTML(String detailText) {
212195
213 StringBuilder buf = new StringBuilder();
214 buf.append("<!DOCTYPE HTML\">\n");
215 buf.append("<HTML><HEAD><TITLE>");
216 buf.append(getShortDescription());
217 buf.append("</TITLE></HEAD><BODY><H1>");
218 buf.append(wrapInDescriptionLink(getShortDescription()));
219 buf.append("</H1>\n");
220 buf.append(detailText);
221 buf.append("</BODY></HTML>\n");
222 return buf.toString();
223
224 }
225
196 StringBuilder buf = new StringBuilder();
197 buf.append("<!DOCTYPE HTML\">\n");
198 buf.append("<HTML><HEAD><TITLE>");
199 buf.append(getShortDescription());
200 buf.append("</TITLE></HEAD><BODY><H1>");
201 buf.append(wrapInDescriptionLink(getShortDescription()));
202 buf.append("</H1>\n");
203 buf.append(detailText);
204 buf.append("</BODY></HTML>\n");
205 return buf.toString();
206
207 }
226208
227209 public String wrapInDescriptionLink(String text) {
228 if (isExperimental())
229 return
230 "<a href=\"http://findbugs.sourceforge.net/allBugDescriptions.html#"
231 + type +"\">"
232 + text + "</a>";
233 return
234 "<a href=\"http://findbugs.sourceforge.net/bugDescriptions.html#"
235 + type +"\">"
236 + text + "</a>";
237
238 }
210 if(url == null) {
211 return text;
212 }
213 return "<a href=\"" + url + "#" + type + "\">" + text + "</a>";
214 }
215
216 @Override
239217 public int compareTo(BugPattern other) {
240218 return type.compareTo(other.type);
241219 }
247225
248226 @Override
249227 public boolean equals(Object o) {
250 if (!(o instanceof BugPattern))
228 if (!(o instanceof BugPattern)) {
251229 return false;
230 }
252231 BugPattern other = (BugPattern) o;
253232 return type.equals(other.type);
254233 }
2929 * Name/value metadata pair that may be attached to a BugInstance. These are
3030 * different from BugAnnotations because they are not meant to be shown directly
3131 * to the user.
32 *
32 *
3333 * @author David Hovemeyer
3434 */
3535 public class BugProperty implements XMLWriteable, Serializable, Cloneable {
4949 public static final String SEVERITY = "severity";
5050
5151 // Fields
52 private String name;
52 private final String name;
5353
5454 private String value;
5555
5757
5858 /**
5959 * Constructor.
60 *
60 *
6161 * @param name
6262 * name of property
6363 * @param value
7979
8080 /**
8181 * Get name of property.
82 *
82 *
8383 * @return name of property
8484 */
8585 public String getName() {
8888
8989 /**
9090 * Get value of property.
91 *
91 *
9292 * @return value of property
9393 */
9494 public String getValue() {
9797
9898 /**
9999 * Get value of property as boolean.
100 *
100 *
101101 * @return value of property as a boolean
102102 */
103103 public boolean getValueAsBoolean() {
106106
107107 /**
108108 * Get value of property as an integer.
109 *
109 *
110110 * @return value of property as integer
111111 * @throws NumberFormatException
112112 * if the value cannot be parsed as an integer
117117
118118 /**
119119 * Set value of property.
120 *
120 *
121121 * @param value
122122 */
123123 public void setValue(String value) {
126126
127127 /**
128128 * Set next property in list.
129 *
129 *
130130 * @param next
131131 * next property in list
132132 */
136136
137137 /**
138138 * Get next property in list.
139 *
139 *
140140 * @return next property in list
141141 */
142142 BugProperty getNext() {
145145
146146 /*
147147 * (non-Javadoc)
148 *
148 *
149149 * @see
150150 * edu.umd.cs.findbugs.xml.XMLWriteable#writeXML(edu.umd.cs.findbugs.xml
151151 * .XMLOutput)
152152 */
153 @Override
153154 public void writeXML(XMLOutput xmlOutput) throws IOException {
154155 xmlOutput.openCloseTag("Property",
155156 new XMLAttributeList().addAttribute("name", getName()).addAttribute("value", getValue()));
9898
9999 int get(String key) {
100100 Integer v = adjustment.get(key);
101 if (v == null)
101 if (v == null) {
102102 return 0;
103 }
103104 return v;
104105 }
105106
110111 void storeAdjustment(String key, String value) {
111112 for (String k : key.split(",")) {
112113 char firstChar = value.charAt(0);
113 if (firstChar == '+')
114 if (firstChar == '+') {
114115 value = value.substring(1);
116 }
115117
116118 int v = Integer.parseInt(value);
117119 adjustment.put(k, v);
118 if (firstChar == '+' || firstChar == '-')
120 if (firstChar == '+' || firstChar == '-') {
119121 isRelative.add(k);
122 }
120123
121124
122125 }
137140 try {
138141 while (true) {
139142 String s = in.readLine();
140 if (s == null)
143 if (s == null) {
141144 break;
145 }
142146
143147 s = s.trim();
144 if (s.length() == 0)
148 if (s.length() == 0) {
145149 continue;
150 }
146151
147152 String parts[] = s.split(" ");
153 if (parts.length != 3) {
154 AnalysisContext.logError("Can't parse bug rank line: '" + s + "'. "
155 + "Valid line must contain 3 parts separated by spaces.");
156 continue;
157 }
148158 String rank = parts[0];
149159 String kind = parts[1];
150160 String what = parts[2];
151 if (kind.equals("BugPattern"))
161 if ("BugPattern".equals(kind)) {
152162 bugPatterns.storeAdjustment(what, rank);
153 else if (kind.equals("BugKind"))
163 } else if ("BugKind".equals(kind)) {
154164 bugKinds.storeAdjustment(what, rank);
155 else if (kind.equals("Category"))
165 } else if ("Category".equals(kind)) {
156166 bugCategories.storeAdjustment(what, rank);
157 else
158 AnalysisContext.logError("Can't parse bug rank " + s);
167 } else {
168 AnalysisContext.logError("Can't parse rank kind from line: '" + s + "'. "
169 + "Valid kind must be either 'BugPattern', 'BugKind' or 'Category'.");
170 }
159171 }
160172 } finally {
161173 Util.closeSilently(in);
190202
191203 private static int adjustRank(int patternRank, int priority) {
192204 int priorityAdjustment = priorityAdjustment(priority);
193 if (patternRank > VISIBLE_RANK_MAX)
205 if (patternRank > VISIBLE_RANK_MAX) {
194206 return patternRank + priorityAdjustment;
207 }
195208 return Math.max(VISIBLE_RANK_MIN, Math.min(patternRank + priorityAdjustment, VISIBLE_RANK_MAX));
196209 }
197210
198211 private static int rankBugPattern(BugPattern bugPattern, BugRanker... rankers) {
199212 String type = bugPattern.getType();
200213 int rank = 0;
201 for (BugRanker b : rankers)
214 for (BugRanker b : rankers) {
202215 if (b != null) {
203216 rank += b.bugPatterns.get(type);
204 if (!b.bugPatterns.isRelative(type))
217 if (!b.bugPatterns.isRelative(type)) {
205218 return rank;
206 }
219 }
220 }
221 }
207222 String kind = bugPattern.getAbbrev();
208 for (BugRanker b : rankers)
223 for (BugRanker b : rankers) {
209224 if (b != null) {
210225 rank += b.bugKinds.get(kind);
211 if (!b.bugKinds.isRelative(kind))
226 if (!b.bugKinds.isRelative(kind)) {
212227 return rank;
213 }
228 }
229 }
230 }
214231 String category = bugPattern.getCategory();
215 for (BugRanker b : rankers)
232 for (BugRanker b : rankers) {
216233 if (b != null) {
217234 rank += b.bugCategories.get(category);
218 if (!b.bugCategories.isRelative(category))
235 if (!b.bugCategories.isRelative(category)) {
219236 return rank;
220 }
237 }
238 }
239 }
221240 return rank;
222241 }
223242
251270 public static int findRank(BugPattern pattern, @CheckForNull DetectorFactory detectorFactory) {
252271 boolean haveCache = Global.getAnalysisCache() != null;
253272 if (haveCache) {
254 Integer cachedResult = rankForBugPattern.get().get(pattern);
255 if (cachedResult != null)
256 return cachedResult;
273 Integer cachedResult = rankForBugPattern.get().get(pattern);
274 if (cachedResult != null) {
275 return cachedResult;
276 }
257277 }
258278
259279 int rank;
260 if (detectorFactory == null)
280 if (detectorFactory == null) {
261281 rank = findRankUnknownPlugin(pattern);
262 else {
282 } else {
263283 Plugin plugin = detectorFactory.getPlugin();
264 BugRanker pluginRanker = plugin.getBugRanker();
284 BugRanker pluginRanker = plugin.getBugRanker();
265285 BugRanker coreRanker = getCoreRanker();
266286
267 if (pluginRanker == coreRanker)
287 if (pluginRanker == coreRanker) {
268288 rank = rankBugPattern(pattern, coreRanker);
269 else
289 } else {
270290 rank = rankBugPattern(pattern, pluginRanker, coreRanker);
271 }
272 if (haveCache)
291 }
292 }
293 if (haveCache) {
273294 rankForBugPattern.get().put(pattern, rank);
295 }
274296 return rank;
275297 }
276298
278300
279301 List<BugRanker> rankers = new ArrayList<BugRanker>();
280302 pluginLoop: for (Plugin plugin : Plugin.getAllPlugins()) {
281 if (plugin.isCorePlugin())
303 if (plugin.isCorePlugin()) {
282304 continue;
305 }
283306 if (false) {
284307 rankers.add(plugin.getBugRanker());
285308 continue pluginLoop;
287310 for (DetectorFactory df : plugin.getDetectorFactories()) {
288311
289312 if (df.getReportedBugPatterns().contains(pattern)) {
290 if (PLUGIN_DEBUG)
313 if (PLUGIN_DEBUG) {
291314 System.out.println("Bug rank match " + plugin + " " + df + " for " + pattern);
315 }
292316 rankers.add(plugin.getBugRanker());
293317 continue pluginLoop;
294318 }
295319 }
296 if (PLUGIN_DEBUG)
320 if (PLUGIN_DEBUG) {
297321 System.out.println("plugin " + plugin + " doesn't match " + pattern);
322 }
298323
299324 }
300325 rankers.add(getCoreRanker());
301326
302 return rankBugPattern(pattern, rankers.toArray(new BugRanker[] {}));
327 return rankBugPattern(pattern, rankers.toArray(new BugRanker[rankers.size()]));
303328 }
304329
305330 public static void trimToMaxRank(BugCollection origCollection, int maxRank) {
306331 for (Iterator<BugInstance> i = origCollection.getCollection().iterator(); i.hasNext();) {
307332 BugInstance b = i.next();
308 if (BugRanker.findRank(b) > maxRank)
333 if (BugRanker.findRank(b) > maxRank) {
309334 i.remove();
335 }
310336
311337 }
312338 }
2929 * the information reported by the analysis, which includes bug reports, and
3030 * also auxiliary information such as analysis errors, missing classes, and
3131 * class to source file mapping.
32 *
32 *
3333 * @author David Hovemeyer
3434 */
3535 public interface BugReporter extends RepositoryLookupFailureCallback, IClassObserver {
4646
4747 /**
4848 * Set the error-reporting verbosity level.
49 *
49 *
5050 * @param level
5151 * the verbosity level
5252 */
5454
5555 /**
5656 * Set the priority threshold.
57 *
57 *
5858 * @param threshold
5959 * bug instances must be at least as important as this priority
6060 * to be reported
6464 /**
6565 * Report a bug. The implementation may report the bug immediately, or queue
6666 * it for later.
67 *
67 *
6868 * @param bugInstance
6969 * object describing the bug instance
7070 */
8383
8484 /**
8585 * Add an observer.
86 *
86 *
8787 * @param observer
8888 * the observer
8989 */
9797
9898 /**
9999 * Get the bug collection (if any) associated with this bug reporter
100 *
101 * @return
102100 */
103101 public @CheckForNull
104102 BugCollection getBugCollection();
105103 }
106104
107 // vim:ts=4
2323 * observer, it is possible to determine which bugs a BugReporter is actually
2424 * reporting, because due to filtering, priorities, etc., not all bugs sent to a
2525 * BugReporter will actually be processed.
26 *
26 *
2727 * @author David Hovemeyer
2828 * @see BugReporter
2929 */
3030 public interface BugReporterObserver {
3131 /**
3232 * Called when a BugReporter reports a bug.
33 *
33 *
3434 * @param bugInstance
3535 * the BugInstance
3636 */
3737 public void reportBug(BugInstance bugInstance);
3838 }
3939
40 // vim:ts=4
3636 * A base class for bug detectors that are based on a ByteCodePattern.
3737 * ByteCodePatterns provide an easy way to detect patterns of bytecode
3838 * instructions, taking into account control flow and uses of fields and values.
39 *
39 *
4040 * @see ByteCodePattern
4141 */
4242 public abstract class ByteCodePatternDetector implements Detector {
4646
4747 protected abstract BugReporter getBugReporter();
4848
49 @Override
4950 public void visitClassContext(ClassContext classContext) {
5051 try {
5152 ByteCodePattern pattern = getPattern();
5354 Method[] methodList = jclass.getMethods();
5455
5556 for (Method method : methodList) {
56 if (method.isAbstract() || method.isNative())
57 if (method.isAbstract() || method.isNative()) {
5758 continue;
59 }
5860
59 if (METHOD != null && !method.getName().equals(METHOD))
61 if (METHOD != null && !method.getName().equals(METHOD)) {
6062 continue;
63 }
6164
6265 if (DEBUG) {
6366 System.out.print("=====================================================================\n" + "Method "
6568 + "=====================================================================\n");
6669 }
6770
68 if (!prescreen(method, classContext))
71 if (!prescreen(method, classContext)) {
6972 continue;
73 }
7074
7175 MethodGen methodGen = classContext.getMethodGen(method);
72 if (methodGen == null)
76 if (methodGen == null) {
7377 continue;
78 }
7479
7580 PatternMatcher matcher = new PatternMatcher(pattern, classContext, method);
7681 matcher.execute();
107112 return className;
108113 }
109114
115 @Override
110116 public void report() {
111117 }
112118
129135 * As a datapoint, prescreening speeds up the BCPDoubleCheck detector <b>by
130136 * a factor of 5</b> with no loss of generality and only a dozen or so extra
131137 * lines of code.
132 *
138 *
133139 * @param method
134140 * the method
135141 * @param classContext
141147
142148 /**
143149 * Called to report an instance of the ByteCodePattern.
144 *
150 *
145151 * @param classContext
146152 * the ClassContext for the analyzed class
147153 * @param method
154160 throws CFGBuilderException, DataflowAnalysisException;
155161 }
156162
157 // vim:ts=4
2525
2626 /**
2727 * Base class for Detectors which want to extend DismantleBytecode.
28 *
28 *
2929 * @see DismantleBytecode
3030 */
3131 public class BytecodeScanningDetector extends DismantleBytecode implements Detector {
3232 private ClassContext classContext;
3333
34 @Override
3435 public void visitClassContext(ClassContext classContext) {
3536 this.classContext = classContext;
3637 classContext.getJavaClass().accept(this);
3839
3940 /**
4041 * Get the ClassContext of the class currently being visited.
41 *
42 *
4243 * @return the current ClassContext
4344 */
4445 public ClassContext getClassContext() {
4748
4849 /**
4950 * Check see if the Code for this method should be visited.
50 *
51 *
5152 * @param obj
5253 * Code attribute
5354 * @return true if the Code should be visited
5657 return true;
5758 }
5859
60 @Override
5961 public void report() {
6062 }
6163 }
6264
63 // vim:ts=4
2525 import edu.umd.cs.findbugs.graph.AbstractGraph;
2626
2727 public class CallGraph extends AbstractGraph<CallGraphEdge, CallGraphNode> {
28 private IdentityHashMap<Method, CallGraphNode> methodToNodeMap;
28 private final IdentityHashMap<Method, CallGraphNode> methodToNodeMap;
2929
3030 public CallGraph() {
3131 this.methodToNodeMap = new IdentityHashMap<Method, CallGraphNode>();
5555 }
5656 }
5757
58 // vim:ts=4
3434
3535 /**
3636 * Constructor.
37 *
37 *
3838 * @param method
3939 * the method containing the call site
4040 * @param basicBlock
8383
8484 @Override
8585 public boolean equals(Object o) {
86 if (!(o instanceof CallSite))
86 if (!(o instanceof CallSite)) {
8787 return false;
88 }
8889 CallSite other = (CallSite) o;
8990 return method == other.method && getBasicBlock() == other.getBasicBlock() && getHandle() == other.getHandle();
9091 }
9192 }
9293
93 // vim:ts=4
2828 public class CategoryFilteringBugReporter extends DelegatingBugReporter {
2929 private static final boolean DEBUG = SystemProperties.getBoolean("cfbreporter.debug");
3030
31 private Set<String> categorySet;
31 private final Set<String> categorySet;
3232
3333 public CategoryFilteringBugReporter(BugReporter realBugReporter, Set<String> categorySet) {
3434 super(realBugReporter);
4242 if (categorySet.contains(category)) {
4343 getDelegate().reportBug(bugInstance);
4444 } else {
45 if (DEBUG)
45 if (DEBUG) {
4646 System.out.println("CategoryFilteringBugReporter: filtered due to category " + category);
47 }
4748 }
4849 }
4950 }
2424 * Check that the BCEL classes present seem to be the right ones. Specifically,
2525 * we check whether the ones extended in FindBugs code are non-final. The
2626 * following BCEL classes are extended in FindBugs code:
27 *
27 *
2828 * org.apache.bcel.generic.ObjectType; org.apache.bcel.generic.Type;
2929 * org.apache.bcel.Constants; org.apache.bcel.classfile.EmptyVisitor
3030 * org.apache.bcel.Repository;
31 *
31 *
3232 * @author langmead
3333 */
3434
6161
6262 /**
6363 * Check whether given Class is declared final
64 *
64 *
6565 * @param c
6666 * the class to check
6767 * @return true iff Class is declared final
7272
7373 /**
7474 * Output an appropriate error when a BCEL class looks wrong.
75 *
75 *
7676 * @param cname
7777 * name of the BCEL class
7878 */
8888 * Check that the BCEL classes present seem to be the right ones.
8989 * Specifically, we check whether the ones extended in FindBugs code are
9090 * non-final.
91 *
91 *
9292 * @return true iff all checks passed
9393 */
9494 public static boolean check() {
8585 return new ClassAnnotation(classDescriptor.toDottedClassName());
8686 }
8787
88 @Override
8889 public void accept(BugAnnotationVisitor visitor) {
8990 visitor.visitClassAnnotation(this);
9091 }
9192
9293 @Override
9394 protected String formatPackageMember(String key, ClassAnnotation primaryClass) {
94 if (key.equals("") || key.equals("hash"))
95 if ("".equals(key) || "hash".equals(key)) {
9596 return className;
96 else if (key.equals("givenClass"))
97 } else if ("givenClass".equals(key)) {
9798 return shorten(primaryClass.getPackageName(), className);
98 else if (key.equals("excludingPackage"))
99 } else if ("excludingPackage".equals(key)) {
99100 return shorten(getPackageName(), className);
100 else if (key.equals("simpleClass") || key.equals("simpleName"))
101 } else if ("simpleClass".equals(key) || "simpleName".equals(key)) {
101102 return ClassName.extractSimpleName(className);
102 else
103 } else {
103104 throw new IllegalArgumentException("unknown key " + key);
105 }
104106 }
105107
106108 @Override
110112
111113 @Override
112114 public boolean equals(Object o) {
113 if (!(o instanceof ClassAnnotation))
115 if (!(o instanceof ClassAnnotation)) {
114116 return false;
117 }
115118 ClassAnnotation other = (ClassAnnotation) o;
116119 return className.equals(other.className);
117120 }
122125
123126 public ClassAnnotation getTopLevelClass() {
124127 int firstDollar = className.indexOf('$');
125 if (firstDollar <= 0)
128 if (firstDollar <= 0) {
126129 return this;
130 }
127131 return new ClassAnnotation(className.substring(0, firstDollar));
128132
129133 }
130134
135 @Override
131136 public int compareTo(BugAnnotation o) {
132 if (!(o instanceof ClassAnnotation)) // BugAnnotations must be
133 // Comparable with any type of
134 // BugAnnotation
137 if (!(o instanceof ClassAnnotation)) {
138 // Comparable with any type of
139 // BugAnnotation
135140 return this.getClass().getName().compareTo(o.getClass().getName());
141 }
136142 ClassAnnotation other = (ClassAnnotation) o;
137143 return className.compareTo(other.className);
138144 }
144150 */
145151 @Override
146152 public SourceLineAnnotation getSourceLines() {
147 if (sourceLines == null)
153 if (sourceLines == null) {
148154 this.sourceLines = getSourceLinesForClass(className, sourceFileName);
155 }
149156 return sourceLines;
150157 }
151158
155162
156163 AnalysisContext currentAnalysisContext = AnalysisContext.currentAnalysisContext();
157164
158 if (currentAnalysisContext == null)
165 if (currentAnalysisContext == null) {
159166 return new SourceLineAnnotation(className, sourceFileName, -1, -1, -1, -1);
167 }
160168
161169 SourceInfoMap.SourceLineRange classLine = currentAnalysisContext.getSourceInfoMap().getClassLine(className);
162170
163 if (classLine == null)
171 if (classLine == null) {
164172 return SourceLineAnnotation.getSourceAnnotationForClass(className, sourceFileName);
165 else
173 } else {
166174 return new SourceLineAnnotation(className, sourceFileName, classLine.getStart(), classLine.getEnd(), -1, -1);
175 }
167176 }
168177
169178 /*
174183
175184 private static final String ELEMENT_NAME = "Class";
176185
186 @Override
177187 public void writeXML(XMLOutput xmlOutput) throws IOException {
178188 writeXML(xmlOutput, false, false);
179189 }
180190
191 @Override
181192 public void writeXML(XMLOutput xmlOutput, boolean addMessages, boolean isPrimary) throws IOException {
182193 XMLAttributeList attributeList = new XMLAttributeList().addAttribute("classname", getClassName());
183 if (isPrimary)
194 if (isPrimary) {
184195 attributeList.addAttribute("primary", "true");
196 }
185197
186198 String role = getDescription();
187 if (!role.equals(DEFAULT_ROLE))
199 if (!DEFAULT_ROLE.equals(role)) {
188200 attributeList.addAttribute("role", role);
201 }
189202
190203 xmlOutput.openTag(ELEMENT_NAME, attributeList);
191204 getSourceLines().writeXML(xmlOutput, addMessages, false);
199212 }
200213 }
201214
202 // vim:ts=4
2525 /**
2626 * Class to pre-screen class files, so that only a subset are analyzed. This
2727 * supports the -onlyAnalyze command line option.
28 *
28 *
2929 * Modified February 2006 in four ways: a) don't break windows platform by
3030 * hard-coding '/' as the directory separator b) store list of Matchers, not
3131 * Patterns, so we don't keep instantiating Matchers c) fix suffix bug, so
3232 * FooBar and Foo$Bar no longer match Bar d) addAllowedPackage() can now handle
3333 * unicode chars in filenames, though we still may not be handling every case
3434 * mentioned in section 7.2.1 of the JLS
35 *
35 *
3636 * @see FindBugs
3737 * @author David Hovemeyer
3838 */
4545 * general enough
4646 */
4747 private static final String SEP = "[/\\\\]"; // could include ':' for
48 // classic macOS
48 // classic macOS
4949
5050 private static final String START = "(?:^|" + SEP + ")"; // (?:) is a
51 // non-capturing
52 // group
51 // non-capturing
52 // group
5353
5454 /**
5555 * regular expression fragment to match a char of a class or package name.
5757 */
5858 private static final String JAVA_IDENTIFIER_PART = "[^./\\\\]";
5959
60 private LinkedList<Matcher> patternList;
60 private final LinkedList<Matcher> patternList;
6161
6262 /**
6363 * Constructor. By default, the ClassScreener will match <em>all</em> class
7272 /**
7373 * replace the dots in a fully-qualified class/package name to a regular
7474 * expression fragment that will match file names.
75 *
75 *
7676 * @param dotsName
7777 * such as "java.io" or "java.io.File"
7878 * @return regex fragment such as "java[/\\\\]io" (single backslash escaped
9595
9696 /**
9797 * Add the name of a class to be matched by the screener.
98 *
98 *
9999 * @param className
100100 * name of a class to be matched
101101 */
102102 public void addAllowedClass(String className) {
103103 String classRegex = START + dotsToRegex(className) + ".class$";
104 if (DEBUG)
104 if (DEBUG) {
105105 System.out.println("Class regex: " + classRegex);
106 }
106107 patternList.add(Pattern.compile(classRegex).matcher(""));
107108 }
108109
109110 /**
110111 * Add the name of a package to be matched by the screener. All class files
111112 * that appear to be in the package should be matched.
112 *
113 *
113114 * @param packageName
114115 * name of the package to be matched
115116 */
119120 }
120121
121122 String packageRegex = START + dotsToRegex(packageName) + SEP + JAVA_IDENTIFIER_PART + "+.class$";
122 if (DEBUG)
123 if (DEBUG) {
123124 System.out.println("Package regex: " + packageRegex);
125 }
124126 patternList.add(Pattern.compile(packageRegex).matcher(""));
125127 }
126128
128130 * Add the name of a prefix to be matched by the screener. All class files
129131 * that appear to be in the package specified by the prefix, or a more
130132 * deeply nested package, should be matched.
131 *
133 *
132134 * @param prefix
133135 * name of the prefix to be matched
134136 */
136138 if (prefix.endsWith(".")) {
137139 prefix = prefix.substring(0, prefix.length() - 1);
138140 }
139 if (DEBUG)
141 if (DEBUG) {
140142 System.out.println("Allowed prefix: " + prefix);
143 }
141144 String packageRegex = START + dotsToRegex(prefix) + SEP;
142 if (DEBUG)
145 if (DEBUG) {
143146 System.out.println("Prefix regex: " + packageRegex);
147 }
144148 patternList.add(Pattern.compile(packageRegex).matcher(""));
145149 }
146150
147151 /*
148152 * (non-Javadoc)
149 *
153 *
150154 * @see edu.umd.cs.findbugs.IClassScreener#matches(java.lang.String)
151155 */
156 @Override
152157 public boolean matches(String fileName) {
153158 // Special case: if no classes or packages have been defined,
154159 // then the screener matches all class files.
155 if (patternList.isEmpty())
160 if (patternList.isEmpty()) {
156161 return true;
162 }
157163
158 if (DEBUG)
164 if (DEBUG) {
159165 System.out.println("Matching: " + fileName);
166 }
160167
161168 // Scan through list of regexes
162169 for (Matcher matcher : patternList) {
163 if (DEBUG)
170 if (DEBUG) {
164171 System.out.print("\tTrying [" + matcher.pattern());
172 }
165173 matcher.reset(fileName);
166174 if (matcher.find()) {
167 if (DEBUG)
175 if (DEBUG) {
168176 System.out.println("]: yes!");
177 }
169178 return true;
170179 }
171 if (DEBUG)
180 if (DEBUG) {
172181 System.out.println("]: no");
182 }
173183 }
174184 return false;
175185 }
176186
177187 /*
178188 * (non-Javadoc)
179 *
189 *
180190 * @see edu.umd.cs.findbugs.IClassScreener#vacuous()
181191 */
192 @Override
182193 public boolean vacuous() {
183194 return patternList.isEmpty();
184195 }
185196 }
186197
187 // vim:ts=4
66 public ClassWarningSuppressor(String bugPattern, ClassAnnotation clazz) {
77 super(bugPattern);
88 this.clazz = clazz;
9 if (DEBUG)
9 if (DEBUG) {
1010 System.out.println("Suppressing " + bugPattern + " in " + clazz);
11 }
1112 }
1213
1314 public ClassAnnotation getClassAnnotation() {
1718 @Override
1819 public boolean match(BugInstance bugInstance) {
1920
20 if (!super.match(bugInstance))
21 if (!super.match(bugInstance)) {
2122 return false;
23 }
2224
2325 ClassAnnotation primaryClassAnnotation = bugInstance.getPrimaryClass();
24 if (DEBUG)
26 if (DEBUG) {
2527 System.out.println("Compare " + primaryClassAnnotation + " with " + clazz);
28 }
2629
2730 return clazz.contains(primaryClassAnnotation);
2831
4444 }
4545 BufferedReader br = UserTextFile.bufferedReader(System.in);
4646
47 @Override
4748 public void showMessageDialogAndWait(String message) throws InterruptedException {
4849 System.out.println(message);
4950 }
5051
52 @Override
5153 public void showMessageDialog(String message) {
5254 System.out.println(message);
5355 }
5456
57 @Override
5558 public int showConfirmDialog(String message, String title, String ok, String cancel) {
5659 String confirmStr = "Yes (Y) or No (N)?";
5760
9497 }
9598 }
9699
100 @Override
97101 public InputStream getProgressMonitorInputStream(InputStream in, int length, String msg) {
98102 return in;
99103 }
100104
105 @Override
101106 public void setErrorMessage(String errorMsg) {
102107 System.err.println(errorMsg);
103108 }
104109
110 @Override
105111 public void displayNonmodelMessage(String title, String message) {
106112 System.out.println(String.format("Message: %s%n%s", title, message));
107113 }
113119 * edu.umd.cs.findbugs.IGuiCallback#showQuestionDialog(java.lang.String,
114120 * java.lang.String, java.lang.String)
115121 */
122 @Override
116123 public String showQuestionDialog(String message, String title, String defaultValue) {
117124 throw new UnsupportedOperationException();
118125 }
119126
127 @Override
120128 public List<String> showForm(String message, String title, List<FormItem> labels) {
121129 throw new UnsupportedOperationException();
122130 }
126134 *
127135 * @see edu.umd.cs.findbugs.IGuiCallback#showDocument(java.net.URL)
128136 */
137 @Override
129138 public boolean showDocument(URL u) {
130139 return false;
131140 }
132141
142 @Override
133143 public void registerCloud(Project project, BugCollection collection, Cloud cloud) {
134144 }
135145
146 @Override
136147 public ExecutorService getBugUpdateExecutor() {
137148 return bugUpdateExecutor;
138149 }
139150
140151 private static class CurrentThreadExecutorService extends AbstractExecutorService {
152 @Override
141153 public void shutdown() {
142154 }
143155
156 @Override
144157 public List<Runnable> shutdownNow() {
145158 return null;
146159 }
147160
161 @Override
148162 public boolean isShutdown() {
149163 return false;
150164 }
151165
166 @Override
152167 public boolean isTerminated() {
153168 return false;
154169 }
155170
171 @Override
156172 public boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException {
157173 return false;
158174 }
159175
176 @Override
160177 public void execute(Runnable command) {
161178 command.run();
162179 }
167184 *
168185 * @see edu.umd.cs.findbugs.IGuiCallback#isHeadless()
169186 */
187 @Override
170188 public boolean isHeadless() {
171189 return true;
172190 }
179197 * Project, edu.umd.cs.findbugs.BugCollection,
180198 * edu.umd.cs.findbugs.cloud.Cloud)
181199 */
200 @Override
182201 public void unregisterCloud(Project project, BugCollection collection, Cloud cloud) {
183202
184203 }
189208 * @see
190209 * edu.umd.cs.findbugs.IGuiCallback#invokeInGUIThread(java.lang.Runnable)
191210 */
211 @Override
192212 public void invokeInGUIThread(Runnable r) {
193213 throw new UnsupportedOperationException();
194214
9292
9393 public Class<? extends T> getComponentClass() {
9494 if (!isAvailable()) {
95 if (FindBugs.isNoAnalysis())
95 if (FindBugs.isNoAnalysis()) {
9696 throw new IllegalStateException("No analysis set; no component class loaded for " + getPlugin());
97 }
9798 throw new IllegalStateException("No component class for " + getPlugin());
9899 }
99100 return componentClass;
4343 try {
4444 remote = AnalysisContext.lookupSystemClass("java.rmi.Remote");
4545 } catch (ClassNotFoundException e) {
46 if (storedException == null)
46 if (storedException == null) {
4747 storedException = e;
48 }
4849 }
4950 }
5051
5253 if (type instanceof ArrayType) {
5354 ArrayType a = (ArrayType) type;
5455 Type t = a.getBasicType();
55 if (t instanceof ReferenceType)
56 if (t instanceof ReferenceType) {
5657 type = (ReferenceType) t;
57 else
58 } else {
5859 return 1.0;
60 }
5961 }
6062 double result = isDeepSerializable(type.getSignature());
6163 if (type instanceof GenericObjectType && Subtypes2.isContainer(type)) {
6264 GenericObjectType gt = (GenericObjectType) type;
6365 List<? extends ReferenceType> parameters = gt.getParameters();
64 if (parameters != null) for(ReferenceType t : parameters) {
65 double r = isDeepSerializable(t);
66 if (result > r)
67 result = r;
68 }
69 }
70
66 if (parameters != null) {
67 for(ReferenceType t : parameters) {
68 double r = isDeepSerializable(t);
69 if (result > r) {
70 result = r;
71 }
72 }
73 }
74 }
75
7176 return result;
7277 }
73 public static ReferenceType getLeastSerializableTypeComponent(ReferenceType type)
78 public static ReferenceType getLeastSerializableTypeComponent(ReferenceType type)
7479 throws ClassNotFoundException {
7580 if (type instanceof ArrayType) {
7681 ArrayType a = (ArrayType) type;
7782 Type t = a.getBasicType();
78 if (t instanceof ReferenceType)
83 if (t instanceof ReferenceType) {
7984 type = (ReferenceType) t;
80 else
85 } else {
8186 return type;
87 }
8288 }
8389
8490 ReferenceType result = type;
8692 if (type instanceof GenericObjectType && Subtypes2.isContainer(type)) {
8793 GenericObjectType gt = (GenericObjectType) type;
8894 List<? extends ReferenceType> parameters = gt.getParameters();
89 if (parameters != null) for(ReferenceType t : parameters) {
90 double r = isDeepSerializable(t);
91 if (value > r) {
92 value = r;
93 result = getLeastSerializableTypeComponent(t);
95 if (parameters != null) {
96 for(ReferenceType t : parameters) {
97 double r = isDeepSerializable(t);
98 if (value > r) {
99 value = r;
100 result = getLeastSerializableTypeComponent(t);
101 }
94102 }
95103 }
96104 }
97
105
98106 return result;
99107 }
100108
101109 public static double isDeepSerializable(@DottedClassName String refSig) throws ClassNotFoundException {
102 if (storedException != null)
110 if (storedException != null) {
103111 throw storedException;
112 }
104113
105114 if (isPrimitiveComponentClass(refSig)) {
106115 if (DEBUG) {
110119 }
111120
112121 String refName = getComponentClass(refSig);
113 if (refName.equals("java.lang.Object"))
122 if ("java.lang.Object".equals(refName)) {
114123 return 0.99;
124 }
115125
116126 JavaClass refJavaClass = Repository.lookupClass(refName);
117127 return isDeepSerializable(refJavaClass);
122132 }
123133
124134 public static double isDeepRemote(String refSig) {
125 if (remote == null)
135 if (remote == null) {
126136 return 0.1;
137 }
127138
128139 String refName = getComponentClass(refSig);
129 if (refName.equals("java.lang.Object"))
140 if ("java.lang.Object".equals(refName)) {
130141 return 0.99;
142 }
131143
132144 JavaClass refJavaClass;
133145 try {
158170 }
159171
160172 public static String getComponentClass(String refSig) {
161 while (refSig.charAt(0) == '[')
173 while (refSig.charAt(0) == '[') {
162174 refSig = refSig.substring(1);
175 }
163176
164177 // TODO: This method now returns primitive type signatures, is this ok?
165 if (refSig.charAt(0) == 'L')
178 if (refSig.charAt(0) == 'L') {
166179 return refSig.substring(1, refSig.length() - 1).replace('/', '.');
180 }
167181 return refSig;
168182 }
169183
170184 public static double isDeepSerializable(JavaClass x) throws ClassNotFoundException {
171 if (storedException != null)
185 if (storedException != null) {
172186 throw storedException;
173
174 if (x.getClassName().equals("java.lang.Object"))
187 }
188
189 if ("java.lang.Object".equals(x.getClassName())) {
175190 return 0.4;
191 }
176192
177193 if (DEBUG) {
178194 System.out.println("checking " + x.getClassName());
186202 return result;
187203 }
188204
189 if (x.isFinal())
190 return result;
205 if (x.isFinal()) {
206 return result;
207 }
191208
192209 double collectionResult = Analyze.deepInstanceOf(x, collection);
193210 double mapResult = Analyze.deepInstanceOf(x, map);
210227 if (x.isAbstract() || x.isInterface()) {
211228 confidence = 0.8;
212229 result = Math.max(result, 0.4);
213 } else if (directSubtypes.isEmpty())
230 } else if (directSubtypes.isEmpty()) {
214231 confidence = 0.2;
232 }
215233
216234 double confidence2 = (1 + confidence) / 2;
217235 result = Math.max(result, confidence2 * collectionResult);
249267 }
250268
251269
252 if (DEBUG) {
270 if (DEBUG) {
253271 System.out.println("No high results; max: " + result);
254272 }
255273 return result;
3232 * @author David Hovemeyer
3333 */
3434 public class DelegatingBugReporter implements BugReporter {
35 private BugReporter delegate;
35 private final BugReporter delegate;
3636
3737 /**
3838 * Constructor.
4848 return this.delegate;
4949 }
5050
51 @Override
5152 public void setErrorVerbosity(int level) {
5253 delegate.setErrorVerbosity(level);
5354 }
5455
56 @Override
5557 public void setPriorityThreshold(int threshold) {
5658 delegate.setPriorityThreshold(threshold);
5759 }
5860
61 @Override
5962 public void observeClass(ClassDescriptor classDescriptor) {
6063 delegate.observeClass(classDescriptor);
6164 }
6265
66 @Override
6367 public void reportBug(@Nonnull BugInstance bugInstance) {
6468 delegate.reportBug(bugInstance);
6569 }
6670
71 @Override
6772 public void logError(String message) {
6873 delegate.logError(message);
6974 }
7075
76 @Override
7177 public void reportMissingClass(ClassNotFoundException ex) {
7278 delegate.reportMissingClass(ex);
7379 }
7480
81 @Override
7582 public void reportMissingClass(ClassDescriptor classDescriptor) {
7683 delegate.reportMissingClass(classDescriptor);
7784 }
7885
86 @Override
7987 public void finish() {
8088 delegate.finish();
8189 }
8290
91 @Override
8392 public void reportQueuedErrors() {
8493 delegate.reportQueuedErrors();
8594 }
8695
96 @Override
8797 public void addObserver(BugReporterObserver observer) {
8898 delegate.addObserver(observer);
8999 }
90100
101 @Override
91102 public ProjectStats getProjectStats() {
92103 return delegate.getProjectStats();
93104 }
94105
106 @Override
95107 public void logError(String message, Throwable e) {
96 if (e instanceof MethodUnprofitableException)
108 if (e instanceof MethodUnprofitableException) {
97109 return;
110 }
98111 delegate.logError(message, e);
99112 }
100113
103116 *
104117 * @param method
105118 */
119 @Override
106120 public void reportSkippedAnalysis(MethodDescriptor method) {
107121 delegate.reportSkippedAnalysis(method);
108122 }
109123
124 @Override
110125 public @CheckForNull
111126 BugCollection getBugCollection() {
112127 return delegate.getBugCollection();
113128 }
114129 }
115130
116 // vim:ts=4
2828 /**
2929 * Visit the ClassContext for a class which should be analyzed for instances
3030 * of bug patterns.
31 *
31 *
3232 * @param classContext
3333 * the ClassContext
3434 */
4242 public void report();
4343 }
4444
45 // vim:ts=4
2323
2424 /**
2525 * Detector interface for new bytecode-framework-neutral architecture.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public interface Detector2 extends Priorities {
3030
3131 /**
3232 * Visit a class.
33 *
33 *
3434 * @param classDescriptor
3535 * descriptor naming the class to visit
3636 * @throws CheckedAnalysisException
4545
4646 /**
4747 * Get the name of the detector class.
48 *
48 *
4949 * @return the name of the detector class.
5050 */
5151 public String getDetectorClassName();
5656
5757 ReflectionDetectorCreator(Class<?> detectorClass) {
5858 this.detectorClass = detectorClass;
59 if (SUPPORT_OLD_DETECTOR_INTERFACE)
59 if (SUPPORT_OLD_DETECTOR_INTERFACE) {
6060 try {
6161 setAnalysisContext = detectorClass.getDeclaredMethod("setAnalysisContext",
6262 new Class[] { AnalysisContext.class });
6363 } catch (NoSuchMethodException e) {
6464 // Ignore
6565 }
66 }
6667 }
6768
6869 @Override
9495 }
9596
9697 if (Detector.class.isAssignableFrom(detectorClass)) {
97 if (NonReportingDetector.class.isAssignableFrom(detectorClass))
98 if (NonReportingDetector.class.isAssignableFrom(detectorClass)) {
9899 return new NonReportingDetectorToDetector2Adapter(createDetector(bugReporter));
100 }
99101 return new DetectorToDetector2Adapter(createDetector(bugReporter));
100102
101103 }
213215 * interface
214216 */
215217 public boolean isDetectorClassSubtypeOf(Class<?> otherClass) {
216 if (FindBugs.isNoAnalysis())
218 if (FindBugs.isNoAnalysis()) {
217219 throw new IllegalStateException("No analysis specified");
220 }
218221 return otherClass.isAssignableFrom(detectorCreator.getDetectorClass());
219222 }
220223
228231 return !isDetectorClassSubtypeOf(TrainingDetector.class) && !isDetectorClassSubtypeOf(FirstPassDetector.class);
229232 }
230233
231
234
232235 /**
233236 * Check to see if we are running on a recent-enough JRE for this detector
234237 * to be enabled.
237240 * false if it is too old
238241 */
239242 public boolean isEnabledForCurrentJRE() {
240 if (requireJRE.equals(""))
243 if ("".equals(requireJRE)) {
241244 return true;
245 }
242246 try {
243247 JavaVersion requiredVersion = new JavaVersion(requireJRE);
244248 JavaVersion runtimeVersion = JavaVersion.getRuntimeVersion();
308312 * @return the priority adjustment
309313 */
310314 public int getPriorityAdjustment() {
311 if (enabledButNonReporting)
315 if (enabledButNonReporting) {
312316 return 100;
317 }
313318 return priorityAdjustment;
314319 }
315320
338343 while (tok.hasMoreTokens()) {
339344 String type = tok.nextToken();
340345 BugPattern bugPattern = DetectorFactoryCollection.instance().lookupBugPattern(type);
341 if (bugPattern != null)
346 if (bugPattern != null) {
342347 result.add(bugPattern);
348 }
343349 }
344350 return result;
345351 }
369375 */
370376 @Deprecated
371377 public Detector create(BugReporter bugReporter) {
372 if (FindBugs.isNoAnalysis())
378 if (FindBugs.isNoAnalysis()) {
373379 throw new IllegalStateException("No analysis specified");
380 }
374381 return detectorCreator.createDetector(bugReporter);
375382 }
376383
382389 * @return the Detector2
383390 */
384391 public Detector2 createDetector2(BugReporter bugReporter) {
385 if (FindBugs.isNoAnalysis())
392 if (FindBugs.isNoAnalysis()) {
386393 throw new IllegalStateException("No analysis specified");
394 }
387395 return detectorCreator.createDetector2(bugReporter);
388396 }
389397
393401 */
394402 public String getShortName() {
395403 int endOfPkg = className.lastIndexOf('.');
396 if (endOfPkg >= 0)
404 if (endOfPkg >= 0) {
397405 return className.substring(endOfPkg + 1);
406 }
398407 return className;
399408 }
400409
2020
2121 /**
2222 * Predicate for choosing DetectorFactory objects.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public interface DetectorFactoryChooser {
2727 /**
2828 * Return whether or not given DetectorFactory should be chosen.
29 *
29 *
3030 * @param factory
3131 * the DetectorFactory
3232 * @return true if the DetectorFactory should be chosen, false if not
3636 /**
3737 * Enable the factory due to ordering constraints with other enabled
3838 * detectors
39 *
39 *
4040 * @param factory
4141 */
4242 public void enable(DetectorFactory factory);
6060
6161 private static final Logger LOGGER = Logger.getLogger(DetectorFactoryCollection.class.getName());
6262 private static final boolean DEBUG_JAWS = SystemProperties.getBoolean("findbugs.jaws.debug");
63 // private static final boolean DEBUG = Boolean.getBoolean("dfc.debug");
63 // private static final boolean DEBUG = Boolean.getBoolean("dfc.debug");
6464
6565 private static DetectorFactoryCollection theInstance;
6666 private static final Object lock = new Object();
7777
7878 private final UpdateChecker updateChecker;
7979 private final CopyOnWriteArrayList<PluginUpdateListener> pluginUpdateListeners
80 = new CopyOnWriteArrayList<PluginUpdateListener>();
80 = new CopyOnWriteArrayList<PluginUpdateListener>();
8181 private volatile List<UpdateChecker.PluginUpdate> updates;
8282 private boolean updatesForced;
8383 private final Collection<Plugin> pluginsToUpdate;
165165 }
166166 }
167167
168 private void setGlobalOptions() {
169 globalOptions.clear();
170 globalOptionsSetter.clear();
171
172 for(Plugin p : plugins()) {
173 if (p.isGloballyEnabled()) {
174 for(Map.Entry<String, String> e : p.getMyGlobalOptions().entrySet()) {
175 String key = e.getKey();
176 String value = e.getValue();
177 String oldValue = globalOptions.get(key);
178
179 if (oldValue != null) {
180 if (oldValue.equals(value)) {
181 continue;
182 }
183 Plugin oldP = globalOptionsSetter.get(key);
184 throw new RuntimeException(
185 "Incompatible global options for " + key + "; conflict between " + oldP.getPluginId() + " and " + p.getPluginId());
186 }
187 globalOptions.put(key, value);
188 globalOptionsSetter.put(key, p);
189 }
190 }
191 }
192 }
168 private void setGlobalOptions() {
169 globalOptions.clear();
170 globalOptionsSetter.clear();
171
172 for(Plugin p : plugins()) {
173 if (p.isGloballyEnabled()) {
174 for(Map.Entry<String, String> e : p.getMyGlobalOptions().entrySet()) {
175 String key = e.getKey();
176 String value = e.getValue();
177 String oldValue = globalOptions.get(key);
178
179 if (oldValue != null) {
180 if (oldValue.equals(value)) {
181 continue;
182 }
183 Plugin oldP = globalOptionsSetter.get(key);
184 throw new RuntimeException(
185 "Incompatible global options for " + key + "; conflict between " + oldP.getPluginId() + " and " + p.getPluginId());
186 }
187 globalOptions.put(key, value);
188 globalOptionsSetter.put(key, p);
189 }
190 }
191 }
192 }
193 @Override
193194 public @CheckForNull String getGlobalOption(String key) {
194195 return globalOptions.get(key);
195196 }
196197
198 @Override
197199 public @CheckForNull Plugin getGlobalOptionSetter(String key) {
198200 return globalOptionsSetter.get(key);
199201 }
215217
216218 @Nonnull
217219 public Plugin getCorePlugin() {
218 if (corePlugin == null)
220 if (corePlugin == null) {
219221 throw new IllegalStateException("No core plugin");
222 }
220223 return corePlugin;
221224 }
222225
246249 */
247250 public Iterable<DetectorFactory> getFactories() {
248251 return factoryList;
252 }
253
254 public boolean isDisabledByDefault(String bugPatternOrCode) {
255 @CheckForNull BugPattern pattern = lookupBugPattern(bugPatternOrCode);
256 if (pattern != null) {
257 for(DetectorFactory fac : factoryList) {
258 if (fac.isDefaultEnabled() && fac.getReportedBugPatterns().contains(pattern)) {
259 return false;
260 }
261 }
262 return true;
263 }
264 @CheckForNull BugCode code = lookupBugCode(bugPatternOrCode);
265 if (code != null) {
266 for(DetectorFactory fac : factoryList) {
267 if (fac.isDefaultEnabled()) {
268 for(BugPattern p : fac.getReportedBugPatterns()) {
269 if (p.getBugCode().equals(code)) {
270 return false;
271 }
272 }
273 }
274 }
275 return true;
276 }
277
278 return false;
249279 }
250280
251281 /**
276306 * Register a DetectorFactory.
277307 */
278308 void registerDetector(DetectorFactory factory) {
279 if (FindBugs.DEBUG)
309 if (FindBugs.DEBUG) {
280310 System.out.println("Registering detector: " + factory.getFullName());
311 }
281312 String detectorName = factory.getShortName();
282313 if(!factoryList.contains(factory)) {
283314 factoryList.add(factory);
290321 }
291322
292323 void unRegisterDetector(DetectorFactory factory) {
293 if (FindBugs.DEBUG)
324 if (FindBugs.DEBUG) {
294325 System.out.println("Unregistering detector: " + factory.getFullName());
326 }
295327 String detectorName = factory.getShortName();
296328 factoryList.remove(factory);
297329 factoriesByName.remove(detectorName);
314346 if (findbugsJarCodeBase != null) {
315347 File findbugsJar = new File(findbugsJarCodeBase);
316348 File libDir = findbugsJar.getParentFile();
317 if (libDir.getName().equals("lib")) {
349 if ("lib".equals(libDir.getName())) {
318350 String fbHome = libDir.getParent();
319351 FindBugs.setHome(fbHome);
320352 return fbHome;
323355 }
324356 String classFilePath = FindBugs.class.getName().replaceAll("\\.", "/") + ".class";
325357 URL resource = FindBugs.class.getClassLoader().getResource(classFilePath);
326 if (resource != null && resource.getProtocol().equals("file")) {
358 if (resource != null && "file".equals(resource.getProtocol())) {
327359 try {
328360 String classfile = URLDecoder.decode(resource.getPath(), Charset.defaultCharset().name());
329361 Matcher m = Pattern.compile("(.*)/.*?/edu/umd.*").matcher(classfile);
366398 }
367399
368400 public static void jawsDebugMessage(String message) {
369 if (DEBUG_JAWS)
401 if (DEBUG_JAWS) {
370402 JOptionPane.showMessageDialog(null, message);
371 else if (FindBugs.DEBUG)
403 } else if (FindBugs.DEBUG) {
372404 System.err.println(message);
405 }
373406 }
374407
375408 void loadPlugin(Plugin plugin) {
426459 }
427460 }
428461
462 @Override
429463 @SuppressWarnings({"ConstantConditions"})
430464 public void pluginUpdateCheckComplete(List<UpdateChecker.PluginUpdate> newUpdates, boolean force) {
431465 this.updates = newUpdates;
451485 public FutureValue<Collection<UpdateChecker.PluginUpdate>> getUpdates() {
452486 final FutureValue<Collection<UpdateChecker.PluginUpdate>> results = new FutureValue<Collection<UpdateChecker.PluginUpdate>>();
453487 addPluginUpdateListener(new PluginUpdateListener() {
488 @Override
454489 public void pluginUpdateCheckComplete(Collection<UpdateChecker.PluginUpdate> u, boolean force) {
455490 results.set(u);
456491 }
458493 return results;
459494 }
460495
461
496
462497 public Map<String, CloudPlugin> getRegisteredClouds() {
463498 return Collections.unmodifiableMap(registeredClouds);
464499 }
483518 */
484519 public boolean registerBugCategory(BugCategory bc) {
485520 String category = bc.getCategory();
486 if (categoryDescriptionMap.get(category) != null)
521 if (categoryDescriptionMap.get(category) != null) {
487522 return false;
523 }
488524 categoryDescriptionMap.put(category, bc);
489525 return true;
490526 }
520556 * Get an Iterator over all registered bug patterns.
521557 */
522558 public Collection<BugPattern> getBugPatterns() {
523 return bugPatternMap.values();
559 return bugPatternMap.values();
524560 }
525561
526562 /**
531567 * @return the BugPattern, or null if it can't be found
532568 */
533569 public @CheckForNull BugPattern lookupBugPattern(String bugType) {
570 if (bugType == null) {
571 return null;
572 }
534573 return bugPatternMap.get(bugType);
535574 }
536575
557596 */
558597 public @Nonnull BugCode getBugCode(String shortBugType) {
559598 BugCode bugCode = lookupBugCode(shortBugType);
560 if (bugCode == null)
599 if (bugCode == null) {
561600 throw new IllegalArgumentException("Error: missing bug code for key" + shortBugType);
601 }
562602 return bugCode;
563603 }
564604
592632 */
593633 public Collection<String> getBugCategories() {
594634 ArrayList<String> result = new ArrayList<String>(categoryDescriptionMap.size());
595 for(BugCategory c : categoryDescriptionMap.values())
596 if (!c.isHidden())
635 for(BugCategory c : categoryDescriptionMap.values()) {
636 if (!c.isHidden()) {
597637 result.add(c.getCategory());
638 }
639 }
598640 return result;
599 }
641 }
600642
601643 public Collection<BugCategory> getBugCategoryObjects() {
602644 return categoryDescriptionMap.values(); // backed by the Map
2828 /**
2929 * An adapter allowing classes implementing the Detector interface to support
3030 * the new Detector2 interface.
31 *
31 *
3232 * @author David Hovemeyer
3333 */
3434 public class DetectorToDetector2Adapter implements Detector2 {
35 private Detector detector;
35 private final Detector detector;
3636
3737 /**
3838 * Constructor.
39 *
39 *
4040 * @param detector
4141 * the Detector we want to adapt
4242 */
4646
4747 /*
4848 * (non-Javadoc)
49 *
49 *
5050 * @see edu.umd.cs.findbugs.Detector2#finishPass()
5151 */
52 @Override
5253 public void finishPass() {
5354 detector.report();
5455 }
5556
5657 /*
5758 * (non-Javadoc)
58 *
59 *
5960 * @see
6061 * edu.umd.cs.findbugs.Detector2#visitClass(edu.umd.cs.findbugs.classfile
6162 * .ClassDescriptor)
6263 */
64 @Override
6365 public void visitClass(ClassDescriptor classDescriptor) throws CheckedAnalysisException {
6466
6567 // Just get the ClassContext from the analysis cache
7880
7981 /*
8082 * (non-Javadoc)
81 *
83 *
8284 * @see edu.umd.cs.findbugs.Detector2#getDetectorClassName()
8385 */
86 @Override
8487 public String getDetectorClassName() {
8588 return detector.getClass().getName();
8689 }
7777
7878 private static class NoOpErrorLogger implements IErrorLogger {
7979
80 @Override
8081 public void reportMissingClass(ClassNotFoundException ex) {
8182 }
8283
84 @Override
8385 public void reportMissingClass(ClassDescriptor classDescriptor) {
8486 }
8587
88 @Override
8689 public void logError(String message) {
8790 }
8891
92 @Override
8993 public void logError(String message, Throwable e) {
9094 }
9195
96 @Override
9297 public void reportSkippedAnalysis(MethodDescriptor method) {
9398 }
9499 }
95100
96101 private static class NoOpProgress implements Progress {
102 @Override
97103 public void startScanningArchives(int numArchivesToScan) {
98104 }
99105
106 @Override
100107 public void doneScanningArchives() {
101108 }
102109
110 @Override
103111 public void startScanningClasses(int numClassesToScan) {
104112 }
105113
114 @Override
106115 public void finishClass() {
107116 }
108117
118 @Override
109119 public void doneScanningClasses() {
110120 }
111121
122 @Override
112123 public void finishArchive() {
113124 }
114125
126 @Override
115127 public void startRecursiveDirectorySearch() {
116128 }
117129
130 @Override
118131 public void doneRecursiveDirectorySearch() {
119132 }
120133
134 @Override
121135 public void startArchive(String name) {
122136 }
123137
224238 // Find all directories underneath the root source directory
225239 progress.startRecursiveDirectorySearch();
226240 RecursiveFileSearch rfs = new RecursiveFileSearch(rootSourceDirectory, new FileFilter() {
241 @Override
227242 public boolean accept(File pathname) {
228243 return pathname.isDirectory();
229244 }
258273 }
259274
260275 private IClassPath buildClassPath(IClassPathBuilder builder, IClassFactory factory) throws InterruptedException, IOException,
261 CheckedAnalysisException {
276 CheckedAnalysisException {
262277
263278 progress.startScanningArchives(project.getFileCount());
264279
280295 }
281296
282297 private String findFullyQualifiedSourceFileName(IClassPath classPath, ClassDescriptor classDesc) throws IOException,
283 CheckedAnalysisException {
298 CheckedAnalysisException {
284299 try {
285300 // Open and parse the class file to attempt
286301 // to discover the source file name.
298313 String packageName = classDesc.getPackageName();
299314 String sourceFile = classInfo.getSource();
300315
301 if (!packageName.equals("")) {
316 if (!"".equals(packageName)) {
302317 packageName = packageName.replace('.', '/');
303318 packageName += "/";
304319 }
386401
387402 IErrorLogger errorLogger = new IErrorLogger() {
388403
404 @Override
389405 public void reportMissingClass(ClassNotFoundException ex) {
390406 String className = ClassNotFoundExceptionParser.getMissingClassName(ex);
391407 if (className != null) {
395411 }
396412 }
397413
414 @Override
398415 public void reportMissingClass(ClassDescriptor classDescriptor) {
399416 logError("Missing class: " + classDescriptor.toDottedClassName());
400417 }
401418
419 @Override
402420 public void logError(String message) {
403421 System.err.println("Error: " + message);
404422 }
405423
424 @Override
406425 public void logError(String message, Throwable e) {
407426 logError(message + ": " + e.getMessage());
408427 }
409428
429 @Override
410430 public void reportSkippedAnalysis(MethodDescriptor method) {
411431 logError("Skipped analysis of method " + method.toString());
412432 }
415435
416436 DiscoverSourceDirectories.Progress progress = new DiscoverSourceDirectories.Progress() {
417437
438 @Override
418439 public void startRecursiveDirectorySearch() {
419440 System.out.print("Scanning directories...");
420441 System.out.flush();
421442 }
422443
444 @Override
423445 public void doneRecursiveDirectorySearch() {
424446 System.out.println("done");
425447 }
426448
449 @Override
427450 public void startScanningArchives(int numArchivesToScan) {
428451 System.out.print("Scanning " + numArchivesToScan + " archives..");
429452 System.out.flush();
430453 }
431454
455 @Override
432456 public void doneScanningArchives() {
433457 System.out.println("done");
434458 }
435459
460 @Override
436461 public void startScanningClasses(int numClassesToScan) {
437462 System.out.print("Scanning " + numClassesToScan + " classes...");
438463 System.out.flush();
439464 }
440465
466 @Override
441467 public void finishClass() {
442468 System.out.print(".");
443469 System.out.flush();
444470 }
445471
472 @Override
446473 public void doneScanningClasses() {
447474 System.out.println("done");
448475 }
449476
477 @Override
450478 public void finishArchive() {
451479 System.out.print(".");
452480 System.out.flush();
453481 }
454482
483 @Override
455484 public void startArchive(String name) {
456485 // noop
457486 }
3838 */
3939 public class EmacsBugReporter extends TextUIBugReporter {
4040
41 private HashSet<BugInstance> seenAlready = new HashSet<BugInstance>();
41 private final HashSet<BugInstance> seenAlready = new HashSet<BugInstance>();
4242
43 private HashMap<String, String> sourceFileNameCache = new HashMap<String, String>();
43 private final HashMap<String, String> sourceFileNameCache = new HashMap<String, String>();
4444
45 @Override
4546 public void observeClass(ClassDescriptor classDescriptor) {
4647 try {
4748 JavaClass javaClass = AnalysisContext.currentAnalysisContext().lookupClass(classDescriptor.toDottedClassName());
7879 try {
7980 fullPath = sourceFinder.findSourceFile(pkgName, line.getSourceFile()).getFullFileName();
8081 } catch (IOException e) {
81 if (pkgName.equals(""))
82 if ("".equals(pkgName)) {
8283 fullPath = line.getSourceFile();
83 else
84 } else {
8485 fullPath = pkgName.replace('.', '/') + "/" + line.getSourceFile();
86 }
8587 }
8688 outputStream.print(fullPath + ":" + lineStart + ":" + lineEnd + " " + bugInstance.getMessage());
8789
113115 }
114116 }
115117
118 @Override
116119 public void finish() {
117120 outputStream.close();
118121 }
119122
123 @Override
120124 public @CheckForNull
121125 BugCollection getBugCollection() {
122126 return null;
2727 public class ErrorCountingBugReporter extends DelegatingBugReporter {
2828 private int bugCount;
2929
30 private HashSet<String> errors = new HashSet<String>();
30 private final HashSet<String> errors = new HashSet<String>();
3131
32 private Set<String> missingClassSet = new HashSet<String>();
32 private final Set<String> missingClassSet = new HashSet<String>();
3333
3434 public ErrorCountingBugReporter(BugReporter realBugReporter) {
3535 super(realBugReporter);
3636 this.bugCount = 0;
37
37
3838 // Add an observer to record when bugs make it through
3939 // all priority and filter criteria, so our bug count is
4040 // accurate.
4141 realBugReporter.addObserver(new BugReporterObserver() {
42 @Override
4243 public void reportBug(BugInstance bugInstance) {
4344 ++bugCount;
4445 }
5960
6061 @Override
6162 public void logError(String message) {
62 if (errors.add(message))
63 super.logError(message);
63 if (errors.add(message)) {
64 super.logError(message);
65 }
6466 }
6567
6668 @Override
6769 public void reportMissingClass(ClassNotFoundException ex) {
6870 String missing = AbstractBugReporter.getMissingClassName(ex);
69 if (missing == null || missing.startsWith("[") || missing.equals("java.lang.Synthetic")) {
71 if (missing == null || missing.startsWith("[") || "java.lang.Synthetic".equals(missing)) {
7072 return;
7173 }
7274 if (missingClassSet.add(missing)) {
4949 * @throws DocumentException
5050 */
5151 public static void addToExcludedInstanceHashes(Set<String> instanceHashesToExclude, String baseline) throws IOException,
52 DocumentException {
52 DocumentException {
5353 Project project = new Project();
5454 BugCollection origCollection;
5555 origCollection = new SortedBugCollection(project);
5656 origCollection.readXML(baseline);
57 for (BugInstance b : origCollection.getCollection())
57 for (BugInstance b : origCollection.getCollection()) {
5858 instanceHashesToExclude.add(b.getInstanceHash());
59 }
5960 }
6061
6162 @Override
6263 public void reportBug(@Nonnull BugInstance bugInstance) {
6364 String instanceHash = bugInstance.getInstanceHash();
64 if (!excludedHashes.contains(instanceHash))
65 if (!excludedHashes.contains(instanceHash)) {
6566 getDelegate().reportBug(bugInstance);
67 }
6668 }
6769 }
4040 public static final int BUGS_FOUND_FLAG = 1;
4141 }
4242
43 // vim:ts=4
2020 /**
2121 * A fatal exception which should completely halt the FindBugs analysis. Use
2222 * sparingly.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public class FatalException extends RuntimeException {
2828
2929 /**
3030 * Constructor.
31 *
31 *
3232 * @param msg
3333 * message describing the exception
3434 */
3838
3939 /**
4040 * Constructor.
41 *
41 *
4242 * @param msg
4343 * message describing the exception
4444 * @param cause
4646
4747 /**
4848 * A BugAnnotation specifying a particular field in particular class.
49 *
49 *
5050 * @author David Hovemeyer
5151 * @see BugAnnotation
5252 */
6767
6868 public static final String ARGUMENT_ROLE = "FIELD_ARGUMENT";
6969
70 private String fieldName;
71
72 private String fieldSig;
70 private final String fieldName;
71
72 private final String fieldSig;
7373
7474 private String fieldSourceSig;
7575
76 private boolean isStatic;
76 private final boolean isStatic;
7777
7878 /**
7979 * Constructor.
80 *
80 *
8181 * @param className
8282 * the name of the class containing the field
8383 * @param fieldName
8787 */
8888 public FieldAnnotation(@DottedClassName String className, String fieldName, String fieldSig, boolean isStatic) {
8989 super(className, DEFAULT_ROLE);
90 if (fieldSig.indexOf(".") >= 0) {
90 if (fieldSig.indexOf('.') >= 0) {
9191 assert false : "signatures should not be dotted: " + fieldSig;
92 fieldSig = fieldSig.replace('.', '/');
92 fieldSig = fieldSig.replace('.', '/');
9393 }
9494 this.fieldName = fieldName;
9595 this.fieldSig = fieldSig;
104104
105105 /**
106106 * Constructor.
107 *
107 *
108108 * @param className
109109 * the name of the class containing the field
110110 * @param fieldName
121121 /**
122122 * Factory method. Class name, field name, and field signatures are taken
123123 * from the given visitor, which is visiting the field.
124 *
124 *
125125 * @param visitor
126126 * the visitor which is visiting the field
127127 * @return the FieldAnnotation object
135135 * Factory method. Class name, field name, and field signatures are taken
136136 * from the given visitor, which is visiting a reference to the field (i.e.,
137137 * a getfield or getstatic instruction).
138 *
138 *
139139 * @param visitor
140140 * the visitor which is visiting the field reference
141141 * @return the FieldAnnotation object
148148
149149 /**
150150 * Factory method. Construct from class name and BCEL Field object.
151 *
151 *
152152 * @param className
153153 * the name of the class which defines the field
154154 * @param field
161161
162162 /**
163163 * Factory method. Construct from class name and BCEL Field object.
164 *
164 *
165165 * @param jClass
166166 * the class which defines the field
167167 * @param field
174174
175175 /**
176176 * Factory method. Construct from a FieldDescriptor.
177 *
177 *
178178 * @param fieldDescriptor
179179 * the FieldDescriptor
180180 * @return the FieldAnnotation
189189 fieldDescriptor.getSourceSignature(), fieldDescriptor.isStatic());
190190 }
191191
192
192
193193 public XField toXField() {
194194 return XFactory.createXField(className, fieldName, fieldSig, isStatic);
195195 }
196
196
197197 public FieldDescriptor toFieldDescriptor() {
198 return DescriptorFactory.instance().getFieldDescriptor(this);
198 return DescriptorFactory.instance().getFieldDescriptor(this);
199199 }
200200 /**
201201 * Get the field name.
220220
221221 /**
222222 * Is the given instruction a read of a field?
223 *
223 *
224224 * @param ins
225225 * the Instruction to check
226226 * @param cpg
232232 FieldInstruction fins = (FieldInstruction) ins;
233233 String className = fins.getClassName(cpg);
234234 return new FieldAnnotation(className, fins.getName(cpg), fins.getSignature(cpg), fins instanceof GETSTATIC);
235 } else
235 } else {
236236 return null;
237 }
237238 }
238239
239240 /**
240241 * Is the instruction a write of a field?
241 *
242 *
242243 * @param ins
243244 * the Instruction to check
244245 * @param cpg
250251 FieldInstruction fins = (FieldInstruction) ins;
251252 String className = fins.getClassName(cpg);
252253 return new FieldAnnotation(className, fins.getName(cpg), fins.getSignature(cpg), fins instanceof PUTSTATIC);
253 } else
254 } else {
254255 return null;
255 }
256
256 }
257 }
258
259 @Override
257260 public void accept(BugAnnotationVisitor visitor) {
258261 visitor.visitFieldAnnotation(this);
259262 }
260263
261264 @Override
262265 protected String formatPackageMember(String key, ClassAnnotation primaryClass) {
263 if (key.equals("") || key.equals("hash"))
266 if ("".equals(key) || "hash".equals(key)) {
264267 return className + "." + fieldName;
265 else if (key.equals("givenClass")) {
268 } else if ("givenClass".equals(key)) {
266269 String primaryClassName = primaryClass.getClassName();
267 if (className.equals(primaryClassName))
270 if (className.equals(primaryClassName)) {
268271 return getNameInClass(primaryClass);
269 else
272 } else {
270273 return shorten(primaryClass.getPackageName(), className) + "." + fieldName;
271 } else if (key.equals("name"))
274 }
275 } else if ("name".equals(key)) {
272276 return fieldName;
273 else if (key.equals("fullField")) {
277 } else if ("fullField".equals(key)) {
274278 SignatureConverter converter = new SignatureConverter(fieldSig);
275279 StringBuilder result = new StringBuilder();
276 if (isStatic)
280 if (isStatic) {
277281 result.append("static ");
282 }
278283 result.append(converter.parseNext());
279284 result.append(' ');
280285 result.append(className);
281286 result.append('.');
282287 result.append(fieldName);
283288 return result.toString();
284 } else
289 } else {
285290 throw new IllegalArgumentException("unknown key " + key);
286 }
287
288 /**
289 * @param primaryClass
290 * @return
291 */
291 }
292 }
293
292294 private String getNameInClass(ClassAnnotation primaryClass) {
293 if (primaryClass == null)
295 if (primaryClass == null) {
294296 return className + "." + fieldName;
297 }
295298 String givenPackageName = primaryClass.getPackageName();
296299 String thisPackageName = this.getPackageName();
297 if (thisPackageName.equals(givenPackageName))
298 if (thisPackageName.length() == 0)
300 if (thisPackageName.equals(givenPackageName)) {
301 if (thisPackageName.length() == 0) {
299302 return fieldName;
300 else
303 } else {
301304 return className.substring(thisPackageName.length() + 1) + "." + fieldName;
305 }
306 }
302307 return className + "." + fieldName;
303308 }
304309
309314
310315 @Override
311316 public boolean equals(Object o) {
312 if (!(o instanceof FieldAnnotation))
317 if (!(o instanceof FieldAnnotation)) {
313318 return false;
319 }
314320 FieldAnnotation other = (FieldAnnotation) o;
315321 return className.equals(other.className) && fieldName.equals(other.fieldName) && fieldSig.equals(other.fieldSig)
316322 && isStatic == other.isStatic;
317323 }
318324
325 @Override
319326 public int compareTo(BugAnnotation o) {
320 if (!(o instanceof FieldAnnotation)) // BugAnnotations must be
321 // Comparable with any type of
322 // BugAnnotation
327 if (!(o instanceof FieldAnnotation)) {
328 // Comparable with any type of
329 // BugAnnotation
323330 return this.getClass().getName().compareTo(o.getClass().getName());
331 }
324332 FieldAnnotation other = (FieldAnnotation) o;
325333 int cmp;
326334 cmp = className.compareTo(other.className);
327 if (cmp != 0)
335 if (cmp != 0) {
328336 return cmp;
337 }
329338 cmp = fieldName.compareTo(other.fieldName);
330 if (cmp != 0)
339 if (cmp != 0) {
331340 return cmp;
341 }
332342 return fieldSig.compareTo(other.fieldSig);
333343 }
334344
335 /*
336 * (non-Javadoc)
337 *
338 * @see edu.umd.cs.findbugs.PackageMemberAnnotation#getSourceLines()
339 */
340345 @Override
341346 public SourceLineAnnotation getSourceLines() {
342347 if (sourceLines == null) {
343348 // Create source line annotation for field on demand
344349 AnalysisContext currentAnalysisContext = AnalysisContext.currentAnalysisContext();
345 if (currentAnalysisContext == null)
350 if (currentAnalysisContext == null) {
346351 sourceLines = new SourceLineAnnotation(className, sourceFileName, -1, -1, -1, -1);
347 else {
352 } else {
348353 SourceInfoMap.SourceLineRange fieldLine = currentAnalysisContext.getSourceInfoMap().getFieldLine(className,
349354 fieldName);
350 if (fieldLine == null)
355 if (fieldLine == null) {
351356 sourceLines = new SourceLineAnnotation(className, sourceFileName, -1, -1, -1, -1);
352 else
357 } else {
353358 sourceLines = new SourceLineAnnotation(className, sourceFileName, fieldLine.getStart(), fieldLine.getEnd(),
354359 -1, -1);
360 }
355361 }
356362 }
357363 return sourceLines;
365371
366372 private static final String ELEMENT_NAME = "Field";
367373
374 @Override
368375 public void writeXML(XMLOutput xmlOutput) throws IOException {
369376 writeXML(xmlOutput, false, false);
370377 }
371378
379 @Override
372380 public void writeXML(XMLOutput xmlOutput, boolean addMessages, boolean isPrimary) throws IOException {
373381 XMLAttributeList attributeList = new XMLAttributeList().addAttribute("classname", getClassName())
374382 .addAttribute("name", getFieldName()).addAttribute("signature", getFieldSignature());
375 if (fieldSourceSig != null)
383 if (fieldSourceSig != null) {
376384 attributeList.addAttribute("sourceSignature", fieldSourceSig);
385 }
377386 attributeList.addAttribute("isStatic", String.valueOf(isStatic()));
378 if (isPrimary)
387 if (isPrimary) {
379388 attributeList.addAttribute("primary", "true");
389 }
380390
381391 String role = getDescription();
382 if (!role.equals(DEFAULT_ROLE))
392 if (!DEFAULT_ROLE.equals(role)) {
383393 attributeList.addAttribute("role", role);
394 }
384395
385396 xmlOutput.openTag(ELEMENT_NAME, attributeList);
386397 getSourceLines().writeXML(xmlOutput, addMessages, false);
392403 xmlOutput.closeTag(ELEMENT_NAME);
393404 }
394405 }
395
396 // vim:ts=4
88 this.field = field;
99 }
1010
11
11
1212 @Override
1313 public String toString() {
1414 return String.format("Supress %s in %s.%s", bugPattern, clazz, field);
1616 @Override
1717 public boolean match(BugInstance bugInstance) {
1818
19 if (!super.match(bugInstance))
19 if (!super.match(bugInstance)) {
2020 return false;
21 }
2122
2223 FieldAnnotation bugField = bugInstance.getPrimaryField();
23 if (bugField == null || !field.equals(bugField))
24 if (bugField == null || !field.equals(bugField)) {
2425 return false;
25 if (DEBUG)
26 }
27 if (DEBUG) {
2628 System.out.println("Suppressing " + bugInstance);
29 }
2730 return true;
2831 }
2932 }
2525 public class FilterBugReporter extends DelegatingBugReporter {
2626 private static final boolean DEBUG = SystemProperties.getBoolean("filter.debug");
2727
28 private Matcher filter;
28 private final Matcher filter;
2929
30 private boolean include;
30 private final boolean include;
3131
3232 public FilterBugReporter(BugReporter realBugReporter, Matcher filter, boolean include) {
3333 super(realBugReporter);
3737
3838 @Override
3939 public void reportBug(@Nonnull BugInstance bugInstance) {
40 if (DEBUG)
40 if (DEBUG) {
4141 System.out.print("Match ==> ");
42 }
4243 boolean match = filter.match(bugInstance);
43 if (DEBUG)
44 if (DEBUG) {
4445 System.out.println(match ? "YES" : "NO");
45 if (include == match)
46 }
47 if (include == match) {
4648 getDelegate().reportBug(bugInstance);
49 }
4750 }
4851 }
4952
50 // vim:ts=4
5757 * Analysis settings for -effort:min.
5858 */
5959 public static final AnalysisFeatureSetting[] MIN_EFFORT = new AnalysisFeatureSetting[] {
60 new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, true),
61 new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, false),
62 new AnalysisFeatureSetting(AnalysisFeatures.MERGE_SIMILAR_WARNINGS, true),
63 new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, false),
64 new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true),
65 new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true),
66 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, false),
67 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, false),
68 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, false),
69 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false), };
60 new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, true),
61 new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, false),
62 new AnalysisFeatureSetting(AnalysisFeatures.MERGE_SIMILAR_WARNINGS, true),
63 new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, false),
64 new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true),
65 new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true),
66 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, false),
67 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, false),
68 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, false),
69 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false), };
7070
7171 /**
7272 * Analysis settings for -effort:less.
7373 */
7474 public static final AnalysisFeatureSetting[] LESS_EFFORT = new AnalysisFeatureSetting[] {
75 new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false),
76 new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true),
77 new AnalysisFeatureSetting(AnalysisFeatures.MERGE_SIMILAR_WARNINGS, true),
78 new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true),
79 new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true),
80 new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true),
81 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, false),
82 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, false),
83 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, false),
84 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false), };
75 new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false),
76 new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true),
77 new AnalysisFeatureSetting(AnalysisFeatures.MERGE_SIMILAR_WARNINGS, true),
78 new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true),
79 new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true),
80 new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true),
81 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, false),
82 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, false),
83 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, false),
84 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false), };
8585
8686 /**
8787 * Analysis settings for -effort:default.
8888 */
8989 public static final AnalysisFeatureSetting[] DEFAULT_EFFORT = new AnalysisFeatureSetting[] {
90 new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false),
91 new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true),
92 new AnalysisFeatureSetting(AnalysisFeatures.MERGE_SIMILAR_WARNINGS, true),
93 new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true),
94 new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true),
95 new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true),
96 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, true),
97 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, true),
98 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, true),
99 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false), };
90 new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false),
91 new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true),
92 new AnalysisFeatureSetting(AnalysisFeatures.MERGE_SIMILAR_WARNINGS, true),
93 new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true),
94 new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true),
95 new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true),
96 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, true),
97 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, true),
98 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, true),
99 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false), };
100100
101101 /**
102102 * Analysis settings for -effort:more.
103103 */
104104 public static final AnalysisFeatureSetting[] MORE_EFFORT = new AnalysisFeatureSetting[] {
105 new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false),
106 new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true),
107 new AnalysisFeatureSetting(AnalysisFeatures.MERGE_SIMILAR_WARNINGS, true),
108 new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true),
109 new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true),
110 new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true),
111 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, true),
112 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, true),
113 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, true),
114 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false), };
105 new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false),
106 new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true),
107 new AnalysisFeatureSetting(AnalysisFeatures.MERGE_SIMILAR_WARNINGS, true),
108 new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true),
109 new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, true),
110 new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true),
111 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, true),
112 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, true),
113 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, true),
114 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, false), };
115115
116116 /**
117117 * Analysis settings for -effort:max.
118118 */
119119 public static final AnalysisFeatureSetting[] MAX_EFFORT = new AnalysisFeatureSetting[] {
120 new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false),
121 new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true),
122 new AnalysisFeatureSetting(AnalysisFeatures.MERGE_SIMILAR_WARNINGS, true),
123 new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true),
124 new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, false),
125 new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true),
126 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, true),
127 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, true),
128 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, true),
129 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, true), };
120 new AnalysisFeatureSetting(AnalysisFeatures.CONSERVE_SPACE, false),
121 new AnalysisFeatureSetting(AnalysisFeatures.ACCURATE_EXCEPTIONS, true),
122 new AnalysisFeatureSetting(AnalysisFeatures.MERGE_SIMILAR_WARNINGS, true),
123 new AnalysisFeatureSetting(AnalysisFeatures.MODEL_INSTANCEOF, true),
124 new AnalysisFeatureSetting(AnalysisFeatures.SKIP_HUGE_METHODS, false),
125 new AnalysisFeatureSetting(AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS, true),
126 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_GUARANTEED_VALUE_DEREFS_IN_NULL_POINTER_ANALYSIS, true),
127 new AnalysisFeatureSetting(AnalysisFeatures.TRACK_VALUE_NUMBERS_IN_NULL_POINTER_ANALYSIS, true),
128 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS, true),
129 new AnalysisFeatureSetting(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES, true), };
130130
131131 /**
132132 * Debug tracing.
213213 if (findBugs.emitTrainingOutput()) {
214214 String trainingOutputDir = findBugs.getTrainingOutputDir();
215215
216 if (!new File(trainingOutputDir).isDirectory())
216 if (!new File(trainingOutputDir).isDirectory()) {
217217 throw new IOException("Training output directory " + trainingOutputDir + " does not exist");
218 }
218219 AnalysisContext.currentAnalysisContext().setDatabaseOutputDir(trainingOutputDir);
219220 // XXX: hack
220221 System.setProperty("findbugs.checkreturn.savetraining", new File(trainingOutputDir, "checkReturn.db").getPath());
222223 if (findBugs.useTrainingInput()) {
223224 String trainingInputDir = findBugs.getTrainingInputDir();
224225
225 if (!new File(trainingInputDir).isDirectory())
226 if (!new File(trainingInputDir).isDirectory()) {
226227 throw new IOException("Training input directory " + trainingInputDir + " does not exist");
228 }
227229 AnalysisContext.currentAnalysisContext().setDatabaseInputDir(trainingInputDir);
228230 AnalysisContext.currentAnalysisContext().loadInterproceduralDatabases();
229231 // XXX: hack
246248 */
247249 public static boolean isDetectorEnabled(IFindBugsEngine findBugs, DetectorFactory factory, int rankThreshold) {
248250
249 if (!findBugs.getUserPreferences().isDetectorEnabled(factory))
251 if (!findBugs.getUserPreferences().isDetectorEnabled(factory)) {
250252 return false;
251
252 if (!factory.isEnabledForCurrentJRE())
253 }
254
255 if (!factory.isEnabledForCurrentJRE()) {
253256 return false;
257 }
254258
255259 // Slow first pass detectors are usually disabled, but may be explicitly
256260 // enabled
257261 if (!AnalysisContext.currentAnalysisContext().getBoolProperty(FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS)
258 && factory.isDetectorClassSubtypeOf(InterproceduralFirstPassDetector.class))
262 && factory.isDetectorClassSubtypeOf(InterproceduralFirstPassDetector.class)) {
259263 return false;
264 }
260265
261266 int maxRank = Integer.MAX_VALUE;
262267 Set<BugPattern> reportedBugPatterns = factory.getReportedBugPatterns();
264269 if (!isNonReportingDetector && !reportedBugPatterns.isEmpty()) {
265270 for (BugPattern b : reportedBugPatterns) {
266271 int rank = BugRanker.findRank(b, factory);
267 if (maxRank > rank)
272 if (maxRank > rank) {
268273 maxRank = rank;
274 }
269275 }
270276 if (maxRank > rankThreshold) {
271277 if (false) {
283289 return isTrainingDetector || isNonReportingDetector;
284290 }
285291
286 if (isTrainingDetector)
292 if (isTrainingDetector) {
287293 return false;
294 }
288295
289296 return true;
290297 }
345352 }
346353
347354 Project project = commandLine.getProject();
348 for (int i = argCount; i < argv.length; ++i)
355 for (int i = argCount; i < argv.length; ++i) {
349356 project.addFile(argv[i]);
357 }
350358 commandLine.handleXArgs();
351359
352360 commandLine.configureEngine(findBugs);
384392 public static void runMain(IFindBugsEngine findBugs, TextUICommandLine commandLine) throws IOException {
385393 boolean verbose = !commandLine.quiet() || commandLine.setExitCode();
386394
387 FutureValue<Collection<UpdateChecker.PluginUpdate>>
395 FutureValue<Collection<UpdateChecker.PluginUpdate>>
388396 updateHolder = null;
389 if (verbose)
397 if (verbose) {
390398 updateHolder = DetectorFactoryCollection.instance().getUpdates();
391 try {
399 }
400 try {
392401 findBugs.execute();
393402 } catch (InterruptedException e) {
394403 assert false; // should not occur
407416 int errorCount = findBugs.getErrorCount();
408417
409418 if (verbose) {
410 if (bugCount > 0)
419 if (bugCount > 0) {
411420 System.err.println("Warnings generated: " + bugCount);
412 if (missingClassCount > 0)
421 }
422 if (missingClassCount > 0) {
413423 System.err.println("Missing classes: " + missingClassCount);
414 if (errorCount > 0)
424 }
425 if (errorCount > 0) {
415426 System.err.println("Analysis errors: " + errorCount);
427 }
416428 if (updateHolder.isDone()) {
417 try {
418 Collection<PluginUpdate> updates = updateHolder.get();
419 if (!DetectorFactoryCollection.instance().getUpdateChecker().updatesHaveBeenSeenBefore(updates))
420 for(UpdateChecker.PluginUpdate u : updates) {
421 System.err.println(u);
422 }
423 } catch (InterruptedException e) {
424 assert true;
425 }
429 try {
430 Collection<PluginUpdate> updates = updateHolder.get();
431 if (!DetectorFactoryCollection.instance().getUpdateChecker().updatesHaveBeenSeenBefore(updates)) {
432 for(UpdateChecker.PluginUpdate u : updates) {
433 System.err.println(u);
434 }
435 }
436 } catch (InterruptedException e) {
437 assert true;
438 }
426439
427440 }
428441 }
481494 */
482495 public static void showSynopsis() {
483496 System.out
484 .println("Usage: findbugs [general options] -textui [command line options...] [jar/zip/class files, directories...]");
497 .println("Usage: findbugs [general options] -textui [command line options...] [jar/zip/class files, directories...]");
485498 }
486499
487500 /**
515528 * @throws org.dom4j.DocumentException
516529 */
517530 public static BugReporter configureBaselineFilter(BugReporter bugReporter, String baselineFileName) throws IOException,
518 DocumentException {
531 DocumentException {
519532 return new ExcludingHashesBugReporter(bugReporter, baselineFileName);
520533 }
521534
553566 /** Date of release of Java 1.0 */
554567 public final static long MINIMUM_TIMESTAMP = new GregorianCalendar(1996, 0, 23).getTime().getTime();
555568
556 /**
557 * @param timestamp
558 * @return
559 */
560569 public static boolean validTimestamp(long timestamp) {
561570 return timestamp > MINIMUM_TIMESTAMP;
562571 }
563572 }
564573
565 // vim:ts=4
4545 import edu.umd.cs.findbugs.ba.SourceInfoMap;
4646 import edu.umd.cs.findbugs.ba.XClass;
4747 import edu.umd.cs.findbugs.ba.XFactory;
48 import edu.umd.cs.findbugs.ba.XField;
4948 import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierAnnotation;
5049 import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierApplications;
5150 import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierValue;
138137
139138 // By default, do not exclude any classes via the class screener
140139 this.classScreener = new IClassScreener() {
141 /*
142 * (non-Javadoc)
143 *
144 * @see edu.umd.cs.findbugs.IClassScreener#matches(java.lang.String)
145 */
140 @Override
146141 public boolean matches(String fileName) {
147142 return true;
148143 }
149144
145 @Override
150146 public boolean vacuous() {
151147 return true;
152148 }
158154 hostApp = "FindBugs TextUI";
159155 hostAppVersion = System.getProperty(PROP_FINDBUGS_HOST_APP_VERSION);
160156 }
161 if (hostAppVersion == null)
157 if (hostAppVersion == null) {
162158 hostAppVersion = "";
159 }
163160 Version.registerApplication(hostApp, hostAppVersion);
164161
165162 // By default, we do not want to scan nested archives
176173 * @param detectorFactoryCollection
177174 * The detectorFactoryCollection to set.
178175 */
176 @Override
179177 public void setDetectorFactoryCollection(DetectorFactoryCollection detectorFactoryCollection) {
180178 this.detectorFactoryCollection = detectorFactoryCollection;
181179 }
188186 * @throws IOException
189187 * @throws InterruptedException
190188 */
189 @Override
191190 public void execute() throws IOException, InterruptedException {
192191
193 if (FindBugs.isNoAnalysis())
192 if (FindBugs.isNoAnalysis()) {
194193 throw new UnsupportedOperationException("This FindBugs invocation was started without analysis capabilities");
194 }
195195
196196 Profiler profiler = bugReporter.getProjectStats().getProfiler();
197197
198198 try {
199 try {
200 // Get the class factory for creating classpath/codebase/etc.
201 classFactory = ClassFactory.instance();
202
203 // The class path object
204 createClassPath();
205
206 progress.reportNumberOfArchives(project.getFileCount() + project.getNumAuxClasspathEntries());
207 profiler.start(this.getClass());
208
209 // The analysis cache object
210 createAnalysisCache();
211
212 // Create BCEL compatibility layer
213 createAnalysisContext(project, appClassList, analysisOptions.sourceInfoFileName);
214
215 // Discover all codebases in classpath and
216 // enumerate all classes (application and non-application)
217 buildClassPath();
218
219
220 // Build set of classes referenced by application classes
221 buildReferencedClassSet();
222
223 // Create BCEL compatibility layer
224 setAppClassList(appClassList);
225
226 // Configure the BugCollection (if we are generating one)
227 FindBugs.configureBugCollection(this);
228
229 // Enable/disabled relaxed reporting mode
230 FindBugsAnalysisFeatures.setRelaxedMode(analysisOptions.relaxedReportingMode);
231 FindBugsDisplayFeatures.setAbridgedMessages(analysisOptions.abridgedMessages);
232
233 // Configure training databases
234 FindBugs.configureTrainingDatabases(this);
235
236 // Configure analysis features
237 configureAnalysisFeatures();
238
239 // Create the execution plan (which passes/detectors to execute)
240 createExecutionPlan();
241
242 for (Plugin p : detectorFactoryCollection.plugins()) {
243 for (ComponentPlugin<BugReporterDecorator> brp
244 : p.getComponentPlugins(BugReporterDecorator.class)) {
245 if (brp.isEnabledByDefault() && !brp.isNamed(explicitlyDisabledBugReporterDecorators)
246 || brp.isNamed(explicitlyEnabledBugReporterDecorators))
247 bugReporter = BugReporterDecorator.construct(brp, bugReporter);
248 }
249 }
250 if (!classScreener.vacuous()) {
251 bugReporter = new DelegatingBugReporter(bugReporter) {
252
253 @Override
254 public void reportBug(@Nonnull BugInstance bugInstance) {
255 String className = bugInstance.getPrimaryClass().getClassName();
256 String resourceName = className.replace('.', '/') + ".class";
257 if (classScreener.matches(resourceName)) {
258 this.getDelegate().reportBug(bugInstance);
199 try {
200 // Get the class factory for creating classpath/codebase/etc.
201 classFactory = ClassFactory.instance();
202
203 // The class path object
204 createClassPath();
205
206 progress.reportNumberOfArchives(project.getFileCount() + project.getNumAuxClasspathEntries());
207 profiler.start(this.getClass());
208
209 // The analysis cache object
210 createAnalysisCache();
211
212 // Create BCEL compatibility layer
213 createAnalysisContext(project, appClassList, analysisOptions.sourceInfoFileName);
214
215 // Discover all codebases in classpath and
216 // enumerate all classes (application and non-application)
217 buildClassPath();
218
219
220 // Build set of classes referenced by application classes
221 buildReferencedClassSet();
222
223 // Create BCEL compatibility layer
224 setAppClassList(appClassList);
225
226 // Configure the BugCollection (if we are generating one)
227 FindBugs.configureBugCollection(this);
228
229 // Enable/disabled relaxed reporting mode
230 FindBugsAnalysisFeatures.setRelaxedMode(analysisOptions.relaxedReportingMode);
231 FindBugsDisplayFeatures.setAbridgedMessages(analysisOptions.abridgedMessages);
232
233 // Configure training databases
234 FindBugs.configureTrainingDatabases(this);
235
236 // Configure analysis features
237 configureAnalysisFeatures();
238
239 // Create the execution plan (which passes/detectors to execute)
240 createExecutionPlan();
241
242 for (Plugin p : detectorFactoryCollection.plugins()) {
243 for (ComponentPlugin<BugReporterDecorator> brp
244 : p.getComponentPlugins(BugReporterDecorator.class)) {
245 if (brp.isEnabledByDefault() && !brp.isNamed(explicitlyDisabledBugReporterDecorators)
246 || brp.isNamed(explicitlyEnabledBugReporterDecorators)) {
247 bugReporter = BugReporterDecorator.construct(brp, bugReporter);
259248 }
260249 }
261 };
262 }
263
264 if (executionPlan.isActive(NoteSuppressedWarnings.class)) {
265 SuppressionMatcher m = AnalysisContext.currentAnalysisContext().getSuppressionMatcher();
266 bugReporter = new FilterBugReporter(bugReporter, m, false);
267 }
268
269 if (appClassList.size() == 0) {
270 Map<String, ICodeBaseEntry> codebase = classPath.getApplicationCodebaseEntries();
271 if (analysisOptions.noClassOk) {
272 System.err.println("No classfiles specified; output will have no warnings");
273 } else if (codebase.isEmpty()) {
274 throw new IOException("No files to analyze could be opened");
275 } else {
276 throw new NoClassesFoundToAnalyzeException(classPath);
277 }
278 }
279
280 // Analyze the application
281 analyzeApplication();
282 } catch (CheckedAnalysisException e) {
283 IOException ioe = new IOException("IOException while scanning codebases");
284 ioe.initCause(e);
285 throw ioe;
286 } catch (OutOfMemoryError e) {
287 System.err.println("Out of memory");
288 System.err.println("Total memory: " + Runtime.getRuntime().maxMemory() / 1000000 + "M");
289 System.err.println(" free memory: " + Runtime.getRuntime().freeMemory() / 1000000 + "M");
290
291 for (String s : project.getFileList()) {
292 System.err.println("Analyzed: " + s);
293 }
294 for (String s : project.getAuxClasspathEntryList()) {
295 System.err.println(" Aux: " + s);
296 }
297 throw e;
298 } finally {
299 clearCaches();
300 profiler.end(this.getClass());
301 profiler.report();
302 }
250 }
251 if (!classScreener.vacuous()) {
252 bugReporter = new DelegatingBugReporter(bugReporter) {
253
254 @Override
255 public void reportBug(@Nonnull BugInstance bugInstance) {
256 String className = bugInstance.getPrimaryClass().getClassName();
257 String resourceName = className.replace('.', '/') + ".class";
258 if (classScreener.matches(resourceName)) {
259 this.getDelegate().reportBug(bugInstance);
260 }
261 }
262 };
263 }
264
265 if (executionPlan.isActive(NoteSuppressedWarnings.class)) {
266 SuppressionMatcher m = AnalysisContext.currentAnalysisContext().getSuppressionMatcher();
267 bugReporter = new FilterBugReporter(bugReporter, m, false);
268 }
269
270 if (appClassList.size() == 0) {
271 Map<String, ICodeBaseEntry> codebase = classPath.getApplicationCodebaseEntries();
272 if (analysisOptions.noClassOk) {
273 System.err.println("No classfiles specified; output will have no warnings");
274 } else if (codebase.isEmpty()) {
275 throw new IOException("No files to analyze could be opened");
276 } else {
277 throw new NoClassesFoundToAnalyzeException(classPath);
278 }
279 }
280
281 // Analyze the application
282 analyzeApplication();
283 } catch (CheckedAnalysisException e) {
284 IOException ioe = new IOException("IOException while scanning codebases");
285 ioe.initCause(e);
286 throw ioe;
287 } catch (OutOfMemoryError e) {
288 System.err.println("Out of memory");
289 System.err.println("Total memory: " + Runtime.getRuntime().maxMemory() / 1000000 + "M");
290 System.err.println(" free memory: " + Runtime.getRuntime().freeMemory() / 1000000 + "M");
291
292 for (String s : project.getFileList()) {
293 System.err.println("Analyzed: " + s);
294 }
295 for (String s : project.getAuxClasspathEntryList()) {
296 System.err.println(" Aux: " + s);
297 }
298 throw e;
299 } finally {
300 clearCaches();
301 profiler.end(this.getClass());
302 profiler.report();
303 }
303304 } catch (IOException e) {
304305 bugReporter.reportQueuedErrors();
305306 throw e;
328329 * needed. (used by Eclipse plugin)
329330 */
330331 public void dispose() {
331 if (executionPlan != null)
332 if (executionPlan != null) {
332333 executionPlan.dispose();
333 if (appClassList != null)
334 }
335 if (appClassList != null) {
334336 appClassList.clear();
335 if (classObserverList != null)
337 }
338 if (classObserverList != null) {
336339 classObserverList.clear();
337 if (referencedClassSet != null)
340 }
341 if (referencedClassSet != null) {
338342 referencedClassSet.clear();
343 }
339344 analysisOptions.analysisFeatureSettingList = null;
340345 bugReporter = null;
341346 classFactory = null;
348353 analysisOptions.userPreferences = null;
349354 }
350355
351 /*
352 * (non-Javadoc)
353 *
354 * @see edu.umd.cs.findbugs.IFindBugsEngine#getBugReporter()
355 */
356 @Override
356357 public BugReporter getBugReporter() {
357358 return bugReporter;
358359 }
359360
360 /*
361 * (non-Javadoc)
362 *
363 * @see edu.umd.cs.findbugs.IFindBugsEngine#getProject()
364 */
361 @Override
365362 public Project getProject() {
366363 return project;
367364 }
368365
369 /*
370 * (non-Javadoc)
371 *
372 * @see
373 * edu.umd.cs.findbugs.IFindBugsEngine#addClassObserver(edu.umd.cs.findbugs
374 * .classfile.IClassObserver)
375 */
366 @Override
376367 public void addClassObserver(IClassObserver classObserver) {
377368 classObserverList.add(classObserver);
378369 }
379370
380 /*
381 * (non-Javadoc)
382 *
383 * @see edu.umd.cs.findbugs.IFindBugsEngine#addFilter(java.lang.String,
384 * boolean)
385 */
371 @Override
386372 public void addFilter(String filterFileName, boolean include) throws IOException, FilterException {
387373 bugReporter = FindBugs.configureFilter(bugReporter, filterFileName, include);
388374 }
389375
390 /*
391 * (non-Javadoc)
392 *
393 * @see
394 * edu.umd.cs.findbugs.IFindBugsEngine#addBaselineBugs(java.lang.String)
395 */
376 @Override
396377 public void excludeBaselineBugs(String baselineBugs) throws IOException, DocumentException {
397378 bugReporter = FindBugs.configureBaselineFilter(bugReporter, baselineBugs);
398379 }
399380
400 /*
401 * (non-Javadoc)
402 *
403 * @see
404 * edu.umd.cs.findbugs.IFindBugsEngine#enableTrainingInput(java.lang.String)
405 */
381 @Override
406382 public void enableTrainingInput(String trainingInputDir) {
407383 this.analysisOptions.trainingInputDir = trainingInputDir;
408384 }
409385
410 /*
411 * (non-Javadoc)
412 *
413 * @see
414 * edu.umd.cs.findbugs.IFindBugsEngine#enableTrainingOutput(java.lang.String
415 * )
416 */
386 @Override
417387 public void enableTrainingOutput(String trainingOutputDir) {
418388 this.analysisOptions.trainingOutputDir = trainingOutputDir;
419389 }
420390
421 /*
422 * (non-Javadoc)
423 *
424 * @see edu.umd.cs.findbugs.IFindBugsEngine#getBugCount()
425 */
391 @Override
426392 public int getBugCount() {
427393 return errorCountingBugReporter.getBugCount();
428394 }
429395
430 /*
431 * (non-Javadoc)
432 *
433 * @see edu.umd.cs.findbugs.IFindBugsEngine#getCurrentClass()
434 */
396 @Override
435397 public String getCurrentClass() {
436398 return currentClassName;
437399 }
438400
439 /*
440 * (non-Javadoc)
441 *
442 * @see edu.umd.cs.findbugs.IFindBugsEngine#getErrorCount()
443 */
401 @Override
444402 public int getErrorCount() {
445403 return errorCountingBugReporter.getErrorCount();
446404 }
447405
448 /*
449 * (non-Javadoc)
450 *
451 * @see edu.umd.cs.findbugs.IFindBugsEngine#getMissingClassCount()
452 */
406 @Override
453407 public int getMissingClassCount() {
454408 return errorCountingBugReporter.getMissingClassCount();
455409 }
456410
457 /*
458 * (non-Javadoc)
459 *
460 * @see edu.umd.cs.findbugs.IFindBugsEngine#getReleaseName()
461 */
462
411 @Override
463412 public String getReleaseName() {
464413 return analysisOptions.releaseName;
465414 }
466415
416 @Override
467417 public String getProjectName() {
468418 return analysisOptions.projectName;
469419 }
470420
421 @Override
471422 public void setProjectName(String name) {
472423 analysisOptions.projectName = name;
473424 }
474425
475 /*
476 * (non-Javadoc)
477 *
478 * @see
479 * edu.umd.cs.findbugs.IFindBugsEngine#setAnalysisFeatureSettings(edu.umd
480 * .cs.findbugs.config.AnalysisFeatureSetting[])
481 */
426 @Override
482427 public void setAnalysisFeatureSettings(AnalysisFeatureSetting[] settingList) {
483428 this.analysisOptions.analysisFeatureSettingList = settingList;
484429 }
485430
486 /*
487 * (non-Javadoc)
488 *
489 * @see
490 * edu.umd.cs.findbugs.IFindBugsEngine#setBugReporter(edu.umd.cs.findbugs
491 * .BugReporter)
492 */
431 @Override
493432 public void setBugReporter(BugReporter bugReporter) {
494433 this.bugReporter = this.errorCountingBugReporter = new ErrorCountingBugReporter(bugReporter);
495434
496435 addClassObserver(bugReporter);
497436 }
498437
499 /*
500 * (non-Javadoc)
501 *
502 * @see
503 * edu.umd.cs.findbugs.IFindBugsEngine#setClassScreener(edu.umd.cs.findbugs
504 * .ClassScreener)
505 */
438 @Override
506439 public void setClassScreener(IClassScreener classScreener) {
507440 this.classScreener = classScreener;
508441 }
509442
510 /*
511 * (non-Javadoc)
512 *
513 * @see
514 * edu.umd.cs.findbugs.IFindBugsEngine#setProgressCallback(edu.umd.cs.findbugs
515 * .FindBugsProgress)
516 */
443 @Override
517444 public void setProgressCallback(FindBugsProgress progressCallback) {
518445 this.progress = progressCallback;
519446 }
520447
521 /*
522 * (non-Javadoc)
523 *
524 * @see
525 * edu.umd.cs.findbugs.IFindBugsEngine#setProject(edu.umd.cs.findbugs.Project
526 * )
527 */
448 @Override
528449 public void setProject(Project project) {
529450 this.project = project;
530451 }
531452
532 /*
533 * (non-Javadoc)
534 *
535 * @see edu.umd.cs.findbugs.IFindBugsEngine#setRelaxedReportingMode(boolean)
536 */
453 @Override
537454 public void setRelaxedReportingMode(boolean relaxedReportingMode) {
538455 this.analysisOptions.relaxedReportingMode = relaxedReportingMode;
539456 }
540457
541 /*
542 * (non-Javadoc)
543 *
544 * @see edu.umd.cs.findbugs.IFindBugsEngine#setReleaseName(java.lang.String)
545 */
458 @Override
546459 public void setReleaseName(String releaseName) {
547460 this.analysisOptions.releaseName = releaseName;
548461 }
549462
550 /*
551 * (non-Javadoc)
552 *
553 * @see
554 * edu.umd.cs.findbugs.IFindBugsEngine#setSourceInfoFile(java.lang.String)
555 */
463 @Override
556464 public void setSourceInfoFile(String sourceInfoFile) {
557465 this.analysisOptions.sourceInfoFileName = sourceInfoFile;
558466 }
559467
468 @Override
560469 public void setUserPreferences(UserPreferences userPreferences) {
561470 this.analysisOptions.userPreferences = userPreferences;
562471 // TODO should set it here too, but gui2 seems to have issues with it
600509 }
601510 }
602511 Set<Entry<String, Boolean>> excludeFilterFiles = userPreferences.getExcludeFilterFiles().entrySet();
603
512
604513 for (Entry<String, Boolean> entry : excludeFilterFiles) {
605514 Boolean value = entry.getValue();
606515 if (value == null || !value) {
618527 }
619528 }
620529 }
621 if (deferredError != null)
530 if (deferredError != null) {
622531 throw deferredError;
623 }
624
625 /*
626 * (non-Javadoc)
627 *
628 * @see edu.umd.cs.findbugs.IFindBugsEngine#emitTrainingOutput()
629 */
532 }
533 }
534
535 @Override
630536 public boolean emitTrainingOutput() {
631537 return analysisOptions.trainingOutputDir != null;
632538 }
633539
634 /*
635 * (non-Javadoc)
636 *
637 * @see edu.umd.cs.findbugs.IFindBugsEngine#getUserPreferences()
638 */
540 @Override
639541 public UserPreferences getUserPreferences() {
640542 return analysisOptions.userPreferences;
641543 }
647549 classPath = classFactory.createClassPath();
648550 }
649551
650 /*
651 * (non-Javadoc)
652 *
653 * @see edu.umd.cs.findbugs.IFindBugsEngine#getTrainingInputDir()
654 */
552 @Override
655553 public String getTrainingInputDir() {
656554 return analysisOptions.trainingInputDir;
657555 }
658556
659 /*
660 * (non-Javadoc)
661 *
662 * @see edu.umd.cs.findbugs.IFindBugsEngine#getTrainingOutputDir()
663 */
557 @Override
664558 public String getTrainingOutputDir() {
665559 return analysisOptions.trainingOutputDir;
666560 }
667561
668 /*
669 * (non-Javadoc)
670 *
671 * @see edu.umd.cs.findbugs.IFindBugsEngine#useTrainingInput()
672 */
562 @Override
673563 public boolean useTrainingInput() {
674564 return analysisOptions.trainingInputDir != null;
675565 }
676566
677 /*
678 * (non-Javadoc)
679 *
680 * @see edu.umd.cs.findbugs.IFindBugsEngine#setScanNestedArchives(boolean)
681 */
567 @Override
682568 public void setScanNestedArchives(boolean scanNestedArchives) {
683569 this.analysisOptions.scanNestedArchives = scanNestedArchives;
684570 }
685571
686 /*
687 * (non-Javadoc)
688 *
689 * @see edu.umd.cs.findbugs.IFindBugsEngine#setNoClassOk(boolean)
690 */
572 @Override
691573 public void setNoClassOk(boolean noClassOk) {
692574 this.analysisOptions.noClassOk = noClassOk;
693575 }
722604 * @param analysisCache
723605 * an IAnalysisCache
724606 */
725 @SuppressWarnings({"UnusedDeclaration"})
726607 public static void registerBuiltInAnalysisEngines(IAnalysisCache analysisCache) {
727608 new edu.umd.cs.findbugs.classfile.engine.EngineRegistrar().registerAnalysisEngines(analysisCache);
728609 new edu.umd.cs.findbugs.classfile.engine.asm.EngineRegistrar().registerAnalysisEngines(analysisCache);
739620 * an IAnalysisCache
740621 * @throws IOException
741622 */
742 @SuppressWarnings({"UnusedDeclaration"})
743623 public static void registerPluginAnalysisEngines(DetectorFactoryCollection detectorFactoryCollection,
744624 IAnalysisCache analysisCache) throws IOException {
745625 for (Iterator<Plugin> i = detectorFactoryCollection.pluginIterator(); i.hasNext();) {
778658 IClassPathBuilder builder = classFactory.createClassPathBuilder(bugReporter);
779659
780660 {
781 HashSet<String> seen = new HashSet<String>();
782 for (String path : project.getFileArray()) {
783 if (seen.add(path))
784 builder.addCodeBase(classFactory.createFilesystemCodeBaseLocator(path), true);
785 }
786 for (String path : project.getAuxClasspathEntryList()) {
787 if (seen.add(path))
788 builder.addCodeBase(classFactory.createFilesystemCodeBaseLocator(path), false);
789 }
661 HashSet<String> seen = new HashSet<String>();
662 for (String path : project.getFileArray()) {
663 if (seen.add(path)) {
664 builder.addCodeBase(classFactory.createFilesystemCodeBaseLocator(path), true);
665 }
666 }
667 for (String path : project.getAuxClasspathEntryList()) {
668 if (seen.add(path)) {
669 builder.addCodeBase(classFactory.createFilesystemCodeBaseLocator(path), false);
670 }
671 }
790672 }
791673
792674 builder.scanNestedArchives(analysisOptions.scanNestedArchives);
818700
819701 }
820702
821 private void buildReferencedClassSet() throws CheckedAnalysisException, InterruptedException {
703 private void buildReferencedClassSet() throws InterruptedException {
822704 // XXX: should drive progress dialog (scanning phase)?
823705
824706 if (PROGRESS) {
840722
841723 // add fields
842724 //noinspection ConstantIfStatement
725 /*
843726 if (false)
844727 for (ClassDescriptor classDesc : appClassList) {
845728 try {
863746 }
864747 }
865748 }
866
749 */
867750 while (!workList.isEmpty()) {
868751 if (Thread.interrupted()) {
869752 throw new InterruptedException();
895778 }
896779
897780 for (ClassDescriptor ifaceDesc : classNameAndInfo.getInterfaceDescriptorList()) {
898 if (addedToWorkList.add(ifaceDesc))
781 if (addedToWorkList.add(ifaceDesc)) {
899782 workList.addLast(ifaceDesc);
783 }
900784 }
901785
902786 ClassDescriptor enclosingClass = classNameAndInfo.getImmediateEnclosingClass();
903 if (enclosingClass != null && addedToWorkList.add(enclosingClass))
787 if (enclosingClass != null && addedToWorkList.add(enclosingClass)) {
904788 workList.addLast(enclosingClass);
789 }
905790
906791 } catch (RuntimeException e) {
907792 bugReporter.logError("Error scanning " + classDesc + " for referenced classes", e);
938823 referencedPackageSet.remove("");
939824 System.out.println("Added " + count + " referenced classes");
940825 System.out.println("Total of " + referencedPackageSet.size() + " packages");
941 for (ClassDescriptor d : referencedClassSet)
826 for (ClassDescriptor d : referencedClassSet) {
942827 System.out.println(" " + d);
943
944 }
945
828 }
829 }
946830 }
947831
948832 public List<ClassDescriptor> sortByCallGraph(Collection<ClassDescriptor> classList, OutEdges<ClassDescriptor> outEdges) {
952836
953837 }
954838
955 @SuppressWarnings({"UnusedDeclaration"})
956839 public static void clearAnalysisContext() {
957840 AnalysisContext.removeCurrentAnalysisContext();
958
959841 }
960842
961843 /**
969851 * @param sourceInfoFileName
970852 * name of source info file (null if none)
971853 */
972 @SuppressWarnings({"UnusedDeclaration"})
973854 public static void createAnalysisContext(Project project, List<ClassDescriptor> appClassList,
974855 @CheckForNull String sourceInfoFileName) throws IOException {
975 AnalysisContext analysisContext = new AnalysisContext();
856 AnalysisContext analysisContext = new AnalysisContext(project);
976857
977858 // Make this the current analysis context
978859 AnalysisContext.setCurrentAnalysisContext(analysisContext);
986867 SourceInfoMap sourceInfoMap = analysisContext.getSourceInfoMap();
987868 sourceInfoMap.read(new FileInputStream(sourceInfoFileName));
988869 }
989 analysisContext.setProject(project);
990870 }
991871
992872 public static void setAppClassList(List<ClassDescriptor> appClassList) {
1020900 DetectorFactoryChooser detectorFactoryChooser = new DetectorFactoryChooser() {
1021901 HashSet<DetectorFactory> forcedEnabled = new HashSet<DetectorFactory>();
1022902
1023 /*
1024 * (non-Javadoc)
1025 *
1026 * @see
1027 * edu.umd.cs.findbugs.DetectorFactoryChooser#choose(edu.umd.cs.
1028 * findbugs.DetectorFactory)
1029 */
903 @Override
1030904 public boolean choose(DetectorFactory factory) {
1031905 boolean result = FindBugs.isDetectorEnabled(FindBugs2.this, factory, rankThreshold) || forcedEnabled.contains(factory);
1032 if (ExecutionPlan.DEBUG)
906 if (ExecutionPlan.DEBUG) {
1033907 System.out.printf(" %6s %s %n", result, factory.getShortName());
908 }
1034909 return result;
1035910 }
1036911
912 @Override
1037913 public void enable(DetectorFactory factory) {
1038914 forcedEnabled.add(factory);
1039915 factory.setEnabledButNonReporting(true);
1042918 };
1043919 executionPlan.setDetectorFactoryChooser(detectorFactoryChooser);
1044920
1045 if (ExecutionPlan.DEBUG)
921 if (ExecutionPlan.DEBUG) {
1046922 System.out.println("rank threshold is " + rankThreshold);
923 }
1047924 // Add plugins
1048925 for (Iterator<Plugin> i = detectorFactoryCollection.pluginIterator(); i.hasNext();) {
1049926 Plugin plugin = i.next();
11231000 AnalysisContext.currentXFactory().canonicalizeAll();
11241001 if (PROGRESS || LIST_ORDER) {
11251002 System.out.printf("%6d : Pass %d: %d classes%n", (System.currentTimeMillis() - startTime)/1000, passCount, classCollection.size());
1126 if (DEBUG)
1003 if (DEBUG) {
11271004 XFactory.profile();
1005 }
11281006 }
11291007 if (!isNonReportingFirstPass) {
11301008 OutEdges<ClassDescriptor> outEdges = new OutEdges<ClassDescriptor>() {
11311009
1010 @Override
11321011 public Collection<ClassDescriptor> getOutEdges(ClassDescriptor e) {
11331012 try {
11341013 XClass classNameAndInfo = Global.getAnalysisCache().getClassAnalysis(XClass.class, e);
11641043 passCount, executionPlan.getNumPasses(), count,
11651044 classCollection.size(), classDescriptor);
11661045 }
1167 count++;
1168 if (!isNonReportingFirstPass && count % 1000 == 0)
1169 yourkitController.advanceGeneration(String.format("Pass %d.%02d", passCount, count/1000));
1170
1046 count++;
1047 if (!isNonReportingFirstPass && count % 1000 == 0) {
1048 yourkitController.advanceGeneration(String.format("Pass %d.%02d", passCount, count/1000));
1049 }
1050
11711051
11721052 // Check to see if class is excluded by the class screener.
11731053 // In general, we do not want to screen classes from the
11821062 boolean isHuge = currentAnalysisContext.isTooBig(classDescriptor);
11831063 if (isHuge && currentAnalysisContext.isApplicationClass(classDescriptor)) {
11841064 bugReporter.reportBug(new BugInstance("SKIPPED_CLASS_TOO_BIG", Priorities.NORMAL_PRIORITY)
1185 .addClass(classDescriptor));
1065 .addClass(classDescriptor));
11861066 }
11871067 currentClassName = ClassName.toDottedClassName(classDescriptor.getClassName());
11881068 notifyClassObservers(classDescriptor);
11901070 currentAnalysisContext.setClassBeingAnalyzed(classDescriptor);
11911071
11921072 try {
1193 for (Detector2 detector : detectorList) {
1194 if (Thread.interrupted()) {
1195 throw new InterruptedException();
1073 for (Detector2 detector : detectorList) {
1074 if (Thread.interrupted()) {
1075 throw new InterruptedException();
1076 }
1077 if (isHuge && !FirstPassDetector.class.isAssignableFrom(detector.getClass())) {
1078 continue;
1079 }
1080 if (DEBUG) {
1081 System.out.println("Applying " + detector.getDetectorClassName() + " to " + classDescriptor);
1082 // System.out.println("foo: " +
1083 // NonReportingDetector.class.isAssignableFrom(detector.getClass())
1084 // + ", bar: " + detector.getClass().getName());
1085 }
1086 try {
1087 profiler.start(detector.getClass());
1088 detector.visitClass(classDescriptor);
1089 } catch (ClassFormatException e) {
1090 logRecoverableException(classDescriptor, detector, e);
1091 } catch (MissingClassException e) {
1092 Global.getAnalysisCache().getErrorLogger().reportMissingClass(e.getClassDescriptor());
1093 } catch (CheckedAnalysisException e) {
1094 logRecoverableException(classDescriptor, detector, e);
1095 } catch (RuntimeException e) {
1096 logRecoverableException(classDescriptor, detector, e);
1097 } finally {
1098 profiler.end(detector.getClass());
1099 }
11961100 }
1197 if (isHuge && !FirstPassDetector.class.isAssignableFrom(detector.getClass())) {
1198 continue;
1199 }
1200 if (DEBUG) {
1201 System.out.println("Applying " + detector.getDetectorClassName() + " to " + classDescriptor);
1202 // System.out.println("foo: " +
1203 // NonReportingDetector.class.isAssignableFrom(detector.getClass())
1204 // + ", bar: " + detector.getClass().getName());
1205 }
1206 try {
1207 profiler.start(detector.getClass());
1208 detector.visitClass(classDescriptor);
1209 } catch (ClassFormatException e) {
1210 logRecoverableException(classDescriptor, detector, e);
1211 } catch (MissingClassException e) {
1212 Global.getAnalysisCache().getErrorLogger().reportMissingClass(e.getClassDescriptor());
1213 } catch (CheckedAnalysisException e) {
1214 logRecoverableException(classDescriptor, detector, e);
1215 } catch (RuntimeException e) {
1216 logRecoverableException(classDescriptor, detector, e);
1217 } finally {
1218 profiler.end(detector.getClass());
1219 }
1220 }
12211101 } finally {
12221102
12231103 progress.finishClass();
12281108 if (usecs > 15000) {
12291109 int classSize = currentAnalysisContext.getClassSize(classDescriptor);
12301110 long speed = usecs /classSize;
1231 if (speed > 15)
1232 System.out.printf(" %6d usecs/byte %6d msec %6d bytes %d pass %s%n", speed, usecs/1000, classSize, passCount,
1233 classDescriptor);
1111 if (speed > 15) {
1112 System.out.printf(" %6d usecs/byte %6d msec %6d bytes %d pass %s%n", speed, usecs/1000, classSize, passCount,
1113 classDescriptor);
1114 }
12341115 }
12351116
12361117 }
12371118 }
12381119 }
12391120
1240 if (!passIterator.hasNext())
1121 if (!passIterator.hasNext()) {
12411122 yourkitController.captureMemorySnapshot();
1123 }
12421124 // Call finishPass on each detector
12431125 for (Detector2 detector : detectorList) {
12441126 detector.finishPass();
12551137 bugReporter.finish();
12561138 bugReporter.reportQueuedErrors();
12571139 profiler.end(this.getClass());
1258 if (PROGRESS)
1140 if (PROGRESS) {
12591141 System.out.println("Analysis completed");
1142 }
12601143 }
12611144
12621145 }
13131196 // Away we go!
13141197
13151198
1316 FindBugs.runMain(findBugs, commandLine);
1317
1318 }
1319
1320
1199 FindBugs.runMain(findBugs, commandLine);
1200
1201 }
1202
1203
1204 @Override
13211205 public void setAbridgedMessages(boolean xmlWithAbridgedMessages) {
13221206 analysisOptions.abridgedMessages = xmlWithAbridgedMessages;
13231207 }
13241208
1209 @Override
13251210 public void setMergeSimilarWarnings(boolean mergeSimilarWarnings) {
13261211 this.analysisOptions.mergeSimilarWarnings = mergeSimilarWarnings;
13271212 }
13281213
1214 @Override
13291215 public void setApplySuppression(boolean applySuppression) {
13301216 this.analysisOptions.applySuppression = applySuppression;
13311217 }
13321218
1219 @Override
13331220 public void setRankThreshold(int rankThreshold) {
13341221 this.rankThreshold = rankThreshold;
13351222 }
13361223
1224 @Override
13371225 public void finishSettings() {
13381226 if (analysisOptions.applySuppression) {
13391227 bugReporter = new FilterBugReporter(bugReporter, getProject().getSuppressionFilter(), false);
13461234 @Nonnull
13471235 Set<String> explicitlyDisabledBugReporterDecorators = Collections.emptySet();
13481236
1237 @Override
13491238 public void setBugReporterDecorators(Set<String> explicitlyEnabled, Set<String> explicitlyDisabled) {
13501239 explicitlyEnabledBugReporterDecorators = explicitlyEnabled;
13511240 explicitlyDisabledBugReporterDecorators = explicitlyDisabled;
2222 METHOD_DANGEROUS_TARGET =Dangerous method call target {0}
2323 METHOD_DANGEROUS_TARGET_ACTUAL_GUARANTEED_NULL =Definite null passed to dangerous method call target {0}
2424 METHOD_EQUALS_USED ={0} used to determine equality
25 METHOD_RETURN_VALUE_OF =Return value of {0}
25 METHOD_RETURN_VALUE_OF =Return value of {0} of type {0.returnType}
2626 METHOD_COMPUTED_IN =Value computed in {0}
2727 METHOD_INHERITED =Inherited method {0}
2828 METHOD_OVERRIDDEN =Overrides {0}
5858 STRING_FORMAT_SPECIFIER =Format specifier "{0}"
5959 INT_OCCURRENCES =Occurs {0} times
6060 INT_VALUE =Value {0}
61 INT_MIN_VALUE =Minimum valid value {0}
62 INT_MAX_VALUE =Maximum valid value {0}
6163 INT_SHIFT =Shifted by {0} bits
6264 INT_SYNC_PERCENT =Synchronized {0}% of the time
6365 INT_BYTECODE_OFFSET =At bytecode offset {0}
9698 SOURCE_LINE_OBLIGATION_CREATED_BY_WILLCLOSE_PARAMETER=Obligation to clean up resource passed via a @WillClose parameter (near {0}) is not discharged
9799 SOURCE_LINE_PATH_CONTINUES=Path continues at {0}
98100 SOURCE_LINE_LOCK_OBTAINED_AT=Lock obtained at {0}
101 SOURCE_UNREACHABLE_CODE=Unreachable code at {0}
99102 LOCAL_VARIABLE_DEFAULT =Local variable {0}
100103 LOCAL_VARIABLE_NAMED =Local variable named {0.name}
101104 LOCAL_VARIABLE_UNKNOWN =Local variable stored in JVM register {0.register}
0 CLASS_DEFAULT=\u30af\u30e9\u30b9 {0}
1 CLASS_EXCEPTION=\u4f8b\u5916\u30af\u30e9\u30b9 {0}
2 CLASS_REFTYPE=\u53c2\u7167\u578b {0}
3 CLASS_SUBCLASS=\u30b5\u30d6\u30af\u30e9\u30b9 {0}
4 INTERFACE_TYPE=\u30a4\u30f3\u30bf\u30fc\u30d5\u30a7\u30fc\u30b9 {0}
5 TYPE_DEFAULT=\u578b
6 TYPE_EXPECTED=\u4e88\u671f\u3055\u308c\u308b\u5024 {0}
7 TYPE_FOUND=\u5b9f\u969b\u306e\u578b {0}
8 TYPE_CLOSEIT=\u30af\u30ed\u30fc\u30ba\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b {0}
9 TYPE_UNHASHABLE=Unhashable class {0}
10 TYPE_ANNOTATION=\u30a2\u30ce\u30c6\u30fc\u30b7\u30e7\u30f3\u30af\u30e9\u30b9 {0}
11 METHOD_DEFAULT=\u30e1\u30bd\u30c3\u30c9 {0}
12 METHOD_CALLED=\u547c\u3073\u51fa\u3055\u308c\u305f\u30e1\u30bd\u30c3\u30c9 {0}
13 SHOULD_CALL=\u4ee3\u308f\u308a\u306b {0} \u3092\u547c\u3073\u51fa\u3059\u3079\u304d
14 METHOD_DANGEROUS_TARGET=\u5371\u967a\u306a\u30e1\u30bd\u30c3\u30c9\u547c\u3073\u51fa\u3057\u5148 {0}
15 METHOD_DANGEROUS_TARGET_ACTUAL_GUARANTEED_NULL=\u660e\u78ba\u306bnull\u304c\u6e21\u3055\u308c\u308b\u5371\u967a\u306a\u30e1\u30bd\u30c3\u30c9\u547c\u3073\u51fa\u3057\u5148 {0}
16 METHOD_EQUALS_USED={0} \u304c\u540c\u5024\u6027\u306e\u5224\u5b9a\u306b\u4f7f\u7528\u3055\u308c\u3066\u3044\u308b
17 METHOD_RETURN_VALUE_OF=\u623b\u308a\u5024 {0}
18 METHOD_INHERITED=\u7d99\u627f\u30e1\u30bd\u30c3\u30c9 {0}
19 METHOD_ALTERNATIVE_TARGET=\u547c\u3073\u51fa\u3057\u305f\u304b\u3063\u305f\u3067\u3042\u308d\u3046\u30e1\u30bd\u30c3\u30c9 {0}
20 METHOD_SAFE_TARGET=\u5b89\u5168\u306a\u30e1\u30bd\u30c3\u30c9\u547c\u3073\u51fa\u3057\u5148 {0}
21 METHOD_DECLARED_NONNULL=\u30e1\u30bd\u30c3\u30c9 {0} \u306f @NonNull \u3092\u5ba3\u8a00\u3057\u3066\u3044\u308b
22 FIELD_DEFAULT=\u30d5\u30a3\u30fc\u30eb\u30c9 {0}
23 FIELD_LOADED_FROM=\u5024\u306e\u8aad\u307f\u8fbc\u307f\u5143 {0}
24 FIELD_CONTAINS_VALUE=\u5024\u306e\u53d6\u5f97\u5143 {0}
25 FIELD_ON=\u30d5\u30a3\u30fc\u30eb\u30c9 {0}
26 FIELD_SUPER=\u30b9\u30fc\u30d1\u30fc\u30af\u30e9\u30b9\u306e\u30d5\u30a3\u30fc\u30eb\u30c9 {0}
27 FIELD_MASKED=\u30de\u30b9\u30af\u3055\u308c\u305f\u30d5\u30a3\u30fc\u30eb\u30c9 {0}
28 FIELD_MASKING=\u30de\u30b9\u30af\u3057\u3066\u3044\u308b\u30d5\u30a3\u30fc\u30eb\u30c9 {0}
29 FIELD_STORED=\u683c\u7d0d\u3055\u308c\u305f\u30d5\u30a3\u30fc\u30eb\u30c9 {0}
30 FIELD_LOADED_FROM=\u8aad\u307f\u8fbc\u307e\u308c\u305f\u30d5\u30a3\u30fc\u30eb\u30c9 {0}
31 FIELD_DID_YOU_MEAN=\u53c2\u7167\u3059\u308b\u306e\u306f\u30d5\u30a3\u30fc\u30eb\u30c9 {0} \u3067\u672c\u5f53\u306b\u3044\u3044\u3067\u3059\u304b?
32 FIELD_INVOKED_ON=\u547c\u3073\u51fa\u3057\u5834\u6240 {0}
33 FIELD_ARGUMENT={0} \u306f\u5f15\u6570\u3068\u3057\u3066\u6e21\u3055\u308c\u3066\u3044\u308b
34 FIELD_VALUE_OF=\u5024\u306e\u8aad\u307f\u8fbc\u307f\u5143 {0}
35 FILE_NAME=\u30d5\u30a1\u30a4\u30eb\u540d {0}
36 FileName=\u30d5\u30a1\u30a4\u30eb\u540d {0}
37 INT_DEFAULT=\u5024 {0}
38 STRING_DEFAULT=\u5024 {0}
39 STRING_CONSTANT=\u6587\u5b57\u5217\u5b9a\u6570 "{0}"
40 STRING_PARAMETER_NAME=\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc "{0}"
41 STRING_TYPE_QUALIFIER=\u578b\u4fee\u98fe\u5b50 {0}
42 STRING_REMAINING_OBLIGATIONS=\u7fa9\u52d9\u304c\u679c\u305f\u3055\u308c\u3066\u3044\u306a\u3044: {0}
43 STRING_FORMAT_STRING=\u66f8\u5f0f\u6587\u5b57\u5217 "{0}"
44 STRING_FORMAT_SPECIFIER=\u66f8\u5f0f\u6307\u793a\u5b50 "{0}"
45 INT_OCCURRENCES=\ {0} \u56de\u767a\u751f
46 INT_VALUE=\u5024 {0}
47 INT_SHIFT={0} \u30d3\u30c3\u30c8\u5206\u30b7\u30d5\u30c8\u3057\u3066\u3044\u308b
48 INT_SYNC_PERCENT={0} % \u306e\u5834\u5408\u540c\u671f\u30a2\u30af\u30bb\u30b9\u3057\u3066\u3044\u308b
49 INT_BYTECODE_OFFSET=At bytecode offset {0}
50 INT_BIASED_LOCKED={0} biased locked accesses
51 INT_BIASED_UNLOCKED={0} biased unlocked accesses
52 INT_NULL_ARG=\u5f15\u6570 {0} \u306f null \u3067\u3042\u3063\u3066\u306f\u3044\u3051\u307e\u305b\u3093 \u304c\u3001\u9593\u9055\u3044\u306a\u304f null \u3067\u3059
53 INT_MAYBE_NULL_ARG=\u5f15\u6570 {0} \u306f null \u3067\u3042\u3063\u3066\u306f\u3044\u3051\u307e\u305b\u3093 \u304c\u3001 null \u306e\u6050\u308c\u304c\u3042\u308a\u307e\u3059
54 INT_NONNULL_PARAM=\u5f15\u6570 {0} \u306f null \u3067\u3042\u3063\u3066\u306f\u3044\u3051\u307e\u305b\u3093
55 INT_EXPECTED_ARGUMENTS=\u4e88\u671f\u3055\u308c\u308b\u5f15\u6570\u306e\u6570 {0}
56 INT_ACTUAL_ARGUMENTS=\u5b9f\u969b\u306e\u5f15\u6570\u306e\u6570 {0}
57 INT_OBLIGATIONS_REMAINING={0} \u500b\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306b\u3064\u3044\u3066\u7fa9\u52d9\u3092\u679c\u305f\u3057\u3066\u3044\u307e\u305b\u3093
58 SOURCE_LINE_DEFAULT=\u8a72\u5f53\u7b87\u6240 {0}
59 SOURCE_LINE_DEFAULT_UNKNOWN_LINE=\u8a72\u5f53\u5834\u6240 {0}
60 SOURCE_LINE_UNSYNC_ACCESS=\u540c\u671f\u3057\u3066\u3044\u306a\u3044\u7b87\u6240 {0}
61 SOURCE_LINE_SYNC_ACCESS=\u540c\u671f\u3057\u3066\u3044\u308b\u7b87\u6240 {0}
62 SOURCE_REDUNDANT_NULL_CHECK=\u5197\u9577\u306a null \u30c1\u30a7\u30c3\u30af\u304c\u884c\u308f\u308c\u3066\u3044\u308b\u5834\u6240 {0}
63 SOURCE_NOTIFICATION_DEADLOCK=\u901a\u77e5\u4e2d\u306b\u30c7\u30c3\u30c9\u30ed\u30c3\u30af\u304c\u8d77\u3053\u308a\u3046\u308b\u5834\u6240 {0}
64 SOURCE_LINE_NULL_VALUE=Null value at {0}
65 SOURCE_LINE_KNOWN_NULL=Known null at {0}
66 SOURCE_LINE_DEREF=Dereferenced at {0}
67 SOURCE_LINE_RETURNED=\u30ea\u30bf\u30fc\u30f3\u7b87\u6240 {0}
68 SOURCE_LINE_INVOKED=\u30e1\u30bd\u30c3\u30c9\u547c\u3073\u51fa\u3057\u7b87\u6240 {0}
69 SOURCE_LINE_STORED=\u683c\u7d0d\u3092\u884c\u3063\u305f\u7b87\u6240 {0}
70 SOURCE_LINE_LAST_CHANGE=\u6700\u5f8c\u306b\u5909\u66f4\u3055\u308c\u305f\u7b87\u6240 {0}
71 SOURCE_LINE_LOOP_BOTTOM=\u30eb\u30fc\u30d7\u306e\u6700\u4e0b\u90e8 {0}
72 SOURCE_LINE_UNKNOWN=At unknown source line
73 SOURCE_LINE_ANOTHER_INSTANCE=\u4ed6\u306e\u51fa\u73fe\u7b87\u6240 {0}
74 SOURCE_LINE_VALUE_SOURCE=\u5024\u306e\u63d0\u4f9b\u7b87\u6240 {0}
75 SOURCE_LINE_VALUE_SINK=Value consumed at {0}
76 SOURCE_LINE_VALUE_DOOMED=Value is doomed at {0}
77 SOURCE_LINE_GENERATED_AT=\u5024\u306e\u751f\u6210\u7b87\u6240 {0}
78 SOURCE_LINE_OBLIGATION_CREATED={0} \u3067\u4f5c\u6210\u3055\u308c\u305f\u30ea\u30bd\u30fc\u30b9\u306e\u30af\u30ea\u30fc\u30f3\u30a2\u30c3\u30d7\u304c\u884c\u308f\u308c\u3066\u3044\u306a\u3044
79 SOURCE_LINE_OBLIGATION_CREATED_BY_WILLCLOSE_PARAMETER={0} \u5468\u8fba\u306b\u304a\u3044\u3066\u3001@WillClose \u30d1\u30e9\u30e1\u30fc\u30bf\u30fc\u3068\u3057\u3066\u6e21\u3055\u308c\u305f\u308a\u30bd\u30fc\u30b9\u306e\u30af\u30ea\u30fc\u30f3\u30a2\u30c3\u30d7\u304c\u884c\u308f\u308c\u3066\u3044\u306a\u3044
80 SOURCE_LINE_PATH_CONTINUES=\u8a72\u5f53\u30d1\u30b9 {0}
81 LOCAL_VARIABLE_DEFAULT=\u30ed\u30fc\u30ab\u30eb\u5909\u6570 {0}
82 LOCAL_VARIABLE_NAMED=\u30ed\u30fc\u30ab\u30eb\u5909\u6570\u540d {0.name}
83 LOCAL_VARIABLE_UNKNOWN=Local variable stored in JVM register {0.register}
84 LOCAL_VARIABLE_PARAMETER=\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc {0}
85 LOCAL_VARIABLE_PARAMETER_NAMED=\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc {0.name}
86 LOCAL_VARIABLE_PARAMETER_VALUE_SOURCE=\u5024\u306e\u8aad\u307f\u8fbc\u307f\u5143\u306e\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc {0}
87 LOCAL_VARIABLE_PARAMETER_VALUE_SOURCE_NAMED=\u5024\u306e\u8aad\u307f\u8fbc\u307f\u5143\u306e\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc {0.name}
88 LOCAL_VARIABLE_VALUE_DOOMED=Doomed value in local variable {0}
89 LOCAL_VARIABLE_VALUE_DOOMED_NAMED=Doomed value in local variable named {0.name}
90 LOCAL_VARIABLE_DID_YOU_MEAN=Did you mean to refer to the local variable {0.name}
91 LOCAL_VARIABLE_INVOKED_ON=Invoked on {0.name}
92 LOCAL_VARIABLE_ARGUMENT={0.name} \u306f\u5f15\u6570\u3068\u3057\u3066\u6e21\u3055\u308c\u3066\u3044\u308b
93 LOCAL_VARIABLE_VALUE_OF=\u5024\u306e\u8aad\u307f\u8fbc\u307f\u5143\u306e\u30d1\u30e9\u30e1\u30fc\u30bf\u30fc {0.name}
0 CLASS_ANNOTATION = {0.simpleName} \u3067\u30a2\u30ce\u30c6\u30fc\u30c8\u3055\u308c\u305f
1 CLASS_DEFAULT = \u30af\u30e9\u30b9 {0}
2 CLASS_EXCEPTION = \u4f8b\u5916\u30af\u30e9\u30b9 {0}
3 CLASS_IMPLEMENTED_INTERFACE = \u5b9f\u88c5\u30a4\u30f3\u30bf\u30fc\u30d5\u30a7\u30fc\u30b9 {0}
4 CLASS_RECOMMENDED_SUPERCLASS = \u304a\u305d\u3089\u304f\u3001\u30af\u30e9\u30b9\u3092\u62e1\u5f35\u3059\u308b\u305f\u3081\u306b\u30ea\u30d5\u30a1\u30af\u30bf\u30ea\u30f3\u30b0\u3059\u308b\u5fc5\u8981\u304c\u3042\u308a\u307e\u3059 {0}
5 CLASS_REFTYPE = \u53c2\u7167\u578b {0}
6 CLASS_SUBCLASS = \u30b5\u30d6\u30af\u30e9\u30b9 {0} \u304c\u3042\u308a\u307e\u3059
7 CLASS_SUPERCLASS = \u30b9\u30fc\u30d1\u30fc\u30af\u30e9\u30b9 {0}
8 CLASS_TYPE_QUALIFIER = \u578b\u4fee\u98fe\u5b50 {0.simpleName}
9 FIELD_ARGUMENT = {0} \u306f\u5f15\u6570\u3068\u3057\u3066\u6e21\u3055\u308c\u3066\u3044\u308b
10 FIELD_CONTAINS_VALUE = {0} \u306b\u542b\u307e\u308c\u308b\u5024
11 FIELD_DEFAULT = \u30d5\u30a3\u30fc\u30eb\u30c9 {0}
12 FIELD_DID_YOU_MEAN = \u30d5\u30a3\u30fc\u30eb\u30c9 {0} \u3092\u53c2\u7167\u3059\u308b\u3064\u3082\u308a\u3060\u3063\u305f\u306e\u3067\u3059\u304b?
13 FIELD_INVOKED_ON = {0} \u3067\u547c\u3073\u51fa\u3055\u308c\u305f
14 FIELD_LOADED_FROM = \u30d5\u30a3\u30fc\u30eb\u30c9 {0} \u304b\u3089\u30ed\u30fc\u30c9\u3055\u308c\u305f
15 FIELD_MASKED = \u30de\u30b9\u30af\u3055\u308c\u305f\u30d5\u30a3\u30fc\u30eb\u30c9 {0}
16 FIELD_MASKING = \u30de\u30b9\u30af\u3057\u3066\u3044\u308b\u30d5\u30a3\u30fc\u30eb\u30c9 {0}
17 FIELD_ON = \u30d5\u30a3\u30fc\u30eb\u30c9 {0}
18 FIELD_STORED = \u30d5\u30a3\u30fc\u30eb\u30c9 {0} \u306b\u683c\u7d0d\u3055\u308c\u305f
19 FIELD_SUPER = \u30b9\u30fc\u30d1\u30fc\u30af\u30e9\u30b9\u306e\u30d5\u30a3\u30fc\u30eb\u30c9 {0}
20 FIELD_VALUE_OF = \u30d5\u30a3\u30fc\u30eb\u30c9 {0} \u304b\u3089\u30ed\u30fc\u30c9\u3055\u308c\u305f\u5024
21 FILE_NAME = \u30d5\u30a1\u30a4\u30eb\u540d {0}
22 FileName = \u30d5\u30a1\u30a4\u30eb\u540d {0}
23 INTERFACE_TYPE = \u30a4\u30f3\u30bf\u30fc\u30d5\u30a7\u30fc\u30b9 {0}
24 INT_ACTUAL_ARGUMENTS = {0} \u500b\u306e\u5f15\u6570\u304c\u898b\u3064\u304b\u308a\u307e\u3057\u305f
25 INT_BIASED_LOCKED = {0} \u500b\u306e\u30d0\u30a4\u30a2\u30b9\u30fb\u30ed\u30c3\u30af\u30fb\u30a2\u30af\u30bb\u30b9
26 INT_BIASED_UNLOCKED = {0} \u500b\u306e\u30d0\u30a4\u30a2\u30b9\u30fb\u30a2\u30f3\u30ed\u30c3\u30af\u30fb\u30a2\u30af\u30bb\u30b9
27 INT_BYTECODE_OFFSET = \u30d0\u30a4\u30c8\u30b3\u30fc\u30c9\u30fb\u30aa\u30d5\u30bb\u30c3\u30c8 {0}
28 INT_DEFAULT = \u5024 {0}
29 INT_EXPECTED_ARGUMENTS = {0} \u500b\u306e\u671f\u5f85\u3055\u308c\u308b\u5f15\u6570
30 INT_MAYBE_NULL_ARG = \u5f15\u6570 {0} \u306f null \u304b\u3082\u3057\u308c\u307e\u305b\u3093\u304c\u3001null \u3067\u3042\u3063\u3066\u306f\u306a\u308a\u307e\u305b\u3093
31 INT_NONNULL_PARAM = \u5f15\u6570 {0} \u306f null \u3067\u3042\u3063\u3066\u306f\u306a\u308a\u307e\u305b\u3093
32 INT_NULL_ARG = \u5f15\u6570 {0} \u306f\u9593\u9055\u3044\u306a\u304f null \u3067\u3059\u304c\u3001null \u3067\u3042\u3063\u3066\u306f\u306a\u308a\u307e\u305b\u3093\u304c\u3001
33 INT_OBLIGATIONS_REMAINING = {0} \u500b\u306e\u30a4\u30f3\u30b9\u30bf\u30f3\u30b9\u306b\u3064\u3044\u3066\u7fa9\u52d9\u3092\u679c\u305f\u3057\u3066\u3044\u307e\u305b\u3093
34 INT_OCCURRENCES = {0} \u56de\u767a\u751f
35 INT_SHIFT = {0} \u30d3\u30c3\u30c8\u30fb\u30b7\u30d5\u30c8
36 INT_SYNC_PERCENT = {0} % \u306e\u78ba\u7387\u3067\u540c\u671f\u5316\u3057\u3066\u3044\u308b
37 INT_VALUE = \u5024 {0}
38 LOCAL_VARIABLE_ARGUMENT = {0.name} \u306f\u5f15\u6570\u3068\u3057\u3066\u6e21\u3055\u308c\u3066\u3044\u308b
39 LOCAL_VARIABLE_DEFAULT = \u30ed\u30fc\u30ab\u30eb\u5909\u6570 {0}
40 LOCAL_VARIABLE_DID_YOU_MEAN = \u30ed\u30fc\u30ab\u30eb\u5909\u6570 {0.name} \u3092\u53c2\u7167\u3059\u308b\u3064\u3082\u308a\u3060\u3063\u305f\u306e\u3067\u3059\u304b?
41 LOCAL_VARIABLE_INVOKED_ON = {0.name} \u3067\u547c\u3073\u51fa\u3055\u308c\u305f
42 LOCAL_VARIABLE_NAMED = \u30ed\u30fc\u30ab\u30eb\u5909\u6570\u540d {0.name}
43 LOCAL_VARIABLE_PARAMETER = \u30d1\u30e9\u30e1\u30fc\u30bf\u30fc {0}
44 LOCAL_VARIABLE_PARAMETER_NAMED = \u30d1\u30e9\u30e1\u30fc\u30bf\u30fc {0.name}
45 LOCAL_VARIABLE_PARAMETER_VALUE_SOURCE = \u30d1\u30e9\u30e1\u30fc\u30bf\u30fc {0} \u304b\u3089\u30ed\u30fc\u30c9\u3055\u308c\u305f\u5024
46 LOCAL_VARIABLE_PARAMETER_VALUE_SOURCE_NAMED = \u30d1\u30e9\u30e1\u30fc\u30bf\u30fc {0.name} \u304b\u3089\u30ed\u30fc\u30c9\u3055\u308c\u305f\u5024
47 LOCAL_VARIABLE_UNKNOWN = JVM \u30ec\u30b8\u30b9\u30bf\u30fc {0.register} \u306b\u683c\u7d0d\u3055\u308c\u305f\u30ed\u30fc\u30ab\u30eb\u5909\u6570
48 LOCAL_VARIABLE_VALUE_DOOMED = \u30ed\u30fc\u30ab\u30eb\u5909\u6570 {0} \u306b\u7834\u7dbb\u3057\u305f\u5024
49 LOCAL_VARIABLE_VALUE_DOOMED_NAMED = \u30ed\u30fc\u30ab\u30eb\u5909\u6570\u540d {0.name} \u306b\u7834\u7dbb\u3057\u305f\u5024
50 LOCAL_VARIABLE_VALUE_OF = {0.name} \u304b\u3089\u30ed\u30fc\u30c9\u3055\u308c\u305f\u5024
51 METHOD_ALTERNATIVE_TARGET = {0} \u306e\u547c\u3073\u51fa\u3057\u3092\u610f\u56f3\u3057\u307e\u3057\u305f\u304b
52 METHOD_CALLED = \u547c\u3073\u51fa\u3055\u308c\u305f\u30e1\u30bd\u30c3\u30c9 {0}
53 METHOD_CALLED_FROM = {0} \u304b\u3089\u547c\u3073\u51fa\u3055\u308c\u305f
54 METHOD_COMPUTED_IN = \u5024\u304c\u8a08\u7b97\u3055\u308c\u305f {0}
55 METHOD_CONSTRUCTOR = \u30b3\u30f3\u30b9\u30c8\u30e9\u30af\u30bf\u30fc {0}
56 METHOD_DANGEROUS_TARGET = \u5371\u967a\u306a\u30e1\u30bd\u30c3\u30c9\u547c\u3073\u51fa\u3057\u5bfe\u8c61 {0}
57 METHOD_DANGEROUS_TARGET_ACTUAL_GUARANTEED_NULL = \u660e\u3089\u304b\u306b null \u304c\u6e21\u3055\u308c\u308b\u5371\u967a\u306a\u30e1\u30bd\u30c3\u30c9\u547c\u3073\u51fa\u3057\u5bfe\u8c61 {0}
58 METHOD_DECLARED_NONNULL = \u30e1\u30bd\u30c3\u30c9 {0} \u306f @NonNull \u3092\u5ba3\u8a00\u3057\u3066\u3044\u308b
59 METHOD_DEFAULT = \u30e1\u30bd\u30c3\u30c9 {0}
60 METHOD_DID_YOU_MEAN_TO_OVERRIDE = \u30aa\u30fc\u30d0\u30fc\u30e9\u30a4\u30c9\u3059\u308b\u3064\u3082\u308a\u3060\u3063\u305f {0}
61 METHOD_EQUALS_USED = {0} \u304c\u7b49\u4fa1\u6027\u3092\u6c7a\u5b9a\u3059\u308b\u305f\u3081\u306b\u4f7f\u308f\u308c\u308b
62 METHOD_INHERITED = \u7d99\u627f\u3055\u308c\u305f\u30e1\u30bd\u30c3\u30c9 {0}
63 METHOD_OVERRIDDEN = \u30aa\u30fc\u30d0\u30fc\u30e9\u30a4\u30c9 {0}
64 METHOD_RETURN_VALUE_OF = \u623b\u308a\u5024 {0}
65 METHOD_SAFE_TARGET = \u5b89\u5168\u306a\u30e1\u30bd\u30c3\u30c9\u547c\u3073\u51fa\u3057\u5bfe\u8c61 {0}
66 METHOD_SUPERCLASS_CONSTRUCTOR = \u30b9\u30fc\u30d1\u30fc\u30af\u30e9\u30b9\u306e\u30b3\u30f3\u30b9\u30c8\u30e9\u30af\u30bf\u30fc {0}
67 SHOULD_CALL = \u4ee3\u308f\u308a\u306b {0} \u3092\u547c\u3073\u51fa\u3059\u3079\u304d
68 SOURCE_LINE_ANOTHER_INSTANCE = \u4ed6\u306e\u51fa\u73fe\u7b87\u6240 {0}
69 SOURCE_LINE_CALLED_FROM_SUPERCLASS_AT = \u30b9\u30fc\u30d1\u30fc\u30af\u30e9\u30b9\u306e\u30b3\u30f3\u30b9\u30c8\u30e9\u30af\u30bf\u30fc\u304b\u3089\u547c\u3073\u51fa\u3057\u305f\u7b87\u6240 {0}
70 SOURCE_LINE_DEFAULT = \u8a72\u5f53\u7b87\u6240 {0}
71 SOURCE_LINE_DEFAULT_UNKNOWN_LINE = \u8a72\u5f53\u5834\u6240 {0}
72 SOURCE_LINE_DEREF = \u53c2\u7167\u5916\u3057\u3092\u3057\u305f\u7b87\u6240 {0}
73 SOURCE_LINE_FIELD_SET_TOO_LATE_AT = \u30d5\u30a3\u30fc\u30eb\u30c9\u3092\u8a2d\u5b9a\u3057\u305f (\u9045\u3059\u304e\u305f) \u7b87\u6240 {0}
74 SOURCE_LINE_GENERATED_AT = \u5024\u306e\u751f\u6210\u7b87\u6240 {0}
75 SOURCE_LINE_INVOKED = \u30e1\u30bd\u30c3\u30c9\u547c\u3073\u51fa\u3057\u7b87\u6240 {0}
76 SOURCE_LINE_KNOWN_NULL = null \u3068\u308f\u304b\u3063\u3066\u3044\u308b\u7b87\u6240 {0}
77 SOURCE_LINE_LAST_CHANGE = \u6700\u5f8c\u306b\u5909\u66f4\u3055\u308c\u305f\u7b87\u6240 {0}
78 SOURCE_LINE_LOCK_OBTAINED_AT = \u30ed\u30c3\u30af\u3092\u7372\u5f97\u3057\u305f\u7b87\u6240 {0}
79 SOURCE_LINE_LOOP_BOTTOM = \u30eb\u30fc\u30d7\u4e0b\u90e8 {0}
80 SOURCE_LINE_NULL_CHECKED = \u30c1\u30a7\u30c3\u30af\u3057\u3066 null \u3067\u3042\u308b\u3053\u3068\u304c\u5224\u660e\u3057\u305f\u7b87\u6240 {0}
81 SOURCE_LINE_NULL_VALUE = null \u5024\u306e\u7b87\u6240 {0}
82 SOURCE_LINE_OBLIGATION_CREATED = {0} \u3067\u4f5c\u6210\u3055\u308c\u305f\u30ea\u30bd\u30fc\u30b9\u306e\u30af\u30ea\u30fc\u30f3\u30a2\u30c3\u30d7\u304c\u884c\u308f\u308c\u3066\u3044\u306a\u3044
83 SOURCE_LINE_OBLIGATION_CREATED_BY_WILLCLOSE_PARAMETER = {0} \u5468\u8fba\u306b\u304a\u3044\u3066\u3001@WillClose \u30d1\u30e9\u30e1\u30fc\u30bf\u30fc\u3068\u3057\u3066\u6e21\u3055\u308c\u305f\u30ea\u30bd\u30fc\u30b9\u306e\u30af\u30ea\u30fc\u30f3\u30a2\u30c3\u30d7\u304c\u884c\u308f\u308c\u3066\u3044\u306a\u3044
84 SOURCE_LINE_PATH_CONTINUES = \u30d1\u30b9\u306e\u7d99\u7d9a\u7b87\u6240 {0}
85 SOURCE_LINE_RETURNED = \u30ea\u30bf\u30fc\u30f3\u7b87\u6240 {0}
86 SOURCE_LINE_STORED = \u683c\u7d0d\u3057\u305f\u7b87\u6240 {0}
87 SOURCE_LINE_SYNC_ACCESS = \u540c\u671f\u30a2\u30af\u30bb\u30b9\u3057\u3066\u3044\u308b\u7b87\u6240 {0}
88 SOURCE_LINE_UNKNOWN = \u4e0d\u660e\u306a\u30bd\u30fc\u30b9\u884c
89 SOURCE_LINE_UNSYNC_ACCESS = \u540c\u671f\u30a2\u30af\u30bb\u30b9\u3057\u3066\u3044\u306a\u3044\u7b87\u6240 {0}
90 SOURCE_LINE_VALUE_DOOMED = \u7834\u7dbb\u3057\u305f\u5024\u306e\u7b87\u6240 {0}
91 SOURCE_LINE_VALUE_SINK = \u6d88\u8cbb\u3055\u308c\u305f\u5024\u306e\u7b87\u6240 {0}
92 SOURCE_LINE_VALUE_SOURCE = \u5024\u306e\u63d0\u4f9b\u7b87\u6240 {0}
93 SOURCE_NOTIFICATION_DEADLOCK = \u901a\u77e5\u4e2d\u306b\u30c7\u30c3\u30c9\u30ed\u30c3\u30af\u304c\u8d77\u3053\u308a\u3046\u308b\u7b87\u6240 {0}
94 SOURCE_REDUNDANT_NULL_CHECK = \u5197\u9577\u306a null \u30c1\u30a7\u30c3\u30af\u304c\u884c\u308f\u308c\u3066\u3044\u308b\u7b87\u6240 {0}
95 STRING_CONSTANT = \u6587\u5b57\u5217\u5b9a\u6570 "{0}"
96 STRING_DEFAULT = \u5024 {0}
97 STRING_ERROR_MSG = \u30a8\u30e9\u30fc\u30fb\u30e1\u30c3\u30bb\u30fc\u30b8: {0}
98 STRING_FORMAT_SPECIFIER = \u66f8\u5f0f\u6307\u793a\u5b50 "{0}"
99 STRING_FORMAT_STRING = \u66f8\u5f0f\u6587\u5b57\u5217 "{0}"
100 STRING_MESSAGE = {0}
101 STRING_NONSTRING_CONSTANT = \u5b9a\u6570\u5024 {0}
102 STRING_PARAMETER_NAME = \u30d1\u30e9\u30e1\u30fc\u30bf\u30fc "{0}"
103 STRING_REGEX = \u6b63\u898f\u8868\u73fe "{0}"
104 STRING_REMAINING_OBLIGATIONS = \u7fa9\u52d9\u304c\u679c\u305f\u3055\u308c\u3066\u3044\u306a\u3044: {0}
105 STRING_TYPE_QUALIFIER = \u578b\u4fee\u98fe\u5b50 {0}
106 TYPE_ANNOTATION = \u30a2\u30ce\u30c6\u30fc\u30b7\u30e7\u30f3\u30fb\u30af\u30e9\u30b9 {0}
107 TYPE_CLOSEIT = {0} \u3092\u30af\u30ed\u30fc\u30ba\u3059\u308b\u5fc5\u8981\u304c\u3042\u308b
108 TYPE_DEFAULT = \u578b {0}
109 TYPE_EXPECTED = \u671f\u5f85\u3055\u308c\u305f {0}
110 TYPE_FOUND = \u5b9f\u969b\u306e\u578b {0}
111 TYPE_UNHASHABLE = \u975e\u30cf\u30c3\u30b7\u30e5\u30fb\u30af\u30e9\u30b9 {0}
102102
103103 @Override
104104 protected void handleOption(String option, String optionExtraPart) {
105 if (option.equals("-effort")) {
106 if (optionExtraPart.equals("min")) {
105 if ("-effort".equals(option)) {
106 if ("min".equals(optionExtraPart)) {
107107 settingList = FindBugs.MIN_EFFORT;
108 } else if (optionExtraPart.equals("less")) {
108 } else if ("less".equals(optionExtraPart)) {
109109 settingList = FindBugs.LESS_EFFORT;
110 } else if (optionExtraPart.equals("default")) {
110 } else if ("default".equals(optionExtraPart)) {
111111 settingList = FindBugs.DEFAULT_EFFORT;
112 } else if (optionExtraPart.equals("more")) {
112 } else if ("more".equals(optionExtraPart)) {
113113 settingList = FindBugs.MORE_EFFORT;
114 } else if (optionExtraPart.equals("max")) {
114 } else if ("max".equals(optionExtraPart)) {
115115 settingList = FindBugs.MAX_EFFORT;
116116 } else {
117117 throw new IllegalArgumentException("-effort:<value> must be one of min,default,more,max");
118118 }
119 } else if (option.equals("-workHard")) {
120 if (settingList != FindBugs.MAX_EFFORT)
119 } else if ("-workHard".equals(option)) {
120 if (settingList != FindBugs.MAX_EFFORT) {
121121 settingList = FindBugs.MORE_EFFORT;
122 }
122123
123 } else if (option.equals("-conserveSpace")) {
124 } else if ("-conserveSpace".equals(option)) {
124125 settingList = FindBugs.MIN_EFFORT;
125 } else if (option.equals("-adjustExperimental")) {
126 } else if ("-adjustExperimental".equals(option)) {
126127 BugInstance.setAdjustExperimental(true);
127128 } else {
128129 throw new IllegalArgumentException("Don't understand option " + option);
131132
132133 @Override
133134 protected void handleOptionWithArgument(String option, String argument) throws IOException {
134 if (option.equals("-home")) {
135 if ("-home".equals(option)) {
135136 FindBugs.setHome(argument);
136 } else if (option.equals("-pluginList")) {
137 } else if ("-pluginList".equals(option)) {
137138 String pluginListStr = argument;
138139 Map<String, Boolean> customPlugins = getProject().getConfiguration().getCustomPlugins();
139140 StringTokenizer tok = new StringTokenizer(pluginListStr, File.pathSeparator);
150151 }
151152 }
152153 }
153 } else if (option.equals("-project")) {
154 } else if ("-project".equals(option)) {
154155 loadProject(argument);
155156 } else {
156157 throw new IllegalStateException();
00 /*
11 * FindBugs - Find Bugs in Java programs
22 * Copyright (C) 2003-2008 University of Maryland
3 *
3 *
44 * This library is free software; you can redistribute it and/or
55 * modify it under the terms of the GNU Lesser General Public
66 * License as published by the Free Software Foundation; either
77 * version 2.1 of the License, or (at your option) any later version.
8 *
8 *
99 * This library is distributed in the hope that it will be useful,
1010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1111 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1212 * Lesser General Public License for more details.
13 *
13 *
1414 * You should have received a copy of the GNU Lesser General Public
1515 * License along with this library; if not, write to the Free Software
1616 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
5555 final boolean analysis;
5656
5757 public void invoke(String[] args) throws Exception {
58 if (!analysis)
58 if (!analysis) {
5959 FindBugs.setNoAnalysis();
60 }
6061 mainMethod.invoke(null, (Object) args);
6162 }
6263
64 @Override
6365 public int compareTo(FindBugsMain that) {
6466 int result = kind.compareTo(that.kind);
65 if (result != 0)
67 if (result != 0) {
6668 return result;
69 }
6770 return cmd.compareTo(that.cmd);
6871
6972 }
7073
7174 @Override
7275 public boolean equals(Object o) {
73 if (!(o instanceof FindBugsMain))
76 if (!(o instanceof FindBugsMain)) {
7477 return false;
78 }
7579 FindBugsMain that = (FindBugsMain) o;
7680 return kind.equals(that.kind) && cmd.equals(that.cmd);
7781 }
2626 * <p/>
2727 * <p>
2828 * Example:
29 *
29 *
3030 * <pre>
3131 * new FindBugsMessageFormat(&quot;BUG: {1} does something bad to field {2.fullField}&quot;)
3232 * </pre>
33 *
33 *
3434 * In this example, the method annotation at position 1 is formatted using the
3535 * empty (default) key. The field annotation at position 2 is formatted using
3636 * the "fullField" key, which uses the long format for the field rather than the
3737 * usual "class.fieldname" format.
38 *
38 *
3939 * @author David Hovemeyer
4040 * @see BugInstance
4141 */
4242 public class FindBugsMessageFormat {
43 private String pattern;
43 private final String pattern;
4444
4545 /**
4646 * Constructor.
47 *
47 *
4848 * @param pattern
4949 * the pattern for the message
5050 */
5959 /**
6060 * Format the message using the given array of BugAnnotations as arguments
6161 * to bind to the placeholders in the pattern string.
62 *
62 *
6363 * @param args
6464 * the BugAnnotations used as arguments
6565 * @param primaryClass
8181 pat = pat.substring(subst + 1);
8282
8383 int end = pat.indexOf('}');
84 if (end < 0)
84 if (end < 0) {
8585 throw new IllegalStateException("unmatched { in " + pat);
86 }
8687
8788 String substPat = pat.substring(0, end);
8889
9192 if (dot >= 0) {
9293 key = substPat.substring(dot + 1);
9394 substPat = substPat.substring(0, dot);
94 } else if (abridgedMessages && primaryClass != null)
95 } else if (abridgedMessages && primaryClass != null) {
9596 key = "givenClass";
97 }
9698
9799 int fieldNum;
98100 try {
120122 // unknown key -- not unprecedented when reading xml
121123 // generated by older versions of findbugs
122124 formatted = "\u00BF" + fieldNum + ".(key=" + key + ")?"; // "\u00BF"
123 // is
124 // inverted
125 // question
126 // mark
125 // is
126 // inverted
127 // question
128 // mark
127129 // System.err.println(iae.getMessage()+" in FindBugsMessageFormat");
128130 // // FIXME: log this error better
129131 }
137139 }
138140 }
139141
140 // vim:ts=4
2323 /**
2424 * A callback that may be installed in a FindBugs instance to asynchronously
2525 * keep track of its progress.
26 *
26 *
2727 * @author David Hovemeyer
2828 * @see FindBugs
2929 */
3131 /**
3232 * Report the total number of archives (Jar or zip files) that will be
3333 * analyzed.
34 *
34 *
3535 * @param numArchives
3636 * the number of archives
3737 */
4141 * Report that FindBugs has started scanning an archive in order to add its
4242 * classes to the repository.
4343 */
44 @Override
4445 public void startArchive(String name);
4546
4647 /**
4748 * Report that FindBugs has finished scanning an archive in order to add its
4849 * classes to the repository.
4950 */
51 @Override
5052 public void finishArchive();
5153
5254 /**
5355 * Provide an array that predicts the number of classes in each pass
54 *
56 *
5557 * @param classesPerPass
5658 */
5759 public void predictPassCount(int[] classesPerPass);
5961 /**
6062 * Report that FindBugs has finished scanning the archives and will start
6163 * analysing the classes contained therein.
62 *
64 *
6365 * @param numClasses
6466 * number of classes found in all of the archives
6567 */
7779 public void finishPerClassAnalysis();
7880 }
7981
80 // vim:ts=4
2020
2121 /**
2222 * A Detector that can be run in the first pass.
23 *
23 *
2424 * A detector can be run in the first pass if either:
2525 * <ul>
2626 * <li>It doesn't report any warnings, or
2727 * <li>It doesn't depend upon information from analyzing any other classes
2828 * </ul>
29 *
29 *
3030 * @author Bill Pugh
3131 */
3232 public interface FirstPassDetector {
2525
2626 /**
2727 * Class to maintain a snapshot of a processes's time and memory usage.
28 *
28 *
2929 * This uses some JDK 1.5 APIs so must be careful that it doesn't cause any harm
3030 * when run from 1.4.
31 *
31 *
3232 * @see FindBugs
3333 * @author Brian Cole
3434 */
8888 } // catch possible Error thrown when complied by the Eclipse compiler
8989
9090 clockTime = System.currentTimeMillis(); // or new
91 // java.util.Date().getTime() ;
91 // java.util.Date().getTime() ;
9292
9393 try {
9494 peakMem = new MemoryBeanWrapper().getPeakUsage();
160160 try {
161161 java.lang.management.MemoryUsage memUsage = mpBean.getPeakUsage();
162162 if (memUsage != null)
163 {
163164 sum += memUsage.getUsed(); // or getCommitted()
164 // System.out.println(mpBean.getType()+", "+mpBean.getName()+", "+memUsage.getUsed());
165 // System.out.println("Memory type="+mpBean.getType()+", Pool name="+mpBean.getName()+", Memory usage="+mpBean.getPeakUsage());
165 // System.out.println(mpBean.getType()+", "+mpBean.getName()+", "+memUsage.getUsed());
166 // System.out.println("Memory type="+mpBean.getType()+", Pool name="+mpBean.getName()+", Memory usage="+mpBean.getPeakUsage());
167 }
166168 } catch (RuntimeException e) {
167169 assert true;
168170 // AnalysisContext.logError("Error getting peak usage", e);
4545 private static final boolean DEBUG = false;
4646
4747 // Don't use hashes for now. Still ironing out issues there.
48 private static final boolean USE_HASHES = false;
49
50 private static final long serialVersionUID = 1L;
48 // private static final boolean USE_HASHES = false;
49
50 // private static final long serialVersionUID = 1L;
5151
5252 /**
5353 * Filter ignored BugAnnotations from given Iterator.
7474 }
7575 }
7676
77 /*
78 * (non-Javadoc)
79 *
80 * @see java.util.Iterator#hasNext()
81 */
77 @Override
8278 public boolean hasNext() {
8379 findNext();
8480 return next != null;
8581 }
8682
87 /*
88 * (non-Javadoc)
89 *
90 * @see java.util.Iterator#next()
91 */
83 @Override
9284 public BugAnnotation next() {
9385 findNext();
94 if (next == null)
86 if (next == null) {
9587 throw new NoSuchElementException();
88 }
9689 BugAnnotation result = next;
9790 next = null;
9891 return result;
9992 }
10093
101 /*
102 * (non-Javadoc)
103 *
104 * @see java.util.Iterator#remove()
105 */
94 @Override
10695 public void remove() {
10796 throw new UnsupportedOperationException();
10897 }
123112 // private Map<ClassHash, String> classHashToCanonicalClassNameMap;
124113
125114 public FuzzyBugComparator() {
126 if (DEBUG)
115 if (DEBUG) {
127116 System.out.println("Created fuzzy comparator");
117 }
128118 this.bugCollectionMap = new IdentityHashMap<BugInstance, BugCollection>();
129119 // this.classHashToCanonicalClassNameMap = new TreeMap<ClassHash,
130120 // String>();
141131 // For now, nothing to do
142132 }
143133
144 /*
145 * (non-Javadoc)
146 *
147 * @see
148 * edu.umd.cs.findbugs.WarningComparator#setClassNameRewriter(edu.umd.cs
149 * .findbugs.model.MovedClassMap)
150 */
134 @Override
151135 public void setClassNameRewriter(ClassNameRewriter classNameRewriter) {
152136 this.classNameRewriter = classNameRewriter;
153137 }
154138
139 @Override
155140 public int compare(BugInstance lhs, BugInstance rhs) {
156141 int cmp;
157142
158 if (DEBUG)
143 if (DEBUG) {
159144 System.out.println("Fuzzy comparison");
145 }
160146
161147 // Bug abbreviations must match.
162148 BugPattern lhsPattern = lhs.getBugPattern();
163149 BugPattern rhsPattern = rhs.getBugPattern();
164150
165 if (lhsPattern == null || rhsPattern == null) {
166 if (DEBUG) {
167 if (lhsPattern == null)
168 System.out.println("Missing pattern: " + lhs.getType());
169 if (rhsPattern == null)
170 System.out.println("Missing pattern: " + rhs.getType());
171 }
172 String lhsCode = getCode(lhs.getType());
173 String rhsCode = getCode(rhs.getType());
174 if ((cmp = lhsCode.compareTo(rhsCode)) != 0)
175 return cmp;
176 } else {
177 if ((cmp = lhsPattern.getAbbrev().compareTo(rhsPattern.getAbbrev())) != 0)
178 return cmp;
151 if ((cmp = lhsPattern.getAbbrev().compareTo(rhsPattern.getAbbrev())) != 0) {
152 return cmp;
179153 }
180154
181155 BugCollection lhsCollection = bugCollectionMap.get(lhs);
190164 BugAnnotation lhsAnnotation = lhsIter.next();
191165 BugAnnotation rhsAnnotation = rhsIter.next();
192166
193 if (DEBUG)
167 if (DEBUG) {
194168 System.out.println("Compare annotations: " + lhsAnnotation + "," + rhsAnnotation);
169 }
195170
196171 // Annotation classes must match exactly
197172 cmp = lhsAnnotation.getClass().getName().compareTo(rhsAnnotation.getClass().getName());
198173 if (cmp != 0) {
199 if (DEBUG)
174 if (DEBUG) {
200175 System.out.println("annotation class mismatch: " + lhsAnnotation.getClass().getName() + ","
201176 + rhsAnnotation.getClass().getName());
177 }
202178 return cmp;
203179 }
204180
205 if (lhsAnnotation.getClass() == ClassAnnotation.class)
181 if (lhsAnnotation.getClass() == ClassAnnotation.class) {
206182 cmp = compareClasses(lhsCollection, rhsCollection, (ClassAnnotation) lhsAnnotation,
207183 (ClassAnnotation) rhsAnnotation);
208 else if (lhsAnnotation.getClass() == MethodAnnotation.class)
184 } else if (lhsAnnotation.getClass() == MethodAnnotation.class) {
209185 cmp = compareMethods(lhsCollection, rhsCollection, (MethodAnnotation) lhsAnnotation,
210186 (MethodAnnotation) rhsAnnotation);
211 else if (lhsAnnotation.getClass() == SourceLineAnnotation.class)
187 } else if (lhsAnnotation.getClass() == SourceLineAnnotation.class) {
212188 cmp = compareSourceLines(lhsCollection, rhsCollection, (SourceLineAnnotation) lhsAnnotation,
213189 (SourceLineAnnotation) rhsAnnotation);
214 else
190 } else {
215191 // everything else just compare directly
216192 cmp = lhsAnnotation.compareTo(rhsAnnotation);
217
218 if (cmp != 0)
193 }
194
195 if (cmp != 0) {
219196 return cmp;
197 }
220198 }
221199
222200 // Number of bug annotations must match
223201 if (!lhsIter.hasNext() && !rhsIter.hasNext()) {
224 if (DEBUG)
202 if (DEBUG) {
225203 System.out.println("Match!");
204 }
226205 return 0;
227 } else
206 } else {
228207 return (lhsIter.hasNext() ? 1 : -1);
229 }
230
231 /**
208 }
209 }
210
211 /*
232212 * @param type
233213 * @return the code of the Bug
234 */
214 *
235215 private String getCode(String type) {
236216 int bar = type.indexOf('_');
237217 if (bar < 0)
238218 return "";
239219 else
240220 return type.substring(0, bar);
241 }
221 }*/
242222
243223 private static int compareNullElements(Object a, Object b) {
244 if (a != null)
224 if (a != null) {
245225 return 1;
246 else if (b != null)
226 } else if (b != null) {
247227 return -1;
248 else
228 } else {
249229 return 0;
230 }
250231 }
251232
252233 public int compareClasses(BugCollection lhsCollection, BugCollection rhsCollection, ClassAnnotation lhsClass,
298279 * For now, just look at the 2 preceeding and succeeding opcodes for fuzzy
299280 * source line matching.
300281 */
301 private static final int NUM_CONTEXT_OPCODES = 2;
282 // private static final int NUM_CONTEXT_OPCODES = 2;
302283
303284 /**
304285 * Compare source line annotations.
321302
322303 // Classes must match fuzzily.
323304 int cmp = compareClassesByName(lhsCollection, rhsCollection, lhs.getClassName(), rhs.getClassName());
324 if (cmp != 0)
305 if (cmp != 0) {
325306 return cmp;
307 }
326308
327309 return 0;
328310 }
341323 significantDescriptionSet.add("METHOD_DEFAULT");
342324 significantDescriptionSet.add(MethodAnnotation.METHOD_CALLED);
343325 significantDescriptionSet.add("METHOD_DANGEROUS_TARGET"); // but do NOT
344 // use safe
345 // targets
326 // use safe
327 // targets
346328 significantDescriptionSet.add("METHOD_DECLARED_NONNULL");
347329 significantDescriptionSet.add("FIELD_DEFAULT");
348330 significantDescriptionSet.add("FIELD_ON");
3333 import org.dom4j.io.DocumentSource;
3434
3535 public class HTMLBugReporter extends BugCollectionBugReporter {
36 private String stylesheet;
36 private final String stylesheet;
3737
3838 private Exception fatalException;
3939
7373 } catch (Exception e) {
7474 logError("Could not generate HTML output", e);
7575 fatalException = e;
76 if (FindBugs.DEBUG)
76 if (FindBugs.DEBUG) {
7777 e.printStackTrace();
78 }
7879 }
7980 outputStream.close();
8081 }
8485 }
8586
8687 private static InputStream getStylesheetStream(String stylesheet) throws IOException {
87 if (FindBugs.DEBUG)
88 if (FindBugs.DEBUG) {
8889 System.out.println("Attempting to load stylesheet " + stylesheet);
90 }
8991 try {
9092 URL u = new URL(stylesheet);
9193 return u.openStream();
105107 }
106108 }
107109
108 // vim:ts=4
8383 public @Nonnull
8484 String getMessage(String key) {
8585 BugPattern bugPattern = DetectorFactoryCollection.instance().lookupBugPattern(key);
86 if (bugPattern == null)
86 if (bugPattern == null) {
8787 return L10N.getLocalString("err.missing_pattern", "Error: missing bug pattern for key") + " " + key;
88 }
8889 return bugPattern.getAbbrev() + ": " + bugPattern.getLongDescription();
8990 }
9091
99100 public @Nonnull
100101 String getShortMessage(String key) {
101102 BugPattern bugPattern = DetectorFactoryCollection.instance().lookupBugPattern(key);
102 if (bugPattern == null)
103 if (bugPattern == null) {
103104 return L10N.getLocalString("err.missing_pattern", "Error: missing bug pattern for key") + " " + key;
105 }
104106 return bugPattern.getAbbrev() + ": " + bugPattern.getShortDescription();
105107 }
106108
107109 public @Nonnull
108110 String getShortMessageWithoutCode(String key) {
109111 BugPattern bugPattern = DetectorFactoryCollection.instance().lookupBugPattern(key);
110 if (bugPattern == null)
112 if (bugPattern == null) {
111113 return L10N.getLocalString("err.missing_pattern", "Error: missing bug pattern for key") + " " + key;
114 }
112115 return bugPattern.getShortDescription();
113116 }
114117
121124 public @Nonnull
122125 String getDetailHTML(String key) {
123126 BugPattern bugPattern = DetectorFactoryCollection.instance().lookupBugPattern(key);
124 if (bugPattern == null)
127 if (bugPattern == null) {
125128 return L10N.getLocalString("err.missing_pattern", "Error: missing bug pattern for key") + " " + key;
129 }
126130 return bugPattern.getDetailHTML();
127131 }
128132
138142 try {
139143 return annotationDescriptionBundle.getString(key);
140144 } catch (MissingResourceException mre) {
141 if (DEBUG)
145 if (DEBUG) {
142146 return "TRANSLATE(" + key + ") (param={0}}";
143 else
147 } else {
144148 try {
145149 return englishAnnotationDescriptionBundle.getString(key);
146150 } catch (MissingResourceException mre2) {
147151 return key + " {0}";
148152 }
153 }
149154 }
150155 }
151156
162167 public @Nonnull
163168 String getBugTypeDescription(String shortBugType) {
164169 BugCode bugCode = DetectorFactoryCollection.instance().lookupBugCode(shortBugType);
165 if (bugCode == null)
170 if (bugCode == null) {
166171 return L10N.getLocalString("err.missing_code", "Error: missing bug code for key") + " " + shortBugType;
172 }
167173 return bugCode.getDescription();
168174 }
169175
218224 */
219225 public List<String> getUserDesignationKeys(boolean sort) {
220226 List<String> result = getUserDesignationKeys();
221 if (sort)
227 if (sort) {
222228 Collections.sort(result, designationKeyComparator);
229 }
223230 return result;
224231 }
225232
235242 * Returns a negative integer, zero, or a positive integer as the left
236243 * key is less than, equal to, or greater than the right key.
237244 */
245 @Override
238246 public int compare(String lKey, String rKey) {
239247 int lCat = categoryOf(lKey);
240248 int catDiff = lCat - categoryOf(rKey);
241 if (catDiff != 0 || lCat != 0)
249 if (catDiff != 0 || lCat != 0) {
242250 return catDiff;
251 }
243252 // if we get this far we have two unrecognized strings
244253 return lKey.compareTo(rKey);
245254 }
246255
247256 private static int categoryOf(String key) {
248 if (key == null)
257 if (key == null) {
249258 return -30;
250 if (key.length() <= 0)
259 }
260 if (key.length() <= 0) {
251261 return -29;
262 }
252263 switch (key.charAt(0)) {
253264 case 'U':
254 if ("UNCLASSIFIED".equals(key))
265 if ("UNCLASSIFIED".equals(key)) {
255266 return 20;
267 }
256268 break;
257269 case 'I':
258 if ("I_WILL_FIX".equals(key))
270 if ("I_WILL_FIX".equals(key)) {
259271 return 12;
272 }
260273 break;
261274
262275 case 'B':
263 if ("BAD_ANALYSIS".equals(key))
276 if ("BAD_ANALYSIS".equals(key)) {
264277 return 15;
278 }
265279 break;
266280 case 'N':
267 if ("NEEDS_STUDY".equals(key))
281 if ("NEEDS_STUDY".equals(key)) {
268282 return -22;
269 if ("NOT_A_BUG".equals(key))
283 }
284 if ("NOT_A_BUG".equals(key)) {
270285 return -15;
286 }
271287 break;
272288 case 'O':
273 if ("OBSOLETE_CODE".equals(key))
289 if ("OBSOLETE_CODE".equals(key)) {
274290 return 30;
291 }
275292 break;
276293 case 'M':
277 if ("MOSTLY_HARMLESS".equals(key))
294 if ("MOSTLY_HARMLESS".equals(key)) {
278295 return -10;
279 if ("MUST_FIX".equals(key))
296 }
297 if ("MUST_FIX".equals(key)) {
280298 return 10;
299 }
281300 break;
282301 case 'S':
283 if ("SHOULD_FIX".equals(key))
302 if ("SHOULD_FIX".equals(key)) {
284303 return 5;
304 }
285305 }
286306 return 0; // between MOSTLY_HARMLESS and SHOULD_FIX
287307 }
292312
293313 }
294314
295 // vim:ts=4
2020
2121 /**
2222 * Screen class files to decide which subset of them to analyze.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public interface IClassScreener {
3232 * Interface for a FindBugs engine class. An instance of this interface takes a
3333 * project, user configuration options, orchestrates the analysis of the classes
3434 * in the project, and reports the results to the configured BugReporter.
35 *
35 *
3636 * @author David Hovemeyer
3737 */
3838 public interface IFindBugsEngine {
3939
4040 /**
4141 * Get the BugReporter.
42 *
42 *
4343 * @return the BugReporter
4444 */
4545 public BugReporter getBugReporter();
4646
4747 /**
4848 * Set the BugReporter.
49 *
49 *
5050 * @param bugReporter
5151 * The BugReporter to set
5252 */
5454
5555 /**
5656 * Set the Project.
57 *
57 *
5858 * @param project
5959 * The Project to set
6060 */
6262
6363 /**
6464 * Get the Project.
65 *
65 *
6666 * @return the Project
6767 */
6868 public Project getProject();
7070 /**
7171 * Set the progress callback that will be used to keep track of the progress
7272 * of the analysis.
73 *
73 *
7474 * @param progressCallback
7575 * the progress callback
7676 */
7878
7979 /**
8080 * Set filter of bug instances to include or exclude.
81 *
81 *
8282 * @param filterFileName
8383 * the name of the filter file
8484 * @param include
8989
9090 /**
9191 * Provide baseline of bugs not to report
92 *
92 *
9393 * @param baselineBugs
9494 * the name of the xml bug baseline file
9595 * @throws DocumentException
100100 * Set the UserPreferences representing which Detectors should be used. If
101101 * UserPreferences are not set explicitly, the default set of Detectors will
102102 * be used.
103 *
103 *
104104 * @param userPreferences
105105 * the UserPreferences
106106 */
108108
109109 /**
110110 * Add an IClassObserver.
111 *
111 *
112112 * @param classObserver
113113 * the IClassObserver
114114 */
117117 /**
118118 * Set the ClassScreener. This object chooses which individual classes to
119119 * analyze. By default, all classes are analyzed.
120 *
120 *
121121 * @param classScreener
122122 * the ClassScreener to use
123123 */
125125
126126 /**
127127 * Set relaxed reporting mode.
128 *
128 *
129129 * @param relaxedReportingMode
130130 * true if relaxed reporting mode should be enabled, false if not
131131 */
133133
134134 /**
135135 * Set whether or not training output should be emitted.
136 *
136 *
137137 * @param trainingOutputDir
138138 * directory to save training output in
139139 */
142142 /**
143143 * Set whether or not training input should be used to make the analysis
144144 * more precise.
145 *
145 *
146146 * @param trainingInputDir
147147 * directory to load training input from
148148 */
150150
151151 /**
152152 * Set analysis feature settings.
153 *
153 *
154154 * @param settingList
155155 * list of analysis feature settings
156156 */
181181 /**
182182 * Set the filename of the source info file containing line numbers for
183183 * fields and classes.
184 *
184 *
185185 * @param sourceInfoFile
186186 * the source info filename
187187 */
190190 /**
191191 * Execute FindBugs on the Project. All bugs found are reported to the
192192 * BugReporter object which was set when this object was constructed.
193 *
193 *
194194 * @throws java.io.IOException
195195 * if an I/O exception occurs analyzing one of the files
196196 * @throws InterruptedException
222222
223223 /**
224224 * Get the UserPreferences.
225 *
225 *
226226 * @return the UserPreferences
227227 */
228228 public UserPreferences getUserPreferences();
230230 /**
231231 * Return whether or not training output should be emitted after analysis
232232 * completes.
233 *
233 *
234234 * @return true if training output should be emitted, false if not
235235 */
236236 public boolean emitTrainingOutput();
237237
238238 /**
239239 * Get the training output directory.
240 *
240 *
241241 * @return the training output directory
242242 */
243243 public String getTrainingOutputDir();
244244
245245 /**
246246 * Return whether or not we should make use of training data.
247 *
247 *
248248 * @return true if training data should be used, false if not
249249 */
250250 public boolean useTrainingInput();
251251
252252 /**
253253 * Get the training input database directory.
254 *
254 *
255255 * @return the training input database directory
256256 */
257257 public String getTrainingInputDir();
258258
259259 /**
260260 * Set whether or not nested archives should be scanned.
261 *
261 *
262262 * @param scanNestedArchives
263263 * true if nested archives should be scanned, false if not
264264 */
267267 /**
268268 * Set whether or not to generate an empty output file if there were no
269269 * class files specified.
270 *
270 *
271271 * @param noClassOk
272272 * true if FindBugs should generate empty output file
273273 */
276276 /**
277277 * Set the DetectorFactoryCollection from which plugins/detectors may be
278278 * accessed.
279 *
279 *
280280 * @param detectorFactoryCollection
281281 * the DetectorFactoryCollection
282282 */
2828
2929 /**
3030 * Interface for any kind of GUI attached to the current FindBug analysis
31 *
31 *
3232 * @author Andrei
3333 */
3434 public interface IGuiCallback {
8282 void showMessageDialogAndWait(String message) throws InterruptedException;
8383
8484 public class FormItem {
85 private String label;
85 private final String label;
8686
87 private String defaultValue;
87 private final String defaultValue;
8888
8989 private boolean password = false;
9090
91 private List<String> possibleValues;
91 private final List<String> possibleValues;
9292
9393 private JComponent field;
94
94
9595 private String currentValue;
96
96
9797 private List<FormItem> items;
9898
9999 public FormItem(String label) {
4343 public boolean isDone();
4444 }
4545
46 // vim:ts=4
3232 * and edge is fed to all scanners so created.
3333 */
3434 public class InstructionScannerDriver {
35 private Iterator<Edge> edgeIter;
35 private final Iterator<Edge> edgeIter;
3636
37 private LinkedList<InstructionScanner> scannerList;
37 private final LinkedList<InstructionScanner> scannerList;
3838
3939 private static final boolean DEBUG = SystemProperties.getBoolean("isd.debug");
4040
4141 /**
4242 * Constructor.
43 *
43 *
4444 * @param edgeIter
4545 * iterator over Edges specifying path to be scanned
4646 */
5353 * Execute by driving the InstructionScannerGenerator over all instructions.
5454 * Each generated InstructionScanner is driven over all instructions and
5555 * edges.
56 *
56 *
5757 * @param generator
5858 * the InstructionScannerGenerator
5959 */
6363 while (edgeIter.hasNext()) {
6464 Edge edge = edgeIter.next();
6565 BasicBlock source = edge.getSource();
66 if (DEBUG)
66 if (DEBUG) {
6767 System.out.println("ISD: scanning instructions in block " + source.getLabel());
68 }
6869
6970 // Traverse all instructions in the source block
7071 Iterator<InstructionHandle> i = source.instructionIterator();
7374 InstructionHandle handle = i.next();
7475
7576 // Check if the generator wants to create a new scanner
76 if (generator.start(handle))
77 if (generator.start(handle)) {
7778 scannerList.add(generator.createScanner());
79 }
7880
7981 // Pump the instruction into all scanners
8082 for (InstructionScanner scanner : scannerList) {
8486 ++count;
8587 }
8688
87 if (DEBUG)
89 if (DEBUG) {
8890 System.out.println("ISD: scanned " + count + " instructions");
91 }
8992
9093 // Now that we've finished the source block, pump the edge
9194 // into all scanners
9699 }
97100 }
98101
99 // vim:ts=4
3737 public InstructionScanner createScanner();
3838 }
3939
40 // vim:ts=4
2727
2828 /**
2929 * Bug annotation class for integer values.
30 *
30 *
3131 * @author David Hovemeyer
3232 * @see BugAnnotation
3333 */
3636
3737 private static final String DEFAULT_ROLE = "INT_DEFAULT";
3838
39 private int value;
39 private final int value;
4040
4141 private String description;
4242
4949
5050 public static final String INT_VALUE = "INT_VALUE";
5151
52 public static final String INT_MIN_VALUE = "INT_MIN_VALUE";
53 public static final String INT_MAX_VALUE = "INT_MAX_VALUE";
54
5255 public static final String INT_SHIFT = "INT_SHIFT";
5356
5457 public static final String INT_EXPECTED_ARGUMENTS = "INT_EXPECTED_ARGUMENTS";
5962
6063 /**
6164 * Constructor.
62 *
65 *
6366 * @param value
6467 * the integer value
6568 */
7982
8083 /**
8184 * Get the integer value.
82 *
85 *
8386 * @return the integer value
8487 */
8588 public int getValue() {
8689 return value;
8790 }
8891
92 @Override
8993 public void accept(BugAnnotationVisitor visitor) {
9094 visitor.visitIntAnnotation(this);
9195 }
9296
97 @Override
9398 public String format(String key, ClassAnnotation primaryClass) {
94 if (key.equals("hash")) {
95 if (isSignificant())
99 if ("hash".equals(key)) {
100 if (isSignificant()) {
96101 return Integer.toString(value);
97 else
102 } else {
98103 return "";
104 }
99105 }
100106 return getShortInteger(value);
101107 }
102
108
103109 public static String getShortInteger(int value) {
104110 String base16 = Integer.toHexString(value);
105111 int unique = uniqueDigits(base16);
106112 String base10 = Integer.toString(value);
107
108 if (unique <= 3 && base16.length() - unique >= 3 && base10.length() > base16.length())
113
114 if (unique <= 3 && base16.length() - unique >= 3 && base10.length() > base16.length()) {
109115 return "0x"+base16;
116 }
110117 return base10;
111118 }
112119 public static String getShortInteger(long value) {
113120 String base16 = Long.toHexString(value);
114121 int unique = uniqueDigits(base16);
115122 String base10 = Long.toString(value);
116
117 if (unique <= 3 && base16.length() - unique >= 3 && base10.length() > base16.length())
123
124 if (unique <= 3 && base16.length() - unique >= 3 && base10.length() > base16.length()) {
118125 return "0x"+base16;
126 }
119127 return base10;
120128 }
121129 private static int uniqueDigits(String value) {
122130 Set<Character> used = new HashSet<Character>();
123 for(int i = 0; i < value.length(); i++)
131 for(int i = 0; i < value.length(); i++) {
124132 used.add(value.charAt(i));
133 }
125134 return used.size();
126135 }
127136
137 @Override
128138 public void setDescription(String description) {
129139 this.description = description;
130140 }
131141
142 @Override
132143 public String getDescription() {
133144 return description;
134145 }
140151
141152 @Override
142153 public boolean equals(Object o) {
143 if (!(o instanceof IntAnnotation))
154 if (!(o instanceof IntAnnotation)) {
144155 return false;
156 }
145157 return value == ((IntAnnotation) o).value;
146158 }
147159
160 @Override
148161 public int compareTo(BugAnnotation o) {
149 if (!(o instanceof IntAnnotation)) // BugAnnotations must be Comparable
150 // with any type of BugAnnotation
162 if (!(o instanceof IntAnnotation)) {
163 // with any type of BugAnnotation
151164 return this.getClass().getName().compareTo(o.getClass().getName());
165 }
152166 return value - ((IntAnnotation) o).value;
153167 }
154168
167181
168182 private static final String ELEMENT_NAME = "Int";
169183
184 @Override
170185 public void writeXML(XMLOutput xmlOutput) throws IOException {
171186 writeXML(xmlOutput, false, false);
172187 }
173188
189 @Override
174190 public void writeXML(XMLOutput xmlOutput, boolean addMessages, boolean isPrimary) throws IOException {
175191 XMLAttributeList attributeList = new XMLAttributeList().addAttribute("value", String.valueOf(value));
176192
177193 String role = getDescription();
178 if (!role.equals(DEFAULT_ROLE))
194 if (!DEFAULT_ROLE.equals(role)) {
179195 attributeList.addAttribute("role", role);
196 }
180197
181198 BugAnnotationUtil.writeXML(xmlOutput, ELEMENT_NAME, this, attributeList, addMessages);
182199 }
183200
201 @Override
184202 public boolean isSignificant() {
185 return !description.equals(INT_SYNC_PERCENT) && !description.equals(INT_OCCURRENCES);
186 }
187
203 return !INT_SYNC_PERCENT.equals(description) && !INT_OCCURRENCES.equals(description);
204 }
205
206 @Override
188207 public String toString(ClassAnnotation primaryClass) {
189208 return toString();
190209 }
191210 }
192211
193 // vim:ts=4
2222 * Detector implementing a slow first pass to collect interprocedural properties
2323 * for a later detector pass. We assign these a special interface because they
2424 * are probably too slow to be enabled by default.
25 *
25 *
2626 * @author David Hovemeyer
2727 */
2828 public interface InterproceduralFirstPassDetector extends NonReportingDetector {
5656
5757 /**
5858 * Constructor.
59 *
59 *
6060 * @param versionString
6161 * a version string, as returned from the
6262 * <code>java.version</code> system property: e.g., "1.4.2_04"
6363 */
6464 public JavaVersion(String versionString) throws JavaVersionException {
6565 Matcher matcher = PATTERN.matcher(versionString);
66 if (!matcher.matches())
66 if (!matcher.matches()) {
6767 throw new JavaVersionException("Could not parse Java version string: " + versionString);
68 }
6869 try {
6970 major = Integer.parseInt(matcher.group(1));
7071 minor = Integer.parseInt(matcher.group(2));
71 if (matcher.group(3) != null)
72 if (matcher.group(3) != null) {
7273 rest = matcher.group(3);
73 else
74 } else {
7475 rest = "";
76 }
7577 } catch (NumberFormatException e) {
7678 throw new JavaVersionException("Could not parse Java Version string: " + versionString, e);
7779 }
7981
8082 /**
8183 * Constructor.
82 *
84 *
8385 * @param major
8486 * major version
8587 * @param minor
135137 /**
136138 * Return whether the Java version represented by this object is at least as
137139 * recent as the one given.
138 *
140 *
139141 * @param other
140142 * another JavaVersion
141143 * @return true if this Java version is at least as recent as the one given
145147 }
146148 }
147149
148 // vim:ts=3
4545 private static PrintWriter extraProperties;
4646 static {
4747 try {
48 if (GENERATE_MISSING_KEYS)
48 if (GENERATE_MISSING_KEYS) {
4949 try {
5050 extraProperties = UserTextFile.printWriter("/tmp/extra.properties");
5151 } catch (IOException e) {
5252 e.printStackTrace();
5353 }
54 }
5455
5556 bundle = ResourceBundle.getBundle("edu.umd.cs.findbugs.gui.bundle.findbugs");
5657 bundle_en = ResourceBundle.getBundle("edu.umd.cs.findbugs.gui.bundle.findbugs", Locale.ENGLISH);
6364 }
6465
6566 private static String lookup(ResourceBundle b, String key) {
66 if (b == null || key == null)
67 if (b == null || key == null) {
6768 throw new MissingResourceException(null, null, null);
69 }
6870
6971 return b.getString(key);
7072 }
7173
7274 public static String getLocalString(String key, String defaultString) {
73 if (key == null)
75 if (key == null) {
7476 return "TRANSLATE(" + defaultString + ")";
77 }
7578 try {
7679 return lookup(bundle, key);
7780 } catch (MissingResourceException mre) {
7881 try {
7982 String en = lookup(bundle_en, key);
80 if (DEBUG)
83 if (DEBUG) {
8184 return "TRANSLATE(" + en + ")";
82 else
85 } else {
8386 return en;
87 }
8488 } catch (MissingResourceException mre2) {
8589 if (extraProperties != null) {
8690 extraProperties.println(key + "=" + defaultString);
8892 }
8993 // String en = "Default("+defaultString+")";
9094 String en = defaultString;
91 if (DEBUG)
95 if (DEBUG) {
9296 return "TRANSLATE(" + en + ")";
93 else
97 } else {
9498 return en;
99 }
95100 }
96101 }
97102 }
163163 String s = System.getProperty("findbugs.launchUI");
164164
165165 if (s == null) {
166 for (String a : args)
167 if (a.equals("-output") || a.equals("-xml") || a.endsWith(".class") || a.endsWith(".jar"))
166 for (String a : args) {
167 if ("-output".equals(a) || "-xml".equals(a) || a.endsWith(".class") || a.endsWith(".jar")) {
168168 return TEXTUI;
169 }
170 }
169171 s = "gui2";
170172 }
171173
4949
5050 /**
5151 * Bug annotation class for local variable names
52 *
52 *
5353 * @author William Pugh
5454 * @see BugAnnotation
5555 */
9292
9393 /**
9494 * Constructor.
95 *
95 *
9696 * @param name
9797 * the name of the local variable
9898 * @param register
107107 this.pc = pc;
108108 this.line = -1;
109109 this.description = DEFAULT_ROLE;
110 this.setDescription(name.equals("?") ? "LOCAL_VARIABLE_UNKNOWN" : "LOCAL_VARIABLE_NAMED");
110 this.setDescription("?".equals(name) ? "LOCAL_VARIABLE_UNKNOWN" : "LOCAL_VARIABLE_NAMED");
111111 }
112112
113113 /**
114114 * Constructor.
115 *
115 *
116116 * @param name
117117 * the name of the local variable
118118 * @param register
127127 this.pc = pc;
128128 this.line = line;
129129 this.description = DEFAULT_ROLE;
130 this.setDescription(name.equals("?") ? "LOCAL_VARIABLE_UNKNOWN" : "LOCAL_VARIABLE_NAMED");
130 this.setDescription("?".equals(name) ? "LOCAL_VARIABLE_UNKNOWN" : "LOCAL_VARIABLE_NAMED");
131131 }
132132
133133 public static LocalVariableAnnotation getLocalVariableAnnotation(Method method, Location location, IndexedInstruction ins) {
148148 lv1 = localVariableTable.getLocalVariable(local, position2);
149149 position1 = position2;
150150 }
151 if (lv1 != null)
151 if (lv1 != null) {
152152 localName = lv1.getName();
153 else
153 } else {
154154 for (LocalVariable lv : localVariableTable.getLocalVariableTable()) {
155155 if (lv.getIndex() == local) {
156 if (!localName.equals("?") && !localName.equals(lv.getName())) {
156 if (!"?".equals(localName) && !localName.equals(lv.getName())) {
157157 // not a single consistent name
158158 localName = "?";
159159 break;
161161 localName = lv.getName();
162162 }
163163 }
164 }
164165 }
165166 LineNumberTable lineNumbers = method.getLineNumberTable();
166 if (lineNumbers == null)
167 if (lineNumbers == null) {
167168 return new LocalVariableAnnotation(localName, local, position1);
169 }
168170 int line = lineNumbers.getSourceLine(position1);
169171 return new LocalVariableAnnotation(localName, local, position1, line);
170172 }
171173
172174 /**
173175 * Get a local variable annotation describing a parameter.
174 *
176 *
175177 * @param method
176178 * a Method
177179 * @param local
180182 */
181183 public static LocalVariableAnnotation getParameterLocalVariableAnnotation(Method method, int local) {
182184 LocalVariableAnnotation lva = getLocalVariableAnnotation(method, local, 0, 0);
183 if (lva.isNamed())
185 if (lva.isNamed()) {
184186 lva.setDescription(LocalVariableAnnotation.PARAMETER_NAMED_ROLE);
185 else
187 } else {
186188 lva.setDescription(LocalVariableAnnotation.PARAMETER_ROLE);
189 }
187190 return lva;
188191 }
189192
196199 }
197200 }
198201
202 @Override
199203 public void accept(BugAnnotationVisitor visitor) {
200204 visitor.visitLocalVariableAnnotation(this);
201205 }
202206
207 @Override
203208 public String format(String key, ClassAnnotation primaryClass) {
204209 // System.out.println("format: " + key + " reg: " + register + " name: "
205210 // + value);
206 if (key.equals("hash")) {
207 if (register < 0)
211 if ("hash".equals(key)) {
212 if (register < 0) {
208213 return "??";
214 }
209215 return name;
210216 }
211 if (register < 0)
217 if (register < 0) {
212218 return "?";
213 if (key.equals("register"))
219 }
220 if ("register".equals(key)) {
214221 return String.valueOf(register);
215 else if (key.equals("pc"))
222 } else if ("pc".equals(key)) {
216223 return String.valueOf(pc);
217 else if (key.equals("name") || key.equals("givenClass"))
224 } else if ("name".equals(key) || "givenClass".equals(key)) {
218225 return name;
219 else if (!name.equals("?"))
226 } else if (!"?".equals(name)) {
220227 return name;
228 }
221229 return "$L" + register;
222230 }
223231
232 @Override
224233 public void setDescription(String description) {
225234 this.description = description.intern();
226235 }
227236
237 @Override
228238 public String getDescription() {
229239 return description;
230240 }
236246
237247 @Override
238248 public boolean equals(Object o) {
239 if (!(o instanceof LocalVariableAnnotation))
249 if (!(o instanceof LocalVariableAnnotation)) {
240250 return false;
251 }
241252 return name.equals(((LocalVariableAnnotation) o).name);
242253 }
243254
255 @Override
244256 public int compareTo(BugAnnotation o) {
245 if (!(o instanceof LocalVariableAnnotation)) // BugAnnotations must be
246 // Comparable with any type
247 // of BugAnnotation
257 if (!(o instanceof LocalVariableAnnotation)) {
258 // Comparable with any type
259 // of BugAnnotation
248260 return this.getClass().getName().compareTo(o.getClass().getName());
261 }
249262 return name.compareTo(((LocalVariableAnnotation) o).name);
250263 }
251264
264277
265278 private static final String ELEMENT_NAME = "LocalVariable";
266279
280 @Override
267281 public void writeXML(XMLOutput xmlOutput) throws IOException {
268282 writeXML(xmlOutput, false, false);
269283 }
270284
285 @Override
271286 public void writeXML(XMLOutput xmlOutput, boolean addMessages, boolean isPrimary) throws IOException {
272287 XMLAttributeList attributeList = new XMLAttributeList().addAttribute("name", name)
273288 .addAttribute("register", String.valueOf(register)).addAttribute("pc", String.valueOf(pc));
274289
275290 String role = getDescription();
276 if (!role.equals(DEFAULT_ROLE))
291 if (!DEFAULT_ROLE.equals(role)) {
277292 attributeList.addAttribute("role", role);
293 }
278294
279295 BugAnnotationUtil.writeXML(xmlOutput, ELEMENT_NAME, this, attributeList, addMessages);
280296 }
281297
282298 public boolean isNamed() {
283 return register >= 0 && !name.equals("?");
299 return register >= 0 && !"?".equals(name);
284300 }
285301
286302 /**
298314 return register;
299315 }
300316
317 @Override
301318 public boolean isSignificant() {
302 return !name.equals("?");
303 }
304
305 /**
306 * @param method
307 * @param item
308 * @param pc2
309 * @return
310 */
319 return !"?".equals(name);
320 }
321
311322 public static @CheckForNull
312323 LocalVariableAnnotation getLocalVariableAnnotation(Method method, Item item, int pc) {
313324 int reg = item.getRegisterNumber();
314 if (reg < 0)
325 if (reg < 0) {
315326 return null;
327 }
316328 return getLocalVariableAnnotation(method, reg, pc, item.getPC());
317329
318330 }
320332 public static @CheckForNull
321333 LocalVariableAnnotation getLocalVariableAnnotation(DismantleBytecode visitor, Item item) {
322334 int reg = item.getRegisterNumber();
323 if (reg < 0)
335 if (reg < 0) {
324336 return null;
337 }
325338 return getLocalVariableAnnotation(visitor.getMethod(), reg, visitor.getPC(), item.getPC());
326339
327340 }
338351 BitSet liveStoreSetAtEntry = llsaDataflow.getAnalysis().getResultFact(cfg.getEntry());
339352 int localsThatAreParameters = PreorderVisitor.getNumberArguments(method.getSignature());
340353 int startIndex = 0;
341 if (!method.isStatic())
354 if (!method.isStatic()) {
342355 startIndex = 1;
356 }
343357 SignatureParser parser = new SignatureParser(method.getSignature());
344358 Iterator<String> signatureIterator = parser.parameterSignatureIterator();
345359 for (int i = startIndex; i < localsThatAreParameters + startIndex; i++) {
348362 // parameter isn't live and signatures match
349363 LocalVariableAnnotation potentialMatch = LocalVariableAnnotation.getLocalVariableAnnotation(method, i, 0, 0);
350364 potentialMatch.setDescription(DID_YOU_MEAN_ROLE);
351 if (!potentialMatch.isNamed())
365 if (!potentialMatch.isNamed()) {
352366 return potentialMatch;
367 }
353368 int distance = EditDistance.editDistance(name, potentialMatch.getName());
354369 if (distance < lowestCost) {
355370 match = potentialMatch;
377392 LocalVariableAnnotation match = null;
378393 int localsThatAreParameters = PreorderVisitor.getNumberArguments(method.getSignature());
379394 int startIndex = 0;
380 if (!method.isStatic())
395 if (!method.isStatic()) {
381396 startIndex = 1;
397 }
382398 SignatureParser parser = new SignatureParser(method.getSignature());
383399 Iterator<String> signatureIterator = parser.parameterSignatureIterator();
384400 int lowestCost = Integer.MAX_VALUE;
386402 String sig = signatureIterator.next();
387403 if (signature.equals(sig)) {
388404 LocalVariableAnnotation potentialMatch = LocalVariableAnnotation.getLocalVariableAnnotation(method, i, 0, 0);
389 if (!potentialMatch.isNamed())
405 if (!potentialMatch.isNamed()) {
390406 continue;
407 }
391408 int distance = EditDistance.editDistance(name, potentialMatch.getName());
392409 if (distance < lowestCost) {
393410 match = potentialMatch;
400417 // signatures match
401418 }
402419 }
403 if (lowestCost < 5)
420 if (lowestCost < 5) {
404421 return match;
422 }
405423 return null;
406424 }
407425
426 @Override
408427 public String toString(ClassAnnotation primaryClass) {
409428 return toString();
410429 }
411430 }
412431
413 // vim:ts=4
2424 import org.apache.bcel.classfile.JavaClass;
2525 import org.apache.bcel.classfile.Method;
2626
27 import edu.umd.cs.findbugs.ba.AnalysisContext;
2827 import edu.umd.cs.findbugs.ba.XClass;
2928 import edu.umd.cs.findbugs.ba.XFactory;
3029 import edu.umd.cs.findbugs.ba.XMethod;
31 import edu.umd.cs.findbugs.ba.ch.Subtypes2;
3230 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
3331 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
3432 import edu.umd.cs.findbugs.classfile.DescriptorFactory;
4038
4139 public class Lookup implements Constants2 {
4240
41 /*
4342 private static Subtypes2 subtypes2() {
4443 return AnalysisContext.currentAnalysisContext().getSubtypes2();
4544 }
45 */
4646
4747 public static XClass getXClass(ClassDescriptor c) throws CheckedAnalysisException {
4848 return Global.getAnalysisCache().getClassAnalysis(XClass.class, c);
8585 throws CheckedAnalysisException {
8686
8787 ClassDescriptor superclassDescriptor = clazz.getSuperclassDescriptor();
88 if (superclassDescriptor == null)
88 if (superclassDescriptor == null) {
8989 return clazz;
90 }
9091 return findImplementor(getXClass(superclassDescriptor), name, signature, isStatic);
9192 }
9293
9394 public static XClass findImplementor(XClass clazz, String name, String signature, boolean isStatic)
9495 throws CheckedAnalysisException {
9596 XMethod m = clazz.findMethod(name, signature, isStatic);
96 if (m != null)
97 if (m != null) {
9798 return clazz;
99 }
98100 return findSuperImplementor(clazz, name, signature, isStatic);
99101 }
100102
104106 JavaClass c = clazz;
105107 while (true) {
106108 c = c.getSuperClass();
107 if (c == null)
109 if (c == null) {
108110 return null;
111 }
109112 Method m = findImplementation(c, name, signature);
110113 if (m != null) {
111114 return c;
123126 JavaClass c = clazz;
124127 while (true) {
125128 c = c.getSuperClass();
126 if (c == null)
129 if (c == null) {
127130 return null;
131 }
128132 Method m = findImplementation(c, name, signature);
129 if (m != null && !m.isAbstract())
133 if (m != null && !m.isAbstract()) {
130134 return c;
135 }
131136
132137 }
133138 } catch (ClassNotFoundException e) {
142147 JavaClass c = clazz;
143148 while (true) {
144149 c = c.getSuperClass();
145 if (c == null)
150 if (c == null) {
146151 return null;
152 }
147153 Method m = findImplementation(c, name, signature);
148 if (m != null && !m.isAbstract())
154 if (m != null && !m.isAbstract()) {
149155 return XFactory.createXMethod(c, m);
156 }
150157
151158 }
152159 } catch (ClassNotFoundException e) {
171178
172179 for (JavaClass aClazz : clazz) {
173180 Method m = findImplementation(aClazz, name, signature);
174 if (m != null && !m.isAbstract())
181 if (m != null && !m.isAbstract()) {
175182 return aClazz;
183 }
176184
177185 }
178186 return null;
180188
181189 public static Method findImplementation(JavaClass clazz, String name, String signature) {
182190 Method[] m = clazz.getMethods();
183 for (Method aM : m)
184 if (aM.getName().equals(name) && aM.getSignature().equals(signature) && !aM.isPrivate() && !aM.isStatic())
191 for (Method aM : m) {
192 if (aM.getName().equals(name) && aM.getSignature().equals(signature) && !aM.isPrivate() && !aM.isStatic()) {
185193 return aM;
194 }
195 }
186196 return null;
187197 }
188198 }
8484 public static final String METHOD_COMPUTED_IN = "METHOD_COMPUTED_IN";
8585
8686 public static final String METHOD_ALTERNATIVE_TARGET = "METHOD_ALTERNATIVE_TARGET";
87
87
8888 public static final String SHOULD_CALL = "SHOULD_CALL";
89
89
9090 /**
9191 * Constructor.
9292 *
102102 public MethodAnnotation(@DottedClassName String className, String methodName, String methodSig, boolean isStatic) {
103103 super(className, DEFAULT_ROLE);
104104 this.methodName = methodName;
105 if (methodSig.indexOf(".") >= 0) {
105 if (methodSig.indexOf('.') >= 0) {
106106 assert false : "signatures should not be dotted: " + methodSig;
107 methodSig = methodSig.replace('.', '/');
107 methodSig = methodSig.replace('.', '/');
108108 }
109109 this.methodSig = methodSig;
110110 this.isStatic = isStatic;
150150 if (!oVisitor.getStack().isTop() && oVisitor.getStack().getStackDepth() > params) {
151151 OpcodeStack.Item item = oVisitor.getStack().getStackItem(params);
152152 String cName = ClassName.fromFieldSignature(item.getSignature());
153 if (cName != null)
153 if (cName != null) {
154154 className = cName;
155 }
155156
156157 }
157158
281282
282283
283284 public String getJavaSourceMethodName() {
284 if (methodName.equals("<clinit>"))
285 if ("<clinit>".equals(methodName)) {
285286 return "<static initializer for " + getSimpleClassName() + ">";
286
287 if (methodName.equals("<init>")) {
287 }
288
289 if ("<init>".equals(methodName)) {
288290 return getSimpleClassName();
289291 }
290292 return methodName;
319321 return DescriptorFactory.instance().getMethodDescriptor(this);
320322 }
321323
324 @Override
322325 public void accept(BugAnnotationVisitor visitor) {
323326 visitor.visitMethodAnnotation(this);
324327 }
325328
326329 @Override
327330 protected String formatPackageMember(String key, ClassAnnotation primaryClass) {
328 if (key.equals(""))
331 if ("".equals(key)) {
329332 return UGLY_METHODS ? getUglyMethod() : getFullMethod(primaryClass);
330 else if (key.equals("givenClass")) {
331 if (methodName.equals("<init>")) {
333 } else if ("givenClass".equals(key)) {
334 if ("<init>".equals(methodName)) {
332335 return "new " + shorten(primaryClass.getPackageName(), className) + getSignatureInClass(primaryClass);
333336 }
334 if (className.equals(primaryClass.getClassName()))
337 if (className.equals(primaryClass.getClassName())) {
335338 return getNameInClass(primaryClass);
336 else
339 } else {
337340 return shorten(primaryClass.getPackageName(), className) + "." + getNameInClass(primaryClass);
338 } else if (key.equals("name")) {
341 }
342 } else if ("name".equals(key)) {
339343 return methodName;
340 } else if (key.equals("nameAndSignature")) {
344 } else if ("nameAndSignature".equals(key)) {
341345 return getNameInClass(primaryClass);
342 } else if (key.equals("shortMethod"))
346 } else if ("shortMethod".equals(key)) {
343347 return className + "." + methodName + "(...)";
344 else if (key.equals("hash")) {
348 } else if ("hash".equals(key)) {
345349 String tmp = getNameInClass(false, true, true);
346350
347351 return className + "." + tmp;
348 } else if (key.equals("returnType")) {
352 } else if ("returnType".equals(key)) {
349353 int i = methodSig.indexOf(')');
350354 String returnType = methodSig.substring(i + 1);
351355 String pkgName = primaryClass == null ? "" : primaryClass.getPackageName();
352356 SignatureConverter converter = new SignatureConverter(returnType);
353357 return shorten(pkgName, converter.parseNext());
354 } else
358 } else {
355359 throw new IllegalArgumentException("unknown key " + key);
360 }
356361 }
357362
358363 /**
359364 * Get the "full" method name. This is a format which looks sort of like a
360365 * method signature that would appear in Java source code.
361 *
362 * @param primaryClass
363 * TODO
364366 */
365367 public String getNameInClass(ClassAnnotation primaryClass) {
366368 return getNameInClass(true, false, false, false);
385387 * @param shortenPackages
386388 * whether to shorten package names if they are in java or in the
387389 * same package as this method.
388 * @param useJVMMethodName
389 * TODO
390 * @param hash
391 * TODO
392390 */
393391 public String getNameInClass(boolean shortenPackages, boolean useJVMMethodName, boolean hash, boolean omitMethodName) {
394392 // Convert to "nice" representation
395393 StringBuilder result = new StringBuilder();
396394 if (!omitMethodName) {
397 if (useJVMMethodName)
395 if (useJVMMethodName) {
398396 result.append(getMethodName());
399 else
397 } else {
400398 result.append(getJavaSourceMethodName());
399 }
401400 }
402401 result.append('(');
403402
404403 // append args
405404 SignatureConverter converter = new SignatureConverter(methodSig);
406405
407 if (converter.getFirst() != '(')
406 if (converter.getFirst() != '(') {
408407 throw new IllegalStateException("bad method signature " + methodSig);
408 }
409409 converter.skip();
410410
411411 boolean needsComma = false;
412412 while (converter.getFirst() != ')') {
413 if (needsComma)
414 if (hash)
413 if (needsComma) {
414 if (hash) {
415415 result.append(",");
416 else
416 } else {
417417 result.append(", ");
418 if (shortenPackages)
418 }
419 }
420 if (shortenPackages) {
419421 result.append(removePackageName(converter.parseNext()));
420 else
422 } else {
421423 result.append(converter.parseNext());
424 }
422425 needsComma = true;
423426 }
424427 converter.skip();
430433 /**
431434 * Get the "full" method name. This is a format which looks sort of like a
432435 * method signature that would appear in Java source code.
433 *
434 * @param primaryClass
435 * TODO
436436 */
437437 public String getFullMethod(ClassAnnotation primaryClass) {
438438 if (fullMethod == null) {
439 if (methodName.equals("<init>"))
439 if ("<init>".equals(methodName)) {
440440 fullMethod = "new " + stripJavaLang(className) + getSignatureInClass(primaryClass);
441 else
441 } else {
442442 fullMethod = stripJavaLang(className) + "." + getNameInClass(primaryClass);
443 }
443444 }
444445
445446 return fullMethod;
446447 }
447448
448449 public String stripJavaLang(@DottedClassName String className) {
449 if (className.startsWith("java.lang."))
450 if (className.startsWith("java.lang.")) {
450451 return className.substring(10);
452 }
451453 return className;
452454 }
453455
462464
463465 @Override
464466 public boolean equals(Object o) {
465 if (!(o instanceof MethodAnnotation))
467 if (!(o instanceof MethodAnnotation)) {
466468 return false;
469 }
467470 MethodAnnotation other = (MethodAnnotation) o;
468471 return className.equals(other.className) && methodName.equals(other.methodName) && methodSig.equals(other.methodSig);
469472 }
470473
474 @Override
471475 public int compareTo(BugAnnotation o) {
472 if (!(o instanceof MethodAnnotation)) // BugAnnotations must be
473 // Comparable with any type of
474 // BugAnnotation
476 if (!(o instanceof MethodAnnotation)) {
477 // Comparable with any type of
478 // BugAnnotation
475479 return this.getClass().getName().compareTo(o.getClass().getName());
480 }
476481 MethodAnnotation other = (MethodAnnotation) o;
477482 int cmp;
478483 cmp = className.compareTo(other.className);
479 if (cmp != 0)
484 if (cmp != 0) {
480485 return cmp;
486 }
481487 cmp = methodName.compareTo(other.methodName);
482 if (cmp != 0)
488 if (cmp != 0) {
483489 return cmp;
490 }
484491 return methodSig.compareTo(other.methodSig);
485492 }
486493
492499
493500 private static final String ELEMENT_NAME = "Method";
494501
502 @Override
495503 public void writeXML(XMLOutput xmlOutput) throws IOException {
496504 }
497505
506 @Override
498507 public void writeXML(XMLOutput xmlOutput, boolean addMessages, boolean isPrimary) throws IOException {
499508 XMLAttributeList attributeList = new XMLAttributeList().addAttribute("classname", getClassName())
500509 .addAttribute("name", getMethodName()).addAttribute("signature", getMethodSignature())
501510 .addAttribute("isStatic", String.valueOf(isStatic()));
502 if (isPrimary)
511 if (isPrimary) {
503512 attributeList.addAttribute("primary", "true");
513 }
504514
505515 String role = getDescription();
506 if (!role.equals(DEFAULT_ROLE))
516 if (!DEFAULT_ROLE.equals(role)) {
507517 attributeList.addAttribute("role", role);
518 }
508519
509520 if (sourceLines == null && !addMessages) {
510521 xmlOutput.openCloseTag(ELEMENT_NAME, attributeList);
526537 public boolean isSignificant() {
527538 String role = getDescription();
528539 if (METHOD_DANGEROUS_TARGET.equals(role) || METHOD_DANGEROUS_TARGET_ACTUAL_GUARANTEED_NULL.equals(role)
529 || METHOD_SAFE_TARGET.equals(role) || METHOD_EQUALS_USED.equals(role) || METHOD_COMPUTED_IN.equals(role))
540 || METHOD_SAFE_TARGET.equals(role) || METHOD_EQUALS_USED.equals(role) || METHOD_COMPUTED_IN.equals(role)) {
530541 return false;
542 }
531543 return true;
532544 }
533545 }
534
535 // vim:ts=4
1111 @Override
1212 public boolean match(BugInstance bugInstance) {
1313
14 if (!super.match(bugInstance))
14 if (!super.match(bugInstance)) {
1515 return false;
16 }
1617
1718 MethodAnnotation bugMethod = bugInstance.getPrimaryMethod();
18 if (bugMethod != null && !method.equals(bugMethod))
19 if (bugMethod != null && !method.equals(bugMethod)) {
1920 return false;
20 if (DEBUG)
21 }
22 if (DEBUG) {
2123 System.out.println("Suppressing " + bugInstance);
24 }
2225 return true;
2326 }
2427 }
2424
2525 @Deprecated
2626 public class NewResults {
27 private SortedBugCollection origCollection;
27 private final SortedBugCollection origCollection;
2828
29 private SortedBugCollection newCollection;
29 private final SortedBugCollection newCollection;
3030
3131 public NewResults(String origFilename, String newFilename) throws IOException, DocumentException {
3232 this(new SortedBugCollection(), new SortedBugCollection());
7171 }
7272 }
7373
74 // vim:ts=4
2929 * To be consistent with FindBugs 1.3 this exception is an {@link IOException}
3030 * and replicates the message used in that release (because I suspect some tools
3131 * looked for that text pattern to come out at the console).
32 *
32 *
3333 * @author Tim Halloran
3434 */
3535 public class NoClassesFoundToAnalyzeException extends IOException {
3838
3939 /**
4040 * Gets the classpath this exception is about.
41 *
41 *
4242 * @return Gets the non-null classpath this exception is about.
4343 */
4444 public IClassPath getClassPath() {
4747
4848 /**
4949 * Constructs an {@code NoClassesFoundToAnalyze} on the passed classpath.
50 *
50 *
5151 * @param classPath
5252 * the classpath used
5353 */
5454 public NoClassesFoundToAnalyzeException(final IClassPath classPath) {
5555 super("No classes found to analyze in " + classPath);
56 if (classPath == null)
56 if (classPath == null) {
5757 throw new IllegalArgumentException("classpath must be non-null");
58 }
5859 f_classPath = classPath;
5960 }
6061 }
33 * @author David Hovemeyer
44 */
55 public class NoOpFindBugsProgress implements FindBugsProgress {
6 @Override
67 public void reportNumberOfArchives(int numArchives) {
78 }
89
10 @Override
911 public void finishArchive() {
1012 }
1113
14 @Override
1215 public void startAnalysis(int numClasses) {
1316 }
1417
18 @Override
1519 public void finishClass() {
1620 }
1721
22 @Override
1823 public void finishPerClassAnalysis() {
1924 }
2025
26 @Override
2127 public void predictPassCount(int[] classesPerPass) {
2228 // noop
2329 }
2430
31 @Override
2532 public void startArchive(String name) {
2633 // noop
2734 }
2020
2121 /**
2222 * A Detector which does not report warnings.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public interface NonReportingDetector extends FirstPassDetector {
5555 }
5656
5757 public static String hashClass(@DottedClassName String className) {
58 if (className.startsWith("java"))
58 if (className.startsWith("java")) {
5959 return className;
60 }
6061 return "obfuscated.H" + hashData(className);
6162 }
6263
7576 case 'J':
7677 case 'D':
7778 case 'F':
78 if (signature.length() == 1)
79 if (signature.length() == 1) {
7980 return signature;
81 }
8082 throw new IllegalArgumentException("bad signature: " + signature);
8183 case 'L':
82 if (!signature.endsWith(";"))
84 if (!signature.endsWith(";")) {
8385 throw new IllegalArgumentException("bad signature: " + signature);
86 }
8487 return hashFieldSignature(signature);
8588 default:
8689 throw new IllegalArgumentException("bad signature: " + signature);
8992
9093 public static String hashFieldSignature(String signature) {
9194 signature = signature.substring(1, signature.length() - 1);
92 if (!signature.startsWith("java"))
93 signature = "obfuscated/H" + hashData(signature);
95 if (!signature.startsWith("java")) {
96 signature = "obfuscated/H" + hashData(signature);
97 }
9498 return "L" + signature + ";";
9599 }
96
100
97101 public static String hashMethodSignature(String signature) {
98102 SignatureParser parser = new SignatureParser(signature);
99103 StringBuilder buf = new StringBuilder("(");
109113
110114 static MethodAnnotation obfuscate(MethodAnnotation m) {
111115 String className = m.getClassName();
112 if (className.startsWith("java"))
116 if (className.startsWith("java")) {
113117 return m;
118 }
114119
115120 String methodName = m.getMethodName();
116121 String methodSignature = m.getMethodSignature();
117122
118 if (methodName.equals("hashCode") && methodSignature.equals("()I") || methodName.equals("equals")
119 && methodSignature.equals("(Ljava/lang/Object;)Z") || methodName.equals("compareTo")
120 && methodSignature.equals("(Ljava/lang/Object;)I") || methodName.equals("<init>")
121 || methodName.equals("<clinit>")) {
123 if ("hashCode".equals(methodName) && "()I".equals(methodSignature) || "equals".equals(methodName)
124 && "(Ljava/lang/Object;)Z".equals(methodSignature) || "compareTo".equals(methodName)
125 && "(Ljava/lang/Object;)I".equals(methodSignature) || "<init>".equals(methodName)
126 || "<clinit>".equals(methodName)) {
122127 // don't need to obfuscate method name
123128 } else {
124129 methodName = hashData(methodName);
137142 return result;
138143
139144 }
140
145
141146
142147 static ClassAnnotation obfuscate(ClassAnnotation m) {
143148 ClassAnnotation result = new ClassAnnotation(hashClass(m.getClassName()));
186191 final BugInstance result = new BugInstance(b.getType(), b.getPriority());
187192 BugAnnotationVisitor visitor = new BugAnnotationVisitor() {
188193
194 @Override
189195 public void visitTypeAnnotation(TypeAnnotation typeAnnotation) {
190196 result.add(obfuscate(typeAnnotation));
191197
192198 }
193199
200 @Override
194201 public void visitStringAnnotation(StringAnnotation stringAnnotation) {
195202 result.add(obfuscate(stringAnnotation));
196203
197204 }
198205
206 @Override
199207 public void visitSourceLineAnnotation(SourceLineAnnotation sourceLineAnnotation) {
200208 result.add(obfuscate(sourceLineAnnotation));
201209
202210 }
203211
212 @Override
204213 public void visitMethodAnnotation(MethodAnnotation methodAnnotation) {
205214 result.add(obfuscate(methodAnnotation));
206215
207216 }
208217
218 @Override
209219 public void visitLocalVariableAnnotation(LocalVariableAnnotation fieldAnnotation) {
210220 result.add(obfuscate(fieldAnnotation));
211221
212222 }
213223
224 @Override
214225 public void visitIntAnnotation(IntAnnotation fieldAnnotation) {
215226 result.add(obfuscate(fieldAnnotation));
216227
217228 }
218229
230 @Override
219231 public void visitFieldAnnotation(FieldAnnotation fieldAnnotation) {
220232 result.add(obfuscate(fieldAnnotation));
221233
222234 }
223235
236 @Override
224237 public void visitClassAnnotation(ClassAnnotation classAnnotation) {
225238 result.add(obfuscate(classAnnotation));
226239
227240 }
228241 };
229 for (BugAnnotation a : b.getAnnotations())
242 for (BugAnnotation a : b.getAnnotations()) {
230243 a.accept(visitor);
244 }
231245 result.setOldInstanceHash(hashData(b.getInstanceHash()));
232246 result.setHistory(b);
233247 return result;
3232 import java.util.Map;
3333
3434 import javax.annotation.CheckForNull;
35 import javax.annotation.Nonnull;
36 import javax.annotation.Nullable;
3537 import javax.annotation.meta.TypeQualifier;
3638
3739 import org.apache.bcel.Repository;
5860 import edu.umd.cs.findbugs.ba.AnalysisFeatures;
5961 import edu.umd.cs.findbugs.ba.ClassMember;
6062 import edu.umd.cs.findbugs.ba.FieldSummary;
63 import edu.umd.cs.findbugs.ba.SignatureParser;
6164 import edu.umd.cs.findbugs.ba.XFactory;
6265 import edu.umd.cs.findbugs.ba.XField;
6366 import edu.umd.cs.findbugs.ba.XMethod;
7073 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
7174 import edu.umd.cs.findbugs.classfile.analysis.MethodInfo;
7275 import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName;
76 import edu.umd.cs.findbugs.internalAnnotations.StaticConstant;
7377 import edu.umd.cs.findbugs.util.ClassName;
7478 import edu.umd.cs.findbugs.util.Util;
7579 import edu.umd.cs.findbugs.visitclass.Constants2;
113117 public @interface CustomUserValue {
114118 }
115119
116 /**
117 *
118 */
119120 private static final String JAVA_UTIL_ARRAYS_ARRAY_LIST = "Ljava/util/Arrays$ArrayList;";
120121
121122 private static final boolean DEBUG = SystemProperties.getBoolean("ocstack.debug");
122123
123124 private static final boolean DEBUG2 = DEBUG;
125
126 @StaticConstant
127 static final HashMap<String, String> boxedTypes = new HashMap<String, String>();
124128
125129 private List<Item> stack;
126130
141145 int pc;
142146 }
143147
144 private boolean seenTransferOfControl = false;
148 private boolean seenTransferOfControl;
145149
146150 private final boolean useIterativeAnalysis = AnalysisContext.currentAnalysisContext().getBoolProperty(
147151 AnalysisFeatures.INTERATIVE_OPCODE_STACK_ANALYSIS);
152
153 boolean encountedTop;
154
155 boolean backwardsBranch;
156
157 BitSet exceptionHandlers = new BitSet();
158
159 private boolean jumpInfoChangedByBackwardsBranch;
160
161 private boolean jumpInfoChangedByNewTarget;
162
163 private Map<Integer, List<Item>> jumpEntries = new HashMap<Integer, List<Item>>();
164
165 private Map<Integer, List<Item>> jumpStackEntries = new HashMap<Integer, List<Item>>();
166
167 private BitSet jumpEntryLocations = new BitSet();
168
169 int convertJumpToOneZeroState = 0;
170
171 int convertJumpToZeroOneState = 0;
172
173 int registerTestedFoundToBeNonnegative = -1;
174
175 int zeroOneComing = -1;
176
177 boolean oneMeansNull;
178
179 boolean needToMerge = true;
180
181 private boolean reachOnlyByBranch;
148182
149183 public static class Item {
150184
229263
230264 public static final @SpecialKind
231265 int SERVLET_OUTPUT = 23;
232
266
233267 public static final @SpecialKind
234268 int TYPE_ONLY = 24;
235269
270304
271305 private int registerNumber = -1;
272306
273 private Object userValue = null;
274
275 private HttpParameterInjection injection = null;
307 @Nullable
308 private Object userValue;
309
310 private HttpParameterInjection injection;
276311
277312 private int fieldLoadedFromRegister = -1;
278313
283318 }
284319
285320 public int getSize() {
286 if (signature.equals("J") || signature.equals("D"))
321 if ("J".equals(signature) || "D".equals(signature)) {
287322 return 2;
323 }
288324 return 1;
289325 }
290326
303339 @Override
304340 public int hashCode() {
305341 int r = 42 + specialKind;
306 if (signature != null)
342 if (signature != null) {
307343 r += signature.hashCode();
344 }
308345 r *= 31;
309 if (constValue != null)
346 if (constValue != null) {
310347 r += constValue.hashCode();
348 }
311349 r *= 31;
312 if (source != null)
350 if (source != null) {
313351 r += source.hashCode();
352 }
314353 r *= 31;
315354 r += flags;
316355 r *= 31;
324363 }
325364 @Override
326365 public boolean equals(Object o) {
327 if (!(o instanceof Item))
366 if (!(o instanceof Item)) {
328367 return false;
368 }
329369 Item that = (Item) o;
330370
331371 return Util.nullSafeEquals(this.signature, that.signature) && Util.nullSafeEquals(this.constValue, that.constValue)
334374 && this.registerNumber == that.registerNumber && this.flags == that.flags
335375 && this.fieldLoadedFromRegister == that.fieldLoadedFromRegister;
336376
377 }
378
379
380 public boolean sameValue(OpcodeStack.Item that) {
381
382 return this.equals(that) && (this.registerNumber != -1 && this.registerNumber == that.registerNumber || this.fieldLoadedFromRegister != -1);
337383 }
338384
339385 @Override
427473 }
428474 if (source instanceof XField) {
429475 buf.append(", ");
430 if (fieldLoadedFromRegister != -1 && fieldLoadedFromRegister != Integer.MAX_VALUE)
476 if (fieldLoadedFromRegister != -1 && fieldLoadedFromRegister != Integer.MAX_VALUE) {
431477 buf.append(fieldLoadedFromRegister).append(':');
478 }
432479 buf.append(source);
433480 }
434481 if (source instanceof XMethod) {
446493 buf.append(", r");
447494 buf.append(registerNumber);
448495 }
449 if (isCouldBeZero() && !isZero())
496 if (isCouldBeZero() && !isZero()) {
450497 buf.append(", cbz");
498 }
451499 if (userValue != null) {
452500 buf.append(", uv: ");
453501 buf.append(userValue.toString());
458506 }
459507
460508 public static Item merge(Item i1, Item i2) {
461 if (i1 == null)
509 if (i1 == null) {
462510 return i2;
463 if (i2 == null)
511 }
512 if (i2 == null) {
464513 return i1;
465 if (i1.equals(i2))
514 }
515 if (i1.equals(i2)) {
466516 return i1;
517 }
467518 Item m = new Item();
468 if (i1.getSpecialKind() == TYPE_ONLY && i2.getSpecialKind() != TYPE_ONLY)
519 if (i1.getSpecialKind() == TYPE_ONLY && i2.getSpecialKind() != TYPE_ONLY) {
469520 return i2;
470 else if (i2.getSpecialKind() == TYPE_ONLY && i1.getSpecialKind() != TYPE_ONLY)
521 } else if (i2.getSpecialKind() == TYPE_ONLY && i1.getSpecialKind() != TYPE_ONLY) {
471522 return i1;
523 }
472524 m.flags = i1.flags & i2.flags;
473525 m.setCouldBeZero(i1.isCouldBeZero() || i2.isCouldBeZero());
474 if (i1.pc == i2.pc)
526 if (i1.pc == i2.pc) {
475527 m.pc = i1.pc;
476 if (Util.nullSafeEquals(i1.signature, i2.signature))
528 }
529 if (Util.nullSafeEquals(i1.signature, i2.signature)) {
477530 m.signature = i1.signature;
478 else if (i1.isNull())
531 } else if (i1.isNull()) {
479532 m.signature = i2.signature;
480 else if (i2.isNull())
533 } else if (i2.isNull()) {
481534 m.signature = i1.signature;
482 if (Util.nullSafeEquals(i1.constValue, i2.constValue))
535 }
536 if (Util.nullSafeEquals(i1.constValue, i2.constValue)) {
483537 m.constValue = i1.constValue;
538 }
484539 if (Util.nullSafeEquals(i1.source, i2.source)) {
485540 m.source = i1.source;
486 } else if ("".equals(i1.constValue))
541 } else if ("".equals(i1.constValue)) {
487542 m.source = i2.source;
488 else if ("".equals(i2.constValue))
543 } else if ("".equals(i2.constValue)) {
489544 m.source = i1.source;
490
491 if (Util.nullSafeEquals(i1.userValue, i2.userValue))
545 }
546
547 if (Util.nullSafeEquals(i1.userValue, i2.userValue)) {
492548 m.userValue = i1.userValue;
493
494 if (i1.registerNumber == i2.registerNumber)
549 }
550
551 if (i1.registerNumber == i2.registerNumber) {
495552 m.registerNumber = i1.registerNumber;
496 if (i1.fieldLoadedFromRegister == i2.fieldLoadedFromRegister)
553 }
554 if (i1.fieldLoadedFromRegister == i2.fieldLoadedFromRegister) {
497555 m.fieldLoadedFromRegister = i1.fieldLoadedFromRegister;
556 }
498557
499558 if (i1.specialKind == SERVLET_REQUEST_TAINTED) {
500559 m.specialKind = SERVLET_REQUEST_TAINTED;
502561 } else if (i2.specialKind == SERVLET_REQUEST_TAINTED) {
503562 m.specialKind = SERVLET_REQUEST_TAINTED;
504563 m.injection = i2.injection;
505 } else if (i1.specialKind == i2.specialKind)
564 } else if (i1.specialKind == i2.specialKind) {
506565 m.specialKind = i1.specialKind;
507 else if (i1.specialKind == NASTY_FLOAT_MATH || i2.specialKind == NASTY_FLOAT_MATH)
566 } else if (i1.specialKind == NASTY_FLOAT_MATH || i2.specialKind == NASTY_FLOAT_MATH) {
508567 m.specialKind = NASTY_FLOAT_MATH;
509 else if (i1.specialKind == FLOAT_MATH || i2.specialKind == FLOAT_MATH)
568 } else if (i1.specialKind == FLOAT_MATH || i2.specialKind == FLOAT_MATH) {
510569 m.specialKind = FLOAT_MATH;
511 if (DEBUG)
570 }
571 if (DEBUG) {
512572 System.out.println("Merge " + i1 + " and " + i2 + " gives " + m);
573 }
513574 return m;
514575 }
515576
517578 this(signature, Integer.valueOf(constValue));
518579 }
519580
520
581
521582 public static Item initialArgument(String signature, int reg) {
522583 Item it = new Item(signature);
523584 it.setInitialParameter(true);
524585 it.registerNumber = reg;
525586 return it;
526
587
527588 }
528589 public Item(String signature) {
529590 this(signature, UNKNOWN);
546607 this.pc = it.pc;
547608 }
548609
549
550
610
611
551612 public Item(Item it, String signature) {
552613 this(it);
553614 this.signature = DescriptorFactory.canonicalizeString(signature);
554615 if (constValue instanceof Number) {
555616 Number constantNumericValue = (Number) constValue;
556 if (signature.equals("B"))
617 if ("B".equals(signature)) {
557618 this.constValue = constantNumericValue.byteValue();
558 else if (signature.equals("S"))
619 } else if ("S".equals(signature)) {
559620 this.constValue = constantNumericValue.shortValue();
560 else if (signature.equals("C"))
621 } else if ("C".equals(signature)) {
561622 this.constValue = (char) constantNumericValue.intValue();
562 else if (signature.equals("I"))
623 } else if ("I".equals(signature)) {
563624 this.constValue = constantNumericValue.intValue();
564 else if (signature.equals("D"))
625 } else if ("D".equals(signature)) {
565626 this.constValue = constantNumericValue.doubleValue();
566 else if (signature.equals("F"))
627 } else if ("F".equals(signature)) {
567628 this.constValue = constantNumericValue.floatValue();
629 }
568630
569631 }
570632 char s = signature.charAt(0);
571 if (s != 'L' && s != '[')
633 if (s != 'L' && s != '[') {
572634 this.source = null;
635 }
573636
574637 setSpecialKindFromSignature();
575638 }
582645 public Item(String signature, FieldAnnotation f) {
583646 this.signature = DescriptorFactory.canonicalizeString(signature);
584647 setSpecialKindFromSignature();
585 if (f != null)
648 if (f != null) {
586649 source = XFactory.createXField(f);
650 }
587651 fieldLoadedFromRegister = -1;
588652 }
589653
590654 public Item(String signature, FieldAnnotation f, int fieldLoadedFromRegister) {
591655 this.signature = DescriptorFactory.canonicalizeString(signature);
592 if (f != null)
656 if (f != null) {
593657 source = XFactory.createXField(f);
658 }
594659 this.fieldLoadedFromRegister = fieldLoadedFromRegister;
595660 }
596661
600665 * If Integer.MAX value, the value was loaded from a static field
601666 * If -1, we don't know or don't have the register containing the object that
602667 * the field was loaded from.
603 * @return
604 *
605668 */
606669 public int getFieldLoadedFromRegister() {
607670 return fieldLoadedFromRegister;
615678
616679 public @CheckForNull
617680 String getHttpParameterName() {
618 if (!isServletParameterTainted())
681 if (!isServletParameterTainted()) {
619682 throw new IllegalStateException();
620 if (injection == null)
683 }
684 if (injection == null) {
621685 return null;
686 }
622687 return injection.parameterName;
623688 }
624689
625690 public int getInjectionPC() {
626 if (!isServletParameterTainted())
691 if (!isServletParameterTainted()) {
627692 throw new IllegalStateException();
628 if (injection == null)
693 }
694 if (injection == null) {
629695 return -1;
696 }
630697 return injection.pc;
631698 }
632699
636703 constValue = constantValue;
637704 if (constantValue instanceof Integer) {
638705 int value = ((Integer) constantValue).intValue();
639 if (value != 0 && (value & 0xff) == 0)
706 if (value != 0 && (value & 0xff) == 0) {
640707 specialKind = LOW_8_BITS_CLEAR;
641 if (value == 0)
708 }
709 if (value == 0) {
642710 setCouldBeZero(true);
711 }
643712
644713 } else if (constantValue instanceof Long) {
645714 long value = ((Long) constantValue).longValue();
646 if (value != 0 && (value & 0xff) == 0)
715 if (value != 0 && (value & 0xff) == 0) {
647716 specialKind = LOW_8_BITS_CLEAR;
648 if (value == 0)
717 }
718 if (value == 0) {
649719 setCouldBeZero(true);
720 }
650721 }
651722
652723 }
653724
654725 private void setSpecialKindFromSignature() {
655 if (false && specialKind != NOT_SPECIAL)
726 /*
727 if (false && specialKind != NOT_SPECIAL) {
656728 return;
657 if (signature.equals("B"))
729 }
730 */
731 if ("B".equals(signature)) {
658732 specialKind = SIGNED_BYTE;
659 else if (signature.equals("C"))
733 } else if ("C".equals(signature)) {
660734 specialKind = NON_NEGATIVE;
735 }
661736 }
662737
663738 public void setCouldBeNegative() {
664 if (specialKind == NON_NEGATIVE)
739 if (specialKind == NON_NEGATIVE) {
665740 specialKind = NOT_SPECIAL;
741 }
666742 }
667743
668744 public Item() {
683759 JavaClass getJavaClass() throws ClassNotFoundException {
684760 String baseSig;
685761
686 if (isPrimitive() || isArray())
762 if (isPrimitive() || isArray()) {
687763 return null;
764 }
688765
689766 baseSig = signature;
690767
691 if (baseSig.length() == 0)
768 if (baseSig.length() == 0) {
692769 return null;
770 }
693771 baseSig = baseSig.substring(1, baseSig.length() - 1);
694772 baseSig = baseSig.replace('/', '.');
695773 return Repository.lookupClass(baseSig);
701779
702780 @Deprecated
703781 public String getElementSignature() {
704 if (!isArray())
782 if (!isArray()) {
705783 return signature;
706 else {
784 } else {
707785 int pos = 0;
708786 int len = signature.length();
709787 while (pos < len) {
710 if (signature.charAt(pos) != '[')
788 if (signature.charAt(pos) != '[') {
711789 break;
790 }
712791 pos++;
713792 }
714793 return signature.substring(pos);
716795 }
717796
718797 public boolean isNonNegative() {
719 if (specialKind == NON_NEGATIVE)
798 if (specialKind == NON_NEGATIVE) {
720799 return true;
800 }
721801 if (constValue instanceof Number) {
722802 double value = ((Number) constValue).doubleValue();
723803 return value >= 0;
741821 * Returns a constant value for this Item, if known. NOTE: if the value
742822 * is a constant Class object, the constant value returned is the name
743823 * of the class.
824 * if the value is an array of known length, the constant value returned is its length (Integer)
744825 */
745826 public Object getConstant() {
746827 return constValue;
753834 }
754835
755836 public XField getXField() {
756 if (source instanceof XField)
837 if (source instanceof XField) {
757838 return (XField) source;
839 }
758840 return null;
759841 }
760842
788870 }
789871
790872 /**
791 * attaches a detector specified value to this item
873 * <p>attaches a detector specified value to this item</p>
874 * <p>to use this method, detector should be annotated with {@code CustomUserValue}.</p>
792875 *
793876 * @param value
794877 * the custom value to set
878 * @see OpcodeStack.CustomUserValue
795879 */
796 public void setUserValue(Object value) {
880 public void setUserValue(@Nullable Object value) {
797881 userValue = value;
798882 }
799883
804888 */
805889 public @CheckForNull
806890 XMethod getReturnValueOf() {
807 if (source instanceof XMethod)
891 if (source instanceof XMethod) {
808892 return (XMethod) source;
893 }
809894 return null;
810895 }
811896
823908 *
824909 * @return the custom value
825910 */
911 @Nullable
826912 public Object getUserValue() {
827913 return userValue;
828914 }
840926
841927
842928 public boolean isServletWriter() {
843 if (getSpecialKind() == Item.SERVLET_OUTPUT)
929 if (getSpecialKind() == Item.SERVLET_OUTPUT) {
844930 return true;
845 if (getSignature().equals("Ljavax/servlet/ServletOutputStream;"))
931 }
932 if ("Ljavax/servlet/ServletOutputStream;".equals(getSignature())) {
846933 return true;
934 }
847935 XMethod writingToSource = getReturnValueOf();
848936
849937
850 return writingToSource != null && writingToSource.getClassName().equals("javax.servlet.http.HttpServletResponse")
851 && (writingToSource.getName().equals("getWriter") || writingToSource.getName().equals("getOutputStream"));
938 return writingToSource != null && "javax.servlet.http.HttpServletResponse".equals(writingToSource.getClassName())
939 && ("getWriter".equals(writingToSource.getName()) || "getOutputStream".equals(writingToSource.getName()));
852940 }
853941
854942 public boolean valueCouldBeNegative() {
855943 return !isNonNegative()
856944 && (getSpecialKind() == Item.RANDOM_INT || getSpecialKind() == Item.SIGNED_BYTE
857 || getSpecialKind() == Item.HASHCODE_INT || getSpecialKind() == Item.RANDOM_INT_REMAINDER
858 || getSpecialKind() == Item.HASHCODE_INT_REMAINDER || getSpecialKind() == Item.MATH_ABS_OF_RANDOM || getSpecialKind() == Item.MATH_ABS_OF_HASHCODE);
945 || getSpecialKind() == Item.HASHCODE_INT || getSpecialKind() == Item.RANDOM_INT_REMAINDER
946 || getSpecialKind() == Item.HASHCODE_INT_REMAINDER || getSpecialKind() == Item.MATH_ABS_OF_RANDOM || getSpecialKind() == Item.MATH_ABS_OF_HASHCODE);
859947
860948 }
861949
9231011 || isZero();
9241012 }
9251013
926 /**
927 * @return
928 */
9291014 private boolean isZero() {
9301015 return constValue != null && constValue.equals(0);
9311016 }
9391024 }
9401025
9411026 private void setFlag(boolean value, int flagBit) {
942 if (value)
1027 if (value) {
9431028 flags |= flagBit;
944 else
1029 } else {
9451030 flags &= ~flagBit;
1031 }
9461032 }
9471033
9481034 /**
9571043 */
9581044 public void clearNewlyAllocated() {
9591045 if (specialKind == NEWLY_ALLOCATED) {
960 if (signature.startsWith("Ljava/lang/StringB"))
1046 if (signature.startsWith("Ljava/lang/StringB")) {
9611047 constValue = null;
1048 }
9621049 specialKind = NOT_SPECIAL;
9631050 }
9641051 }
9671054 return specialKind == NEWLY_ALLOCATED;
9681055 }
9691056
970 /**
971 * @param i
972 * @return
973 */
9741057 public boolean hasConstantValue(int value) {
975 if (constValue instanceof Number)
1058 if (constValue instanceof Number) {
9761059 return ((Number) constValue).intValue() == value;
1060 }
9771061 return false;
9781062 }
9791063
9801064 public boolean hasConstantValue(long value) {
981 if (constValue instanceof Number)
1065 if (constValue instanceof Number) {
9821066 return ((Number) constValue).longValue() == value;
1067 }
9831068 return false;
9841069 }
9851070 }
9861071
9871072 @Override
9881073 public String toString() {
989 if (isTop())
1074 if (isTop()) {
9901075 return "TOP";
1076 }
9911077 return stack.toString() + "::" + lvValues.toString();
9921078 }
9931079
10021088
10031089 }
10041090
1005 boolean needToMerge = true;
1006
1007 private boolean reachOnlyByBranch = false;
1008
10091091 public static String getExceptionSig(DismantleBytecode dbc, CodeException e) {
1010 if (e.getCatchType() == 0)
1092 if (e.getCatchType() == 0) {
10111093 return "Ljava/lang/Throwable;";
1094 }
10121095 Constant c = dbc.getConstantPool().getConstant(e.getCatchType());
1013 if (c instanceof ConstantClass)
1096 if (c instanceof ConstantClass) {
10141097 return "L" + ((ConstantClass) c).getBytes(dbc.getConstantPool()) + ";";
1098 }
10151099 return "Ljava/lang/Throwable;";
10161100 }
10171101
10181102 public void mergeJumps(DismantleBytecode dbc) {
1019 if (!needToMerge)
1103 if (!needToMerge) {
10201104 return;
1105 }
10211106 needToMerge = false;
10221107 if (dbc.getPC() == zeroOneComing) {
10231108 pop();
10241109 top = false;
10251110 OpcodeStack.Item item = new Item("I");
1026 if (oneMeansNull)
1111 if (oneMeansNull) {
10271112 item.setSpecialKind(Item.NONZERO_MEANS_NULL);
1028 else
1113 } else {
10291114 item.setSpecialKind(Item.ZERO_MEANS_NULL);
1115 }
10301116 item.setPC(dbc.getPC() - 8);
10311117 item.setCouldBeZero(true);
10321118
10331119 push(item);
10341120
10351121 zeroOneComing = -1;
1036 if (DEBUG)
1122 if (DEBUG) {
10371123 System.out.println("Updated to " + this);
1124 }
10381125 return;
10391126 }
10401127
10491136 }
10501137
10511138 List<Item> jumpEntry = null;
1052 if (jumpEntryLocations.get(dbc.getPC()))
1139 if (jumpEntryLocations.get(dbc.getPC())) {
10531140 jumpEntry = jumpEntries.get(Integer.valueOf(dbc.getPC()));
1141 }
10541142 boolean wasReachOnlyByBranch = isReachOnlyByBranch();
10551143 if (jumpEntry != null) {
10561144 setReachOnlyByBranch(false);
10591147 if (DEBUG2) {
10601148 if (wasReachOnlyByBranch) {
10611149 System.out.println("Reached by branch at " + dbc.getPC() + " with " + jumpEntry);
1062 if (jumpStackEntry != null)
1063 System.out.println(" and stack " + jumpStackEntry);
1150 if (jumpStackEntry != null) {
1151 System.out.println(" and stack " + jumpStackEntry);
1152 }
10641153 } else if (!jumpEntry.equals(lvValues)
1065 || jumpStackEntry != null && !jumpStackEntry.equals(stack)) {
1154 || jumpStackEntry != null && !jumpStackEntry.equals(stack)) {
10661155
10671156 System.out.println("Merging at " + dbc.getPC() + " with " + jumpEntry);
1068 if (jumpStackEntry != null)
1069 System.out.println(" and stack " + jumpStackEntry);
1157 if (jumpStackEntry != null) {
1158 System.out.println(" and stack " + jumpStackEntry);
1159 }
10701160
10711161 }
10721162
10731163 }
10741164 if (isTop()) {
10751165 lvValues = new ArrayList<Item>(jumpEntry);
1076 if (jumpStackEntry != null)
1166 if (jumpStackEntry != null) {
10771167 stack = new ArrayList<Item>(jumpStackEntry);
1078 else
1168 } else {
10791169 stack.clear();
1170 }
10801171 setTop(false);
10811172 return;
10821173 }
10841175 setTop(false);
10851176 lvValues = new ArrayList<Item>(jumpEntry);
10861177 if (!stackUpdated) {
1087 if (jumpStackEntry != null)
1178 if (jumpStackEntry != null) {
10881179 stack = new ArrayList<Item>(jumpStackEntry);
1089 else
1180 } else {
10901181 stack.clear();
1182 }
10911183 }
10921184
10931185 } else {
10941186 setTop(false);
10951187 mergeLists(lvValues, jumpEntry, false);
1096 if (!stackUpdated && jumpStackEntry != null)
1188 if (!stackUpdated && jumpStackEntry != null) {
10971189 mergeLists(stack, jumpStackEntry, false);
1098 }
1099 if (DEBUG)
1190 }
1191 }
1192 if (DEBUG) {
11001193 System.out.println(" merged lvValues " + lvValues);
1194 }
11011195 } else if (isReachOnlyByBranch() && !stackUpdated) {
11021196 stack.clear();
11031197
1198 Item item = null;
11041199 for (CodeException e : dbc.getCode().getExceptionTable()) {
11051200 if (e.getHandlerPC() == dbc.getPC()) {
1106 push(new Item(getExceptionSig(dbc, e)));
1107 setReachOnlyByBranch(false);
1108 setTop(false);
1109 return;
1110
1111 }
1112 }
1113 setTop(true);
1114
1115
1116 }
1117
1118 }
1119
1120 int convertJumpToOneZeroState = 0;
1121
1122 int convertJumpToZeroOneState = 0;
1123
1124 int registerTestedFoundToBeNonnegative = -1;
1201 Item newItem = new Item(getExceptionSig(dbc, e));
1202 if (item == null) {
1203 item = newItem;
1204 } else {
1205 item = Item.merge(item, newItem);
1206 }
1207
1208
1209 }
1210 }
1211
1212 if (item != null) {
1213 push(item);
1214 setReachOnlyByBranch(false);
1215 setTop(false);
1216 } else {
1217
1218 setTop(true);
1219 }
1220
1221
1222 }
1223
1224 }
11251225
11261226 private void setLastUpdate(int reg, int pc) {
1127 while (lastUpdate.size() <= reg)
1227 while (lastUpdate.size() <= reg) {
11281228 lastUpdate.add(Integer.valueOf(0));
1229 }
11291230 lastUpdate.set(reg, Integer.valueOf(pc));
11301231 }
11311232
11321233 public int getLastUpdate(int reg) {
1133 if (lastUpdate.size() <= reg)
1234 if (lastUpdate.size() <= reg) {
11341235 return 0;
1236 }
11351237 return lastUpdate.get(reg).intValue();
11361238 }
11371239
11381240 public int getNumLastUpdates() {
11391241 return lastUpdate.size();
11401242 }
1141
1142 int zeroOneComing = -1;
1143
1144 boolean oneMeansNull;
11451243
11461244 public void sawOpcode(DismantleBytecode dbc, int seen) {
11471245 int register;
11511249
11521250 // System.out.printf("%3d %12s%s%n", dbc.getPC(), OPCODE_NAMES[seen],
11531251 // this);
1154 if (dbc.isRegisterStore())
1252 if (dbc.isRegisterStore()) {
11551253 setLastUpdate(dbc.getRegisterOperand(), dbc.getPC());
1254 }
11561255
11571256 precomputation(dbc);
11581257 needToMerge = true;
11751274 && (prevOpcode2 == IFNULL || prevOpcode2 == IFNONNULL)
11761275 && (nextOpcode == ICONST_0 || nextOpcode == ICONST_1) && prevOpcode1 != nextOpcode) {
11771276 oneMeansNull = prevOpcode1 == ICONST_0;
1178 if (prevOpcode2 != IFNULL)
1277 if (prevOpcode2 != IFNULL) {
11791278 oneMeansNull = !oneMeansNull;
1279 }
11801280 zeroOneComing = nextPC + 1;
11811281 convertJumpToOneZeroState = convertJumpToZeroOneState = 0;
11821282 }
11831283 } catch (ArrayIndexOutOfBoundsException e) {
11841284 throw e; // throw new
1185 // ArrayIndexOutOfBoundsException(nextPC + " "
1186 // + dbc.getMaxPC());
1285 // ArrayIndexOutOfBoundsException(nextPC + " "
1286 // + dbc.getMaxPC());
11871287 }
11881288 }
11891289 }
11931293 convertJumpToOneZeroState = 1;
11941294 break;
11951295 case GOTO:
1196 if (convertJumpToOneZeroState == 1 && dbc.getBranchOffset() == 4)
1296 if (convertJumpToOneZeroState == 1 && dbc.getBranchOffset() == 4) {
11971297 convertJumpToOneZeroState = 2;
1198 else
1298 } else {
11991299 convertJumpToOneZeroState = 0;
1300 }
12001301 break;
12011302 case ICONST_0:
1202 if (convertJumpToOneZeroState == 2)
1303 if (convertJumpToOneZeroState == 2) {
12031304 convertJumpToOneZeroState = 3;
1204 else
1305 } else {
12051306 convertJumpToOneZeroState = 0;
1307 }
12061308 break;
12071309 default:
12081310 convertJumpToOneZeroState = 0;
12131315 convertJumpToZeroOneState = 1;
12141316 break;
12151317 case GOTO:
1216 if (convertJumpToZeroOneState == 1 && dbc.getBranchOffset() == 4)
1318 if (convertJumpToZeroOneState == 1 && dbc.getBranchOffset() == 4) {
12171319 convertJumpToZeroOneState = 2;
1218 else
1320 } else {
12191321 convertJumpToZeroOneState = 0;
1322 }
12201323 break;
12211324 case ICONST_1:
1222 if (convertJumpToZeroOneState == 2)
1325 if (convertJumpToZeroOneState == 2) {
12231326 convertJumpToZeroOneState = 3;
1224 else
1327 } else {
12251328 convertJumpToZeroOneState = 0;
1329 }
12261330 break;
12271331 default:
12281332 convertJumpToZeroOneState = 0;
13001404 }
13011405 FieldAnnotation field = FieldAnnotation.fromReferencedField(dbc);
13021406 Item i = new Item(dbc.getSigConstantOperand(), field, Integer.MAX_VALUE);
1303 if (field.getFieldName().equals("separator") && field.getClassName().equals("java.io.File")) {
1407 if ("separator".equals(field.getFieldName()) && "java.io.File".equals(field.getClassName())) {
13041408 i.setSpecialKind(Item.FILE_SEPARATOR_STRING);
13051409 }
13061410 i.setPC(dbc.getPC());
13531457 // reset all other such values on the opcode stack
13541458 if (topItem.valueCouldBeNegative() && (seen == IFLT || seen == IFLE || seen == IFGT || seen == IFGE)) {
13551459 int specialKind = topItem.getSpecialKind();
1356 for (Item item : stack)
1357 if (item != null && item.getSpecialKind() == specialKind)
1460 for (Item item : stack) {
1461 if (item != null && item.getSpecialKind() == specialKind) {
13581462 item.setSpecialKind(Item.NOT_SPECIAL);
1359 for (Item item : lvValues)
1360 if (item != null && item.getSpecialKind() == specialKind)
1463 }
1464 }
1465 for (Item item : lvValues) {
1466 if (item != null && item.getSpecialKind() == specialKind) {
13611467 item.setSpecialKind(Item.NOT_SPECIAL);
1468 }
1469 }
13621470
13631471 }
13641472 }
13731481 pop();
13741482 addJumpValue(dbc.getPC(), dbc.getBranchTarget());
13751483 int pc = dbc.getBranchTarget() - dbc.getBranchOffset();
1376 for (int offset : dbc.getSwitchOffsets())
1484 for (int offset : dbc.getSwitchOffsets()) {
13771485 addJumpValue(dbc.getPC(), offset + pc);
1486 }
13781487
13791488 break;
13801489 case ARETURN:
13931502 case POP:
13941503 pop();
13951504 break;
1396
1505
13971506 case PUTSTATIC:
13981507 pop();
13991508 eraseKnowledgeOf(dbc.getXFieldOperand());
14721581 }
14731582 if (right.hasConstantValue(Integer.MIN_VALUE) && left.mightRarelyBeNegative()
14741583 || left.hasConstantValue(Integer.MIN_VALUE) && right.mightRarelyBeNegative()) {
1475 for (Item i : stack)
1476 if (i != null && i.mightRarelyBeNegative())
1584 for (Item i : stack) {
1585 if (i != null && i.mightRarelyBeNegative()) {
14771586 i.setSpecialKind(Item.NOT_SPECIAL);
1478 for (Item i : lvValues)
1479 if (i != null && i.mightRarelyBeNegative())
1587 }
1588 }
1589 for (Item i : lvValues) {
1590 if (i != null && i.mightRarelyBeNegative()) {
14801591 i.setSpecialKind(Item.NOT_SPECIAL);
1592 }
1593 }
14811594 }
14821595 int branchTarget = dbc.getBranchTarget();
14831596 addJumpValue(dbc.getPC(), branchTarget);
14861599
14871600 case POP2:
14881601 it = pop();
1489 if (it.getSize() == 1)
1602 if (it.getSize() == 1) {
14901603 pop();
1604 }
14911605 break;
14921606
14931607
15401654 case CHECKCAST: {
15411655 String castTo = dbc.getClassConstantOperand();
15421656
1543 if (castTo.charAt(0) != '[')
1657 if (castTo.charAt(0) != '[') {
15441658 castTo = "L" + castTo + ";";
1659 }
15451660 it = pop();
15461661
15471662 if (!it.signature.equals(castTo)) {
16671782 push(valueLoaded);
16681783
16691784 }
1670 break;
1785 break;
16711786
16721787 case ARRAYLENGTH: {
1673 pop();
1674 Item newItem = new Item("I");
1788 Item array = pop();
1789 Item newItem = new Item("I", array.getConstant());
16751790 newItem.setSpecialKind(Item.NON_NEGATIVE);
16761791 push(newItem);
16771792 }
1678 break;
1793 break;
16791794
16801795 case BALOAD: {
16811796 pop(2);
16841799 push(newItem);
16851800 break;
16861801 }
1687 case CALOAD:
1802 case CALOAD: {
16881803 pop(2);
1689 push(new Item("I"));
1690 break;
1691
1804 Item newItem = new Item("I");
1805 newItem.setSpecialKind(Item.NON_NEGATIVE);
1806 push(newItem);
1807 break;
1808 }
16921809 case DALOAD:
16931810 pop(2);
16941811 push(new Item("D"));
18281945
18291946 push(newValue);
18301947 }
1831 break;
1948 break;
18321949
18331950
18341951
18381955
18391956 push(newValue);
18401957 }
1841 break;
1958 break;
18421959
18431960 case I2L:
18441961 case D2L:
18481965
18491966 int specialKind = it.getSpecialKind();
18501967
1851 if (specialKind != Item.SIGNED_BYTE && seen == I2L)
1968 if (specialKind != Item.SIGNED_BYTE && seen == I2L) {
18521969 newValue.setSpecialKind(Item.RESULT_OF_I2L);
1970 }
18531971
18541972 push(newValue);
18551973 }
18561974 break;
18571975
18581976 case I2S:
1859 {
1860 Item item1 = pop();
1861 Item newValue = new Item(item1, "S");
1862 newValue.setCouldBeNegative();
1863 push(newValue);
1864 }
1865 break;
1977 {
1978 Item item1 = pop();
1979 Item newValue = new Item(item1, "S");
1980 newValue.setCouldBeNegative();
1981 push(newValue);
1982 }
1983 break;
18661984
18671985 case L2I:
18681986 case D2I:
18711989 int oldSpecialKind = it.getSpecialKind();
18721990 it = new Item(it, "I");
18731991
1874 if (oldSpecialKind == Item.NOT_SPECIAL)
1992 if (oldSpecialKind == Item.NOT_SPECIAL) {
18751993 it.setSpecialKind(Item.RESULT_OF_L2I);
1994 }
18761995 push(it);
18771996
18781997 break;
19042023 item.setSpecialKind(Item.NEWLY_ALLOCATED);
19052024 push(item);
19062025 }
1907 break;
1908
1909 case NEWARRAY:
1910 pop();
2026 break;
2027
2028 case NEWARRAY: {
2029 Item length = pop();
19112030 signature = "[" + BasicType.getType((byte) dbc.getIntConstant()).getSignature();
1912 pushBySignature(signature, dbc);
1913 break;
2031 Item item = new Item(signature, length.getConstant());
2032 item.setPC(dbc.getPC());
2033 item.setSpecialKind(Item.NEWLY_ALLOCATED);
2034 push(item);
2035 break;
2036 }
19142037
19152038 // According to the VM Spec 4.4.1, anewarray and multianewarray
19162039 // can refer to normal class/interface types (encoded in
19172040 // "internal form"), or array classes (encoded as signatures
19182041 // beginning with "[").
19192042
1920 case ANEWARRAY:
1921 pop();
2043 case ANEWARRAY: {
2044 Item length = pop();
19222045 signature = dbc.getClassConstantOperand();
1923 if (signature.charAt(0) == '[')
2046 if (signature.charAt(0) == '[') {
19242047 signature = "[" + signature;
1925 else
2048 } else {
19262049 signature = "[L" + signature + ";";
1927 pushBySignature(signature, dbc);
1928 break;
2050 }
2051 Item item = new Item(signature, length.getConstant());
2052 item.setPC(dbc.getPC());
2053 item.setSpecialKind(Item.NEWLY_ALLOCATED);
2054 push(item);
2055 break;
2056 }
19292057
19302058 case MULTIANEWARRAY:
19312059 int dims = dbc.getIntConstant();
1932 for (int i = 0; i < dims; i++)
2060 for (int i = 0; i < dims; i++) {
19332061 pop();
2062 }
19342063
19352064 signature = dbc.getClassConstantOperand();
19362065 pushBySignature(signature, dbc);
2066 getStackItem(0).setSpecialKind(Item.NEWLY_ALLOCATED);
19372067 break;
19382068
19392069 case AALOAD: {
19402070 pop();
19412071 it = pop();
19422072 String arraySig = it.getSignature();
1943 if (arraySig.charAt(0) == '[')
2073 if (arraySig.charAt(0) == '[') {
19442074 pushBySignature(arraySig.substring(1), dbc);
1945 else
2075 } else {
19462076 push(new Item());
1947 }
1948 break;
2077 }
2078 }
2079 break;
19492080
19502081 case JSR:
19512082 seenTransferOfControl = true;
19572088 // OK, backwards JSRs are weird; reset the stack.
19582089 int stackSize = stack.size();
19592090 stack.clear();
1960 for (int i = 0; i < stackSize; i++)
2091 for (int i = 0; i < stackSize; i++) {
19612092 stack.add(new Item());
2093 }
19622094 }
19632095 setTop(false);
19642096 break;
19692101 case INVOKEVIRTUAL:
19702102 processMethodCall(dbc, seen);
19712103 break;
1972
2104 case INVOKEDYNAMIC:
2105 processInvokeDynamic(dbc);
2106 break;
19732107 default:
1974 throw new UnsupportedOperationException("OpCode " + OPCODE_NAMES[seen] + " not supported ");
2108 throw new UnsupportedOperationException("OpCode " + seen + ":" + OPCODE_NAMES[seen] + " not supported ");
19752109 }
19762110 }
19772111
19862120 String msg = "Error processing opcode " + OPCODE_NAMES[seen] + " @ " + dbc.getPC() + " in "
19872121 + dbc.getFullyQualifiedMethodName();
19882122 AnalysisContext.logError(msg, e);
1989 if (DEBUG)
2123 if (DEBUG) {
19902124 e.printStackTrace();
2125 }
19912126 clear();
2127 setTop(true);
19922128 } finally {
19932129 if (DEBUG) {
19942130 System.out.printf("%4d: %14s %s%n", dbc.getPC(), OPCODE_NAMES[seen] , this);
19962132 }
19972133 }
19982134
1999 /**
2000 * @param fieldOperand
2001 */
20022135 private void eraseKnowledgeOf(XField fieldOperand) {
2003 if (fieldOperand == null) return;
2004 for (Item item : stack)
2005 if (item != null && fieldOperand.equals(item.getXField()))
2136 if (fieldOperand == null) {
2137 return;
2138 }
2139 for (Item item : stack) {
2140 if (item != null && fieldOperand.equals(item.getXField())) {
20062141 item.setLoadedFromField(null, -1);
2007 for (Item item : lvValues)
2008 if (item != null && fieldOperand.equals(item.getXField()))
2142 }
2143 }
2144 for (Item item : lvValues) {
2145 if (item != null && fieldOperand.equals(item.getXField())) {
20092146 item.setLoadedFromField(null, -1);
2010 }
2011
2012
2147 }
2148 }
2149 }
20132150
20142151 public void precomputation(DismantleBytecode dbc) {
20152152 if (registerTestedFoundToBeNonnegative >= 0) {
20162153 for (int i = 0; i < stack.size(); i++) {
20172154 Item item = stack.get(i);
2018 if (item != null && item.registerNumber == registerTestedFoundToBeNonnegative)
2155 if (item != null && item.registerNumber == registerTestedFoundToBeNonnegative) {
20192156 stack.set(i, item.cloneAndSetSpecialKind(Item.NON_NEGATIVE));
2157 }
20202158 }
20212159 for (int i = 0; i < lvValues.size(); i++) {
20222160 Item item = lvValues.get(i);
2023 if (item != null && item.registerNumber == registerTestedFoundToBeNonnegative)
2161 if (item != null && item.registerNumber == registerTestedFoundToBeNonnegative) {
20242162 lvValues.set(i, item.cloneAndSetSpecialKind(Item.NON_NEGATIVE));
2163 }
20252164 }
20262165 }
20272166 registerTestedFoundToBeNonnegative = -1;
20282167 mergeJumps(dbc);
20292168 }
20302169
2031 /**
2032 * @param it
2033 * @return
2034 */
20352170 private int constantToInt(Item it) {
20362171 Object constant = it.getConstant();
20372172 if (constant instanceof Number) {
20432178 throw new IllegalArgumentException(String.valueOf(constant));
20442179 }
20452180
2046 /**
2047 * @param it
2048 * @return
2049 */
20502181 private float constantToFloat(Item it) {
20512182 return ((Number) it.getConstant()).floatValue();
20522183 }
20532184
2054 /**
2055 * @param it
2056 * @return
2057 */
20582185 private double constantToDouble(Item it) {
20592186 return ((Number) it.getConstant()).doubleValue();
20602187 }
20612188
2062 /**
2063 * @param it
2064 * @return
2065 */
20662189 private long constantToLong(Item it) {
20672190 return ((Number) it.getConstant()).longValue();
20682191 }
20692192
2070 /**
2071 * handle dcmp
2072 *
2073 */
20742193 private void handleDcmp(int opcode) {
20752194 Item it = pop();
20762195 Item it2 = pop();
20792198 double d = constantToDouble(it);
20802199 double d2 = constantToDouble(it2);
20812200 if (Double.isNaN(d) || Double.isNaN(d2)) {
2082 if (opcode == DCMPG)
2201 if (opcode == DCMPG) {
20832202 push(new Item("I", Integer.valueOf(1)));
2084 else
2203 } else {
20852204 push(new Item("I", Integer.valueOf(-1)));
2086 }
2087 if (d2 < d)
2205 }
2206 }
2207 if (d2 < d) {
20882208 push(new Item("I", Integer.valueOf(-1)));
2089 else if (d2 > d)
2209 } else if (d2 > d) {
20902210 push(new Item("I", Integer.valueOf(1)));
2091 else
2211 } else {
20922212 push(new Item("I", Integer.valueOf(0)));
2213 }
20932214 } else {
20942215 push(new Item("I"));
20952216 }
20962217 }
20972218
2098 /**
2099 * handle fcmp
2100 *
2101 */
21022219 private void handleFcmp(int opcode) {
21032220 Item it = pop();
21042221 Item it2 = pop();
21062223 float f = constantToFloat(it);
21072224 float f2 = constantToFloat(it2);
21082225 if (Float.isNaN(f) || Float.isNaN(f2)) {
2109 if (opcode == FCMPG)
2226 if (opcode == FCMPG) {
21102227 push(new Item("I", Integer.valueOf(1)));
2111 else
2228 } else {
21122229 push(new Item("I", Integer.valueOf(-1)));
2113 }
2114 if (f2 < f)
2230 }
2231 }
2232 if (f2 < f) {
21152233 push(new Item("I", Integer.valueOf(-1)));
2116 else if (f2 > f)
2234 } else if (f2 > f) {
21172235 push(new Item("I", Integer.valueOf(1)));
2118 else
2236 } else {
21192237 push(new Item("I", Integer.valueOf(0)));
2238 }
21202239 } else {
21212240 push(new Item("I"));
21222241 }
21232242 }
21242243
2125 /**
2126 * handle lcmp
2127 */
21282244 private void handleLcmp() {
21292245 Item it = pop();
21302246 Item it2 = pop();
21322248 if ((it.getConstant() != null) && it2.getConstant() != null) {
21332249 long l = constantToLong(it);
21342250 long l2 = constantToLong(it2);
2135 if (l2 < l)
2251 if (l2 < l) {
21362252 push(new Item("I", Integer.valueOf(-1)));
2137 else if (l2 > l)
2253 } else if (l2 > l) {
21382254 push(new Item("I", Integer.valueOf(1)));
2139 else
2255 } else {
21402256 push(new Item("I", Integer.valueOf(0)));
2257 }
21412258 } else {
21422259 push(new Item("I"));
21432260 }
21442261
21452262 }
21462263
2147 /**
2148 * handle swap
2149 */
21502264 private void handleSwap() {
21512265 Item i1 = pop();
21522266 Item i2 = pop();
21542268 push(i2);
21552269 }
21562270
2157 /**
2158 * handleDup
2159 */
21602271 private void handleDup() {
21612272 Item it;
21622273 it = pop();
21642275 push(it);
21652276 }
21662277
2167 /**
2168 * handle dupX1
2169 */
21702278 private void handleDupX1() {
21712279 Item it;
21722280 Item it2;
21772285 push(it);
21782286 }
21792287
2180 /**
2181 * handle dup2
2182 */
21832288 private void handleDup2() {
21842289 Item it, it2;
21852290 it = pop();
21952300 }
21962301 }
21972302
2198 /**
2199 * handle Dup2x1
2200 */
22012303 private void handleDup2X1() {
22022304 String signature;
22032305 Item it;
22082310
22092311 it2 = pop();
22102312 signature = it.getSignature();
2211 if (signature.equals("J") || signature.equals("D")) {
2313 if ("J".equals(signature) || "D".equals(signature)) {
22122314 push(it);
22132315 push(it2);
22142316 push(it);
22582360 }
22592361 }
22602362
2261 /**
2262 * Handle DupX2
2263 */
22642363 private void handleDupX2() {
22652364 String signature;
22662365 Item it;
22692368 it = pop();
22702369 it2 = pop();
22712370 signature = it2.getSignature();
2272 if (signature.equals("J") || signature.equals("D")) {
2371 if ("J".equals(signature) || "D".equals(signature)) {
22732372 push(it);
22742373 push(it2);
22752374 push(it);
22822381 }
22832382 }
22842383
2285 @edu.umd.cs.findbugs.internalAnnotations.StaticConstant
2286 static final HashMap<String, String> boxedTypes = new HashMap<String, String>();
2287
22882384 static private void addBoxedType(Class<?>... clss) {
22892385 for (Class<?> c : clss) {
22902386 Class<?> primitiveType;
22912387 try {
22922388 primitiveType = (Class<?>) c.getField("TYPE").get(null);
22932389 boxedTypes.put(ClassName.toSlashedClassName(c.getName()), primitiveType.getName());
2294 } catch (Exception e) {
2390 } catch (IllegalArgumentException | IllegalAccessException | NoSuchFieldException | SecurityException e) {
22952391 throw new AssertionError(e);
22962392 }
2297
22982393 }
22992394 }
23002395
23072402 item.constValue = null;
23082403 if (item.registerNumber >= 0) {
23092404 OpcodeStack.Item sbItem = getLVValue(item.registerNumber);
2310 if (sbItem != null && sbItem.getSignature().equals(item.getSignature()))
2405 if (sbItem.getSignature().equals(item.getSignature())) {
23112406 sbItem.constValue = null;
2407 }
23122408 }
23132409
23142410 }
23212417 boolean sawUnknownAppend = false;
23222418 Item sbItem = null;
23232419 Item topItem = null;
2324 if (getStackDepth() > 0)
2420 if (getStackDepth() > 0) {
23252421 topItem = getStackItem(0);
2422 }
23262423
23272424 int numberArguments = PreorderVisitor.getNumberArguments(signature);
23282425
23292426 if (boxedTypes.containsKey(clsName)
23302427 && topItem != null
2331 && (methodName.equals("valueOf") && !signature.contains("String") || methodName.equals(boxedTypes.get(clsName)
2428 && ("valueOf".equals(methodName) && !signature.contains("String") || methodName.equals(boxedTypes.get(clsName)
23322429 + "Value"))) {
23332430 // boxing/unboxing conversion
23342431 Item value = pop();
2335 String newSignature = Type.getReturnType(signature).getSignature();
2432 String newSignature = new SignatureParser(signature).getReturnTypeSignature();
23362433 Item newValue = new Item(value, newSignature);
2337 if (newValue.source == null)
2434 if (newValue.source == null) {
23382435 newValue.source = XFactory.createReferencedXMethod(dbc);
2436 }
23392437 if (newValue.specialKind == Item.NOT_SPECIAL) {
2340 if (newSignature.equals("B") || newSignature.equals("Ljava/lang/Boolean;"))
2438 if ("B".equals(newSignature) || "Ljava/lang/Boolean;".equals(newSignature)) {
23412439 newValue.specialKind = Item.SIGNED_BYTE;
2342 else if (newSignature.equals("C") || newSignature.equals("Ljava/lang/Character;"))
2440 } else if ("C".equals(newSignature) || "Ljava/lang/Character;".equals(newSignature)) {
23432441 newValue.specialKind = Item.NON_NEGATIVE;
2442 }
23442443 }
23452444 push(newValue);
23462445 return;
23482447
23492448 int firstArgument = seen == INVOKESTATIC ? 0 : 1;
23502449 for (int i = firstArgument; i < firstArgument + numberArguments; i++) {
2351 if (i >= getStackDepth())
2352 break;
2450 if (i >= getStackDepth()) {
2451 break;
2452 }
23532453 Item item = getStackItem(i);
23542454 String itemSignature = item.getSignature();
2355 if (itemSignature.equals("Ljava/lang/StringBuilder;") || itemSignature.equals("Ljava/lang/StringBuffer;"))
2455 if ("Ljava/lang/StringBuilder;".equals(itemSignature) || "Ljava/lang/StringBuffer;".equals(itemSignature)) {
23562456 markConstantValueUnknown(item);
2457 }
23572458 }
23582459 boolean initializingServletWriter = false;
2359 if (seen == INVOKESPECIAL && methodName.equals("<init>") && clsName.startsWith("java/io") && clsName.endsWith("Writer")
2460 if (seen == INVOKESPECIAL && "<init>".equals(methodName) && clsName.startsWith("java/io") && clsName.endsWith("Writer")
23602461 && numberArguments > 0) {
23612462 Item firstArg = getStackItem(numberArguments-1);
2362 if (firstArg.isServletWriter())
2463 if (firstArg.isServletWriter()) {
23632464 initializingServletWriter = true;
2465 }
23642466 }
23652467 boolean topIsTainted = topItem != null && topItem.isServletParameterTainted();
23662468 HttpParameterInjection injection = null;
2367 if (topIsTainted)
2469 if (topIsTainted) {
23682470 injection = topItem.injection;
2471 }
23692472
23702473 // TODO: stack merging for trinaries kills the constant.. would be nice
23712474 // to maintain.
23742477 if ("(Ljava/lang/String;)V".equals(signature)) {
23752478 Item i = getStackItem(0);
23762479 appenderValue = (String) i.getConstant();
2377 if (i.isServletParameterTainted())
2480 if (i.isServletParameterTainted()) {
23782481 servletRequestParameterTainted = true;
2482 }
23792483 } else if ("()V".equals(signature)) {
23802484 appenderValue = "";
23812485 }
23822486 } else if ("toString".equals(methodName) && getStackDepth() >= 1) {
23832487 Item i = getStackItem(0);
23842488 appenderValue = (String) i.getConstant();
2385 if (i.isServletParameterTainted())
2489 if (i.isServletParameterTainted()) {
23862490 servletRequestParameterTainted = true;
2491 }
23872492 } else if ("append".equals(methodName)) {
23882493 if (signature.indexOf("II)") == -1 && getStackDepth() >= 2) {
23892494 sbItem = getStackItem(1);
23902495 Item i = getStackItem(0);
2391 if (i.isServletParameterTainted() || sbItem.isServletParameterTainted())
2496 if (i.isServletParameterTainted() || sbItem.isServletParameterTainted()) {
23922497 servletRequestParameterTainted = true;
2498 }
23932499 Object sbVal = sbItem.getConstant();
23942500 Object sVal = i.getConstant();
23952501 if ((sbVal != null) && (sVal != null)) {
23962502 appenderValue = sbVal + sVal.toString();
2397 } else
2503 } else {
23982504 markConstantValueUnknown(sbItem);
2505 }
23992506 } else if (signature.startsWith("([CII)")) {
24002507 sawUnknownAppend = true;
24012508 sbItem = getStackItem(3);
24042511 sawUnknownAppend = true;
24052512 }
24062513 }
2407 } else if (seen == INVOKESPECIAL && clsName.equals("java/io/FileOutputStream") && methodName.equals("<init>")
2408 && (signature.equals("(Ljava/io/File;Z)V") || signature.equals("(Ljava/lang/String;Z)V")) && stack.size() > 3) {
2514 } else if (seen == INVOKESPECIAL && "java/io/FileOutputStream".equals(clsName) && "<init>".equals(methodName)
2515 && ("(Ljava/io/File;Z)V".equals(signature) || "(Ljava/lang/String;Z)V".equals(signature)) && stack.size() > 3) {
24092516 OpcodeStack.Item item = getStackItem(0);
24102517 Object value = item.getConstant();
24112518 if (value instanceof Integer && ((Integer) value).intValue() == 1) {
24122519 pop(3);
24132520 Item newTop = getStackItem(0);
2414 if (newTop.signature.equals("Ljava/io/FileOutputStream;")) {
2521 if ("Ljava/io/FileOutputStream;".equals(newTop.signature)) {
24152522 newTop.setSpecialKind(Item.FILE_OPENED_IN_APPEND_MODE);
24162523 newTop.source = XFactory.createReferencedXMethod(dbc);
24172524 newTop.setPC(dbc.getPC());
24182525 }
24192526 return;
24202527 }
2421 } else if (seen == INVOKESPECIAL && clsName.equals("java/io/BufferedOutputStream") && methodName.equals("<init>")
2422 && signature.equals("(Ljava/io/OutputStream;)V")) {
2528 } else if (seen == INVOKESPECIAL && "java/io/BufferedOutputStream".equals(clsName) && "<init>".equals(methodName)
2529 && "(Ljava/io/OutputStream;)V".equals(signature)) {
24232530
24242531 if (getStackItem(0).getSpecialKind() == Item.FILE_OPENED_IN_APPEND_MODE
2425 && getStackItem(2).signature.equals("Ljava/io/BufferedOutputStream;")) {
2532 && "Ljava/io/BufferedOutputStream;".equals(getStackItem(2).signature)) {
24262533
24272534 pop(2);
24282535 Item newTop = getStackItem(0);
24312538 newTop.setPC(dbc.getPC());
24322539 return;
24332540 }
2434 } else if (seen == INVOKEINTERFACE && methodName.equals("getParameter")
2435 && clsName.equals("javax/servlet/http/HttpServletRequest") || clsName.equals("javax/servlet/http/ServletRequest")) {
2541 } else if (seen == INVOKEINTERFACE && "getParameter".equals(methodName)
2542 && "javax/servlet/http/HttpServletRequest".equals(clsName) || "javax/servlet/http/ServletRequest".equals(clsName)) {
24362543 Item requestParameter = pop();
24372544 pop();
24382545 Item result = new Item("Ljava/lang/String;");
24392546 result.setServletParameterTainted();
24402547 result.source = XFactory.createReferencedXMethod(dbc);
24412548 String parameterName = null;
2442 if (requestParameter.getConstant() instanceof String)
2549 if (requestParameter.getConstant() instanceof String) {
24432550 parameterName = (String) requestParameter.getConstant();
2551 }
24442552
24452553 result.injection = new HttpParameterInjection(parameterName, dbc.getPC());
24462554 result.setPC(dbc.getPC());
24472555 push(result);
24482556 return;
2449 } else if (seen == INVOKEINTERFACE && methodName.equals("getQueryString")
2450 && clsName.equals("javax/servlet/http/HttpServletRequest") || clsName.equals("javax/servlet/http/ServletRequest")) {
2557 } else if (seen == INVOKEINTERFACE && "getQueryString".equals(methodName)
2558 && "javax/servlet/http/HttpServletRequest".equals(clsName) || "javax/servlet/http/ServletRequest".equals(clsName)) {
24512559 pop();
24522560 Item result = new Item("Ljava/lang/String;");
24532561 result.setServletParameterTainted();
24552563 result.setPC(dbc.getPC());
24562564 push(result);
24572565 return;
2458 } else if (seen == INVOKEINTERFACE && methodName.equals("getHeader")
2459 && clsName.equals("javax/servlet/http/HttpServletRequest") || clsName.equals("javax/servlet/http/ServletRequest")) {
2566 } else if (seen == INVOKEINTERFACE && "getHeader".equals(methodName)
2567 && "javax/servlet/http/HttpServletRequest".equals(clsName) || "javax/servlet/http/ServletRequest".equals(clsName)) {
24602568 /* Item requestParameter = */pop();
24612569 pop();
24622570 Item result = new Item("Ljava/lang/String;");
24652573 result.setPC(dbc.getPC());
24662574 push(result);
24672575 return;
2468 } else if (seen == INVOKESTATIC && methodName.equals("asList") && clsName.equals("java/util/Arrays")) {
2576 } else if (seen == INVOKESTATIC && "asList".equals(methodName) && "java/util/Arrays".equals(clsName)) {
24692577 /* Item requestParameter = */pop();
24702578 Item result = new Item(JAVA_UTIL_ARRAYS_ARRAY_LIST);
24712579 push(result);
24722580 return;
2473 } else if (seen == INVOKESTATIC && signature.equals("(Ljava/util/List;)Ljava/util/List;")
2474 && clsName.equals("java/util/Collections")) {
2581 } else if (seen == INVOKESTATIC && "(Ljava/util/List;)Ljava/util/List;".equals(signature)
2582 && "java/util/Collections".equals(clsName)) {
24752583 Item requestParameter = pop();
2476 if (requestParameter.getSignature().equals(JAVA_UTIL_ARRAYS_ARRAY_LIST)) {
2584 if (JAVA_UTIL_ARRAYS_ARRAY_LIST.equals(requestParameter.getSignature())) {
24772585 Item result = new Item(JAVA_UTIL_ARRAYS_ARRAY_LIST);
24782586 push(result);
24792587 return;
24832591
24842592 pushByInvoke(dbc, seen != INVOKESTATIC);
24852593
2486 if (initializingServletWriter)
2594 if (sbItem != null && sbItem.isNewlyAllocated()) {
2595 this.getStackItem(0).setSpecialKind(Item.NEWLY_ALLOCATED);
2596 }
2597
2598 if (initializingServletWriter) {
24872599 this.getStackItem(0).setIsServletWriter();
2600 }
24882601
24892602 if ((sawUnknownAppend || appenderValue != null || servletRequestParameterTainted) && getStackDepth() > 0) {
24902603 Item i = this.getStackItem(0);
24962609 if (sbItem != null) {
24972610 i.registerNumber = sbItem.registerNumber;
24982611 i.source = sbItem.source;
2499 if (i.injection == null)
2612 if (i.injection == null) {
25002613 i.injection = sbItem.injection;
2501 if (sbItem.registerNumber >= 0)
2614 }
2615 if (sbItem.registerNumber >= 0) {
25022616 setLVValue(sbItem.registerNumber, i);
2617 }
25032618 }
25042619 return;
25052620 }
25062621
2507 if ((clsName.equals("java/util/Random") || clsName.equals("java/security/SecureRandom")) &&
2508 (methodName.equals("nextInt") && signature.equals("()I")
2509 || methodName.equals("nextLong") && signature.equals("()J"))
2622 if (("java/util/Random".equals(clsName) || "java/security/SecureRandom".equals(clsName)) &&
2623 ("nextInt".equals(methodName) && "()I".equals(signature)
2624 || "nextLong".equals(methodName) && "()J".equals(signature))
25102625 ) {
25112626 Item i = new Item(pop());
25122627 i.setSpecialKind(Item.RANDOM_INT);
25132628 push(i);
2514 } else if (methodName.equals("size") && signature.equals("()I")
2629 } else if ("size".equals(methodName) && "()I".equals(signature)
25152630 && Subtypes2.instanceOf(ClassName.toDottedClassName(clsName), "java.util.Collection")) {
25162631 Item i = new Item(pop());
2517 if (i.getSpecialKind() == Item.NOT_SPECIAL)
2518 i.setSpecialKind(Item.NON_NEGATIVE);
2632 if (i.getSpecialKind() == Item.NOT_SPECIAL) {
2633 i.setSpecialKind(Item.NON_NEGATIVE);
2634 }
25192635 push(i);
2520 } else if (ClassName.isMathClass(clsName) && methodName.equals("abs")) {
2636 } else if("java/lang/String".equals(clsName) && numberArguments == 0 && topItem.getConstant() instanceof String) {
2637 String input = (String) topItem.getConstant();
2638 Object result;
2639 switch(methodName) {
2640 case "length":
2641 result = input.length();
2642 break;
2643 case "trim":
2644 result = input.trim();
2645 break;
2646 case "toString":
2647 case "intern":
2648 result = input;
2649 break;
2650 default:
2651 result = null;
2652 }
2653 if(result != null) {
2654 Item i = new Item(pop());
2655 i.constValue = result;
2656 push(i);
2657 }
2658 } else if (ClassName.isMathClass(clsName) && "abs".equals(methodName)) {
25212659 Item i = new Item(pop());
2522 if (i.getSpecialKind() == Item.HASHCODE_INT)
2660 if (i.getSpecialKind() == Item.HASHCODE_INT) {
25232661 i.setSpecialKind(Item.MATH_ABS_OF_HASHCODE);
2524 else if (i.getSpecialKind() == Item.RANDOM_INT)
2662 } else if (i.getSpecialKind() == Item.RANDOM_INT) {
25252663 i.setSpecialKind(Item.MATH_ABS_OF_RANDOM);
2526 else
2664 } else {
25272665 i.setSpecialKind(Item.MATH_ABS);
2666 }
25282667 push(i);
2529 } else if (seen == INVOKEVIRTUAL && methodName.equals("hashCode") && signature.equals("()I") || seen == INVOKESTATIC
2530 && clsName.equals("java/lang/System") && methodName.equals("identityHashCode")
2531 && signature.equals("(Ljava/lang/Object;)I")) {
2668 } else if (seen == INVOKEVIRTUAL && "hashCode".equals(methodName) && "()I".equals(signature) || seen == INVOKESTATIC
2669 && "java/lang/System".equals(clsName) && "identityHashCode".equals(methodName)
2670 && "(Ljava/lang/Object;)I".equals(signature)) {
25322671 Item i = new Item(pop());
25332672 i.setSpecialKind(Item.HASHCODE_INT);
25342673 push(i);
25352674 } else if (topIsTainted
2536 && (methodName.startsWith("encode") && clsName.equals("javax/servlet/http/HttpServletResponse") || methodName
2537 .equals("trim") && clsName.equals("java/lang/String"))) {
2675 && (methodName.startsWith("encode") && "javax/servlet/http/HttpServletResponse".equals(clsName) || "trim".equals(methodName) && "java/lang/String".equals(clsName))) {
25382676 Item i = new Item(pop());
25392677 i.setSpecialKind(Item.SERVLET_REQUEST_TAINTED);
25402678 i.injection = injection;
25492687
25502688 }
25512689
2552 boolean foobar = false;
2690 private void processInvokeDynamic(DismantleBytecode dbc) {
2691 String signature = dbc.getSigConstantOperand();
2692
2693 int numberArguments = PreorderVisitor.getNumberArguments(signature);
2694
2695 pop(numberArguments);
2696 pushBySignature(new SignatureParser(signature).getReturnTypeSignature(), dbc);
2697 }
2698
25532699 private boolean mergeLists(List<Item> mergeInto, List<Item> mergeFrom, boolean errorIfSizesDoNotMatch) {
25542700 // merge stacks
25552701 int intoSize = mergeInto.size();
25682714 System.out.println("current items: " + mergeInto);
25692715 System.out.println("jump items: " + mergeFrom);
25702716 }
2571
2717
25722718 }
25732719
25742720 List<Item> mergeIntoCopy = null;
2575 if (DEBUG2)
2721 if (DEBUG2) {
25762722 mergeIntoCopy = new ArrayList<Item>(mergeInto);
2723 }
25772724 int common = Math.min(intoSize, fromSize);
25782725 for (int i = 0; i < common; i++) {
25792726 Item oldValue = mergeInto.get(i);
25802727 Item newValue = mergeFrom.get(i);
25812728 Item merged = Item.merge(oldValue, newValue);
25822729 if (merged != null && !merged.equals(oldValue)) {
2583 if (foobar)
2584 System.out.println("foobar");
25852730 mergeInto.set(i, merged);
25862731 changed = true;
25872732 }
25882733 }
2734 /*
25892735 if (false) for (int i = common; i < fromSize; i++) {
25902736 Item newValue = mergeFrom.get(i);
2591 if (foobar)
2592 System.out.println("foobar");
25932737 mergeInto.add(newValue);
25942738 changed = true;
25952739
25962740 }
2741 */
25972742 if (DEBUG2 && changed) {
25982743 System.out.println("Merge results:");
25992744 System.out.println("updating: " + mergeIntoCopy);
26092754 lvValues.clear();
26102755 }
26112756
2612 boolean encountedTop;
2613
2614 boolean backwardsBranch;
2615
2616 BitSet exceptionHandlers = new BitSet();
2617
2618 private boolean jumpInfoChangedByBackwardsBranch = false;
2619
2620 private Map<Integer, List<Item>> jumpEntries = new HashMap<Integer, List<Item>>();
2621
2622 private Map<Integer, List<Item>> jumpStackEntries = new HashMap<Integer, List<Item>>();
2623
2624 private BitSet jumpEntryLocations = new BitSet();
2625
26262757 public void printJumpEntries() {
26272758 for(int i=jumpEntryLocations.nextSetBit(0); i>=0; i=jumpEntryLocations.nextSetBit(i+1)) {
26282759 List<Item> stack = jumpStackEntries.get(i);
26292760 List<Item> locals = jumpEntries.get(i);
2630 if (stack != null)
2631 System.out.printf("%4d: %s::%s%n", i, stack, locals);
2632 else
2761 if (stack != null) {
2762 System.out.printf("%4d: %s::%s%n", i, stack, locals);
2763 } else {
26332764 System.out.printf("%4d: ::%s%n", i, locals);
2765 }
26342766 }
26352767 }
26362768
26592791 super("Jump info for opcode stack", JumpInfo.class);
26602792 }
26612793
2794 @Override
26622795 public @CheckForNull JumpInfo analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
26632796 Method method = analysisCache.getMethodAnalysis(Method.class, descriptor);
26642797 JavaClass jclass = getJavaClass(analysisCache, descriptor.getClassDescriptor());
26662799 if (code == null) {
26672800 return null;
26682801 }
2669
2802
26702803 JumpStackComputation branchAnalysis = new JumpStackComputation(descriptor);
2671
2804
26722805 return computeJumpInfo(jclass, method, branchAnalysis);
26732806 }
26742807
2675 static class JumpStackComputation extends BytecodeScanningDetector {
2676
2677 static final boolean DEBUG = false;
2678 final MethodDescriptor descriptor;
2679 private JumpStackComputation(MethodDescriptor descriptor) {
2680 this.descriptor = descriptor;
2681 }
2682
2683 protected OpcodeStack stack = new OpcodeStack();
2684
2685 public OpcodeStack getStack() {
2686 return stack;
2687 }
2688
2689 @Override
2690 public final void visitCode(Code obj) {
2691 if (!getMethodDescriptor().equals(descriptor))
2692 throw new IllegalStateException();
2693 if (DEBUG) System.out.println(descriptor);
2694 stack.resetForMethodEntry0(this);
2695 super.visitCode(obj);
2696 if (DEBUG) System.out.println();
2697 }
2698
2699 @Override
2808 static class JumpStackComputation extends BytecodeScanningDetector {
2809
2810 static final boolean DEBUG1 = false;
2811 final MethodDescriptor descriptor;
2812 private JumpStackComputation(MethodDescriptor descriptor) {
2813 this.descriptor = descriptor;
2814 }
2815
2816 protected OpcodeStack stack = new OpcodeStack();
2817
2818 public OpcodeStack getStack() {
2819 return stack;
2820 }
2821
2822 @Override
2823 public final void visitCode(Code obj) {
2824 if (!getMethodDescriptor().equals(descriptor)) {
2825 throw new IllegalStateException();
2826 }
2827 if (DEBUG1) {
2828 System.out.println(descriptor);
2829 }
2830 stack.resetForMethodEntry0(this);
2831 super.visitCode(obj);
2832 if (DEBUG1) {
2833 System.out.println();
2834 }
2835 }
2836
2837 @Override
27002838 public void sawOpcode(int seen) {
2701 stack.precomputation(this);
2702
2703 if (DEBUG) System.out.printf("%4d %-15s %s%n", getPC(), OPCODE_NAMES[seen], stack);
2704 try {
2705 stack.sawOpcode(this, seen);
2706 } catch (RuntimeException e) {
2707 throw e;
2708 }
2709 }
2710 }
2711
2712 /**
2713 * @param jclass
2714 * @param method
2715 * @param stack
2716 * @param branchAnalysis
2717 * @return
2718 */
2839 stack.precomputation(this);
2840
2841 if (DEBUG1) {
2842 System.out.printf("%4d %-15s %s%n", getPC(), OPCODE_NAMES[seen], stack);
2843 }
2844 try {
2845 stack.sawOpcode(this, seen);
2846 } catch (RuntimeException e) {
2847 throw e;
2848 }
2849 }
2850 }
2851
27192852 public static @CheckForNull JumpInfo computeJumpInfo(JavaClass jclass, Method method,
27202853 JumpStackComputation branchAnalysis) {
27212854 branchAnalysis.setupVisitorForClass(jclass);
27222855 XMethod createXMethod = XFactory.createXMethod(jclass, method);
2723 if (!(createXMethod instanceof MethodInfo))
2856 if (!(createXMethod instanceof MethodInfo)) {
27242857 return null;
2858 }
27252859 MethodInfo xMethod = (MethodInfo) createXMethod;
2726
2860
27272861 int iteration = 1;
27282862 OpcodeStack myStack = branchAnalysis.stack;
2729 if (false)
2863 /*
2864 if (false) {
27302865 myStack.learnFrom(myStack.getJumpInfoFromStackMap());
2866 }
2867 */
27312868 do {
27322869 if (DEBUG && iteration > 1 ) {
27332870 System.out.println("Iterative jump info for " + xMethod +", iteration " + iteration);
27412878 String.format("For %s, mismatch on existence of backedge: %s for precomputation, %s for bytecode analysis",
27422879 xMethod, xMethod.hasBackBranch(), myStack.backwardsBranch));
27432880 }
2881 if (myStack.isJumpInfoChangedByNewTarget()) {
2882 if (DEBUG) {
2883 System.out.println("new target found, resetting iteration count");
2884 }
2885 iteration = 1;
2886 }
27442887 if (iteration++ > 40) {
2745 AnalysisContext.logError("Iterative jump info didn't converge after " + iteration + " iterations in " + xMethod
2746 + ", size " + method.getCode().getLength());
2888 AnalysisContext.logError("Iterative jump info didn't converge after " + iteration + " iterations in " + xMethod
2889 + ", size " + method.getCode().getLength());
27472890 break;
27482891 }
27492892 } while (myStack.isJumpInfoChangedByBackwardsBranch() && myStack.backwardsBranch);
27602903 }
27612904
27622905 private void addJumpValue(int from, int target) {
2763 if (DEBUG)
2906 if (DEBUG) {
27642907 System.out.println("Set jump entry at " + methodName + ":" + target + " pc to " + stack + " : " + lvValues);
2765
2766 if (from >= target)
2908 }
2909
2910 if (from >= target) {
27672911 backwardsBranch = true;
2912 }
27682913 List<Item> atTarget = jumpEntries.get(Integer.valueOf(target));
27692914 if (atTarget == null) {
27702915 setJumpInfoChangedByBackwardBranch("new target", from, target);
2916 setJumpInfoChangedByNewTarget();
27712917 jumpEntries.put(Integer.valueOf(target), new ArrayList<Item>(lvValues));
27722918 jumpEntryLocations.set(target);
27732919 if (stack.size() > 0) {
27742920 jumpStackEntries.put(Integer.valueOf(target), new ArrayList<Item>(stack));
27752921 }
27762922 } else {
2777 if (mergeLists(atTarget, lvValues, false))
2778 setJumpInfoChangedByBackwardBranch("locals", from, target);
2779 List<Item> stackAtTarget = jumpStackEntries.get(Integer.valueOf(target));
2780 if (stack.size() > 0 && stackAtTarget != null)
2781 if (mergeLists(stackAtTarget, stack, false))
2782 setJumpInfoChangedByBackwardBranch("stack", from, target);
2783 }
2784 if (DEBUG)
2785 System.out.println("merge target for " + methodName + ":" + target + " pc is " + atTarget);
2923 if (mergeLists(atTarget, lvValues, false)) {
2924 setJumpInfoChangedByBackwardBranch("locals", from, target);
2925 }
2926 List<Item> stackAtTarget = jumpStackEntries.get(Integer.valueOf(target));
2927 if (stack.size() > 0 && stackAtTarget != null) {
2928 if (mergeLists(stackAtTarget, stack, false)) {
2929 setJumpInfoChangedByBackwardBranch("stack", from, target);
2930 }
2931 }
2932 }
2933
27862934 }
27872935
27882936 private String methodName;
27902938 DismantleBytecode v;
27912939
27922940 public void learnFrom(JumpInfo info) {
2793 if (info == null)
2941 if (info == null) {
27942942 return;
2943 }
27952944 jumpEntries = new HashMap<Integer, List<Item>>(info.jumpEntries);
27962945 jumpStackEntries = new HashMap<Integer, List<Item>>(info.jumpStackEntries);
27972946 jumpEntryLocations = (BitSet) info.jumpEntryLocations.clone();
28172966
28182967 int result = resetForMethodEntry0(v);
28192968 Code code = v.getMethod().getCode();
2820 if (code == null)
2969 if (code == null) {
28212970 return result;
2971 }
28222972 JumpInfo jump = null;
28232973 if (useIterativeAnalysis) {
28242974 if (visitor instanceof OpcodeStackDetector.WithCustomJumpInfo) {
28372987 }
28382988
28392989 int nullSafeSize(@CheckForNull Collection<?> c) {
2840 if (c == null)
2990 if (c == null) {
28412991 return 0;
2992 }
28422993 return c.size();
28432994 }
2844
2995
28452996 private JumpInfo getJumpInfo() {
28462997 IAnalysisCache analysisCache = Global.getAnalysisCache();
28472998 XMethod xMethod = XFactory.createXMethod(v.getThisClass(), v.getMethod());
28482999 if (xMethod instanceof MethodInfo) {
28493000 MethodInfo mi = (MethodInfo) xMethod;
2850 if (!mi.hasBackBranch())
3001 if (!mi.hasBackBranch()) {
28513002 return null;
3003 }
28523004 }
28533005 try {
28543006 return analysisCache.getMethodAnalysis(JumpInfo.class, xMethod.getMethodDescriptor());
28623014 XMethod xMethod = XFactory.createXMethod(v.getThisClass(), v.getMethod());
28633015 if (xMethod instanceof MethodInfo) {
28643016 MethodInfo mi = (MethodInfo) xMethod;
2865 if (!mi.hasBackBranch())
3017 if (!mi.hasBackBranch()) {
28663018 return null;
2867 }
2868
3019 }
3020 }
3021
28693022 try {
28703023 return analysisCache.getMethodAnalysis(JumpInfoFromStackMap.class, xMethod.getMethodDescriptor());
2871
3024
28723025 } catch (CheckedAnalysisException e) {
28733026 AnalysisContext.logError("Error getting jump information from StackMap", e);
28743027 return null;
28763029 }
28773030
28783031 public void setJumpInfoChangedByBackwardBranch(String kind, int from, int to) {
2879 if (from < to)
3032 if (from < to) {
28803033 return ;
2881
2882
2883 if (DEBUG && !this.isJumpInfoChangedByBackwardsBranch())
3034 }
3035
3036
3037 if (DEBUG && !this.isJumpInfoChangedByBackwardsBranch()) {
28843038 System.out.printf("%s jump info at %d changed by jump from %d%n", kind, to, from);
3039 }
28853040 this.setJumpInfoChangedByBackwardsBranch(from,to);
28863041 return ;
28873042 }
28903045 return resetForMethodEntry0(visitor.getClassName(), visitor.getMethod());
28913046 }
28923047
2893 int resetForMethodEntry0(@SlashedClassName String className, Method m) {
3048 int resetForMethodEntry0(@SlashedClassName String className, Method m) {
28943049 methodName = m.getName();
28953050
2896 if (DEBUG)
3051 if (DEBUG) {
28973052 System.out.println(" --- ");
3053 }
28983054 String signature = m.getSignature();
28993055 stack.clear();
29003056 lvValues.clear();
29023058 encountedTop = false;
29033059 backwardsBranch = false;
29043060 clearJumpInfoChangedByBackwardsBranch();
3061 clearJumpInfoChangedByNewTarget();
29053062
29063063 setReachOnlyByBranch(false);
29073064 seenTransferOfControl = false;
29093066 Code code = m.getCode();
29103067 if (code != null) {
29113068 CodeException[] exceptionTable = code.getExceptionTable();
2912 if (exceptionTable != null)
2913 for (CodeException ex : exceptionTable)
3069 if (exceptionTable != null) {
3070 for (CodeException ex : exceptionTable) {
29143071 exceptionHandlers.set(ex.getHandlerPC());
2915 }
2916 if (DEBUG)
3072 }
3073 }
3074 }
3075 if (DEBUG) {
29173076 System.out.println(" --- " + className + " " + m.getName() + " " + signature);
3077 }
29183078 Type[] argTypes = Type.getArgumentTypes(signature);
29193079 int reg = 0;
29203080 if (!m.isStatic()) {
29383098 if (stackOffset < 0 || stackOffset >= stack.size()) {
29393099 AnalysisContext.logError("Can't get stack offset " + stackOffset + " from " + stack.toString() + " @ " + v.getPC()
29403100 + " in " + v.getFullyQualifiedMethodName(), new IllegalArgumentException(stackOffset
2941 + " is not a value stack offset"));
3101 + " is not a value stack offset"));
29423102 return new Item("Lfindbugs/OpcodeStackError;");
29433103
29443104 }
29573117 }
29583118
29593119 public void replace(int stackOffset, Item value) {
2960 if (stackOffset < 0 || stackOffset >= stack.size()) {
2961 AnalysisContext.logError("Can't get replace stack offset " + stackOffset + " from " + stack.toString() + " @ " + v.getPC()
2962 + " in " + v.getFullyQualifiedMethodName(), new IllegalArgumentException(stackOffset
2963 + " is not a value stack offset"));
2964
2965 }
2966 int tos = stack.size() - 1;
2967 int pos = tos - stackOffset;
2968
2969 stack.set(pos, value);
2970
2971 }
3120 if (stackOffset < 0 || stackOffset >= stack.size()) {
3121 AnalysisContext.logError("Can't get replace stack offset " + stackOffset + " from " + stack.toString() + " @ " + v.getPC()
3122 + " in " + v.getFullyQualifiedMethodName(), new IllegalArgumentException(stackOffset
3123 + " is not a value stack offset"));
3124
3125 }
3126 int tos = stack.size() - 1;
3127 int pos = tos - stackOffset;
3128
3129 stack.set(pos, value);
3130
3131 }
29723132
29733133 public void replaceTop(Item newTop) {
29743134 pop();
29763136 }
29773137
29783138 private void pop(int count) {
2979 while ((count--) > 0)
3139 while ((count--) > 0) {
29803140 pop();
3141 }
29813142 }
29823143
29833144 private void push(Item i) {
29863147
29873148 private void pushByConstant(DismantleBytecode dbc, Constant c) {
29883149
2989 if (c instanceof ConstantClass)
3150 if (c instanceof ConstantClass) {
29903151 push(new Item("Ljava/lang/Class;", ((ConstantClass) c).getConstantValue(dbc.getConstantPool())));
2991 else if (c instanceof ConstantInteger)
3152 } else if (c instanceof ConstantInteger) {
29923153 push(new Item("I", Integer.valueOf(((ConstantInteger) c).getBytes())));
2993 else if (c instanceof ConstantString) {
3154 } else if (c instanceof ConstantString) {
29943155 int s = ((ConstantString) c).getStringIndex();
29953156 push(new Item("Ljava/lang/String;", getStringFromIndex(dbc, s)));
2996 } else if (c instanceof ConstantFloat)
3157 } else if (c instanceof ConstantFloat) {
29973158 push(new Item("F", Float.valueOf(((ConstantFloat) c).getBytes())));
2998 else if (c instanceof ConstantDouble)
3159 } else if (c instanceof ConstantDouble) {
29993160 push(new Item("D", Double.valueOf(((ConstantDouble) c).getBytes())));
3000 else if (c instanceof ConstantLong)
3161 } else if (c instanceof ConstantLong) {
30013162 push(new Item("J", Long.valueOf(((ConstantLong) c).getBytes())));
3002 else
3163 } else {
30033164 throw new UnsupportedOperationException("StaticConstant type not expected");
3165 }
30043166 }
30053167
30063168 private void pushByLocalObjectLoad(DismantleBytecode dbc, int register) {
30253187 }
30263188
30273189 try {
3028 if (DEBUG)
3190 if (DEBUG) {
30293191 System.out.println("pushByIntMath " + dbc.getFullyQualifiedMethodName() + " @ " + dbc.getPC() + " : " + lhs
30303192 + OPCODE_NAMES[seen] + rhs);
3193 }
30313194
30323195 if (rhs.getConstant() != null && lhs.getConstant() != null) {
30333196 int lhsValue = constantToInt(lhs);
30553218 break;
30563219 case IAND:
30573220 newValue = new Item("I", lhsValue & rhsValue);
3058 if ((rhsValue & 0xff) == 0 && rhsValue != 0 || (lhsValue & 0xff) == 0 && lhsValue != 0)
3221 if ((rhsValue & 0xff) == 0 && rhsValue != 0 || (lhsValue & 0xff) == 0 && lhsValue != 0) {
30593222 newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
3223 }
30603224
30613225 break;
30623226 case IOR:
30673231 break;
30683232 case ISHL:
30693233 newValue = new Item("I", lhsValue << rhsValue);
3070 if (rhsValue >= 8)
3234 if (rhsValue >= 8) {
30713235 newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
3236 }
30723237
30733238 break;
30743239 case ISHR:
30833248 } else if ((seen == ISHL || seen == ISHR || seen == IUSHR)) {
30843249 if (rhs.getConstant() != null) {
30853250 int constant = constantToInt(rhs);
3086 if ((constant & 0x1f) == 0)
3251 if ((constant & 0x1f) == 0) {
30873252 newValue = new Item(lhs);
3088 else if (seen == ISHL && (constant & 0x1f) >= 8)
3253 } else if (seen == ISHL && (constant & 0x1f) >= 8) {
30893254 newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
3255 }
30903256 } else if (lhs.getConstant() != null) {
30913257 int constant = constantToInt(lhs);
3092 if (constant == 0)
3258 if (constant == 0) {
30933259 newValue = new Item("I", 0);
3260 }
30943261 }
30953262 } else if (lhs.getConstant() != null && seen == IAND) {
30963263 int value = constantToInt(lhs);
3097 if (value == 0)
3264 if (value == 0) {
30983265 newValue = new Item("I", 0);
3099 else if ((value & 0xff) == 0)
3266 } else if ((value & 0xff) == 0) {
31003267 newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
3101 else if (value >= 0)
3268 } else if (value >= 0) {
31023269 newValue.setSpecialKind(Item.NON_NEGATIVE);
3270 }
31033271 } else if (rhs.getConstant() != null && seen == IAND) {
31043272 int value = constantToInt(rhs);
3105 if (value == 0)
3273 if (value == 0) {
31063274 newValue = new Item("I", 0);
3107 else if ((value & 0xff) == 0)
3275 } else if ((value & 0xff) == 0) {
31083276 newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
3109 else if (value >= 0)
3277 } else if (value >= 0) {
31103278 newValue.setSpecialKind(Item.NON_NEGATIVE);
3279 }
31113280 } else if (seen == IAND && lhs.getSpecialKind() == Item.ZERO_MEANS_NULL) {
31123281 newValue.setSpecialKind(Item.ZERO_MEANS_NULL);
31133282 newValue.setPC(lhs.getPC());
31313300 }
31323301 if (lhs.getSpecialKind() == Item.INTEGER_SUM && rhs.getConstant() != null) {
31333302 int rhsValue = constantToInt(rhs);
3134 if (seen == IDIV && rhsValue == 2 || seen == ISHR && rhsValue == 1)
3303 if (seen == IDIV && rhsValue == 2 || seen == ISHR && rhsValue == 1) {
31353304 newValue.setSpecialKind(Item.AVERAGE_COMPUTED_USING_DIVISION);
3305 }
31363306 }
31373307 if (seen == IADD && newValue.getSpecialKind() == Item.NOT_SPECIAL && lhs.getConstant() == null
3138 && rhs.getConstant() == null)
3308 && rhs.getConstant() == null) {
31393309 newValue.setSpecialKind(Item.INTEGER_SUM);
3140 if (seen == IREM && lhs.getSpecialKind() == Item.HASHCODE_INT)
3310 }
3311 if (seen == IREM && lhs.getSpecialKind() == Item.HASHCODE_INT) {
31413312 newValue.setSpecialKind(Item.HASHCODE_INT_REMAINDER);
3142 if (seen == IREM && lhs.getSpecialKind() == Item.RANDOM_INT)
3313 }
3314 if (seen == IREM && lhs.getSpecialKind() == Item.RANDOM_INT) {
31433315 newValue.setSpecialKind(Item.RANDOM_INT_REMAINDER);
3316 }
31443317 if (seen == IREM && lhs.checkForIntegerMinValue()) {
31453318 if (rhs.getConstant() != null) {
31463319 int rhsValue = constantToInt(rhs);
3147 if (!Util.isPowerOfTwo(rhsValue))
3320 if (!Util.isPowerOfTwo(rhsValue)) {
31483321 newValue.setSpecialKind(lhs.getSpecialKindForRemainder());
3149 } else
3322 }
3323 } else {
31503324 newValue.setSpecialKind(lhs.getSpecialKindForRemainder());
3151 }
3152 if (DEBUG)
3325 }
3326 }
3327 if (DEBUG) {
31533328 System.out.println("push: " + newValue);
3329 }
31543330 newValue.setPC(dbc.getPC());
31553331 push(newValue);
31563332 }
31643340 long lhsValue = constantToLong(lhs);
31653341 if (seen == LSHL) {
31663342 newValue = new Item("J", Long.valueOf(lhsValue << constantToInt(rhs)));
3167 if (constantToInt(rhs) >= 8)
3343 if (constantToInt(rhs) >= 8) {
31683344 newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
3169 } else if (seen == LSHR)
3345 }
3346 } else if (seen == LSHR) {
31703347 newValue = new Item("J", Long.valueOf(lhsValue >> constantToInt(rhs)));
3171 else if (seen == LUSHR)
3348 } else if (seen == LUSHR) {
31723349 newValue = new Item("J", Long.valueOf(lhsValue >>> constantToInt(rhs)));
3173
3174 else {
3350 } else {
31753351 long rhsValue = constantToLong(rhs);
3176 if (seen == LADD)
3352 if (seen == LADD) {
31773353 newValue = new Item("J", Long.valueOf(lhsValue + rhsValue));
3178 else if (seen == LSUB)
3354 } else if (seen == LSUB) {
31793355 newValue = new Item("J", Long.valueOf(lhsValue - rhsValue));
3180 else if (seen == LMUL)
3356 } else if (seen == LMUL) {
31813357 newValue = new Item("J", Long.valueOf(lhsValue * rhsValue));
3182 else if (seen == LDIV)
3358 } else if (seen == LDIV) {
31833359 newValue = new Item("J", Long.valueOf(lhsValue / rhsValue));
3184 else if (seen == LAND) {
3360 } else if (seen == LAND) {
31853361 newValue = new Item("J", Long.valueOf(lhsValue & rhsValue));
3186 if ((rhsValue & 0xff) == 0 && rhsValue != 0 || (lhsValue & 0xff) == 0 && lhsValue != 0)
3362 if ((rhsValue & 0xff) == 0 && rhsValue != 0 || (lhsValue & 0xff) == 0 && lhsValue != 0) {
31873363 newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
3188 } else if (seen == LOR)
3364 }
3365 } else if (seen == LOR) {
31893366 newValue = new Item("J", Long.valueOf(lhsValue | rhsValue));
3190 else if (seen == LXOR)
3367 } else if (seen == LXOR) {
31913368 newValue = new Item("J", Long.valueOf(lhsValue ^ rhsValue));
3192 else if (seen == LREM)
3369 } else if (seen == LREM) {
31933370 newValue = new Item("J", Long.valueOf(lhsValue % rhsValue));
3194 }
3195 } else if (rhs.getConstant() != null && seen == LSHL && constantToInt(rhs) >= 8)
3371 }
3372 }
3373 } else if (rhs.getConstant() != null && seen == LSHL && constantToInt(rhs) >= 8) {
31963374 newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
3197 else if (lhs.getConstant() != null && seen == LAND && (constantToLong(lhs) & 0xff) == 0)
3375 } else if (lhs.getConstant() != null && seen == LAND && (constantToLong(lhs) & 0xff) == 0) {
31983376 newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
3199 else if (rhs.getConstant() != null && seen == LAND && (constantToLong(rhs) & 0xff) == 0)
3377 } else if (rhs.getConstant() != null && seen == LAND && (constantToLong(rhs) & 0xff) == 0) {
32003378 newValue.setSpecialKind(Item.LOW_8_BITS_CLEAR);
3379 }
32013380 } catch (RuntimeException e) {
32023381 // ignore it
32033382 }
32083387 Item result;
32093388 @SpecialKind int specialKind = Item.FLOAT_MATH;
32103389 if ((it.getConstant() instanceof Float) && it2.getConstant() instanceof Float) {
3211 if (seen == FADD)
3390 if (seen == FADD) {
32123391 result = new Item("F", Float.valueOf(constantToFloat(it2) + constantToFloat(it)));
3213 else if (seen == FSUB)
3392 } else if (seen == FSUB) {
32143393 result = new Item("F", Float.valueOf(constantToFloat(it2) - constantToFloat(it)));
3215 else if (seen == FMUL)
3394 } else if (seen == FMUL) {
32163395 result = new Item("F", Float.valueOf(constantToFloat(it2) * constantToFloat(it)));
3217 else if (seen == FDIV)
3396 } else if (seen == FDIV) {
32183397 result = new Item("F", Float.valueOf(constantToFloat(it2) / constantToFloat(it)));
3219 else if (seen == FREM)
3398 } else if (seen == FREM) {
32203399 result = new Item("F", Float.valueOf(constantToFloat(it2) % constantToFloat(it)));
3221 else
3400 } else {
32223401 result = new Item("F");
3402 }
32233403 } else {
32243404 result = new Item("F");
3225 if (seen == DDIV)
3405 if (seen == DDIV) {
32263406 specialKind = Item.NASTY_FLOAT_MATH;
3407 }
32273408 }
32283409 result.setSpecialKind(specialKind);
32293410 push(result);
32333414 Item result;
32343415 @SpecialKind int specialKind = Item.FLOAT_MATH;
32353416 if ((it.getConstant() instanceof Double) && it2.getConstant() instanceof Double) {
3236 if (seen == DADD)
3417 if (seen == DADD) {
32373418 result = new Item("D", Double.valueOf(constantToDouble(it2) + constantToDouble(it)));
3238 else if (seen == DSUB)
3419 } else if (seen == DSUB) {
32393420 result = new Item("D", Double.valueOf(constantToDouble(it2) - constantToDouble(it)));
3240 else if (seen == DMUL)
3421 } else if (seen == DMUL) {
32413422 result = new Item("D", Double.valueOf(constantToDouble(it2) * constantToDouble(it)));
3242 else if (seen == DDIV)
3423 } else if (seen == DDIV) {
32433424 result = new Item("D", Double.valueOf(constantToDouble(it2) / constantToDouble(it)));
3244 else if (seen == DREM)
3425 } else if (seen == DREM) {
32453426 result = new Item("D", Double.valueOf(constantToDouble(it2) % constantToDouble(it)));
3246 else
3427 }
3428 else {
32473429 result = new Item("D"); // ?
3430 }
32483431 } else {
32493432 result = new Item("D");
3250 if (seen == DDIV)
3433 if (seen == DDIV) {
32513434 specialKind = Item.NASTY_FLOAT_MATH;
3435 }
32523436 }
32533437 result.setSpecialKind(specialKind);
32543438 push(result);
32563440
32573441 private void pushByInvoke(DismantleBytecode dbc, boolean popThis) {
32583442 String signature = dbc.getSigConstantOperand();
3259 if (dbc.getNameConstantOperand().equals("<init>") && signature.endsWith(")V") && popThis) {
3443 if ("<init>".equals(dbc.getNameConstantOperand()) && signature.endsWith(")V") && popThis) {
32603444 pop(PreorderVisitor.getNumberArguments(signature));
32613445 Item constructed = pop();
32623446 if (getStackDepth() > 0) {
32713455 return;
32723456 }
32733457 pop(PreorderVisitor.getNumberArguments(signature) + (popThis ? 1 : 0));
3274 pushBySignature(Type.getReturnType(signature).getSignature(), dbc);
3458 pushBySignature(new SignatureParser(signature).getReturnTypeSignature(), dbc);
32753459 }
32763460
32773461 public Item getItemMethodInvokedOn(DismantleBytecode dbc) {
32943478 }
32953479
32963480 private void pushBySignature(String s, DismantleBytecode dbc) {
3297 if ("V".equals(s))
3481 if ("V".equals(s)) {
32983482 return;
3483 }
32993484 Item item = new Item(s, (Object) null);
3300 if (dbc != null)
3485 if (dbc != null) {
33013486 item.setPC(dbc.getPC());
3302 if ("B".equals(s))
3487 }
3488 if ("B".equals(s)) {
33033489 item.setSpecialKind(Item.SIGNED_BYTE);
3304 else if ("C".equals(s))
3490 } else if ("C".equals(s)) {
33053491 item.setSpecialKind(Item.NON_NEGATIVE);
3492 }
33063493 push(item);
33073494 }
33083495
33093496 private void pushByLocalStore(int register) {
33103497 Item it = new Item(pop());
33113498 if (it.getRegisterNumber() != register) {
3312 for (Item i : lvValues)
3313 if (i != null) {
3314 if (i.registerNumber == register)
3315 i.registerNumber = -1;
3316 if (i.fieldLoadedFromRegister == register)
3317 i.fieldLoadedFromRegister = -1;
3318 }
3319 for (Item i : stack)
3320 if (i != null) {
3321 if (i.registerNumber == register)
3322 i.registerNumber = -1;
3323 if (i.fieldLoadedFromRegister == register)
3324 i.fieldLoadedFromRegister = -1;
3325 }
3326 }
3327 if (it.registerNumber == -1)
3499 clearRegisterLoad(lvValues, register);
3500 clearRegisterLoad(stack, register);
3501 }
3502 if (it.registerNumber == -1) {
33283503 it.registerNumber = register;
3504 }
33293505 setLVValue(register, it);
3506 }
3507
3508 private static void clearRegisterLoad(List<Item> list, int register) {
3509 for (int pos=0; pos<list.size(); pos++) {
3510 Item i = list.get(pos);
3511 if(i != null && (i.registerNumber == register || i.fieldLoadedFromRegister == register)) {
3512 i = new Item(i);
3513 if (i.registerNumber == register) {
3514 i.registerNumber = -1;
3515 }
3516 if (i.fieldLoadedFromRegister == register) {
3517 i.fieldLoadedFromRegister = -1;
3518 }
3519 list.set(pos, i);
3520 }
3521 }
33303522 }
33313523
33323524 private void pushByLocalLoad(String signature, int register) {
33333525 Item oldItem = new Item(getLVValue(register));
33343526
33353527 Item newItem = oldItem;
3336 if (newItem.signature.equals("Ljava/lang/Object;") && !signature.equals("Ljava/lang/Object;")) {
3528 if ("Ljava/lang/Object;".equals(newItem.signature) && !"Ljava/lang/Object;".equals(signature)) {
33373529 newItem = new Item(oldItem);
33383530 newItem.signature = signature;
33393531 }
33403532 if (newItem.getRegisterNumber() < 0) {
3341 if (newItem == oldItem)
3533 if (newItem == oldItem) {
33423534 newItem = new Item(oldItem);
3535 }
33433536 newItem.registerNumber = register;
33443537 }
33453538
33493542
33503543 private void setLVValue(int index, Item value) {
33513544 int addCount = index - lvValues.size() + 1;
3352 while ((addCount--) > 0)
3545 while ((addCount--) > 0) {
33533546 lvValues.add(null);
3354 if (!useIterativeAnalysis && seenTransferOfControl)
3547 }
3548 if (!useIterativeAnalysis && seenTransferOfControl) {
33553549 value = Item.merge(value, lvValues.get(index));
3550 }
33563551 lvValues.set(index, value);
33573552 }
33583553
3554 @Nonnull
33593555 public Item getLVValue(int index) {
3360 if (index >= lvValues.size())
3556 if (index >= lvValues.size()) {
33613557 return new Item();
3558 }
33623559
33633560 Item item = lvValues.get(index);
3364 if (item != null)
3561 if (item != null) {
33653562 return item;
3563 }
33663564
33673565 return new Item();
33683566 }
33713569 return lvValues.size();
33723570 }
33733571
3374 /**
3375 * @param top
3376 * The top to set.
3377 */
33783572 private void setTop(boolean top) {
33793573 if (top) {
3380 if (!this.top)
3574 if (!this.top) {
33813575 this.top = true;
3382 } else if (this.top)
3576 }
3577 } else if (this.top) {
33833578 this.top = false;
3384 }
3385
3386 /**
3387 * @return Returns the top.
3388 */
3579 }
3580 }
3581
33893582 public boolean isTop() {
3390 if (top)
3583 if (top) {
33913584 return true;
3585 }
33923586 return false;
33933587 }
33943588
3395 /**
3396 * @param reachOnlyByBranch
3397 * The reachOnlyByBranch to set.
3398 */
33993589 void setReachOnlyByBranch(boolean reachOnlyByBranch) {
3400 if (reachOnlyByBranch)
3590 if (reachOnlyByBranch) {
34013591 setTop(true);
3592 }
34023593 this.reachOnlyByBranch = reachOnlyByBranch;
34033594 }
34043595
3405 /**
3406 * @return Returns the reachOnlyByBranch.
3407 */
34083596 boolean isReachOnlyByBranch() {
34093597 return reachOnlyByBranch;
34103598 }
34113599
3412 /**
3413 * @return Returns the jumpInfoChangedByBackwardsBranch.
3414 */
34153600 boolean isJumpInfoChangedByBackwardsBranch() {
34163601 return jumpInfoChangedByBackwardsBranch;
34173602 }
34203605 void clearJumpInfoChangedByBackwardsBranch() {
34213606 this.jumpInfoChangedByBackwardsBranch = false;
34223607 }
3423 /**
3424 * @param jumpInfoChangedByBackwardsBranch The jumpInfoChangedByBackwardsBranch to set.
3425 */
3608
34263609 void setJumpInfoChangedByBackwardsBranch(int from, int to) {
34273610 this.jumpInfoChangedByBackwardsBranch = true;
34283611 }
3612
3613 /**
3614 * @return Returns the jumpInfoChangedByNewTarget.
3615 */
3616 protected boolean isJumpInfoChangedByNewTarget() {
3617 return jumpInfoChangedByNewTarget;
3618 }
3619
3620 void clearJumpInfoChangedByNewTarget() {
3621 this.jumpInfoChangedByNewTarget = false;
3622 }
3623
3624 protected void setJumpInfoChangedByNewTarget() {
3625 this.jumpInfoChangedByNewTarget = true;
3626 }
34293627 }
3430
3431 // vim:ts=4
5454 private static String computeSourceFile(String className) {
5555 AnalysisContext context = AnalysisContext.currentAnalysisContext();
5656
57 if (context != null)
57 if (context != null) {
5858 return context.lookupSourceFile(className);
59 }
5960 return SourceLineAnnotation.UNKNOWN_SOURCE_FILE;
6061
6162 }
6768 * name of the class
6869 */
6970 protected PackageMemberAnnotation(@DottedClassName String className, String description, String sourceFileName) {
70 if (className.length() == 0)
71 if (className.length() == 0) {
7172 throw new IllegalArgumentException("Empty classname not allowed");
73 }
7274 if (className.indexOf('/') >= 0) {
7375 assert false : "classname " + className + " should be dotted";
74 className = className.replace('/', '.');
76 className = className.replace('/', '.');
7577 }
7678 this.className = DescriptorFactory.canonicalizeString(className);
7779 this.sourceFileName = sourceFileName;
78 if (description != null)
80 if (description != null) {
7981 description = description.intern();
82 }
8083 this.description = description;
8184 }
8285
113116 public final @DottedClassName
114117 String getPackageName() {
115118 int lastDot = className.lastIndexOf('.');
116 if (lastDot < 0)
119 if (lastDot < 0) {
117120 return "";
118 else
121 } else {
119122 return className.substring(0, lastDot);
123 }
120124 }
121125
122126 /**
128132 * the key
129133 * @return the formatted annotation
130134 */
135 @Override
131136 public final String format(String key, ClassAnnotation primaryClass) {
132 if (key.equals("class.givenClass"))
137 if ("class.givenClass".equals(key)) {
133138 return shorten(primaryClass.getPackageName(), className);
134 if (key.equals("simpleClass"))
139 }
140 if ("simpleClass".equals(key)) {
135141 return ClassName.extractSimpleName(className);
136 if (key.equals("class"))
142 }
143 if ("class".equals(key)) {
137144 return className;
138 if (key.equals("package"))
145 }
146 if ("package".equals(key)) {
139147 return getPackageName();
140 if (key.equals("") && FindBugsDisplayFeatures.isAbridgedMessages() && primaryClass != null)
148 }
149 if ("".equals(key) && FindBugsDisplayFeatures.isAbridgedMessages() && primaryClass != null) {
141150 return formatPackageMember("givenClass", primaryClass);
151 }
142152 return formatPackageMember(key, primaryClass);
143153 }
144154
155 @Override
145156 public void setDescription(String description) {
146157 this.description = description.intern();
147158 }
148159
160 @Override
149161 public String getDescription() {
150162 return description;
151163 }
159171 int index = typeName.lastIndexOf('.');
160172 if (index >= 0) {
161173 String otherPkg = typeName.substring(0, index);
162 if (otherPkg.equals(pkgName) || otherPkg.equals("java.lang"))
174 if (otherPkg.equals(pkgName) || "java.lang".equals(otherPkg)) {
163175 typeName = typeName.substring(index + 1);
176 }
164177 }
165178 return typeName;
166179 }
212225 return format.format(new BugAnnotation[] { this }, primaryClass);
213226 }
214227
228 @Override
215229 public boolean isSignificant() {
216230 return true;
217231 }
218232
219233 }
220234
221 // vim:ts=4
4343
4444 class BugCounts {
4545 protected int[] nBugs;
46
46
4747 @OverridingMethodsMustInvokeSuper
4848 public void addError(BugInstance bug) {
4949 ensureNonnullBugCounts();
5050 ++nBugs[bug.getPriority()];
5151 ++nBugs[0];
5252 }
53
53
5454 protected void ensureNonnullBugCounts() {
55 if (nBugs == null)
55 if (nBugs == null) {
5656 nBugs = new int[] { 0, 0, 0, 0, 0 };
57
57 }
58
5859 }
5960 public final int getTotalBugs() {
60 if (nBugs == null)
61 if (nBugs == null) {
6162 return 0;
63 }
6264 return nBugs[0];
6365 }
6466
6567 public final int getBugsAtPriority(int p) {
66 if (nBugs == null)
68 if (nBugs == null) {
6769 return 0;
70 }
6871 return nBugs[p];
6972 }
70
73
7174 public void clearBugCounts() {
7275 nBugs = null;
7376
7477 }
75
78
7679 /**
7780 * Add priority attributes to a started tag. Each priority at offset n,
7881 * where n &gt; 0, is output using attribute priority_n if the value at
8184 * @param xmlOutput
8285 * an output stream for which startTag has been called but
8386 * stopTag has not.
84 * @param bugs
85 * an array for which the element at offset n is the number of
86 * bugs for priority n.
8787 */
8888 public void writeBugPriorities(XMLOutput xmlOutput) throws IOException {
89 if (nBugs == null)
89 if (nBugs == null) {
9090 return;
91 }
9192 writeBugPriorities(xmlOutput, nBugs);
9293 }
9394 public static void writeBugPriorities(XMLOutput xmlOutput, @Nonnull int nBugs[]) throws IOException {
102103
103104 public class PackageStats extends BugCounts implements XMLWriteable {
104105
105
106
106107 public static class ClassStats extends BugCounts implements XMLWriteable, Cloneable {
107108 private final String name;
108109
148149 return sourceFile;
149150 }
150151
152 @Override
151153 public void writeXML(XMLOutput xmlOutput) throws IOException {
152 if (size == 0)
154 if (size == 0) {
153155 return;
156 }
154157 xmlOutput.startTag("ClassStats");
155158
156159 xmlOutput.addAttribute("class", name);
157 if (sourceFile != null)
160 if (sourceFile != null) {
158161 xmlOutput.addAttribute("sourceFile", sourceFile);
162 }
159163 xmlOutput.addAttribute("interface", String.valueOf(isInterface));
160164 xmlOutput.addAttribute("size", String.valueOf(size));
161165 xmlOutput.addAttribute("bugs", String.valueOf(getTotalBugs()));
167171 /**
168172 *
169173 */
170
174
171175 }
172176
173177 public static final String ELEMENT_NAME = "PackageStats";
189193 // LinkedList<BugInstance>();
190194
191195 // all classes and interfaces in this package
192 private Map<String, ClassStats> packageMembers = new HashMap<String, ClassStats>(5);
196 private final Map<String, ClassStats> packageMembers = new HashMap<String, ClassStats>(5);
193197
194198 public PackageStats(String packageName) {
195199 this.packageName = packageName;
260264 this.numClasses = 0;
261265 }
262266 packageMembers.put(classStats.getName(), classStats);
263 if (updatePackageStats)
267 if (updatePackageStats) {
264268 size += classStats.size();
269 }
265270 }
266271
267272 public String getPackageName() {
276281 this.numClasses = numClasses;
277282 }
278283
284 @Override
279285 public void writeXML(XMLOutput xmlOutput) throws IOException {
280 if (size == 0)
286 if (size == 0) {
281287 return;
288 }
282289
283290 xmlOutput.startTag(ELEMENT_NAME);
284291
285292 xmlOutput.addAttribute("package", packageName);
286293 xmlOutput.addAttribute("total_bugs", String.valueOf(getTotalBugs()));
287294 int numClasses = packageMembers.size();
288 if (numClasses == 0)
295 if (numClasses == 0) {
289296 numClasses = this.numClasses;
297 }
290298 xmlOutput.addAttribute("total_types", String.valueOf(numClasses));
291299 xmlOutput.addAttribute("total_size", String.valueOf(size));
292300 writeBugPriorities(xmlOutput);
306314
307315 }
308316
309
317
310318
311319 public void recomputeFromClassStats() {
312320 super.clearBugCounts();
314322 numClasses = packageMembers.size();
315323 ensureNonnullBugCounts();
316324 for (ClassStats classStats : packageMembers.values()) {
317 for (int i = 0; i < nBugs.length; i++)
325 for (int i = 0; i < nBugs.length; i++) {
318326 nBugs[i] += classStats.getBugsAtPriority(i);
327 }
319328 size += classStats.size;
320329 }
321330 }
339348 public void purgeClassesThatDontMatch(Pattern classPattern) {
340349 for (Iterator<Map.Entry<String, ClassStats>> i = packageMembers.entrySet().iterator(); i.hasNext();) {
341350 Map.Entry<String, ClassStats> e = i.next();
342 if (!classPattern.matcher(e.getKey()).find())
351 if (!classPattern.matcher(e.getKey()).find()) {
343352 i.remove();
353 }
344354 }
345355 }
346356 }
347357
348 // vim:ts=4
1515 @Override
1616 public boolean match(BugInstance bugInstance) {
1717
18 if (!super.match(bugInstance))
18 if (!super.match(bugInstance)) {
1919 return false;
20 }
2021
2122 ClassAnnotation primaryClassAnnotation = bugInstance.getPrimaryClass();
22 if (DEBUG)
23 if (DEBUG) {
2324 System.out.println("Compare " + primaryClassAnnotation + " with " + packageName);
25 }
2426
2527 String className = primaryClassAnnotation.getClassName();
2628
1414 @Override
1515 public boolean match(BugInstance bugInstance) {
1616
17 if (!super.match(bugInstance))
17 if (!super.match(bugInstance)) {
1818 return false;
19 }
1920
2021 MethodAnnotation bugMethod = bugInstance.getPrimaryMethod();
2122 LocalVariableAnnotation localVariable = bugInstance.getPrimaryLocalVariableAnnotation();
22 if (bugMethod == null || !method.equals(bugMethod))
23 if (bugMethod == null || !method.equals(bugMethod)) {
2324 return false;
24 if (localVariable == null || localVariable.getRegister() != register)
25 }
26 if (localVariable == null || localVariable.getRegister() != register) {
2527 return false;
26 if (DEBUG)
28 }
29 if (DEBUG) {
2730 System.out.println("Suppressing " + bugInstance);
31 }
2832 return true;
2933 }
3034 }
5757
5858
5959 private static final String USE_FINDBUGS_VERSION = "USE_FINDBUGS_VERSION";
60 static Map<URI, Plugin> allPlugins = new LinkedHashMap<URI, Plugin>();
6061 private final String pluginId;
6162
6263 private final String version;
8081 private final LinkedHashMap<String, BugCategory> bugCategories;
8182 private final LinkedHashSet<CloudPlugin> cloudList;
8283
83 private final HashMap<String,String> myGlobalOptions = new HashMap<String, String>();
84 private final HashMap<String,String> myGlobalOptions;
8485
8586 private final DualKeyHashMap<Class<?>, String, ComponentPlugin<?>> componentPlugins;
8687
101102
102103 private final boolean cannotDisable;
103104
104 static Map<URI, Plugin> allPlugins = new LinkedHashMap<URI, Plugin>();
105
106 enum EnabledState { PLUGIN_DEFAULT, ENABLED, DISABLED}
105 static enum EnabledState { PLUGIN_DEFAULT, ENABLED, DISABLED }
107106
108107 private EnabledState enabled;
109
110108
111109 /**
112110 * Constructor. Creates an empty plugin object.
113111 *
114112 * @param pluginId
115113 * the plugin's unique identifier
116 * @param version TODO
117 * @param enabled TODO
118 * @param cannotDisable TODO
119114 */
120115 public Plugin(String pluginId, String version, Date releaseDate, @Nonnull PluginLoader pluginLoader, boolean enabled, boolean cannotDisable) {
121116 this.pluginId = pluginId;
122117 if (version == null) {
123118 version = "";
124 } else if (version.equals(USE_FINDBUGS_VERSION)) {
119 } else if (USE_FINDBUGS_VERSION.equals(version)) {
125120 version = Version.COMPUTED_RELEASE;
126121 releaseDate = Version.getReleaseDate();
127122 }
128123 assert enabled || !cannotDisable;
124 myGlobalOptions = new HashMap<String, String>();
129125 cloudList = new LinkedHashSet<CloudPlugin>();
130126 componentPlugins = new DualKeyHashMap<Class<?>, String, ComponentPlugin<?>> ();
131127 this.version = version;
176172 return provider;
177173 }
178174
179
180175 public void setUpdateUrl(String url) throws URISyntaxException {
181176 this.updateUrl = new URI(url);
182177 }
192187 Map<String,String> getMyGlobalOptions() {
193188 return Collections.unmodifiableMap(myGlobalOptions);
194189 }
190
195191 /**
196192 * Set plugin website.
197193 *
209205 * @return the website, or null if the was not specified
210206 */
211207 public @CheckForNull String getWebsite() {
212 if (website == null)
208 if (website == null) {
213209 return null;
210 }
214211 return website.toASCIIString();
215212 }
216213
281278 */
282279 public void addBugCategory(BugCategory bugCategory) {
283280 BugCategory old = bugCategories.get(bugCategory.getCategory());
284 if (old != null)
281 if (old != null) {
285282 throw new IllegalArgumentException("Category already exists");
283 }
286284 bugCategories.put(bugCategory.getCategory(), bugCategory);
287285 }
288
289286
290287 public BugCategory addOrCreateBugCategory(String id) {
291288 BugCategory c = bugCategories.get(id);
292 if (c != null)
289 if (c != null) {
293290 return c;
291 }
294292 c = new BugCategory(id);
295293 bugCategories.put(id, c);
296294 return c;
297295 }
296
298297 /**
299298 * Add an inter-pass Detector ordering constraint.
300299 *
324323 */
325324 public DetectorFactory getFactoryByShortName(final String shortName) {
326325 return findFirstMatchingFactory(new FactoryChooser() {
326 @Override
327327 public boolean choose(DetectorFactory factory) {
328328 return factory.getShortName().equals(shortName);
329329 }
339339 */
340340 public DetectorFactory getFactoryByFullName(final String fullName) {
341341 return findFirstMatchingFactory(new FactoryChooser() {
342 @Override
342343 public boolean choose(DetectorFactory factory) {
343344 return factory.getFullName().equals(fullName);
344345 }
370371 public Set<BugCode> getBugCodes() {
371372 return bugCodeList;
372373 }
374
373375 /**
374376 * Get Iterator over BugCategories objects in the Plugin.
375377 *
391393 public Set<CloudPlugin> getCloudPlugins() {
392394 return cloudList;
393395 }
396
394397 /**
395398 * Return an Iterator over the inter-pass Detector ordering constraints.
396399 */
405408 return intraPassConstraintList.iterator();
406409 }
407410
408 /**
409 * @return Returns the pluginId.
410 */
411411 public String getPluginId() {
412412 return pluginId;
413413 }
414 /**
415 * @return Returns the short pluginId.
416 */
414
417415 public String getShortPluginId() {
418416 int i = pluginId.lastIndexOf('.');
419417 return pluginId.substring(i+1);
439437 return engineRegistrarClass;
440438 }
441439
442 /**
443 * @return Returns the pluginLoader.
444 */
445440 public PluginLoader getPluginLoader() {
446441 return pluginLoader;
447442 }
453448 private @CheckForNull
454449 DetectorFactory findFirstMatchingFactory(FactoryChooser chooser) {
455450 for (DetectorFactory factory : getDetectorFactories()) {
456 if (chooser.choose(factory))
451 if (chooser.choose(factory)) {
457452 return factory;
453 }
458454 }
459455 return null;
460456 }
461457
462 /**
463 * @param ranker
464 */
465458 public void setBugRanker(BugRanker ranker) {
466459 this.bugRanker = ranker;
467460 }
488481
489482 <T> void addComponentPlugin(Class<T> componentKind, ComponentPlugin<T> plugin) {
490483 Class<? extends T> componentClass = plugin.getComponentClass();
491 if (componentClass != null && !componentKind.isAssignableFrom(componentClass))
492 throw new IllegalArgumentException();
484 if (componentClass != null && !componentKind.isAssignableFrom(componentClass)) {
485 throw new IllegalArgumentException();
486 }
493487 componentPlugins.put(componentKind, plugin.getId(), plugin);
494488 }
495489
511505 }
512506 for(Plugin plugin : allPlugins.values()) {
513507 // the second part is questionable, as this may lead to id collisions
514 if (name.equals(plugin.getPluginId()) /*|| name.equals(plugin.getShortPluginId())*/)
508 if (name.equals(plugin.getPluginId()) /*|| name.equals(plugin.getShortPluginId())*/) {
515509 return plugin;
510 }
516511 }
517512 return null;
518513 }
519514
520515 public static synchronized void removePlugin(URI uri) {
521 allPlugins.remove(uri);
516 allPlugins.remove(uri);
522517 }
523518
524519 /**
529524 }
530525
531526 public static synchronized Collection<String> getAllPluginIds() {
532 ArrayList<String> result = new ArrayList<String>();
533 for(Plugin p : allPlugins.values())
534 result.add(p.getPluginId());
535 return result;
536 }
527 ArrayList<String> result = new ArrayList<String>();
528 for(Plugin p : allPlugins.values()) {
529 result.add(p.getPluginId());
530 }
531 return result;
532 }
533
537534 /**
538535 * @return a copy of the internal plugins collection
539536 */
548545
549546 try {
550547 URI uri = plugin.getPluginLoader().getURL().toURI();
551 if(uri != null) {
552 uris.add(uri);
553 }
548 uris.add(uri);
554549 } catch (URISyntaxException e) {
555550 AnalysisContext.logError("Unable to get URI", e);
556551 }
584579 }
585580
586581 public boolean isGloballyEnabled() {
587 if (isCorePlugin())
582 if (isCorePlugin()) {
588583 return true;
584 }
589585 switch (enabled) {
590586 case ENABLED:
591587 return true;
600596
601597 public void setGloballyEnabled(boolean enabled) {
602598 if (isCorePlugin()) {
603 if (!enabled)
599 if (!enabled) {
604600 throw new IllegalArgumentException("Can't disable core plugin");
601 }
605602 return;
606603 }
607604 if (cannotDisable) {
608 if (enabled) return;
605 if (enabled) {
606 return;
607 }
609608 throw new IllegalArgumentException("Cannot disable " + pluginId);
610609 }
611 EnabledState oldState = this.enabled;
610 // EnabledState oldState = this.enabled;
612611
613612 if (enabled) {
614 if (isEnabledByDefault())
613 if (isEnabledByDefault()) {
615614 this.enabled = EnabledState.PLUGIN_DEFAULT;
616 else
615 } else {
617616 this.enabled = EnabledState.ENABLED;
617 }
618618 } else {
619 if (isEnabledByDefault())
619 if (isEnabledByDefault()) {
620620 this.enabled = EnabledState.DISABLED;
621 else
621 } else {
622622 this.enabled = EnabledState.PLUGIN_DEFAULT;
623 }
624 if(oldState != this.enabled) {
625 // TODO update detector factory collection?
626 }
623 }
624 }
625 // if(oldState != this.enabled) {
626 // TODO update detector factory collection?
627 // }
627628 }
628629
629630 public boolean isInitialPlugin() {
639640 }
640641
641642 public @CheckForNull Plugin getParentPlugin() {
642 if (getPluginLoader().hasParent())
643 if (getPluginLoader().hasParent()) {
643644 return Plugin.getByPluginId(getPluginLoader().parentId);
645 }
644646 return null;
645647 }
646648
672674 public static @CheckForNull Plugin addCustomPlugin(URL u) throws PluginException {
673675 return addCustomPlugin(u, PluginLoader.class.getClassLoader());
674676 }
677
675678 public static @CheckForNull Plugin addCustomPlugin(URI u) throws PluginException {
676679 return addCustomPlugin(u, PluginLoader.class.getClassLoader());
677680 }
681
678682 public static @CheckForNull Plugin addCustomPlugin(URL u, ClassLoader parent) throws PluginException {
679683 PluginLoader pluginLoader = PluginLoader.getPluginLoader(u, parent, false, true);
680684 Plugin plugin = pluginLoader.loadPlugin();
681 if (plugin != null)
685 if (plugin != null) {
682686 DetectorFactoryCollection.instance().loadPlugin(plugin);
683
687 }
684688 return plugin;
685689 }
686690
693697 }
694698 return addCustomPlugin(url, parent);
695699 }
700
696701 public static synchronized void removeCustomPlugin(Plugin plugin) {
697702 Set<Entry<URI, Plugin>> entrySet = Plugin.allPlugins.entrySet();
698703 for (Entry<URI, Plugin> entry : entrySet) {
2020
2121 /**
2222 * An exception to indicate that a plugin could not be loaded.
23 *
23 *
2424 * @author David Hovemeyer
2525 * @see PluginLoader
2626 */
3232
3333 /**
3434 * Constructor.
35 *
35 *
3636 * @param msg
3737 * message describing the exception
3838 */
4242
4343 /**
4444 * Constructor.
45 *
45 *
4646 * @param msg
4747 * message describing the exception
4848 * @param cause
5353 }
5454 }
5555
56 // vim:ts=4
211211 if (!hasParent()) {
212212 classLoader = new URLClassLoader(loaderURLs, parent);
213213 } else {
214 if (parent != PluginLoader.class.getClassLoader())
214 if (parent != PluginLoader.class.getClassLoader()) {
215215 throw new IllegalArgumentException("Can't specify parentid " + parentId + " and provide a seperate class loader");
216 }
216217 Plugin parentPlugin = Plugin.getByPluginId(parentId);
217218 if (parentPlugin != null) {
218219 parent = parentPlugin.getClassLoader();
220221 }
221222 }
222223 if (classLoader == null) {
223 if (!lazyInitialization)
224 if (!lazyInitialization) {
224225 throw new IllegalStateException("Can't find parent plugin " + parentId);
226 }
225227 partiallyInitialized.add(this);
226228 } else {
227229 loadPluginComponents();
230232 }
231233
232234 private static void finishLazyInitialization() {
233 if (!lazyInitialization)
235 if (!lazyInitialization) {
234236 throw new IllegalStateException("Not in lazy initialization mode");
237 }
235238 while (!partiallyInitialized.isEmpty()) {
236239 boolean changed = false;
237240 LinkedList<String> unresolved = new LinkedList<String>();
344347 String classPath = atts.getValue(Attributes.Name.CLASS_PATH);
345348 if (classPath != null) {
346349 String jarRoot = url.toString();
347 jarRoot = jarRoot.substring(0, jarRoot.lastIndexOf("/") + 1);
350 jarRoot = jarRoot.substring(0, jarRoot.lastIndexOf('/') + 1);
348351 String[] jars = classPath.split(",");
349352 for (String jar : jars) {
350353 jar = jarRoot + jar.trim();
423426 URL from;
424427 String findBugsClassFile = ClassName.toSlashedClassName(FindBugs.class) + ".class";
425428 URL me = FindBugs.class.getClassLoader().getResource(findBugsClassFile);
426 if (DEBUG)
429 if (DEBUG) {
427430 System.out.println("FindBugs.class loaded from " + me);
431 }
428432 if(me == null) {
429433 throw new IllegalStateException("Failed to load " + findBugsClassFile);
430434 }
445449 } catch (MalformedURLException e) {
446450 throw new IllegalArgumentException("Failed to parse url: " + me);
447451 }
448 if (DEBUG)
452 if (DEBUG) {
449453 System.out.println("Core class files loaded from " + from);
454 }
450455 return from;
451456 }
452457
468473
469474 private static String getJarName(URL url) {
470475 String location = url.getPath();
471 int i = location.lastIndexOf("/");
476 int i = location.lastIndexOf('/');
472477 location = location.substring(i + 1);
473478 return location;
474479 }
475480
476481 public ClassLoader getClassLoader() {
477 if (classLoader == null)
482 if (classLoader == null) {
478483 throw new IllegalStateException("Plugin not completely initialized; classloader not set yet");
484 }
479485 return classLoader;
480486 }
481487
490496 }
491497
492498 public Plugin getPlugin() {
493 if (plugin == null)
499 if (plugin == null) {
494500 throw new AssertionError("plugin not already loaded");
501 }
495502
496503 return plugin;
497504 }
523530 public URL getResource(String name) {
524531 if (isCorePlugin()) {
525532 URL url = getCoreResource(name);
526 if (url != null && IO.verifyURL(url))
533 if (url != null && IO.verifyURL(url)) {
527534 return url;
535 }
528536 }
529537 if (loadedFrom != null) {
530538 try {
532540 if (DEBUG) {
533541 System.out.println("Trying to load " + name + " from " + url);
534542 }
535 if (IO.verifyURL(url))
543 if (IO.verifyURL(url)) {
536544 return url;
545 }
537546 } catch (MalformedURLException e) {
538547 assert true;
539548 }
548557 System.out.println(" from urls: " + Arrays.asList(urlClassLoader.getURLs()));
549558 }
550559 URL url = urlClassLoader.findResource(name);
551 if (url == null)
560 if (url == null) {
552561 url = urlClassLoader.findResource("/" + name);
553 if (IO.verifyURL(url))
562 }
563 if (IO.verifyURL(url)) {
554564 return url;
565 }
555566 }
556567
557568 if (DEBUG) {
558569 System.out.println("Trying to load " + name + " using ClassLoader.getResource");
559570 }
560571 URL url = classLoaderForResources.getResource(name);
561 if (url == null)
572 if (url == null) {
562573 url = classLoaderForResources.getResource("/" + name);
563 if (IO.verifyURL(url))
574 }
575 if (IO.verifyURL(url)) {
564576 return url;
577 }
565578
566579 return null;
567580 }
569582 static @CheckForNull
570583 URL getCoreResource(String name) {
571584 URL u = loadFromFindBugsPluginDir(name);
572 if (u != null)
585 if (u != null) {
573586 return u;
587 }
574588 u = loadFromFindBugsEtcDir(name);
575 if (u != null)
589 if (u != null) {
576590 return u;
591 }
577592 u = PluginLoader.class.getResource(name);
578 if (u != null)
593 if (u != null) {
579594 return u;
595 }
580596 u = PluginLoader.class.getResource("/"+name);
581597 return u;
582598 }
628644 }
629645
630646 private Plugin init() throws PluginException {
631 if (DEBUG)
647 if (DEBUG) {
632648 System.out.println("Loading plugin from " + loadedFrom);
649 }
633650 // Plugin descriptor (a.k.a, "findbugs.xml"). Defines
634651 // the bug detectors and bug patterns that the plugin provides.
635652 Document pluginDescriptor = getPluginDescriptor();
638655 Plugin constructedPlugin = constructMinimalPlugin(pluginDescriptor, messageCollectionList);
639656
640657 // Success!
641 if (DEBUG)
658 if (DEBUG) {
642659 System.out.println("Loaded " + constructedPlugin.getPluginId() + " from " + loadedFrom);
660 }
643661 return constructedPlugin;
644662 }
645663
656674 boolean onlineStorage = Boolean.valueOf(cloudNode.valueOf("@onlineStorage"));
657675 String propertiesLocation = cloudNode.valueOf("@properties");
658676 boolean disabled = Boolean.valueOf(cloudNode.valueOf("@disabled")) && !cloudId.equals(CloudFactory.DEFAULT_CLOUD);
659 if (disabled)
677 if (disabled) {
660678 continue;
679 }
661680 boolean hidden = Boolean.valueOf(cloudNode.valueOf("@hidden")) && !cloudId.equals(CloudFactory.DEFAULT_CLOUD);
662681
663682 Class<? extends Cloud> cloudClass = getClass(classLoader, cloudClassname, Cloud.class);
670689 PropertyBundle properties = new PropertyBundle();
671690 if (propertiesLocation != null && propertiesLocation.length() > 0) {
672691 URL properiesURL = classLoader.getResource(propertiesLocation);
673 if (properiesURL == null)
692 if (properiesURL == null) {
674693 continue;
694 }
675695 properties.loadPropertiesFromURL(properiesURL);
676696 }
677697 List<Node> propertyNodes = XMLUtil.selectNodes(cloudNode, "Property");
702722 + " loaded from " + loadedFrom);
703723 }
704724 String componentId = componentNode.valueOf("@id");
705 if (componentId == null) throw new PluginException("Missing @id for " + plugin.getPluginId()
706 + " loaded from " + loadedFrom);
725 if (componentId == null) {
726 throw new PluginException("Missing @id for " + plugin.getPluginId()
727 + " loaded from " + loadedFrom);
728 }
707729
708730 try {
709731 String propertiesLocation = componentNode.valueOf("@properties");
799821 throw new PluginException("Class " + className + " does not implement Detector or Detector2");
800822 }
801823 }
802 DetectorFactory factory = new DetectorFactory(plugin, className, detectorClass, !disabled.equals("true"), speed,
824 DetectorFactory factory = new DetectorFactory(plugin, className, detectorClass, !"true".equals(disabled), speed,
803825 reports, requireJRE);
804826 if (Boolean.valueOf(hidden).booleanValue()) {
805827 factory.setHidden(true);
843865 constraint.setSingleSource(earlierSelector instanceof SingleDetectorFactorySelector);
844866
845867 // Add the constraint to the plugin
846 if (constraintElement.getName().equals("SplitPass")) {
868 if ("SplitPass".equals(constraintElement.getName())) {
847869 plugin.addInterPassOrderingConstraint(constraint);
848870 } else {
849871 plugin.addIntraPassOrderingConstraint(constraint);
856878 List<Node> categoryNodeListGlobal = XMLUtil.selectNodes(pluginDescriptor, "/FindbugsPlugin/BugCategory");
857879 for(Node categoryNode : categoryNodeListGlobal) {
858880 String key = categoryNode.valueOf("@category");
859 if (key.equals("")) {
881 if ("".equals(key)) {
860882 throw new PluginException("BugCategory element with missing category attribute");
861883 }
862884 BugCategory bc = plugin.addOrCreateBugCategory(key);
870892
871893 for (Document messageCollection : messageCollectionList) {
872894 List<Node> categoryNodeList = XMLUtil.selectNodes(messageCollection, "/MessageCollection/BugCategory");
873 if (DEBUG)
895 if (DEBUG) {
874896 System.out.println("found " + categoryNodeList.size() + " categories in " + plugin.getPluginId());
897 }
875898 for (Node categoryNode : categoryNodeList) {
876899 String key = categoryNode.valueOf("@category");
877 if (key.equals("")) {
900 if ("".equals(key)) {
878901 throw new PluginException("BugCategory element with missing category attribute");
879902 }
880903 BugCategory bc = plugin.addOrCreateBugCategory(key);
884907 String abbrev = getChildText(categoryNode, "Abbreviation");
885908 if (bc.getAbbrev() == null) {
886909 bc.setAbbrev(abbrev);
887 if (DEBUG)
910 if (DEBUG) {
888911 System.out.println("category " + key + " abbrev -> " + abbrev);
889 } else if (DEBUG)
912 }
913 } else if (DEBUG) {
890914 System.out.println("rejected abbrev '" + abbrev + "' for category " + key + ": " + bc.getAbbrev());
915 }
891916 } catch (PluginException pe) {
892917 if (DEBUG)
918 {
893919 System.out.println("missing Abbreviation for category " + key + "/" + shortDesc);
894 // do nothing else -- Abbreviation is required, but handle
895 // its omission gracefully
920 // do nothing else -- Abbreviation is required, but handle
921 // its omission gracefully
922 }
896923 }
897924 try {
898925 String details = getChildText(categoryNode, "Details");
899926 if (bc.getDetailText() == null) {
900927 bc.setDetailText(details);
901 if (DEBUG)
928 if (DEBUG) {
902929 System.out.println("category " + key + " details -> " + details);
903 } else if (DEBUG)
930 }
931 } else if (DEBUG) {
904932 System.out.println("rejected details [" + details + "] for category " + key + ": [" + bc.getDetailText()
905933 + ']');
934 }
906935 } catch (PluginException pe) {
907936 // do nothing -- LongDescription is optional
908937 }
916945 String type = bugPatternNode.valueOf("@type");
917946 String abbrev = bugPatternNode.valueOf("@abbrev");
918947 String category = bugPatternNode.valueOf("@category");
919 String experimental = bugPatternNode.valueOf("@experimental");
948 boolean experimental = Boolean.parseBoolean(bugPatternNode.valueOf("@experimental"));
920949
921950 // Find the matching element in messages.xml (or translations)
922951 String query = "/MessageCollection/BugPattern[@type='" + type + "']";
923952 Node messageNode = findMessageNode(messageCollectionList, query, "messages.xml missing BugPattern element for type "
924953 + type);
954 Node bugsUrlNode = messageNode.getDocument().selectSingleNode("/MessageCollection/Plugin/"+(experimental?"AllBugsUrl":"BugsUrl"));
955
956 String bugsUrl = bugsUrlNode == null ? null : bugsUrlNode.getText();
925957
926958 String shortDesc = getChildText(messageNode, "ShortDescription");
927959 String longDesc = getChildText(messageNode, "LongDescription");
929961 int cweid = 0;
930962 try {
931963 String cweString = bugPatternNode.valueOf("@cweid");
932 if (cweString.length() > 0)
964 if (cweString.length() > 0) {
933965 cweid = Integer.parseInt(cweString);
966 }
934967 } catch (RuntimeException e) {
935968 assert true; // ignore
936969 }
937970
938 BugPattern bugPattern = new BugPattern(type, abbrev, category, Boolean.valueOf(experimental).booleanValue(),
939 shortDesc, longDesc, detailText, cweid);
971 BugPattern bugPattern = new BugPattern(type, abbrev, category, experimental, shortDesc, longDesc, detailText, bugsUrl, cweid);
940972
941973 try {
942974 String deprecatedStr = bugPatternNode.valueOf("@deprecated");
958990 List<Node> bugCodeNodeList = XMLUtil.selectNodes(messageCollection, "/MessageCollection/BugCode");
959991 for (Node bugCodeNode : bugCodeNodeList) {
960992 String abbrev = bugCodeNode.valueOf("@abbrev");
961 if (abbrev.equals("")) {
993 if ("".equals(abbrev)) {
962994 throw new PluginException("BugCode element with missing abbrev attribute");
963995 }
964996 if (definedBugCodes.contains(abbrev)) {
9691001 String query = "/FindbugsPlugin/BugCode[@abbrev='" + abbrev + "']";
9701002 Node fbNode = pluginDescriptor.selectSingleNode(query);
9711003 int cweid = 0;
972 if (fbNode != null)
1004 if (fbNode != null) {
9731005 try {
9741006 cweid = Integer.parseInt(fbNode.valueOf("@cweid"));
9751007 } catch (RuntimeException e) {
9761008 assert true; // ignore
9771009 }
1010 }
9781011 BugCode bugCode = new BugCode(abbrev, description, cweid);
9791012 plugin.addBugCode(bugCode);
9801013 definedBugCodes.add(abbrev);
10111044 // https://sourceforge.net/tracker/?func=detail&aid=2816102&group_id=96405&atid=614693
10121045 // plugin can not have bugrank.txt. In this case, an empty
10131046 // bugranker will be created
1014 if (DEBUG)
1047 if (DEBUG) {
10151048 System.out.println("No " + BugRanker.FILENAME + " for plugin " + plugin.getPluginId());
1049 }
10161050 }
10171051 BugRanker ranker = new BugRanker(bugRankURL);
10181052 plugin.setBugRanker(ranker);
10261060 // Get the unique plugin id (or generate one, if none is present)
10271061 // Unique plugin id
10281062 String pluginId = pluginDescriptor.valueOf(XPATH_PLUGIN_PLUGINID);
1029 if (pluginId.equals("")) {
1063 if ("".equals(pluginId)) {
10301064 synchronized (PluginLoader.class) {
10311065 pluginId = "plugin" + nextUnknownId++;
10321066 }
10341068 cannotDisable = Boolean.parseBoolean(pluginDescriptor.valueOf("/FindbugsPlugin/@cannotDisable"));
10351069
10361070 String de = pluginDescriptor.valueOf("/FindbugsPlugin/@defaultenabled");
1037 if (de != null && de.toLowerCase().trim().equals("false")) {
1071 if (de != null && "false".equals(de.toLowerCase().trim())) {
10381072 optionalPlugin = true;
10391073 }
10401074 if (optionalPlugin) {
10641098 Plugin constructedPlugin = new Plugin(pluginId, version, parsedDate, this, !optionalPlugin, cannotDisable);
10651099 // Set provider and website, if specified
10661100 String provider = pluginDescriptor.valueOf(XPATH_PLUGIN_PROVIDER).trim();
1067 if (!provider.equals("")) {
1101 if (!"".equals(provider)) {
10681102 constructedPlugin.setProvider(provider);
10691103 }
10701104 String website = pluginDescriptor.valueOf(XPATH_PLUGIN_WEBSITE).trim();
1071 if (!website.equals("")) {
1105 if (!"".equals(website)) {
10721106 try {
10731107 constructedPlugin.setWebsite(website);
10741108 } catch (URISyntaxException e1) {
10771111 }
10781112
10791113 String updateUrl = pluginDescriptor.valueOf("/FindbugsPlugin/@update-url").trim();
1080 if (!updateUrl.equals("")) {
1114 if (!"".equals(updateUrl)) {
10811115 try {
10821116 constructedPlugin.setUpdateUrl(updateUrl);
10831117 } catch (URISyntaxException e1) {
11241158 if (findbugsXML_URL == null) {
11251159 throw new PluginException("Couldn't find \"" + name + "\" in plugin " + this);
11261160 }
1127 if (DEBUG)
1161 if (DEBUG) {
11281162 System.out.println("PluginLoader found " + name + " at: " + findbugsXML_URL);
1163 }
11291164
11301165 if (jarName != null && !findbugsXML_URL.toString().contains(jarName)
11311166 && !(corePlugin && findbugsXML_URL.toString().endsWith("etc/findbugs.xml"))) {
11591194 String country = locale.getCountry();
11601195
11611196 List<String> potential = new ArrayList<String>(3);
1162 if (country != null)
1197 if (country != null) {
11631198 potential.add("messages_" + language + "_" + country + ".xml");
1199 }
11641200 potential.add("messages_" + language + ".xml");
11651201 potential.add("messages.xml");
11661202 return potential;
11701206 // List of message translation files in decreasing order of precedence
11711207 ArrayList<Document> messageCollectionList = new ArrayList<Document>();
11721208 PluginException caught = null;
1173 for (String m : getPotentialMessageFiles())
1209 for (String m : getPotentialMessageFiles()) {
11741210 try {
11751211 addCollection(messageCollectionList, m);
11761212 } catch (PluginException e) {
11781214 AnalysisContext.logError(
11791215 "Error loading localized message file:" + m, e);
11801216 }
1217 }
11811218 if (messageCollectionList.isEmpty()) {
11821219 if (caught != null) {
11831220 throw caught;
11881225 }
11891226
11901227
1191 private <T> void loadComponentPlugin(@SuppressWarnings("hiding") Plugin plugin,
1228 private <T> void loadComponentPlugin(Plugin plugin,
11921229 Class<T> componentKind, @DottedClassName String componentClassname, String filterId,
11931230 boolean disabled, String description, String details, PropertyBundle properties) throws PluginException {
11941231 Class<? extends T> componentClass = null;
12311268 boolean spanPlugins = Boolean.valueOf(node.valueOf("@spanplugins")).booleanValue();
12321269
12331270 String categoryName = node.valueOf("@name");
1234 if (!categoryName.equals("")) {
1235 if (categoryName.equals("reporting")) {
1271 if (!"".equals(categoryName)) {
1272 if ("reporting".equals(categoryName)) {
12361273 return new ReportingDetectorFactorySelector(spanPlugins ? null : plugin);
1237 } else if (categoryName.equals("training")) {
1274 } else if ("training".equals(categoryName)) {
12381275 return new ByInterfaceDetectorFactorySelector(spanPlugins ? null : plugin, TrainingDetector.class);
1239 } else if (categoryName.equals("interprocedural")) {
1276 } else if ("interprocedural".equals(categoryName)) {
12401277 return new ByInterfaceDetectorFactorySelector(spanPlugins ? null : plugin,
12411278 InterproceduralFirstPassDetector.class);
12421279 } else {
12501287 boolean spanPlugins = Boolean.valueOf(node.valueOf("@spanplugins")).booleanValue();
12511288
12521289 String superName = node.valueOf("@super");
1253 if (!superName.equals("")) {
1290 if (!"".equals(superName)) {
12541291 try {
12551292 Class<?> superClass = Class.forName(superName);
12561293 return new ByInterfaceDetectorFactorySelector(spanPlugins ? null : plugin, superClass);
12751312 stream.close();
12761313 }
12771314 messageCollectionList.add(messageCollection);
1278 } catch (Exception e) {
1315 } catch (IOException | DocumentException e) {
12791316 throw new PluginException("Couldn't parse \"" + messageURL + "\"", e);
12801317 }
12811318 }
13711408 URL url = file.toURI().toURL();
13721409 if (IO.verifyURL(url)) {
13731410 loadInitialPlugin(url, true, optional);
1374 if (FindBugs.DEBUG)
1411 if (FindBugs.DEBUG) {
13751412 System.out.println("Found plugin: " + file.toString());
1413 }
13761414 }
13771415 } catch (MalformedURLException e) {
13781416
14441482 assert true;
14451483 }catch (PluginException e) {
14461484 AnalysisContext.logError("Unable to load plugin from " + u, e);
1447 if (DEBUG)
1485 if (DEBUG) {
14481486 e.printStackTrace();
1487 }
14491488 }
14501489 }
14511490
14731512 ((HttpURLConnection) connection).disconnect();
14741513 }
14751514 loadInitialPlugin(url, true, false);
1476 } catch (Exception e) {
1515 } catch (IOException e) {
14771516 DetectorFactoryCollection.jawsDebugMessage("error loading " + url + " : " + e.getMessage());
14781517 }
14791518 }
1480 } catch (Exception e) {
1519 } catch (IOException e) {
14811520 DetectorFactoryCollection.jawsDebugMessage("error : " + e.getMessage());
14821521 } finally {
14831522 Util.closeSilently(in);
4242 public class PrintingBugReporter extends TextUIBugReporter {
4343 private final HashSet<BugInstance> seenAlready = new HashSet<BugInstance>();
4444
45 @Override
4546 public void observeClass(ClassDescriptor classDescriptor) {
4647 // Don't need to do anything special, since we won't be
4748 // reporting statistics.
5556 }
5657 }
5758
59 @Override
5860 public void finish() {
5961 outputStream.close();
6062 }
6971 private int summarizeMaxRank = maxRank;
7072
7173 private final Project project;
74
75 private boolean setExitCode;
7276
7377 public PrintingCommandLine() {
7478 project = new Project();
8286 addSwitch("-annotationUpload", "generate annotations in upload format");
8387 addSwitchWithOptionalExtraPart("-html", "stylesheet", "Generate HTML output (default stylesheet is default.xsl)");
8488 addOption("-pluginList", "jar1[" + File.pathSeparator + "jar2...]", "specify list of plugin Jar files to load");
89 addSwitch("-exitcode", "set exit code of process");
8590 }
8691
8792 public @Nonnull
9196
9297 @Override
9398 protected void handleOption(String option, String optionExtraPart) throws IOException {
94 if (option.equals("-longBugCodes"))
99 if ("-longBugCodes".equals(option)) {
95100 setUseLongBugCodes(true);
96 else if (option.equals("-rank"))
101 } else if ("-rank".equals(option)) {
97102 setShowRank(true);
98 else if (option.equals("-designations"))
103 } else if ("-designations".equals(option)) {
99104 setReportUserDesignations(true);
100 else if (option.equals("-applySuppression"))
105 } else if ("-applySuppression".equals(option)) {
101106 setApplySuppressions(true);
102 else if (option.equals("-history"))
107 } else if ("-history".equals(option)) {
103108 setReportHistory(true);
104 else if (option.equals("-annotationUpload"))
109 } else if ("-annotationUpload".equals(option)) {
105110 annotationUploadFormat = true;
106 else if (option.equals("-html")) {
107 if (!optionExtraPart.equals("")) {
111 } else if ("-html".equals(option)) {
112 if (!"".equals(optionExtraPart)) {
108113 stylesheet = optionExtraPart;
109114 } else {
110115 stylesheet = "default.xsl";
111116 }
112 } else
117 } else if ("-exitcode".equals(option)) {
118 setExitCode = true;
119 } else {
113120 throw new IllegalArgumentException("Unknown option '" + option + "'");
121 }
114122 }
115123
116124 @Override
117125 protected void handleOptionWithArgument(String option, String argument) throws IOException {
118 if (option.equals("-pluginList")) {
126 if ("-pluginList".equals(option)) {
119127 String pluginListStr = argument;
120128 Map<String, Boolean> customPlugins = getProject().getConfiguration().getCustomPlugins();
121129 StringTokenizer tok = new StringTokenizer(pluginListStr, File.pathSeparator);
132140 }
133141 }
134142 }
135 } else if (option.equals("-maxRank")) {
143 } else if ("-maxRank".equals(option)) {
136144 maxRank = Integer.parseInt(argument);
137 } else if (option.equals("-summarizeMaxRank")) {
145 } else if ("-summarizeMaxRank".equals(option)) {
138146 summarizeMaxRank = Integer.parseInt(argument);
139147 } else {
140148 throw new IllegalStateException();
160168 }
161169
162170 SortedBugCollection bugCollection = new SortedBugCollection(commandLine.getProject());
163 if (argCount < args.length)
171 if (argCount < args.length) {
164172 bugCollection.readXML(args[argCount++]);
165 else
173 } else {
166174 bugCollection.readXML(System.in);
167
168 if (argCount < args.length)
175 }
176
177 if (argCount < args.length) {
169178 reporter.setOutputStream(UTF8.printStream(new FileOutputStream(args[argCount++]), true));
179 }
180 boolean bugsReported = false;
170181 RuntimeException storedException = null;
171182 if (commandLine.annotationUploadFormat) {
172183 bugCollection.computeBugHashes();
178189
179190 System.out.print("#" + fHash);
180191 String key = warning.getUserDesignationKey();
181 if (key.equals(BugDesignation.UNCLASSIFIED) || key.equals("NEEDS_FURTHER_STUDY"))
192 if (key.equals(BugDesignation.UNCLASSIFIED) || "NEEDS_FURTHER_STUDY".equals(key)) {
182193 System.out.print("#-1#" + key);
183 else if (key.equals("MUST_FIX") || key.equals("SHOULD_FIX") || key.equals("I_WILL_FIX"))
194 } else if ("MUST_FIX".equals(key) || "SHOULD_FIX".equals(key) || "I_WILL_FIX".equals(key)) {
184195 System.out.print("#7#" + key);
185 else
196 } else {
186197 System.out.print("#0#" + key);
198 }
187199 SourceLineAnnotation sourceLine = warning.getPrimarySourceLineAnnotation();
188200 System.out.println("#" + sourceLine.getSourceFile() + "#" + sourceLine.getStartLine());
189201 System.out.println(warning.getAnnotationText());
190202 } catch (RuntimeException e) {
191 if (storedException == null)
203 if (storedException == null) {
192204 storedException = e;
205 }
193206 }
194207 }
195208 } else {
196209
197210 Bag<String> lowRank = new Bag<String>(new TreeMap<String, Integer>());
198 for (BugInstance warning : bugCollection.getCollection())
211 for (BugInstance warning : bugCollection.getCollection()) {
199212 if (!reporter.isApplySuppressions() || !bugCollection.getProject().getSuppressionFilter().match(warning)) {
200213 int rank = warning.getBugRank();
201214 BugPattern pattern = warning.getBugPattern();
202215 if (rank <= commandLine.maxRank) {
216 bugsReported = true;
203217 try {
204218 reporter.printBug(warning);
205219 } catch (RuntimeException e) {
206 if (storedException == null)
220 if (storedException == null) {
207221 storedException = e;
222 }
208223 }
209224 } else if (rank <= commandLine.summarizeMaxRank) {
225 bugsReported = true;
210226 lowRank.add(pattern.getCategory());
211227 }
212228
213229 }
230 }
214231
215232 reporter.finish();
216233 for (Map.Entry<String, Integer> e : lowRank.entrySet()) {
219236 }
220237
221238 }
222 if (storedException != null)
223 throw storedException;
239 if(commandLine.setExitCode){
240 int exitCode = 0;
241 System.err.println("Calculating exit code...");
242 if (storedException != null) {
243 exitCode |= ExitCodes.ERROR_FLAG;
244 System.err.println("Setting 'errors encountered' flag (" + ExitCodes.ERROR_FLAG + ")");
245 storedException.printStackTrace(System.err);
246 }
247 if (bugsReported) {
248 exitCode |= ExitCodes.BUGS_FOUND_FLAG;
249 System.err.println("Setting 'bugs found' flag (" + ExitCodes.BUGS_FOUND_FLAG + ")");
250 }
251 System.err.println("Exit code set to: " + exitCode);
252 System.exit(exitCode);
253 } else
254 if (storedException != null) {
255 throw storedException;
256 }
224257
225258 }
226259
232265 bugCollection.setApplySuppressions(applySuppression);
233266 if (argCount < args.length) {
234267 bugCollection.readXML(args[argCount++]);
235 } else
268 } else {
236269 bugCollection.readXML(System.in);
237
238 if (argCount < args.length)
270 }
271
272 if (argCount < args.length) {
239273 reporter.setOutputStream(UTF8.printStream(new FileOutputStream(args[argCount++]), true));
274 }
240275
241276 reporter.finish();
242277 Exception e = reporter.getFatalException();
243 if (e != null)
278 if (e != null) {
244279 throw e;
245 }
246
280 }
281 }
282
283 @Override
247284 public @CheckForNull
248285 BugCollection getBugCollection() {
249286 return null;
250287 }
251288 }
252289
253 // vim:ts=4
3030
3131 /*
3232 * (non-Javadoc)
33 *
33 *
3434 * @see java.lang.Object#hashCode()
3535 */
3636 @Override
4444
4545 /*
4646 * (non-Javadoc)
47 *
47 *
4848 * @see java.lang.Object#equals(java.lang.Object)
4949 */
5050 @Override
5151 public boolean equals(Object obj) {
52 if (this == obj)
52 if (this == obj) {
5353 return true;
54 if (obj == null)
54 }
55 if (obj == null) {
5556 return false;
56 if (getClass() != obj.getClass())
57 }
58 if (getClass() != obj.getClass()) {
5759 return false;
60 }
5861 ProgramPoint other = (ProgramPoint) obj;
5962 if (method == null) {
60 if (other.method != null)
63 if (other.method != null) {
6164 return false;
62 } else if (!method.equals(other.method))
65 }
66 } else if (!method.equals(other.method)) {
6367 return false;
64 if (pc != other.pc)
68 }
69 if (pc != other.pc) {
6570 return false;
71 }
6672 return true;
6773 }
6874
2323 */
2424
2525 package edu.umd.cs.findbugs;
26
2726 import static edu.umd.cs.findbugs.xml.XMLOutputUtil.writeElementList;
27 import static java.util.Objects.requireNonNull;
2828
2929 import java.io.BufferedInputStream;
30 import java.io.BufferedReader;
3130 import java.io.File;
3231 import java.io.FileInputStream;
3332 import java.io.FileOutputStream;
137136 * @param configuration The configuration to set, non null
138137 */
139138 public void setConfiguration(@Nonnull UserPreferences configuration) {
140 if (configuration == null)
141 throw new NullPointerException();
139 requireNonNull(configuration);
142140 this.configuration = configuration;
143141 }
144142
145 /**
146 * @return Returns the cloudId.
147 */
148143 public @CheckForNull String getCloudId() {
149144 return cloudId;
150145 }
151146
152 /**
153 * @param cloudId
154 * The cloudId to set.
155 */
156147 public void setCloudId(@Nullable String cloudId) {
157148 if (cloudId != null && cloudId.indexOf('.') == -1) {
158149 Map<String, CloudPlugin> registeredClouds = DetectorFactoryCollection.instance().getRegisteredClouds();
159150 String check = "." + cloudId;
160151 int count = 0;
161152 String result = cloudId;
162 for(String name : registeredClouds.keySet())
153 for(String name : registeredClouds.keySet()) {
163154 if (name.endsWith(check)) {
164155 count++;
165156 result = name;
166157 }
167 if (count == 1)
158 }
159 if (count == 1) {
168160 cloudId = result;
161 }
169162 }
170163 this.cloudId = cloudId;
171164 }
259252 }
260253
261254 public void setCurrentWorkingDirectory(File f) {
262 if (f != null)
255 if (f != null) {
263256 addWorkingDir(f.toString());
257 }
264258 }
265259
266260 /**
315309 * directory was already present
316310 */
317311 public boolean addWorkingDir(String dirName) {
318 if (dirName == null)
312 if (dirName == null) {
319313 throw new NullPointerException();
314 }
320315 return addToListInternal(currentWorkingDirectoryList, new File(dirName));
321316 }
322317
711706 try {
712707 String tag = Util.getXMLType(in);
713708 SAXBugCollectionHandler handler;
714 if (tag.equals("Project")) {
709 if ("Project".equals(tag)) {
715710 handler = new SAXBugCollectionHandler(project, f);
716 } else if (tag.equals("BugCollection")) {
711 } else if ("BugCollection".equals(tag)) {
717712 SortedBugCollection bugs = new SortedBugCollection(project);
718713 handler = new SAXBugCollectionHandler(bugs, f);
719714 } else {
775770
776771 /**
777772 * Read a line from a BufferedReader, ignoring blank lines and comments.
778 */
773 *
779774 private static String getLine(BufferedReader reader) throws IOException {
780775 String line;
781776 while ((line = reader.readLine()) != null) {
785780 }
786781 }
787782 return line;
788 }
783 }*/
789784
790785 /**
791786 * Convert to a string in a nice (displayable) format.
832827
833828 static final String PLUGIN_STATUS_ELEMENT_NAME = "enabled";
834829
830 @Override
835831 public void writeXML(XMLOutput xmlOutput) throws IOException {
836832 writeXML(xmlOutput, null, null);
837833 }
838834
839 public void writeXML(XMLOutput xmlOutput, @CheckForNull File destination, @CheckForNull BugCollection bugCollection)
835 public void writeXML(XMLOutput xmlOutput, @CheckForNull File destination, @CheckForNull BugCollection bugCollection)
840836 throws IOException {
841837 {
842838 XMLAttributeList attributeList = new XMLAttributeList();
853849 writeElementList(xmlOutput, AUX_CLASSPATH_ENTRY_ELEMENT_NAME, convertToRelative(auxClasspathEntryList, base));
854850 writeElementList(xmlOutput, SRC_DIR_ELEMENT_NAME, convertToRelative(srcDirList, base));
855851 List<String> cwdStrings = new ArrayList<String>();
856 for (File file : currentWorkingDirectoryList)
852 for (File file : currentWorkingDirectoryList) {
857853 cwdStrings.add(file.getPath());
854 }
858855 XMLOutputUtil.writeElementList(xmlOutput, WRK_DIR_ELEMENT_NAME, convertToRelative(cwdStrings, base));
859856 } else {
860857 // TODO to allow relative paths: refactor the code which uses null
865862 XMLOutputUtil.writeFileList(xmlOutput, WRK_DIR_ELEMENT_NAME, currentWorkingDirectoryList);
866863 }
867864
868 if (suppressionFilter != null && !suppressionFilter.isEmpty()) {
865 if (!suppressionFilter.isEmpty()) {
869866 xmlOutput.openTag("SuppressionFilter");
870867 suppressionFilter.writeBodyAsXML(xmlOutput);
871868 xmlOutput.closeTag("SuppressionFilter");
886883 CloudPlugin cloudPlugin = bugCollection == null ? null : CloudFactory.getCloudPlugin(bugCollection);
887884 if (cloudPlugin != null) {
888885 String id = cloudPlugin.getId();
889 if (id == null)
886 if (id == null) {
890887 id = cloudId;
888 }
891889 xmlOutput.startTag(CLOUD_ELEMENT_NAME);
892890 xmlOutput.addAttribute(CLOUD_ID_ATTRIBUTE_NAME, id);
893891 boolean onlineCloud = cloudPlugin.isOnline();
894892 xmlOutput.addAttribute("online", Boolean.toString(onlineCloud));
895893 String url = cloudPlugin.getProperties().getProperty("cloud.detailsUrl");
896 if (url != null)
894 if (url != null) {
897895 xmlOutput.addAttribute("detailsUrl", url);
896 }
898897 xmlOutput.stopTag(false);
899898 for (Map.Entry<?,?> e : cloudProperties.entrySet()) {
900899 xmlOutput.startTag(CLOUD_PROPERTY_ELEMENT_NAME);
989988 return path.toString();
990989 }
991990 }
992
993991 return srcFile;
994
995992 }
996993
997994 /**
1000997 * @param fileName
1001998 * path to convert
1002999 * @return the converted filename
1003 */
1000 *
10041001 private String convertToAbsolute(String fileName) {
10051002 // At present relative paths are only calculated if the fileName is
10061003 // below the project file. This need not be the case, and we could use
10121009 if (!file.isAbsolute()) {
10131010 for (File cwd : currentWorkingDirectoryList) {
10141011 File test = new File(cwd, fileName);
1015 if (test.canRead())
1012 if (test.canRead()) {
10161013 return test.getAbsolutePath();
1014 }
10171015 }
10181016 return file.getAbsolutePath();
10191017 }
10201018 return fileName;
1021 }
1019 }*/
10221020
10231021 /**
10241022 * Make the given filename absolute relative to the current working
10931091 /**
10941092 * Make the given list of pathnames absolute relative to the absolute path
10951093 * of the project file.
1096 */
1094 *
10971095 private void makeListAbsoluteProject(List<String> list) {
10981096 List<String> replace = new LinkedList<String>();
10991097 for (String fileName : list) {
11031101
11041102 list.clear();
11051103 list.addAll(replace);
1106 }
1107
1108 /**
1109 * @param timestamp
1110 * The timestamp to set.
1111 */
1104 }*/
1105
11121106 public void setTimestamp(long timestamp) {
11131107 this.timestampForAnalyzedClasses = timestamp;
11141108 }
11191113 }
11201114 }
11211115
1122 /**
1123 * @return Returns the timestamp.
1124 */
11251116 public long getTimestamp() {
11261117 return timestampForAnalyzedClasses;
11271118 }
11281119
1129 /**
1130 * @param projectName
1131 * The projectName to set.
1132 */
11331120 public void setProjectName(String projectName) {
11341121 this.projectName = projectName;
11351122 }
11361123
1137 /**
1138 * @return Returns the projectName.
1139 */
11401124 public String getProjectName() {
11411125 return projectName;
11421126 }
11431127
1144 /**
1145 * @param suppressionFilter
1146 * The suppressionFilter to set.
1147 */
11481128 public void setSuppressionFilter(@Nonnull Filter suppressionFilter) {
1149 if (suppressionFilter == null)
1150 throw new NullPointerException();
1129 requireNonNull(suppressionFilter);
11511130 this.suppressionFilter = suppressionFilter;
11521131 }
11531132
1154 /**
1155 * @return Returns the suppressionFilter.
1156 */
1133 @Nonnull
11571134 public Filter getSuppressionFilter() {
11581135 return suppressionFilter;
11591136 }
11631140 }
11641141
11651142 public IGuiCallback getGuiCallback() {
1166 if (guiCallback == null)
1143 if (guiCallback == null) {
11671144 guiCallback = new CommandLineUiCallback();
1145 }
11681146 return guiCallback;
11691147 }
11701148
1171 /**
1172 * @return
1173 */
11741149 public Iterable<String> getResolvedSourcePaths() {
11751150 List<String> result = new ArrayList<String>();
11761151 for (String s : srcDirList) {
11811156 }
11821157 File f = new File(s);
11831158 if (f.isAbsolute() || currentWorkingDirectoryList.isEmpty()) {
1184 if (f.canRead())
1159 if (f.canRead()) {
11851160 result.add(s);
1161 }
11861162 continue;
11871163 }
1188 for (File d : currentWorkingDirectoryList)
1164 for (File d : currentWorkingDirectoryList) {
11891165 if (d.canRead() && d.isDirectory()) {
11901166 File a = new File(d, s);
1191 if (a.canRead())
1167 if (a.canRead()) {
11921168 result.add(a.getAbsolutePath());
1193
1194 }
1169 }
1170
1171 }
1172 }
11951173
11961174 }
11971175 return result;
11981176 }
11991177 }
1200
1201 // vim:ts=4
4646
4747 PrefixFilter(String prefixes) {
4848 prefixes = prefixes.replace('/', '.').trim();
49 if (prefixes.length() == 0)
49 if (prefixes.length() == 0) {
5050 parts = new String[0];
51 else
51 } else {
5252 parts = prefixes.split("[ ,:]+");
53 }
5354 }
5455
5556 boolean matches(@DottedClassName String className) {
56 if (parts.length == 0)
57 if (parts.length == 0) {
5758 return true;
58 for (String p : parts)
59 if (p.length() > 0 && className.startsWith(p))
59 }
60 for (String p : parts) {
61 if (p.length() > 0 && className.startsWith(p)) {
6062 return true;
63 }
64 }
6165 return false;
6266 }
6367
109113 public TreeSet<String> getProjects(@DottedClassName String className) {
110114 TreeSet<String> results = new TreeSet<String>();
111115 for (Map.Entry<String, PrefixFilter> e : map.entrySet()) {
112 if (e.getValue().matches(className))
116 if (e.getValue().matches(className)) {
113117 results.add(e.getKey());
118 }
114119 }
115120 return results;
116121 }
121126
122127 static <T> void incrementCount(Map<T, Integer> counter, T t, int valueToAdd) {
123128 Integer v = counter.get(t);
124 if (v == null)
129 if (v == null) {
125130 counter.put(t, valueToAdd);
126 else
131 } else {
127132 counter.put(t, v + valueToAdd);
133 }
128134 }
129135
130136 static final Pattern FORBIDDEN_PACKAGE_PREFIXES = Pattern.compile(SystemProperties.getProperty(
137143
138144 for (Map.Entry<String, Integer> e : rawPackageCount.entrySet()) {
139145 String packageName = e.getKey();
140 if (e.getValue() > 5)
146 if (e.getValue() > 5) {
141147 System.out.printf("%5d %s%n", e.getValue(), packageName);
148 }
142149 }
143150 System.out.println("Count by project");
144151
145152 for (Map.Entry<Set<String>, Integer> e : count.entrySet()) {
146153 Set<String> projects = e.getKey();
147 if (e.getValue() > 5)
154 if (e.getValue() > 5) {
148155 System.out.printf("%5d %s%n", e.getValue(), projects);
156 }
149157 }
150158 System.out.println("Count by package for items not associated with a project");
151159
156164 for (String p1 : packages) {
157165 int num = missingProjectCount.get(p1);
158166 if (num < 3) {
159 int x = p1.lastIndexOf(".");
167 int x = p1.lastIndexOf('.');
160168 String p2 = p1.substring(0, x);
161 if (FORBIDDEN_PACKAGE_PREFIXES.matcher(p2).matches())
169 if (FORBIDDEN_PACKAGE_PREFIXES.matcher(p2).matches()) {
162170 continue;
171 }
163172
164173 extraSuperPackages.add(p2);
165174 }
166175 }
167 for (String p1 : extraSuperPackages)
176 for (String p1 : extraSuperPackages) {
168177 missingProjectCount.put(p1, 0);
178 }
169179
170180 for (Iterator<String> i = packages.iterator(); i.hasNext();) {
171181 String p1 = i.next();
172182 int num = missingProjectCount.get(p1);
173183
174 for (String p2 : packages)
184 for (String p2 : packages) {
175185 if (p2.length() < p1.length() && p1.startsWith(p2)) {
176186 // p1 is a subpackage of p2
177187 // System.out.printf("%s is a subpackage of %s\n", p1,
180190 incrementCount(missingProjectCount, p2, num);
181191 break;
182192 }
193 }
183194
184195 }
185196 }
186197
187198 System.out.println("Count of missing files in packages not associated with a project");
188199 for (Map.Entry<String, Integer> e : missingProjectCount.entrySet()) {
189 if (e.getValue() > 5)
200 if (e.getValue() > 5) {
190201 System.out.printf("%5d %s%n", e.getValue(), e.getKey());
202 }
191203 }
192204 }
193205
197209
198210 BufferedReader in = null;
199211 try {
200 in = UTF8.bufferedReader(u.openStream());
212 in = UTF8.bufferedReader(u.openStream());
201213 while (true) {
202214 String s = in.readLine();
203 if (s == null)
215 if (s == null) {
204216 break;
217 }
205218 String[] parts = s.split("=");
206 if (parts.length == 2 && !map.containsKey(parts[0]))
219 if (parts.length == 2 && !map.containsKey(parts[0])) {
207220 map.put(parts[0], new PrefixFilter(parts[1]));
221 }
208222 }
209223 } catch (IOException e1) {
210224
211225 AnalysisContext.logError("Error loading projects paths", e1);
212226 } finally {
213 Util.closeSilently(in);
227 Util.closeSilently(in);
214228 }
215229
216230 }
6666
6767 private static final boolean OMIT_PACKAGE_STATS = SystemProperties.getBoolean("findbugs.packagestats.omit");
6868
69 private SortedMap<String, PackageStats> packageStatsMap;
70
71 private int[] totalErrors = new int[] { 0, 0, 0, 0, 0 };
69 private final SortedMap<String, PackageStats> packageStatsMap;
70
71 private final int[] totalErrors = new int[] { 0, 0, 0, 0, 0 };
7272
7373 private int totalClasses;
7474
8686
8787 private boolean hasPackageStats;
8888
89 private Footprint baseFootprint;
90
89 private final Footprint baseFootprint;
90
91 private final String java_version = SystemProperties.getProperty("java.version");
9192 private String java_vm_version = SystemProperties.getProperty("java.vm.version");
9293
9394 private final Profiler profiler;
9697 public String toString() {
9798 StringBuilder buf = new StringBuilder();
9899 buf.append(getNumClasses()).append(" classes: ");
99 for (PackageStats pStats : getPackageStats())
100 for (ClassStats cStats : pStats.getSortedClassStats())
100 for (PackageStats pStats : getPackageStats()) {
101 for (ClassStats cStats : pStats.getSortedClassStats()) {
101102 buf.append(cStats.getName()).append(" ");
103 }
104 }
102105 return buf.toString();
103106 }
104107
132135 }
133136
134137 public int getCodeSize() {
135 if (totalSizeFromPackageStats > 0)
138 if (totalSizeFromPackageStats > 0) {
136139 return totalSizeFromPackageStats;
140 }
137141 return totalSize;
138142
139143 }
148152
149153 /**
150154 * Set the timestamp for this analysis run.
151 *
155 *
152156 * @param timestamp
153157 * the time of the analysis run this ProjectStats represents, as
154158 * previously reported by writeXML.
169173 * Get the number of classes analyzed.
170174 */
171175 public int getNumClasses() {
172 if (totalClassesFromPackageStats > 0)
176 if (totalClassesFromPackageStats > 0) {
173177 return totalClassesFromPackageStats;
178 }
174179 return totalClasses;
175180 }
176181
183188
184189 /**
185190 * Report that a class has been analyzed.
186 *
191 *
187192 * @param className
188193 * the full name of the class
189194 * @param sourceFile
200205
201206 /**
202207 * Report that a class has been analyzed.
203 *
208 *
204209 * @param className
205210 * the full name of the class
206211 * @param sourceFile
213218 * @param updatePackageStats TODO
214219 */
215220 public void addClass(@DottedClassName String className, @CheckForNull String sourceFile, boolean isInterface, int size, boolean updatePackageStats) {
221 if(!hasClassStats) {
222 // totalClasses/totalSize might be set from FindBugsSummary before when parsing XML: reset them
223 totalClasses = 0;
224 totalSize = 0;
225 }
216226 hasClassStats = true;
217227 String packageName;
218228 int lastDot = className.lastIndexOf('.');
219 if (lastDot < 0)
229 if (lastDot < 0) {
220230 packageName = "";
221 else
231 } else {
222232 packageName = className.substring(0, lastDot);
233 }
223234 PackageStats stat = getPackageStats(packageName);
224235 stat.addClass(className, sourceFile, isInterface, size, updatePackageStats);
225236 totalClasses++;
230241
231242 /**
232243 * Report that a class has been analyzed.
233 *
244 *
234245 * @param className
235246 * the full name of the class
236247 */
237248 public @CheckForNull
238249 ClassStats getClassStats(@DottedClassName String className) {
239 if (hasClassStats)
250 if (hasClassStats) {
240251 return null;
252 }
241253 String packageName;
242254 int lastDot = className.lastIndexOf('.');
243 if (lastDot < 0)
255 if (lastDot < 0) {
244256 packageName = "";
245 else
257 } else {
246258 packageName = className.substring(0, lastDot);
259 }
247260 PackageStats stat = getPackageStats(packageName);
248261 return stat.getClassStatsOrNull(className);
249262 }
267280 * Clear bug counts
268281 */
269282 public void clearBugCounts() {
270 for (int i = 0; i < totalErrors.length; i++)
283 for (int i = 0; i < totalErrors.length; i++) {
271284 totalErrors[i] = 0;
285 }
272286 for (PackageStats stats : packageStatsMap.values()) {
273287 stats.clearBugCounts();
274288 }
275289 }
276290
277291 public void purgeClassesThatDontMatch(Pattern classPattern) {
278 if (hasClassStats)
292 if (hasClassStats) {
279293 for (Iterator<Map.Entry<String, PackageStats>> i = packageStatsMap.entrySet().iterator(); i.hasNext();) {
280294 Map.Entry<String, PackageStats> e = i.next();
281295 PackageStats stats = e.getValue();
282296 stats.purgeClassesThatDontMatch(classPattern);
283 if (stats.getClassStats().isEmpty())
297 if (stats.getClassStats().isEmpty()) {
284298 i.remove();
285 }
286 else if (hasPackageStats) {
299 }
300 }
301 } else if (hasPackageStats) {
287302 boolean matchAny = false;
288303 for (String packageName : packageStatsMap.keySet()) {
289304 Matcher m = classPattern.matcher(packageName);
292307 break;
293308 }
294309 }
295 if (matchAny)
310 if (matchAny) {
296311 for (Iterator<String> i = packageStatsMap.keySet().iterator(); i.hasNext();) {
297312 String packageName = i.next();
298313 Matcher m = classPattern.matcher(packageName);
301316 }
302317
303318 }
319 }
304320 }
305321 }
306322
307323 public void purgeClassStats() {
308324 hasClassStats = false;
309 if (totalClassesFromPackageStats == 0)
325 if (totalClassesFromPackageStats == 0) {
310326 totalClassesFromPackageStats = totalClasses;
311 if (totalSizeFromPackageStats == 0)
327 }
328 if (totalSizeFromPackageStats == 0) {
312329 totalSizeFromPackageStats = totalSize;
330 }
313331
314332 for (PackageStats ps : getPackageStats()) {
315333 ps.getClassStats().clear();
318336
319337 public void purgePackageStats() {
320338 hasPackageStats = false;
321 if (totalClassesFromPackageStats == 0)
339 if (totalClassesFromPackageStats == 0) {
322340 totalClassesFromPackageStats = totalClasses;
323 if (totalSizeFromPackageStats == 0)
341 }
342 if (totalSizeFromPackageStats == 0) {
324343 totalSizeFromPackageStats = totalSize;
344 }
325345
326346 getPackageStats().clear();
327347 }
328348
329349 public void recomputeFromComponents() {
330 if (!hasClassStats && !hasPackageStats)
350 if (!hasClassStats && !hasPackageStats) {
331351 return;
332 for (int i = 0; i < totalErrors.length; i++)
352 }
353 for (int i = 0; i < totalErrors.length; i++) {
333354 totalErrors[i] = 0;
355 }
334356 totalSize = 0;
335357 totalClasses = 0;
336358 totalSizeFromPackageStats = 0;
337359 totalClassesFromPackageStats = 0;
338360
339361 for (PackageStats stats : packageStatsMap.values()) {
340 if (hasClassStats)
362 if (hasClassStats) {
341363 stats.recomputeFromClassStats();
364 }
342365 totalSize += stats.size();
343366 totalClasses += stats.getNumClasses();
344 for (int i = 0; i < totalErrors.length; i++)
367 for (int i = 0; i < totalErrors.length; i++) {
345368 totalErrors[i] += stats.getBugsAtPriority(i);
369 }
346370 }
347371 }
348372
349373 FileBugHash fileBugHashes;
350374
351375 public void computeFileStats(BugCollection bugs) {
352 if (bugs.getProjectStats() != this)
376 if (bugs.getProjectStats() != this) {
353377 throw new IllegalArgumentException("Collection doesn't own stats");
378 }
354379 fileBugHashes = FileBugHash.compute(bugs);
355380 }
356381
357382 /**
358383 * Output as XML.
359384 */
385 @Override
360386 public void writeXML(XMLOutput xmlOutput) throws IOException {
361387 writeXML(xmlOutput, true);
362388 }
375401 xmlOutput.addAttribute("total_size", String.valueOf(getCodeSize()));
376402 xmlOutput.addAttribute("num_packages", String.valueOf(packageStatsMap.size()));
377403
378 if (java_vm_version != null)
404 if (java_version != null) {
405 xmlOutput.addAttribute("java_version", java_version);
406 }
407 if (java_vm_version != null) {
379408 xmlOutput.addAttribute("vm_version", java_vm_version);
409 }
380410 Footprint delta = new Footprint(baseFootprint);
381411 NumberFormat twoPlaces = NumberFormat.getInstance(Locale.ENGLISH);
382412 twoPlaces.setMinimumFractionDigits(2);
400430 xmlOutput.addAttribute("gc_seconds", twoPlaces.format(gcTime / 1000.0));
401431 }
402432
403 PackageStats.writeBugPriorities(xmlOutput, totalErrors);
433 BugCounts.writeBugPriorities(xmlOutput, totalErrors);
404434
405435 xmlOutput.stopTag(false);
406436
412442 xmlOutput.addAttribute("size", String.valueOf(fileBugHashes.getSize(sourceFile)));
413443
414444 String hash = fileBugHashes.getHash(sourceFile);
415 if (hash != null)
445 if (hash != null) {
416446 xmlOutput.addAttribute("bugHash", hash);
447 }
417448 xmlOutput.stopTag(true);
418449
419450 }
420451 }
421452
422 if (!OMIT_PACKAGE_STATS)
453 if (!OMIT_PACKAGE_STATS) {
423454 for (PackageStats stats : packageStatsMap.values()) {
424455 stats.writeXML(xmlOutput);
425456 }
457 }
426458
427459 getProfiler().writeXML(xmlOutput);
428460 xmlOutput.closeTag("FindBugsSummary");
429461 }
430462
431463 public Map<String, String> getFileHashes(BugCollection bugs) {
432 if (bugs.getProjectStats() != this)
464 if (bugs.getProjectStats() != this) {
433465 throw new IllegalArgumentException("Collection doesn't own stats");
434
435 if (fileBugHashes == null)
466 }
467
468 if (fileBugHashes == null) {
436469 computeFileStats(bugs);
470 }
437471
438472 HashMap<String, String> result = new HashMap<String, String>();
439473 for (String sourceFile : fileBugHashes.getSourceFiles()) {
457491
458492 /**
459493 * Transform summary information to HTML.
460 *
494 *
461495 * @param htmlWriter
462496 * the Writer to write the HTML output to
463497 */
469503 StreamSource in = new StreamSource(new ByteArrayInputStream(summaryOut.toByteArray()));
470504 StreamResult out = new StreamResult(htmlWriter);
471505 InputStream xslInputStream = this.getClass().getClassLoader().getResourceAsStream("summary.xsl");
472 if (xslInputStream == null)
506 if (xslInputStream == null) {
473507 throw new IOException("Could not load summary stylesheet");
508 }
474509 StreamSource xsl = new StreamSource(xslInputStream);
475510
476511 TransformerFactory tf = TransformerFactory.newInstance();
478513 transformer.transform(in, out);
479514
480515 Reader rdr = in.getReader();
481 if (rdr != null)
516 if (rdr != null) {
482517 rdr.close();
518 }
483519 htmlWriter.close();
484520 InputStream is = xsl.getInputStream();
485 if (is != null)
521 if (is != null) {
486522 is.close();
523 }
487524 }
488525
489526 public Collection<PackageStats> getPackageStats() {
521558 * @param stats2
522559 */
523560 public void addStats(ProjectStats stats2) {
524 if (totalSize == totalSizeFromPackageStats)
561 if (totalSize == totalSizeFromPackageStats) {
525562 totalSizeFromPackageStats += stats2.getCodeSize();
563 }
526564 totalSize += stats2.getCodeSize();
527 if (totalClasses == totalClassesFromPackageStats)
565 if (totalClasses == totalClassesFromPackageStats) {
528566 totalClassesFromPackageStats += stats2.getNumClasses();
567 }
529568 totalClasses += stats2.getNumClasses();
530 for (int i = 0; i < totalErrors.length; i++)
569 for (int i = 0; i < totalErrors.length; i++) {
531570 totalErrors[i] += stats2.totalErrors[i];
532
533 if (stats2.hasPackageStats)
571 }
572
573 if (stats2.hasPackageStats) {
534574 hasPackageStats = true;
535 if (stats2.hasClassStats)
575 }
576 if (stats2.hasClassStats) {
536577 hasClassStats = true;
578 }
537579
538580 for (Map.Entry<String, PackageStats> entry : stats2.packageStatsMap.entrySet()) {
539581 String key = entry.getKey();
567609 return profiler;
568610 }
569611
570 /**
571 * @param parseInt
572 */
573612 public void setTotalClasses(int totalClasses) {
574613 this.totalClasses = totalClasses;
575614 }
576615
577 /**
578 * @param parseInt
579 */
580616 public void setTotalSize(int totalSize) {
581617 this.totalSize = totalSize;
582618 }
4545
4646 Rewriter() {
4747 Pattern p = null;
48 if (urlRewritePatternString != null && urlRewriteFormat != null)
48 if (urlRewritePatternString != null && urlRewriteFormat != null) {
4949 try {
5050 p = Pattern.compile(urlRewritePatternString);
51 } catch (Exception e) {
51 } catch (Throwable e) {
5252 assert true;
5353 }
54 }
5455
5556 urlRewritePattern = p;
5657 }
6162 Rewriter getRewriter() {
6263 if (rewriter == null) {
6364 synchronized (this) {
64 if (rewriter == null)
65 if (rewriter == null) {
6566 rewriter = new Rewriter();
67 }
6668 }
6769 }
6870 return rewriter;
122124 /**
123125 * Get boolean property, returning false if a security manager prevents us
124126 * from accessing system properties
125 *
127 *
126128 * @return true if the property exists and is set to true
127129 */
128130 public boolean getBoolean(String name) {
133135 boolean result = defaultValue;
134136 try {
135137 String value = getProperty(name);
136 if (value == null)
138 if (value == null) {
137139 return defaultValue;
140 }
138141 result = toBoolean(value);
139142 } catch (IllegalArgumentException e) {
140143 } catch (NullPointerException e) {
143146 }
144147
145148 private boolean toBoolean(String name) {
146 return ((name != null) && name.equalsIgnoreCase("true"));
149 return ((name != null) && "true".equalsIgnoreCase(name));
147150 }
148151
149152 /**
156159 public int getInt(String name, int defaultValue) {
157160 try {
158161 String value = getProperty(name);
159 if (value != null)
162 if (value != null) {
160163 return Integer.decode(value);
164 }
161165 } catch (Exception e) {
162166 assert true;
163167 }
172176 public String getOSDependentProperty(String name) {
173177 String osDependentName = name + SystemProperties.OS_NAME;
174178 String value = getProperty(osDependentName);
175 if (value != null)
179 if (value != null) {
176180 return value;
181 }
177182 return getProperty(name);
178183 }
179184
185190 public String getProperty(String name) {
186191 try {
187192 String value = SystemProperties.getProperty(name);
188 if (value != null)
193 if (value != null) {
189194 return value;
195 }
190196 return properties.getProperty(name);
191197 } catch (Exception e) {
192198 return null;
212218 */
213219 public String getProperty(String name, String defaultValue) {
214220 String value = getProperty(name);
215 if (value != null)
221 if (value != null) {
216222 return value;
223 }
217224 return defaultValue;
218225 }
219226
220227 public String rewriteURLAccordingToProperties(String u) {
221 if (getRewriter().urlRewritePattern == null || getRewriter().urlRewriteFormat == null)
228 if (getRewriter().urlRewritePattern == null || getRewriter().urlRewriteFormat == null) {
222229 return u;
230 }
223231 Matcher m = getRewriter().urlRewritePattern.matcher(u);
224 if (!m.matches())
232 if (!m.matches()) {
225233 return u;
234 }
226235 String result = String.format(getRewriter().urlRewriteFormat, m.group(1));
227236 return result;
228237 }
2929 public abstract class QueryBugAnnotations {
3030 // Bug's text annotation must contain one of the key
3131 // words in order to match
32 private HashSet<String> keywordSet = new HashSet<String>();
32 private final HashSet<String> keywordSet = new HashSet<String>();
3333
3434 /**
3535 * Add a keyword to the query. A BugInstance's text annotation must contain
3636 * at least one keyword in order to match the query.
37 *
37 *
3838 * @param keyword
3939 * the keyword
4040 */
4545 /**
4646 * Scan bug instances contained in given file, reporting those whose text
4747 * annotations contain at least one of the keywords in the query.
48 *
48 *
4949 * @param filename
5050 * an XML file containing bug instances
5151 */
5858 /**
5959 * Scan bug instances contained in given bug collection, reporting those
6060 * whose text annotations contain at least one of the keywords in the query.
61 *
61 *
6262 * @param bugCollection
6363 * the bug collection
6464 * @param filename
8181
8282 /**
8383 * Called when a bug instance contains a query keyword.
84 *
84 *
8585 * @param bugInstance
8686 * the bug instance containing the keyword
8787 * @param filename
9090 protected abstract void match(BugInstance bugInstance, String filename) throws Exception;
9191 }
9292
93 // vim:ts=4
3232 * Recursively search a directory, its subdirectories, etc. Note that the search
3333 * algorithm uses a worklist, so its implementation does not use recursive
3434 * method calls.
35 *
35 *
3636 * @author David Hovemeyer
3737 */
3838 public class RecursiveFileSearch {
39 private String baseDir;
39 private final String baseDir;
4040
41 private FileFilter fileFilter;
41 private final FileFilter fileFilter;
4242
43 private LinkedList<File> directoryWorkList;
43 private final LinkedList<File> directoryWorkList;
4444
45 private HashSet<String> directoriesScanned = new HashSet<String>();
45 private final HashSet<String> directoriesScanned = new HashSet<String>();
4646
47 private List<String> directoriesScannedList = new LinkedList<String>();
47 private final List<String> directoriesScannedList = new LinkedList<String>();
4848
49 private ArrayList<String> resultList;
49 private final ArrayList<String> resultList;
5050
5151 /**
5252 * Constructor.
53 *
53 *
5454 * @param baseDir
5555 * the base directory for the search
5656 * @param fileFilter
7474
7575 /**
7676 * Perform the search.
77 *
77 *
7878 * @return this object
7979 * @throws InterruptedException
8080 * if the thread is interrupted before the search completes
8888
8989 while (!directoryWorkList.isEmpty()) {
9090 File dir = directoryWorkList.removeFirst();
91 if (!dir.isDirectory())
91 if (!dir.isDirectory()) {
9292 continue;
93 }
9394
9495 File[] contentList = dir.listFiles();
95 if (contentList == null)
96 if (contentList == null) {
9697 continue;
98 }
9799 for (File aContentList : contentList) {
98 if (Thread.interrupted())
100 if (Thread.interrupted()) {
99101 throw new InterruptedException();
102 }
100103
101104 File file = aContentList;
102105
136139
137140 }
138141
139 // vim:ts=4
3737 * This distinction is important because some resources which exist in the
3838 * method aren't created in the method: for example, resources passed in as
3939 * parameters.
40 *
40 *
4141 * @author David Hovemeyer
4242 */
4343 public class ResourceCollection<Resource> {
44 private List<Resource> resourceList;
44 private final List<Resource> resourceList;
4545
46 private Map<Location, Resource> locationToResourceMap;
46 private final Map<Location, Resource> locationToResourceMap;
4747
4848 /**
4949 * Constructor. Creates empty collection.
5757 * Add a preexisting resource. That is, one that is not created within the
5858 * analyzed method. Resources passed to the method as parameters fall into
5959 * this category.
60 *
60 *
6161 * @param resource
6262 * the preexisting resource
6363 */
6767
6868 /**
6969 * Add a resource created within the analyzed method.
70 *
70 *
7171 * @param location
7272 * the location
7373 * @param resource
9595
9696 /**
9797 * Get the resource that is created at given location.
98 *
98 *
9999 * @param location
100100 * the Location
101101 * @return the Resource created at that location, or null if no resource is
106106 }
107107 }
108108
109 // vim:ts=4
2323 /**
2424 * A resource creation point. This serves as an embodiment of the resource for
2525 * use with ResourceValueAnalysis.
26 *
26 *
2727 * @author David Hovemeyer
2828 * @see edu.umd.cs.findbugs.ba.ResourceValueAnalysis
2929 * @see ResourceTrackingDetector
4141
4242 /**
4343 * Constructor.
44 *
44 *
4545 * @param location
4646 * location where resource is created
4747 * @param resourceClass
6767 }
6868 }
6969
70 // vim:ts=4
4545 * of created resource is not cleaned up or closed properly. Subclasses should
4646 * override the abstract methods to determine what kinds of resources are
4747 * tracked by the detector.
48 *
48 *
4949 * @author David Hovemeyer
5050 */
5151 public abstract class ResourceTrackingDetector<Resource, ResourceTrackerType extends ResourceTracker<Resource>> implements
52 Detector {
52 Detector {
5353
5454 private static final boolean DEBUG = SystemProperties.getBoolean("rtd.debug");
5555
7272 public abstract void inspectResult(ClassContext classContext, MethodGen methodGen, CFG cfg,
7373 Dataflow<ResourceValueFrame, ResourceValueAnalysis<Resource>> dataflow, Resource resource);
7474
75 @Override
7576 public void visitClassContext(ClassContext classContext) {
7677
7778 final JavaClass jclass = classContext.getJavaClass();
7879 Method[] methodList = jclass.getMethods();
7980 for (Method method : methodList) {
80 if (method.isAbstract() || method.isNative())
81 if (method.isAbstract() || method.isNative()) {
8182 continue;
83 }
8284
8385 MethodGen methodGen = classContext.getMethodGen(method);
84 if (methodGen == null)
86 if (methodGen == null) {
8587 continue;
86
87 if (DEBUG_METHOD_NAME != null && !DEBUG_METHOD_NAME.equals(method.getName()))
88 }
89
90 if (DEBUG_METHOD_NAME != null && !DEBUG_METHOD_NAME.equals(method.getName())) {
8891 continue;
92 }
8993
9094 if (DEBUG) {
9195 System.out.println("----------------------------------------------------------------------");
97101 ResourceTrackerType resourceTracker = getResourceTracker(classContext, method);
98102 boolean mightClose = mightCloseResource(classContext, method, resourceTracker);
99103
100 if (!prescreen(classContext, method, mightClose))
104 if (!prescreen(classContext, method, mightClose)) {
101105 continue;
106 }
102107
103108 ResourceCollection<Resource> resourceCollection = buildResourceCollection(classContext, method, resourceTracker);
104 if (resourceCollection.isEmpty())
109 if (resourceCollection.isEmpty()) {
105110 continue;
111 }
106112
107113 analyzeMethod(classContext, method, resourceTracker, resourceCollection);
108114 } catch (CFGBuilderException e) {
126132 for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
127133 Location location = i.next();
128134 Resource resource = resourceTracker.isResourceCreation(location.getBasicBlock(), location.getHandle(), cpg);
129 if (resource != null)
135 if (resource != null) {
130136 resourceCollection.addCreatedResource(location, resource);
137 }
131138 }
132139
133140 return resourceCollection;
141148
142149 for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
143150 Location location = i.next();
144 if (resourceTracker.mightCloseResource(location.getBasicBlock(), location.getHandle(), cpg))
151 if (resourceTracker.mightCloseResource(location.getBasicBlock(), location.getHandle(), cpg)) {
145152 return true;
153 }
146154
147155 }
148156
153161 ResourceCollection<Resource> resourceCollection) throws CFGBuilderException, DataflowAnalysisException {
154162
155163 MethodGen methodGen = classContext.getMethodGen(method);
156 if (methodGen == null)
164 if (methodGen == null) {
157165 return;
166 }
158167 try {
159168 CFG cfg = classContext.getCFG(method);
160169 DepthFirstSearch dfs = classContext.getDepthFirstSearch(method);
161170
162 if (DEBUG)
171 if (DEBUG) {
163172 System.out.println(SignatureConverter.convertMethodSignature(methodGen));
173 }
164174
165175 for (Iterator<Resource> i = resourceCollection.resourceIterator(); i.hasNext();) {
166176 Resource resource = i.next();
185195 }
186196 }
187197
198 @Override
188199 public void report() {
189200 }
190201
191202 }
192203
193 // vim:ts=3
3939 import org.xml.sax.SAXException;
4040 import org.xml.sax.helpers.DefaultHandler;
4141
42 import edu.umd.cs.findbugs.ba.ClassHash;
4342 import edu.umd.cs.findbugs.filter.AndMatcher;
4443 import edu.umd.cs.findbugs.filter.BugMatcher;
4544 import edu.umd.cs.findbugs.filter.ClassMatcher;
4645 import edu.umd.cs.findbugs.filter.CompoundMatcher;
46 import edu.umd.cs.findbugs.filter.ConfidenceMatcher;
4747 import edu.umd.cs.findbugs.filter.DesignationMatcher;
4848 import edu.umd.cs.findbugs.filter.FieldMatcher;
4949 import edu.umd.cs.findbugs.filter.Filter;
5656 import edu.umd.cs.findbugs.filter.OrMatcher;
5757 import edu.umd.cs.findbugs.filter.PriorityMatcher;
5858 import edu.umd.cs.findbugs.filter.RankMatcher;
59 import edu.umd.cs.findbugs.filter.SourceMatcher;
60 import edu.umd.cs.findbugs.filter.TypeMatcher;
5961 import edu.umd.cs.findbugs.model.ClassFeatureSet;
6062 import edu.umd.cs.findbugs.util.MapCache;
6163 import edu.umd.cs.findbugs.util.Strings;
7577
7678 private static final Logger LOGGER = Logger.getLogger(SAXBugCollectionHandler.class.getName());
7779
78 /**
79 * @param attributes
80 * @param qName
81 * @return
82 */
8380 public String getOptionalAttribute(Attributes attributes, String qName) {
8481 return memoized(attributes.getValue(qName));
8582 }
162159 }
163160
164161 private String memoized(String s) {
165 if (s == null)
162 if (s == null) {
166163 return s;
164 }
167165 String result = cache.get(s);
168 if (result != null)
166 if (result != null) {
169167 return result;
168 }
170169 cache.put(s, s);
171170 return s;
172171 }
173172
174173 private static boolean DEBUG = false;
175174
176 @SuppressWarnings("hiding")
177175 @Override
178176 public void startElement(String uri, String name, String qName, Attributes attributes) throws SAXException {
179177 // URI should always be empty.
185183 // ignore it
186184 } else {
187185 // We should be parsing the outer BugCollection element.
188 if (elementStack.isEmpty() && !qName.equals(topLevelName))
186 if (elementStack.isEmpty() && !qName.equals(topLevelName)) {
189187 throw new SAXException("Invalid top-level element (expected " + topLevelName + ", saw " + qName + ")");
190
191 if (qName.equals(BUG_COLLECTION)) {
188 }
189
190 if (BUG_COLLECTION.equals(qName)) {
192191 BugCollection bugCollection = this.bugCollection;
193192 assert bugCollection != null;
194193 // Read and set the sequence number.
195194 String version = getOptionalAttribute(attributes, "version");
196 if (bugCollection instanceof SortedBugCollection)
195 if (bugCollection instanceof SortedBugCollection) {
197196 bugCollection.setAnalysisVersion(version);
197 }
198198
199199 // Read and set the sequence number.
200200 String sequence = getOptionalAttribute(attributes, "sequence");
225225 }
226226 matcherStack.clear();
227227 pushCompoundMatcher(filter);
228 } else if (qName.equals(PROJECT)) {
228 } else if (PROJECT.equals(qName)) {
229229 Project project = this.project;
230230 assert project != null;
231231 // Project element
232232 String projectName = getOptionalAttribute(attributes, Project.PROJECTNAME_ATTRIBUTE_NAME);
233 if (projectName != null)
233 if (projectName != null) {
234234 project.setProjectName(projectName);
235 }
235236 } else {
236237 String outerElement = elementStack.get(elementStack.size() - 1);
237 if (outerElement.equals(BUG_COLLECTION)) {
238 if (BUG_COLLECTION.equals(outerElement)) {
238239
239240 // Parsing a top-level element of the BugCollection
240 if (qName.equals("BugInstance")) {
241 if ("BugInstance".equals(qName)) {
241242 // BugInstance element - get required type and priority
242243 // attributes
243244 String type = getRequiredAttribute(attributes, "type", qName);
259260 bugInstance.setLastVersion(Long.parseLong(lastVersion));
260261 }
261262
262 if (bugInstance.isDead() && bugInstance.getFirstVersion() > bugInstance.getLastVersion())
263 if (bugInstance.isDead() && bugInstance.getFirstVersion() > bugInstance.getLastVersion()) {
263264 throw new IllegalStateException("huh");
265 }
264266
265267 String introducedByChange = getOptionalAttribute(attributes, "introducedByChange");
266268 if (introducedByChange != null) {
271273 bugInstance.setRemovedByChangeOfPersistingClass(Boolean.parseBoolean(removedByChange));
272274 }
273275 String oldInstanceHash = getOptionalAttribute(attributes, "instanceHash");
274 if (oldInstanceHash == null)
276 if (oldInstanceHash == null) {
275277 oldInstanceHash = getOptionalAttribute(attributes, "oldInstanceHash");
278 }
276279 if (oldInstanceHash != null) {
277280 bugInstance.setOldInstanceHash(oldInstanceHash);
278281 }
288291 }
289292
290293 String isInCloud = getOptionalAttribute(attributes, "isInCloud");
291 if (isInCloud != null)
294 if (isInCloud != null) {
292295 bugInstance.getXmlProps().setIsInCloud(Boolean.parseBoolean(isInCloud));
296 }
293297
294298 String reviewCount = getOptionalAttribute(attributes, "reviews");
295299 if (reviewCount != null) {
301305 bugInstance.getXmlProps().setConsensus(consensus);
302306 }
303307
304 } else if (qName.equals("FindBugsSummary")) {
308 } else if ("FindBugsSummary".equals(qName)) {
305309 BugCollection bugCollection = this.bugCollection;
306310 assert bugCollection != null;
307311 String timestamp = getRequiredAttribute(attributes, "timestamp", qName);
308312 String vmVersion = getOptionalAttribute(attributes, "vm_version");
309313 String totalClasses = getOptionalAttribute(attributes, "total_classes");
310 if (totalClasses != null && totalClasses.length() > 0)
314 if (totalClasses != null && totalClasses.length() > 0) {
311315 bugCollection.getProjectStats().setTotalClasses(Integer.parseInt(totalClasses));
316 }
312317
313318 String totalSize = getOptionalAttribute(attributes, "total_size");
314 if (totalSize != null && totalSize.length() > 0)
319 if (totalSize != null && totalSize.length() > 0) {
315320 bugCollection.getProjectStats().setTotalSize(Integer.parseInt(totalSize));
321 }
316322
317323 String referencedClasses = getOptionalAttribute(attributes, "referenced_classes");
318 if (referencedClasses != null && referencedClasses.length() > 0)
324 if (referencedClasses != null && referencedClasses.length() > 0) {
319325 bugCollection.getProjectStats().setReferencedClasses(Integer.parseInt(referencedClasses));
326 }
320327 bugCollection.getProjectStats().setVMVersion(vmVersion);
321328 try {
322329 bugCollection.getProjectStats().setTimestamp(timestamp);
324331 throw new SAXException("Unparseable sequence number: '" + timestamp + "'", e);
325332 }
326333 }
327 } else if (outerElement.equals("BugInstance")) {
334 } else if ("BugInstance".equals(outerElement)) {
328335 parseBugInstanceContents(qName, attributes);
329 } else if (outerElement.equals("Method") || outerElement.equals("Field") || outerElement.equals("Class")
330 || outerElement.equals("Type")) {
331 if (qName.equals("SourceLine")) {
336 } else if ("Method".equals(outerElement) || "Field".equals(outerElement) || "Class".equals(outerElement)
337 || "Type".equals(outerElement)) {
338 if ("SourceLine".equals(qName)) {
332339 // package member elements can contain nested SourceLine
333340 // elements.
334341 bugAnnotationWithSourceLines.setSourceLines(createSourceLineAnnotation(qName, attributes));
335342 }
336 } else if (outerElement.equals(BugCollection.ERRORS_ELEMENT_NAME)) {
337 if (qName.equals(BugCollection.ANALYSIS_ERROR_ELEMENT_NAME) || qName.equals(BugCollection.ERROR_ELEMENT_NAME)) {
343 } else if (BugCollection.ERRORS_ELEMENT_NAME.equals(outerElement)) {
344 if (BugCollection.ANALYSIS_ERROR_ELEMENT_NAME.equals(qName) || BugCollection.ERROR_ELEMENT_NAME.equals(qName)) {
338345 analysisError = new AnalysisError("Unknown error");
339346 stackTrace.clear();
340347 }
341 } else if (outerElement.equals("FindBugsSummary") && qName.equals("PackageStats")) {
348 } else if ("FindBugsSummary".equals(outerElement) && "PackageStats".equals(qName)) {
342349 BugCollection bugCollection = this.bugCollection;
343350 assert bugCollection != null;
344351 String packageName = getRequiredAttribute(attributes, "package", qName);
346353 int size = Integer.parseInt(getRequiredAttribute(attributes, "total_size", qName));
347354 bugCollection.getProjectStats().putPackageStats(packageName, numClasses, size);
348355
349 } else if (outerElement.equals("PackageStats")) {
356 } else if ("PackageStats".equals(outerElement)) {
350357 BugCollection bugCollection = this.bugCollection;
351358 assert bugCollection != null;
352 if (qName.equals("ClassStats")) {
359 if ("ClassStats".equals(qName)) {
353360 String className = getRequiredAttribute(attributes, "class", qName);
354361 Boolean isInterface = Boolean.valueOf(getRequiredAttribute(attributes, "interface", qName));
355362 int size = Integer.parseInt(getRequiredAttribute(attributes, "size", qName));
359366
360367 } else if (isTopLevelFilter(outerElement) || isCompoundElementTag(outerElement)) {
361368 parseMatcher(qName, attributes);
362 } else if (outerElement.equals("ClassFeatures")) {
363 if (qName.equals(ClassFeatureSet.ELEMENT_NAME)) {
369 } else if ("ClassFeatures".equals(outerElement)) {
370 if (ClassFeatureSet.ELEMENT_NAME.equals(qName)) {
364371 String className = getRequiredAttribute(attributes, "class", qName);
365372 classFeatureSet = new ClassFeatureSet();
366373 classFeatureSet.setClassName(className);
367374 }
368 } else if (outerElement.equals(ClassFeatureSet.ELEMENT_NAME)) {
369 if (qName.equals(ClassFeatureSet.FEATURE_ELEMENT_NAME)) {
375 } else if (ClassFeatureSet.ELEMENT_NAME.equals(outerElement)) {
376 if (ClassFeatureSet.FEATURE_ELEMENT_NAME.equals(qName)) {
370377 String value = getRequiredAttribute(attributes, "value", qName);
371378 classFeatureSet.addFeature(value);
372379 }
373 } else if (outerElement.equals(BugCollection.HISTORY_ELEMENT_NAME)) {
374 if (qName.equals(AppVersion.ELEMENT_NAME)) {
380 } else if (BugCollection.HISTORY_ELEMENT_NAME.equals(outerElement)) {
381 if (AppVersion.ELEMENT_NAME.equals(qName)) {
375382 BugCollection bugCollection = this.bugCollection;
376383 assert bugCollection != null;
377384
382389 String codeSize = getOptionalAttribute(attributes, "codeSize");
383390 String numClasses = getOptionalAttribute(attributes, "numClasses");
384391 AppVersion appVersion = new AppVersion(Long.parseLong(sequence));
385 if (timestamp != null)
392 if (timestamp != null) {
386393 appVersion.setTimestamp(Long.parseLong(timestamp));
387 if (releaseName != null)
394 }
395 if (releaseName != null) {
388396 appVersion.setReleaseName(releaseName);
389 if (codeSize != null)
397 }
398 if (codeSize != null) {
390399 appVersion.setCodeSize(Integer.parseInt(codeSize));
391 if (numClasses != null)
400 }
401 if (numClasses != null) {
392402 appVersion.setNumClasses(Integer.parseInt(numClasses));
403 }
393404
394405 bugCollection.addAppVersion(appVersion);
395406 } catch (NumberFormatException e) {
396407 throw new SAXException("Invalid AppVersion element", e);
397408 }
398409 }
399 } else if (outerElement.equals(BugCollection.PROJECT_ELEMENT_NAME)) {
410 } else if (BugCollection.PROJECT_ELEMENT_NAME.equals(outerElement)) {
400411 Project project = this.project;
401412 assert project != null;
402 if (qName.equals(Project.CLOUD_ELEMENT_NAME)) {
413 if (Project.CLOUD_ELEMENT_NAME.equals(qName)) {
403414 String cloudId = getRequiredAttribute(attributes, Project.CLOUD_ID_ATTRIBUTE_NAME, qName);
404415 project.setCloudId(cloudId);
405416 if(bugCollection != null){
409420 }
410421 bugCollection.setXmlCloudDetails(Collections.unmodifiableMap(map));
411422 }
412 } else if (qName.equals(Project.PLUGIN_ELEMENT_NAME)) {
423 } else if (Project.PLUGIN_ELEMENT_NAME.equals(qName)) {
413424 String pluginId = getRequiredAttribute(attributes, Project.PLUGIN_ID_ATTRIBUTE_NAME, qName);
414425 Boolean enabled = Boolean.valueOf(getRequiredAttribute(attributes, Project.PLUGIN_STATUS_ELEMENT_NAME, qName));
415426 project.setPluginStatusTrinary(pluginId, enabled);
416427 }
417428
418 } else if (outerElement.equals(Project.CLOUD_ELEMENT_NAME)) {
419 if (qName.equals(Project.CLOUD_PROPERTY_ELEMENT_NAME)) {
429 } else if (Project.CLOUD_ELEMENT_NAME.equals(outerElement)) {
430 if (Project.CLOUD_PROPERTY_ELEMENT_NAME.equals(qName)) {
420431 cloudPropertyKey = getRequiredAttribute(attributes, "key", qName);
421432 }
422433
433444 }
434445
435446 private boolean isTopLevelFilter(String qName) {
436 return qName.equals(FIND_BUGS_FILTER) || qName.equals("SuppressionFilter");
447 return FIND_BUGS_FILTER.equals(qName) || "SuppressionFilter".equals(qName);
437448 }
438449
439450 private void addMatcher(Matcher m) {
440 if (m == null)
451 if (m == null) {
441452 throw new IllegalArgumentException("matcher must not be null");
453 }
442454
443455 CompoundMatcher peek = matcherStack.peek();
444 if (peek == null)
456 if (peek == null) {
445457 throw new NullPointerException("Top of stack is null");
458 }
446459 peek.addChild(m);
447460 if (nextMatchedIsDisabled) {
448 if (peek instanceof Filter)
461 if (peek instanceof Filter) {
449462 ((Filter) peek).disable(m);
450 else
463 } else {
451464 assert false;
465 }
452466 nextMatchedIsDisabled = false;
453467 }
454468 }
459473 }
460474
461475 private void pushCompoundMatcher(CompoundMatcher m) {
462 if (m == null)
476 if (m == null) {
463477 throw new IllegalArgumentException("matcher must not be null");
478 }
464479 matcherStack.push(m);
465480 }
466481
468483 private final Set<String> outerElementTags = unmodifiableSet(new HashSet<String>(asList("And", "Match", "Or", "Not")));
469484
470485 private void parseMatcher(String qName, Attributes attributes) throws SAXException {
471 if (DEBUG)
486 if (DEBUG) {
472487 System.out.println(elementStack + " " + qName + " " + matcherStack);
488 }
473489 String disabled = getOptionalAttribute(attributes, "disabled");
474490 nextMatchedIsDisabled = "true".equals(disabled);
475 if (qName.equals("Bug")) {
491 if ("Bug".equals(qName)) {
476492 addMatcher(new BugMatcher(getOptionalAttribute(attributes, "code"), getOptionalAttribute(attributes, "pattern"),
477493 getOptionalAttribute(attributes, "category")));
478 } else if (qName.equals("Class")) {
479 addMatcher(new ClassMatcher(getRequiredAttribute(attributes, "name", qName)));
480 } else if (qName.equals("FirstVersion")) {
494 } else if ("Class".equals(qName)) {
495 String role = getOptionalAttribute(attributes, "role");
496 addMatcher(new ClassMatcher(getRequiredAttribute(attributes, "name", qName), role));
497 } else if ("Type".equals(qName)) {
498 String role = getOptionalAttribute(attributes, "role");
499 String typeParameters = getOptionalAttribute(attributes, "typeParameters");
500 addMatcher(new TypeMatcher(getRequiredAttribute(attributes, "descriptor", qName), role, typeParameters));
501 } else if ("FirstVersion".equals(qName)) {
481502 addMatcher(new FirstVersionMatcher(getRequiredAttribute(attributes, "value", qName), getRequiredAttribute(attributes,
482503 "relOp", qName)));
483 } else if (qName.equals("LastVersion")) {
504 } else if ("LastVersion".equals(qName)) {
484505 addMatcher(new LastVersionMatcher(getRequiredAttribute(attributes, "value", qName), getRequiredAttribute(attributes,
485506 "relOp", qName)));
486 } else if (qName.equals("Designation")) {
507 } else if ("Designation".equals(qName)) {
487508 addMatcher(new DesignationMatcher(getRequiredAttribute(attributes, "designation", qName)));
488 } else if (qName.equals("BugCode")) {
509 } else if ("BugCode".equals(qName)) {
489510 addMatcher(new BugMatcher(getRequiredAttribute(attributes, "name", qName), "", ""));
490 } else if (qName.equals("Local")) {
511 } else if ("Local".equals(qName)) {
491512 addMatcher(new LocalMatcher(getRequiredAttribute(attributes, "name", qName)));
492 } else if (qName.equals("BugPattern")) {
513 } else if ("BugPattern".equals(qName)) {
493514 addMatcher(new BugMatcher("", getRequiredAttribute(attributes, "name", qName), ""));
494 } else if (qName.equals("Priority") || qName.equals("Confidence")) {
515 } else if ("Priority".equals(qName)) {
495516 addMatcher(new PriorityMatcher(getRequiredAttribute(attributes, "value", qName)));
496 } else if (qName.equals("Rank")) {
517 } else if ("Confidence".equals(qName)) {
518 addMatcher(new ConfidenceMatcher(getRequiredAttribute(attributes, "value", qName)));
519 } else if ("Rank".equals(qName)) {
497520 addMatcher(new RankMatcher(getRequiredAttribute(attributes, "value", qName)));
498 } else if (qName.equals("Package")) {
521 } else if ("Package".equals(qName)) {
499522 String pName = getRequiredAttribute(attributes, "name", qName);
500523 pName = pName.startsWith("~") ? pName : "~" + pName.replace(".", "\\.");
501524 addMatcher(new ClassMatcher(pName + "\\.[^.]+"));
502 } else if (qName.equals("Method")) {
525 } else if ("Method".equals(qName)) {
503526 String name = getOptionalAttribute(attributes, "name");
504527 String params = getOptionalAttribute(attributes, "params");
505528 String returns = getOptionalAttribute(attributes, "returns");
506529 String role = getOptionalAttribute(attributes, "role");
507530 addMatcher(new MethodMatcher(name, params, returns, role));
508 } else if (qName.equals("Field")) {
531 } else if ("Field".equals(qName)) {
509532 String name = getOptionalAttribute(attributes, "name");
510533 String type = getOptionalAttribute(attributes, "type");
511 addMatcher(new FieldMatcher(name, type));
512 } else if (qName.equals("Or")) {
534 String role = getOptionalAttribute(attributes, "role");
535 addMatcher(new FieldMatcher(name, type, role));
536 } else if ("Or".equals(qName)) {
513537 CompoundMatcher matcher = new OrMatcher();
514538 pushCompoundMatcherAsChild(matcher);
515 } else if (qName.equals("And") || qName.equals("Match")) {
539 } else if ("And".equals(qName) || "Match".equals(qName)) {
516540 AndMatcher matcher = new AndMatcher();
517541 pushCompoundMatcherAsChild(matcher);
518 if (qName.equals("Match")) {
542 if ("Match".equals(qName)) {
519543 String classregex = getOptionalAttribute(attributes, "classregex");
520544 String classMatch = getOptionalAttribute(attributes, "class");
521545
522 if (classregex != null)
546 if (classregex != null) {
523547 addMatcher(new ClassMatcher("~" + classregex));
524 else if (classMatch != null)
548 } else if (classMatch != null) {
525549 addMatcher(new ClassMatcher(classMatch));
526 }
527 } else if(qName.equals("Not")) {
550 }
551 }
552 } else if("Not".equals(qName)) {
528553 NotMatcher matcher = new NotMatcher();
529554 pushCompoundMatcherAsChild(matcher);
555 } else if ("Source".equals(qName)) {
556 addMatcher(new SourceMatcher(getRequiredAttribute(attributes, "name", qName)));
530557 }
531558 nextMatchedIsDisabled = false;
532559 }
534561 private void parseBugInstanceContents(String qName, Attributes attributes) throws SAXException {
535562 // Parsing an attribute or property of a BugInstance
536563 BugAnnotation bugAnnotation = null;
537 if (qName.equals("Class")) {
564 if ("Class".equals(qName)) {
538565 String className = getRequiredAttribute(attributes, "classname", qName);
539566 bugAnnotation = bugAnnotationWithSourceLines = new ClassAnnotation(className);
540 } else if (qName.equals("Type")) {
567 } else if ("Type".equals(qName)) {
541568 String typeDescriptor = getRequiredAttribute(attributes, "descriptor", qName);
542569 TypeAnnotation typeAnnotation;
543570 bugAnnotation = bugAnnotationWithSourceLines = typeAnnotation = new TypeAnnotation(typeDescriptor);
544571 String typeParameters = getOptionalAttribute(attributes, "typeParameters");
545 if (typeParameters != null)
572 if (typeParameters != null) {
546573 typeAnnotation.setTypeParameters(Strings.unescapeXml(typeParameters));
547
548 } else if (qName.equals("Method") || qName.equals("Field")) {
574 }
575
576 } else if ("Method".equals(qName) || "Field".equals(qName)) {
549577 String classname = getRequiredAttribute(attributes, "classname", qName);
550578 String fieldOrMethodName = getRequiredAttribute(attributes, "name", qName);
551579 String signature = getRequiredAttribute(attributes, "signature", qName);
552 if (qName.equals("Method")) {
580 if ("Method".equals(qName)) {
553581 String isStatic = getOptionalAttribute(attributes, "isStatic");
554582 if (isStatic == null) {
555583 isStatic = "false"; // Hack for old data
560588
561589 } else {
562590 String isStatic = getRequiredAttribute(attributes, "isStatic", qName);
591 String sourceSignature = getOptionalAttribute(attributes, "sourceSignature");
563592 bugAnnotation = bugAnnotationWithSourceLines = new FieldAnnotation(classname, fieldOrMethodName, signature,
564 Boolean.valueOf(isStatic));
565 }
566
567 } else if (qName.equals("SourceLine")) {
593 sourceSignature, Boolean.valueOf(isStatic));
594 }
595
596 } else if ("SourceLine".equals(qName)) {
568597 SourceLineAnnotation sourceAnnotation = createSourceLineAnnotation(qName, attributes);
569 if (!sourceAnnotation.isSynthetic())
598 if (!sourceAnnotation.isSynthetic()) {
570599 bugAnnotation = sourceAnnotation;
571 } else if (qName.equals("Int")) {
600 }
601 } else if ("Int".equals(qName)) {
572602 try {
573603 String value = getRequiredAttribute(attributes, "value", qName);
574604 bugAnnotation = new IntAnnotation(Integer.parseInt(value));
575605 } catch (NumberFormatException e) {
576606 throw new SAXException("Bad integer value in Int");
577607 }
578 } else if (qName.equals("String")) {
608 } else if ("String".equals(qName)) {
579609 String value = getRequiredAttribute(attributes, "value", qName);
580610 bugAnnotation = StringAnnotation.fromXMLEscapedString(value);
581 } else if (qName.equals("LocalVariable")) {
611 } else if ("LocalVariable".equals(qName)) {
582612 try {
583613 String varName = getRequiredAttribute(attributes, "name", qName);
584614 int register = Integer.parseInt(getRequiredAttribute(attributes, "register", qName));
587617 } catch (NumberFormatException e) {
588618 throw new SAXException("Invalid integer value in attribute of LocalVariable element");
589619 }
590 } else if (qName.equals("Property")) {
620 } else if ("Property".equals(qName)) {
591621 // A BugProperty.
592622 String propName = getRequiredAttribute(attributes, "name", qName);
593623 String propValue = getRequiredAttribute(attributes, "value", qName);
594624 bugInstance.setProperty(propName, propValue);
595 } else if (qName.equals("UserAnnotation")) {
625 } else if ("UserAnnotation".equals(qName)) {
596626 // ignore AnnotationText for now; will handle in endElement
597627 String s = getOptionalAttribute(attributes, "designation"); // optional
598628 if (s != null) {
599629 bugInstance.setUserDesignationKey(s, null);
600630 }
601631 s = getOptionalAttribute(attributes, "user"); // optional
602 if (s != null)
632 if (s != null) {
603633 bugInstance.setUser(s);
634 }
604635 s = getOptionalAttribute(attributes, "timestamp"); // optional
605 if (s != null)
636 if (s != null) {
606637 try {
607638 long timestamp = Long.parseLong(s);
608639 bugInstance.setUserAnnotationTimestamp(timestamp);
611642 // designation.
612643 // but is there anyplace to report this?
613644 }
645 }
614646 s = getOptionalAttribute(attributes, "needsSync"); // optional
615 if (s == null || s.equals("false"))
647 if (s == null || "false".equals(s)) {
616648 bugInstance.setUserAnnotationDirty(false);
617
618 } else
649 }
650
651 } else {
619652 throw new SAXException("Unknown bug annotation named " + qName);
653 }
620654
621655 if (bugAnnotation != null) {
622 String role = getOptionalAttribute(attributes, "role");
623 if (role != null)
624 bugAnnotation.setDescription(role);
625656 setAnnotationRole(attributes, bugAnnotation);
626657 bugInstance.add(bugAnnotation);
627658 }
637668 return value;
638669 }
639670
640 /**
671 /*
641672 * Extract a hash value from an element.
642673 *
643674 * @param qName
646677 * element attributes
647678 * @return the decoded hash value
648679 * @throws SAXException
649 */
680 *
650681 private byte[] extractHash(String qName, Attributes attributes) throws SAXException {
651682 String encodedHash = getRequiredAttribute(attributes, "value", qName);
652683 byte[] hash;
657688 throw new SAXException("Invalid class hash", e);
658689 }
659690 return hash;
660 }
691 }*/
661692
662693 private void setAnnotationRole(Attributes attributes, BugAnnotation bugAnnotation) {
663694 String role = getOptionalAttribute(attributes, "role");
664 if (role != null)
695 if (role != null) {
665696 bugAnnotation.setDescription(role);
697 }
666698 }
667699
668700 private SourceLineAnnotation createSourceLineAnnotation(String qName, Attributes attributes) throws SAXException {
669701 String classname = getRequiredAttribute(attributes, "classname", qName);
670702 String sourceFile = getOptionalAttribute(attributes, "sourcefile");
671 if (sourceFile == null)
703 if (sourceFile == null) {
672704 sourceFile = SourceLineAnnotation.UNKNOWN_SOURCE_FILE;
705 }
673706 String startLine = getOptionalAttribute(attributes, "start"); // "start"/"end"
674 // are now
675 // optional
707 // are now
708 // optional
676709 String endLine = getOptionalAttribute(attributes, "end"); // (were too
677 // many "-1"s
678 // in the xml)
710 // many "-1"s
711 // in the xml)
679712 String startBytecode = getOptionalAttribute(attributes, "startBytecode");
680713 String endBytecode = getOptionalAttribute(attributes, "endBytecode");
681714 String synthetic = getOptionalAttribute(attributes, "synthetic");
687720 int eb = endBytecode != null ? Integer.parseInt(endBytecode) : -1;
688721
689722 SourceLineAnnotation s = new SourceLineAnnotation(classname, sourceFile, sl, el, sb, eb);
690 if ("true".equals(synthetic))
723 if ("true".equals(synthetic)) {
691724 s.setSynthetic(true);
725 }
692726 return s;
693727 } catch (NumberFormatException e) {
694728 throw new SAXException("Bad integer value in SourceLine element", e);
695729 }
696730 }
697731
698 @SuppressWarnings("hiding")
699732 @Override
700733 public void endElement(String uri, String name, String qName) throws SAXException {
701734 // URI should always be empty.
705738 nestingOfIgnoredElements--;
706739 } else if (nestingOfIgnoredElements > 0) {
707740 // ignore it
708 } else if (qName.equals("Project")) {
741 } else if ("Project".equals(qName)) {
709742 // noop
710743 } else if (elementStack.size() > 1) {
711744 String outerElement = elementStack.get(elementStack.size() - 2);
712745
713746 if (isTopLevelFilter(qName) || isCompoundElementTag(qName)) {
714 if (DEBUG)
747 if (DEBUG) {
715748 System.out.println(" ending " + elementStack + " " + qName + " " + matcherStack);
749 }
716750
717751 matcherStack.pop();
718 } else if (outerElement.equals(BUG_COLLECTION)) {
752 } else if (BUG_COLLECTION.equals(outerElement)) {
719753 BugCollection bugCollection = this.bugCollection;
720754 assert bugCollection != null;
721 if (qName.equals("BugInstance")) {
755 if ("BugInstance".equals(qName)) {
722756 bugCollection.add(bugInstance, false);
723757 }
724 } else if (outerElement.equals(PROJECT)) {
758 } else if (PROJECT.equals(outerElement)) {
725759 Project project = this.project;
726760 assert project != null;
727 if (qName.equals("Jar"))
761 if ("Jar".equals(qName)) {
728762 project.addFile(makeAbsolute(getTextContents()));
729 else if (qName.equals("SrcDir"))
763 } else if ("SrcDir".equals(qName)) {
730764 project.addSourceDir(makeAbsolute(getTextContents()));
731 else if (qName.equals("AuxClasspathEntry"))
765 } else if ("AuxClasspathEntry".equals(qName)) {
732766 project.addAuxClasspathEntry(makeAbsolute(getTextContents()));
733
734
735
736 } else if (outerElement.equals(Project.CLOUD_ELEMENT_NAME) && qName.equals(Project.CLOUD_PROPERTY_ELEMENT_NAME)) {
767 }
768
769
770
771 } else if (Project.CLOUD_ELEMENT_NAME.equals(outerElement) && Project.CLOUD_PROPERTY_ELEMENT_NAME.equals(qName)) {
737772 Project project = this.project;
738773 assert project != null;
739774 assert cloudPropertyKey != null;
740775 project.getCloudProperties().setProperty(cloudPropertyKey, getTextContents());
741776 cloudPropertyKey = null;
742 } else if (outerElement.equals("BugInstance")) {
743 if (qName.equals("UserAnnotation")) {
777 } else if ("BugInstance".equals(outerElement)) {
778 if ("UserAnnotation".equals(qName)) {
744779 bugInstance.setAnnotationText(getTextContents(), null);
745780 }
746 } else if (outerElement.equals(BugCollection.ERRORS_ELEMENT_NAME)) {
781 } else if (BugCollection.ERRORS_ELEMENT_NAME.equals(outerElement)) {
747782 BugCollection bugCollection = this.bugCollection;
748783 assert bugCollection != null;
749 if (qName.equals(BugCollection.ANALYSIS_ERROR_ELEMENT_NAME)) {
784 if (BugCollection.ANALYSIS_ERROR_ELEMENT_NAME.equals(qName)) {
750785 analysisError.setMessage(getTextContents());
751786 bugCollection.addError(analysisError);
752 } else if (qName.equals(BugCollection.ERROR_ELEMENT_NAME)) {
787 } else if (BugCollection.ERROR_ELEMENT_NAME.equals(qName)) {
753788 if (stackTrace.size() > 0) {
754789 analysisError.setStackTrace(stackTrace.toArray(new String[stackTrace.size()]));
755790 }
756791 bugCollection.addError(analysisError);
757 } else if (qName.equals(BugCollection.MISSING_CLASS_ELEMENT_NAME)) {
792 } else if (BugCollection.MISSING_CLASS_ELEMENT_NAME.equals(qName)) {
758793 bugCollection.addMissingClass(getTextContents());
759794 }
760795
761 } else if (outerElement.equals(BugCollection.ERROR_ELEMENT_NAME)) {
762 if (qName.equals(BugCollection.ERROR_MESSAGE_ELEMENT_NAME)) {
796 } else if (BugCollection.ERROR_ELEMENT_NAME.equals(outerElement)) {
797 if (BugCollection.ERROR_MESSAGE_ELEMENT_NAME.equals(qName)) {
763798 analysisError.setMessage(getTextContents());
764 } else if (qName.equals(BugCollection.ERROR_EXCEPTION_ELEMENT_NAME)) {
799 } else if (BugCollection.ERROR_EXCEPTION_ELEMENT_NAME.equals(qName)) {
765800 analysisError.setExceptionMessage(getTextContents());
766 } else if (qName.equals(BugCollection.ERROR_STACK_TRACE_ELEMENT_NAME)) {
801 } else if (BugCollection.ERROR_STACK_TRACE_ELEMENT_NAME.equals(qName)) {
767802 stackTrace.add(getTextContents());
768803 }
769 } else if (outerElement.equals("ClassFeatures")) {
770 if (qName.equals(ClassFeatureSet.ELEMENT_NAME)) {
804 } else if ("ClassFeatures".equals(outerElement)) {
805 if (ClassFeatureSet.ELEMENT_NAME.equals(qName)) {
771806 BugCollection bugCollection = this.bugCollection;
772807 assert bugCollection != null;
773808
782817
783818 private String makeAbsolute(String possiblyRelativePath) {
784819 if (possiblyRelativePath.contains("://") || possiblyRelativePath.startsWith("http:")
785 || possiblyRelativePath.startsWith("https:") || possiblyRelativePath.startsWith("file:"))
820 || possiblyRelativePath.startsWith("https:") || possiblyRelativePath.startsWith("file:")) {
786821 return possiblyRelativePath;
787 if (base == null)
822 }
823 if (base == null) {
788824 return possiblyRelativePath;
789 if (new File(possiblyRelativePath).isAbsolute())
825 }
826 if (new File(possiblyRelativePath).isAbsolute()) {
790827 return possiblyRelativePath;
828 }
791829
792830 return new File(base.getParentFile(), possiblyRelativePath).getAbsolutePath();
793831 }
799837
800838 private String getRequiredAttribute(Attributes attributes, String attrName, String elementName) throws SAXException {
801839 String value = attributes.getValue(attrName);
802 if (value == null)
840 if (value == null) {
803841 throw new SAXException(elementName + " element missing " + attrName + " attribute");
842 }
804843 return memoized(Strings.unescapeXml(value));
805844 }
806845
807846 }
808
809 // vim:ts=4
4343 public class SelfCalls {
4444 private static final boolean DEBUG = SystemProperties.getBoolean("selfcalls.debug");
4545
46 private ClassContext classContext;
47
48 private CallGraph callGraph;
49
50 private HashSet<Method> calledMethodSet;
46 private final ClassContext classContext;
47
48 private final CallGraph callGraph;
49
50 private final HashSet<Method> calledMethodSet;
5151
5252 private boolean hasSynchronization;
5353
5454 /**
5555 * Constructor.
56 *
56 *
5757 * @param classContext
5858 * the ClassContext for the class
5959 */
7171 JavaClass jclass = classContext.getJavaClass();
7272 Method[] methods = jclass.getMethods();
7373
74 if (DEBUG)
74 if (DEBUG) {
7575 System.out.println("Class has " + methods.length + " methods");
76 }
7677
7778 // Add call graph nodes for all methods
7879 for (Method method : methods) {
7980 callGraph.addNode(method);
8081 }
81 if (DEBUG)
82 if (DEBUG) {
8283 System.out.println("Added " + callGraph.getNumVertices() + " nodes to graph");
84 }
8385
8486 // Scan methods for self calls
8587 for (Method method : methods) {
8688 MethodGen mg = classContext.getMethodGen(method);
87 if (mg == null)
89 if (mg == null) {
8890 continue;
91 }
8992
9093 scan(callGraph.getNodeForMethod(method));
9194 }
9295
93 if (DEBUG)
96 if (DEBUG) {
9497 System.out.println("Found " + callGraph.getNumEdges() + " self calls");
98 }
9599 }
96100
97101 /**
112116 * Determine whether we are interested in calls for the given method.
113117 * Subclasses may override. The default version returns true for every
114118 * method.
115 *
119 *
116120 * @param method
117121 * the method
118122 * @return true if we want call sites for the method, false if not
126130 */
127131 public Iterator<CallSite> callSiteIterator() {
128132 return new Iterator<CallSite>() {
129 private Iterator<CallGraphEdge> iter = callGraph.edgeIterator();
130
133 private final Iterator<CallGraphEdge> iter = callGraph.edgeIterator();
134
135 @Override
131136 public boolean hasNext() {
132137 return iter.hasNext();
133138 }
134139
140 @Override
135141 public CallSite next() {
136142 return iter.next().getCallSite();
137143 }
138144
145 @Override
139146 public void remove() {
140147 iter.remove();
141148 }
151158
152159 /**
153160 * Scan a method for self call sites.
154 *
161 *
155162 * @param node
156163 * the CallGraphNode for the method to be scanned
157164 */
159166 Method method = node.getMethod();
160167 CFG cfg = classContext.getCFG(method);
161168
162 if (method.isSynchronized())
169 if (method.isSynchronized()) {
163170 hasSynchronization = true;
171 }
164172
165173 Iterator<BasicBlock> i = cfg.blockIterator();
166174 while (i.hasNext()) {
201209 // Not a big deal for now, as we are mostly just interested in calls
202210 // to private methods, for which we will definitely see the right
203211 // called class name.
204 if (!calledClassName.equals(jclass.getClassName()))
212 if (!calledClassName.equals(jclass.getClassName())) {
205213 return null;
214 }
206215
207216 String calledMethodName = inv.getMethodName(cpg);
208217 String calledMethodSignature = inv.getSignature(cpg);
224233 // Hmm...no matching method found.
225234 // This is almost certainly because the named method
226235 // was inherited from a superclass.
227 if (DEBUG)
236 if (DEBUG) {
228237 System.out.println("No method found for " + calledClassName + "." + calledMethodName + " : " + calledMethodSignature);
238 }
229239 return null;
230240 }
231241 }
232242
233 // vim:ts=4
2121
2222 /**
2323 * Show command line help.
24 *
24 *
2525 * @author David Hovemeyer
2626 */
2727 public class ShowHelp {
28
28
2929 public static void main(String[] args) {
3030
3131 System.out.println("FindBugs version " + Version.RELEASE + ", " + Version.WEBSITE);
32
32
3333 DetectorFactoryCollection.instance();
3434 System.out.println("Command line options");
35
35
3636 TreeSet<FindBugsMain> cmds = new TreeSet<FindBugsMain>();
37 for(Plugin p : Plugin.getAllPlugins())
38 for(FindBugsMain m : p.getAllFindBugsMain())
37 for(Plugin p : Plugin.getAllPlugins()) {
38 for(FindBugsMain m : p.getAllFindBugsMain()) {
3939 cmds.add(m);
40 for(FindBugsMain m : cmds)
41 System.out.printf("fb %-12s %-12s %s%n", m.cmd, m.kind, m.description);
42
43 // System.out.println();
44 // System.out.println("GUI Options:");
45 // FindBugsCommandLine guiCmd = new FindBugsCommandLine(true) {
46 // };
47 // guiCmd.printUsage(System.out);
48 // System.out.println();
49 // System.out.println("TextUI Options:");
50 // FindBugs.showCommandLineOptions();
40 }
41 }
42 for(FindBugsMain m : cmds) {
43 System.out.printf("fb %-12s %-12s %s%n", m.cmd, m.kind, m.description);
44 }
45
46 // System.out.println();
47 // System.out.println("GUI Options:");
48 // FindBugsCommandLine guiCmd = new FindBugsCommandLine(true) {
49 // };
50 // guiCmd.printUsage(System.out);
51 // System.out.println();
52 // System.out.println("TextUI Options:");
53 // FindBugs.showCommandLineOptions();
5154 System.out.println();
5255 showGeneralOptions();
53
56
5457 }
5558
5659 public static void showSynopsis() {
5861 }
5962
6063 public static void showGeneralOptions() {
61
64
6265 System.out.println("General options:");
6366 System.out.println(" -jvmArgs args Pass args to JVM");
6467 System.out.println(" -maxHeap size Maximum Java heap size in megabytes (default=768)");
6568 System.out.println(" -javahome <dir> Specify location of JRE");
66
69
6770 }
6871 }
2525 /**
2626 * Very sloppy bug comparator: if the warnings are of the same type, and in the
2727 * same class/method/field, assume they are the same.
28 *
28 *
2929 * @author David Hovemeyer
3030 */
3131 public class SloppyBugComparator implements WarningComparator {
3232
3333 private static final boolean DEBUG = SystemProperties.getBoolean("sloppyComparator.debug");
3434
35 private static final long serialVersionUID = 1L;
36
3735 private ClassNameRewriter classNameRewriter = IdentityClassNameRewriter.instance();
3836
39 /**
40 * Constructor.
41 */
4237 public SloppyBugComparator() {
4338 }
4439
40 @Override
4541 public void setClassNameRewriter(ClassNameRewriter classNameRewriter) {
4642 this.classNameRewriter = classNameRewriter;
4743 }
4844
4945 private int compareNullElements(Object lhs, Object rhs) {
50 if (lhs == null && rhs == null)
46 if (lhs == null && rhs == null) {
5147 return 0;
52 else
48 } else {
5349 return (lhs == null) ? -1 : 1;
50 }
5451 }
5552
5653 /**
5754 * Compare class annotations.
58 *
55 *
5956 * @param lhs
6057 * left hand class annotation
6158 * @param rhs
7067 String lhsClassName = classNameRewriter.rewriteClassName(lhs.getClassName());
7168 String rhsClassName = classNameRewriter.rewriteClassName(rhs.getClassName());
7269
73 if (DEBUG)
70 if (DEBUG) {
7471 System.err.println("Comparing " + lhsClassName + " and " + rhsClassName);
72 }
7573
7674 int cmp = lhsClassName.compareTo(rhsClassName);
77 if (DEBUG)
75 if (DEBUG) {
7876 System.err.println("\t==> " + cmp);
77 }
7978 return cmp;
8079 }
8180
8382 if (lhs == null || rhs == null) {
8483 return compareNullElements(lhs, rhs);
8584 }
86
8785 lhs = convertMethod(lhs);
8886 rhs = convertMethod(rhs);
89
9087 return lhs.compareTo(rhs);
9188 }
9289
9491 if (lhs == null || rhs == null) {
9592 return compareNullElements(lhs, rhs);
9693 }
97
9894 lhs = convertField(lhs);
9995 rhs = convertField(rhs);
100
101 if (DEBUG)
96 if (DEBUG) {
10297 System.err.println("Compare fields: " + lhs + " and " + rhs);
103
98 }
10499 return lhs.compareTo(rhs);
105100 }
106101
112107 return ClassNameRewriterUtil.convertFieldAnnotation(classNameRewriter, fieldAnnotation);
113108 }
114109
115 /*
116 * (non-Javadoc)
117 *
118 * @see
119 * edu.umd.cs.findbugs.WarningComparator#compare(edu.umd.cs.findbugs.BugInstance
120 * , edu.umd.cs.findbugs.BugInstance)
121 */
110 @Override
122111 public int compare(BugInstance lhs, BugInstance rhs) {
123112
124113 int cmp;
127116 BugPattern lhsPattern = lhs.getBugPattern();
128117 BugPattern rhsPattern = rhs.getBugPattern();
129118 String lhsAbbrev, rhsAbbrev;
130 if (lhsPattern == null || rhsPattern == null) {
131 lhsAbbrev = getAbbrevFromBugType(lhs.getType());
132 rhsAbbrev = getAbbrevFromBugType(rhs.getType());
133 } else {
134 lhsAbbrev = lhsPattern.getAbbrev();
135 rhsAbbrev = rhsPattern.getAbbrev();
136 }
119 lhsAbbrev = lhsPattern.getAbbrev();
120 rhsAbbrev = rhsPattern.getAbbrev();
137121 cmp = lhsAbbrev.compareTo(rhsAbbrev);
138122 if (cmp != 0) {
139 if (DEBUG)
123 if (DEBUG) {
140124 System.err.println("bug abbrevs do not match");
125 }
141126 return cmp;
142127 }
143128
144129 // Primary class must match
145130 cmp = compareClassesAllowingNull(lhs.getPrimaryClass(), rhs.getPrimaryClass());
146 if (cmp != 0)
131 if (cmp != 0) {
147132 return cmp;
133 }
148134
149135 boolean havePrimaryMethods = lhs.getPrimaryMethod() != null && rhs.getPrimaryMethod() != null;
150136 // Primary method must match (if any)
151137 cmp = compareMethodsAllowingNull(lhs.getPrimaryMethod(), rhs.getPrimaryMethod());
152138 if (cmp != 0) {
153 if (DEBUG)
139 if (DEBUG) {
154140 System.err.println("primary methods do not match");
141 }
155142 return cmp;
156143 }
157144
159146 // Primary field must match (if any)
160147 cmp = compareFieldsAllowingNull(lhs.getPrimaryField(), rhs.getPrimaryField());
161148 if (cmp != 0) {
162 if (DEBUG)
149 if (DEBUG) {
163150 System.err.println("primary fields do not match");
151 }
164152 return cmp;
165153 }
166154 }
169157 return 0;
170158 }
171159
172 /**
173 * @param type
174 * @return
175 */
160 /*
176161 private static String getAbbrevFromBugType(String type) {
177162 int bar = type.indexOf('_');
178163 return (bar >= 0) ? type.substring(0, bar) : "";
179164 }
165 */
180166 }
167167 return dataSource;
168168 }
169169
170 @Override
170171 public Project getProject() {
171172 return project;
172173 }
173174
175 @Override
174176 public @CheckForNull Cloud getCloudLazily() {
175 if (cloud != null && bugsPopulated)
176 cloud.bugsPopulated();
177 if (cloud != null && bugsPopulated) {
178 cloud.bugsPopulated();
179 }
177180 return cloud;
178181 }
179182
183 @Override
180184 public @Nonnull Cloud getCloud() {
181185 if (shouldNotUsePlugin) {
182186 return CloudFactory.getPlainCloud(this);
185189 if (result == null) {
186190 IGuiCallback callback = getProject().getGuiCallback();
187191 result = cloud = CloudFactory.createCloudWithoutInitializing(this);
188 try {
192 try {
189193 CloudFactory.initializeCloud(this, result);
190194 } catch (Exception e) {
191195 LOGGER.log(Level.SEVERE, "Could not load cloud plugin "+ result.getCloudName(), e);
192196 callback.showMessageDialog("Unable to connect to " + result.getCloudName() + ": " + Util.getNetworkErrorMessage(e));
193 if (CloudFactory.FAIL_ON_CLOUD_ERROR)
197 if (CloudFactory.FAIL_ON_CLOUD_ERROR) {
194198 throw new IllegalStateException("Could not load FindBugs Cloud plugin - to avoid this message, " +
195199 "set -D" + CloudFactory.FAIL_ON_CLOUD_ERROR_PROP + "=false", e);
200 }
196201 result = cloud = CloudFactory.getPlainCloud(this);
197202 }
198203 callback.registerCloud(getProject(), this, result);
202207 if (!result.isInitialized()) {
203208 LOGGER.log(Level.SEVERE, "Cloud " + result.getCloudName() + " is not initialized ");
204209 }
205 if (bugsPopulated)
210 if (bugsPopulated) {
206211 result.bugsPopulated();
212 }
207213 return result;
208214 }
209215
216 @Override
210217 public boolean isApplySuppressions() {
211218 return applySuppressions;
212219 }
213220
221 @Override
214222 public void setApplySuppressions(boolean applySuppressions) {
215223 this.applySuppressions = applySuppressions;
216224 }
217225
226 @Override
218227 public long getAnalysisTimestamp() {
219228 return analysisTimestamp;
220229 }
221230
231 @Override
222232 public void setAnalysisTimestamp(long timestamp) {
223233 analysisTimestamp = timestamp;
224234 }
260270 * @return true if the BugInstance was added, or false if a matching
261271 * BugInstance was already in the BugCollection
262272 */
273 @Override
263274 public boolean add(BugInstance bugInstance) {
264275 return add(bugInstance,
265276 bugInstance.getFirstVersion() == 0L && bugInstance.getLastVersion() == 0L);
271282 * @param message
272283 * the error message
273284 */
285 @Override
274286 public void addError(String message) {
275287 addError(message, null);
276288 }
278290 /**
279291 * Get the current AppVersion.
280292 */
293 @Override
281294 public AppVersion getCurrentAppVersion() {
282295 return new AppVersion(getSequenceNumber()).setReleaseName(getReleaseName()).setTimestamp(getTimestamp())
283296 .setNumClasses(getProjectStats().getNumClasses()).setCodeSize(getProjectStats().getCodeSize());
290303 * @param fileName
291304 * name of the file to read
292305 */
306 @Override
293307 public void readXML(String fileName) throws IOException, DocumentException {
294308 readXML(new File(fileName));
295309 }
348362 }
349363 }
350364
365 @Override
351366 public void readXML(@WillClose InputStream in) throws IOException, DocumentException {
352367 assert project != null;
353368 assert in != null;
354369 doReadXML(in, null);
355370 }
356371
372 @Override
357373 public void readXML(@WillClose Reader reader) throws IOException, DocumentException {
358374 assert project != null;
359375 assert reader != null;
394410
395411 xr.parse(new InputSource(reader));
396412 } catch (SAXParseException e) {
397 if (base != null)
413 if (base != null) {
398414 throw new DocumentException("Parse error at line " + e.getLineNumber() + " : " + e.getColumnNumber() + " of "
399415 + base, e);
416 }
400417 throw new DocumentException("Parse error at line " + e.getLineNumber() + " : " + e.getColumnNumber(), e);
401418 } catch (SAXException e) {
402419 // FIXME: throw SAXException from method?
403 if (base != null)
420 if (base != null) {
404421 throw new DocumentException("Sax error while parsing " + base, e);
422 }
405423 throw new DocumentException("Sax error ", e);
406424 } finally {
407425 Util.closeSilently(reader);
414432 }
415433
416434
435 @Override
417436 public void writeXML(OutputStream out) throws IOException {
418437 writeXML(UTF8.writer(out));
419438 }
424443 * @param fileName
425444 * the file to write to
426445 */
446 @Override
427447 public void writeXML(String fileName) throws IOException {
428448 OutputStream out = new FileOutputStream(fileName);
429 if (fileName.endsWith(".gz"))
449 if (fileName.endsWith(".gz")) {
430450 out = new GZIPOutputStream(out);
451 }
431452 writeXML(out);
432453 }
433454
439460 */
440461 public void writeXML(File file) throws IOException {
441462 OutputStream out = new FileOutputStream(file);
442 if (file.getName().endsWith(".gz"))
463 if (file.getName().endsWith(".gz")) {
443464 out = new GZIPOutputStream(out);
465 }
444466 writeXML(out);
445467 }
446468
449471 *
450472 * @return the Document representing the BugCollection as a dom4j tree
451473 */
474 @Override
452475 public Document toDocument() {
453476 // if (project == null) throw new NullPointerException("No project");
454477 assert project != null;
472495 * @param out
473496 * the OutputStream to write to
474497 */
498 @Override
475499 public void writeXML(@WillClose Writer out) throws IOException {
476500 assert project != null;
477501 bugsPopulated();
496520 writeXML(xmlOutput);
497521 }
498522
523 @Override
499524 public void writePrologue(XMLOutput xmlOutput) throws IOException {
500525 xmlOutput.beginDocument();
501526 xmlOutput.openTag(
502527 ROOT_ELEMENT_NAME,
503528 new XMLAttributeList().addAttribute("version", analysisVersion)
504 .addAttribute("sequence", String.valueOf(getSequenceNumber()))
505 .addAttribute("timestamp", String.valueOf(getTimestamp()))
506 .addAttribute("analysisTimestamp", String.valueOf(getAnalysisTimestamp()))
507 .addAttribute("release", getReleaseName()));
529 .addAttribute("sequence", String.valueOf(getSequenceNumber()))
530 .addAttribute("timestamp", String.valueOf(getTimestamp()))
531 .addAttribute("analysisTimestamp", String.valueOf(getAnalysisTimestamp()))
532 .addAttribute("release", getReleaseName()));
508533 project.writeXML(xmlOutput, null, this);
509534 }
510535
511536 public void computeBugHashes() {
512 if (preciseHashOccurrenceNumbersAvailable)
537 if (preciseHashOccurrenceNumbersAvailable) {
513538 return;
539 }
514540 invalidateHashes();
515541 HashMap<String, Integer> seen = new HashMap<String, Integer>();
516542
526552 seen.put(hash, count + 1);
527553 }
528554 }
529 for (BugInstance bugInstance : getCollection())
555 for (BugInstance bugInstance : getCollection()) {
530556 bugInstance.setInstanceOccurrenceMax(seen.get(bugInstance.getInstanceHash()));
557 }
531558 preciseHashOccurrenceNumbersAvailable = true;
532559 }
533560
543570 * @param xmlOutput
544571 * the XMLOutput object
545572 */
573 @Override
546574 public void writeXML(@WillClose XMLOutput xmlOutput) throws IOException {
547575 assert project != null;
548576 try {
552580 getProjectStats().computeFileStats(this);
553581 String commonBase = null;
554582 for (String s : project.getSourceDirList()) {
555 if (commonBase == null)
583 if (commonBase == null) {
556584 commonBase = s;
557 else
585 } else {
558586 commonBase = commonBase.substring(0, commonPrefix(commonBase, s));
587 }
559588
560589 }
561590 if (commonBase != null && commonBase.length() > 0) {
562 if (commonBase.indexOf("/./") > 0)
591 if (commonBase.indexOf("/./") > 0) {
563592 commonBase = commonBase.substring(0, commonBase.indexOf("/."));
593 }
564594 File base = new File(commonBase);
565 if (base.exists() && base.isDirectory() && base.canRead())
595 if (base.exists() && base.isDirectory() && base.canRead()) {
566596 SourceLineAnnotation.generateRelativeSource(base, project);
597 }
567598 }
568599 }
569 if (earlyStats && !minimalXML)
600 if (earlyStats && !minimalXML) {
570601 getProjectStats().writeXML(xmlOutput, withMessages);
602 }
571603
572604 // Write BugInstances
573 for (BugInstance bugInstance : getCollection())
574 if (!applySuppressions || !project.getSuppressionFilter().match(bugInstance))
605 for (BugInstance bugInstance : getCollection()) {
606 if (!applySuppressions || !project.getSuppressionFilter().match(bugInstance)) {
575607 bugInstance.writeXML(xmlOutput, this, withMessages);
608 }
609 }
576610
577611 writeEpilogue(xmlOutput);
578612
584618
585619 int commonPrefix(String s1, String s2) {
586620 int pos = 0;
587 while (pos < s1.length() && pos < s2.length() && s1.charAt(pos) == s2.charAt(pos))
621 while (pos < s1.length() && pos < s2.length() && s1.charAt(pos) == s2.charAt(pos)) {
588622 pos++;
623 }
589624 return pos;
590625 }
591626
627 @Override
592628 public void writeEpilogue(XMLOutput xmlOutput) throws IOException {
593629 if (withMessages) {
594630 writeBugCategories(xmlOutput);
596632 writeBugCodes(xmlOutput);
597633 }
598634 // Errors, missing classes
599 if (!minimalXML)
635 if (!minimalXML) {
600636 emitErrors(xmlOutput);
637 }
601638
602639 if (!earlyStats && !minimalXML) {
603640 // Statistics
631668 // Summary HTML
632669 if (REPORT_SUMMARY_HTML) {
633670 String html = getSummaryHTML();
634 if (html != null && !html.equals("")) {
671 if (html != null && !"".equals(html)) {
635672 xmlOutput.openTag(SUMMARY_HTML_ELEMENT_NAME);
636673 xmlOutput.writeCDATA(html);
637674 xmlOutput.closeTag(SUMMARY_HTML_ELEMENT_NAME);
651688 // Emit element describing each reported bug pattern
652689 for (String bugType : bugTypeSet) {
653690 BugPattern bugPattern = DetectorFactoryCollection.instance().lookupBugPattern(bugType);
654 if (bugPattern == null)
691 if (bugPattern == null) {
655692 continue;
693 }
656694
657695 XMLAttributeList attributeList = new XMLAttributeList();
658696 attributeList.addAttribute("type", bugType);
689727 for (String bugCodeAbbrev : bugCodeSet) {
690728 BugCode bugCode = DetectorFactoryCollection.instance().getBugCode(bugCodeAbbrev);
691729 String bugCodeDescription = bugCode.getDescription();
692 if (bugCodeDescription == null)
730 if (bugCodeDescription == null) {
693731 continue;
732 }
694733
695734 XMLAttributeList attributeList = new XMLAttributeList();
696735 attributeList.addAttribute("abbrev", bugCodeAbbrev);
718757 // Emit element describing each reported bug code
719758 for (String bugCat : bugCatSet) {
720759 String bugCatDescription = I18N.instance().getBugCategoryDescription(bugCat);
721 if (bugCatDescription == null)
760 if (bugCatDescription == null) {
722761 continue;
762 }
723763
724764 XMLAttributeList attributeList = new XMLAttributeList();
725765 attributeList.addAttribute("category", bugCat);
763803 }
764804 }
765805
766 // if (false && error.getNestedExceptionMessage() != null) {
767 // xmlOutput.openTag(ERROR_EXCEPTION_ELEMENT_NAME);
768 // xmlOutput.writeText(error.getNestedExceptionMessage());
769 // xmlOutput.closeTag(ERROR_EXCEPTION_ELEMENT_NAME);
770 //
771 // stackTrace = error.getNestedStackTrace();
772 // if (stackTrace != null) {
773 // for (String aStackTrace : stackTrace) {
774 // xmlOutput.openTag(ERROR_STACK_TRACE_ELEMENT_NAME);
775 // xmlOutput.writeText(aStackTrace);
776 // xmlOutput.closeTag(ERROR_STACK_TRACE_ELEMENT_NAME);
777 // }
778 // }
779 // }
806 // if (false && error.getNestedExceptionMessage() != null) {
807 // xmlOutput.openTag(ERROR_EXCEPTION_ELEMENT_NAME);
808 // xmlOutput.writeText(error.getNestedExceptionMessage());
809 // xmlOutput.closeTag(ERROR_EXCEPTION_ELEMENT_NAME);
810 //
811 // stackTrace = error.getNestedStackTrace();
812 // if (stackTrace != null) {
813 // for (String aStackTrace : stackTrace) {
814 // xmlOutput.openTag(ERROR_STACK_TRACE_ELEMENT_NAME);
815 // xmlOutput.writeText(aStackTrace);
816 // xmlOutput.closeTag(ERROR_STACK_TRACE_ELEMENT_NAME);
817 // }
818 // }
819 // }
780820 }
781821 xmlOutput.closeTag(ERROR_ELEMENT_NAME);
782822 }
788828 }
789829
790830 private static void checkInputStream(@WillNotClose InputStream in) throws IOException {
791 if (!in.markSupported())
831 if (!in.markSupported()) {
792832 return;
833 }
793834
794835 byte[] buf = new byte[200];
795836 in.mark(buf.length);
841882 private static final class BoundedLinkedHashSet extends LinkedHashSet<AnalysisError> {
842883 @Override
843884 public boolean add(AnalysisError a) {
844 if (this.size() > 1000)
885 if (this.size() > 1000) {
845886 return false;
887 }
846888 return super.add(a);
847889 }
848890 }
852894 private BugInstanceComparator() {
853895 }
854896
897 @Override
855898 public int compare(BugInstance lhs, BugInstance rhs) {
856899 ClassAnnotation lca = lhs.getPrimaryClass();
857900 ClassAnnotation rca = rhs.getPrimaryClass();
858 if (lca == null || rca == null)
901 if (lca == null || rca == null) {
859902 throw new IllegalStateException("null class annotation: " + lca + "," + rca);
903 }
860904 int cmp = lca.getClassName().compareTo(rca.getClassName());
861 if (cmp != 0)
905 if (cmp != 0) {
862906 return cmp;
907 }
863908 return lhs.compareTo(rhs);
864909 }
865910
874919 @Override
875920 public int compare(BugInstance lhs, BugInstance rhs) {
876921 int result = super.compare(lhs, rhs);
877 if (result != 0)
922 if (result != 0) {
878923 return result;
924 }
879925 long diff = lhs.getFirstVersion() - rhs.getFirstVersion();
880 if (diff == 0)
926 if (diff == 0) {
881927 diff = lhs.getLastVersion() - rhs.getLastVersion();
882 if (diff < 0)
928 }
929 if (diff < 0) {
883930 return -1;
884 if (diff > 0)
931 }
932 if (diff > 0) {
885933 return 1;
934 }
886935 return 0;
887936 }
888937
9531002 timestamp = -1L;
9541003 }
9551004
1005 @Override
9561006 public boolean add(BugInstance bugInstance, boolean updateActiveTime) {
9571007 assert !bugsPopulated;
9581008
9671017 }
9681018
9691019 invalidateHashes();
970 if (!bugInstance.isDead())
1020 if (!bugInstance.isDead()) {
9711021 projectStats.addBug(bugInstance);
1022 }
9721023 return bugSet.add(bugInstance);
9731024 }
9741025
9811032 return bugSet.remove(bugInstance);
9821033 }
9831034
1035 @Override
9841036 public Iterator<BugInstance> iterator() {
9851037 return bugSet.iterator();
9861038 }
9871039
1040 @Override
9881041 public Collection<BugInstance> getCollection() {
9891042 return Collections.unmodifiableCollection(bugSet);
9901043 }
10081061 errorList.add(new AnalysisError(message, exception));
10091062 }
10101063
1064 @Override
10111065 public void addError(AnalysisError error) {
10121066 errorList.add(error);
10131067 }
10161070 errorList.clear();
10171071 }
10181072
1073 @Override
10191074 public void addMissingClass(String className) {
1020 if (className == null || className.length() == 0)
1075 if (className == null || className.length() == 0) {
10211076 return;
1077 }
10221078 if (className.startsWith("[")) {
10231079 assert false : "Bad class name " + className;
1024 return;
1025 }
1026 if (className.endsWith(";"))
1080 return;
1081 }
1082 if (className.endsWith(";")) {
10271083 addError("got signature rather than classname: " + className, new IllegalArgumentException());
1028 else
1084 } else {
10291085 missingClassSet.add(className);
1086 }
10301087 }
10311088
10321089 public Collection<? extends AnalysisError> getErrors() {
10431100
10441101 public BugInstance getMatching(BugInstance bugInstance) {
10451102 SortedSet<BugInstance> tailSet = bugSet.tailSet(bugInstance);
1046 if (tailSet.isEmpty())
1103 if (tailSet.isEmpty()) {
10471104 return null;
1105 }
10481106 BugInstance first = tailSet.first();
10491107 return bugInstance.equals(first) ? first : null;
10501108 }
10651123 return summaryHTML;
10661124 }
10671125
1126 @Override
10681127 public ProjectStats getProjectStats() {
10691128 return projectStats;
10701129 }
10711130
1131 @Override
10721132 @Deprecated
10731133 public BugInstance lookupFromUniqueId(String uniqueId) {
1074 for (BugInstance bug : bugSet)
1075 if (bug.getInstanceHash().equals(uniqueId))
1134 for (BugInstance bug : bugSet) {
1135 if (bug.getInstanceHash().equals(uniqueId)) {
10761136 return bug;
1137 }
1138 }
10771139 return null;
10781140 }
10791141
10801142 /** Returns whether this bug collection contains results from multiple analysis runs,
10811143 * either of different version of the software or from different versions of FindBugs.
1082 *
1083 * @return
1084 */
1144 */
1145 @Override
10851146 public boolean isMultiversion() {
10861147 return sequence > 0;
10871148 }
10881149
1150 @Override
10891151 public boolean hasDeadBugs() {
1090 if (sequence == 0)
1152 if (sequence == 0) {
10911153 return false;
1092 for(BugInstance b : bugSet)
1093 if (b.isDead())
1154 }
1155 for(BugInstance b : bugSet) {
1156 if (b.isDead()) {
10941157 return true;
1158 }
1159 }
10951160 return false;
10961161 }
10971162
1163 @Override
10981164 public long getSequenceNumber() {
10991165 return sequence;
11001166 }
11011167
1168 @Override
11021169 public void setSequenceNumber(long sequence) {
11031170 this.sequence = sequence;
11041171 }
11091176 return dup;
11101177 }
11111178
1179 @Override
11121180 public SortedBugCollection createEmptyCollectionWithMetadata() {
11131181 SortedBugCollection dup = new SortedBugCollection(projectStats.clone(), comparator, project);
11141182 dup.projectStats.clearBugCounts();
11321200 invalidateHashes();
11331201 }
11341202
1203 @Override
11351204 public void clearMissingClasses() {
11361205 missingClassSet.clear();
11371206 }
11381207
1208 @Override
11391209 public String getReleaseName() {
1140 if (releaseName == null)
1210 if (releaseName == null) {
11411211 return "";
1212 }
11421213 return releaseName;
11431214 }
11441215
1216 @Override
11451217 public void setReleaseName(String releaseName) {
11461218 this.releaseName = releaseName;
11471219 }
11481220
1221 @Override
11491222 public Iterator<AppVersion> appVersionIterator() {
11501223 return appVersionList.iterator();
11511224 }
11521225
1226 @Override
11531227 public void addAppVersion(AppVersion appVersion) {
11541228 appVersionList.add(appVersion);
11551229 }
11561230
1231 @Override
11571232 public void clearAppVersions() {
11581233 appVersionList.clear();
11591234 sequence = 0;
11601235 }
11611236
11621237 public void trimAppVersions(long numberToRetain) {
1163 while (appVersionList.size() > numberToRetain)
1238 while (appVersionList.size() > numberToRetain) {
11641239 appVersionList.remove(appVersionList.size() - 1);
1240 }
11651241 sequence = appVersionList.size();
11661242 }
11671243
1244 @Override
11681245 public void setTimestamp(long timestamp) {
11691246 this.timestamp = timestamp;
11701247 }
11711248
1249 @Override
11721250 public long getTimestamp() {
11731251 return timestamp;
11741252 }
11771255 return classFeatureSetMap.get(className);
11781256 }
11791257
1258 @Override
11801259 public void setClassFeatureSet(ClassFeatureSet classFeatureSet) {
11811260 classFeatureSetMap.put(classFeatureSet.getClassName(), classFeatureSet);
11821261 }
11851264 return classFeatureSetMap.values().iterator();
11861265 }
11871266
1267 @Override
11881268 public void clearClassFeatures() {
11891269 classFeatureSetMap.clear();
11901270 }
11911271
1272 @Override
11921273 public void setWithMessages(boolean withMessages) {
11931274 this.withMessages = withMessages;
11941275 }
11951276
1277 @Override
11961278 public boolean getWithMessages() {
11971279 return withMessages;
11981280 }
11991281
1282 @Override
12001283 public AppVersion getAppVersionFromSequenceNumber(long target) {
1201 for (AppVersion av : appVersionList)
1202 if (av.getSequenceNumber() == target)
1284 for (AppVersion av : appVersionList) {
1285 if (av.getSequenceNumber() == target) {
12031286 return av;
1204 if (target == this.getSequenceNumber())
1287 }
1288 }
1289 if (target == this.getSequenceNumber()) {
12051290 return this.getCurrentAppVersion();
1291 }
12061292 return null;
12071293 }
12081294
1295 @Override
12091296 public BugInstance findBug(String instanceHash, String bugType, int lineNumber) {
1210 for (BugInstance bug : bugSet)
1297 for (BugInstance bug : bugSet) {
12111298 if (bug.getInstanceHash().equals(instanceHash) && bug.getBugPattern().getType().equals(bugType)
1212 && bug.getPrimarySourceLineAnnotation().getStartLine() == lineNumber)
1299 && bug.getPrimarySourceLineAnnotation().getStartLine() == lineNumber) {
12131300 return bug;
1301 }
1302 }
12141303 return null;
12151304 }
12161305
1306 @Override
12171307 public void setAnalysisVersion(String version) {
12181308 this.analysisVersion = version;
12191309 }
12381328 }
12391329
12401330 public InputStream progressMonitoredInputStream(InputStream in, int length, String msg) {
1241 if (GraphicsEnvironment.isHeadless())
1331 if (GraphicsEnvironment.isHeadless()) {
12421332 return in;
1333 }
12431334 IGuiCallback guiCallback = project.getGuiCallback();
12441335 return guiCallback.getProgressMonitorInputStream(in, length, msg);
12451336 }
12741365 cloud = null;
12751366 }
12761367
1368 @Override
12771369 public @Nonnull Cloud reinitializeCloud() {
12781370 Cloud oldCloud = cloud;
12791371 IGuiCallback callback = project.getGuiCallback();
12931385 return cloud;
12941386 }
12951387
1388 @Override
12961389 public void setXmlCloudDetails(Map<String, String> map) {
12971390 this.xmlCloudDetails = map;
12981391 }
12991392
1393 @Override
13001394 public Map<String, String> getXmlCloudDetails() {
13011395 return xmlCloudDetails;
13021396 }
13031397
1398 @Override
13041399 public void setMinimalXML(boolean minimalXML) {
13051400 this.minimalXML = minimalXML;
13061401 }
13091404 this.shouldNotUsePlugin = b;
13101405 }
13111406
1407 @Override
13121408 public void bugsPopulated() {
13131409 bugsPopulated = true;
13141410 }
13151411
13161412 }
13171413
1318 // vim:ts=4
2929 * by class name before printing them.
3030 */
3131 public class SortingBugReporter extends TextUIBugReporter {
32 private SortedBugCollection bugCollection = new SortedBugCollection();
32 private final SortedBugCollection bugCollection = new SortedBugCollection();
3333
34 @Override
3435 public void observeClass(ClassDescriptor classDescriptor) {
3536 // Don't need to do anything special, since we won't be
3637 // reporting statistics.
3839
3940 @Override
4041 public void doReportBug(BugInstance bugInstance) {
41 if (bugCollection.add(bugInstance))
42 if (bugCollection.add(bugInstance)) {
4243 notifyObservers(bugInstance);
44 }
4345 }
4446
47 @Override
4548 public void finish() {
4649 Iterator<BugInstance> i = bugCollection.iterator();
4750 while (i.hasNext()) {
5255 outputStream.close();
5356 }
5457
58 @Override
5559 public @Nonnull
5660 BugCollection getBugCollection() {
5761 return bugCollection;
5862 }
5963 }
6064
61 // vim:ts=4
2020
2121 import java.io.File;
2222 import java.io.IOException;
23 import java.util.Objects;
2324
2425 import javax.annotation.CheckForNull;
2526 import javax.annotation.Nonnull;
7980
8081 public static final String ROLE_LOCK_OBTAINED_AT = "SOURCE_LINE_LOCK_OBTAINED_AT";
8182
83 public static final String ROLE_UNREACHABLE_CODE = "SOURCE_UNREACHABLE_CODE";
84
8285 /**
8386 * String returned if the source file is unknown. This must match what BCEL
8487 * uses when the source file is unknown.
106109 public static final String DESCRIPTION_LAST_CHANGE = "SOURCE_LINE_LAST_CHANGE";
107110
108111 public static final String DESCRIPTION_LOOP_BOTTOM = "SOURCE_LINE_LOOP_BOTTOM";
112
113 static final ThreadLocal<Project> myProject = new ThreadLocal<Project>();
114
115 static final ThreadLocal<String> relativeSourceBase = new ThreadLocal<String>();
116
117 private static final String ELEMENT_NAME = "SourceLine";
109118
110119 /**
111120 * Constructor.
125134 */
126135 public SourceLineAnnotation(@Nonnull @DottedClassName String className, @Nonnull String sourceFile, int startLine, int endLine,
127136 int startBytecode, int endBytecode) {
128 if (className == null)
129 throw new IllegalArgumentException("class name is null");
130 if (sourceFile == null)
131 throw new IllegalArgumentException("source file is null");
137 Objects.requireNonNull(className, "class name is null");
138 Objects.requireNonNull(sourceFile, "source file is null");
132139 this.description = DEFAULT_ROLE;
133140 this.className = className;
134141 this.sourceFile = sourceFile;
195202 * the source file name
196203 * @return the SourceLineAnnotation
197204 */
205 @Nonnull
198206 public static SourceLineAnnotation createUnknown(@DottedClassName String className, String sourceFile, int startBytecode, int endBytecode) {
199207 SourceLineAnnotation result = new SourceLineAnnotation(className, sourceFile, -1, -1, startBytecode, endBytecode);
200208 // result.setDescription("SOURCE_LINE_UNKNOWN");
229237 LineNumberTable lineNumberTable = methodGen.getLineNumberTable(methodGen.getConstantPool());
230238 String className = methodGen.getClassName();
231239 int codeSize = methodGen.getInstructionList().getLength();
232 if (lineNumberTable == null)
240 if (lineNumberTable == null) {
233241 return createUnknown(className, sourceFile, 0, codeSize - 1);
242 }
234243 return forEntireMethod(className, sourceFile, lineNumberTable, codeSize);
235244 }
236245
270279 */
271280 public static SourceLineAnnotation forEntireMethod(JavaClass javaClass, @CheckForNull Method method) {
272281 String sourceFile = javaClass.getSourceFileName();
273 if (method == null)
282 if (method == null) {
274283 return createUnknown(javaClass.getClassName(), sourceFile);
284 }
275285 Code code = method.getCode();
276286 LineNumberTable lineNumberTable = method.getLineNumberTable();
277287 if (code == null || lineNumberTable == null) {
458468 LineNumberTable lineNumberTable = method.getCode().getLineNumberTable();
459469 String className = jclass.getClassName();
460470 String sourceFile = jclass.getSourceFileName();
461 if (lineNumberTable == null)
471 if (lineNumberTable == null) {
462472 return createUnknown(className, sourceFile, pc, pc);
473 }
463474
464475 int startLine = lineNumberTable.getSourceLine(pc);
465476 return new SourceLineAnnotation(className, sourceFile, startLine, startLine, pc, pc);
484495 String className = visitor.getDottedClassName();
485496 String sourceFile = visitor.getSourceFile();
486497
487 if (lineNumberTable == null)
498 if (lineNumberTable == null) {
488499 return createUnknown(className, sourceFile, startPC, endPC);
500 }
489501
490502 int startLine = lineNumberTable.getSourceLine(startPC);
491503 int endLine = lineNumberTable.getSourceLine(endPC);
510522 */
511523 public static @Nonnull SourceLineAnnotation fromVisitedInstructionRange(ClassContext classContext, PreorderVisitor visitor,
512524 int startPC, int endPC) {
513 if (startPC > endPC)
525 if (startPC > endPC) {
514526 throw new IllegalArgumentException("Start pc " + startPC + " greater than end pc " + endPC);
527 }
515528
516529 LineNumberTable lineNumberTable = getLineNumberTable(visitor);
517530 String className = visitor.getDottedClassName();
518531 String sourceFile = visitor.getSourceFile();
519532
520 if (lineNumberTable == null)
533 if (lineNumberTable == null) {
521534 return createUnknown(className, sourceFile, startPC, endPC);
535 }
522536
523537 int startLine = lineNumberTable.getSourceLine(startPC);
524538 int endLine = lineNumberTable.getSourceLine(endPC);
527541
528542 public static SourceLineAnnotation fromRawData(String className, String sourceFile, int startLine, int endLine, int startPC,
529543 int endPC) {
530 if (startLine == -1)
544 if (startLine == -1) {
531545 return createUnknown(className, sourceFile, startPC, endPC);
546 }
532547
533548 return new SourceLineAnnotation(className, sourceFile, startLine, endLine, startPC, endPC);
534549 }
559574 * @return the SourceLineAnnotation, or null if we do not have line number
560575 * information for the instruction
561576 */
577 @Nonnull
562578 public static SourceLineAnnotation fromVisitedInstruction(ClassContext classContext, MethodGen methodGen, String sourceFile,
563579 @Nonnull InstructionHandle handle) {
564580 LineNumberTable table = methodGen.getLineNumberTable(methodGen.getConstantPool());
566582
567583 int bytecodeOffset = handle.getPosition();
568584
569 if (table == null)
585 if (table == null) {
570586 return createUnknown(className, sourceFile, bytecodeOffset, bytecodeOffset);
587 }
571588
572589 int lineNumber = table.getSourceLine(handle.getPosition());
573590 return new SourceLineAnnotation(className, sourceFile, lineNumber, lineNumber, bytecodeOffset, bytecodeOffset);
591608 LineNumberTable lineNumberTable = methodGen.getLineNumberTable(methodGen.getConstantPool());
592609 String className = methodGen.getClassName();
593610
594 if (lineNumberTable == null)
611 if (lineNumberTable == null) {
595612 return createUnknown(className, sourceFile, start.getPosition(), end.getPosition());
613 }
596614
597615 int startLine = lineNumberTable.getSourceLine(start.getPosition());
598616 int endLine = lineNumberTable.getSourceLine(end.getPosition());
601619
602620 private static LineNumberTable getLineNumberTable(PreorderVisitor visitor) {
603621 Code code = visitor.getMethod().getCode();
604 if (code == null)
622 if (code == null) {
605623 return null;
624 }
606625 return code.getLineNumberTable();
607626 }
608627
609 /**
610 * Get the class name.
611 */
612 public @DottedClassName String getClassName() {
628 @Nonnull
629 @DottedClassName
630 public String getClassName() {
613631 return className;
614632 }
615633
616634 /**
617635 * Get the source file name.
618636 */
637 @Nonnull
619638 public String getSourceFile() {
620639 return sourceFile;
621640 }
624643 * Is the source file known?
625644 */
626645 public boolean isSourceFileKnown() {
627 return !sourceFile.equals(UNKNOWN_SOURCE_FILE);
646 return !UNKNOWN_SOURCE_FILE.equals(sourceFile);
628647 }
629648
630649 /**
645664 return className.substring(lastDot + 1);
646665 }
647666
648 /**
649 * Get the package name.
650 */
651667 public String getPackageName() {
652668 int lastDot = className.lastIndexOf('.');
653 if (lastDot < 0)
669 if (lastDot < 0) {
654670 return "";
655 else
671 } else {
656672 return className.substring(0, lastDot);
673 }
657674 }
658675
659676 /**
691708 return startLine < 0 || endLine < 0;
692709 }
693710
711 @Override
694712 public void accept(BugAnnotationVisitor visitor) {
695713 visitor.visitSourceLineAnnotation(this);
696714 }
697715
716 @Override
698717 public String format(String key, ClassAnnotation primaryClass) {
699 if (key.equals("hash"))
718 if ("hash".equals(key)) {
700719 return "";
701 if (key.equals("")) {
720 }
721 if ("".equals(key)) {
702722 StringBuilder buf = new StringBuilder();
703723 buf.append(sourceFile);
704724 appendLines(buf);
705725 return buf.toString();
706 } else if (key.equals("lineNumber")) {
726 } else if ("lineNumber".equals(key)) {
707727 StringBuilder buf = new StringBuilder();
708728 appendLinesRaw(buf);
709729 return buf.toString();
710 } else if (key.equals("full")) {
730 } else if ("full".equals(key)) {
711731 StringBuilder buf = new StringBuilder();
712732 String pkgName = getPackageName();
713 if (!pkgName.equals("")) {
733 if (!"".equals(pkgName)) {
714734 buf.append(pkgName.replace('.', CANONICAL_PACKAGE_SEPARATOR));
715735 buf.append(CANONICAL_PACKAGE_SEPARATOR);
716736 }
717737 buf.append(sourceFile);
718738 appendLines(buf);
719739 return buf.toString();
720 } else
740 } else {
721741 throw new IllegalArgumentException("Unknown format key " + key);
742 }
722743 }
723744
724745 private void appendLines(StringBuilder buf) {
725 if (isUnknown())
746 if (isUnknown()) {
726747 return;
748 }
727749 buf.append(":[");
728750 appendLinesRaw(buf);
729751 buf.append(']');
730752 }
731753
732754 private void appendLinesRaw(StringBuilder buf) {
733 if (isUnknown())
755 if (isUnknown()) {
734756 return;
757 }
735758 if (startLine == endLine) {
736759 buf.append("line ");
737760 buf.append(startLine);
744767
745768 }
746769
770 @Override
747771 public String getDescription() {
748772 return description;
749773 }
750774
775 @Override
751776 public void setDescription(String description) {
752777 this.description = description.intern();
753778 }
755780 @Override
756781 public String toString() {
757782 String desc = description;
758 if (desc.equals(DEFAULT_ROLE) && isUnknown())
783 if (DEFAULT_ROLE.equals(desc) && isUnknown()) {
759784 desc = DEFAULT_ROLE_UNKNOWN_LINE;
785 }
760786 String pattern = I18N.instance().getAnnotationDescription(desc);
761787 FindBugsMessageFormat format = new FindBugsMessageFormat(pattern);
762788 return format.format(new BugAnnotation[] { this }, null);
763789 }
764790
791 @Override
765792 public int compareTo(BugAnnotation o) {
766 if (!(o instanceof SourceLineAnnotation)) // All BugAnnotations must be
767 // comparable
793 if (!(o instanceof SourceLineAnnotation)) {
794 // comparable
768795 return this.getClass().getName().compareTo(o.getClass().getName());
796 }
769797
770798 SourceLineAnnotation other = (SourceLineAnnotation) o;
771799
772800 int cmp = className.compareTo(other.className);
773 if (cmp != 0)
801 if (cmp != 0) {
774802 return cmp;
803 }
775804 cmp = startLine - other.startLine;
776 if (cmp != 0)
805 if (cmp != 0) {
777806 return cmp;
807 }
778808 cmp = endLine - other.endLine;
779 if (startLine != -1)
809 if (startLine != -1) {
780810 return 0;
781 if (cmp != 0)
811 }
812 if (cmp != 0) {
782813 return cmp;
814 }
783815 cmp = startBytecode - other.startBytecode;
784 if (cmp != 0)
816 if (cmp != 0) {
785817 return cmp;
818 }
786819 return endBytecode - other.endBytecode;
787820 }
788821
789822 @Override
790823 public int hashCode() {
791 if (startLine != -1)
824 if (startLine != -1) {
792825 return className.hashCode() + startLine + 3 * endLine + getDescription().hashCode();
826 }
793827 return className.hashCode() + startBytecode + 3 * endBytecode + getDescription().hashCode();
794828 }
795829
796830 @Override
797831 public boolean equals(Object o) {
798 if (!(o instanceof SourceLineAnnotation))
832 if (!(o instanceof SourceLineAnnotation)) {
799833 return false;
834 }
800835 SourceLineAnnotation other = (SourceLineAnnotation) o;
801 if (!getDescription().equals(other.getDescription()))
836 if (!getDescription().equals(other.getDescription())) {
802837 return false;
803 if (startLine != -1)
838 }
839 if (startLine != -1) {
804840 return className.equals(other.className) && startLine == other.startLine && endLine == other.endLine;
841 }
805842 return className.equals(other.className) && startBytecode == other.startBytecode && endBytecode == other.endBytecode;
806843
807844 }
812849 * ----------------------------------------------------------------------
813850 */
814851
815 private static final String ELEMENT_NAME = "SourceLine";
816
852 @Override
817853 public void writeXML(XMLOutput xmlOutput) throws IOException {
818854 writeXML(xmlOutput, false, false);
819855 }
820
821 static final ThreadLocal<Project> myProject = new ThreadLocal<Project>();
822
823 static final ThreadLocal<String> relativeSourceBase = new ThreadLocal<String>();
824856
825857 public static void generateRelativeSource(File relativeSourceBase, Project project) {
826858 try {
836868 relativeSourceBase.remove();
837869 }
838870
871 @Override
839872 public void writeXML(XMLOutput xmlOutput, boolean addMessages, boolean isPrimary) throws IOException {
840873 String classname = getClassName();
841874 String sourcePath = getSourcePath();
842875
843876 XMLAttributeList attributeList = new XMLAttributeList().addAttribute("classname", classname);
844 if (isPrimary)
877 if (isPrimary) {
845878 attributeList.addAttribute("primary", "true");
879 }
846880
847881 int n = getStartLine(); // start/end are now optional (were too many
848 // "-1"s in the xml)
849 if (n >= 0)
882 // "-1"s in the xml)
883 if (n >= 0) {
850884 attributeList.addAttribute("start", String.valueOf(n));
885 }
851886 n = getEndLine();
852 if (n >= 0)
887 if (n >= 0) {
853888 attributeList.addAttribute("end", String.valueOf(n));
889 }
854890 n = getStartBytecode(); // startBytecode/endBytecode haven't been set
855 // for a while now
856 if (n >= 0)
891 // for a while now
892 if (n >= 0) {
857893 attributeList.addAttribute("startBytecode", String.valueOf(n));
894 }
858895 n = getEndBytecode();
859 if (n >= 0)
896 if (n >= 0) {
860897 attributeList.addAttribute("endBytecode", String.valueOf(n));
898 }
861899
862900 if (isSourceFileKnown()) {
863901 attributeList.addAttribute("sourcefile", sourceFile);
868906 SourceFinder mySourceFinder = project.getSourceFinder();
869907 String fullPath = new File(mySourceFinder.findSourceFile(this).getFullFileName()).getCanonicalPath();
870908 String myRelativeSourceBase = relativeSourceBase.get();
871 if (fullPath.startsWith(myRelativeSourceBase) && fullPath.length() > myRelativeSourceBase.length())
872 attributeList.addAttribute("relSourcepath", fullPath.substring(myRelativeSourceBase.length() + 1));
909 if (fullPath.startsWith(myRelativeSourceBase) && fullPath.length() > myRelativeSourceBase.length()) {
910 attributeList.addAttribute("relSourcepath", fullPath.substring(myRelativeSourceBase.length() + 1));
911 }
873912 } catch (IOException e) {
874913 assert true;
875914 }
876
877915 }
878916 }
879917
880918 String role = getDescription();
881 if (!role.equals(DEFAULT_ROLE))
919 if (!DEFAULT_ROLE.equals(role)) {
882920 attributeList.addAttribute("role", getDescription());
883 if (synthetic)
921 }
922 if (synthetic) {
884923 attributeList.addAttribute("synthetic", "true");
924 }
885925 if (addMessages) {
886926 xmlOutput.openTag(ELEMENT_NAME, attributeList);
887927 xmlOutput.openTag("Message");
896936 public String getSourcePath() {
897937 String classname = getClassName();
898938 String packageName = "";
899 if (classname.indexOf('.') > 0)
939 if (classname.indexOf('.') > 0) {
900940 packageName = classname.substring(0, 1 + classname.lastIndexOf('.'));
941 }
901942 String sourcePath = packageName.replace('.', CANONICAL_PACKAGE_SEPARATOR) + sourceFile;
902943 return sourcePath;
903944 }
904945
905 /**
906 * @param synthetic
907 * The synthetic to set.
908 */
909946 public void setSynthetic(boolean synthetic) {
910947 this.synthetic = synthetic;
911948 }
912949
913 /**
914 * @return Returns the synthetic.
915 */
916950 public boolean isSynthetic() {
917951 return synthetic;
918952 }
919953
954 @Override
920955 public boolean isSignificant() {
921956 return false;
922957 }
923958
924 /**
925 * @param className
926 * @param methodName
927 * @param methodSig
928 * @return
929 */
930959 static SourceLineAnnotation getSourceAnnotationForMethod(String className, String methodName, String methodSig) {
931960 JavaClassAndMethod targetMethod = null;
932961 Code code = null;
936965 targetMethod = Hierarchy.findMethod(targetClass, methodName, methodSig);
937966 if (targetMethod != null) {
938967 Method method = targetMethod.getMethod();
939 if (method != null)
968 if (method != null) {
940969 code = method.getCode();
970 }
941971 }
942972
943973 } catch (ClassNotFoundException e) {
946976 SourceInfoMap sourceInfoMap = AnalysisContext.currentAnalysisContext().getSourceInfoMap();
947977 SourceInfoMap.SourceLineRange range = sourceInfoMap.getMethodLine(className, methodName, methodSig);
948978
949 if (range != null)
979 if (range != null) {
950980 return new SourceLineAnnotation(className, AnalysisContext.currentAnalysisContext().lookupSourceFile(className),
951981 range.getStart(), range.getEnd(), 0, code == null ? -1 : code.getLength());
952
953 if (sourceInfoMap.fallBackToClassfile() && targetMethod != null)
982 }
983
984 if (sourceInfoMap.fallBackToClassfile() && targetMethod != null) {
954985 return forEntireMethod(targetMethod.getJavaClass(), targetMethod.getMethod());
986 }
955987
956988 // If we couldn't find the source lines,
957989 // create an unknown source line annotation referencing
960992 return createUnknown(className);
961993 }
962994
963 /**
964 * @param className
965 * @return
966 */
967995 static SourceLineAnnotation getSourceAnnotationForClass(String className, String sourceFileName) {
968996
969997 int lastLine = -1;
9751003 Code c = m.getCode();
9761004 if (c != null) {
9771005 LineNumberTable table = c.getLineNumberTable();
978 if (table != null)
1006 if (table != null) {
9791007 for (LineNumber line : table.getLineNumberTable()) {
9801008 lastLine = Math.max(lastLine, line.getLineNumber());
9811009 firstLine = Math.min(firstLine, line.getLineNumber());
9821010 }
1011 }
9831012 }
9841013 }
9851014 } catch (ClassNotFoundException e) {
9861015 AnalysisContext.reportMissingClass(e);
9871016 }
988 if (firstLine < Integer.MAX_VALUE)
1017 if (firstLine < Integer.MAX_VALUE) {
9891018 return new SourceLineAnnotation(className, sourceFileName, firstLine, lastLine, -1, -1);
1019 }
9901020 return SourceLineAnnotation.createUnknown(className, sourceFileName);
9911021 }
9921022
1023 @Override
9931024 public String toString(ClassAnnotation primaryClass) {
9941025 return toString();
9951026 }
9961027 }
997 // vim:ts=4
00 /*
11 * FindBugs - Find Bugs in Java programs
22 * Copyright (C) 2003-2008 University of Maryland
3 *
3 *
44 * This library is free software; you can redistribute it and/or
55 * modify it under the terms of the GNU Lesser General Public
66 * License as published by the Free Software Foundation; either
77 * version 2.1 of the License, or (at your option) any later version.
8 *
8 *
99 * This library is distributed in the hope that it will be useful,
1010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1111 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1212 * Lesser General Public License for more details.
13 *
13 *
1414 * You should have received a copy of the GNU Lesser General Public
1515 * License along with this library; if not, write to the Free Software
1616 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
5151 * @author pugh
5252 */
5353 public class StackMapAnalyzer {
54
55
56
57
54
55
56
57
5858 public static class StackMapAnalysisFactory extends edu.umd.cs.findbugs.classfile.engine.bcel.AnalysisFactory<JumpInfoFromStackMap> {
5959 public StackMapAnalysisFactory() {
6060 super("Jump info for opcode stack from stack map analysis", JumpInfoFromStackMap.class);
6161 }
62
62
63 @Override
6364 public JumpInfoFromStackMap analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) {
64
65
6566 return getFromStackMap( analysisCache, descriptor);
66
67
68 }
69 }
70
67
68
69 }
70 }
71
7172 static class JumpInfoFromStackMap extends JumpInfo {
7273
7374
7475 JumpInfoFromStackMap(Map<Integer, List<Item>> jumpEntries, Map<Integer, List<Item>> jumpStackEntries, BitSet jumpEntryLocations) {
7576 super(jumpEntries, jumpStackEntries, jumpEntryLocations);
7677 }
77
78
7879 }
7980 static final boolean DEBUG = false;
8081
105106 }
106107
107108 static @CheckForNull StackMapTable getStackMapTable(Code code) {
108 for(Attribute a : code.getAttributes())
109 if (a instanceof StackMapTable)
109 for(Attribute a : code.getAttributes()) {
110 if (a instanceof StackMapTable) {
110111 return (StackMapTable) a;
112 }
113 }
111114 return null;
112115 }
113
116
114117 static List<Item> getInitialLocals(MethodDescriptor descriptor) {
115118 List<Item> locals = new ArrayList<Item>();
116119 Type[] argTypes = Type.getArgumentTypes(descriptor.getSignature());
124127 Item it = Item.typeOnly(argType.getSignature());
125128 locals.add(it);
126129 reg += it.getSize();
127 if (it.usesTwoSlots())
130 if (it.usesTwoSlots()) {
128131 locals.add(null);
132 }
129133 }
130134 return locals;
131135 }
132
136
133137 static final @CheckForNull Field frame_type_field;
134138 static {
135139 Field f;
136140 try {
137 f = AccessController.doPrivileged(new PrivilegedAction<Field>() {
138
139 public Field run() {
140 Class<StackMapTableEntry> c = StackMapTableEntry.class;
141 Field result;
142 try {
143 result = c.getDeclaredField("frame_type");
144 result.setAccessible(true);
145 return result;
146 } catch (NoSuchFieldException e) {
147 throw new AssertionError("frame_type field doesn't exist");
148 } catch (SecurityException e) {
149 return null;
141 f = AccessController.doPrivileged(new PrivilegedAction<Field>() {
142
143 @Override
144 public Field run() {
145 Class<StackMapTableEntry> c = StackMapTableEntry.class;
146 Field result;
147 try {
148 result = c.getDeclaredField("frame_type");
149 result.setAccessible(true);
150 return result;
151 } catch (NoSuchFieldException e) {
152 throw new AssertionError("frame_type field doesn't exist");
153 } catch (SecurityException e) {
154 return null;
155 }
156
150157 }
151
152 }
153
154 });
158
159 });
155160 } catch (Exception e) {
156161 AnalysisContext.logError("Unable to create frame_type accessor",e );
157162 f = null;
158163 }
159 System.out.println("Frame type field is null:" + (f == null));
160 frame_type_field = f;
161 }
162
164 if (DEBUG) {
165 System.out.println("Frame type field is null:" + (f == null));
166 }
167 frame_type_field = f;
168 }
169
163170 static int getFrameType(StackMapTableEntry e) {
164 if (frame_type_field == null)
171 if (frame_type_field == null) {
165172 return -1;
173 }
166174 try {
167175 return (Integer) frame_type_field.get(e);
168176 } catch (IllegalArgumentException e1) {
169 return -1;
177 return -1;
170178 } catch (IllegalAccessException e1) {
171 return -1;
179 return -1;
172180 }
173181 }
174182 static private @CheckForNull JumpInfoFromStackMap getFromStackMap(IAnalysisCache analysisCache, MethodDescriptor descriptor) {
175 if (frame_type_field == null)
183 if (frame_type_field == null) {
176184 return null;
185 }
177186
178187 Method method;
179188 try {
182191 analysisCache.getErrorLogger().logError("Unable to get method for " + descriptor, e1);
183192 return null;
184193 }
185
194
186195 Code code = method.getCode();
187196 if (code == null) {
188197 return null;
189198 }
190199 StackMapTable stackMapTable = getStackMapTable(code);
191 if (stackMapTable == null)
200 if (stackMapTable == null) {
192201 return null;
193 Map<Integer, List<Item>> jumpEntries = new HashMap<Integer, List<Item>>();
194
195 Map<Integer, List<Item>> jumpStackEntries = new HashMap<Integer, List<Item>>();
196
197 List<Item> locals = getInitialLocals(descriptor);
198 List<Item> stack = new ArrayList<Item>();
199 BitSet jumpEntryLocations = new BitSet();
200 if (DEBUG) {
201 System.out.println(descriptor);
202 System.out.println(locals);
203 }
204 int pc = 0;
205 for(StackMapTableEntry e : stackMapTable.getStackMapTable()) {
206 pc += e.getByteCodeOffsetDelta();
207 StackFrameType stackFrameType = StackFrameType.get(getFrameType(e));
208 switch (stackFrameType) {
209 case SAME_FRAME:
210 stack.clear();
211 break;
212 case SAME_LOCALS_1_STACK_ITEM_FRAME:
213 stack.clear();
214 addStack(stack, e.getTypesOfStackItems());
215 break;
216 case CHOP_FRAME :
217 stack.clear();
218 for(int i = 0; i < e.getNumberOfLocals(); i++) {
219 Item it = locals.remove(locals.size()-1);
220 if (it == null) {
221 it = locals.remove(locals.size()-1);
222 assert it.usesTwoSlots();
223 }
224 }
225 break;
226
227 case APPEND_FRAME:
228
229 stack.clear();
230 addLocals(locals, e.getTypesOfLocals());
231
232 break;
233 case FULL_FRAME:
234 stack.clear();
235 locals.clear();
236 addLocals(locals, e.getTypesOfLocals());
237 addStack(stack, e.getTypesOfStackItems());
238 break;
239
240 }
241 if (DEBUG) {
242 System.out.printf("%4d %2d %2d %12s %s%n",
243
244 pc, e.getNumberOfLocals(), e.getNumberOfStackItems(), stackFrameType, e);
245 System.out.printf(" %s :: %s%n", stack, locals);
246 }
247 if (pc > 0) {
248 jumpEntries.put(pc, new ArrayList<Item>(locals));
249 if (!stack.isEmpty())
250 jumpStackEntries.put(pc, new ArrayList<Item>(stack));
251 jumpEntryLocations.set(pc);
252 }
253 pc++;
254 }
255 if (DEBUG)
256 System.out.println("\n");
257 return new JumpInfoFromStackMap(jumpEntries, jumpStackEntries, jumpEntryLocations);
258
259 }
260
202 }
203 Map<Integer, List<Item>> jumpEntries = new HashMap<Integer, List<Item>>();
204
205 Map<Integer, List<Item>> jumpStackEntries = new HashMap<Integer, List<Item>>();
206
207 List<Item> locals = getInitialLocals(descriptor);
208 List<Item> stack = new ArrayList<Item>();
209 BitSet jumpEntryLocations = new BitSet();
210 if (DEBUG) {
211 System.out.println(descriptor);
212 System.out.println(locals);
213 }
214 int pc = 0;
215 for(StackMapTableEntry e : stackMapTable.getStackMapTable()) {
216 pc += e.getByteCodeOffsetDelta();
217 int rawFrameType = getFrameType(e);
218 StackFrameType stackFrameType = StackFrameType.get(rawFrameType);
219 switch (stackFrameType) {
220 case SAME_FRAME:
221 stack.clear();
222 break;
223 case SAME_LOCALS_1_STACK_ITEM_FRAME:
224 stack.clear();
225 addStack(stack, e.getTypesOfStackItems());
226 break;
227 case CHOP_FRAME :
228 stack.clear();
229 int n = Constants.CHOP_FRAME_MAX+1-rawFrameType;
230 for(int i = 0; i < n; i++) {
231 Item it = locals.remove(locals.size()-1);
232 if (it == null) {
233 it = locals.remove(locals.size()-1);
234 assert it.usesTwoSlots();
235 }
236 }
237 break;
238
239 case APPEND_FRAME:
240
241 stack.clear();
242 addLocals(locals, e.getTypesOfLocals());
243
244 break;
245 case FULL_FRAME:
246 stack.clear();
247 locals.clear();
248 addLocals(locals, e.getTypesOfLocals());
249 addStack(stack, e.getTypesOfStackItems());
250 break;
251
252 }
253 if (DEBUG) {
254 System.out.printf("%4d %2d %2d %12s %s%n",
255
256 pc, e.getNumberOfLocals(), e.getNumberOfStackItems(), stackFrameType, e);
257 System.out.printf(" %s :: %s%n", stack, locals);
258 }
259 if (pc > 0) {
260 jumpEntries.put(pc, new ArrayList<Item>(locals));
261 if (!stack.isEmpty()) {
262 jumpStackEntries.put(pc, new ArrayList<Item>(stack));
263 }
264 jumpEntryLocations.set(pc);
265 }
266 pc++;
267 }
268 if (DEBUG) {
269 System.out.println("\n");
270 }
271 return new JumpInfoFromStackMap(jumpEntries, jumpStackEntries, jumpEntryLocations);
272
273 }
274
261275 static private Item getItem(StackMapType t) {
262276
263277 switch (t.getType()) {
264
278
265279 case Constants.ITEM_Double:
266280 return Item.typeOnly("D");
267281 case Constants.ITEM_Float:
283297 int index = t.getIndex();
284298 ConstantClass c = (ConstantClass) t.getConstantPool().getConstant(index);
285299 String name = c.getBytes(t.getConstantPool());
286 if (name.charAt(0) != '[') name = "L" + name + ";";
300 if (name.charAt(0) != '[') {
301 name = "L" + name + ";";
302 }
287303 return Item.typeOnly(name);
288304 default:
289305 throw new IllegalArgumentException("Bad item type: " + t.getType());
294310 for(StackMapType t : typesOfStackItems) {
295311 Item item = getItem(t);
296312 lst.add(item);
297 if (item.usesTwoSlots())
313 if (item.usesTwoSlots()) {
298314 lst.add(null);
299 }
300
315 }
316 }
317
301318 }
302319 static private void addStack(List<Item> lst, StackMapType[] typesOfStackItems) {
303320 for(StackMapType t : typesOfStackItems) {
304321 Item item = getItem(t);
305322 lst.add(item);
306323 }
307
324
308325 }
309326
310327 }
2222 /**
2323 * is a marker interface for detectors that don't save state from one class file
2424 * to the next.
25 *
25 *
2626 * If a detector implements this interface, a clone will be generated for each
2727 * element it is applied to.
28 *
28 *
2929 * The idea of using this interface is questionable. Better for people writing
3030 * stateless detectors to just not keep around state they don't need, rather
3131 * than depending on cloning and garbage collection.
32 *
32 *
3333 */
3434
3535 public interface StatelessDetector extends Cloneable {
102102 return value;
103103 }
104104
105 @Override
105106 public void accept(BugAnnotationVisitor visitor) {
106107 visitor.visitStringAnnotation(this);
107108 }
108109
110 @Override
109111 public String format(String key, ClassAnnotation primaryClass) {
110112 String txt = value;
111113 return txt;
112114 }
113115
116 @Override
114117 public void setDescription(String description) {
115118 this.description = description;
116119 }
117120
121 @Override
118122 public String getDescription() {
119123 return description;
120124 }
126130
127131 @Override
128132 public boolean equals(Object o) {
129 if (!(o instanceof StringAnnotation))
133 if (!(o instanceof StringAnnotation)) {
130134 return false;
135 }
131136 return value.equals(((StringAnnotation) o).value);
132137 }
133138
139 @Override
134140 public int compareTo(BugAnnotation o) {
135 if (!(o instanceof StringAnnotation)) // BugAnnotations must be
136 // Comparable with any type of
137 // BugAnnotation
141 if (!(o instanceof StringAnnotation)) {
142 // Comparable with any type of
143 // BugAnnotation
138144 return this.getClass().getName().compareTo(o.getClass().getName());
145 }
139146 return value.compareTo(((StringAnnotation) o).value);
140147 }
141148
154161
155162 private static final String ELEMENT_NAME = "String";
156163
164 @Override
157165 public void writeXML(XMLOutput xmlOutput) throws IOException {
158166 writeXML(xmlOutput, false, false);
159167 }
160168
169 @Override
161170 public void writeXML(XMLOutput xmlOutput, boolean addMessages, boolean isPrimary) throws IOException {
162171 XMLAttributeList attributeList = new XMLAttributeList().addAttribute("value", value);
163172
164173 String role = getDescription();
165 if (!role.equals(DEFAULT_ROLE))
174 if (!DEFAULT_ROLE.equals(role)) {
166175 attributeList.addAttribute("role", role);
176 }
167177
168178 BugAnnotationUtil.writeXML(xmlOutput, ELEMENT_NAME, this, attributeList, addMessages);
169179 }
170180
181 @Override
171182 public boolean isSignificant() {
172183 return true;
173184 }
174185
186 @Override
175187 public String toString(ClassAnnotation primaryClass) {
176188 return toString();
177189 }
178190 }
179191
180 // vim:ts=4
2828 import edu.umd.cs.findbugs.xml.XMLOutput;
2929
3030 public class SuppressionMatcher implements Matcher {
31 private Map<ClassAnnotation, Collection<WarningSuppressor>> suppressedWarnings = new HashMap<ClassAnnotation, Collection<WarningSuppressor>>();
31 private final Map<ClassAnnotation, Collection<WarningSuppressor>> suppressedWarnings = new HashMap<ClassAnnotation, Collection<WarningSuppressor>>();
3232
33 private Map<String, Collection<WarningSuppressor>> suppressedPackageWarnings = new HashMap<String, Collection<WarningSuppressor>>();
33 private final Map<String, Collection<WarningSuppressor>> suppressedPackageWarnings = new HashMap<String, Collection<WarningSuppressor>>();
3434
3535 int count = 0;
3636
5959 return count;
6060 }
6161
62 @Override
6263 public boolean match(BugInstance b) {
6364 ClassAnnotation clazz = b.getPrimaryClass().getTopLevelClass();
6465 Collection<WarningSuppressor> c = suppressedWarnings.get(clazz);
65 if (c != null)
66 for (WarningSuppressor w : c)
66 if (c != null) {
67 for (WarningSuppressor w : c) {
6768 if (w.match(b)) {
6869 count++;
6970 return true;
7071 }
71 for (Collection<WarningSuppressor> c2 : suppressedPackageWarnings.values())
72 }
73 }
74 for (Collection<WarningSuppressor> c2 : suppressedPackageWarnings.values()) {
7275 for (WarningSuppressor w : c2) {
7376 if (w.match(b)) {
7477 count++;
7578 return true;
7679 }
7780 }
81 }
7882 return false;
7983 }
8084
85 @Override
8186 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
8287 // no-op; these aren't saved to XML
8388 }
8489
8590 }
8691
87 // vim:ts=4
3636 public SwitchHandler() {
3737 switchOffsetStack = new ArrayList<SwitchDetails>();
3838 }
39
39
4040 public int stackSize() {
4141 return switchOffsetStack.size();
4242 }
4343 int numEnumValues(@CheckForNull XClass c) {
44 if (c == null)
44 if (c == null) {
4545 return -1;
46 }
4647 int total = 0;
4748 String enumSignature = ClassName.toSignature(c.getClassDescriptor().getClassName());
4849 for(XField f : c.getXFields()) {
4950 if (f.getSignature().equals(enumSignature)
50 && f.isPublic() && f.isFinal())
51 && f.isPublic() && f.isFinal()) {
5152 total++;
53 }
5254 }
5355 return total;
5456 }
5961 int[] switchOffsets = dbc.getSwitchOffsets();
6062 SwitchDetails details = new SwitchDetails(dbc.getPC(), switchOffsets, dbc.getDefaultSwitchOffset(), switchOffsets.length == numEnumValues(enumType));
6163
62
64
6365 int size = switchOffsetStack.size();
6466 while (--size >= 0) {
6567 SwitchDetails existingDetail = switchOffsetStack.get(size);
66 if (details.switchPC > (existingDetail.switchPC + existingDetail.swOffsets[existingDetail.swOffsets.length - 1]))
68 if (details.switchPC > (existingDetail.switchPC + existingDetail.swOffsets[existingDetail.swOffsets.length - 1])) {
6769 switchOffsetStack.remove(size);
70 }
6871 }
6972 switchOffsetStack.add(details);
7073 }
7174
7275 public boolean isOnSwitchOffset(DismantleBytecode dbc) {
7376 int pc = dbc.getPC();
74 if (pc == getDefaultOffset())
77 if (pc == getDefaultOffset()) {
7578 return false;
79 }
7680
7781 return (pc == getNextSwitchOffset(dbc));
7882 }
8387 SwitchDetails details = switchOffsetStack.get(size - 1);
8488
8589 int nextSwitchOffset = details.getNextSwitchOffset(dbc.getPC());
86 if (nextSwitchOffset >= 0)
90 if (nextSwitchOffset >= 0) {
8791 return nextSwitchOffset;
92 }
8893
89 if (dbc.getPC() <= details.getDefaultOffset())
94 if (dbc.getPC() <= details.getDefaultOffset()) {
9095 return -1;
96 }
9197 switchOffsetStack.remove(size - 1);
9298 size--;
9399 }
97103
98104 public int getDefaultOffset() {
99105 int size = switchOffsetStack.size();
100 if (size == 0)
106 if (size == 0) {
101107 return -1;
108 }
102109
103110 SwitchDetails details = switchOffsetStack.get(size - 1);
104111 return details.getDefaultOffset();
105112 }
106113
107114 public SourceLineAnnotation getCurrentSwitchStatement(BytecodeScanningDetector detector) {
108 if (switchOffsetStack.isEmpty())
115 if (switchOffsetStack.isEmpty()) {
109116 throw new IllegalStateException("No current switch statement");
117 }
110118 SwitchDetails details = switchOffsetStack.get(switchOffsetStack.size()-1);
111119 return SourceLineAnnotation.fromVisitedInstructionRange(
112120 detector.getClassContext(), detector, details.switchPC, details.switchPC + details.maxOffset-1);
120128 final int maxOffset;
121129
122130 int nextOffset;
123
131
124132 final boolean exhaustive;
125133
126134 public SwitchDetails(int pc, int[] offsets, int defOffset, boolean exhaustive) {
129137 int lastValue = -1;
130138 int maxOffset = defOffset;
131139 for (int offset : offsets) {
132 if (maxOffset < offset)
140 if (maxOffset < offset) {
133141 maxOffset = offset;
142 }
134143 if (offset == defOffset) {
135144 exhaustive = false;
136145 }
156165 }
157166
158167 public int getNextSwitchOffset(int currentPC) {
159 while ((nextOffset < swOffsets.length) && (currentPC > (switchPC + swOffsets[nextOffset])))
168 while ((nextOffset < swOffsets.length) && (currentPC > (switchPC + swOffsets[nextOffset]))) {
160169 nextOffset++;
170 }
161171
162 if (nextOffset >= swOffsets.length)
172 if (nextOffset >= swOffsets.length) {
163173 return -1;
174 }
164175
165176 return switchPC + swOffsets[nextOffset];
166177 }
167178
168179 public int getDefaultOffset() {
169 if (exhaustive)
180 if (exhaustive) {
170181 return Short.MIN_VALUE;
182 }
171183 return switchPC + defaultOffset;
172184 }
173185 }
4141
4242 public final static boolean ASSERTIONS_ENABLED;
4343
44 public static boolean RUNNING_IN_ECLIPSE = SystemProperties.class.getClassLoader().getClass().getCanonicalName()
45 .startsWith("org.eclipse.osgi");
46
44 public final static boolean RUNNING_IN_ECLIPSE = SystemProperties.class.getClassLoader().getClass().getCanonicalName()
45 .startsWith("org.eclipse.osgi");
46
4747 final static String OS_NAME;
4848 static {
4949 boolean tmp = false;
7676 URL systemProperties = DetectorFactoryCollection.getCoreResource("systemProperties.properties");
7777 loadPropertiesFromURL(systemProperties);
7878 String u = System.getProperty("findbugs.loadPropertiesFrom");
79 if (u != null)
79 if (u != null) {
8080 try {
8181 URL configURL = new URL(u);
8282 loadPropertiesFromURL(configURL);
8484 AnalysisContext.logError("Unable to load properties from " + u, e);
8585
8686 }
87 }
8788 }
8889
8990 public static Properties getLocalProperties() {
139140 boolean result = defaultValue;
140141 try {
141142 String value = getProperty(name);
142 if (value == null)
143 if (value == null) {
143144 return defaultValue;
145 }
144146 result = toBoolean(value);
145147 } catch (IllegalArgumentException e) {
146148 } catch (NullPointerException e) {
149151 }
150152
151153 private static boolean toBoolean(String name) {
152 return ((name != null) && name.equalsIgnoreCase("true"));
154 return ((name != null) && "true".equalsIgnoreCase(name));
153155 }
154156
155157 /**
175177 public static int getInt(String name, int defaultValue) {
176178 try {
177179 String value = getProperty(name);
178 if (value != null)
180 if (value != null) {
179181 return Integer.decode(value);
182 }
180183 } catch (Exception e) {
181184 assert true;
182185 }
191194 public static String getOSDependentProperty(String name) {
192195 String osDependentName = name + OS_NAME;
193196 String value = getProperty(osDependentName);
194 if (value != null)
197 if (value != null) {
195198 return value;
199 }
196200 return getProperty(name);
197201 }
198202
204208 public static String getProperty(String name) {
205209 try {
206210 String value = properties.getProperty(name);
207 if (value != null)
211 if (value != null) {
208212 return value;
213 }
209214 return System.getProperty(name);
210215 } catch (Exception e) {
211216 return null;
227232 public static String getProperty(String name, String defaultValue) {
228233 try {
229234 String value = properties.getProperty(name);
230 if (value != null)
235 if (value != null) {
231236 return value;
237 }
232238 return System.getProperty(name, defaultValue);
233239 } catch (Exception e) {
234240 return defaultValue;
240246 private static final String URL_REWRITE_FORMAT = getOSDependentProperty("findbugs.urlRewriteFormat");
241247
242248 private static final Pattern URL_REWRITE_PATTERN;
243
249
244250 static {
245251 Pattern p = null;
246252 if (URL_REWRITE_PATTERN_STRING != null && URL_REWRITE_FORMAT != null) {
263269 }
264270
265271 public static String rewriteURLAccordingToProperties(String u) {
266 if (URL_REWRITE_PATTERN == null || URL_REWRITE_FORMAT == null)
272 if (URL_REWRITE_PATTERN == null || URL_REWRITE_FORMAT == null) {
267273 return u;
274 }
268275 Matcher m = URL_REWRITE_PATTERN.matcher(u);
269 if (!m.matches() || m.groupCount() == 0)
276 if (!m.matches() || m.groupCount() == 0) {
270277 return u;
278 }
271279 String result = String.format(URL_REWRITE_FORMAT, m.group(1));
272280 return result;
273281 }
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2008 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs;
20
21 import org.objectweb.asm.tree.ClassNode;
22
23 /**
24 * @author pugh
25 */
26 public class Test {
27
28 public static void main(String args[]) {
29 Class<?> c = ClassNode.class;
30 System.out.println(c.getName());
31 }
32 }
5050
5151 static final String OTHER_CATEGORY_ABBREV = "X";
5252
53 protected PrintWriter outputStream = UTF8.printWriter(System.out, true);
53 protected PrintWriter outputStream = UTF8.printWriter(System.out, true);
5454
5555 public TextUIBugReporter() {
5656 reportStackTrace = true;
112112 if (pattern != null) {
113113 String categoryAbbrev = null;
114114 BugCategory bcat = DetectorFactoryCollection.instance().getBugCategory(pattern.getCategory());
115 if (bcat != null)
115 if (bcat != null) {
116116 categoryAbbrev = bcat.getAbbrev();
117 if (categoryAbbrev == null)
117 }
118 if (categoryAbbrev == null) {
118119 categoryAbbrev = OTHER_CATEGORY_ABBREV;
120 }
119121 outputStream.print(categoryAbbrev);
120122 outputStream.print(" ");
121123 }
150152 boolean errors = analysisErrors || missingClasses || getQueuedErrors().size() > 0;
151153 analysisErrors = missingClasses = false;
152154 super.reportQueuedErrors();
153 if (errors)
155 if (errors) {
154156 emitLine("");
157 }
155158 }
156159
157160 @Override
252255
253256 }
254257
255 // vim:ts=4
204204 startOptionGroup("Project configuration options:");
205205 addOption("-auxclasspath", "classpath", "set aux classpath for analysis");
206206 addSwitch("-auxclasspathFromInput", "read aux classpath from standard input");
207 addOption("-auxclasspathFromFile", "filepath", "read aux classpaths from a designated file");
207208 addOption("-sourcepath", "source path", "set source path for analyzed classes");
208209 addSwitch("-exitcode", "set exit code of process");
209210 addSwitch("-noClassOk", "output empty warning file if no classes are specified");
210211 addSwitch("-xargs", "get list of classfiles/jarfiles from standard input rather than command line");
212 addOption("-analyzeFromFile", "filepath", "get the list of class/jar files from a designated file");
211213 addOption("-cloud", "id", "set cloud id");
212214 addOption("-cloudProperty", "key=value", "set cloud property");
213215 addOption("-bugReporters", "name,name2,-name3", "bug reporter decorators to explicitly enable/disable");
256258 protected void handleOption(String option, String optionExtraPart) {
257259 parsedOptions.put(option, optionExtraPart);
258260 if (DEBUG) {
259 if (optionExtraPart != null)
261 if (optionExtraPart != null) {
260262 System.out.println("option " + option + ":" + optionExtraPart);
261 else
263 } else {
262264 System.out.println("option " + option);
263 }
264 if (option.equals("-showPlugins")) {
265 }
266 }
267 if ("-showPlugins".equals(option)) {
265268 System.out.println("Available plugins:");
266269 int count = 0;
267270 for (Iterator<Plugin> i = DetectorFactoryCollection.instance().pluginIterator(); i.hasNext();) {
268271 Plugin plugin = i.next();
269272 System.out.println(" " + plugin.getPluginId() + " (default: " + (plugin.isEnabledByDefault() ? "enabled" : "disabled")
270273 + ")");
271 if (plugin.getShortDescription() != null)
274 if (plugin.getShortDescription() != null) {
272275 System.out.println(" Description: " + plugin.getShortDescription());
273 if (plugin.getProvider() != null)
276 }
277 if (plugin.getProvider() != null) {
274278 System.out.println(" Provider: " + plugin.getProvider());
275 if (plugin.getWebsite() != null)
279 }
280 if (plugin.getWebsite() != null) {
276281 System.out.println(" Website: " + plugin.getWebsite());
282 }
277283 ++count;
278284 }
279285 if (count == 0) {
280286 System.out.println(" No plugins are available (FindBugs installed incorrectly?)");
281287 }
282288 System.exit(0);
283 } else if (option.equals("-experimental"))
284 priorityThreshold = Detector.EXP_PRIORITY;
285 else if (option.equals("-longBugCodes"))
289 } else if ("-experimental".equals(option)) {
290 priorityThreshold = Priorities.EXP_PRIORITY;
291 } else if ("-longBugCodes".equals(option)) {
286292 useLongBugCodes = true;
287 else if (option.equals("-progress")) {
293 } else if ("-progress".equals(option)) {
288294 showProgress = true;
289 } else if (option.equals("-timestampNow"))
295 } else if ("-timestampNow".equals(option)) {
290296 project.setTimestamp(System.currentTimeMillis());
291 else if (option.equals("-low"))
292 priorityThreshold = Detector.LOW_PRIORITY;
293 else if (option.equals("-medium"))
294 priorityThreshold = Detector.NORMAL_PRIORITY;
295 else if (option.equals("-high"))
296 priorityThreshold = Detector.HIGH_PRIORITY;
297 else if (option.equals("-dontCombineWarnings"))
297 } else if ("-low".equals(option)) {
298 priorityThreshold = Priorities.LOW_PRIORITY;
299 } else if ("-medium".equals(option)) {
300 priorityThreshold = Priorities.NORMAL_PRIORITY;
301 } else if ("-high".equals(option)) {
302 priorityThreshold = Priorities.HIGH_PRIORITY;
303 } else if ("-dontCombineWarnings".equals(option)) {
298304 mergeSimilarWarnings = false;
299 else if (option.equals("-sortByClass"))
305 } else if ("-sortByClass".equals(option)) {
300306 bugReporterType = SORTING_REPORTER;
301 else if (option.equals("-xml")) {
307 } else if ("-xml".equals(option)) {
302308 bugReporterType = XML_REPORTER;
303 if (!optionExtraPart.equals("")) {
304 if (optionExtraPart.equals("withMessages"))
309 if (!"".equals(optionExtraPart)) {
310 if ("withMessages".equals(optionExtraPart)) {
305311 xmlWithMessages = true;
306 else if (optionExtraPart.equals("withAbridgedMessages")) {
312 } else if ("withAbridgedMessages".equals(optionExtraPart)) {
307313 xmlWithMessages = true;
308314 xmlWithAbridgedMessages = true;
309 } else if (optionExtraPart.equals("minimal")) {
315 } else if ("minimal".equals(optionExtraPart)) {
310316 xmlWithMessages = false;
311317 xmlMinimal = true;
312 } else
318 } else {
313319 throw new IllegalArgumentException("Unknown option: -xml:" + optionExtraPart);
314 }
315 } else if (option.equals("-emacs")) {
320 }
321 }
322 } else if ("-emacs".equals(option)) {
316323 bugReporterType = EMACS_REPORTER;
317 } else if (option.equals("-relaxed")) {
324 } else if ("-relaxed".equals(option)) {
318325 relaxedReportingMode = true;
319 } else if (option.equals("-train")) {
320 trainingOutputDir = !optionExtraPart.equals("") ? optionExtraPart : ".";
321 } else if (option.equals("-useTraining")) {
322 trainingInputDir = !optionExtraPart.equals("") ? optionExtraPart : ".";
323 } else if (option.equals("-html")) {
326 } else if ("-train".equals(option)) {
327 trainingOutputDir = !"".equals(optionExtraPart) ? optionExtraPart : ".";
328 } else if ("-useTraining".equals(option)) {
329 trainingInputDir = !"".equals(optionExtraPart) ? optionExtraPart : ".";
330 } else if ("-html".equals(option)) {
324331 bugReporterType = HTML_REPORTER;
325 if (!optionExtraPart.equals("")) {
332 if (!"".equals(optionExtraPart)) {
326333 stylesheet = optionExtraPart;
327334 } else {
328335 stylesheet = "default.xsl";
329336 }
330 } else if (option.equals("-xdocs")) {
337 } else if ("-xdocs".equals(option)) {
331338 bugReporterType = XDOCS_REPORTER;
332 } else if (option.equals("-applySuppression")) {
339 } else if ("-applySuppression".equals(option)) {
333340 applySuppression = true;
334 } else if (option.equals("-quiet")) {
341 } else if ("-quiet".equals(option)) {
335342 quiet = true;
336 } else if (option.equals("-nested")) {
337 scanNestedArchives = optionExtraPart.equals("") || Boolean.valueOf(optionExtraPart).booleanValue();
338 } else if (option.equals("-exitcode")) {
343 } else if ("-nested".equals(option)) {
344 scanNestedArchives = "".equals(optionExtraPart) || Boolean.valueOf(optionExtraPart).booleanValue();
345 } else if ("-exitcode".equals(option)) {
339346 setExitCode = true;
340 } else if (option.equals("-auxclasspathFromInput")) {
347 } else if ("-auxclasspathFromInput".equals(option)) {
341348 try {
342349 BufferedReader in = UTF8.bufferedReader(System.in);
343350 while (true) {
344351 String s = in.readLine();
345 if (s == null)
352 if (s == null) {
346353 break;
354 }
347355 addAuxClassPathEntries(s);
348356 }
349357 in.close();
350358 } catch (IOException e) {
351359 throw new RuntimeException(e);
352360 }
353 } else if (option.equals("-noClassOk")) {
361 } else if ("-noClassOk".equals(option)) {
354362 noClassOk = true;
355 } else if (option.equals("-xargs")) {
363 } else if ("-xargs".equals(option)) {
356364 xargs = true;
357 } else if (option.equals("-justListOptions")) {
365 } else if ("-justListOptions".equals(option)) {
358366 throw new RuntimeException("textui options are: " + parsedOptions);
359 } else if (option.equals("-printConfiguration")) {
367 } else if ("-printConfiguration".equals(option)) {
360368 printConfiguration = true;
361 } else if (option.equals("-version")) {
369 } else if ("-version".equals(option)) {
362370 printVersion = true;
363371 } else {
364372 if(DEBUG) {
377385 if (DEBUG) {
378386 System.out.println("option " + option + " is " + argument);
379387 }
380 if (option.equals("-outputFile") || option.equals("-output")) {
381 if (outputFile != null)
388 if ("-outputFile".equals(option) || "-output".equals(option)) {
389 if (outputFile != null) {
382390 throw new IllegalArgumentException("output set twice; to " + outputFile + " and to " + argument);
391 }
383392 outputFile = new File(argument);
384393
385394 String fileName = outputFile.getName();
386395 String extension = Util.getFileExtensionIgnoringGz(outputFile);
387 if (bugReporterType == PRINTING_REPORTER && (extension.equals("xml") || extension.equals("fba")))
396 if (bugReporterType == PRINTING_REPORTER && ("xml".equals(extension) || "fba".equals(extension))) {
388397 bugReporterType = XML_REPORTER;
398 }
389399
390400 try {
391401 OutputStream oStream = new BufferedOutputStream(new FileOutputStream(outputFile));
392 if (fileName.endsWith(".gz"))
402 if (fileName.endsWith(".gz")) {
393403 oStream = new GZIPOutputStream(oStream);
404 }
394405 outputStream = UTF8.printStream(oStream);
395406 } catch (IOException e) {
396407 System.err.println("Couldn't open " + outputFile + " for output: " + e.toString());
397408 System.exit(1);
398409 }
399 } else if (option.equals("-cloud"))
410 } else if ("-cloud".equals(option)) {
400411 project.setCloudId(argument);
401 else if (option.equals("-cloudProperty")) {
412 } else if ("-cloudProperty".equals(option)) {
402413 int e = argument.indexOf('=');
403 if (e == -1)
414 if (e == -1) {
404415 throw new IllegalArgumentException("Bad cloud property: " + argument);
416 }
405417 String key = argument.substring(0, e);
406418 String value = argument.substring(e + 1);
407419 project.getCloudProperties().setProperty(key, value);
408420
409 } else if (option.equals("-bugReporters")) {
421 } else if ("-bugReporters".equals(option)) {
410422 for (String s : argument.split(",")) {
411 if (s.charAt(0) == '-')
423 if (s.charAt(0) == '-') {
412424 disabledBugReporterDecorators.add(s.substring(1));
413 else if (s.charAt(0) == '+')
425 } else if (s.charAt(0) == '+') {
414426 enabledBugReporterDecorators.add(s.substring(1));
415 else
427 } else {
416428 enabledBugReporterDecorators.add(s);
417 }
418
419 } else if (option.equals("-maxRank")) {
429 }
430 }
431
432 } else if ("-maxRank".equals(option)) {
420433 this.rankThreshold = Integer.parseInt(argument);
421 } else if (option.equals("-projectName")) {
434 } else if ("-projectName".equals(option)) {
422435 this.projectName = argument;
423 } else if (option.equals("-release")) {
436 } else if ("-release".equals(option)) {
424437 this.releaseName = argument;
425 } else if (option.equals("-redoAnalysis")) {
438 } else if ("-redoAnalysis".equals(option)) {
426439 redoAnalysisFile = argument;
427 } else if (option.equals("-sourceInfo")) {
440 } else if ("-sourceInfo".equals(option)) {
428441 sourceInfoFile = argument;
429 } else if (option.equals("-visitors") || option.equals("-omitVisitors")) {
430 boolean omit = option.equals("-omitVisitors");
442 } else if ("-visitors".equals(option) || "-omitVisitors".equals(option)) {
443 boolean omit = "-omitVisitors".equals(option);
431444
432445 if (!omit) {
433446 // Selecting detectors explicitly, so start out by
441454 while (tok.hasMoreTokens()) {
442455 String visitorName = tok.nextToken().trim();
443456 DetectorFactory factory = DetectorFactoryCollection.instance().getFactory(visitorName);
444 if (factory == null)
457 if (factory == null) {
445458 throw new IllegalArgumentException("Unknown detector: " + visitorName);
459 }
446460 getUserPreferences().enableDetector(factory, !omit);
447461 }
448 } else if (option.equals("-chooseVisitors")) {
462 } else if ("-chooseVisitors".equals(option)) {
449463 // This is like -visitors and -omitVisitors, but
450464 // you can selectively enable and disable detectors,
451465 // starting from the default set (or whatever set
452466 // happens to be in effect).
453467 choose(argument, "Detector choices", new Chooser() {
468 @Override
454469 public void choose(boolean enabled, String what) {
455470 DetectorFactory factory = DetectorFactoryCollection.instance().getFactory(what);
456 if (factory == null)
471 if (factory == null) {
457472 throw new IllegalArgumentException("Unknown detector: " + what);
473 }
458474 if (FindBugs.DEBUG) {
459475 System.err.println("Detector " + factory.getShortName() + " " + (enabled ? "enabled" : "disabled")
460476 + ", userPreferences=" + System.identityHashCode(getUserPreferences()));
462478 getUserPreferences().enableDetector(factory, enabled);
463479 }
464480 });
465 } else if (option.equals("-choosePlugins")) {
481 } else if ("-choosePlugins".equals(option)) {
466482 // Selectively enable/disable plugins
467483 choose(argument, "Plugin choices", new Chooser() {
484 @Override
468485 public void choose(boolean enabled, String what) {
469486 Plugin plugin = DetectorFactoryCollection.instance().getPluginById(what);
470 if (plugin == null)
487 if (plugin == null) {
471488 throw new IllegalArgumentException("Unknown plugin: " + what);
489 }
472490 plugin.setGloballyEnabled(enabled);
473491 }
474492 });
475 } else if (option.equals("-adjustPriority")) {
493 } else if ("-adjustPriority".equals(option)) {
476494 // Selectively raise or lower the priority of warnings
477495 // produced by specified detectors.
478496
480498 while (tok.hasMoreTokens()) {
481499 String token = tok.nextToken();
482500 int eq = token.indexOf('=');
483 if (eq < 0)
501 if (eq < 0) {
484502 throw new IllegalArgumentException("Illegal priority adjustment: " + token);
503 }
485504
486505 String adjustmentTarget = token.substring(0, eq);
487506 String adjustment = token.substring(eq + 1);
488507
489508 int adjustmentAmount;
490 if (adjustment.equals("raise"))
509 if ("raise".equals(adjustment)) {
491510 adjustmentAmount = -1;
492 else if (adjustment.equals("lower"))
511 } else if ("lower".equals(adjustment)) {
493512 adjustmentAmount = +1;
494 else if (adjustment.equals("suppress"))
513 } else if ("suppress".equals(adjustment)) {
495514 adjustmentAmount = +100;
496 else
515 } else {
497516 throw new IllegalArgumentException("Illegal priority adjustment value: " + adjustment);
517 }
498518
499519 DetectorFactory factory = DetectorFactoryCollection.instance().getFactory(adjustmentTarget);
500 if (factory != null)
520 if (factory != null) {
501521 factory.setPriorityAdjustment(adjustmentAmount);
502 else {
522 } else {
503523 //
504524 DetectorFactoryCollection i18n = DetectorFactoryCollection.instance();
505525 BugPattern pattern = i18n.lookupBugPattern(adjustmentTarget);
506 if (pattern == null)
526 if (pattern == null) {
507527 throw new IllegalArgumentException("Unknown detector: " + adjustmentTarget);
528 }
508529 pattern.adjustPriority(adjustmentAmount);
509530 }
510531
511532 }
512 } else if (option.equals("-bugCategories")) {
533 } else if ("-bugCategories".equals(option)) {
513534 this.bugCategorySet = FindBugs.handleBugCategories(argument);
514 } else if (option.equals("-onlyAnalyze")) {
535 } else if ("-onlyAnalyze".equals(option)) {
515536 // The argument is a comma-separated list of classes and packages
516537 // to select to analyze. (If a list item ends with ".*",
517538 // it specifies a package, otherwise it's a class.)
518539 StringTokenizer tok = new StringTokenizer(argument, ",");
519540 while (tok.hasMoreTokens()) {
520541 String item = tok.nextToken();
521 if (item.endsWith(".-"))
542 if (item.endsWith(".-")) {
522543 classScreener.addAllowedPrefix(item.substring(0, item.length() - 1));
523 else if (item.endsWith(".*"))
544 } else if (item.endsWith(".*")) {
524545 classScreener.addAllowedPackage(item.substring(0, item.length() - 1));
525 else
546 } else {
526547 classScreener.addAllowedClass(item);
527 }
528 } else if (option.equals("-exclude")) {
548 }
549 }
550 } else if ("-exclude".equals(option)) {
529551 project.getConfiguration().getExcludeFilterFiles().put(argument, true);
530 } else if (option.equals("-excludeBugs")) {
552 } else if ("-excludeBugs".equals(option)) {
531553 project.getConfiguration().getExcludeBugsFiles().put(argument, true);
532 } else if (option.equals("-include")) {
554 } else if ("-include".equals(option)) {
533555 project.getConfiguration().getIncludeFilterFiles().put(argument, true);
534 } else if (option.equals("-auxclasspath")) {
556 } else if ("-auxclasspathFromFile".equals(option)) {
557 handleAuxClassPathFromFile(argument);
558 } else if ("-analyzeFromFile".equals(option)) {
559 handleAnalyzeFromFile(argument);
560 } else if ("-auxclasspath".equals(option)) {
535561 addAuxClassPathEntries(argument);
536 } else if (option.equals("-sourcepath")) {
562 } else if ("-sourcepath".equals(option)) {
537563 StringTokenizer tok = new StringTokenizer(argument, File.pathSeparator);
538 while (tok.hasMoreTokens())
564 while (tok.hasMoreTokens()) {
539565 project.addSourceDir(new File(tok.nextToken()).getAbsolutePath());
540 } else if(option.equals("-userPrefs")){
566 }
567 } else if("-userPrefs".equals(option)){
541568 UserPreferences prefs = UserPreferences.createDefaultUserPreferences();
542569 prefs.read(new FileInputStream(argument));
543570 project.setConfiguration(prefs);
553580 */
554581 private void addAuxClassPathEntries(String argument) {
555582 StringTokenizer tok = new StringTokenizer(argument, File.pathSeparator);
556 while (tok.hasMoreTokens())
583 while (tok.hasMoreTokens()) {
557584 project.addAuxClasspathEntry(tok.nextToken());
585 }
558586 }
559587
560588 /**
571599 StringTokenizer tok = new StringTokenizer(argument, ",");
572600 while (tok.hasMoreTokens()) {
573601 String what = tok.nextToken().trim();
574 if (!what.startsWith("+") && !what.startsWith("-"))
602 if (!what.startsWith("+") && !what.startsWith("-")) {
575603 throw new IllegalArgumentException(desc + " must start with " + "\"+\" or \"-\" (saw " + what + ")");
604 }
576605 boolean enabled = what.startsWith("+");
577606 chooser.choose(enabled, what.substring(1));
578607 }
612641
613642 textuiBugReporter = xmlBugReporter;
614643 }
615 break;
644 break;
616645 case EMACS_REPORTER:
617646 textuiBugReporter = new EmacsBugReporter();
618647 break;
626655 throw new IllegalStateException();
627656 }
628657
629 if (quiet)
658 if (quiet) {
630659 textuiBugReporter.setErrorVerbosity(BugReporter.SILENT);
660 }
631661
632662 textuiBugReporter.setPriorityThreshold(priorityThreshold);
633663 textuiBugReporter.setRankThreshold(rankThreshold);
634664 textuiBugReporter.setUseLongBugCodes(useLongBugCodes);
635665
636666 findBugs.setRankThreshold(rankThreshold);
637 if (outputStream != null)
667 if (outputStream != null) {
638668 textuiBugReporter.setOutputStream(outputStream);
669 }
639670
640671 BugReporter bugReporter = textuiBugReporter;
641672
694725 if (getXargs()) {
695726 BufferedReader in = UTF8.bufferedReader(System.in);
696727 try {
728 while (true) {
729 String s = in.readLine();
730 if (s == null) {
731 break;
732 }
733 project.addFile(s);
734 }
735 } finally {
736 Util.closeSilently(in);
737 }
738 }
739 }
740
741 /**
742 * Handle -readAuxFromFile command line option by reading classpath entries
743 * from a file and adding them to the project.
744 *
745 * @throws IOException
746 */
747 private void handleAuxClassPathFromFile(String filePath) throws IOException {
748 BufferedReader in = new BufferedReader(UTF8.fileReader(filePath));
749 try {
697750 while (true) {
698751 String s = in.readLine();
699 if (s == null)
752 if (s == null) {
700753 break;
754 }
755 project.addAuxClasspathEntry(s);
756 }
757 } finally {
758 Util.closeSilently(in);
759 }
760 }
761
762 /**
763 * Handle -analyzeFromFile command line option by reading jar file names
764 * from a file and adding them to the project.
765 *
766 * @throws IOException
767 */
768 private void handleAnalyzeFromFile(String filePath) throws IOException {
769 BufferedReader in = new BufferedReader(UTF8.fileReader(filePath));
770 try {
771 while (true) {
772 String s = in.readLine();
773 if (s == null) {
774 break;
775 }
701776 project.addFile(s);
702777 }
703 } finally {
704 Util.closeSilently(in);
705 }
778 } finally {
779 Util.closeSilently(in);
706780 }
707781 }
708782
2323 /**
2424 * Display FindBugs progress in the terminal window using ASCII codes. We assume
2525 * that the terminal window is at least 80 characters wide.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class TextUIProgressCallback implements FindBugsProgress {
30 private PrintStream out;
30 private final PrintStream out;
3131
3232 private int goal;
3333
4141 this.out = out;
4242 }
4343
44 @Override
4445 public void reportNumberOfArchives(int numArchives) {
4546 this.goal = numArchives;
4647 this.count = 0;
4748 scanningArchives(0);
4849 }
4950
51 @Override
5052 public void finishArchive() {
5153 scanningArchives(++count);
5254 }
5355
56 @Override
5457 public void predictPassCount(int[] classesPerPass) {
5558 out.println();
5659 printMessage(classesPerPass.length + " analysis passes to perform");
5861 this.pass = 0;
5962 }
6063
64 @Override
6165 public void startAnalysis(int numClasses) {
6266 if (pass == 0) {
6367 out.println();
6771 analyzingClasses(0);
6872 }
6973
74 @Override
7075 public void finishClass() {
7176 analyzingClasses(++count);
7277 }
7378
79 @Override
7480 public void finishPerClassAnalysis() {
7581 out.println();
7682 ++pass;
96102 out.print("\r" + msg);
97103 }
98104
105 @Override
99106 public void startArchive(String name) {
100107 // noop
101108 }
2020
2121 /**
2222 * Simple token class.
23 *
23 *
2424 * @author David Hovemeyer
2525 * @see Tokenizer
2626 */
5555 */
5656 public static final int COMMENT = 3;
5757
58 private int kind;
58 private final int kind;
5959
60 private String lexeme;
60 private final String lexeme;
6161
6262 /**
6363 * Constructor.
64 *
64 *
6565 * @param kind
6666 * the kind of token
6767 * @param lexeme
7474
7575 /**
7676 * Constructor when there is no text. E.g., EOF and EOL.
77 *
77 *
7878 * @param kind
7979 * the kind of token
8080 */
9898 }
9999 }
100100
101 // vim:ts=4
2626 /**
2727 * A simple tokenizer for Java source text. This is not intended to be a
2828 * compliant lexer; instead, it is for quick and dirty scanning.
29 *
29 *
3030 * @author David Hovemeyer
3131 * @see Token
3232 */
6868 single.set('~');
6969 }
7070
71 private PushbackReader reader;
71 private final PushbackReader reader;
7272
7373 /**
7474 * Constructor.
75 *
75 *
7676 * @param reader
7777 * the Reader for the Java source text
7878 */
8282
8383 /**
8484 * Get the next Token in the stream.
85 *
85 *
8686 * @return the Token
8787 */
8888 public Token next() throws IOException {
8989 skipWhitespace();
9090 int c = reader.read();
91 if (c < 0)
91 if (c < 0) {
9292 return new Token(Token.EOF);
93 else if (c == '\n')
93 } else if (c == '\n') {
9494 return new Token(Token.EOL);
95 else if (c == '\'' || c == '"')
95 } else if (c == '\'' || c == '"') {
9696 return munchString(c);
97 else if (c == '/')
97 } else if (c == '/') {
9898 return maybeComment();
99 else if (single.get(c))
99 } else if (single.get(c)) {
100100 return new Token(Token.SINGLE, String.valueOf((char) c));
101 else {
101 } else {
102102 reader.unread(c);
103103 return parseWord();
104104 }
107107 private void skipWhitespace() throws IOException {
108108 for (;;) {
109109 int c = reader.read();
110 if (c < 0)
111 break;
110 if (c < 0) {
111 break;
112 }
112113 if (!whiteSpace.get(c)) {
113114 reader.unread(c);
114115 break;
127128
128129 while (state != DONE) {
129130 int c = reader.read();
130 if (c < 0)
131 break;
131 if (c < 0) {
132 break;
133 }
132134 result.append((char) c);
133135 switch (state) {
134136 case SCAN:
135 if (c == delimiter)
137 if (c == delimiter) {
136138 state = DONE;
137 else if (c == '\\')
139 } else if (c == '\\') {
138140 state = ESCAPE;
141 }
139142 break;
140143 case ESCAPE:
141144 state = SCAN;
145 break;
146 default:
142147 break;
143148 }
144149 }
153158 result.append("//");
154159 for (;;) {
155160 c = reader.read();
156 if (c < 0)
157 break;
158 else if (c == '\n') {
161 if (c < 0) {
162 break;
163 } else if (c == '\n') {
159164 reader.unread(c);
160165 break;
161166 }
172177 int state = SCAN;
173178 while (state != DONE) {
174179 c = reader.read();
175 if (c < 0)
180 if (c < 0) {
176181 state = DONE;
177 else
182 } else {
178183 result.append((char) c);
184 }
179185 switch (state) {
180186 case SCAN:
181 if (c == '*')
187 if (c == '*') {
182188 state = STAR;
189 }
183190 break;
184191 case STAR:
185 if (c == '/')
192 if (c == '/') {
186193 state = DONE;
187 else if (c != '*')
194 } else if (c != '*') {
188195 state = SCAN;
196 }
189197 break;
190198 case DONE:
191199 break;
193201 }
194202 return new Token(Token.COMMENT, result.toString());
195203 } else {
196 if (c >= 0)
204 if (c >= 0) {
197205 reader.unread(c);
206 }
198207 return new Token(Token.SINGLE, "/");
199208 }
200209 }
203212 StringBuilder result = new StringBuilder();
204213 for (;;) {
205214 int c = reader.read();
206 if (c < 0)
207 break;
215 if (c < 0) {
216 break;
217 }
208218 if (whiteSpace.get(c) || c == '\n' || single.get(c)) {
209219 reader.unread(c);
210220 break;
215225 }
216226 }
217227
218 // vim:ts=4
1919
2020 /**
2121 * Interface to mark Detector classes which are used only as a training pass.
22 *
22 *
2323 * @author David Hovemeyer
2424 */
2525 public interface TrainingDetector {
3131 /**
3232 * Bug annotation class for java types. This is of lighter weight than
3333 * ClassAnnotation, and can be used for things like array types.
34 *
34 *
3535 * @see ClassAnnotation
3636 */
3737 public class TypeAnnotation extends BugAnnotationWithSourceLines {
5555
5656 /**
5757 * constructor.
58 *
58 *
5959 * <p>
6060 * For information on type descriptors, <br>
6161 * see http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.
6262 * html#14152 <br>
6363 * or http://www.murrayc.com/learning/java/java_classfileformat.shtml#
6464 * TypeDescriptors
65 *
65 *
6666 * @param typeDescriptor
6767 * a jvm type descriptor, such as "[I"
6868 */
7878 this(objectType.getSignature(), roleDescription);
7979 if (objectType instanceof GenericObjectType) {
8080 GenericObjectType genericObjectType = (GenericObjectType) objectType;
81 if (genericObjectType.getTypeCategory() == GenericUtilities.TypeCategory.PARAMETERIZED)
81 if (genericObjectType.getTypeCategory() == GenericUtilities.TypeCategory.PARAMETERIZED) {
8282 typeParameters = genericObjectType.getGenericParametersAsString();
83 }
8384 }
8485 }
8586
9293 if (context != null) {
9394 this.sourceFileName = context.lookupSourceFile(className);
9495 this.sourceLines = ClassAnnotation.getSourceLinesForClass(className, sourceFileName);
95 } else
96 } else {
9697 this.sourceFileName = SourceLineAnnotation.UNKNOWN_SOURCE_FILE;
98 }
9799 }
98100 }
99101
100102 /**
101103 * Get the type descriptor.
102 *
104 *
103105 * @return the jvm type descriptor, such as "[I"
104106 */
105107 public String getTypeDescriptor() {
106108 return descriptor;
107109 }
108110
111 @Override
109112 public void accept(BugAnnotationVisitor visitor) {
110113 visitor.visitTypeAnnotation(this);
111114 }
112115
116 @Override
113117 public String format(String key, ClassAnnotation primaryClass) {
114118 String name = new SignatureConverter(descriptor).parseNext().replace("java.lang.", "");
115 if (key.equals("givenClass"))
119 if ("givenClass".equals(key)) {
116120 name = PackageMemberAnnotation.shorten(primaryClass.getPackageName(), name);
117 else if (key.equals("excludingPackage"))
121 } else if ("excludingPackage".equals(key)) {
118122 name = PackageMemberAnnotation.removePackage(name);
119
120 if (typeParameters != null && !key.equals("hash"))
123 }
124
125 if (typeParameters != null && !"hash".equals(key)) {
121126 name = name + typeParameters;
127 }
122128 return name;
123129 }
124130
131 @Override
125132 public void setDescription(String roleDescription) {
126133 this.roleDescription = roleDescription.intern();
127134 }
128135
136 @Override
129137 public String getDescription() {
130138 return roleDescription;
131139 }
134142 this.typeParameters = typeParameters;
135143 }
136144
145 public String getTypeParameters() {
146 return typeParameters;
147 }
148
137149 @Override
138150 public int hashCode() {
139151 return descriptor.hashCode();
141153
142154 @Override
143155 public boolean equals(Object o) {
144 if (!(o instanceof TypeAnnotation))
156 if (!(o instanceof TypeAnnotation)) {
145157 return false;
158 }
146159 return descriptor.equals(((TypeAnnotation) o).descriptor);
147160 }
148161
162 @Override
149163 public int compareTo(BugAnnotation o) {
150 if (!(o instanceof TypeAnnotation)) // BugAnnotations must be Comparable
151 // with any type of BugAnnotation
164 if (!(o instanceof TypeAnnotation)) {
165 // with any type of BugAnnotation
152166 return this.getClass().getName().compareTo(o.getClass().getName());
167 }
153168 return descriptor.compareTo(((TypeAnnotation) o).descriptor);
154169 // could try to determine equivalence with ClassAnnotation, but don't
155170 // see how this would be useful
170185
171186 private static final String ELEMENT_NAME = "Type";
172187
188 @Override
173189 public void writeXML(XMLOutput xmlOutput) throws IOException {
174190 writeXML(xmlOutput, false, false);
175191 }
176192
193 @Override
177194 public void writeXML(XMLOutput xmlOutput, boolean addMessages, boolean isPrimary) throws IOException {
178195 XMLAttributeList attributeList = new XMLAttributeList().addAttribute("descriptor", descriptor);
179196
180197 String role = getDescription();
181 if (!role.equals(DEFAULT_ROLE))
198 if (!DEFAULT_ROLE.equals(role)) {
182199 attributeList.addAttribute("role", role);
183 if (typeParameters != null)
200 }
201 if (typeParameters != null) {
184202 attributeList.addAttribute("typeParameters", typeParameters);
203 }
185204
186205 BugAnnotationUtil.writeXML(xmlOutput, ELEMENT_NAME, this, attributeList, addMessages);
187206 }
188207
208 @Override
189209 public boolean isSignificant() {
190210 return true;
191211 }
2020
2121 /**
2222 * A marker interface for detectors which use an AnnotationDatabase.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public interface UseAnnotationDatabase {
0 UNCLASSIFIED=\u672a\u5206\u985e(unclassified)
1 NEEDS_STUDY=\u8abf\u67fb\u304c\u5fc5\u8981(needs further study)
2 BAD_ANALYSIS=\u8aa4\u691c\u51fa(bad analysis)
3 NOT_A_BUG=\u30d0\u30b0\u3067\u306f\u306a\u3044(not a bug)
4 MOSTLY_HARMLESS=\u307b\u3068\u3093\u3069\u7121\u5bb3(mostly harmless)
5 SHOULD_FIX=\u4fee\u6b63\u3057\u305f\u307b\u3046\u304c\u3088\u3044(should fix)
6 MUST_FIX=\u4fee\u6b63\u3057\u306a\u3051\u308c\u3070\u306a\u3089\u306a\u3044(must fix)
0 BAD_ANALYSIS = \u8aa4\u691c\u51fa (bad analysis)
1 I_WILL_FIX = \u4fee\u6b63\u3059\u308b (I will fix)
2 MOSTLY_HARMLESS = \u307b\u3068\u3093\u3069\u7121\u5bb3 (mostly harmless)
3 MUST_FIX = \u4fee\u6b63\u3057\u306a\u3051\u308c\u3070\u306a\u3089\u306a\u3044 (must fix)
4 NEEDS_STUDY = \u8abf\u67fb\u304c\u5fc5\u8981 (needs further study)
5 NOT_A_BUG = \u30d0\u30b0\u3067\u306f\u306a\u3044 (not a bug)
6 OBSOLETE_CODE = \u5ec3\u6b62/\u4f7f\u308f\u308c\u306a\u3044\u30b3\u30fc\u30c9 (obsolete/unused code)
7 SHOULD_FIX = \u4fee\u6b63\u3059\u3079\u304d (should fix)
8 UNCLASSIFIED = \u672a\u5206\u985e (unclassified)
4343 /**
4444 * Major version number.
4545 */
46 public static final int MAJOR = 2;
46 public static final int MAJOR = 3;
4747
4848 /**
4949 * Minor version number.
5353 /**
5454 * Patch level.
5555 */
56 public static final int PATCHLEVEL = 3;
56 public static final int PATCHLEVEL = 1;
5757
5858 /**
5959 * Development version or release candidate?
6060 */
61 public static final boolean IS_DEVELOPMENT = false;
61 public static final boolean IS_DEVELOPMENT = true;
6262
6363 /**
6464 * Release candidate number. "0" indicates that the version is not a release
6767 public static final int RELEASE_CANDIDATE = 0;
6868
6969
70 public static final String SVN_REVISION = System.getProperty("svn.revision", "Unknown");
70 public static final String GIT_REVISION = System.getProperty("git.revision", "UNKNOWN");
71
7172 /**
7273 * Release date.
7374 */
8990 SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss z, dd MMMM, yyyy", Locale.ENGLISH);
9091 SimpleDateFormat eclipseDateFormat = new SimpleDateFormat("yyyyMMdd", Locale.ENGLISH);
9192 SimpleDateFormat releaseDateFormat = new SimpleDateFormat(UpdateChecker.PLUGIN_RELEASE_DATE_FMT, Locale.ENGLISH);
92
9393 Date now = new Date();
9494 COMPUTED_DATE = dateFormat.format(now);
9595 COMPUTED_ECLIPSE_DATE = eclipseDateFormat.format(now);
106106 private static final String RELEASE_SUFFIX_WORD;
107107 static {
108108 String suffix;
109 if (RELEASE_CANDIDATE > 0)
109 if (RELEASE_CANDIDATE > 0) {
110110 suffix = "rc" + RELEASE_CANDIDATE;
111 else if (PREVIEW > 0)
111 } else if (PREVIEW > 0) {
112112 suffix = "preview" + PREVIEW;
113 else {
113 } else {
114114 suffix = "dev-" + COMPUTED_ECLIPSE_DATE;
115 if (!SVN_REVISION.equals("Unknown"))
116 suffix += "-r" + SVN_REVISION;
115 if (!"Unknown".equals(GIT_REVISION)) {
116 suffix += "-" + GIT_REVISION;
117 }
117118 }
118119 RELEASE_SUFFIX_WORD = suffix;
119120 }
138139 static {
139140 Class<Version> c = Version.class;
140141 URL u = c.getResource(c.getSimpleName() + ".class");
141 boolean fromFile = u.getProtocol().equals("file");
142 boolean fromFile = "file".equals(u.getProtocol());
142143 InputStream in = null;
143144 String release = null;
144145 String date = null;
145146 String plugin_release_date = null;
146 if (!fromFile)
147 if (!fromFile) {
147148 try {
148149 Properties versionProperties = new Properties();
149150 in = Version.class.getResourceAsStream("version.properties");
158159 } finally {
159160 Util.closeSilently(in);
160161 }
161 if (release == null)
162 }
163 if (release == null) {
162164 release = COMPUTED_RELEASE;
163 if (date == null)
165 }
166 if (date == null) {
164167 date = COMPUTED_DATE;
165 if (plugin_release_date == null)
168 }
169 if (plugin_release_date == null) {
166170 plugin_release_date = COMPUTED_PLUGIN_RELEASE_DATE;
171 }
167172
168173 RELEASE = release;
169174 DATE = date;
174179
175180 parsedDate = fmt.parse(CORE_PLUGIN_RELEASE_DATE);
176181 } catch (ParseException e) {
177 if (SystemProperties.ASSERTIONS_ENABLED)
182 if (SystemProperties.ASSERTIONS_ENABLED) {
178183 e.printStackTrace();
184 }
179185 parsedDate = null;
180186 }
181187 releaseDate = parsedDate;
222228
223229 String arg = argv[0];
224230
225 if (arg.equals("-release"))
231 if ("-release".equals(arg)) {
226232 System.out.println(RELEASE);
227 else if (arg.equals("-date"))
233 } else if ("-date".equals(arg)) {
228234 System.out.println(DATE);
229 else if (arg.equals("-props")) {
235 } else if ("-props".equals(arg)) {
230236 System.out.println("release.base=" + RELEASE_BASE);
231237 System.out.println("release.number=" + COMPUTED_RELEASE);
232238 System.out.println("release.date=" + COMPUTED_DATE);
234240 System.out.println("eclipse.ui.version=" + COMPUTED_ECLIPSE_UI_VERSION);
235241 System.out.println("findbugs.website=" + WEBSITE);
236242 System.out.println("findbugs.downloads.website=" + DOWNLOADS_WEBSITE);
237 System.out.println("findbugs.svn.revision=" + SVN_REVISION);
238 } else if (arg.equals("-plugins")) {
243 System.out.println("findbugs.git.revision=" + GIT_REVISION);
244 } else if ("-plugins".equals(arg)) {
239245 DetectorFactoryCollection.instance();
240246 for(Plugin p : Plugin.getAllPlugins()) {
241247 System.out.println("Plugin: " + p.getPluginId());
242248 System.out.println(" description: " + p.getShortDescription());
243249 System.out.println(" provider: " + p.getProvider());
244250 String version = p.getVersion();
245 if (version != null && version.length() > 0)
251 if (version != null && version.length() > 0) {
246252 System.out.println(" version: " + version);
253 }
247254 String website = p.getWebsite();
248 if (website != null && website.length() > 0)
249 System.out.println(" website: " + website);
255 if (website != null && website.length() > 0) {
256 System.out.println(" website: " + website);
257 }
250258 System.out.println();
251259 }
252 } else if (arg.equals("-configuration")){
260 } else if ("-configuration".equals(arg)){
253261 printVersion(true);
254262 } else {
255263
263271 }
264272
265273 public static String getReleaseWithDateIfDev() {
266 if (IS_DEVELOPMENT)
274 if (IS_DEVELOPMENT) {
267275 return RELEASE + " (" + DATE + ")";
276 }
268277 return RELEASE;
269278 }
270279
282291 for (Plugin plugin : Plugin.getAllPlugins()) {
283292 System.out.printf("Plugin %s, version %s, loaded from %s%n", plugin.getPluginId(), plugin.getVersion(),
284293 plugin.getPluginLoader().getURL());
285 if (plugin.isCorePlugin())
294 if (plugin.isCorePlugin()) {
286295 System.out.println(" is core plugin");
287 if (plugin.isInitialPlugin())
296 }
297 if (plugin.isInitialPlugin()) {
288298 System.out.println(" is initial plugin");
289 if (plugin.isEnabledByDefault())
299 }
300 if (plugin.isEnabledByDefault()) {
290301 System.out.println(" is enabled by default");
291 if (plugin.isGloballyEnabled())
302 }
303 if (plugin.isGloballyEnabled()) {
292304 System.out.println(" is globally enabled");
305 }
293306 Plugin parent = plugin.getParentPlugin();
294307 if (parent != null) {
295308 System.out.println(" has parent plugin " + parent.getPluginId());
305318 System.out.println();
306319 }
307320 printPluginUpdates(true, 10);
308 } else
321 } else {
309322 printPluginUpdates(false, 3);
323 }
310324 }
311325
312326 private static void printPluginUpdates(boolean verbose, int secondsToWait) throws InterruptedException {
329343 try {
330344 Collection<UpdateChecker.PluginUpdate> updates = updateHolder.get(secondsToWait, TimeUnit.SECONDS);
331345 if (updates.isEmpty()) {
332 if (verbose)
346 if (verbose) {
333347 System.out.println("none!");
348 }
334349 } else {
335350 System.out.println();
336351 for (UpdateChecker.PluginUpdate update : updates) {
340355 }
341356 }
342357 } catch (TimeoutException e) {
343 if (verbose)
358 if (verbose) {
344359 System.out.println("Timeout while trying to get updates");
360 }
345361 }
346362
347363 }
348364
349365 }
350366
351 // vim:ts=4
3535
3636 private boolean exactBugPatternMatch = true;
3737
38 private boolean comparePriorities = false;
38 private boolean comparePriorities;
3939
4040 public VersionInsensitiveBugComparator() {
4141 }
4242
43 @Override
4344 public void setClassNameRewriter(ClassNameRewriter classNameRewriter) {
4445 this.classNameRewriter = classNameRewriter;
4546 }
5354 * don't care about.
5455 */
5556 private class FilteringAnnotationIterator implements Iterator<BugAnnotation> {
56 private Iterator<BugAnnotation> iter;
57 private final Iterator<BugAnnotation> iter;
5758
5859 private BugAnnotation next;
5960
6263 this.next = null;
6364 }
6465
66 @Override
6567 public boolean hasNext() {
6668 findNext();
6769 return next != null;
6870 }
6971
72 @Override
7073 public BugAnnotation next() {
7174 findNext();
72 if (next == null)
75 if (next == null) {
7376 throw new NoSuchElementException();
77 }
7478 BugAnnotation result = next;
7579 next = null;
7680 return result;
7781 }
7882
83 @Override
7984 public void remove() {
8085 throw new UnsupportedOperationException();
8186 }
8287
8388 private void findNext() {
8489 while (next == null) {
85 if (!iter.hasNext())
90 if (!iter.hasNext()) {
8691 break;
92 }
8793 BugAnnotation candidate = iter.next();
8894 if (!isBoring(candidate)) {
8995 next = candidate;
98104 return !(annotation instanceof LocalVariableAnnotation || annotation.isSignificant());
99105 }
100106
107 /*
101108 private static int compareNullElements(Object a, Object b) {
102109 if (a != null)
103110 return 1;
109116
110117 private static String getCode(String pattern) {
111118 int sep = pattern.indexOf('_');
112 if (sep < 0)
119 if (sep < 0) {
113120 return "";
121 }
114122 return pattern.substring(0, sep);
115123 }
116124
120128 while (i.hasNext()) {
121129 System.out.println(" " + i.next());
122130 }
123
124 }
125
131 }*/
132
133 @Override
126134 public int compare(BugInstance lhs, BugInstance rhs) {
127135 // Attributes of BugInstance.
128136 // Compare abbreviation
134142 BugPattern lhsPattern = lhs.getBugPattern();
135143 BugPattern rhsPattern = rhs.getBugPattern();
136144
137 if (lhsPattern == null || rhsPattern == null) {
138 // One of the patterns is missing.
139 // However, we can still accurately match by abbrev (usually) by
140 // comparing
141 // the part of the type before the first '_' character.
142 // This is almost always equivalent to the abbrev.
143
144 String lhsCode = getCode(lhs.getType());
145 String rhsCode = getCode(rhs.getType());
146
147 if ((cmp = lhsCode.compareTo(rhsCode)) != 0) {
148 return cmp;
149 }
150 } else {
151 // Compare by abbrev instead of type. The specific bug type can
152 // change
153 // (e.g., "definitely null" to "null on simple path"). Also, we
154 // often
155 // change bug pattern types from one version of FindBugs to the
156 // next.
157 //
158 // Source line and field name are still matched precisely, so this
159 // shouldn't
160 // cause loss of precision.
161 if ((cmp = lhsPattern.getAbbrev().compareTo(rhsPattern.getAbbrev())) != 0)
162 return cmp;
163 if (isExactBugPatternMatch() && (cmp = lhsPattern.getType().compareTo(rhsPattern.getType())) != 0)
164 return cmp;
145 // Compare by abbrev instead of type. The specific bug type can
146 // change
147 // (e.g., "definitely null" to "null on simple path"). Also, we
148 // often
149 // change bug pattern types from one version of FindBugs to the
150 // next.
151 //
152 // Source line and field name are still matched precisely, so this
153 // shouldn't
154 // cause loss of precision.
155 if ((cmp = lhsPattern.getAbbrev().compareTo(rhsPattern.getAbbrev())) != 0) {
156 return cmp;
157 }
158 if (isExactBugPatternMatch() && (cmp = lhsPattern.getType().compareTo(rhsPattern.getType())) != 0) {
159 return cmp;
165160 }
166161
167162 if (comparePriorities) {
168163 cmp = lhs.getPriority() - rhs.getPriority();
169 if (cmp != 0)
164 if (cmp != 0) {
170165 return cmp;
166 }
171167 }
172168
173169 Iterator<BugAnnotation> lhsIter = new FilteringAnnotationIterator(lhs.annotationIterator());
174170 Iterator<BugAnnotation> rhsIter = new FilteringAnnotationIterator(rhs.annotationIterator());
175171
176 annotationLoop: while (lhsIter.hasNext() && rhsIter.hasNext()) {
172 while (lhsIter.hasNext() && rhsIter.hasNext()) {
177173 BugAnnotation lhsAnnotation = lhsIter.next();
178174 BugAnnotation rhsAnnotation = rhsIter.next();
179175 Class<? extends BugAnnotation> lhsClass;
182178 // so just compare by class name.
183179 lhsClass = lhsAnnotation.getClass();
184180 Class<? extends BugAnnotation> rhsClass = rhsAnnotation.getClass();
185 if (lhsClass == rhsClass)
181 if (lhsClass == rhsClass) {
186182 break;
183 }
187184 if (lhsClass == LocalVariableAnnotation.class && !((LocalVariableAnnotation) lhsAnnotation).isSignificant()
188 && lhsIter.hasNext())
185 && lhsIter.hasNext()) {
189186 lhsAnnotation = lhsIter.next();
190 else if (rhsClass == LocalVariableAnnotation.class && !((LocalVariableAnnotation) rhsAnnotation).isSignificant()
191 && rhsIter.hasNext())
187 } else if (rhsClass == LocalVariableAnnotation.class && !((LocalVariableAnnotation) rhsAnnotation).isSignificant()
188 && rhsIter.hasNext()) {
192189 rhsAnnotation = rhsIter.next();
193 else
190 } else {
194191 return lhsClass.getName().compareTo(rhsClass.getName());
192 }
195193 }
196194
197195 if (lhsClass == ClassAnnotation.class) {
227225 } else if (lhsClass == LocalVariableAnnotation.class) {
228226 String lhsName = ((LocalVariableAnnotation) lhsAnnotation).getName();
229227 String rhsName = ((LocalVariableAnnotation) rhsAnnotation).getName();
230 if (lhsName.equals("?") || rhsName.equals("?"))
228 if ("?".equals(lhsName) || "?".equals(rhsName)) {
231229 continue;
230 }
232231 cmp = lhsName.compareTo(rhsName);
233232
234233 } else if (lhsClass == TypeAnnotation.class) {
245244
246245 } else if (isBoring(lhsAnnotation)) {
247246 throw new IllegalStateException("Impossible");
248 } else
247 } else {
249248 throw new IllegalStateException("Unknown annotation type: " + lhsClass.getName());
250 if (cmp != 0)
249 }
250 if (cmp != 0) {
251251 return cmp;
252 }
253
254 if (interestingNext(rhsIter))
252 }
253 }
254
255 if (interestingNext(rhsIter)) {
255256 return -1;
256 else if (interestingNext(lhsIter))
257 } else if (interestingNext(lhsIter)) {
257258 return 1;
258 else
259 } else {
259260 return 0;
261 }
260262 }
261263
262264 private boolean interestingNext(Iterator<BugAnnotation> i) {
263265 while (i.hasNext()) {
264266 BugAnnotation a = i.next();
265 if (isBoring(a))
267 if (isBoring(a)) {
266268 continue;
267 if (!(a instanceof LocalVariableAnnotation))
269 }
270 if (!(a instanceof LocalVariableAnnotation)) {
268271 return true;
269 if (((LocalVariableAnnotation) a).isSignificant())
272 }
273 if (((LocalVariableAnnotation) a).isSignificant()) {
270274 return true;
275 }
271276 }
272277 return false;
273278 }
274279
275 /**
276 * @param exactBugPatternMatch
277 * The exactBugPatternMatch to set.
278 */
279280 public void setExactBugPatternMatch(boolean exactBugPatternMatch) {
280281 this.exactBugPatternMatch = exactBugPatternMatch;
281282 }
282283
283 /**
284 * @return Returns the exactBugPatternMatch.
285 */
286284 public boolean isExactBugPatternMatch() {
287285 return exactBugPatternMatch;
288286 }
289287 }
290
291 // vim:ts=4
1818
1919 package edu.umd.cs.findbugs;
2020
21 import java.io.Serializable;
2122 import java.util.Comparator;
2223
2324 import edu.umd.cs.findbugs.model.ClassNameRewriter;
2526 /**
2627 * @author David Hovemeyer
2728 */
28 public interface WarningComparator extends Comparator<BugInstance> {
29 public interface WarningComparator extends Comparator<BugInstance>, Serializable {
2930
30 /**
31 * @param classNameRewriter
32 * The classNameRewriter to set.
33 */
3431 public abstract void setClassNameRewriter(ClassNameRewriter classNameRewriter);
3532
36 /*
37 * (non-Javadoc)
38 *
39 * @see java.util.Comparator#compare(T, T)
40 */
33 @Override
4134 public abstract int compare(BugInstance lhs, BugInstance rhs);
4235
4336 }
1212
1313 public WarningSuppressor(String bugPattern) {
1414 this.bugPattern = bugPattern;
15 if (DEBUG)
15 if (DEBUG) {
1616 System.out.println("Suppressing " + bugPattern);
17 }
1718 }
1819
20 @Override
1921 public boolean match(BugInstance bugInstance) {
2022
2123 if (DEBUG) {
2628 }
2729 if (!(bugPattern == null || bugInstance.getType().startsWith(bugPattern)
2830 || bugInstance.getBugPattern().getCategory().equalsIgnoreCase(bugPattern) || bugInstance.getBugPattern()
29 .getAbbrev().equalsIgnoreCase(bugPattern)))
31 .getAbbrev().equalsIgnoreCase(bugPattern))) {
3032 return false;
31 if (DEBUG)
33 }
34 if (DEBUG) {
3235 System.out.println(" pattern matches");
36 }
3337 return true;
3438 }
3539
40 @Override
3641 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
3742 // no-op; these aren't saved to XML
3843 }
4242
4343 final private Project project;
4444
45 private Document document;
46
47 private Element root;
45 private final Document document;
46
47 private final Element root;
4848
4949 private static final String ROOT_ELEMENT_NAME = "BugCollection";
5050
7171
7272 }
7373
74 @Override
7475 public void observeClass(ClassDescriptor classDescriptor) {
7576 }
7677
107108 }
108109 }
109110
111 @Override
110112 public void finish() {
111113
112114 try {
214216 * }
215217 */
216218
219 @Override
217220 public @Nonnull
218221 BugCollection getBugCollection() {
219222 return bugCollection;
221224
222225 }
223226
224 // vim:ts=3
3939 public void finish() {
4040 try {
4141 Project project = getProject();
42 if (project == null)
42 if (project == null) {
4343 throw new NullPointerException("No project");
44 }
4445 getBugCollection().bugsPopulated();
4546 getBugCollection().writeXML(outputStream);
4647 outputStream.close();
6061
6162 }
6263
63 // vim:ts=4
2424
2525 /**
2626 * Write an object to XMLOutput while optionally adding descriptive messages.
27 *
27 *
2828 * @author David Hovemeyer
2929 */
3030 public interface XMLWriteableWithMessages extends XMLWriteable {
2929 /**
3030 * The annotated element might be null, and uses of the element should check for
3131 * null.
32 *
32 *
3333 * When this annotation is applied to a method it applies to the method return
3434 * value.
35 *
36 * @deprecated - use {@link javax.annotation.CheckForNull} instead.
35 *
36 * @deprecated - use {@link javax.annotation.CheckForNull} instead.
3737 **/
3838 @Documented
3939 @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE })
2626 /**
2727 * This annotation is used to denote a method whose return value should always
2828 * be checked when invoking the method.
29 *
29 *
3030 * The checker treats this annotation as inherited by overriding methods.
31 *
32 * @deprecated - use {@link javax.annotation.CheckReturnValue} instead.
31 *
32 * @deprecated - use {@link javax.annotation.CheckReturnValue} instead.
3333 */
3434 @Documented
3535 @Target({ ElementType.METHOD, ElementType.CONSTRUCTOR })
2626
2727 /**
2828 * Mark a class or interface as a resource type requiring cleanup.
29 *
29 *
3030 * @author David Hovemeyer
3131 */
3232 @Documented
2828 * Mark a constructor or method as creating a resource which requires cleanup.
2929 * The marked method must be a member of a class marked with the
3030 * CleanupObligation annotation.
31 *
31 *
3232 * @author David Hovemeyer
3333 */
3434 @Documented
3232 /**
3333 * Indicates that all members of the class or package should be annotated with
3434 * the default value of the supplied annotation class.
35 *
35 *
3636 * This would be used for behavior annotations such as @NonNull, @CheckForNull,
3737 * or @CheckReturnValue.
38 *
38 *
3939 * In particular, you can use @DefaultAnnotation(NonNull.class) on a class or
4040 * package, and then use @Nullable only on those parameters, methods or fields
4141 * that you want to allow to be null.
42 *
42 *
4343 * @deprecated - Use the JSR305 annotations instead.
4444 * For example, you can use {@link javax.annotation.ParametersAreNonnullByDefault} instead
4545 * of @DefaultAnnotation(NonNull.class) so that method parameters are nonnull by default in the annotated
5050 * {@link TypeQualifierDefault}({@link ElementType#PARAMETER})
5151 * public @interface ParametersAreNonnegativeByDefault {}
5252 * </code></pre>
53 *
53 *
5454 * <p>The JSR305 {@link javax.annotation.CheckReturnValue}
5555 * annotation can be applied to a type or package, and it will act as a default for all methods
5656 * in that class or package unless otherwise overridden.
57 *
57 *
5858 * @author William Pugh
5959 */
6060
6767
6868 @Deprecated
6969 Priority priority() default Priority.MEDIUM;
70
70
7171 Confidence confidence() default Confidence.MEDIUM;
7272 }
2828 /**
2929 * Indicates that all members of the class or package should be annotated with
3030 * the default value of the supplied annotation class.
31 *
31 *
3232 * This would be used for behavior annotations such as @NonNull, @CheckForNull,
3333 * or @CheckReturnValue.
34 *
34 *
3535 * In particular, you can use @DefaultAnnotation(NonNull.class) on a class or
3636 * package, and then use @Nullable only on those parameters, methods or fields
3737 * that you want to allow to be null.
38 *
38 *
3939 * @author William Pugh
4040 */
4141
4848
4949 @Deprecated
5050 Priority priority() default Priority.MEDIUM;
51
51
5252 Confidence confidence() default Confidence.MEDIUM;
5353 }
2828 /**
2929 * Indicates that all members of the class or package should be annotated with
3030 * the default value of the supplied annotation class.
31 *
31 *
3232 * This would be used for behavior annotations such as @NonNull, @CheckForNull,
3333 * or @CheckReturnValue.
34 *
34 *
3535 * In particular, you can use @DefaultAnnotation(NonNull.class) on a class or
3636 * package, and then use @Nullable only on those parameters, methods or fields
3737 * that you want to allow to be null.
38 *
38 *
3939 * @author William Pugh
4040 */
4141
4848
4949 @Deprecated
5050 Priority priority() default Priority.MEDIUM;
51
51
5252 Confidence confidence() default Confidence.MEDIUM;
5353 }
2828 /**
2929 * Indicates that all members of the class or package should be annotated with
3030 * the default value of the supplied annotation class.
31 *
31 *
3232 * This would be used for behavior annotations such as @NonNull, @CheckForNull,
3333 * or @CheckReturnValue.
34 *
34 *
3535 * In particular, you can use @DefaultAnnotation(NonNull.class) on a class or
3636 * package, and then use @Nullable only on those parameters, methods or fields
3737 * that you want to allow to be null.
38 *
38 *
3939 * @deprecated - use the JSR305 annotations instead,
4040 * For example, you can use {@link javax.annotation.ParametersAreNonnullByDefault} instead
4141 * of @DefaultAnnotation(NonNull.class), and {@link javax.annotation.meta.TypeQualifierDefault}
4242 * in general to define a type qualifier default. The JSR305 {@link javax.annotation.CheckReturnValue}
4343 * annotation can be applied to a type or package, and it will act as a default for all methods
4444 * in that class or package unless otherwise overridden.
45 *
45 *
4646 * @author William Pugh
4747 */
4848
5555
5656 @Deprecated
5757 Priority priority() default Priority.MEDIUM;
58
58
5959 Confidence confidence() default Confidence.MEDIUM;
6060 }
88 import java.lang.annotation.RetentionPolicy;
99
1010 /**
11 * Annotation indicating that <em>no</em> FindBugs warning of the specified type
12 * is not desired.
11 * Annotation indicating that <em>no</em> FindBugs warnings of the specified type
12 * is desired.
1313 *
1414 * See http://code.google.com/p/findbugs/wiki/FindbugsTestCases
1515 *
2727 /** Want a warning at this priority or higher */
2828 public Confidence confidence() default Confidence.LOW;
2929
30 /** Expect a warning at least this scary */
30 /** Desire a warning at least this scary */
3131 public int rank() default BugRanker.VISIBLE_RANK_MAX;
3232
33 /** Desire at least this many warnings */
34 public int num() default 1;
3335 }
2727 /**
2828 * Mark a method as cleaning up a resource. The marked method must be a member
2929 * of a class marked with the CleanupObligation annotation.
30 *
30 *
3131 * @author David Hovemeyer
3232 */
3333 @Documented
2828
2929 /**
3030 * The annotated element must not be null.
31 *
31 *
3232 * Annotated Fields must only not be null after construction has completed.
3333 * Annotated methods must have non-null return values.
34 *
35 * @deprecated - use {@link javax.annotation.Nonnull} instead.
34 *
35 * @deprecated - use {@link javax.annotation.Nonnull} instead.
3636 **/
3737 @Documented
3838 @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE })
2828
2929 /**
3030 * The annotated element could be null under some circumstances.
31 *
31 *
3232 * In general, this means developers will have to read the documentation to
3333 * determine when a null value is acceptable and whether it is necessary to
3434 * check for a null value.
35 *
35 *
3636 * When this annotation is applied to a method it applies to the method return
3737 * value.
38 *
39 * @deprecated - use {@link javax.annotation.Nullable} instead.
38 *
39 * @deprecated - use {@link javax.annotation.Nullable} instead.
4040 **/
4141 @Documented
4242 @Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.LOCAL_VARIABLE })
2828 * Used to annotate a method that, if overridden, must (or should) be invoked by
2929 * an invocation on super in the overriding method. Examples of such methods
3030 * include finalize() and clone().
31 *
31 *
3232 * The argument to the method indicates when the super invocation should occur:
3333 * at any time, at the beginning of the overriding method, or at the end of the
3434 * overriding method.
35 *
35 *
3636 * @see edu.umd.cs.findbugs.annotations.When
37 *
37 *
3838 * @deprecated - Use {@link javax.annotation.OverridingMethodsMustInvokeSuper} instead
3939 **/
4040 @Documented
2929 /**
3030 * The annotated element should might be null, and uses of the element should
3131 * check for null.
32 *
32 *
3333 * When this annotation is applied to a method it applies to the method return
3434 * value.
35 *
35 *
3636 * @deprecated - use CheckForNull instead; the name of which more clearly
3737 * indicates that not only could the value be null, but that good
3838 * coding practice requires that the value be checked for null.
3636 * annotation of the corresponding parameter in the superclass applies)
3737 * <li>there is a default annotation applied to a more tightly nested element.
3838 * </ul>
39 *
39 *
4040 */
4141 @Documented
4242 @Nonnull
2323
2424 /**
2525 * Used to suppress FindBugs warnings.
26 *
26 *
2727 * It should be used instead of
2828 * {@link edu.umd.cs.findbugs.annotations.SuppressWarnings} to avoid conflicts with
2929 * {@link java.lang.SuppressWarnings}.
30 *
30 *
3131 */
3232 @Retention(RetentionPolicy.CLASS)
3333 public @interface SuppressFBWarnings {
3434 /**
3535 * The set of FindBugs warnings that are to be suppressed in
3636 * annotated element. The value can be a bug category, kind or pattern.
37 *
37 *
3838 */
3939 String[] value() default {};
4040
2525
2626 /**
2727 * Was used to suppress FindBugs warnings but generates name conflicts with {@link java.lang.SuppressWarnings}.
28 *
29 *
28 *
29 *
3030 * @deprecated - Use {@link SuppressFBWarnings} instead
3131 * @author pugh
3232 */
3838 /**
3939 * The set of FindBugs warnings that are to be suppressed by the compiler in the
4040 * annotated element.
41 *
41 *
4242 */
4343 String[] value() default {};
4444
00 /**
11 * Annotations for FindBugs (mostly deprecated except for {@link edu.umd.cs.findbugs.annotations.SuppressFBWarnings}).
2 *
2 *
33 * This annotations are mostly deprecated and replaced by JSR 305 annotations
44 * defined in javax.annotation. The annotations still actively supported are:
55 * <ul>
1111 * <li> {@link edu.umd.cs.findbugs.annotations.DesireWarning} Warnings we wish to generated
1212 * <li> {@link edu.umd.cs.findbugs.annotations.DesireNoWarning} Warnings we wish to not generate generated
1313 * </ul></ul>
14 *
14 *
1515 * There are another set of annotations used by an experimental detector for unclosed resources:
1616 * <ul>
1717 * <li>{@link edu.umd.cs.findbugs.annotations.CleanupObligation}
1818
1919 package edu.umd.cs.findbugs.asm;
2020
21 import org.objectweb.asm.MethodAdapter;
22 import org.objectweb.asm.commons.EmptyVisitor;
2321
2422 /**
2523 * @author pugh
2624 */
27 public abstract class AbstractFBMethodVisitor extends MethodAdapter implements FBMethodVisitor {
25 public abstract class AbstractFBMethodVisitor extends FBMethodVisitor {
2826
2927 public AbstractFBMethodVisitor() {
30 super(new EmptyVisitor());
28 super();
3129 }
3230
3331 int pc;
3432
33 @Override
3534 public void visitOffset(int offset) {
3635 pc = offset;
3736 }
2727 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
2828 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
2929 import edu.umd.cs.findbugs.classfile.Global;
30 import edu.umd.cs.findbugs.classfile.engine.asm.FindBugsASM;
3031
3132 /**
3233 * Abstract base class to to reduce boilerplate needed for writing ASM-based
3334 * Detectors implemented as ClassNode visitors
34 *
35 *
3536 * @author pugh
3637 */
3738 abstract public class ClassNodeDetector extends ClassNode implements Detector2 {
4142 /**
4243 * Construct a ClassNodeDetector. The bugReporter is passed to the
4344 * constructor and stored in a protected final field.
44 *
45 *
4546 * @param bugReporter
4647 * the BugReporter that bug should be reporter to.
4748 */
4849 public ClassNodeDetector(BugReporter bugReporter) {
50 super(FindBugsASM.ASM_VERSION);
4951 this.bugReporter = bugReporter;
5052 }
5153
54 @Override
5255 public String getDetectorClassName() {
5356 return this.getClass().getName();
5457 }
5558
59 @Override
5660 @SuppressWarnings("rawtypes")
5761 public void visitClass(ClassDescriptor classDescriptor) throws CheckedAnalysisException {
5862
5963 FBClassReader cr = Global.getAnalysisCache().getClassAnalysis(FBClassReader.class, classDescriptor);
60 this.interfaces = new ArrayList();
61 this.innerClasses = new ArrayList();
62 this.fields = new ArrayList();
63 this.methods = new ArrayList();
64 this.interfaces = new ArrayList<>();
65 this.innerClasses = new ArrayList<>();
66 this.fields = new ArrayList<>();
67 this.methods = new ArrayList<>();
6468 cr.accept(this, 0);
6569 }
6670
71 @Override
6772 public void finishPass() {
6873 // do nothing
6974 }
1919 package edu.umd.cs.findbugs.asm;
2020
2121 import org.objectweb.asm.Attribute;
22 import org.objectweb.asm.ClassAdapter;
2322 import org.objectweb.asm.ClassReader;
2423 import org.objectweb.asm.ClassVisitor;
2524 import org.objectweb.asm.Label;
26 import org.objectweb.asm.MethodAdapter;
2725 import org.objectweb.asm.MethodVisitor;
26
27 import edu.umd.cs.findbugs.classfile.engine.asm.FindBugsASM;
2828
2929 public class FBClassReader extends ClassReader {
3030
5555 return labels[offset];
5656 }
5757
58 private static class MyClassAdapter extends ClassAdapter {
58 private static class MyClassAdapter extends ClassVisitor {
5959
6060 public MyClassAdapter(ClassVisitor cv) {
61 super(cv);
61 super(FindBugsASM.ASM_VERSION, cv);
6262 }
6363
6464 @Override
7272 }
7373 }
7474
75 private static class MyMethodAdapter extends MethodAdapter {
75 private static class MyMethodAdapter extends MethodVisitor {
7676
7777 public MyMethodAdapter(FBMethodVisitor mv) {
78 super(mv);
78 super(FindBugsASM.ASM_VERSION, mv);
7979 }
8080
8181 @Override
2020
2121 import org.objectweb.asm.MethodVisitor;
2222
23 public interface FBMethodVisitor extends MethodVisitor {
24 void visitOffset(int offset);
23 import edu.umd.cs.findbugs.classfile.engine.asm.FindBugsASM;
24
25 public abstract class FBMethodVisitor extends MethodVisitor {
26
27 public FBMethodVisitor() {
28 super(FindBugsASM.ASM_VERSION);
29 }
30
31 public FBMethodVisitor(MethodVisitor mv) {
32 super(FindBugsASM.ASM_VERSION, mv);
33 }
34
35 public abstract void visitOffset(int offset);
2536 }
2727 * Abstract base class for BlockOrder variants. It allows the subclass to
2828 * specify just a Comparator for BasicBlocks, and handles the work of doing the
2929 * sorting and providing Iterators.
30 *
30 *
3131 * @see BlockOrder
3232 */
3333 public abstract class AbstractBlockOrder implements BlockOrder {
4949
5050 // Put the ordered blocks into an array list
5151 blockList = new ArrayList<BasicBlock>(numBlocks);
52 for (int i = 0; i < numBlocks; ++i)
52 for (int i = 0; i < numBlocks; ++i) {
5353 blockList.add(blocks[i]);
54 }
5455 }
5556
57 @Override
5658 public Iterator<BasicBlock> blockIterator() {
5759 return blockList.iterator();
5860 }
59
61
62 @Override
6063 public int compare(BasicBlock b1, BasicBlock b2) {
6164 return comparator.compare(b1, b2);
6265 }
6366 }
6467
65 // vim:ts=4
3737
3838 private int cachedHashCode = 0;
3939
40 static int slashCountClass = 0;
41
42 static int dottedCountClass = 0;
43
44 static int slashCountSignature = 0;
45
46 static int dottedCountSignature = 0;
40 // static int slashCountClass = 0;
41 // static int dottedCountClass = 0;
42 // static int slashCountSignature = 0;
43 // static int dottedCountSignature = 0;
4744
4845 protected AbstractClassMember(@DottedClassName String className, String name, String signature, int accessFlags) {
4946 if (className.indexOf('.') >= 0) {
5047 // className = className.replace('.','/');
51 dottedCountClass++;
48 // dottedCountClass++;
5249 } else if (className.indexOf('/') >= 0) {
5350 assert false;
54 slashCountClass++;
51 // slashCountClass++;
5552 className = className.replace('/', '.');
5653 }
54
5755 if (signature.indexOf('.') >= 0) {
5856 assert false;
5957 signature = signature.replace('.', '/');
60 dottedCountSignature++;
61 } else if (signature.indexOf('/') >= 0)
62 slashCountSignature++;
58 // dottedCountSignature++;
59 }
60 // else if (signature.indexOf('/') >= 0) {
61 // slashCountSignature++;
62 // }
6363 this.className = DescriptorFactory.canonicalizeString(className);
6464 this.name = DescriptorFactory.canonicalizeString(name);
6565 this.signature = DescriptorFactory.canonicalizeString(signature);
6666 this.accessFlags = accessFlags;
6767 }
6868
69 public @DottedClassName
70 String getClassName() {
69 @Override
70 @DottedClassName
71 public String getClassName() {
7172 return className;
7273 }
7374
74 /*
75 * (non-Javadoc)
76 *
77 * @see edu.umd.cs.findbugs.ba.AccessibleEntity#getClassDescriptor()
78 */
75 @Override
7976 public ClassDescriptor getClassDescriptor() {
8077 return DescriptorFactory.instance().getClassDescriptorForDottedClassName(className);
8178 }
8279
80 @Override
8381 public String getName() {
8482 return name;
8583 }
8684
87 public @DottedClassName
88 String getPackageName() {
85 @Override
86 @DottedClassName
87 public String getPackageName() {
8988 int lastDot = className.lastIndexOf('.');
90 if (lastDot == -1)
89 if (lastDot == -1) {
9190 return className;
91 }
9292 return className.substring(0, lastDot);
9393 }
9494
95 @Override
9596 public String getSignature() {
9697 return signature;
9798 }
100101 return signature.startsWith("L") || signature.startsWith("[");
101102 }
102103
104 @Override
103105 public int getAccessFlags() {
104106 return accessFlags;
105107 }
106108
109 @Override
107110 public boolean isStatic() {
108111 return (accessFlags & Constants.ACC_STATIC) != 0;
109112 }
110113
114 @Override
111115 public boolean isFinal() {
112116 return (accessFlags & Constants.ACC_FINAL) != 0;
113117 }
114118
119 @Override
115120 public boolean isPublic() {
116121 return (accessFlags & Constants.ACC_PUBLIC) != 0;
117122 }
118123
124 @Override
119125 public boolean isProtected() {
120126 return (accessFlags & Constants.ACC_PROTECTED) != 0;
121127 }
122128
129 @Override
123130 public boolean isPrivate() {
124131 return (accessFlags & Constants.ACC_PRIVATE) != 0;
125132 }
157164 // public int compareTo(Object other) {
158165 // return compareTo((FieldOrMethodName) other);
159166 // }
167 @Override
160168 public boolean isResolved() {
161169 return resolved;
162170 }
175183
176184 @Override
177185 public boolean equals(Object o) {
178 if (o == null || this.getClass() != o.getClass())
186 if (o == null || this.getClass() != o.getClass()) {
179187 return false;
188 }
180189 AbstractClassMember other = (AbstractClassMember) o;
181190 return className.equals(other.className) && name.equals(other.name) && signature.equals(other.signature);
182191 }
2323 * BasicAbstractDataflowAnalysis subtype. The main functionality is offering
2424 * getFact{At,After}Location() methods which forward to the actual analysis
2525 * object.
26 *
26 *
2727 * @see edu.umd.cs.findbugs.ba.Dataflow
2828 * @see edu.umd.cs.findbugs.ba.BasicAbstractDataflowAnalysis
2929 * @author David Hovemeyer
3030 */
3131 public class AbstractDataflow<Fact, AnalysisType extends BasicAbstractDataflowAnalysis<Fact>> extends
32 Dataflow<Fact, AnalysisType> {
32 Dataflow<Fact, AnalysisType> {
3333
3434 /**
3535 * Constructor.
36 *
36 *
3737 * @param cfg
3838 * CFG of the method on which dfa is performed
3939 * @param analysis
4545
4646 /**
4747 * Get the fact that is true on the given control edge.
48 *
48 *
4949 * @param edge
5050 * the edge
5151 * @return the fact that is true on the edge
3131 * Abstract base class providing functionality that will be useful for most
3232 * dataflow analysis implementations that model instructions within basic
3333 * blocks.
34 *
34 *
3535 * @author David Hovemeyer
3636 * @see Dataflow
3737 * @see DataflowAnalysis
4747
4848 /**
4949 * Transfer function for a single instruction.
50 *
50 *
5151 * @param handle
5252 * the instruction
5353 * @param basicBlock
7070 * Get the dataflow fact representing the point just before given Location.
7171 * Note "before" is meant in the logical sense, so for backward analyses,
7272 * before means after the location in the control flow sense.
73 *
73 *
7474 * @param location
7575 * the location
7676 * @return the fact at the point just before the location
8888 * Get the dataflow fact representing the point just after given Location.
8989 * Note "after" is meant in the logical sense, so for backward analyses,
9090 * after means before the location in the control flow sense.
91 *
91 *
9292 * @param location
9393 * the location
9494 * @return the fact at the point just after the location
9898 BasicBlock basicBlock = location.getBasicBlock();
9999 InstructionHandle handle = location.getHandle();
100100
101 if (handle == (isForwards() ? basicBlock.getLastInstruction() : basicBlock.getFirstInstruction()))
101 if (handle == (isForwards() ? basicBlock.getLastInstruction() : basicBlock.getFirstInstruction())) {
102102 return getResultFact(basicBlock);
103 else
103 } else {
104104 return getFactAtLocation(new Location(isForwards() ? handle.getNext() : handle.getPrev(), basicBlock));
105 }
105106 }
106107
107108 /*
110111 * ----------------------------------------------------------------------
111112 */
112113
114 @Override
113115 public void transfer(BasicBlock basicBlock, @CheckForNull InstructionHandle end, Fact start, Fact result)
114116 throws DataflowAnalysisException {
115117 copy(start, result);
120122
121123 while (i.hasNext()) {
122124 InstructionHandle handle = i.next();
123 if (handle == end)
125 if (handle == end) {
124126 break;
127 }
125128
126 if (DEBUG && end == null)
129 if (DEBUG && end == null) {
127130 System.out.print("Transfer " + handle);
131 }
128132
129133 // Transfer the dataflow value
130134 transferInstruction(handle, basicBlock, result);
131135
132 if (DEBUG && end == null)
136 if (DEBUG && end == null) {
133137 System.out.println(" ==> " + result.toString());
138 }
134139 }
135140 }
136141 }
137142
138143 }
139144
140 // vim:ts=4
4040 * An EdgeChooser may be specified to select which edges to take into account.
4141 * For example, exception edges could be ignored.
4242 * </p>
43 *
43 *
4444 * @author David Hovemeyer
4545 * @see DataflowAnalysis
4646 * @see CFG
4949 public abstract class AbstractDominatorsAnalysis extends BasicAbstractDataflowAnalysis<BitSet> {
5050 private final CFG cfg;
5151
52 private EdgeChooser edgeChooser;
52 private final EdgeChooser edgeChooser;
5353
5454 /**
5555 * Constructor.
56 *
56 *
5757 * @param cfg
5858 * the CFG to compute dominator relationships for
5959 * @param ignoreExceptionEdges
6161 */
6262 public AbstractDominatorsAnalysis(CFG cfg, final boolean ignoreExceptionEdges) {
6363 this(cfg, new EdgeChooser() {
64 @Override
6465 public boolean choose(Edge edge) {
65 if (ignoreExceptionEdges && edge.isExceptionEdge())
66 if (ignoreExceptionEdges && edge.isExceptionEdge()) {
6667 return false;
67 else
68 } else {
6869 return true;
70 }
6971 }
7072 });
7173 }
7274
7375 /**
7476 * Constructor.
75 *
77 *
7678 * @param cfg
7779 * the CFG to compute dominator relationships for
7880 * @param edgeChooser
8385 this.edgeChooser = edgeChooser;
8486 }
8587
88 @Override
8689 public BitSet createFact() {
8790 return new BitSet();
8891 }
8992
93 @Override
9094 public void copy(BitSet source, BitSet dest) {
9195 dest.clear();
9296 dest.or(source);
9397 }
9498
99 @Override
95100 public void initEntryFact(BitSet result) {
96101 // No blocks dominate the entry block
97102 result.clear();
98103 }
99104
105 @Override
100106 public boolean isTop(BitSet fact) {
101107 // We represent TOP as a bitset with an illegal bit set
102108 return fact.get(cfg.getNumBasicBlocks());
103109 }
104110
111 @Override
105112 public void makeFactTop(BitSet fact) {
106113 // We represent TOP as a bitset with an illegal bit set
107114 fact.set(cfg.getNumBasicBlocks());
108115 }
109116
117 @Override
110118 public boolean same(BitSet fact1, BitSet fact2) {
111119 return fact1.equals(fact2);
112120 }
113121
122 @Override
114123 public void transfer(BasicBlock basicBlock, @CheckForNull InstructionHandle end, BitSet start, BitSet result)
115124 throws DataflowAnalysisException {
116125 // Start with intersection of dominators of predecessors
122131 }
123132 }
124133
134 @Override
125135 public void meetInto(BitSet fact, Edge edge, BitSet result) throws DataflowAnalysisException {
126 if (!edgeChooser.choose(edge))
136 if (!edgeChooser.choose(edge)) {
127137 return;
138 }
128139
129 if (isTop(fact))
140 if (isTop(fact)) {
130141 return;
131 else if (isTop(result))
142 } else if (isTop(result)) {
132143 copy(fact, result);
133 else
144 } else {
134145 // Meet is intersection
135146 result.and(fact);
147 }
136148 }
137149
138150 /**
139151 * Get a bitset containing the unique IDs of all blocks which dominate (or
140152 * postdominate) the given block.
141 *
153 *
142154 * @param block
143155 * a BasicBlock
144156 * @return BitSet of the unique IDs of all blocks that dominate (or
151163 /**
152164 * Get a bitset containing the unique IDs of all blocks in CFG dominated (or
153165 * postdominated, depending on how the analysis was done) by given block.
154 *
166 *
155167 * @param dominator
156168 * we want to get all blocks dominated (or postdominated) by this
157169 * block
162174 for (Iterator<BasicBlock> i = cfg.blockIterator(); i.hasNext();) {
163175 BasicBlock block = i.next();
164176 BitSet dominators = getResultFact(block);
165 if (dominators.get(dominator.getLabel()))
177 if (dominators.get(dominator.getLabel())) {
166178 allDominated.set(block.getLabel());
179 }
167180 }
168181 return allDominated;
169182 }
170183
171184 }
172185
173 // vim:ts=4
3030 super(className, fieldName, fieldSig, accessFlags);
3131 }
3232
33 @Override
3334 public boolean isVolatile() {
3435 return (getAccessFlags() & Constants.ACC_VOLATILE) != 0;
3536 }
3637
38 @Override
3739 public final boolean isSynthetic() {
3840 return (getAccessFlags() & Constants.ACC_SYNTHETIC) != 0;
3941 }
4042
4143 /*
4244 * (non-Javadoc)
43 *
45 *
4446 * @see edu.umd.cs.findbugs.ba.XField#getFieldDescriptor()
4547 */
48 @Override
4649 public FieldDescriptor getFieldDescriptor() {
4750 return DescriptorFactory.instance().getFieldDescriptor(getClassDescriptor().getClassName(), getName(), getSignature(),
4851 isStatic());
4952 }
5053 }
5154
52 // vim:ts=4
4949 * @see Frame
5050 * @see DataflowAnalysis
5151 */
52 public abstract class AbstractFrameModelingVisitor<Value, FrameType extends Frame<Value>> implements Visitor {
52 public abstract class AbstractFrameModelingVisitor<Value, FrameType extends Frame<Value>> implements VisitorSupportsInvokeDynamic {
5353 private FrameType frame;
5454
5555 private Location location;
7878 * analyzed is invalid
7979 */
8080 public void analyzeInstruction(Instruction ins) throws DataflowAnalysisException {
81 if (frame.isValid())
81 if (frame.isValid()) {
8282 try {
8383 ins.accept(this);
8484 } catch (InvalidBytecodeException e) {
8585 String message = "Invalid bytecode: could not analyze instr. " + ins + " at frame " + frame;
8686 throw new DataflowAnalysisException(message, e);
8787 }
88 }
8889 }
8990
9091 /**
137138 */
138139 public int getNumWordsConsumed(Instruction ins) {
139140 int numWordsConsumed = ins.consumeStack(cpg);
140 if (numWordsConsumed == Constants.UNPREDICTABLE)
141 if (numWordsConsumed == Constants.UNPREDICTABLE) {
141142 throw new InvalidBytecodeException("Unpredictable stack consumption");
143 }
142144 return numWordsConsumed;
143145 }
144146
148150 public int getNumWordsProduced(Instruction ins) {
149151
150152 int numWordsProduced = ins.produceStack(cpg);
151 if (numWordsProduced == Constants.UNPREDICTABLE)
153 if (numWordsProduced == Constants.UNPREDICTABLE) {
152154 throw new InvalidBytecodeException("Unpredictable stack productions");
155 }
153156 return numWordsProduced;
154157 }
155158
168171 * ----------------------------------------------------------------------
169172 */
170173
174 @Override
171175 public void visitStackInstruction(StackInstruction obj) {
172176 }
173177
178 @Override
174179 public void visitLocalVariableInstruction(LocalVariableInstruction obj) {
175180 }
176181
182 @Override
177183 public void visitBranchInstruction(BranchInstruction obj) {
178184 }
179185
186 @Override
180187 public void visitLoadClass(LoadClass obj) {
181188 }
182189
190 @Override
183191 public void visitFieldInstruction(FieldInstruction obj) {
184192 }
185193
194 @Override
186195 public void visitIfInstruction(IfInstruction obj) {
187196 }
188197
189198 /** To allow for calls to visitNULL2Z and visitNONNULL2Z, this method is made final.
190199 * If you want to override it, override visitConversionInstruction2 instead.
191200 */
201 @Override
192202 public final void visitConversionInstruction(ConversionInstruction obj) {
193203 visitConversionInstruction2(obj);
194 if (obj instanceof NULL2Z)
204 if (obj instanceof NULL2Z) {
195205 visitNULL2Z((NULL2Z) obj);
196 else if (obj instanceof NONNULL2Z)
206 } else if (obj instanceof NONNULL2Z) {
197207 visitNONNULL2Z((NONNULL2Z) obj);
208 }
198209 }
199210
200211 public final void visitConversionInstruction2(ConversionInstruction obj) {
201212
202213 }
203214
215 @Override
204216 public void visitPopInstruction(PopInstruction obj) {
205217 }
206218
219 @Override
207220 public void visitJsrInstruction(JsrInstruction obj) {
208221 }
209222
223 @Override
210224 public void visitGotoInstruction(GotoInstruction obj) {
211225 }
212226
227 @Override
213228 public void visitStoreInstruction(StoreInstruction obj) {
214229 }
215230
231 @Override
216232 public void visitTypedInstruction(TypedInstruction obj) {
217233 }
218234
235 @Override
219236 public void visitSelect(Select obj) {
220237 }
221238
239 @Override
222240 public void visitUnconditionalBranch(UnconditionalBranch obj) {
223241 }
224242
243 @Override
225244 public void visitPushInstruction(PushInstruction obj) {
226245 }
227246
247 @Override
228248 public void visitArithmeticInstruction(ArithmeticInstruction obj) {
229249 }
230250
251 @Override
231252 public void visitCPInstruction(CPInstruction obj) {
232253 }
233254
255 @Override
234256 public void visitInvokeInstruction(InvokeInstruction obj) {
235257 }
236258
259 @Override
237260 public void visitArrayInstruction(ArrayInstruction obj) {
238261 }
239262
263 @Override
240264 public void visitAllocationInstruction(AllocationInstruction obj) {
241265 }
242266
267 @Override
243268 public void visitReturnInstruction(ReturnInstruction obj) {
244269 }
245270
271 @Override
246272 public void visitFieldOrMethod(FieldOrMethod obj) {
247273 }
248274
275 @Override
249276 public void visitConstantPushInstruction(ConstantPushInstruction obj) {
250277 }
251278
279 @Override
252280 public void visitExceptionThrower(ExceptionThrower obj) {
253281 }
254282
283 @Override
255284 public void visitLoadInstruction(LoadInstruction obj) {
256285 }
257286
287 @Override
258288 public void visitVariableLengthInstruction(VariableLengthInstruction obj) {
259289 }
260290
291 @Override
261292 public void visitStackProducer(StackProducer obj) {
262293 }
263294
295 @Override
264296 public void visitStackConsumer(StackConsumer obj) {
265297 }
266298
278310 public void handleStoreInstruction(StoreInstruction obj) {
279311 try {
280312 int numConsumed = obj.consumeStack(cpg);
281 if (numConsumed == Constants.UNPREDICTABLE)
313 if (numConsumed == Constants.UNPREDICTABLE) {
282314 throw new InvalidBytecodeException("Unpredictable stack consumption");
315 }
283316
284317 int index = obj.getIndex();
285318
301334 */
302335 public void handleLoadInstruction(LoadInstruction obj) {
303336 int numProduced = obj.produceStack(cpg);
304 if (numProduced == Constants.UNPREDICTABLE)
337 if (numProduced == Constants.UNPREDICTABLE) {
305338 throw new InvalidBytecodeException("Unpredictable stack production");
339 }
306340
307341 int index = obj.getIndex() + numProduced;
308342
363397 }
364398 }
365399 try {
366 while (numWordsConsumed-- > 0)
400 while (numWordsConsumed-- > 0) {
367401 frame.popValue();
402 }
368403 } catch (DataflowAnalysisException e) {
369404 throw new InvalidBytecodeException("Not enough values on the stack", e);
370405 }
371406
372 while (numWordsProduced-- > 0)
407 while (numWordsProduced-- > 0) {
373408 frame.pushValue(pushValue);
409 }
374410 }
375411
376412 /*
379415 * ----------------------------------------------------------------------
380416 */
381417
418 @Override
382419 public void visitASTORE(ASTORE obj) {
383420 handleStoreInstruction(obj);
384421 }
385422
423 @Override
386424 public void visitDSTORE(DSTORE obj) {
387425 handleStoreInstruction(obj);
388426 }
389427
428 @Override
390429 public void visitFSTORE(FSTORE obj) {
391430 handleStoreInstruction(obj);
392431 }
393432
433 @Override
394434 public void visitISTORE(ISTORE obj) {
395435 handleStoreInstruction(obj);
396436 }
397437
438 @Override
398439 public void visitLSTORE(LSTORE obj) {
399440 handleStoreInstruction(obj);
400441 }
405446 * ----------------------------------------------------------------------
406447 */
407448
449 @Override
408450 public void visitALOAD(ALOAD obj) {
409451 handleLoadInstruction(obj);
410452 }
411453
454 @Override
412455 public void visitDLOAD(DLOAD obj) {
413456 handleLoadInstruction(obj);
414457 }
415458
459 @Override
416460 public void visitFLOAD(FLOAD obj) {
417461 handleLoadInstruction(obj);
418462 }
419463
464 @Override
420465 public void visitILOAD(ILOAD obj) {
421466 handleLoadInstruction(obj);
422467 }
423468
469 @Override
424470 public void visitLLOAD(LLOAD obj) {
425471 handleLoadInstruction(obj);
426472 }
431477 * ----------------------------------------------------------------------
432478 */
433479
480 @Override
434481 public void visitPOP(POP obj) {
435482 handleNormalInstruction(obj);
436483 }
437484
485 @Override
438486 public void visitPOP2(POP2 obj) {
439487 handleNormalInstruction(obj);
440488 }
441489
490 @Override
442491 public void visitDUP(DUP obj) {
443492 try {
444493 Value value = frame.popValue();
449498 }
450499 }
451500
501 @Override
452502 public void visitDUP_X1(DUP_X1 obj) {
453503 try {
454504 Value value1 = frame.popValue();
461511 }
462512 }
463513
514 @Override
464515 public void visitDUP_X2(DUP_X2 obj) {
465516 try {
466517 Value value1 = frame.popValue();
475526 }
476527 }
477528
529 @Override
478530 public void visitDUP2(DUP2 obj) {
479531 try {
480532 Value value1 = frame.popValue();
488540 }
489541 }
490542
543 @Override
491544 public void visitDUP2_X1(DUP2_X1 obj) {
492545 try {
493546 Value value1 = frame.popValue();
503556 }
504557 }
505558
559 @Override
506560 public void visitDUP2_X2(DUP2_X2 obj) {
507561 try {
508562 Value value1 = frame.popValue();
520574 }
521575 }
522576
577 @Override
523578 public void visitSWAP(SWAP obj) {
524579 try {
525580 Value value1 = frame.popValue();
537592 * ----------------------------------------------------------------------
538593 */
539594
595 @Override
540596 public void visitIMPDEP1(IMPDEP1 obj) {
541597 illegalBytecode(obj);
542598 }
543599
600 @Override
544601 public void visitIMPDEP2(IMPDEP2 obj) {
545602 illegalBytecode(obj);
546603 }
547604
605 @Override
548606 public void visitBREAKPOINT(BREAKPOINT obj) {
549607 illegalBytecode(obj);
550608 }
555613 * ----------------------------------------------------------------------
556614 */
557615
616 @Override
558617 public void visitACONST_NULL(ACONST_NULL obj) {
559618 handleNormalInstruction(obj);
560619 }
561620
621 @Override
562622 public void visitGETSTATIC(GETSTATIC obj) {
563623 handleNormalInstruction(obj);
564624 }
565625
626 @Override
566627 public void visitIF_ICMPLT(IF_ICMPLT obj) {
567628 handleNormalInstruction(obj);
568629 }
569630
631 @Override
570632 public void visitMONITOREXIT(MONITOREXIT obj) {
571633 handleNormalInstruction(obj);
572634 }
573635
636 @Override
574637 public void visitIFLT(IFLT obj) {
575638 handleNormalInstruction(obj);
576639 }
577640
641 @Override
578642 public void visitBASTORE(BASTORE obj) {
579643 handleNormalInstruction(obj);
580644 }
581645
646 @Override
582647 public void visitCHECKCAST(CHECKCAST obj) {
583648 handleNormalInstruction(obj);
584649 }
585650
651 @Override
586652 public void visitFCMPG(FCMPG obj) {
587653 handleNormalInstruction(obj);
588654 }
589655
656 @Override
590657 public void visitI2F(I2F obj) {
591658 handleNormalInstruction(obj);
592659 }
593660
661 @Override
594662 public void visitATHROW(ATHROW obj) {
595663 handleNormalInstruction(obj);
596664 }
597665
666 @Override
598667 public void visitDCMPL(DCMPL obj) {
599668 handleNormalInstruction(obj);
600669 }
601670
671 @Override
602672 public void visitARRAYLENGTH(ARRAYLENGTH obj) {
603673 handleNormalInstruction(obj);
604674 }
605675
676 @Override
606677 public void visitINVOKESTATIC(INVOKESTATIC obj) {
607678 handleNormalInstruction(obj);
608679 }
609680
681 @Override
610682 public void visitLCONST(LCONST obj) {
611683 handleNormalInstruction(obj);
612684 }
613685
686 @Override
614687 public void visitDREM(DREM obj) {
615688 handleNormalInstruction(obj);
616689 }
617690
691 @Override
618692 public void visitIFGE(IFGE obj) {
619693 handleNormalInstruction(obj);
620694 }
621695
696 @Override
622697 public void visitCALOAD(CALOAD obj) {
623698 handleNormalInstruction(obj);
624699 }
625700
701 @Override
626702 public void visitLASTORE(LASTORE obj) {
627703 handleNormalInstruction(obj);
628704 }
629705
706 @Override
630707 public void visitI2D(I2D obj) {
631708 handleNormalInstruction(obj);
632709 }
633710
711 @Override
634712 public void visitDADD(DADD obj) {
635713 handleNormalInstruction(obj);
636714 }
637715
716 @Override
638717 public void visitINVOKESPECIAL(INVOKESPECIAL obj) {
639718 handleNormalInstruction(obj);
640719 }
641720
721 @Override
642722 public void visitIAND(IAND obj) {
643723 handleNormalInstruction(obj);
644724 }
645725
726 @Override
646727 public void visitPUTFIELD(PUTFIELD obj) {
647728 handleNormalInstruction(obj);
648729 }
649730
731 @Override
650732 public void visitDCONST(DCONST obj) {
651733 handleNormalInstruction(obj);
652734 }
653735
736 @Override
654737 public void visitNEW(NEW obj) {
655738 handleNormalInstruction(obj);
656739 }
657740
741 @Override
658742 public void visitIFNULL(IFNULL obj) {
659743 handleNormalInstruction(obj);
660744 }
661745
746 @Override
662747 public void visitLSUB(LSUB obj) {
663748 handleNormalInstruction(obj);
664749 }
665750
751 @Override
666752 public void visitL2I(L2I obj) {
667753 handleNormalInstruction(obj);
668754 }
669755
756 @Override
670757 public void visitISHR(ISHR obj) {
671758 handleNormalInstruction(obj);
672759 }
673760
761 @Override
674762 public void visitTABLESWITCH(TABLESWITCH obj) {
675763 handleNormalInstruction(obj);
676764 }
677765
766 @Override
678767 public void visitIINC(IINC obj) {
679768 handleNormalInstruction(obj);
680769 }
681770
771 @Override
682772 public void visitDRETURN(DRETURN obj) {
683773 handleNormalInstruction(obj);
684774 }
685775
776 @Override
686777 public void visitDASTORE(DASTORE obj) {
687778 handleNormalInstruction(obj);
688779 }
689780
781 @Override
690782 public void visitIALOAD(IALOAD obj) {
691783 handleNormalInstruction(obj);
692784 }
693785
786 @Override
694787 public void visitDDIV(DDIV obj) {
695788 handleNormalInstruction(obj);
696789 }
697790
791 @Override
698792 public void visitIF_ICMPGE(IF_ICMPGE obj) {
699793 handleNormalInstruction(obj);
700794 }
701795
796 @Override
702797 public void visitLAND(LAND obj) {
703798 handleNormalInstruction(obj);
704799 }
705800
801 @Override
706802 public void visitIDIV(IDIV obj) {
707803 handleNormalInstruction(obj);
708804 }
709805
806 @Override
710807 public void visitLOR(LOR obj) {
711808 handleNormalInstruction(obj);
712809 }
713810
811 @Override
714812 public void visitCASTORE(CASTORE obj) {
715813 handleNormalInstruction(obj);
716814 }
717815
816 @Override
718817 public void visitFREM(FREM obj) {
719818 handleNormalInstruction(obj);
720819 }
721820
821 @Override
722822 public void visitLDC(LDC obj) {
723823 handleNormalInstruction(obj);
724824 }
725825
826 @Override
726827 public void visitBIPUSH(BIPUSH obj) {
727828 handleNormalInstruction(obj);
728829 }
729830
831 @Override
730832 public void visitF2L(F2L obj) {
731833 handleNormalInstruction(obj);
732834 }
733835
836 @Override
734837 public void visitFMUL(FMUL obj) {
735838 handleNormalInstruction(obj);
736839 }
737840
841 @Override
738842 public void visitJSR(JSR obj) {
739843 handleNormalInstruction(obj);
740844 }
741845
846 @Override
742847 public void visitFSUB(FSUB obj) {
743848 handleNormalInstruction(obj);
744849 }
745850
851 @Override
746852 public void visitSASTORE(SASTORE obj) {
747853 handleNormalInstruction(obj);
748854 }
749855
856 @Override
750857 public void visitRETURN(RETURN obj) {
751858 handleNormalInstruction(obj);
752859 }
753860
861 @Override
754862 public void visitDALOAD(DALOAD obj) {
755863 handleNormalInstruction(obj);
756864 }
757865
866 @Override
758867 public void visitSIPUSH(SIPUSH obj) {
759868 handleNormalInstruction(obj);
760869 }
761870
871 @Override
762872 public void visitDSUB(DSUB obj) {
763873 handleNormalInstruction(obj);
764874 }
765875
876 @Override
766877 public void visitL2F(L2F obj) {
767878 handleNormalInstruction(obj);
768879 }
769880
881 @Override
770882 public void visitIF_ICMPGT(IF_ICMPGT obj) {
771883 handleNormalInstruction(obj);
772884 }
773885
886 @Override
774887 public void visitF2D(F2D obj) {
775888 handleNormalInstruction(obj);
776889 }
777890
891 @Override
778892 public void visitI2L(I2L obj) {
779893 handleNormalInstruction(obj);
780894 }
781895
896 @Override
782897 public void visitIF_ACMPNE(IF_ACMPNE obj) {
783898 handleNormalInstruction(obj);
784899 }
792907 handleNormalInstruction(obj);
793908 }
794909
910 @Override
795911 public void visitI2S(I2S obj) {
796912 handleNormalInstruction(obj);
797913 }
798914
915 @Override
799916 public void visitIFEQ(IFEQ obj) {
800917 handleNormalInstruction(obj);
801918 }
802919
920 @Override
803921 public void visitIOR(IOR obj) {
804922 handleNormalInstruction(obj);
805923 }
806924
925 @Override
807926 public void visitIREM(IREM obj) {
808927 handleNormalInstruction(obj);
809928 }
810929
930 @Override
811931 public void visitIASTORE(IASTORE obj) {
812932 handleNormalInstruction(obj);
813933 }
814934
935 @Override
815936 public void visitNEWARRAY(NEWARRAY obj) {
816937 handleNormalInstruction(obj);
817938 }
818939
940 @Override
819941 public void visitINVOKEINTERFACE(INVOKEINTERFACE obj) {
820942 handleNormalInstruction(obj);
821943 }
822944
945 @Override
823946 public void visitINEG(INEG obj) {
824947 handleNormalInstruction(obj);
825948 }
826949
950 @Override
827951 public void visitLCMP(LCMP obj) {
828952 handleNormalInstruction(obj);
829953 }
830954
955 @Override
831956 public void visitJSR_W(JSR_W obj) {
832957 handleNormalInstruction(obj);
833958 }
834959
960 @Override
835961 public void visitMULTIANEWARRAY(MULTIANEWARRAY obj) {
836962 handleNormalInstruction(obj);
837963 }
838964
965 @Override
839966 public void visitSALOAD(SALOAD obj) {
840967 handleNormalInstruction(obj);
841968 }
842969
970 @Override
843971 public void visitIFNONNULL(IFNONNULL obj) {
844972 handleNormalInstruction(obj);
845973 }
846974
975 @Override
847976 public void visitDMUL(DMUL obj) {
848977 handleNormalInstruction(obj);
849978 }
850979
980 @Override
851981 public void visitIFNE(IFNE obj) {
852982 handleNormalInstruction(obj);
853983 }
854984
985 @Override
855986 public void visitIF_ICMPLE(IF_ICMPLE obj) {
856987 handleNormalInstruction(obj);
857988 }
858989
990 @Override
859991 public void visitLDC2_W(LDC2_W obj) {
860992 handleNormalInstruction(obj);
861993 }
862994
995 @Override
863996 public void visitGETFIELD(GETFIELD obj) {
864997 handleNormalInstruction(obj);
865998 }
866999
1000 @Override
8671001 public void visitLADD(LADD obj) {
8681002 handleNormalInstruction(obj);
8691003 }
8701004
1005 @Override
8711006 public void visitNOP(NOP obj) {
8721007 handleNormalInstruction(obj);
8731008 }
8741009
1010 @Override
8751011 public void visitFALOAD(FALOAD obj) {
8761012 handleNormalInstruction(obj);
8771013 }
8781014
1015 @Override
8791016 public void visitINSTANCEOF(INSTANCEOF obj) {
8801017 handleNormalInstruction(obj);
8811018 }
8821019
1020 @Override
8831021 public void visitIFLE(IFLE obj) {
8841022 handleNormalInstruction(obj);
8851023 }
8861024
1025 @Override
8871026 public void visitLXOR(LXOR obj) {
8881027 handleNormalInstruction(obj);
8891028 }
8901029
1030 @Override
8911031 public void visitLRETURN(LRETURN obj) {
8921032 handleNormalInstruction(obj);
8931033 }
8941034
1035 @Override
8951036 public void visitFCONST(FCONST obj) {
8961037 handleNormalInstruction(obj);
8971038 }
8981039
1040 @Override
8991041 public void visitIUSHR(IUSHR obj) {
9001042 handleNormalInstruction(obj);
9011043 }
9021044
1045 @Override
9031046 public void visitBALOAD(BALOAD obj) {
9041047 handleNormalInstruction(obj);
9051048 }
9061049
1050 @Override
9071051 public void visitIF_ACMPEQ(IF_ACMPEQ obj) {
9081052 handleNormalInstruction(obj);
9091053 }
9101054
1055 @Override
9111056 public void visitMONITORENTER(MONITORENTER obj) {
9121057 handleNormalInstruction(obj);
9131058 }
9141059
1060 @Override
9151061 public void visitLSHL(LSHL obj) {
9161062 handleNormalInstruction(obj);
9171063 }
9181064
1065 @Override
9191066 public void visitDCMPG(DCMPG obj) {
9201067 handleNormalInstruction(obj);
9211068 }
9221069
1070 @Override
9231071 public void visitD2L(D2L obj) {
9241072 handleNormalInstruction(obj);
9251073 }
9261074
1075 @Override
9271076 public void visitL2D(L2D obj) {
9281077 handleNormalInstruction(obj);
9291078 }
9301079
1080 @Override
9311081 public void visitRET(RET obj) {
9321082 handleNormalInstruction(obj);
9331083 }
9341084
1085 @Override
9351086 public void visitIFGT(IFGT obj) {
9361087 handleNormalInstruction(obj);
9371088 }
9381089
1090 @Override
9391091 public void visitIXOR(IXOR obj) {
9401092 handleNormalInstruction(obj);
9411093 }
942
1094 @Override
1095 public void visitINVOKEDYNAMIC(INVOKEDYNAMIC obj) {
1096 handleNormalInstruction(obj);
1097 }
1098
1099 @Override
9431100 public void visitINVOKEVIRTUAL(INVOKEVIRTUAL obj) {
9441101 handleNormalInstruction(obj);
9451102 }
9461103
1104 @Override
9471105 public void visitFASTORE(FASTORE obj) {
9481106 handleNormalInstruction(obj);
9491107 }
9501108
1109 @Override
9511110 public void visitIRETURN(IRETURN obj) {
9521111 handleNormalInstruction(obj);
9531112 }
9541113
1114 @Override
9551115 public void visitIF_ICMPNE(IF_ICMPNE obj) {
9561116 handleNormalInstruction(obj);
9571117 }
9581118
1119 @Override
9591120 public void visitLDIV(LDIV obj) {
9601121 handleNormalInstruction(obj);
9611122 }
9621123
1124 @Override
9631125 public void visitPUTSTATIC(PUTSTATIC obj) {
9641126 handleNormalInstruction(obj);
9651127 }
9661128
1129 @Override
9671130 public void visitAALOAD(AALOAD obj) {
9681131 handleNormalInstruction(obj);
9691132 }
9701133
1134 @Override
9711135 public void visitD2I(D2I obj) {
9721136 handleNormalInstruction(obj);
9731137 }
9741138
1139 @Override
9751140 public void visitIF_ICMPEQ(IF_ICMPEQ obj) {
9761141 handleNormalInstruction(obj);
9771142 }
9781143
1144 @Override
9791145 public void visitAASTORE(AASTORE obj) {
9801146 handleNormalInstruction(obj);
9811147 }
9821148
1149 @Override
9831150 public void visitARETURN(ARETURN obj) {
9841151 handleNormalInstruction(obj);
9851152 }
9861153
1154 @Override
9871155 public void visitFNEG(FNEG obj) {
9881156 handleNormalInstruction(obj);
9891157 }
9901158
1159 @Override
9911160 public void visitGOTO_W(GOTO_W obj) {
9921161 handleNormalInstruction(obj);
9931162 }
9941163
1164 @Override
9951165 public void visitD2F(D2F obj) {
9961166 handleNormalInstruction(obj);
9971167 }
9981168
1169 @Override
9991170 public void visitGOTO(GOTO obj) {
10001171 handleNormalInstruction(obj);
10011172 }
10021173
1174 @Override
10031175 public void visitISUB(ISUB obj) {
10041176 handleNormalInstruction(obj);
10051177 }
10061178
1179 @Override
10071180 public void visitF2I(F2I obj) {
10081181 handleNormalInstruction(obj);
10091182 }
10101183
1184 @Override
10111185 public void visitDNEG(DNEG obj) {
10121186 handleNormalInstruction(obj);
10131187 }
10141188
1189 @Override
10151190 public void visitICONST(ICONST obj) {
10161191 handleNormalInstruction(obj);
10171192 }
10181193
1194 @Override
10191195 public void visitFDIV(FDIV obj) {
10201196 handleNormalInstruction(obj);
10211197 }
10221198
1199 @Override
10231200 public void visitI2B(I2B obj) {
10241201 handleNormalInstruction(obj);
10251202 }
10261203
1204 @Override
10271205 public void visitLNEG(LNEG obj) {
10281206 handleNormalInstruction(obj);
10291207 }
10301208
1209 @Override
10311210 public void visitLREM(LREM obj) {
10321211 handleNormalInstruction(obj);
10331212 }
10341213
1214 @Override
10351215 public void visitIMUL(IMUL obj) {
10361216 handleNormalInstruction(obj);
10371217 }
10381218
1219 @Override
10391220 public void visitIADD(IADD obj) {
10401221 handleNormalInstruction(obj);
10411222 }
10421223
1224 @Override
10431225 public void visitLSHR(LSHR obj) {
10441226 handleNormalInstruction(obj);
10451227 }
10461228
1229 @Override
10471230 public void visitLOOKUPSWITCH(LOOKUPSWITCH obj) {
10481231 handleNormalInstruction(obj);
10491232 }
10501233
1234 @Override
10511235 public void visitFCMPL(FCMPL obj) {
10521236 handleNormalInstruction(obj);
10531237 }
10541238
1239 @Override
10551240 public void visitI2C(I2C obj) {
10561241 handleNormalInstruction(obj);
10571242 }
10581243
1244 @Override
10591245 public void visitLMUL(LMUL obj) {
10601246 handleNormalInstruction(obj);
10611247 }
10621248
1249 @Override
10631250 public void visitLUSHR(LUSHR obj) {
10641251 handleNormalInstruction(obj);
10651252 }
10661253
1254 @Override
10671255 public void visitISHL(ISHL obj) {
10681256 handleNormalInstruction(obj);
10691257 }
10701258
1259 @Override
10711260 public void visitLALOAD(LALOAD obj) {
10721261 handleNormalInstruction(obj);
10731262 }
10741263
1264 @Override
10751265 public void visitANEWARRAY(ANEWARRAY obj) {
10761266 handleNormalInstruction(obj);
10771267 }
10781268
1269 @Override
10791270 public void visitFRETURN(FRETURN obj) {
10801271 handleNormalInstruction(obj);
10811272 }
10821273
1274 @Override
10831275 public void visitFADD(FADD obj) {
10841276 handleNormalInstruction(obj);
10851277 }
10861278 }
10871279
1088 // vim:ts=4
3030 super(className, methodName, methodSig, accessFlags);
3131 }
3232
33 @Override
3334 public int getNumParams() {
3435 // FIXME: cache this?
3536 return new SignatureParser(getSignature()).getNumParameters();
3637 }
3738
39 @Override
3840 public boolean isNative() {
3941 return (getAccessFlags() & Constants.ACC_NATIVE) != 0;
4042 }
4143
44 @Override
4245 public boolean isSynchronized() {
4346 return (getAccessFlags() & Constants.ACC_SYNCHRONIZED) != 0;
47 }
48
49 @Override
50 public boolean isBridge() {
51 return (getAccessFlags() & Constants.ACC_BRIDGE) != 0;
4452 }
4553
4654 @Override
5058
5159 /*
5260 * (non-Javadoc)
53 *
61 *
5462 * @see edu.umd.cs.findbugs.ba.XMethod#getMethodDescriptor()
5563 */
64 @Override
5665 public MethodDescriptor getMethodDescriptor() {
5766 return DescriptorFactory.instance().getMethodDescriptor(ClassName.toSlashedClassName(getClassName()), getName(),
5867 getSignature(), isStatic());
5968 }
60
69
70 @Override
6171 public XMethod resolveAccessMethodForMethod() {
6272 MethodDescriptor access = getAccessMethodForMethod();
63 if (access != null)
73 if (access != null) {
6474 return XFactory.createXMethod(access);
75 }
6576 return this;
6677
6778 }
2323 /**
2424 * Common superinterface for code entities having access flags: classes, fields,
2525 * and methods.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public interface AccessibleEntity {
1717 */
1818
1919 package edu.umd.cs.findbugs.ba;
20 import static java.util.Objects.requireNonNull;
2021
2122 import java.io.File;
2223 import java.io.IOException;
121122 }
122123 };
123124
124
125
126125 /**
127126 * save the original SyntheticRepository so we may obtain JavaClass objects
128127 * which we can reuse. (A URLClassPathRepository gets closed after
129128 * analysis.)
130129 */
131130 private static final org.apache.bcel.util.Repository originalRepository = Repository.getRepository(); // BCEL
132 // SyntheticRepository
131 // SyntheticRepository
133132
134133 /**
135134 * Default maximum number of ClassContext objects to cache. FIXME: need to
136135 * evaluate this parameter. Need to keep stats about accesses.
137136 */
138 private static final int DEFAULT_CACHE_SIZE = 3;
137 // private static final int DEFAULT_CACHE_SIZE = 3;
139138
140139 // Instance fields
141140 private BitSet boolPropertySet;
144143
145144 private String databaseOutputDir;
146145
147 public AnalysisContext() {
146 boolean missingClassWarningsSuppressed;
147
148 private ClassSummary classSummary;
149
150 private ClassDescriptor classBeingAnalyzed;
151
152 private FieldSummary fieldSummary;
153
154 private UnreadFields unreadFields;
155
156 private TypeQualifierNullnessAnnotationDatabase tqNullnessDatabase;
157
158 private final HashSet<MethodDescriptor> skippedDueToInvokeDynamic;
159
160 private final Project project;
161
162 private final EqualsKindSummary equalsKindSummary;
163
164 private final UnreadFieldsData unreadFieldsData;
165
166 private final SuppressionMatcher suppressionMatcher;
167
168 protected final RepositoryLookupFailureCallback lookupFailureCallback;
169
170 private final Map<MethodInfo, MethodInfo> bridgeTo;
171
172 private final Map<MethodInfo, MethodInfo> bridgeFrom;
173
174
175 public AnalysisContext(@Nonnull Project project) {
176 requireNonNull(project);
177 this.project = project;
148178 this.boolPropertySet = new BitSet();
149179 this.lookupFailureCallback = new DelegatingRepositoryLookupFailureCallback();
150
180 skippedDueToInvokeDynamic = new HashSet<>();
181 equalsKindSummary = new EqualsKindSummary();
182 unreadFieldsData = new UnreadFieldsData();
183 suppressionMatcher = new SuppressionMatcher();
184 bridgeTo = new IdentityHashMap<>();
185 bridgeFrom = new IdentityHashMap<>();
151186 }
152187
153188 private void clear() {
156191 databaseOutputDir = null;
157192 }
158193
159
160194 /**
161195 * Get the AnalysisContext associated with this thread
162196 */
168202 return currentXFactory.get();
169203 }
170204
171 ClassSummary classSummary;
172
173 ClassDescriptor classBeingAnalyzed;
174
175205 public ClassDescriptor getClassBeingAnalyzed() {
176206 return classBeingAnalyzed;
177207 }
179209 public void setClassBeingAnalyzed(@Nonnull ClassDescriptor classBeingAnalyzed) {
180210 this.classBeingAnalyzed = classBeingAnalyzed;
181211 }
212
182213 public void clearClassBeingAnalyzed() {
183214 this.classBeingAnalyzed = null;
184215 }
216
185217 public ClassSummary getClassSummary() {
186 if (classSummary == null)
218 if (classSummary == null) {
187219 throw new IllegalStateException("ClassSummary not set");
220 }
188221 return classSummary;
189222 }
190223
191224 public void setClassSummary(@Nonnull ClassSummary classSummary) {
192 if (this.classSummary != null)
225 if (this.classSummary != null) {
193226 throw new IllegalStateException("ClassSummary already set");
227 }
194228 this.classSummary = classSummary;
195229 }
196
197 final EqualsKindSummary equalsKindSummary = new EqualsKindSummary();
198230
199231 public EqualsKindSummary getEqualsKindSummary() {
200232 return equalsKindSummary;
201233 }
202
203 FieldSummary fieldSummary;
204234
205235 public FieldSummary getFieldSummary() {
206236 if (fieldSummary == null) {
217247 this.fieldSummary = fieldSummary;
218248 }
219249
220 final UnreadFieldsData unreadFieldsData = new UnreadFieldsData();
221 UnreadFields unreadFields;
222
223250 public @Nonnull UnreadFieldsData getUnreadFieldsData() {
224251 return unreadFieldsData;
225252 }
226253
227254 public @Nonnull UnreadFields getUnreadFields() {
228 if (unreadFieldsAvailable())
255 if (!unreadFieldsAvailable()) {
229256 throw new IllegalStateException();
257 }
230258 return unreadFields;
231259 }
232260
235263 }
236264
237265 public void setUnreadFields(@Nonnull UnreadFields unreadFields) {
238 if (this.unreadFields != null)
266 if (this.unreadFields != null) {
239267 throw new IllegalStateException("UnreadFields detector already set");
268 }
240269 this.unreadFields = unreadFields;
241270 }
242271
243 private static boolean skipReportingMissingClass(@CheckForNull @DottedClassName String missing) {
272 private static boolean skipReportingMissingClass(@CheckForNull @DottedClassName String missing) {
244273 return missing == null || missing.length() == 0 || missing.charAt(0) == '[' || missing.endsWith("package-info");
245274 }
246275
247276 private static @CheckForNull
248277 RepositoryLookupFailureCallback getCurrentLookupFailureCallback() {
249278 AnalysisContext currentAnalysisContext2 = currentAnalysisContext();
250 if (currentAnalysisContext2 == null)
279 if (currentAnalysisContext2 == null) {
251280 return null;
252 if (currentAnalysisContext2.missingClassWarningsSuppressed)
281 }
282 if (currentAnalysisContext2.missingClassWarningsSuppressed) {
253283 return null;
284 }
254285 return currentAnalysisContext2.getLookupFailureCallback();
255286 }
256287
260291 * @see #getLookupFailureCallback()
261292 */
262293 static public void reportMissingClass(ClassNotFoundException e) {
263 if (e == null)
264 throw new NullPointerException("argument is null");
294 requireNonNull(e, "argument is null");
265295 String missing = AbstractBugReporter.getMissingClassName(e);
266 if (skipReportingMissingClass(missing))
296 if (skipReportingMissingClass(missing)) {
267297 return;
268 if (!analyzingApplicationClass())
298 }
299 if (!analyzingApplicationClass()) {
269300 return;
301 }
270302
271303 RepositoryLookupFailureCallback lookupFailureCallback = getCurrentLookupFailureCallback();
272 if (lookupFailureCallback != null)
304 if (lookupFailureCallback != null) {
273305 lookupFailureCallback.reportMissingClass(e);
306 }
274307 }
275308
276309 static public void reportMissingClass(edu.umd.cs.findbugs.ba.MissingClassException e) {
277 if (e == null)
278 throw new NullPointerException("argument is null");
310 requireNonNull(e, "argument is null");
279311 reportMissingClass(e.getClassDescriptor());
280312 }
281313
282314 static public boolean analyzingApplicationClass() {
283315 AnalysisContext context = AnalysisContext.currentAnalysisContext();
284 if (context == null)
316 if (context == null) {
285317 return false;
318 }
286319 ClassDescriptor clazz = context.getClassBeingAnalyzed();
287 if (clazz == null)
320 if (clazz == null) {
288321 return false;
322 }
289323 return context.isApplicationClass(clazz);
290324 }
325
291326 static public void reportMissingClass(edu.umd.cs.findbugs.classfile.MissingClassException e) {
292 if (e == null)
293 throw new NullPointerException("argument is null");
327 requireNonNull(e, "argument is null");
294328 reportMissingClass(e.getClassDescriptor());
295329 }
296330
297
298331 static public void reportMissingClass(ClassDescriptor c) {
299 if (c == null)
300 throw new NullPointerException("argument is null");
301 if (!analyzingApplicationClass())
332 requireNonNull(c, "argument is null");
333 if (!analyzingApplicationClass()) {
302334 return;
335 }
303336 String missing = c.getDottedClassName();
304 if (missing.length() == 1)
337 if (missing.length() == 1) {
305338 System.out.println(c);
306 if (skipReportingMissingClass(missing))
339 }
340 if (skipReportingMissingClass(missing)) {
307341 return;
342 }
308343 RepositoryLookupFailureCallback lookupFailureCallback = getCurrentLookupFailureCallback();
309 if (lookupFailureCallback != null)
344 if (lookupFailureCallback != null) {
310345 lookupFailureCallback.reportMissingClass(c);
346 }
311347 }
312348
313349 /**
316352 static public void logError(String msg, Exception e) {
317353 AnalysisContext currentAnalysisContext2 = currentAnalysisContext();
318354 if (currentAnalysisContext2 == null) {
319 if (e instanceof NoSuchBugPattern)
355 if (e instanceof NoSuchBugPattern) {
320356 return;
357 }
358 /*
321359 if (false && SystemProperties.ASSERTIONS_ENABLED) {
322360 AssertionError e2 = new AssertionError("Exception logged with no analysis context");
323361 e2.initCause(e);
324362 throw e2;
325363 }
364 */
326365 e.printStackTrace(System.err);
327366 return;
328367 }
336375 }
337376
338377 RepositoryLookupFailureCallback lookupFailureCallback = currentAnalysisContext2.getLookupFailureCallback();
339 if (lookupFailureCallback != null)
378 if (lookupFailureCallback != null) {
340379 lookupFailureCallback.logError(msg, e);
380 }
341381 }
342382
343383 /**
345385 */
346386 static public void logError(String msg) {
347387 AnalysisContext currentAnalysisContext2 = currentAnalysisContext();
348 if (currentAnalysisContext2 == null)
388 if (currentAnalysisContext2 == null) {
349389 return;
350 RepositoryLookupFailureCallback lookupFailureCallback = currentAnalysisContext2.getLookupFailureCallback();
351 if (lookupFailureCallback != null)
390 }
391 currentAnalysisContext2.logAnError(msg);
392 }
393
394 public void logAnError(String msg) {
395 RepositoryLookupFailureCallback lookupFailureCallback = getLookupFailureCallback();
396 if (lookupFailureCallback != null) {
352397 lookupFailureCallback.logError(msg);
353 }
354
355 boolean missingClassWarningsSuppressed = false;
356
357 protected Project project;
398 }
399 }
400
401 public void analysisSkippedDueToInvokeDynamic(XMethod m) {
402 if (!m.usesInvokeDynamic()) {
403 throw new IllegalArgumentException();
404 }
405 if (skippedDueToInvokeDynamic.add(m.getMethodDescriptor())) {
406 logAnError(m + " skipped due to invoke_dynamic");
407 }
408
409 }
358410
359411 public boolean setMissingClassWarningsSuppressed(boolean value) {
360412 boolean oldValue = missingClassWarningsSuppressed;
361413 missingClassWarningsSuppressed = value;
362414 return oldValue;
363 }
364
365
366 /**
367 * Set the source path.
368 */
369 public final void setProject(Project project) {
370 this.project = project;
371415 }
372416
373417 // /**
418462 IAnalysisCache analysisCache = Global.getAnalysisCache();
419463
420464 try {
421 ClassContext classContext = analysisCache.getClassAnalysis(ClassContext.class, desc);
465 /* ClassContext classContext =*/ analysisCache.getClassAnalysis(ClassContext.class, desc);
422466 ClassData classData = analysisCache.getClassAnalysis(ClassData.class, desc);
423467 return classData.getData().length;
424468
429473 AnalysisContext.logError("Could not get class context for " + desc, e);
430474 return 10000;
431475 }
432
433476 }
434477
435478 public boolean isTooBig(ClassDescriptor desc) {
438481 try {
439482 ClassContext classContext = analysisCache.getClassAnalysis(ClassContext.class, desc);
440483 ClassData classData = analysisCache.getClassAnalysis(ClassData.class, desc);
441 if (classData.getData().length > 1000000)
484 if (classData.getData().length > 1000000) {
442485 return true;
486 }
443487 try {
444 JavaClass javaClass = classContext.getJavaClass();
445 if (javaClass.getMethods().length > 1000)
446 return true;
488 JavaClass javaClass = classContext.getJavaClass();
489 if (javaClass.getMethods().length > 1000) {
490 return true;
491 }
447492 } catch (RuntimeException e) {
448493 AnalysisContext.logError("Error parsing class " + desc
449494 + " from " + classData.getCodeBaseEntry().getCodeBase(), e);
458503 return true;
459504 }
460505 return false;
461
462506 }
463507
464508 /**
491535 public static JavaClass lookupSystemClass(@Nonnull String className) throws ClassNotFoundException {
492536 // TODO: eventually we should move to our own thread-safe repository
493537 // implementation
494 if (className == null)
495 throw new IllegalArgumentException("className is null");
496 if (originalRepository == null)
538 requireNonNull (className, "className is null");
539 if (originalRepository == null) {
497540 throw new IllegalStateException("originalRepository is null");
541 }
498542
499543 JavaClass clazz = originalRepository.findClass(className);
500544 return (clazz == null ? originalRepository.loadClass(className) : clazz);
510554 * determine
511555 */
512556 public final String lookupSourceFile(@Nonnull @DottedClassName String dottedClassName) {
513 if (dottedClassName == null)
514 throw new IllegalArgumentException("className is null");
557 requireNonNull(dottedClassName, "className is null");
515558 try {
516559 XClass xClass = Global.getAnalysisCache().getClassAnalysis(XClass.class,
517560 DescriptorFactory.createClassDescriptorFromDottedClassName(dottedClassName));
541584 * dereference parameters.
542585 */
543586 public final void loadDefaultInterproceduralDatabases() {
544 if (IGNORE_BUILTIN_MODELS)
587 if (IGNORE_BUILTIN_MODELS) {
545588 return;
589 }
546590 loadPropertyDatabaseFromResource(getUnconditionalDerefParamDatabase(), UNCONDITIONAL_DEREF_DB_RESOURCE,
547591 "unconditional param deref database");
548592 loadPropertyDatabaseFromResource(getReturnValueNullnessPropertyDatabase(), NONNULL_RETURN_DB_RESOURCE,
580624 * the interprocedural database input directory
581625 */
582626 public final void setDatabaseInputDir(String databaseInputDir) {
583 if (DEBUG)
627 if (DEBUG) {
584628 System.out.println("Setting database input directory: " + databaseInputDir);
629 }
585630 this.databaseInputDir = databaseInputDir;
586631 }
587632
601646 * the interprocedural database output directory
602647 */
603648 public final void setDatabaseOutputDir(String databaseOutputDir) {
604 if (DEBUG)
649 if (DEBUG) {
605650 System.out.println("Setting database output directory: " + databaseOutputDir);
651 }
606652 this.databaseOutputDir = databaseOutputDir;
607653 }
608654
614660 public final String getDatabaseOutputDir() {
615661 return databaseOutputDir;
616662 }
617
618
619663
620664 /**
621665 * Load an interprocedural property database.
638682 DatabaseType database, String fileName, String description) {
639683 try {
640684 File dbFile = new File(getDatabaseInputDir(), fileName);
641 if (DEBUG)
685 if (DEBUG) {
642686 System.out.println("Loading " + description + " from " + dbFile.getPath() + "...");
687 }
643688
644689 database.readFromFile(dbFile.getPath());
645690 return database;
648693 } catch (PropertyDatabaseFormatException e) {
649694 getLookupFailureCallback().logError("Invalid " + description, e);
650695 }
651
652696 return null;
653697 }
654698
672716 public <DatabaseType extends PropertyDatabase<KeyType, Property>, KeyType extends FieldOrMethodDescriptor, Property> DatabaseType loadPropertyDatabaseFromResource(
673717 DatabaseType database, String resourceName, String description) {
674718 try {
675 if (DEBUG)
719 if (DEBUG) {
676720 System.out.println("Loading default " + description + " from " + resourceName + " @ "
677721 + database.getClass().getResource(resourceName) + " ... ");
678 InputStream in = database.getClass().getResourceAsStream(resourceName);
679 if (in == null) {
680 AnalysisContext.logError("Unable to load " + description + " from resource " + resourceName);
681 } else {
682 database.read(in);
683 in.close();
722 }
723 try(InputStream in = database.getClass().getResourceAsStream(resourceName)){
724 if (in == null) {
725 AnalysisContext.logError("Unable to load " + description + " from resource " + resourceName);
726 } else {
727 database.read(in);
728 }
684729 }
685730 return database;
686731 } catch (IOException e) {
688733 } catch (PropertyDatabaseFormatException e) {
689734 getLookupFailureCallback().logError("Invalid " + description, e);
690735 }
691
692736 return null;
693737 }
694738
713757
714758 try {
715759 File dbFile = new File(getDatabaseOutputDir(), fileName);
716 if (DEBUG)
760 if (DEBUG) {
717761 System.out.println("Writing " + description + " to " + dbFile.getPath() + "...");
762 }
718763 database.writeToFile(dbFile.getPath());
719764 } catch (IOException e) {
720765 getLookupFailureCallback().logError("Error writing " + description, e);
729774 */
730775 public static void setCurrentAnalysisContext(AnalysisContext analysisContext) {
731776 currentAnalysisContext.set(analysisContext);
732 if (Global.getAnalysisCache() != null)
777 if (Global.getAnalysisCache() != null) {
733778 currentXFactory.set(new XFactory());
779 }
734780 }
735781
736782 public static void removeCurrentAnalysisContext() {
737783 AnalysisContext context = currentAnalysisContext();
738 if (context != null)
784 if (context != null) {
739785 context.clear();
786 }
740787 currentAnalysisContext.remove();
741788 }
742
743789
744790 /**
745791 * Get Collection of all XClass objects seen so far.
749795 public Collection<XClass> getXClassCollection() {
750796 return getSubtypes2().getXClassCollection();
751797 }
752
753 private final SuppressionMatcher suppressionMatcher = new SuppressionMatcher();
754
755 private TypeQualifierNullnessAnnotationDatabase tqNullnessDatabase;
756
757 protected final RepositoryLookupFailureCallback lookupFailureCallback;
758
759 final Map<MethodInfo, MethodInfo> bridgeTo = new IdentityHashMap<MethodInfo, MethodInfo>();
760
761 final Map<MethodInfo, MethodInfo> bridgeFrom = new IdentityHashMap<MethodInfo, MethodInfo>();
762798
763799 public SuppressionMatcher getSuppressionMatcher() {
764800 return suppressionMatcher;
792828 Repository.setRepository(new AnalysisCacheToRepositoryAdapter());
793829 }
794830
795
796831 public AnnotationRetentionDatabase getAnnotationRetentionDatabase() {
797832 return getDatabase(AnnotationRetentionDatabase.class);
798833 }
799
800834
801835 public CheckReturnAnnotationDatabase getCheckReturnAnnotationDatabase() {
802836 return getDatabase(CheckReturnAnnotationDatabase.class);
903937 */
904938 public JavaClass lookupClass(@Nonnull @DottedClassName String className) throws ClassNotFoundException {
905939 try {
906 if (className.length() == 0)
940 if (className.length() == 0) {
907941 throw new IllegalArgumentException("Class name is empty");
942 }
908943 if (!ClassName.isValidClassName(className)) {
909944 throw new ClassNotFoundException("Invalid class name: " + className);
910945 }
920955 }
921956
922957 public void setAppClassList(List<ClassDescriptor> appClassCollection) {
923
924958 // FIXME: we really should drive the progress callback here
925959 HashSet<ClassDescriptor> appSet = new HashSet<ClassDescriptor>(appClassCollection);
926960
927961 Collection<ClassDescriptor> allClassDescriptors = new ArrayList<ClassDescriptor>(DescriptorFactory.instance()
928962 .getAllClassDescriptors());
929 for (ClassDescriptor appClass : allClassDescriptors)
963 for (ClassDescriptor appClass : allClassDescriptors) {
930964 try {
931965 XClass xclass = currentXFactory().getXClass(appClass);
932966
933 if (xclass == null)
967 if (xclass == null) {
934968 continue;
969 }
935970
936971 // Add the application class to the database
937 if (appSet.contains(appClass))
972 if (appSet.contains(appClass)) {
938973 getSubtypes2().addApplicationClass(xclass);
939 else if (xclass instanceof ClassInfo)
974 } else if (xclass instanceof ClassInfo) {
940975 getSubtypes2().addClass(xclass);
976 }
941977
942978 } catch (Exception e) {
943979 AnalysisContext.logError("Unable to get XClass for " + appClass, e);
944980 }
981 }
945982
946983 if (true && Subtypes2.DEBUG) {
947984 System.out.println(getSubtypes2().getGraph().getNumVertices() + " vertices in inheritance graph");
10041041 }
10051042
10061043 public TypeQualifierNullnessAnnotationDatabase getNullnessAnnotationDatabase() {
1007 if (tqNullnessDatabase == null) {
1008 tqNullnessDatabase = new TypeQualifierNullnessAnnotationDatabase();
1009 }
1010 return tqNullnessDatabase;
1011
1044 if (tqNullnessDatabase == null) {
1045 tqNullnessDatabase = new TypeQualifierNullnessAnnotationDatabase();
1046 }
1047 return tqNullnessDatabase;
1048
10121049 }
10131050
10141051 protected <E> E getDatabase(Class<E> cls) {
10171054
10181055 static class DelegatingRepositoryLookupFailureCallback implements RepositoryLookupFailureCallback {
10191056
1020 /*
1021 * (non-Javadoc)
1022 *
1023 * @see
1024 * edu.umd.cs.findbugs.classfile.IErrorLogger#logError(java.lang.String)
1025 */
1057 @Override
10261058 public void logError(String message) {
10271059 Global.getAnalysisCache().getErrorLogger().logError(message);
10281060 }
10291061
1030 /*
1031 * (non-Javadoc)
1032 *
1033 * @see
1034 * edu.umd.cs.findbugs.classfile.IErrorLogger#logError(java.lang.String,
1035 * java.lang.Throwable)
1036 */
1062 @Override
10371063 public void logError(String message, Throwable e) {
10381064 Global.getAnalysisCache().getErrorLogger().logError(message, e);
10391065 }
10401066
1041 /*
1042 * (non-Javadoc)
1043 *
1044 * @see
1045 * edu.umd.cs.findbugs.classfile.IErrorLogger#reportMissingClass(java
1046 * .lang.ClassNotFoundException)
1047 */
1067 @Override
10481068 public void reportMissingClass(ClassNotFoundException ex) {
10491069 Global.getAnalysisCache().getErrorLogger().reportMissingClass(ex);
10501070 }
10511071
1052 /*
1053 * (non-Javadoc)
1054 *
1055 * @see
1056 * edu.umd.cs.findbugs.classfile.IErrorLogger#reportMissingClass(edu
1057 * .umd.cs.findbugs.classfile.ClassDescriptor)
1058 */
1072 @Override
10591073 public void reportMissingClass(ClassDescriptor classDescriptor) {
10601074 Global.getAnalysisCache().getErrorLogger().reportMissingClass(classDescriptor);
10611075 }
10621076
1063 /*
1064 * (non-Javadoc)
1065 *
1066 * @see
1067 * edu.umd.cs.findbugs.classfile.IErrorLogger#reportSkippedAnalysis(
1068 * edu.umd.cs.findbugs.classfile.MethodDescriptor)
1069 */
1077 @Override
10701078 public void reportSkippedAnalysis(MethodDescriptor method) {
10711079 Global.getAnalysisCache().getErrorLogger().reportSkippedAnalysis(method);
10721080 }
10751083
10761084 }
10771085
1078 // vim:ts=4
3737
3838 /**
3939 * Constructor.
40 *
40 *
4141 * @param message
4242 * reason for the error
4343 */
4848 /**
4949 * Constructor from another Throwable object. This is useful for chaining
5050 * exceptions.
51 *
51 *
5252 * @param message
5353 * reason for the error
5454 * @param throwable
6161 /**
6262 * Constructor from MethodGen and another Throwable object. This is useful
6363 * for chaining exceptions.
64 *
64 *
6565 * @param message
6666 * reason for the error
6767 * @param methodGen
7575
7676 /**
7777 * Constructor from method and instruction.
78 *
78 *
7979 * @param message
8080 * reason for the error
8181 * @param methodGen
8989
9090 /**
9191 * Constructor from method and instruction.
92 *
92 *
9393 * @param message
9494 * reason for the error
9595 * @param methodGen
103103
104104 /**
105105 * Constructor from method, instruction, and causing Throwable object.
106 *
106 *
107107 * @param message
108108 * reason for the error
109109 * @param methodGen
119119
120120 /**
121121 * Constructor from method, instruction, and causing Throwable object.
122 *
122 *
123123 * @param message
124124 * reason for the error
125125 * @param methodGen
134134 }
135135 }
136136
137 // vim:ts=4
3333
3434 import edu.umd.cs.findbugs.SystemProperties;
3535 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
36 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
3736 import edu.umd.cs.findbugs.classfile.DescriptorFactory;
3837 import edu.umd.cs.findbugs.classfile.Global;
3938 import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
5554 CLASS, ANY
5655 }
5756
58 private static final String DEFAULT_ANNOTATION_ANNOTATION_CLASS = "DefaultAnnotation";
57 // private static final String DEFAULT_ANNOTATION_ANNOTATION_CLASS = "DefaultAnnotation";
5958
6059 private final Map<Object, AnnotationEnum> directAnnotations = new HashMap<Object, AnnotationEnum>();
6160
8584 }
8685
8786 public void addDefaultAnnotation(Target target, String c, AnnotationEnum n) {
88 if (!defaultAnnotation.containsKey(target))
89 return;
90 if (DEBUG)
87 if (!defaultAnnotation.containsKey(target)) {
88 return;
89 }
90 if (DEBUG) {
9191 System.out.println("Default annotation " + target + " " + c + " " + n);
92 }
9293 defaultAnnotation.get(target).put(c, n);
9394 seen.add(n);
9495 }
121122 }
122123 }
123124 Map<Object, AnnotationEnum> cache;
124 if (getMinimal)
125 if (getMinimal) {
125126 cache = cachedMinimal;
126 else
127 } else {
127128 cache = cachedMaximal;
129 }
128130
129131 if (cache.containsKey(o)) {
130132 return cache.get(o);
131133 }
132134 AnnotationEnum n = getUncachedResolvedAnnotation(o, getMinimal);
133 if (DEBUG)
135 if (DEBUG) {
134136 System.out.println("TTT: " + o + " " + n);
137 }
135138 cache.put(o, n);
136139 return n;
137140 }
144147 public AnnotationEnum getUncachedResolvedAnnotation(final Object o, boolean getMinimal) {
145148
146149 AnnotationEnum n = getDirectAnnotation(o);
147 if (n != null)
150 if (n != null) {
148151 return n;
152 }
149153
150154 try {
151155
167171 isSyntheticMethod = m.isSynthetic();
168172 className = m.getClassName();
169173 kind = Target.PARAMETER;
170 if (m.getName().equals("<init>")) {
171 int i = className.lastIndexOf("$");
172 if (i + 1 < className.length() && Character.isDigit(className.charAt(i + 1)))
174 if ("<init>".equals(m.getName())) {
175 int i = className.lastIndexOf('$');
176 if (i + 1 < className.length() && Character.isDigit(className.charAt(i + 1))) {
173177 isParameterToInitMethodofAnonymousInnerClass = true;
174 }
175 } else
178 }
179 }
180 } else {
176181 throw new IllegalStateException("impossible");
177
178 if (!m.isStatic() && !m.getName().equals("<init>")) {
182 }
183
184 if (!m.isStatic() && !"<init>".equals(m.getName())) {
179185 JavaClass c = Repository.lookupClass(className);
180186 // get inherited annotation
181187 TreeSet<AnnotationEnum> inheritedAnnotations = new TreeSet<AnnotationEnum>();
182188 if (c.getSuperclassNameIndex() > 0) {
183189
184190 n = lookInOverriddenMethod(o, c.getSuperclassName(), m, getMinimal);
185 if (n != null)
191 if (n != null) {
186192 inheritedAnnotations.add(n);
193 }
187194 }
188195 for (String implementedInterface : c.getInterfaceNames()) {
189196 n = lookInOverriddenMethod(o, implementedInterface, m, getMinimal);
190 if (n != null)
197 if (n != null) {
191198 inheritedAnnotations.add(n);
192 }
193 if (DEBUG)
199 }
200 }
201 if (DEBUG) {
194202 System.out.println("# of inherited annotations : " + inheritedAnnotations.size());
203 }
195204 if (!inheritedAnnotations.isEmpty()) {
196 if (inheritedAnnotations.size() == 1)
205 if (inheritedAnnotations.size() == 1) {
197206 return inheritedAnnotations.first();
198 if (!getMinimal)
207 }
208 if (!getMinimal) {
199209 return inheritedAnnotations.last();
210 }
200211
201212 AnnotationEnum min = inheritedAnnotations.first();
202213 if (min.getIndex() == 0) {
207218 }
208219 // check to see if method is defined in this class;
209220 // if not, on't consider default annotations
210 if (!classDefinesMethod(c, m))
221 if (!classDefinesMethod(c, m)) {
211222 return null;
212 if (DEBUG)
223 }
224 if (DEBUG) {
213225 System.out.println("looking for default annotations: " + c.getClassName() + " defines " + m);
226 }
214227 } // if not static
215228 } // associated with method
216229 else if (o instanceof XField) {
221234 assert false;
222235 className = (String) o;
223236 kind = Target.CLASS;
224 } else
237 } else {
225238 throw new IllegalArgumentException("Can't look up annotation for " + o.getClass().getName());
239 }
226240
227241 // <init> method parameters for inner classes don't inherit default
228242 // annotations
229243 // since some of them are synthetic
230 if (isParameterToInitMethodofAnonymousInnerClass)
244 if (isParameterToInitMethodofAnonymousInnerClass) {
231245 return null;
246 }
232247
233248 // synthetic elements should not inherit default annotations
234 if (isSyntheticMethod)
249 if (isSyntheticMethod) {
235250 return null;
251 }
236252 try {
237253 XClass c = Global.getAnalysisCache().getClassAnalysis(XClass.class,
238254 DescriptorFactory.createClassDescriptorFromDottedClassName(className));
239255
240 if (c != null && c.isSynthetic())
256 if (c != null && c.isSynthetic()) {
241257 return null;
258 }
242259 } catch (CheckedAnalysisException e) {
243260 assert true;
244261 }
245262
246263 // look for default annotation
247264 n = defaultAnnotation.get(kind).get(className);
248 if (DEBUG)
265 if (DEBUG) {
249266 System.out.println("Default annotation for " + kind + " is " + n);
250 if (n != null)
267 }
268 if (n != null) {
251269 return n;
270 }
252271
253272 n = defaultAnnotation.get(Target.ANY).get(className);
254 if (DEBUG)
273 if (DEBUG) {
255274 System.out.println("Default annotation for any is " + n);
256 if (n != null)
275 }
276 if (n != null) {
257277 return n;
278 }
258279
259280 int p = className.lastIndexOf('.');
260281 className = className.substring(0, p + 1) + "package-info";
261282 n = defaultAnnotation.get(kind).get(className);
262 if (DEBUG)
283 if (DEBUG) {
263284 System.out.println("Default annotation for " + kind + " is " + n);
264 if (n != null)
285 }
286 if (n != null) {
265287 return n;
288 }
266289
267290 n = defaultAnnotation.get(Target.ANY).get(className);
268 if (DEBUG)
291 if (DEBUG) {
269292 System.out.println("Default annotation for any is " + n);
270 if (n != null)
293 }
294 if (n != null) {
271295 return n;
296 }
272297
273298 return n;
274299 } catch (ClassNotFoundException e) {
278303
279304 }
280305
281 /**
282 * @param o
283 * @return
284 */
285306 public AnnotationEnum getDirectAnnotation(final Object o) {
286307 return directAnnotations.get(o);
287308 }
288309
289310 private boolean classDefinesMethod(JavaClass c, XMethod m) {
290 for (Method definedMethod : c.getMethods())
311 for (Method definedMethod : c.getMethods()) {
291312 if (definedMethod.getName().equals(m.getName()) && definedMethod.getSignature().equals(m.getSignature())
292 && definedMethod.isStatic() == m.isStatic())
313 && definedMethod.isStatic() == m.isStatic()) {
293314 return true;
315 }
316 }
294317 return false;
295318 }
296319
301324 // Look in supermethod
302325 XMethod superMethod = XFactory.createXMethod(classToLookIn, originalMethod.getName(), originalMethod.getSignature(),
303326 originalMethod.isStatic());
304 if (!superMethod.isResolved())
327 if (!superMethod.isResolved()) {
305328 return null;
306 if (DEBUG)
329 }
330 if (DEBUG) {
307331 System.out.println("Looking for overridden method " + superMethod);
332 }
308333
309334 Object probe;
310 if (originalQuery instanceof XMethod)
335 if (originalQuery instanceof XMethod) {
311336 probe = superMethod;
312 else if (originalQuery instanceof XMethodParameter)
337 } else if (originalQuery instanceof XMethodParameter) {
313338 probe = new XMethodParameter(superMethod, ((XMethodParameter) originalQuery).getParameterNumber());
314 else
339 } else {
315340 throw new IllegalStateException("impossible");
341 }
316342
317343 n = getResolvedAnnotation(probe, getMinimal);
318344 return n;
334360 // if (!Subtypes.DO_NOT_USE) {
335361 // subtypes.addNamedClass(cName);
336362 // }
337
338 if (addClassOnly)
339 return;
340
363 if (addClassOnly) {
364 return;
365 }
341366 addDefaultAnnotation(AnnotationDatabase.Target.METHOD, cName, annotation);
342
343367 }
344368
345369 protected void addFieldAnnotation(String cName, String mName, String mSig, boolean isStatic, AnnotationEnum annotation) {
346370 // if (!Subtypes.DO_NOT_USE) {
347371 // subtypes.addNamedClass(cName);
348372 // }
349 if (addClassOnly)
350 return;
373 if (addClassOnly) {
374 return;
375 }
351376 XField m = XFactory.createXField(cName, mName, mSig, isStatic);
352377 addDirectAnnotation(m, annotation);
353378 }
354379
355 protected void addMethodAnnotation(Class<?> clazz, String mName, String mSig, boolean isStatic,
356 AnnotationEnum annotation) {
380 protected void addMethodAnnotation(Class<?> clazz, String mName, String mSig, boolean isStatic, AnnotationEnum annotation) {
357381 addMethodAnnotation(clazz.getName(), mName, mSig, isStatic, annotation);
358382 }
359 protected void addMethodAnnotation(@DottedClassName String cName, String mName, String mSig, boolean isStatic,
360 AnnotationEnum annotation) {
361 if (addClassOnly)
362 return;
383
384 protected void addMethodAnnotation(@DottedClassName String cName, String mName, String mSig, boolean isStatic, AnnotationEnum annotation) {
385 if (addClassOnly) {
386 return;
387 }
363388 XMethod m = XFactory.createXMethod(cName, mName, mSig, isStatic);
364 if (!m.getClassName().equals(cName))
365 return;
389 if (!m.getClassName().equals(cName)){
390 return;
391 }
392 /*
366393 if (false && !m.isResolved()) {
367394 System.out.println("Unable to add annotation " + annotation + " to " + m);
368395 ClassDescriptor c = DescriptorFactory.createClassDescriptorFromDottedClassName(cName);
378405 e.printStackTrace();
379406 }
380407 }
381
408 */
382409 addDirectAnnotation(m, annotation);
383410 }
384411
392419 // if (!Subtypes.DO_NOT_USE) {
393420 // subtypes.addNamedClass(cName);
394421 // }
395 if (addClassOnly)
396 return;
422 if (addClassOnly) {
423 return;
424 }
397425 SignatureParser parser = new SignatureParser(mSig);
398 if (param < 0 || param >= parser.getNumParameters())
426 if (param < 0 || param >= parser.getNumParameters()) {
399427 throw new IllegalArgumentException("can't annotation parameter #" + param + " of " + cName + "." + mName + mSig);
428 }
400429 String signature = parser.getParameter(param);
401430 char firstChar = signature.charAt(0);
402431 boolean isReference = firstChar == 'L' || firstChar == '[';
3333
3434 @Override
3535 public final boolean equals(Object o) {
36 if (!(o instanceof AnnotationEnumeration<?>))
36 if (!(o instanceof AnnotationEnumeration<?>)) {
3737 return false;
38 }
3839 return index == ((AnnotationEnumeration<?>) o).getIndex();
3940 }
4041
5758
5859 /*
5960 * (non-Javadoc)
60 *
61 *
6162 * @see java.lang.Comparable#compareTo(T)
6263 */
64 @Override
6365 public int compareTo(E a) {
6466 return index - a.getIndex();
6567 }
2121 import java.util.HashMap;
2222
2323 public class AnnotationRetentionDatabase {
24 private HashMap<String, Boolean> annotationRetention = new HashMap<String, Boolean>();
24 private final HashMap<String, Boolean> annotationRetention = new HashMap<String, Boolean>();
2525
2626 public boolean hasRuntimeRetention(String dottedClassName) {
2727 Boolean result = annotationRetention.get(dottedClassName);
28 if (result == null)
28 if (result == null) {
2929 return false;
30 }
3031 return result;
3132 }
3233
3334 /** return false if it has class retention *or* if the retention is unknown */
3435 public boolean lacksRuntimeRetention(String dottedClassName) {
3536 Boolean result = annotationRetention.get(dottedClassName);
36 if (result == null)
37 if (result == null) {
3738 return false;
39 }
3840 return !result;
3941 }
4042
8686 while (tok.hasMoreTokens()) {
8787 String fullyQualifiedName = tok.nextToken();
8888 int lastDot = fullyQualifiedName.lastIndexOf('.');
89 if (lastDot < 0)
89 if (lastDot < 0) {
9090 continue;
91 }
9192 String className = fullyQualifiedName.substring(0, lastDot);
9293 String methodName = fullyQualifiedName.substring(lastDot + 1);
9394 userAssertionMethodList.add(new UserAssertionMethod(className, methodName));
137138 && methodName.startsWith("is")
138139 || (voidReturnType || boolReturnType)
139140 && (classNameLC.indexOf("assert") >= 0 || methodNameLC.startsWith("throw")
140 || methodName.startsWith("affirm") || methodName.startsWith("panic")
141 || methodName.equals("logTerminal") || methodName.startsWith("logAndThrow")
142 || methodNameLC.equals("insist") || methodNameLC.equals("usage")
143 || methodNameLC.equals("exit") || methodNameLC.startsWith("fail")
144 || methodNameLC.startsWith("fatal") || methodNameLC.indexOf("assert") >= 0
145 || methodNameLC.indexOf("legal") >= 0 || methodNameLC.indexOf("error") >= 0
146 || methodNameLC.indexOf("abort") >= 0
147 // || methodNameLC.indexOf("check") >= 0
148 || methodNameLC.indexOf("failed") >= 0) || methodName.equals("addOrThrowException")) {
141 || methodName.startsWith("affirm") || methodName.startsWith("panic")
142 || "logTerminal".equals(methodName) || methodName.startsWith("logAndThrow")
143 || "insist".equals(methodNameLC) || "usage".equals(methodNameLC)
144 || "exit".equals(methodNameLC) || methodNameLC.startsWith("fail")
145 || methodNameLC.startsWith("fatal") || methodNameLC.indexOf("assert") >= 0
146 || methodNameLC.indexOf("legal") >= 0 || methodNameLC.indexOf("error") >= 0
147 || methodNameLC.indexOf("abort") >= 0
148 // || methodNameLC.indexOf("check") >= 0
149 || methodNameLC.indexOf("failed") >= 0) || "addOrThrowException".equals(methodName)) {
149150 assertionMethodRefSet.set(i);
150151 if (DEBUG) {
151152 System.out.println("==> YES");
164165
165166 private static boolean isUserAssertionMethod(String className, String methodName) {
166167 for (UserAssertionMethod uam : userAssertionMethodList) {
167 if (className.equals(uam.getClassName()) && methodName.equals(uam.getMethodName()))
168 if (className.equals(uam.getClassName()) && methodName.equals(uam.getMethodName())) {
168169 return true;
170 }
169171 }
170172 return false;
171173 }
172174
173175 public boolean isAssertionHandle(InstructionHandle handle, ConstantPoolGen cpg) {
174176 Instruction ins = handle.getInstruction();
175 if (isAssertionInstruction(ins, cpg))
177 if (isAssertionInstruction(ins, cpg)) {
176178 return true;
179 }
177180
178181 if (ins instanceof SIPUSH) {
179182 int v = ((SIPUSH) ins).getValue().intValue();
183186 INVOKEINTERFACE iInterface = (INVOKEINTERFACE) next;
184187 String className = iInterface.getClassName(cpg);
185188 String fieldName = iInterface.getMethodName(cpg);
186 if (className.equals("javax.servlet.http.HttpServletResponse") && fieldName.equals("setStatus"))
189 if ("javax.servlet.http.HttpServletResponse".equals(className) && "setStatus".equals(fieldName)) {
187190 return true;
191 }
188192
189193 }
190194 }
203207
204208 public boolean isAssertionInstruction(Instruction ins, ConstantPoolGen cpg) {
205209
206 if (ins instanceof InvokeInstruction)
210 if (ins instanceof InvokeInstruction) {
207211 return isAssertionCall((InvokeInstruction) ins);
212 }
208213 if (ins instanceof GETSTATIC) {
209214 GETSTATIC getStatic = (GETSTATIC) ins;
210215 String className = getStatic.getClassName(cpg);
211216 String fieldName = getStatic.getFieldName(cpg);
212 if (className.equals("java.util.logging.Level") && fieldName.equals("SEVERE"))
217 if ("java.util.logging.Level".equals(className) && "SEVERE".equals(fieldName)) {
213218 return true;
214 if (className.equals("org.apache.log4j.Level") && (fieldName.equals("ERROR") || fieldName.equals("FATAL")))
219 }
220 if ("org.apache.log4j.Level".equals(className) && ("ERROR".equals(fieldName) || "FATAL".equals(fieldName))) {
215221 return true;
222 }
216223 return false;
217224
218225 }
227234 }
228235 }
229236
230 // vim:ts=4
2424 /*
2525 * private final Map<Method, Set<XField>> assignedFieldSetForMethodMap;
2626 * private final JavaClass myClass;
27 *
27 *
2828 * public AssignedFieldMap(JavaClass jclass) {
2929 * this.assignedFieldSetForMethodMap = new IdentityHashMap<Method,
3030 * Set<XField>>(); this.myClass = jclass; }
31 *
31 *
3232 * public void build() throws ClassNotFoundException { // Build a set of all
3333 * fields that could be assigned // by methods in this class Set<XField>
3434 * assignableFieldSet = new HashSet<XField>(); scanFields(myClass,
3636 * myClass.getSuperClasses(); if (superClassList != null) { for (JavaClass
3737 * aSuperClassList : superClassList) { scanFields(aSuperClassList,
3838 * assignableFieldSet); } }
39 *
39 *
4040 * Method[] methodList = myClass.getMethods(); for (Method method :
4141 * methodList) {
42 *
42 *
4343 * scanMethod(method, assignableFieldSet); } }
44 *
44 *
4545 * public Set<XField> getAssignedFieldSetForMethod(Method method) {
4646 * Set<XField> set = assignedFieldSetForMethodMap.get(method); if (set ==
4747 * null) { set = new HashSet<XField>();
4848 * assignedFieldSetForMethodMap.put(method, set); } return set; }
49 *
49 *
5050 * private void scanFields(JavaClass jclass, Set<XField> assignableFieldSet)
5151 * { // JavaClass myClass = classContext.getJavaClass(); String myClassName
5252 * = myClass.getClassName(); String myPackageName =
5353 * myClass.getPackageName();
54 *
54 *
5555 * String superClassName = jclass.getClassName(); String superPackageName =
5656 * jclass.getPackageName();
57 *
57 *
5858 * Field[] fieldList = jclass.getFields(); for (Field field : fieldList) {
5959 * if (field.isStatic()) continue; boolean assignable; if (field.isPublic()
6060 * || field.isProtected()) assignable = true; else if (field.isPrivate())
6161 * assignable = myClassName.equals(superClassName); else // package
6262 * protected assignable = myPackageName.equals(superPackageName);
63 *
63 *
6464 * if (assignable) { assignableFieldSet.add(new
6565 * InstanceField(superClassName, field.getName(), field.getSignature(),
6666 * field.getAccessFlags())); } } }
67 *
67 *
6868 * private void scanMethod(Method method, Set<XField> assignableFieldSet)
6969 * throws ClassNotFoundException { //MethodGen methodGen =
7070 * classContext.getMethodGen(method);
71 *
71 *
7272 * MethodGen methodGen; try { methodGen=
7373 * Global.getAnalysisCache().getMethodAnalysis(MethodGen.class,
7474 * BCELUtil.getMethodDescriptor(myClass, method)); } catch
7575 * (CheckedAnalysisException e) { // Should not happen throw new
7676 * AnalysisException("Could not get MethodGen for Method", e); }
77 *
77 *
7878 * if (methodGen == null) return; InstructionList il =
7979 * methodGen.getInstructionList(); InstructionHandle handle = il.getStart();
80 *
80 *
8181 * ConstantPoolGen cpg = methodGen.getConstantPool();
82 *
82 *
8383 * while (handle != null) { Instruction ins = handle.getInstruction(); short
8484 * opcode = ins.getOpcode(); if (opcode == Constants.PUTFIELD) { PUTFIELD
8585 * putfield = (PUTFIELD) ins;
86 *
86 *
8787 * XField instanceField = Hierarchy.findXField(putfield, cpg); if
8888 * (instanceField != null && assignableFieldSet.contains(instanceField)) {
8989 * Set<XField> assignedFieldSetForMethod =
9090 * getAssignedFieldSetForMethod(method);
9191 * assignedFieldSetForMethod.add(instanceField); } }
92 *
92 *
9393 * handle = handle.getNext(); } }
9494 */
9595 }
9696
97 // vim:ts=4
2121 /**
2222 * Abstract base class for backward dataflow analyses. Provides convenient
2323 * implementations for isForwards() and getBlockOrder() methods.
24 *
24 *
2525 * @author David Hovemeyer
2626 * @see Dataflow
2727 * @see DataflowAnalysis
2828 */
2929 public abstract class BackwardDataflowAnalysis<Fact> extends AbstractDataflowAnalysis<Fact> {
30 private ReverseDepthFirstSearch rdfs;
30 private final ReverseDepthFirstSearch rdfs;
3131
32 private DepthFirstSearch dfs;
32 private final DepthFirstSearch dfs;
3333
3434 public BackwardDataflowAnalysis(ReverseDepthFirstSearch rdfs, DepthFirstSearch dfs) {
35 if (rdfs == null || dfs == null)
35 if (rdfs == null || dfs == null) {
3636 throw new IllegalArgumentException();
37 }
3738 this.rdfs = rdfs;
3839 this.dfs = dfs;
3940 }
4243 return rdfs;
4344 }
4445
46 @Override
4547 public boolean isForwards() {
4648 return false;
4749 }
4850
51 @Override
4952 public BlockOrder getBlockOrder(CFG cfg) {
5053 return new ReverseDFSOrder(cfg, rdfs, dfs);
5154 }
5255 }
5356
54 // vim:ts=4
3737 * @author David Hovemeyer
3838 */
3939 public abstract class BasicAbstractDataflowAnalysis<Fact> implements DataflowAnalysis<Fact> {
40 private IdentityHashMap<BasicBlock, Fact> startFactMap;
41
42 private IdentityHashMap<BasicBlock, Fact> resultFactMap;
40 private final IdentityHashMap<BasicBlock, Fact> startFactMap;
41
42 private final IdentityHashMap<BasicBlock, Fact> resultFactMap;
4343
4444 /**
4545 * Constructor.
5959 /*
6060 * Default implementation - subclasses may override.
6161 */
62 @Override
6263 public String factToString(Fact fact) {
6364 return fact.toString();
6465 }
6566
67 @Override
6668 public/* final */Fact getStartFact(BasicBlock block) {
6769 return lookupOrCreateFact(startFactMap, block);
6870 }
6971
72 @Override
7073 public/* final */Fact getResultFact(BasicBlock block) {
7174 return lookupOrCreateFact(resultFactMap, block);
7275 }
8689 * @return the dataflow value at given Location
8790 * @throws DataflowAnalysisException
8891 */
92 @Override
8993 public Fact getFactAtLocation(Location location) throws DataflowAnalysisException {
9094 return getStartFact(location.getBasicBlock());
9195 }
105109 * @return the dataflow value after given Location
106110 * @throws DataflowAnalysisException
107111 */
112 @Override
108113 public Fact getFactAfterLocation(Location location) throws DataflowAnalysisException {
109114 return getResultFact(location.getBasicBlock());
110115 }
118123 * @return the fact that is true after applying the edge transfer function
119124 * @throws DataflowAnalysisException
120125 */
126 @Override
121127 public/* final */Fact getFactOnEdge(Edge edge) throws DataflowAnalysisException {
122128 BasicBlock block = isForwards() ? edge.getSource() : edge.getTarget();
123129
128134
129135 Fact result = createFact();
130136 makeFactTop(result);
131 if (this instanceof UnconditionalValueDerefAnalysis)
137 if (this instanceof UnconditionalValueDerefAnalysis) {
132138 ((UnconditionalValueDerefAnalysis)this).meetInto((UnconditionalValueDerefSet)predFact,
133139 edge, (UnconditionalValueDerefSet)result, true);
134 else
140 } else {
135141 meetInto(predFact, edge, result);
142 }
136143
137144
138145 return result;
143150 *
144151 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#startIteration()
145152 */
153 @Override
146154 public void startIteration() {
147155 // Do nothing - subclass may override
148156 }
152160 *
153161 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#finishIteration()
154162 */
163 @Override
155164 public void finishIteration() {
156165 // Do nothing - subclass may override
157166 }
163172 * edu.umd.cs.findbugs.ba.DataflowAnalysis#edgeTransfer(edu.umd.cs.findbugs
164173 * .ba.Edge, java.lang.Object)
165174 */
175 @Override
166176 public void edgeTransfer(Edge edge, Fact fact) throws DataflowAnalysisException {
167177 // By default, edge transfer function is identity.
168178 // Subclasses may override.
177187 return fact;
178188 }
179189
190 @Override
180191 public int getLastUpdateTimestamp(Fact fact) {
181192 return 0;
182193 }
183194
195 @Override
184196 public void setLastUpdateTimestamp(Fact fact, int lastUpdateTimestamp) {
185197
186198 }
2323 import java.util.NoSuchElementException;
2424
2525 import javax.annotation.CheckForNull;
26 import javax.annotation.Nullable;
2627
2728 import org.apache.bcel.Constants;
2829 import org.apache.bcel.generic.CodeExceptionGen;
3334
3435 /**
3536 * Simple basic block abstraction for BCEL.
36 *
37 *
3738 * @author David Hovemeyer
3839 * @see CFG
3940 */
7677 nullCheckInstructionSet.set(Constants.ARRAYLENGTH);
7778 // nullCheckInstructionSet.set(Constants.MONITOREXIT);
7879 nullCheckInstructionSet.set(Constants.ATHROW);
79
80
8081 // Any others?
8182 }
8283
9192 private InstructionHandle lastInstruction;
9293
9394 private InstructionHandle exceptionThrower; // instruction for which this
94 // block is the ETB
95 // block is the ETB
9596
9697 private CodeExceptionGen exceptionGen; // set if this block is the entry
97 // point of an exception handler
98 // point of an exception handler
9899
99100 private boolean inJSRSubroutine;
100101
128129
129130 /**
130131 * Get the basic block's integer label.
131 *
132 *
132133 * @deprecated call getLabel() instead
133134 * @return the BasicBlock's integer label
134135 */
144145
145146 /**
146147 * Set the instruction for which this block is the ETB.
147 *
148 *
148149 * @param exceptionThrower
149150 * the instruction
150151 */
161162
162163 /**
163164 * Get the instruction for which this block is an exception thrower.
164 *
165 *
165166 * @return the instruction, or null if this block is not an exception
166167 * thrower
167168 */
176177 // Null check blocks must be exception throwers,
177178 // and are always empty. (The only kind of non-empty
178179 // exception throwing block is one terminated by an ATHROW).
179 if (!isExceptionThrower() || getFirstInstruction() != null)
180 if (!isExceptionThrower() || getFirstInstruction() != null) {
180181 return false;
182 }
181183 short opcode = exceptionThrower.getInstruction().getOpcode();
182184 return nullCheckInstructionSet.get(opcode);
183185 }
198200
199201 /**
200202 * Get the successor of given instruction within the basic block.
201 *
203 *
202204 * @param handle
203205 * the instruction
204206 * @return the instruction's successor, or null if the instruction is the
207209 public @CheckForNull
208210 InstructionHandle getSuccessorOf(InstructionHandle handle) {
209211 if (VERIFY_INTEGRITY) {
210 if (!containsInstruction(handle))
212 if (!containsInstruction(handle)) {
211213 throw new IllegalStateException();
214 }
212215 }
213216 return handle == lastInstruction ? null : handle.getNext();
214217 }
215218
216219 /**
217220 * Get the predecessor of given instruction within the basic block.
218 *
221 *
219222 * @param handle
220223 * the instruction
221224 * @return the instruction's predecessor, or null if the instruction is the
223226 */
224227 public InstructionHandle getPredecessorOf(InstructionHandle handle) {
225228 if (VERIFY_INTEGRITY) {
226 if (!containsInstruction(handle))
229 if (!containsInstruction(handle)) {
227230 throw new IllegalStateException();
231 }
228232 }
229233 return handle == firstInstruction ? null : handle.getPrev();
230234 }
231235
232236 /**
233237 * Add an InstructionHandle to the basic block.
234 *
238 *
235239 * @param handle
236240 * the InstructionHandle
237241 */
239243 if (firstInstruction == null) {
240244 firstInstruction = lastInstruction = handle;
241245 } else {
242 if (VERIFY_INTEGRITY && handle != lastInstruction.getNext())
246 if (VERIFY_INTEGRITY && handle != lastInstruction.getNext()) {
243247 throw new IllegalStateException("Adding non-consecutive instruction");
248 }
244249 lastInstruction = handle;
245250 }
246251 }
252257 * versa.
253258 */
254259 public class InstructionIterator implements Iterator<InstructionHandle> {
255 private InstructionHandle next, last;
260 private InstructionHandle next;
261 private final InstructionHandle last;
256262
257263 public InstructionIterator(InstructionHandle first, InstructionHandle last) {
258264 this.next = first;
259265 this.last = last;
260266 }
261267
268 @Override
262269 public boolean hasNext() {
263270 return next != null;
264271 }
265272
273 @Override
266274 public InstructionHandle next() {
267 if (!hasNext())
275 if (!hasNext()) {
268276 throw new NoSuchElementException();
277 }
269278 InstructionHandle result = next;
270279 next = (result == last) ? null : next.getNext();
271280 return result;
272281 }
273282
283 @Override
274284 public void remove() {
275285 throw new UnsupportedOperationException();
276286 }
281291
282292 @Override
283293 public boolean equals(Object o) {
284 if (!(o instanceof InstructionIterator))
294 if (!(o instanceof InstructionIterator)) {
285295 return false;
296 }
286297 InstructionIterator other = (InstructionIterator) o;
287298 return this.next == other.next && this.last == other.last;
288299 }
290301 @Override
291302 public int hashCode() {
292303 int code = getBasicBlock().hashCode() * 227;
293 if (next != null)
304 if (next != null) {
294305 code += next.getPosition() + 1;
306 }
295307 return code;
296308 }
297309
327339 * A reverse Iterator over the instructions in a basic block.
328340 */
329341 private static class InstructionReverseIterator implements Iterator<InstructionHandle> {
330 private InstructionHandle next, first;
342 private InstructionHandle next;
343 private final InstructionHandle first;
331344
332345 public InstructionReverseIterator(InstructionHandle last, InstructionHandle first) {
333346 this.next = last;
334347 this.first = first;
335348 }
336349
350 @Override
337351 public boolean hasNext() {
338352 return next != null;
339353 }
340354
355 @Override
341356 public InstructionHandle next() throws NoSuchElementException {
342 if (!hasNext())
357 if (!hasNext()) {
343358 throw new NoSuchElementException();
359 }
344360 InstructionHandle result = next;
345361 next = (result == first) ? null : next.getPrev();
346362 return result;
347363 }
348364
365 @Override
349366 public void remove() {
350367 throw new UnsupportedOperationException();
351368 }
368385
369386 public int pos() {
370387
371 if (isEmpty())
388 if (isEmpty()) {
372389 return getExceptionThrower().getPosition();
390 }
373391 return firstInstruction.getPosition();
374392 }
375393
383401 /**
384402 * Get CodeExceptionGen object; returns null if this basic block is not the
385403 * entry point of an exception handler.
386 *
404 *
387405 * @return the CodeExceptionGen object, or null
388406 */
389407 public CodeExceptionGen getExceptionGen() {
393411 /**
394412 * Set the CodeExceptionGen object. Marks this basic block as the entry
395413 * point of an exception handler.
396 *
414 *
397415 * @param exceptionGen
398416 * the CodeExceptionGen object for the block
399417 */
400 public void setExceptionGen(TypeMerger m, CodeExceptionGen exceptionGen) {
418 public void setExceptionGen(@Nullable TypeMerger m, CodeExceptionGen exceptionGen) {
401419 if (this.exceptionGen != null) {
402420 AnalysisContext.logError("Multiple exception handlers");
403421 }
404
405 this.exceptionGen = exceptionGen;
406 }
407
408 /**
422
423 this.exceptionGen = exceptionGen;
424 }
425
426 /**
409427 * Return whether or not the basic block contains the given instruction.
410 *
428 *
411429 * @param handle
412430 * the instruction
413431 * @return true if the block contains the instruction, false otherwise
415433 public boolean containsInstruction(InstructionHandle handle) {
416434 Iterator<InstructionHandle> i = instructionIterator();
417435 while (i.hasNext()) {
418 if (i.next() == handle)
436 if (i.next() == handle) {
419437 return true;
438 }
420439 }
421440 return false;
422441 }
424443 /**
425444 * Return whether or not the basic block contains the instruction with the
426445 * given bytecode offset.
427 *
446 *
428447 * @param offset
429448 * the bytecode offset
430449 * @return true if the block contains an instruction with the given offset,
433452 public boolean containsInstructionWithOffset(int offset) {
434453 Iterator<InstructionHandle> i = instructionIterator();
435454 while (i.hasNext()) {
436 if (i.next().getPosition() == offset)
455 if (i.next().getPosition() == offset) {
437456 return true;
457 }
438458 }
439459 return false;
440460 }
455475 }
456476 }
457477
458 // vim:ts=4
4444 import org.apache.bcel.generic.GETFIELD;
4545 import org.apache.bcel.generic.GETSTATIC;
4646 import org.apache.bcel.generic.GOTO;
47 import org.apache.bcel.generic.GotoInstruction;
4748 import org.apache.bcel.generic.ICONST;
4849 import org.apache.bcel.generic.IFNONNULL;
4950 import org.apache.bcel.generic.IFNULL;
186187
187188 private final BitSet instructionSet;
188189
189 private final CFG cfg;
190 private final CFG cfgSub;
190191
191192 private final IdentityHashMap<InstructionHandle, BasicBlock> blockMap;
192193
209210 public Subroutine(InstructionHandle start) {
210211 this.start = start;
211212 this.instructionSet = new BitSet();
212 this.cfg = new CFG();
213 this.cfgSub = new CFG();
213214 this.blockMap = new IdentityHashMap<InstructionHandle, BasicBlock>();
214215 this.escapeTargetListMap = new IdentityHashMap<BasicBlock, List<EscapeTarget>>();
215216 this.returnBlockSet = new BitSet();
229230 * Allocate a new basic block in the subroutine.
230231 */
231232 public BasicBlock allocateBasicBlock() {
232 return cfg.allocate();
233 return cfgSub.allocate();
233234 }
234235
235236 /**
257258 * Get the entry block for the subroutine's CFG.
258259 */
259260 public BasicBlock getEntry() {
260 return cfg.getEntry();
261 return cfgSub.getEntry();
261262 }
262263
263264 /**
264265 * Get the exit block for the subroutine's CFG.
265266 */
266267 public BasicBlock getExit() {
267 return cfg.getExit();
268 return cfgSub.getExit();
268269 }
269270
270271 /**
279280 * Get the subroutine's CFG.
280281 */
281282 public CFG getCFG() {
282 return cfg;
283 return cfgSub;
283284 }
284285
285286 /**
292293 */
293294 public void addInstruction(InstructionHandle handle) throws CFGBuilderException {
294295 int position = handle.getPosition();
295 if (usedInstructionSet.get(position))
296 if (usedInstructionSet.get(position)) {
296297 throw new CFGBuilderException("Instruction " + handle + " visited in multiple subroutines");
298 }
297299 instructionSet.set(position);
298300 usedInstructionSet.set(position);
299301 }
323325
324326 // Block is an exception handler?
325327 CodeExceptionGen exceptionGen = exceptionHandlerMap.getHandlerForStartInstruction(start);
326 if (exceptionGen != null)
328 if (exceptionGen != null) {
327329 block.setExceptionGen(null, exceptionGen);
330 }
328331
329332 addItem(new WorkListItem(start, block));
330333 }
424427 */
425428 public void addEdge(BasicBlock sourceBlock, BasicBlock destBlock, @Edge.Type int edgeType) {
426429 if (VERIFY_INTEGRITY) {
427 if (destBlock.isExceptionHandler() && edgeType != HANDLED_EXCEPTION_EDGE)
430 if (destBlock.isExceptionHandler() && edgeType != HANDLED_EXCEPTION_EDGE) {
428431 throw new IllegalStateException("In method " + SignatureConverter.convertMethodSignature(methodGen)
429432 + ": exception handler " + destBlock.getFirstInstruction() + " reachable by non exception edge type "
430433 + edgeType);
431 }
432 cfg.createEdge(sourceBlock, destBlock, edgeType);
434 }
435 }
436 cfgSub.createEdge(sourceBlock, destBlock, edgeType);
433437 }
434438
435439 /**
441445 */
442446 public Iterator<EscapeTarget> escapeTargetIterator(BasicBlock sourceBlock) {
443447 List<EscapeTarget> escapeTargetList = escapeTargetListMap.get(sourceBlock);
444 if (escapeTargetList == null)
448 if (escapeTargetList == null) {
445449 escapeTargetList = Collections.emptyList();
450 }
446451 return escapeTargetList.iterator();
447452 }
448453 }
504509
505510 /**
506511 * Add a basic block to the inlining work list.
507 */
512 *
508513 public void addItem(BasicBlock item) {
509514 workList.add(item);
510 }
515 }*/
511516
512517 /**
513518 * Are there more work list items?
561566 Context callerContext = caller;
562567
563568 while (callerContext != null) {
564 if (callerContext.subroutine == this.subroutine)
569 if (callerContext.subroutine == this.subroutine) {
565570 throw new CFGBuilderException("JSR recursion detected!");
571 }
566572 callerContext = callerContext.caller;
567573 }
568574 }
616622 } catch (CheckedAnalysisException e) {
617623 AnalysisContext.logError("Unable to generate exceptionSetFactory for " + descriptor, e);
618624 }
619
620
625
626
621627 this.exceptionHandlerMap = new ExceptionHandlerMap(methodGen, merger);
622628 this.usedInstructionSet = new BitSet();
623629 this.jsrSubroutineMap = new IdentityHashMap<InstructionHandle, Subroutine>();
626632
627633 public int getIndex(FieldDescriptor f) {
628634 Integer i = addedFields.get(f);
629 if (i != null)
635 if (i != null) {
630636 return i;
637 }
631638 int index = cpg.addFieldref(f.getSlashedClassName(), f.getName(), f.getSignature());
632639 addedFields.put(f, index);
633640 return index;
649656 FieldDescriptor field = invoked.getAccessMethodForField();
650657 if (field != null) {
651658 boolean isSetter = signature.endsWith("V");
652 Instruction replacement;
653 int index = getIndex(field);
654 if (field.isStatic()) {
655 if (isSetter)
656 replacement = new PUTSTATIC(index);
657 else
658 replacement = new GETSTATIC(index);
659 Instruction replacement;
660 int index = getIndex(field);
661 if (field.isStatic()) {
662 if (isSetter) {
663 replacement = new PUTSTATIC(index);
659664 } else {
660 if (isSetter)
661 replacement = new PUTFIELD(index);
662 else
663 replacement = new GETFIELD(index);
665 replacement = new GETSTATIC(index);
664666 }
665 head.swapInstruction(replacement);
667 } else {
668 if (isSetter) {
669 replacement = new PUTFIELD(index);
670 } else {
671 replacement = new GETFIELD(index);
672 }
673 }
674 head.swapInstruction(replacement);
675 /*
666676 if (false)
667677 System.out.println("Substituting " + (isSetter ? "set" : "get") + " of " + field + " for call of "
668678 + invoked + " in " + methodGen.getClassName() + "." + methodGen.getName()
669679 + methodGen.getSignature());
680 */
670681
671682 }
672683
678689 InstructionHandle next = head.getNext();
679690 if (target.equals(next)) {
680691 int consumed = ii.consumeStack(methodGen.getConstantPool());
681 if (consumed != 1 && consumed != 2)
692 if (consumed != 1 && consumed != 2) {
682693 throw new IllegalStateException();
694 }
683695 head.swapInstruction(consumed == 1 ? new POP() : new POP2());
684696 }
685697
688700 IfInstruction ii = (IfInstruction) i;
689701 InstructionHandle target = ii.getTarget();
690702 InstructionHandle next1 = head.getNext(); // ICONST
691 if (next1 == null) break;
703 if (next1 == null) {
704 break;
705 }
692706 if (next1.getInstruction() instanceof ICONST) {
693707 InstructionHandle next2 = next1.getNext(); // GOTO
694 if (next2 == null) break;
708 if (next2 == null) {
709 break;
710 }
695711 InstructionHandle next3 = next2.getNext(); // ICONST
696 if (next3== null) break;
712 if (next3== null) {
713 break;
714 }
697715 InstructionHandle next4 = next3.getNext();
698 if (next4 == null) break;
716 if (next4 == null) {
717 break;
718 }
699719 if (target.equals(next3) && next2.getInstruction() instanceof GOTO
700720 && next3.getInstruction() instanceof ICONST && next1.getTargeters().length == 0
701721 && next2.getTargeters().length == 0 && next3.getTargeters().length == 1
734754 // need to update
735755 head.swapInstruction(new NOP());
736756 IfInstruction ifTest = (IfInstruction) check;
737 if (check instanceof IF_ACMPNE)
757 if (check instanceof IF_ACMPNE) {
738758 next2.swapInstruction(new IFNONNULL(ifTest.getTarget()));
739 else
759 } else {
740760 next2.swapInstruction(new IFNULL(ifTest.getTarget()));
761 }
741762 }
742763 }
743764 }
744765 head = head.getNext();
745766 }
746
747 }
748
767 }
768
769 @Override
749770 public void build() throws CFGBuilderException {
750771 InstructionList instructionList = methodGen.getInstructionList();
751772 optimize(instructionList);
755776 // Build top level subroutine and all JSR subroutines
756777 while (!subroutineWorkList.isEmpty()) {
757778 Subroutine subroutine = subroutineWorkList.removeFirst();
758 if (DEBUG)
779 if (DEBUG) {
759780 System.out.println("Starting subroutine " + subroutine.getStartInstruction());
781 }
760782 build(subroutine);
761783 }
762784
770792 InstructionList il = new InstructionList();
771793 entryBlock.addInstruction(il.append(new NOP()));
772794
773 if (VERIFY_INTEGRITY)
795 if (VERIFY_INTEGRITY) {
774796 cfg.checkIntegrity();
797 }
775798
776799 if (true) {
777800 cfg.checkIntegrity();
779802 }
780803 }
781804
805 @Override
782806 public CFG getCFG() {
783807 return cfg;
784808 }
812836 // Add exception handler block (ETB) for exception-throwing
813837 // instructions
814838 if (isPEI(handle)) {
815 if (DEBUG)
839 if (DEBUG) {
816840 System.out.println("ETB block " + basicBlock.getLabel() + " for " + handle);
841 }
817842 handleExceptions(subroutine, handle, basicBlock);
818843 BasicBlock body = subroutine.allocateBasicBlock();
819844 subroutine.addEdge(basicBlock, body, FALL_THROUGH_EDGE);
820845 basicBlock = body;
821846 }
822847
823 if (DEBUG)
848 if (DEBUG) {
824849 System.out.println("BODY block " + basicBlock.getLabel() + " for " + handle);
825
826 if (!basicBlock.isEmpty())
850 }
851
852 if (!basicBlock.isEmpty()) {
827853 throw new IllegalStateException("Block isn't empty!");
854 }
828855
829856 // Add instructions until we get to the end of the block
830857 boolean endOfBasicBlock = false;
832859 Instruction ins = handle.getInstruction();
833860
834861 // Add the instruction to the block
835 if (DEBUG)
862 if (DEBUG) {
836863 System.out.println("BB " + basicBlock.getLabel() + ": adding" + handle);
864 }
837865 basicBlock.addInstruction(handle);
838866 subroutine.addInstruction(handle);
839867
890918
891919 if (!endOfBasicBlock) {
892920 InstructionHandle next = handle.getNext();
893 if (next == null)
921 if (next == null) {
894922 throw new CFGBuilderException("Control falls off end of method: " + handle);
923 }
895924
896925 // Is the next instruction a control merge or a PEI?
897926 if (isMerge(next) || isPEI(next)) {
931960 InstructionHandle handlerStart = exceptionHandler.getHandlerPC();
932961 subroutine.addEdgeAndExplore(etb, handlerStart, HANDLED_EXCEPTION_EDGE);
933962
934 if (Hierarchy.isUniversalExceptionHandler(exceptionHandler.getCatchType()))
963 if (Hierarchy.isUniversalExceptionHandler(exceptionHandler.getCatchType())) {
935964 sawUniversalExceptionHandler = true;
965 }
936966 }
937967 }
938968
941971 // ANY exception type, then the exception can be thrown out of the
942972 // method.
943973 if (!sawUniversalExceptionHandler) {
944 if (DEBUG)
974 if (DEBUG) {
945975 System.out.println("Adding unhandled exception edge from " + pei);
976 }
946977 subroutine.setUnhandledExceptionBlock(etb);
947978 }
948979 }
953984 * @param handle
954985 * the instruction
955986 * @return true if the instruction can throw an exception, false otherwise
956 */
957 private boolean isPEI(InstructionHandle handle) {
987 * @throws CFGBuilderException
988 */
989 private boolean isPEI(InstructionHandle handle) throws CFGBuilderException {
958990 Instruction ins = handle.getInstruction();
959991
960 if (!(ins instanceof ExceptionThrower))
992 if (!(ins instanceof ExceptionThrower)) {
961993 return false;
962
963 if (ins instanceof NEW)
994 }
995
996 if (ins instanceof NEW) {
964997 return false;
998 }
965999 // if (ins instanceof ATHROW) return false;
966 if (ins instanceof GETSTATIC)
1000 if (ins instanceof GETSTATIC) {
9671001 return false;
968 if (ins instanceof PUTSTATIC)
1002 }
1003 if (ins instanceof PUTSTATIC) {
9691004 return false;
970 if (ins instanceof ReturnInstruction)
1005 }
1006 if (ins instanceof ReturnInstruction) {
9711007 return false;
972 if (ins instanceof INSTANCEOF)
1008 }
1009 if (ins instanceof INSTANCEOF) {
9731010 return false;
974 if (ins instanceof MONITOREXIT)
1011 }
1012 if (ins instanceof MONITOREXIT) {
9751013 return false;
976 if (ins instanceof LDC)
1014 }
1015 if (ins instanceof LDC) {
9771016 return false;
1017 }
1018 if (ins instanceof GETFIELD && !methodGen.isStatic()) {
1019 // Assume that GETFIELD on this object is not PEI
1020 return !isSafeFieldSource(handle.getPrev());
1021 }
1022 if (ins instanceof PUTFIELD && !methodGen.isStatic()) {
1023 // Assume that PUTFIELD on this object is not PEI
1024 int depth = ins.consumeStack(cpg);
1025 for(InstructionHandle prev = handle.getPrev(); prev != null; prev = prev.getPrev()) {
1026 Instruction prevInst = prev.getInstruction();
1027 if(prevInst instanceof BranchInstruction) {
1028 if(prevInst instanceof GotoInstruction) {
1029 // Currently we support only jumps to the PUTFIELD itself
1030 // This will cover simple cases like this.a = flag ? foo : bar
1031 if(((BranchInstruction) prevInst).getTarget() == handle) {
1032 depth = ins.consumeStack(cpg);
1033 } else {
1034 return true;
1035 }
1036 } else if (!(prevInst instanceof IfInstruction)) {
1037 // As IF instructions may fall through then the stack depth remains unchanged
1038 // Actually we should not go here for normal Java bytecode: switch or jsr should not appear in this context
1039 return true;
1040 }
1041 }
1042 depth = depth - prevInst.produceStack(cpg) + prevInst.consumeStack(cpg);
1043 if(depth < 1) {
1044 throw new CFGBuilderException("Invalid stack at "+prev+" when checking "+handle);
1045 }
1046 if(depth == 1) {
1047 InstructionHandle prevPrev = prev.getPrev();
1048 if(prevPrev != null && prevPrev.getInstruction() instanceof BranchInstruction) {
1049 continue;
1050 }
1051 return !isSafeFieldSource(prevPrev);
1052 }
1053 }
1054 }
9781055 return true;
979
1056 }
1057
1058 /**
1059 * @param handle instruction handle which loads the object for further GETFIELD/PUTFIELD operation
1060 * @return true if this object is known to be non-null
1061 */
1062 private boolean isSafeFieldSource(InstructionHandle handle) {
1063 while(handle != null && handle.getInstruction().getOpcode() == Constants.DUP) {
1064 // Some compilers generate DUP for field increment code like
1065 // ALOAD_0 / DUP / GETFIELD x / ICONST_1 / IADD / PUTFIELD x
1066 handle = handle.getPrev();
1067 }
1068 if(handle == null) {
1069 return false;
1070 }
1071 Instruction inst = handle.getInstruction();
1072 if(inst.getOpcode() == Constants.ALOAD_0) {
1073 return true;
1074 }
1075 if(inst instanceof GETFIELD && ((GETFIELD)inst).getFieldName(cpg).startsWith("this$")) {
1076 return true;
1077 }
1078 return false;
9801079 }
9811080
9821081 /**
9921091 // of them are branches. If so, the instruction is a merge.
9931092 InstructionTargeter[] targeterList = handle.getTargeters();
9941093 for (InstructionTargeter targeter : targeterList) {
995 if (targeter instanceof BranchInstruction)
1094 if (targeter instanceof BranchInstruction) {
9961095 return true;
1096 }
9971097 }
9981098 }
9991099 return false;
10531153 }
10541154
10551155 // Set exception thrower status
1056 if (subBlock.isExceptionThrower())
1156 if (subBlock.isExceptionThrower()) {
10571157 resultBlock.setExceptionThrower(subBlock.getExceptionThrower());
1158 }
10581159
10591160 // Set exception handler status
1060 if (subBlock.isExceptionHandler())
1161 if (subBlock.isExceptionHandler()) {
10611162 resultBlock.setExceptionGen(null, subBlock.getExceptionGen());
1163 }
10621164
10631165 // Add control edges (including inlining JSR subroutines)
10641166 Iterator<Edge> edgeIter = subCFG.outgoingEdgeIterator(subBlock);
11081210 // Look for the calling context which has the target instruction
11091211 Context caller = context.getCaller();
11101212 while (caller != null) {
1111 if (caller.getSubroutine().containsInstruction(targetInstruction))
1213 if (caller.getSubroutine().containsInstruction(targetInstruction)) {
11121214 break;
1215 }
11131216 caller = caller.getCaller();
11141217 }
11151218
1116 if (caller == null)
1219 if (caller == null) {
11171220 throw new CFGBuilderException("Unknown caller for escape target " + targetInstruction + " referenced by "
11181221 + context.getSubroutine().getStartInstruction());
1222 }
11191223
11201224 // Find result block in caller
11211225 BasicBlock subCallerTargetBlock = caller.getSubroutine().getBlock(targetInstruction);
11881292
11891293 Method[] methodList = jclass.getMethods();
11901294 for (Method method : methodList) {
1191 if (method.isAbstract() || method.isNative())
1295 if (method.isAbstract() || method.isNative()) {
11921296 continue;
1193
1194 if (methodName != null && !method.getName().equals(methodName))
1297 }
1298
1299 if (methodName != null && !method.getName().equals(methodName)) {
11951300 continue;
1301 }
11961302
11971303 MethodDescriptor descriptor = DescriptorFactory.instance().getMethodDescriptor(jclass, method);
11981304 MethodGen methodGen = new MethodGen(method, jclass.getClassName(), classGen.getConstantPool());
12111317 }
12121318
12131319 }
1214
1215 // vim:ts=4
2828 * Return an Iterator which visits the basic blocks in order.
2929 */
3030 public Iterator<BasicBlock> blockIterator();
31
31
3232 /** Return relative order of blocks.
3333 * If b1.compareTo(b2) < 0, then b1 should occur before b2 in iteration.
3434 */
3535 public int compare(BasicBlock b1, BasicBlock b2);
3636 }
3737
38 // vim:ts=4
2626 * block, and any catch block with an empty catch type (i.e., catch all
2727 * exceptions) is a finally block. This assumption isn't quite accurate, but it
2828 * seems to be a reasonable first approximation.
29 *
29 *
3030 * <p>
3131 * If valid (isValid() returns true), a BlockType value is a stack of elements,
3232 * which are either CATCH or FINALLY values. Call getDepth() to get the current
3333 * nesting depth. Call get(int <i>n</i>) to get the <i>n</i>th stack item. Call
3434 * getTopValue() to get the current top of the stack.
3535 * </p>
36 *
36 *
3737 * <p>
3838 * If invalid (isValid() returns false), a BlockType value is either <i>top</i>
3939 * or <i>bottom</i>. These are the special values at the top and bottom of the
4040 * dataflow lattice.
4141 * </p>
42 *
42 *
4343 * <p>
4444 * The dataflow lattice is effectively finite-height because real Java methods
4545 * are guaranteed to have a finite catch and finally block nesting level.
4646 * </p>
47 *
47 *
4848 * @see BlockTypeAnalysis
4949 * @author David Hovemeyer
5050 */
8282
8383 @Override
8484 public boolean equals(Object obj) {
85 if (this == obj)
85 if (this == obj) {
8686 return true;
87 if (!super.equals(obj))
88 return false;
89 if (!(obj instanceof BlockType))
90 return false;
87 }
88 if (!super.equals(obj)) {
89 return false;
90 }
91 if (!(obj instanceof BlockType)) {
92 return false;
93 }
9194 final BlockType other = (BlockType) obj;
92 if (depth != other.depth)
93 return false;
94 if (isTop != other.isTop)
95 return false;
96 if (isValid != other.isValid)
97 return false;
95 if (depth != other.depth) {
96 return false;
97 }
98 if (isTop != other.isTop) {
99 return false;
100 }
101 if (isValid != other.isValid) {
102 return false;
103 }
98104 return true;
99105 }
100106
110116 * Get the current nesting depth. The value must be valid.
111117 */
112118 public int getDepth() {
113 if (!isValid)
119 if (!isValid) {
114120 throw new IllegalStateException();
121 }
115122 return depth;
116123 }
117124
119126 * Get the top value on the catch and finally block nesting stack.
120127 */
121128 public boolean getTopValue() {
122 if (depth == 0)
129 if (depth == 0) {
123130 throw new IllegalStateException();
131 }
124132 return get(depth - 1);
125133 }
126134
129137 * control flow are all blocks outside any catch or finally block.
130138 */
131139 public boolean isNormal() {
132 if (!isValid)
140 if (!isValid) {
133141 throw new IllegalStateException();
142 }
134143 return getDepth() == 0;
135144 }
136145
174183
175184 /**
176185 * Make this object an exact duplicate of given object.
177 *
186 *
178187 * @param other
179188 * the other BlockType object
180189 */
190199
191200 /**
192201 * Return whether or not this object is identical to the one given.
193 *
202 *
194203 * @param other
195204 * the other BlockType object
196205 * @return true if this object is identical to the one given, false
200209 if (!this.isValid) {
201210 return !other.isValid && (this.isTop == other.isTop);
202211 } else {
203 if (!other.isValid)
212 if (!other.isValid) {
204213 return false;
205 else {
214 } else {
206215 // Both facts are valid
207 if (this.depth != other.depth)
216 if (this.depth != other.depth) {
208217 return false;
218 }
209219
210220 // Compare bits
211221 for (int i = 0; i < this.depth; ++i) {
212 if (this.get(i) != other.get(i))
222 if (this.get(i) != other.get(i)) {
213223 return false;
224 }
214225 }
215226
216227 return true;
220231
221232 /**
222233 * Merge other dataflow value into this value.
223 *
234 *
224235 * @param other
225236 * the other BlockType value
226237 */
232243 int pfxLen = Math.min(this.depth, other.depth);
233244 int commonLen;
234245 for (commonLen = 0; commonLen < pfxLen; ++commonLen) {
235 if (this.get(commonLen) != other.get(commonLen))
246 if (this.get(commonLen) != other.get(commonLen)) {
236247 break;
248 }
237249 }
238250 this.depth = commonLen;
239251 }
255267
256268 @Override
257269 public String toString() {
258 if (isTop())
270 if (isTop()) {
259271 return "<top>";
260 else if (isBottom())
272 } else if (isBottom()) {
261273 return "<bottom>";
262 else {
274 } else {
263275 StringBuilder buf = new StringBuilder();
264276 buf.append("N");
265277 for (int i = 0; i < depth; ++i) {
275287 }
276288 }
277289
278 // vim:ts=4
2929 /**
3030 * Dataflow analysis to determine the nesting of catch and finally blocks within
3131 * a method.
32 *
32 *
3333 * @see BlockType
3434 * @author David Hovemeyer
3535 */
3636 public class BlockTypeAnalysis extends BasicAbstractDataflowAnalysis<BlockType> {
37 private DepthFirstSearch dfs;
37 private final DepthFirstSearch dfs;
3838
3939 /**
4040 * Constructor.
41 *
41 *
4242 * @param dfs
4343 * a DepthFirstSearch for the method to be analyzed
4444 */
4646 this.dfs = dfs;
4747 }
4848
49 @Override
4950 public BlockType createFact() {
5051 return new BlockType();
5152 }
5253
54 @Override
5355 public void copy(BlockType source, BlockType dest) {
5456 dest.copyFrom(source);
5557 }
5658
59 @Override
5760 public void initEntryFact(BlockType result) throws DataflowAnalysisException {
5861 result.setNormal();
5962 }
6063
64 @Override
6165 public void makeFactTop(BlockType fact) {
6266 fact.setTop();
6367 }
6468
69 @Override
6570 public boolean isTop(BlockType fact) {
6671 return fact.isTop();
6772 }
6873
74 @Override
6975 public boolean isForwards() {
7076 return true;
7177 }
7278
79 @Override
7380 public BlockOrder getBlockOrder(CFG cfg) {
7481 return new ReversePostOrder(cfg, dfs);
7582 }
7683
84 @Override
7785 public boolean same(BlockType fact1, BlockType fact2) {
7886 return fact1.sameAs(fact2);
7987 }
8088
89 @Override
8190 public void transfer(BasicBlock basicBlock, @CheckForNull InstructionHandle end, BlockType start, BlockType result)
8291 throws DataflowAnalysisException {
8392 result.copyFrom(start);
99108 }
100109 }
101110
111 @Override
102112 public void meetInto(BlockType fact, Edge edge, BlockType result) throws DataflowAnalysisException {
103113 result.mergeWith(fact);
104114 }
129139 // }
130140 }
131141
132 // vim:ts=4
2323 /**
2424 * Scan the raw bytecodes of a method. This is useful in order to find out
2525 * quickly whether or not a method uses particular instructions.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class BytecodeScanner implements org.apache.bcel.Constants {
3535 public interface Callback {
3636 /**
3737 * Called to indicate that a particular bytecode has been scanned.
38 *
38 *
3939 * @param opcode
4040 * the opcode of the instruction
4141 * @param index
4646
4747 /**
4848 * Convert the unsigned value of a byte into a short.
49 *
49 *
5050 * @param value
5151 * the byte
5252 * @return the byte's unsigned value as a short
6464
6565 /**
6666 * Extract an int from bytes at the given offset in the array.
67 *
67 *
6868 * @param arr
6969 * the array
7070 * @param offset
7979
8080 /**
8181 * Scan the raw bytecodes of a method.
82 *
82 *
8383 * @param instructionList
8484 * the bytecodes
8585 * @param callback
9393 short opcode = unsignedValueOf(instructionList[index]);
9494 callback.handleInstruction(opcode, index);
9595
96 if (DEBUG)
96 if (DEBUG) {
9797 System.out.println(index + ": " + OPCODE_NAMES[opcode]);
98 }
9899
99100 switch (opcode) {
100101
249250 ++index;
250251 break;
251252
252 // Two byte instructions.
253 // Two byte instructions.
253254 case BIPUSH:
254255 case LDC:
255256 case NEWARRAY:
256257 index += 2;
257258 break;
258259
259 // Instructions that can be used with the WIDE prefix.
260 // Instructions that can be used with the WIDE prefix.
260261 case ILOAD:
261262 case LLOAD:
262263 case FLOAD:
278279 }
279280 break;
280281
281 // IINC is a special case for WIDE handling
282 // IINC is a special case for WIDE handling
282283 case IINC:
283284 if (wide) {
284285 // Skip opcode, two byte index, and two byte immediate
291292 }
292293 break;
293294
294 // Three byte instructions.
295 // Three byte instructions.
295296 case SIPUSH:
296297 case LDC_W:
297298 case LDC2_W:
327328 index += 3;
328329 break;
329330
330 // Four byte instructions.
331 // Four byte instructions.
331332 case MULTIANEWARRAY:
332333 index += 4;
333334 break;
334335
335 // Five byte instructions.
336 // Five byte instructions.
336337 case INVOKEINTERFACE:
338 case INVOKEDYNAMIC:
337339 case GOTO_W:
338340 case JSR_W:
339341 index += 5;
340342 break;
341343
342 // TABLESWITCH - variable length.
344 // TABLESWITCH - variable length.
343345 case TABLESWITCH: {
344346 // Skip padding.
345347 int offset = index + 1; // skip the opcode
352354 int low = extractInt(instructionList, offset + 4);
353355 int high = extractInt(instructionList, offset + 8);
354356 int tableSize = (high - low) + 1;
355 if (DEBUG)
357 if (DEBUG) {
356358 System.out.println("tableswitch: low=" + low + ", high=" + high + ", tableSize=" + tableSize);
359 }
357360
358361 // Skip to next instruction.
359362 index = offset + 12 + (tableSize * 4);
360363 }
361 break;
364 break;
362365
363366 // LOOKUPSWITCH - variable length.
364367 case LOOKUPSWITCH: {
371374
372375 // Extract number of value/offset pairs.
373376 int numPairs = extractInt(instructionList, offset + 4);
374 if (DEBUG)
377 if (DEBUG) {
375378 System.out.println("lookupswitch: numPairs=" + numPairs);
379 }
376380
377381 // Skip to next instruction.
378382 index = offset + 8 + (numPairs * 8);
379383 }
380 break;
384 break;
381385
382386 // Wide prefix.
383387 case WIDE:
389393 throw new IllegalArgumentException("Bad opcode " + opcode + " at offset " + index);
390394 }
391395
392 if (index < 0)
396 if (index < 0) {
393397 throw new IllegalStateException("index=" + index + ", opcode=" + opcode);
398 }
394399
395400 }
396401 }
397402 }
398403
399 // vim:ts=4
3636
3737 /**
3838 * Simple control flow graph abstraction for BCEL.
39 *
39 *
4040 * @see BasicBlock
4141 * @see Edge
4242 */
8686 * both the instruction and the basic block.
8787 */
8888 private class LocationIterator implements Iterator<Location> {
89 private Iterator<BasicBlock> blockIter;
89 private final Iterator<BasicBlock> blockIter;
9090
9191 private BasicBlock curBlock;
9292
9999 findNext();
100100 }
101101
102 @Override
102103 public boolean hasNext() {
103104 findNext();
104105 return next != null;
105106 }
106107
108 @Override
107109 public Location next() {
108110 findNext();
109 if (next == null)
111 if (next == null) {
110112 throw new NoSuchElementException();
113 }
111114 Location result = next;
112115 next = null;
113116 return result;
114117 }
115118
119 @Override
116120 public void remove() {
117121 throw new UnsupportedOperationException();
118122 }
122126 // Make sure we have an instruction iterator
123127 if (instructionIter == null) {
124128 if (!blockIter.hasNext())
129 {
125130 return; // At end
131 }
126132 curBlock = blockIter.next();
127133 instructionIter = curBlock.instructionIterator();
128134 }
129135
130 if (instructionIter.hasNext())
136 if (instructionIter.hasNext()) {
131137 next = new Location(instructionIter.next(), curBlock);
132 else
138 }
139 else {
133140 instructionIter = null; // Go to next block
141 }
134142 }
135143 }
136144 }
234242 /**
235243 * Add a unique edge to the graph. There must be no other edge already in
236244 * the CFG with the same source and destination blocks.
237 *
245 *
238246 * @param source
239247 * the source basic block
240248 * @param dest
254262
255263 /**
256264 * Look up an Edge by its id.
257 *
265 *
258266 * @param id
259267 * the id of the edge to look up
260268 * @return the Edge, or null if no matching Edge was found
263271 Iterator<Edge> i = edgeIterator();
264272 while (i.hasNext()) {
265273 Edge edge = i.next();
266 if (edge.getId() == id)
274 if (edge.getId() == id) {
267275 return edge;
276 }
268277 }
269278 return null;
270279 }
271280
272281 /**
273282 * Look up a BasicBlock by its unique label.
274 *
283 *
275284 * @param blockLabel
276285 * the label of a BasicBlock
277286 * @return the BasicBlock with the given label, or null if there is no such
314323 */
315324 public Iterable<Location> locations() {
316325 return new Iterable<Location>() {
326 @Override
317327 public Iterator<Location> iterator() {
318328 return locationIterator();
319329 }
324334 * Returns a collection of locations, ordered according to the compareTo
325335 * ordering over locations. If you want to list all the locations in a CFG
326336 * for debugging purposes, this is a good order to do so in.
327 *
337 *
328338 * @return collection of locations
329339 */
330340 public Collection<Location> orderedLocations() {
338348
339349 /**
340350 * Get Collection of basic blocks whose IDs are specified by given BitSet.
341 *
351 *
342352 * @param labelSet
343353 * BitSet of block labels
344354 * @return a Collection containing the blocks whose IDs are given
347357 LinkedList<BasicBlock> result = new LinkedList<BasicBlock>();
348358 for (Iterator<BasicBlock> i = blockIterator(); i.hasNext();) {
349359 BasicBlock block = i.next();
350 if (labelSet.get(block.getLabel()))
360 if (labelSet.get(block.getLabel())) {
351361 result.add(block);
362 }
352363 }
353364 return result;
354365 }
356367 /**
357368 * Get a Collection of basic blocks which contain the bytecode instruction
358369 * with given offset.
359 *
370 *
360371 * @param offset
361372 * the bytecode offset of an instruction
362373 * @return Collection of BasicBlock objects which contain the instruction
366377 LinkedList<BasicBlock> result = new LinkedList<BasicBlock>();
367378 for (Iterator<BasicBlock> i = blockIterator(); i.hasNext();) {
368379 BasicBlock block = i.next();
369 if (block.containsInstructionWithOffset(offset))
380 if (block.containsInstructionWithOffset(offset)) {
370381 result.add(block);
382 }
371383 }
372384 return result;
373385 }
375387 /**
376388 * Get a Collection of Locations which specify the instruction at given
377389 * bytecode offset.
378 *
390 *
379391 * @param offset
380392 * the bytecode offset
381393 * @return all Locations referring to the instruction at that offset
393405
394406 /**
395407 * Get the first predecessor reachable from given edge type.
396 *
408 *
397409 * @param target
398410 * the target block
399411 * @param edgeType
408420
409421 /**
410422 * Get the first successor reachable from given edge type.
411 *
423 *
412424 * @param source
413425 * the source block
414426 * @param edgeType
424436 /**
425437 * Get the Location where exception(s) thrown on given exception edge are
426438 * thrown.
427 *
439 *
428440 * @param exceptionEdge
429441 * the exception Edge
430442 * @return Location where exception(s) are thrown from
431443 */
432444 public Location getExceptionThrowerLocation(Edge exceptionEdge) {
433 if (!exceptionEdge.isExceptionEdge())
445 if (!exceptionEdge.isExceptionEdge()) {
434446 throw new IllegalArgumentException();
447 }
435448
436449 InstructionHandle handle = exceptionEdge.getSource().getExceptionThrower();
437 if (handle == null)
450 if (handle == null) {
438451 throw new IllegalStateException();
452 }
439453
440454 BasicBlock basicBlock = (handle.getInstruction() instanceof ATHROW) ? exceptionEdge.getSource()
441455 : getSuccessorWithEdgeType(exceptionEdge.getSource(), EdgeTypes.FALL_THROUGH_EDGE);
464478
465479 /**
466480 * Get an Iterator over Edges removed from this CFG.
467 *
481 *
468482 * @return Iterator over Edges removed from this CFG
469483 */
470484 public Iterator<Edge> removedEdgeIterator() {
473487
474488 /**
475489 * Get the first incoming edge in basic block with given type.
476 *
490 *
477491 * @param basicBlock
478492 * the basic block
479493 * @param edgeType
486500
487501 /**
488502 * Get the first outgoing edge in basic block with given type.
489 *
503 *
490504 * @param basicBlock
491505 * the basic block
492506 * @param edgeType
500514 private Edge getEdgeWithType(Iterator<Edge> iter, @Type int edgeType) {
501515 while (iter.hasNext()) {
502516 Edge edge = iter.next();
503 if (edge.getType() == edgeType)
517 if (edge.getType() == edgeType) {
504518 return edge;
519 }
505520 }
506521 return null;
507522 }
539554 InstructionHandle prev = null;
540555 for (Iterator<InstructionHandle> j = basicBlock.instructionIterator(); j.hasNext();) {
541556 InstructionHandle handle = j.next();
542 if (prev != null && prev.getNext() != handle)
557 if (prev != null && prev.getNext() != handle) {
543558 throw new IllegalStateException("Non-consecutive instructions in block " + basicBlock.getLabel() + ": prev="
544559 + prev + ", handle=" + handle);
560 }
545561 prev = handle;
546562 }
547563 }
554570
555571 /*
556572 * (non-Javadoc)
557 *
573 *
558574 * @see
559575 * edu.umd.cs.findbugs.graph.AbstractGraph#removeEdge(edu.umd.cs.findbugs
560576 * .graph.AbstractEdge)
572588
573589 /**
574590 * Get number of non-exception control successors of given basic block.
575 *
591 *
576592 * @param block
577593 * a BasicBlock
578594 * @return number of non-exception control successors of the basic block
596612 * Get the Location representing the entry to the CFG. Note that this is a
597613 * "fake" Location, and shouldn't be relied on to yield source line
598614 * information.
599 *
615 *
600616 * @return Location at entry to CFG
601617 */
602618 public Location getLocationAtEntry() {
614630
615631 while (true) {
616632 prevBlock = getPredecessorWithEdgeType(prevBlock, EdgeTypes.FALL_THROUGH_EDGE);
617 if (prevBlock == null)
633 if (prevBlock == null) {
618634 return loc;
635 }
619636
620637 handle = prevBlock.getLastInstruction();
621 if (handle != null)
638 if (handle != null) {
622639 return new Location(handle, prevBlock);
640 }
623641 }
624642
625643 } else {
632650
633651 }
634652
635 // vim:ts=4
2020
2121 /**
2222 * Abstract interface for CFG builder classes.
23 *
23 *
2424 * @author David Hovemeyer
2525 * @see CFG
2626 * @see CFGBuilderFactory
3434 /**
3535 * Get the CFG built by this object. Assumes that the build() method has
3636 * already been called.
37 *
37 *
3838 * @return the CFG
3939 */
4040 public CFG getCFG();
4141 }
4242
43 // vim:ts=4
3030 * is preferable to direct instantiation of CFGBuilders, because it gives us an
3131 * easy hook for plugging in new CFGBuilder implementations. (CFGs for Java are
3232 * a little tricky to get right.)
33 *
33 *
3434 * @author David Hovemeyer
3535 * @see CFG
3636 * @see CFGBuilder
3939
4040 /**
4141 * Create a CFGBuilder to build a CFG for given method.
42 *
42 *
4343 * @param methodGen
4444 * the method
4545 * @return a CFGBuilder for the method
4949 }
5050 }
5151
52 // vim:ts=4
2626
2727 /**
2828 * Print out a representation of a control-flow graph. For debugging.
29 *
29 *
3030 * @see CFG
3131 * @see CFGBuilder
3232 */
3333 public class CFGPrinter {
34 private CFG cfg;
34 private final CFG cfg;
3535
3636 private boolean isForwards;
3737
9696 }
9797
9898 protected Iterator<InstructionHandle> instructionIterator(BasicBlock bb) {
99 if (isForwards)
99 if (isForwards) {
100100 return bb.instructionIterator();
101 else
101 } else {
102102 return bb.instructionReverseIterator();
103 }
103104 }
104105
105106 // public static void main(String[] argv) throws Exception {
146147 // }
147148 }
148149
149 // vim:ts=4
4141
4242 @Override
4343 public void loadAuxiliaryAnnotations() {
44 if (IGNORE_BUILTIN_ANNOTATIONS)
44 if (IGNORE_BUILTIN_ANNOTATIONS) {
4545 return;
46 }
4647 boolean missingClassWarningsSuppressed = AnalysisContext.currentAnalysisContext().setMissingClassWarningsSuppressed(true);
4748
4849 addMethodAnnotation("java.util.Iterator", "hasNext", "()Z", false, CheckReturnValueAnnotation.CHECK_RETURN_VALUE_LOW);
118119 addWarningAboutSubmit(ThreadPoolExecutor.class);
119120 addWarningAboutSubmit(ScheduledThreadPoolExecutor.class);
120121 addWarningAboutSubmit(AbstractExecutorService.class);
121
122
122123 addMethodAnnotation("java.util.concurrent.BlockingQueue", "poll", "(JLjava/util/concurrent/TimeUnit;)Ljava/lang/Object;",
123124 false, CheckReturnValueAnnotation.CHECK_RETURN_VALUE_MEDIUM);
124125 addMethodAnnotation("java.util.Queue", "poll", "()Ljava/lang/Object;", false,
145146 CheckReturnValueAnnotation.CHECK_RETURN_VALUE_IGNORE);
146147 addMethodAnnotation("java.math.BigDecimal", "precision", "()I", false,
147148 CheckReturnValueAnnotation.CHECK_RETURN_VALUE_MEDIUM);
148
149
149150 addMethodAnnotation("java.math.BigDecimal", "toBigIntegerExact", "()Ljava/math/BigInteger;", false,
150151 CheckReturnValueAnnotation.CHECK_RETURN_VALUE_IGNORE);
151152 addMethodAnnotation("java.math.BigDecimal", "longValueExact", "()J", false,
183184 CheckReturnValueAnnotation.CHECK_RETURN_VALUE_MEDIUM);
184185 addMethodAnnotation("java.lang.ProcessBuilder", "redirectErrorStream", "()Z", false,
185186 CheckReturnValueAnnotation.CHECK_RETURN_VALUE_MEDIUM);
186
187
187188 addDefaultMethodAnnotation("jsr166z.forkjoin.ParallelArray", CheckReturnValueAnnotation.CHECK_RETURN_VALUE_MEDIUM);
188189 addDefaultMethodAnnotation("jsr166z.forkjoin.ParallelLongArray", CheckReturnValueAnnotation.CHECK_RETURN_VALUE_MEDIUM);
189190 addDefaultMethodAnnotation("jsr166z.forkjoin.ParallelDoubleArray", CheckReturnValueAnnotation.CHECK_RETURN_VALUE_MEDIUM);
190
191
191
192
192193 addMethodAnnotation(java.sql.Statement.class, "executeQuery", "(Ljava/lang/String;)Ljava/sql/ResultSet;", false,
193194 CheckReturnValueAnnotation.CHECK_RETURN_VALUE_MEDIUM);
194195 addMethodAnnotation(java.sql.PreparedStatement.class, "executeQuery", "()Ljava/sql/ResultSet;", false,
224225
225226 @Override
226227 public CheckReturnValueAnnotation getResolvedAnnotation(Object o, boolean getMinimal) {
227 if (!(o instanceof XMethod))
228 if (!(o instanceof XMethod)) {
228229 return null;
230 }
229231 XMethod m = (XMethod) o;
230 if (m.getName().startsWith("access$"))
232 if (m.getName().startsWith("access$")) {
231233 return null;
232 else if (m.getName().equals("<init>")) {
234 } else if ("<init>".equals(m.getName())) {
233235 try {
234 if (throwableClass != null && Repository.instanceOf(m.getClassName(), throwableClass))
236 if (throwableClass != null && Repository.instanceOf(m.getClassName(), throwableClass)) {
235237 return CheckReturnValueAnnotation.CHECK_RETURN_VALUE_VERY_HIGH;
238 }
236239 } catch (ClassNotFoundException e) {
237240 AnalysisContext.reportMissingClass(e);
238241 }
239 if (m.getClassName().equals("java.lang.Thread"))
242 if ("java.lang.Thread".equals(m.getClassName())) {
240243 return CheckReturnValueAnnotation.CHECK_RETURN_VALUE_VERY_HIGH;
244 }
241245 try {
242246
243 if (threadClass != null && Repository.instanceOf(m.getClassName(), threadClass))
247 if (threadClass != null && Repository.instanceOf(m.getClassName(), threadClass)) {
244248 return CheckReturnValueAnnotation.CHECK_RETURN_VALUE_LOW;
249 }
245250 } catch (ClassNotFoundException e) {
246251 AnalysisContext.reportMissingClass(e);
247252 }
248 } else if (m.getName().equals("equals") && m.getSignature().equals("(Ljava/lang/Object;)Z") && !m.isStatic())
253 } else if ("equals".equals(m.getName()) && "(Ljava/lang/Object;)Z".equals(m.getSignature()) && !m.isStatic()) {
249254 return CheckReturnValueAnnotation.CHECK_RETURN_VALUE_MEDIUM;
250 else if (m.getSignature().endsWith(")Ljava/lang/String;")
251 && (m.getClassName().equals("java.lang.StringBuffer") || m.getClassName().equals("java.lang.StringBuilder")))
255 } else if (m.getSignature().endsWith(")Ljava/lang/String;")
256 && ("java.lang.StringBuffer".equals(m.getClassName()) || "java.lang.StringBuilder".equals(m.getClassName()))) {
252257 return CheckReturnValueAnnotation.CHECK_RETURN_VALUE_MEDIUM;
258 }
253259 return super.getResolvedAnnotation(o, getMinimal);
254260 }
255261
5454
5555 public final static CheckReturnValueAnnotation CHECK_RETURN_VALUE_MEDIUM_BAD_PRACTICE = new CheckReturnValueAnnotation(
5656 "CheckReturnValueMediumBadPractice", 7, "RV_RETURN_VALUE_IGNORED_BAD_PRACTICE", Detector.NORMAL_PRIORITY);
57
57
5858 public final static CheckReturnValueAnnotation CHECK_RETURN_VALUE_INFERRED = new CheckReturnValueAnnotation(
5959 "CheckReturnValueInferred", 8, "RV_RETURN_VALUE_IGNORED_INFERRED", Detector.NORMAL_PRIORITY);
6060
6161 private final static CheckReturnValueAnnotation[] myValues = { CHECK_RETURN_VALUE_UNKNOWN, CHECK_RETURN_VALUE_HIGH,
62 CHECK_RETURN_VALUE_MEDIUM, CHECK_RETURN_VALUE_LOW, CHECK_RETURN_VALUE_IGNORE, CHECK_RETURN_VALUE_VERY_HIGH,
63 CHECK_RETURN_VALUE_LOW_BAD_PRACTICE, CHECK_RETURN_VALUE_MEDIUM_BAD_PRACTICE, CHECK_RETURN_VALUE_INFERRED};
62 CHECK_RETURN_VALUE_MEDIUM, CHECK_RETURN_VALUE_LOW, CHECK_RETURN_VALUE_IGNORE, CHECK_RETURN_VALUE_VERY_HIGH,
63 CHECK_RETURN_VALUE_LOW_BAD_PRACTICE, CHECK_RETURN_VALUE_MEDIUM_BAD_PRACTICE, CHECK_RETURN_VALUE_INFERRED};
6464
6565 @CheckForNull
6666 public static CheckReturnValueAnnotation parse(String priority) {
67 if (priority == null)
67 if (priority == null) {
6868 return CHECK_RETURN_VALUE_MEDIUM;
69 if (priority.endsWith("HIGH"))
69 }
70 if (priority.endsWith("HIGH")) {
7071 return CHECK_RETURN_VALUE_HIGH;
71 if (priority.endsWith("MEDIUM"))
72 }
73 if (priority.endsWith("MEDIUM")) {
7274 return CHECK_RETURN_VALUE_MEDIUM;
73 if (priority.endsWith("LOW"))
75 }
76 if (priority.endsWith("LOW")) {
7477 return CHECK_RETURN_VALUE_LOW;
78 }
7579 throw new IllegalArgumentException("Bad priority: " + priority);
7680
7781 }
4545 import edu.umd.cs.findbugs.AnalysisLocal;
4646 import edu.umd.cs.findbugs.OpcodeStack.JumpInfo;
4747 import edu.umd.cs.findbugs.SystemProperties;
48 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
4849 import edu.umd.cs.findbugs.ba.ca.CallListDataflow;
4950 import edu.umd.cs.findbugs.ba.constant.ConstantDataflow;
5051 import edu.umd.cs.findbugs.ba.deref.UnconditionalValueDerefDataflow;
125126 public Map<MethodDescriptor, Object> getObjectMap(Class<?> analysisClass) {
126127 Map<MethodDescriptor, Object> objectMap = methodAnalysisObjectMap.get(analysisClass);
127128 if (objectMap == null) {
128 if (analysisClass == ValueNumberDataflow.class)
129 if (analysisClass == ValueNumberDataflow.class) {
129130 objectMap = new MapCache<MethodDescriptor, Object>(300);
130 else if (Dataflow.class.isAssignableFrom(analysisClass))
131 } else if (Dataflow.class.isAssignableFrom(analysisClass)) {
131132 objectMap = new MapCache<MethodDescriptor, Object>(500);
132 else
133 } else {
133134 objectMap = new HashMap<MethodDescriptor, Object>();
135 }
134136 methodAnalysisObjectMap.put(analysisClass, objectMap);
135137 }
136138 return objectMap;
247249 List<Method> methodsInCallOrder = new ArrayList<Method>(xmethodsInCallOrder.size());
248250 for (XMethod x : xmethodsInCallOrder) {
249251 Method m = map.get(x);
250 if (m != null)
252 if (m != null) {
251253 methodsInCallOrder.add(m);
254 }
252255 }
253256 return methodsInCallOrder;
254257 }
317320 * @return the UsagesRequiringNonNullValues
318321 */
319322 public UsagesRequiringNonNullValues getUsagesRequiringNonNullValues(Method method) throws DataflowAnalysisException,
320 CFGBuilderException {
323 CFGBuilderException {
321324 return getMethodAnalysis(UsagesRequiringNonNullValues.class, method);
322325 }
323326
376379 return getMethodAnalysisNoDataflowAnalysisException(ReverseDepthFirstSearch.class, method);
377380 }
378381
379 static final AnalysisLocal<MapCache<XMethod, BitSet>> cachedBitsets_AL
380 = new AnalysisLocal<MapCache<XMethod, BitSet>>() {
382 static final AnalysisLocal<MapCache<XMethod, BitSet>> cachedBitsets_AL =
383 new AnalysisLocal<MapCache<XMethod, BitSet>>() {
381384 @Override
382385 protected MapCache<XMethod, BitSet> initialValue() {
383386 return new MapCache<XMethod, BitSet>(64);
430433 return cachedBitsets().get(xmethod);
431434 }
432435 Code code = method.getCode();
433 if (code == null)
436 if (code == null) {
434437 return null;
438 }
435439
436440 byte[] instructionList = code.getCode();
437441
444448
445449 UnpackedCode unpackedCode = callback.getUnpackedCode();
446450 BitSet result = null;
447 if (unpackedCode != null)
451 if (unpackedCode != null) {
448452 result = unpackedCode.getBytecodeSet();
453 }
449454 cachedBitsets().put(xmethod, result);
450455 return result;
451456 }
452457
453 /**
454 * @return
455 */
456458 private static MapCache<XMethod, BitSet> cachedBitsets() {
457459 return cachedBitsets_AL.get();
458460 }
468470 assert false;
469471 return Collections.<Integer> emptySet();
470472 }
471 return result;
473 return result;
472474 }
473475 Code code = method.getCode();
474476 if (code == null) {
479481 byte[] instructionList = code.getCode();
480482
481483 Set<Integer> result = new HashSet<Integer>();
482 for (int i = 0; i < instructionList.length; i++)
483 if (checkForBranchExit(instructionList, i))
484 for (int i = 0; i < instructionList.length; i++) {
485 if (checkForBranchExit(instructionList, i)) {
484486 result.add(i);
485 if (result.size() == 0)
487 }
488 }
489 if (result.size() == 0) {
486490 result = Collections.<Integer> emptySet();
491 }
487492
488493 cachedLoopExits().put(xmethod, result);
489494 return result;
490495 }
491496
492 /**
493 * @return
494 */
495497 private static MapCache<XMethod, Set<Integer>> cachedLoopExits() {
496498 return cachedLoopExits_AL.get();
497499 }
505507 }
506508
507509 static boolean checkForBranchExit(byte[] codeBytes, int pos) {
508 if (pos < 0 || pos + 2 >= codeBytes.length)
510 if (pos < 0 || pos + 2 >= codeBytes.length) {
509511 return false;
512 }
510513 switch (0xff & codeBytes[pos]) {
511514 case Constants.IF_ACMPEQ:
512515 case Constants.IF_ACMPNE:
521524 return false;
522525 }
523526 int branchTarget = pos + getBranchOffset(codeBytes, pos + 1);
524 if (branchTarget - 3 < pos || branchTarget >= codeBytes.length)
527 if (branchTarget - 3 < pos || branchTarget >= codeBytes.length) {
525528 return false;
526 if ((codeBytes[branchTarget - 3] & 0xff) != Constants.GOTO)
529 }
530 if ((codeBytes[branchTarget - 3] & 0xff) != Constants.GOTO) {
527531 return false;
532 }
528533 int backBranchTarget = branchTarget + getBranchOffset(codeBytes, branchTarget - 2);
529 if (backBranchTarget <= pos && backBranchTarget + 12 >= pos)
534 if (backBranchTarget <= pos && backBranchTarget + 12 >= pos) {
530535 return true;
536 }
531537 return false;
532538 }
533539
542548 * @return map of bytecode offsets to opcodes, or null if the method has no
543549 * code
544550 */
551 @CheckForNull
552 @SuppressFBWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS")
545553 public short[] getOffsetToOpcodeMap(Method method) {
546554 UnpackedCode unpackedCode = getMethodAnalysisNoException(UnpackedCode.class, method);
547555 return unpackedCode != null ? unpackedCode.getOffsetToBytecodeMap() : null;
593601 * @return the DominatorsAnalysis
594602 */
595603 public DominatorsAnalysis getNonExceptionDominatorsAnalysis(Method method) throws CFGBuilderException,
596 DataflowAnalysisException {
604 DataflowAnalysisException {
597605 return getMethodAnalysis(DominatorsAnalysis.class, method);
598606 }
599607
606614 * @return the DominatorsAnalysis
607615 */
608616 public PostDominatorsAnalysis getNonImplicitExceptionDominatorsAnalysis(Method method) throws CFGBuilderException,
609 DataflowAnalysisException {
617 DataflowAnalysisException {
610618 return getMethodAnalysis(NonImplicitExceptionPostDominatorsAnalysis.class, method);
611619 }
612620
619627 * @return the PostDominatorsAnalysis
620628 */
621629 public PostDominatorsAnalysis getNonExceptionPostDominatorsAnalysis(Method method) throws CFGBuilderException,
622 DataflowAnalysisException {
630 DataflowAnalysisException {
623631 return getMethodAnalysis(NonExceptionPostdominatorsAnalysis.class, method);
624632 }
625633
767775 BitSet pcInFinallyBlock = new BitSet();
768776
769777 Code code = method.getCode();
770 if (code == null)
778 if (code == null) {
771779 return lineMentionedMultipleTimes;
780 }
772781 CodeException[] exceptionTable = code.getExceptionTable();
773 if (exceptionTable == null || exceptionTable.length == 0)
782 if (exceptionTable == null || exceptionTable.length == 0) {
774783 return lineMentionedMultipleTimes;
784 }
775785 int firstHandler = Integer.MAX_VALUE;
776 for (CodeException e : exceptionTable)
786 for (CodeException e : exceptionTable) {
777787 if (e.getCatchType() == 0) {
778788 int pc = e.getHandlerPC();
779789 firstHandler = Math.min(firstHandler, pc);
780790 if (jumpInfo != null) {
781791 int end = jumpInfo.getNextJump(pc + 1);
782 if (end >= pc)
792 if (end >= pc) {
783793 pcInFinallyBlock.set(pc, end);
794 }
784795 }
785796 }
797 }
786798 BitSet foundOnce = new BitSet();
787799 BitSet afterHandler = new BitSet();
788800 LineNumberTable lineNumberTable = method.getLineNumberTable();
789801 int lineNum = -1;
790802 int prevStartPc = -1;
791 if (lineNumberTable != null)
803 if (lineNumberTable != null) {
792804 for (LineNumber line : lineNumberTable.getLineNumberTable()) {
793805 int newLine = line.getLineNumber();
794 if (newLine == lineNum || newLine == -1)
806 if (newLine == lineNum || newLine == -1) {
795807 continue;
808 }
796809 if (prevStartPc >= 0) {
797810 int nextPcInFinallyBlock = pcInFinallyBlock.nextSetBit(prevStartPc);
798 if (nextPcInFinallyBlock < line.getStartPC())
811 if (nextPcInFinallyBlock < line.getStartPC()) {
799812 lineMentionedMultipleTimes.set(lineNum);
813 }
800814 }
801 if (line.getStartPC() >= firstHandler)
815 if (line.getStartPC() >= firstHandler) {
802816 afterHandler.set(lineNum);
817 }
803818
804819 lineNum = newLine;
805820 prevStartPc = line.getStartPC();
809824 foundOnce.set(lineNum);
810825 }
811826 }
827 }
812828 lineMentionedMultipleTimes.and(afterHandler);
813829 return lineMentionedMultipleTimes;
814830 }
823839 * @throws DataflowAnalysisException
824840 */
825841 public UnconditionalValueDerefDataflow getUnconditionalValueDerefDataflow(Method method) throws CFGBuilderException,
826 DataflowAnalysisException {
842 DataflowAnalysisException {
827843 return getMethodAnalysis(UnconditionalValueDerefDataflow.class, method);
828844 }
829845
881897 }
882898 }
883899
884 /**
885 * @param method
886 * @param cfg
887 * @param vnd
888 * @param inv
889 * @param dataflow
890 * @param typeDataflow
891 * TODO
892 * @throws DataflowAnalysisException
893 */
894900 public static void dumpDataflowInformation(Method method, CFG cfg, ValueNumberDataflow vnd, IsNullValueDataflow inv,
895901 @CheckForNull UnconditionalValueDerefDataflow dataflow, @CheckForNull TypeDataflow typeDataflow)
896 throws DataflowAnalysisException {
902 throws DataflowAnalysisException {
897903 System.out.println("\n\n{ UnconditionalValueDerefAnalysis analysis for " + method.getName());
898904 TreeSet<Location> tree = new TreeSet<Location>();
899905
903909 }
904910 for (Location loc : tree) {
905911 System.out.println();
906 if (dataflow != null)
912 if (dataflow != null) {
907913 System.out.println("\n Pre: " + dataflow.getFactAfterLocation(loc));
914 }
908915 System.out.println("Vna: " + vnd.getFactAtLocation(loc));
909916 System.out.println("inv: " + inv.getFactAtLocation(loc));
910 if (typeDataflow != null)
917 if (typeDataflow != null) {
911918 System.out.println("type: " + typeDataflow.getFactAtLocation(loc));
919 }
912920 System.out.println("Location: " + loc);
913 if (dataflow != null)
921 if (dataflow != null) {
914922 System.out.println("Post: " + dataflow.getFactAtLocation(loc));
923 }
915924 System.out.println("Vna: " + vnd.getFactAfterLocation(loc));
916925 System.out.println("inv: " + inv.getFactAfterLocation(loc));
917 if (typeDataflow != null)
926 if (typeDataflow != null) {
918927 System.out.println("type: " + typeDataflow.getFactAfterLocation(loc));
928 }
919929 }
920930 System.out.println("}\n\n");
921931 }
922932
923 /**
924 * @param method
925 * @param cfg
926 * @param typeDataflow
927 * @throws DataflowAnalysisException
928 */
929933 public static void dumpTypeDataflow(Method method, CFG cfg, TypeDataflow typeDataflow) throws DataflowAnalysisException {
930934 System.out.println("\n\n{ Type analysis for " + cfg.getMethodGen().getClassName() + "." + method.getName()
931935 + method.getSignature());
943947 System.out.println("}\n\n");
944948 }
945949
946 /**
947 * @param method
948 * @param cfg
949 * @param typeDataflow
950 * @throws DataflowAnalysisException
951 */
952950 public static void dumpLiveLocalStoreDataflow(MethodDescriptor method, CFG cfg, LiveLocalStoreDataflow dataflow)
953951 throws DataflowAnalysisException {
954952 System.out.println("\n\n{ LiveLocalStore analysis for " + method);
997995 }
998996
999997 private <Analysis> Analysis getMethodAnalysis(Class<Analysis> analysisClass, Method method) throws DataflowAnalysisException,
1000 CFGBuilderException {
998 CFGBuilderException {
1001999 try {
10021000 MethodDescriptor methodDescriptor = BCELUtil.getMethodDescriptor(jclass, method);
10031001 return Global.getAnalysisCache().getMethodAnalysis(analysisClass, methodDescriptor);
10461044 // }
10471045 // }
10481046 }
1049
1050 // vim:ts=3
4141 /**
4242 * Compute a hash of method names and signatures. This allows us to find out
4343 * when a class has been renamed, but not changed in any other obvious way.
44 *
44 *
4545 * @author David Hovemeyer
4646 */
4747 public class ClassHash implements XMLWriteable, Comparable<ClassHash> {
6060
6161 private byte[] classHash;
6262
63 private Map<XMethod, MethodHash> methodHashMap;
63 private final Map<XMethod, MethodHash> methodHashMap;
6464
6565 /**
6666 * Constructor.
7171
7272 /**
7373 * Constructor.
74 *
74 *
7575 * @param classHash
7676 * pre-computed class hash
7777 */
8484
8585 /**
8686 * Set method hash for given method.
87 *
87 *
8888 * @param method
8989 * the method
9090 * @param methodHash
103103
104104 /**
105105 * Get class hash.
106 *
106 *
107107 * @return the class hash
108108 */
109109 public byte[] getClassHash() {
112112
113113 /**
114114 * Set class hash.
115 *
115 *
116116 * @param classHash
117117 * the class hash value to set
118118 */
123123
124124 /**
125125 * Get method hash for given method.
126 *
126 *
127127 * @param method
128128 * the method
129129 * @return the MethodHash
134134
135135 /**
136136 * Compute hash for given class and all of its methods.
137 *
137 *
138138 * @param javaClass
139139 * the class
140140 * @return this object
147147 // Sort methods
148148 System.arraycopy(javaClass.getMethods(), 0, methodList, 0, javaClass.getMethods().length);
149149 Arrays.sort(methodList, new Comparator<Method>() {
150 @Override
150151 public int compare(Method o1, Method o2) {
151152 // sort by name, then signature
152153 int cmp = o1.getName().compareTo(o2.getName());
153 if (cmp != 0)
154 if (cmp != 0) {
154155 return cmp;
156 }
155157 return o1.getSignature().compareTo(o2.getSignature());
156158
157159 }
164166 Arrays.sort(fieldList, new Comparator<Field>() {
165167 /*
166168 * (non-Javadoc)
167 *
169 *
168170 * @see java.util.Comparator#compare(T, T)
169171 */
172 @Override
170173 public int compare(Field o1, Field o2) {
171174 int cmp = o1.getName().compareTo(o2.getName());
172 if (cmp != 0)
175 if (cmp != 0) {
173176 return cmp;
177 }
174178 return o1.getSignature().compareTo(o2.getSignature());
175179 }
176180 });
218222 }
219223 }
220224
225 @Override
221226 public void writeXML(XMLOutput xmlOutput) throws IOException {
222227 xmlOutput.startTag(CLASS_HASH_ELEMENT_NAME);
223228 xmlOutput.addAttribute("class", className);
240245
241246 /**
242247 * Convert a hash to a string of hex digits.
243 *
248 *
244249 * @param hash
245250 * the hash
246251 * @return a String representation of the hash
255260 }
256261
257262 private static int hexDigitValue(char c) {
258 if (c >= '0' && c <= '9')
263 if (c >= '0' && c <= '9') {
259264 return c - '0';
260 else if (c >= 'a' && c <= 'f')
265 } else if (c >= 'a' && c <= 'f') {
261266 return 10 + (c - 'a');
262 else if (c >= 'A' && c <= 'F')
267 } else if (c >= 'A' && c <= 'F') {
263268 return 10 + (c - 'A');
264 else
269 } else {
265270 throw new IllegalArgumentException("Illegal hex character: " + c);
271 }
266272 }
267273
268274 /**
269275 * Convert a string of hex digits to a hash.
270 *
276 *
271277 * @param s
272278 * string of hex digits
273279 * @return the hash value represented by the string
274280 */
275281 public static byte[] stringToHash(String s) {
276 if (s.length() % 2 != 0)
282 if (s.length() % 2 != 0) {
277283 throw new IllegalArgumentException("Invalid hash string: " + s);
284 }
278285 byte[] hash = new byte[s.length() / 2];
279286 for (int i = 0; i < s.length(); i += 2) {
280287 byte b = (byte) ((hexDigitValue(s.charAt(i)) << 4) + hexDigitValue(s.charAt(i + 1)));
286293 /**
287294 * Return whether or not this class hash has the same hash value as the one
288295 * given.
289 *
296 *
290297 * @param other
291298 * another ClassHash
292299 * @return true if the hash values are the same, false if not
297304
298305 @Override
299306 public int hashCode() {
300 if (classHash == null)
307 if (classHash == null) {
301308 return 0;
309 }
302310
303311 int result = 1;
304 for (byte element : classHash)
312 for (byte element : classHash) {
305313 result = 31 * result + element;
314 }
306315
307316 return result;
308317
310319
311320 @Override
312321 public boolean equals(Object o) {
313 if (!(o instanceof ClassHash))
322 if (!(o instanceof ClassHash)) {
314323 return false;
324 }
315325 return isSameHash((ClassHash) o);
316326 }
317327
318328 /*
319329 * (non-Javadoc)
320 *
330 *
321331 * @see java.lang.Comparable#compareTo(T)
322332 */
333 @Override
323334 public int compareTo(ClassHash other) {
324335 int cmp = MethodHash.compareHashes(this.classHash, other.classHash);
325336 // System.out.println(this + " <=> " + other + ": compareTo=" + cmp);
328339
329340 /*
330341 * (non-Javadoc)
331 *
342 *
332343 * @see java.lang.Object#toString()
333344 */
334345
2626
2727 /**
2828 * Common super-interface for class members (fields and methods).
29 *
29 *
3030 * @see edu.umd.cs.findbugs.ba.XField
3131 * @see edu.umd.cs.findbugs.ba.XMethod
3232 * @author David Hovemeyer
4949 /**
5050 * Get the name of the field/method.
5151 */
52 @Override
5253 public String getName();
5354
5455 /**
5556 * Get the signature representing the field/method's type.
5657 */
58 @Override
5759 public String getSignature();
5860
5961 /**
2626 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
2727
2828 public class ClassSummary {
29 private Map<ClassDescriptor, ClassDescriptor> map = new HashMap<ClassDescriptor, ClassDescriptor>();
29 private final Map<ClassDescriptor, ClassDescriptor> map = new HashMap<ClassDescriptor, ClassDescriptor>();
3030
31 private Set<ClassDescriptor> veryFunky = new HashSet<ClassDescriptor>();
31 private final Set<ClassDescriptor> veryFunky = new HashSet<ClassDescriptor>();
3232
3333 public boolean mightBeEqualTo(ClassDescriptor checker, ClassDescriptor checkee) {
3434 return checkee.equals(map.get(checker)) || veryFunky.contains(checker);
3636
3737 public void checksForEqualTo(ClassDescriptor checker, ClassDescriptor checkee) {
3838 ClassDescriptor existing = map.get(checker);
39 if (checkee.equals(existing))
39 if (checkee.equals(existing)) {
4040 return;
41 else if (existing != null)
41 } else if (existing != null) {
4242 veryFunky.add(checker);
43 else
43 } else {
4444 map.put(checker, checkee);
45 }
4546 }
4647
4748 }
2424 /**
2525 * Compute a compact numbering of Locations in a CFG. This is useful for
2626 * analyses that want to use a BitSet to keep track of Locations.
27 *
27 *
2828 * @author David Hovemeyer
2929 */
3030 public class CompactLocationNumbering {
31 private HashMap<Location, Integer> locationToNumberMap;
31 private final HashMap<Location, Integer> locationToNumberMap;
3232
33 private HashMap<Integer, Location> numberToLocationMap;
33 private final HashMap<Integer, Location> numberToLocationMap;
3434
3535 /**
3636 * Constructor.
37 *
37 *
3838 * @param cfg
3939 * the CFG containing the Locations to number
4040 */
4747 /**
4848 * Get the size of the numbering, which is the maximum number assigned plus
4949 * one.
50 *
50 *
5151 * @return the maximum number assigned plus one
5252 */
5353 public int getSize() {
5757 /**
5858 * Get the number of given Location, which will be a non-negative integer in
5959 * the range 0..getSize() - 1.
60 *
60 *
6161 * @param location
6262 * @return the number of the location
6363 */
6767
6868 /**
6969 * Get the Location given its number.
70 *
70 *
7171 * @param number
7272 * the number
7373 * @return Location corresponding to that number
00 /*
11 * FindBugs - Find Bugs in Java programs
22 * Copyright (C) 2003-2008 University of Maryland
3 *
3 *
44 * This library is free software; you can redistribute it and/or
55 * modify it under the terms of the GNU Lesser General Public
66 * License as published by the Free Software Foundation; either
77 * version 2.1 of the License, or (at your option) any later version.
8 *
8 *
99 * This library is distributed in the hope that it will be useful,
1010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1111 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1212 * Lesser General Public License for more details.
13 *
13 *
1414 * You should have received a copy of the GNU Lesser General Public
1515 * License along with this library; if not, write to the Free Software
1616 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00 /*
11 * FindBugs - Find Bugs in Java programs
22 * Copyright (C) 2003-2008 University of Maryland
3 *
3 *
44 * This library is free software; you can redistribute it and/or
55 * modify it under the terms of the GNU Lesser General Public
66 * License as published by the Free Software Foundation; either
77 * version 2.1 of the License, or (at your option) any later version.
8 *
8 *
99 * This library is distributed in the hope that it will be useful,
1010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1111 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1212 * Lesser General Public License for more details.
13 *
13 *
1414 * You should have received a copy of the GNU Lesser General Public
1515 * License along with this library; if not, write to the Free Software
1616 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2929 this.conjunctList = conjunctList;
3030 }
3131
32 @Override
3233 public boolean choose(JavaClassAndMethod javaClassAndMethod) {
3334 for (JavaClassAndMethodChooser chooser : conjunctList) {
34 if (!chooser.choose(javaClassAndMethod))
35 if (!chooser.choose(javaClassAndMethod)) {
3536 return false;
37 }
3638 }
3739 return true;
3840 }
3941
4042 /*
4143 * (non-Javadoc)
42 *
44 *
4345 * @see
4446 * edu.umd.cs.findbugs.ba.JavaClassAndMethodChooser#choose(edu.umd.cs.findbugs
4547 * .ba.XMethod)
4648 */
49 @Override
4750 public boolean choose(XMethod method) {
4851 for (JavaClassAndMethodChooser chooser : conjunctList) {
49 if (!chooser.choose(method))
52 if (!chooser.choose(method)) {
5053 return false;
54 }
5155 }
5256 return true;
5357 }
1919 package edu.umd.cs.findbugs.ba;
2020
2121 public class DFSCFGPrinter extends CFGPrinter implements DFSEdgeTypes {
22 private DepthFirstSearch dfs;
22 private final DepthFirstSearch dfs;
2323
2424 public DFSCFGPrinter(CFG cfg, DepthFirstSearch dfs) {
2525 super(cfg);
2222 * Interface defining depth first search edge type constants. This is just a
2323 * placeholder that forwards to the real interface in the
2424 * edu.umd.cs.findbugs.graph package.
25 *
25 *
2626 * @see edu.umd.cs.findbugs.graph.DFSEdgeTypes
2727 * @author David Hovemeyer
2828 */
5252 * @see DataflowAnalysis
5353 */
5454 public class Dataflow<Fact, AnalysisType extends DataflowAnalysis<Fact>> {
55 private CFG cfg;
56
57 private AnalysisType analysis;
58
59 private BlockOrder blockOrder;
60
61 private boolean isForwards;
55 private final CFG cfg;
56
57 private final AnalysisType analysis;
58
59 private final BlockOrder blockOrder;
60
61 private final boolean isForwards;
6262
6363 private int numIterations;
6464
105105 private String getFullyQualifiedMethodName() {
106106 String methodName;
107107 MethodGen methodGen = cfg.getMethodGen();
108 if (methodGen == null)
108 if (methodGen == null) {
109109 methodName = cfg.getMethodName();
110 else
110 } else {
111111 methodName = SignatureConverter.convertMethodSignature(methodGen);
112 }
112113 return methodName;
113114 }
114115
115116 static class ForwardProgramOrder implements Comparator<BasicBlock>, Serializable {
116117
118 @Override
117119 public int compare(BasicBlock o1, BasicBlock o2) {
118120 int p1 = o1.getLabel();
119121 int p2 = o2.getLabel();
201203 }
202204 Iterator<BasicBlock> i = blockOrder.blockIterator();
203205 if (numIterations > 3 && numIterations % 2 == 0 && blockOrder instanceof ReverseDFSOrder) {
204 if (DEBUG)
206 if (DEBUG) {
205207 System.out.println("Trying program order");
208 }
206209 TreeSet<BasicBlock> bb = new TreeSet<BasicBlock>(new BackwardProgramOrder());
207210 Iterator<BasicBlock> j = blockOrder.blockIterator();
208211 while (j.hasNext()) {
209212 BasicBlock block = j.next();
210213 bb.add(block);
211214 }
212 if (DEBUG)
215 if (DEBUG) {
213216 for (BasicBlock block : bb) {
214217 debug(block, "\n");
215218 }
219 }
216220 i = bb.iterator();
217221 }
218 if (DEBUG)
222 if (DEBUG) {
219223 dumpDataflow(analysis);
224 }
220225
221226 // For each block in CFG...
222227
242247 if (block == logicalEntryBlock()) {
243248 analysis.makeFactTop(start);
244249 analysis.initEntryFact(start);
245 if (DEBUG)
250 if (DEBUG) {
246251 debug(block, "Init entry fact ==> " + analysis.factToString(start) + "\n");
252 }
247253 needToRecompute = true;
248254 } else {
249255 int lastCalculated = analysis.getLastUpdateTimestamp(start);
261267 } else {
262268 continue;
263269 }
264
270
265271 }
266272 BasicBlock logicalPred = isForwards ? edge.getSource() : edge.getTarget();
267273
268274 int direction = blockOrder.compare(block, logicalPred);
269
270 if (DEBUG)
275
276 if (DEBUG) {
271277 debug(block, "direction " + direction + " for " + blockId(logicalPred) + "\n");
272 if (direction < 0)
278 }
279 if (direction < 0) {
273280 sawBackEdge = true;
274
281 }
282
275283 // Get the predecessor result fact
276284 Fact predFact = analysis.getResultFact(logicalPred);
277285 int predLastUpdated = analysis.getLastUpdateTimestamp(predFact);
288296 }
289297 }
290298 }
291 if (predCount == 0)
299 if (predCount == 0) {
292300 needToRecompute = true;
301 }
293302
294303 if (!needToRecompute) {
295304 continue;
296305 }
297306
298 if (needToRecompute) {
299
300 analysis.makeFactTop(start);
301 predEdgeIter = logicalPredecessorEdgeIterator(block);
302 while (predEdgeIter.hasNext()) {
303 Edge edge = predEdgeIter.next();
304 BasicBlock logicalPred = isForwards ? edge.getSource() : edge.getTarget();
305
306 // Get the predecessor result fact
307 Fact predFact = analysis.getResultFact(logicalPred);
308
309 // Apply the edge transfer function.
310 Fact edgeFact = analysis.createFact();
311 analysis.copy(predFact, edgeFact);
312 analysis.edgeTransfer(edge, edgeFact);
313
314 if (DEBUG && !analysis.same(edgeFact, predFact)) {
315 debug(block, logicalPred, edge, "Edge transfer " + analysis.factToString(predFact) + " ==> "
316 + analysis.factToString(edgeFact));
307 analysis.makeFactTop(start);
308 predEdgeIter = logicalPredecessorEdgeIterator(block);
309 while (predEdgeIter.hasNext()) {
310 Edge edge = predEdgeIter.next();
311 BasicBlock logicalPred = isForwards ? edge.getSource() : edge.getTarget();
312
313 // Get the predecessor result fact
314 Fact predFact = analysis.getResultFact(logicalPred);
315
316 // Apply the edge transfer function.
317 Fact edgeFact = analysis.createFact();
318 analysis.copy(predFact, edgeFact);
319 analysis.edgeTransfer(edge, edgeFact);
320
321 if (DEBUG && !analysis.same(edgeFact, predFact)) {
322 debug(block, logicalPred, edge, "Edge transfer " + analysis.factToString(predFact) + " ==> "
323 + analysis.factToString(edgeFact));
324 }
325
326 // Merge the predecessor fact (possibly transformed
327 // by the edge transfer function)
328 // into the block's start fact.
329 if (DEBUG) {
330 if (analysis.isTop(start)) {
331 debug(block, logicalPred, edge, "\n First pred is " + analysis.factToString(edgeFact)
332 + "\n last updated at " + analysis.getLastUpdateTimestamp(predFact) + "\n");
333 } else {
334 debug(block, logicalPred, edge, "\n Meet " + analysis.factToString(start) + "\n with "
335 + analysis.factToString(edgeFact)
336
337 + "\n pred last updated at " + analysis.getLastUpdateTimestamp(predFact) + "\n");
317338 }
318
319 // Merge the predecessor fact (possibly transformed
320 // by the edge transfer function)
321 // into the block's start fact.
322 if (DEBUG) {
323 if (analysis.isTop(start))
324 debug(block, logicalPred, edge, "\n First pred is " + analysis.factToString(edgeFact)
325 + "\n last updated at " + analysis.getLastUpdateTimestamp(predFact) + "\n");
326 else
327 debug(block, logicalPred, edge, "\n Meet " + analysis.factToString(start) + "\n with "
328 + analysis.factToString(edgeFact)
329
330 + "\n pred last updated at " + analysis.getLastUpdateTimestamp(predFact) + "\n");
331 }
332
333 if (analysis instanceof UnconditionalValueDerefAnalysis) {
334 ((UnconditionalValueDerefAnalysis) analysis).meetInto((UnconditionalValueDerefSet) edgeFact,
335 edge, (UnconditionalValueDerefSet) start, rawPredCount == 1);
336 } else
337 analysis.meetInto(edgeFact, edge, start);
338 analysis.setLastUpdateTimestamp(start, timestamp);
339
340 int pos = -1;
341 if (block.getFirstInstruction() != null)
342 pos = block.getFirstInstruction().getPosition();
343 if (DEBUG)
344 System.out.println(" [" + pos + "]==> " + analysis.factToString(start) + " @ " + timestamp
345 + " \n");
346 }
347 }
348 }
349 if (DEBUG)
339 }
340
341 if (analysis instanceof UnconditionalValueDerefAnalysis) {
342 ((UnconditionalValueDerefAnalysis) analysis).meetInto((UnconditionalValueDerefSet) edgeFact,
343 edge, (UnconditionalValueDerefSet) start, rawPredCount == 1);
344 } else {
345 analysis.meetInto(edgeFact, edge, start);
346 }
347 analysis.setLastUpdateTimestamp(start, timestamp);
348
349 int pos = -1;
350 if (block.getFirstInstruction() != null) {
351 pos = block.getFirstInstruction().getPosition();
352 }
353 if (DEBUG) {
354 System.out.println(" [" + pos + "]==> " + analysis.factToString(start) + " @ " + timestamp
355 + " \n");
356 }
357 }
358 }
359 if (DEBUG) {
350360 debug(block, "start fact is " + analysis.factToString(start) + "\n");
361 }
351362
352363 // making a copy of result facts (so we can detect if it
353364 // changed).
358369 analysis.copy(result, origResult);
359370 }
360371
361 if (true || analysis.isTop(start)) {
362 // Apply the transfer function.
363
364 analysis.transfer(block, null, start, result);
365 } else {
366 analysis.copy(start, result);
367 }
372 // if (true || analysis.isTop(start)) {
373 // Apply the transfer function.
374
375 analysis.transfer(block, null, start, result);
376 // } else {
377 // analysis.copy(start, result);
378 // }
368379
369380 if (DEBUG && SystemProperties.getBoolean("dataflow.blockdebug")) {
370381 debug(block, "Dumping flow values for block:\n");
378389 }
379390
380391 // See if the result changed.
381 if (DEBUG)
392 if (DEBUG) {
382393 debug(block, "orig result is " + (origResult == null ? "TOP" : analysis.factToString(origResult)) + "\n");
394 }
383395 boolean thisResultChanged = false;
384 if (resultWasTop)
396 if (resultWasTop) {
385397 thisResultChanged = !analysis.isTop(result);
386 else
398 } else {
387399 thisResultChanged = !analysis.same(result, origResult);
400 }
388401 if (thisResultChanged) {
389402 timestamp++;
390 if (DEBUG)
403 if (DEBUG) {
391404 debug(block, "result changed at timestamp " + timestamp + "\n");
405 }
392406 if (DEBUG && !needToRecompute) {
393407 System.out.println("I thought I didn't need to recompute");
394408 }
395409 change = true;
396410 analysis.setLastUpdateTimestamp(result, timestamp);
397 } else
411 } else {
398412 analysis.setLastUpdateTimestamp(result, originalResultTimestamp);
399
400 if (DEBUG)
413 }
414
415 if (DEBUG) {
401416 debug(block,
402417 "result is " + analysis.factToString(result) + " @ timestamp "
403418 + analysis.getLastUpdateTimestamp(result) + "\n");
419 }
404420 }
405421
406422 analysis.finishIteration();
407 if (!sawBackEdge) break;
408
423 if (!sawBackEdge) {
424 break;
425 }
426
409427 } while (change);
410428
411429 if (DEBUG) {
414432 MethodGen mg = cfg.getMethodGen();
415433 System.out.println(mg.getClassName() + "." + mg.getName() + mg.getSignature());
416434 new RuntimeException("Quiescence achieved----------------------------------------------------------------")
417 .printStackTrace(System.out);
435 .printStackTrace(System.out);
418436
419437 }
420438 DEBUG = debugWas;
421439 }
422440
423 /**
424 * @param msg
425 * TODO
426 *
427 */
428441 private void reportAnalysis(String msg) {
429442 String shortAnalysisName = analysis.getClass().getName();
430443 int pkgEnd = shortAnalysisName.lastIndexOf('.');
436449
437450 private static String blockId(BasicBlock bb) {
438451 InstructionHandle handle = bb.getFirstInstruction();
439 if (handle == null)
452 if (handle == null) {
440453 return "" + bb.getLabel();
454 }
441455 return bb.getLabel() + ":" + handle.getPosition() + " " + handle.getInstruction();
442456 }
443457
560574 System.out.println("}");
561575 }
562576 }
563
564 // vim:ts=4
2525
2626 /**
2727 * A dataflow analysis to be used with the {@link Dataflow} class.
28 *
28 *
2929 * <p>
3030 * In order to avoid duplicating functionality (such as caching of start and
3131 * result facts), most analyses should extend the
3232 * {@link BasicAbstractDataflowAnalysis} or {@link AbstractDataflowAnalysis}
3333 * classes rather than directly implementing this interface.
3434 * </p>
35 *
35 *
3636 * @author David Hovemeyer
3737 * @see Dataflow
3838 */
4545
4646 /**
4747 * Get the start fact for given basic block.
48 *
48 *
4949 * @param block
5050 * the basic block
5151 */
5353
5454 /**
5555 * Get the result fact for given basic block.
56 *
56 *
5757 * @param block
5858 * the basic block
5959 */
6363 * Get dataflow fact at (just before) given Location. Note "before" is meant
6464 * in the logical sense, so for backward analyses, before means after the
6565 * location in the control flow sense.
66 *
66 *
6767 * @param location
6868 * the Location
6969 * @return the dataflow value at given Location
7575 * Get the dataflow fact representing the point just after given Location.
7676 * Note "after" is meant in the logical sense, so for backward analyses,
7777 * after means before the location in the control flow sense.
78 *
78 *
7979 * @param location
8080 * the Location
8181 * @return the dataflow value after given Location
8585
8686 /**
8787 * Get the fact that is true on the given control edge.
88 *
88 *
8989 * @param edge
9090 * the edge
9191 * @return the fact that is true on the edge
121121 /**
122122 * Return the BlockOrder specifying the order in which BasicBlocks should be
123123 * visited in the main dataflow loop.
124 *
124 *
125125 * @param cfg
126126 * the CFG upon which we're performing dataflow analysis
127127 */
137137 * might be either the entry or exit of the block, depending on whether the
138138 * analysis is forwards or backwards), modify result to be the facts at the
139139 * other end of the block.
140 *
140 *
141141 * @param basicBlock
142142 * the basic block
143143 * @param end
156156 * Edge transfer function. Modify the given fact that is true on the
157157 * (logical) edge source to modify it so that it is true at the (logical)
158158 * edge target.
159 *
159 *
160160 * <p>
161161 * A do-nothing implementation is legal, and appropriate for analyses where
162162 * branches are not significant.
163163 * </p>
164 *
164 *
165165 * @param edge
166166 * the Edge
167167 * @param fact
173173 /**
174174 * Meet a dataflow fact associated with an incoming edge into another fact.
175175 * This is used to determine the start fact for a basic block.
176 *
176 *
177177 * @param fact
178178 * the predecessor fact (incoming edge)
179179 * @param edge
200200
201201 /**
202202 * Return a String representation of given Fact. For debugging purposes.
203 *
203 *
204204 * @param fact
205205 * a dataflow fact
206206 * @return String representation of the fact
208208 public String factToString(Fact fact);
209209 }
210210
211 // vim:ts=4
2525
2626 /**
2727 * Exception type to indicate a dataflow analysis failure.
28 *
28 *
2929 * @see Dataflow
3030 * @see DataflowAnalysis
3131 */
4040
4141 /**
4242 * Constructor.
43 *
43 *
4444 * @param msg
4545 * message describing the reason for the exception
4646 */
5050
5151 /**
5252 * Constructor from message and another Throwable object.
53 *
53 *
5454 * @param msg
5555 * message describing the reason for the exception
5656 * @param cause
6262
6363 /**
6464 * Constructor from method and instruction.
65 *
65 *
6666 * @param message
6767 * reason for the error
6868 * @param methodGen
7777 /**
7878 * Constructor from message, method and instruction, and Throwable object
7979 * (cause).
80 *
80 *
8181 * @param message
8282 * reason for the error
8383 * @param methodGen
9393 }
9494 }
9595
96 // vim:ts=4
2727 * instruction.
2828 */
2929 public class DataflowCFGPrinter<Fact, AnalysisType extends DataflowAnalysis<Fact>> extends CFGPrinter {
30 private Dataflow<Fact, AnalysisType> dataflow;
30 private final Dataflow<Fact, AnalysisType> dataflow;
3131
3232 /**
3333 * Constructor.
34 *
34 *
3535 * @param dataflow
3636 * the Dataflow object whose values should be used to annotate
3737 * the printed CFG
4545
4646 /*
4747 * (non-Javadoc)
48 *
48 *
4949 * @see
5050 * edu.umd.cs.findbugs.ba.CFGPrinter#edgeAnnotate(edu.umd.cs.findbugs.ba
5151 * .Edge)
9393
9494 /**
9595 * Print CFG annotated with results from given dataflow analysis.
96 *
96 *
9797 * @param <Fact>
9898 * Dataflow fact type
9999 * @param <AnalysisType>
111111
112112 }
113113
114 // vim:ts=4
2020
2121 /**
2222 * Predicate for dataflow values.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public interface DataflowValueChooser<Value> {
2929 public static final boolean VERIFY_INTEGRITY = SystemProperties.getBoolean("ba.verifyIntegrity");
3030 }
3131
32 // vim:ts=4
2626 /**
2727 * DebugRepositoryLookupFailureCallback implementation for debugging. (Test
2828 * drivers, etc.) It just prints a message and exits.
29 *
29 *
3030 * @author David Hovemeyer
3131 */
3232 public class DebugRepositoryLookupFailureCallback implements RepositoryLookupFailureCallback {
3333
3434 /*
3535 * (non-Javadoc)
36 *
36 *
3737 * @see
3838 * edu.umd.cs.findbugs.ba.RepositoryLookupFailureCallback#reportMissingClass
3939 * (java.lang.ClassNotFoundException)
4040 */
41 @Override
4142 @SuppressFBWarnings("DM_EXIT")
4243 public void reportMissingClass(ClassNotFoundException ex) {
4344 String missing = AbstractBugReporter.getMissingClassName(ex);
44 if (missing == null || missing.charAt(0) == '[')
45 if (missing == null || missing.charAt(0) == '[') {
4546 return;
47 }
4648
4749 System.out.println("Missing class");
4850 ex.printStackTrace();
5153
5254 /*
5355 * (non-Javadoc)
54 *
56 *
5557 * @see
5658 * edu.umd.cs.findbugs.classfile.IErrorLogger#reportMissingClass(edu.umd
5759 * .cs.findbugs.classfile.ClassDescriptor)
5860 */
61 @Override
5962 @SuppressFBWarnings("DM_EXIT")
6063 public void reportMissingClass(ClassDescriptor classDescriptor) {
6164 System.out.println("Missing class: " + classDescriptor);
6467
6568 /*
6669 * (non-Javadoc)
67 *
70 *
6871 * @see
6972 * edu.umd.cs.findbugs.ba.RepositoryLookupFailureCallback#logError(java.
7073 * lang.String)
7174 */
75 @Override
7276 @SuppressFBWarnings("DM_EXIT")
7377 public void logError(String message) {
7478 System.err.println("Error: " + message);
7781
7882 /*
7983 * (non-Javadoc)
80 *
84 *
8185 * @see
8286 * edu.umd.cs.findbugs.ba.RepositoryLookupFailureCallback#logError(java.
8387 * lang.String, java.lang.Throwable)
8488 */
89 @Override
8590 @SuppressFBWarnings("DM_EXIT")
8691 public void logError(String message, Throwable e) {
8792 if (e instanceof MissingClassException) {
105110
106111 /**
107112 * Report that we skipped some analysis of a method
108 *
113 *
109114 * @param method
110115 */
116 @Override
111117 public void reportSkippedAnalysis(MethodDescriptor method) {
112118 System.err.println("Skipping " + method);
113119 }
4848 database.addFieldAnnotation("java.math.BigInteger", "ONE", "Ljava/math/BigInteger;", true, NullnessAnnotation.NONNULL);
4949 database.addFieldAnnotation("java.math.BigInteger", "TEN", "Ljava/math/BigInteger;", true, NullnessAnnotation.NONNULL);
5050
51
51 database.addMethodAnnotation("java.nio.file.Files", "probeContentType", "(Ljava/nio/file/Path;)Ljava/lang/String;", true,
52 NullnessAnnotation.CHECK_FOR_NULL);
53
54 database.addMethodAnnotation("java.nio.file.Path", "getRoot", "()Ljava/nio/file/Path;", false,
55 NullnessAnnotation.CHECK_FOR_NULL);
56 database.addMethodAnnotation("java.nio.file.Path", "getFileName", "()Ljava/nio/file/Path;", false,
57 NullnessAnnotation.CHECK_FOR_NULL);
58 database.addMethodAnnotation("java.nio.file.Path", "getParent", "()Ljava/nio/file/Path;", false,
59 NullnessAnnotation.CHECK_FOR_NULL);
60
61
62 database.addMethodAnnotation("java.io.File", "list", "()[Ljava/lang/String;", false,
63 NullnessAnnotation.CHECK_FOR_NULL);
64 database.addMethodAnnotation("java.io.File", "list", "(Ljava/io/FilenameFilter;)[Ljava/lang/String;", false,
65 NullnessAnnotation.CHECK_FOR_NULL);
66 database.addMethodAnnotation("java.io.File", "listFiles", "()[Ljava/io/File;", false,
67 NullnessAnnotation.CHECK_FOR_NULL);
68 database.addMethodAnnotation("java.io.File", "listFiles", "(Ljava/io/FilenameFilter;)[Ljava/io/File;", false,
69 NullnessAnnotation.CHECK_FOR_NULL);
70 database.addMethodAnnotation("java.io.File", "listFiles", "(Ljava/io/FileFilter;)[Ljava/io/File;", false,
71 NullnessAnnotation.CHECK_FOR_NULL);
5272
5373 database.addMethodAnnotation("java.lang.ref.ReferenceQueue", "poll", "()Ljava/lang/ref/Reference;", false,
5474 NullnessAnnotation.CHECK_FOR_NULL);
277297 0, NullnessAnnotation.CHECK_FOR_NULL);
278298 database.addMethodParameterAnnotation("java.util.concurrent.Phaser", "<init>", "(Ljava/util/concurrent/Phaser;I)V",
279299 false, 0, NullnessAnnotation.CHECK_FOR_NULL);
280
300
281301 database.addMethodAnnotation("java.util.concurrent.locks.ReadWriteLock", "readLock",
282302 "()Ljava/util/concurrent/locks/Lock;", false, NullnessAnnotation.NONNULL);
283303 database.addMethodAnnotation("java.util.concurrent.locks.ReadWriteLock", "writeLock",
393413 database.addMethodAnnotation("java.util.Objects","requireNonNull", "(Ljava/lang/Object;Ljava/lang/String;)Ljava/lang/Object;",
394414 true, NullnessAnnotation.NONNULL);
395415
396
416
397417 database.addMethodAnnotation("org.w3c.dom.Element","getAttribute", "(Ljava/lang/String;)Ljava/lang/String;",
398418 false, NullnessAnnotation.NONNULL);
399419 database.addMethodAnnotation("org.w3c.dom.Element","getAttributeNS", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;",
400420 false, NullnessAnnotation.NONNULL);
401
402
421 database.addMethodAnnotation("org.w3c.dom.Element","getElementsByTagName", "(Ljava/lang/String;)Lorg/w3c/dom/NodeList;",
422 false, NullnessAnnotation.NONNULL);
423 database.addMethodAnnotation("org.w3c.dom.Element","getElementsByTagNameNS", "(Ljava/lang/String;Ljava/lang/String;)Lorg/w3c/dom/NodeList;",
424 false, NullnessAnnotation.NONNULL);
425
426
403427 addEclipseSpecificAnnotations(database);
404428
405429 AnalysisContext.currentAnalysisContext().setMissingClassWarningsSuppressed(missingClassWarningsSuppressed);
407431 }
408432
409433 private static void addEclipseSpecificAnnotations(INullnessAnnotationDatabase db) {
410 // if(true){
411 // return;
412 // }
434 // if(true){
435 // return;
436 // }
413437
414438 // usually either uses known common services or checks for unknown. Too much noise
415 // db.addMethodAnnotation("org.eclipse.core.runtime.IAdaptable","getAdapter","(Ljava/lang/Class;)Ljava/lang/Object;",
416 // false, NullnessAnnotation.CHECK_FOR_NULL);
439 // db.addMethodAnnotation("org.eclipse.core.runtime.IAdaptable","getAdapter","(Ljava/lang/Class;)Ljava/lang/Object;",
440 // false, NullnessAnnotation.CHECK_FOR_NULL);
417441
418442 db.addMethodAnnotation("org.eclipse.core.runtime.IAdapterFactory","getAdapter","(Ljava/lang/Object;Ljava/lang/Class;)Ljava/lang/Object;",
419443 false, NullnessAnnotation.CHECK_FOR_NULL);
426450 false, NullnessAnnotation.CHECK_FOR_NULL);
427451
428452 // usually reads are in-sync with writes, so too much noise
429 // db.addMethodAnnotation("org.eclipse.ui.IMemento","getChild","(Ljava/lang/String;)Lorg/eclipse/ui/IMemento;",
430 // false, NullnessAnnotation.CHECK_FOR_NULL);
431 // db.addMethodAnnotation("org.eclipse.ui.IMemento","getID","()Ljava/lang/String;",
432 // false, NullnessAnnotation.CHECK_FOR_NULL);
433 // db.addMethodAnnotation("org.eclipse.ui.IMemento","getInteger","(Ljava/lang/String;)Ljava/lang/Integer;",
434 // false, NullnessAnnotation.CHECK_FOR_NULL);
435 // db.addMethodAnnotation("org.eclipse.ui.IMemento","getString","(Ljava/lang/String;)Ljava/lang/String;",
436 // false, NullnessAnnotation.CHECK_FOR_NULL);
437 // db.addMethodAnnotation("org.eclipse.ui.IMemento","getTextData","()Ljava/lang/String;",
438 // false, NullnessAnnotation.CHECK_FOR_NULL);
453 // db.addMethodAnnotation("org.eclipse.ui.IMemento","getChild","(Ljava/lang/String;)Lorg/eclipse/ui/IMemento;",
454 // false, NullnessAnnotation.CHECK_FOR_NULL);
455 // db.addMethodAnnotation("org.eclipse.ui.IMemento","getID","()Ljava/lang/String;",
456 // false, NullnessAnnotation.CHECK_FOR_NULL);
457 // db.addMethodAnnotation("org.eclipse.ui.IMemento","getInteger","(Ljava/lang/String;)Ljava/lang/Integer;",
458 // false, NullnessAnnotation.CHECK_FOR_NULL);
459 // db.addMethodAnnotation("org.eclipse.ui.IMemento","getString","(Ljava/lang/String;)Ljava/lang/String;",
460 // false, NullnessAnnotation.CHECK_FOR_NULL);
461 // db.addMethodAnnotation("org.eclipse.ui.IMemento","getTextData","()Ljava/lang/String;",
462 // false, NullnessAnnotation.CHECK_FOR_NULL);
439463
440464 // too seldom, usually used after job.join.
441 // db.addMethodAnnotation("org.eclipse.core.runtime.jobs.Job","getResult","()Lorg/eclipse/core/runtime/IStatus;",
442 // false, NullnessAnnotation.CHECK_FOR_NULL);
465 // db.addMethodAnnotation("org.eclipse.core.runtime.jobs.Job","getResult","()Lorg/eclipse/core/runtime/IStatus;",
466 // false, NullnessAnnotation.CHECK_FOR_NULL);
443467
444468 db.addMethodAnnotation("org.eclipse.core.runtime.FileLocator","find","(Ljava/net/URL;)Ljava/net/URL;",
445469 false, NullnessAnnotation.CHECK_FOR_NULL);
458482 false, NullnessAnnotation.CHECK_FOR_NULL);
459483
460484 // usually not a problem - and there is a way to ask before if it is not empty
461 // db.addMethodAnnotation("org.eclipse.core.runtime.IPath","lastSegment","()Ljava/lang/String;",
462 // false, NullnessAnnotation.CHECK_FOR_NULL);
485 // db.addMethodAnnotation("org.eclipse.core.runtime.IPath","lastSegment","()Ljava/lang/String;",
486 // false, NullnessAnnotation.CHECK_FOR_NULL);
463487
464488 // too much noise: similar as if annotating List.get(i)
465 // db.addMethodAnnotation("org.eclipse.core.runtime.IPath","segment","(I)Ljava/lang/String;",
466 // false, NullnessAnnotation.CHECK_FOR_NULL);
489 // db.addMethodAnnotation("org.eclipse.core.runtime.IPath","segment","(I)Ljava/lang/String;",
490 // false, NullnessAnnotation.CHECK_FOR_NULL);
467491
468492 // too much noise: usually search results are validated
469 // db.addMethodAnnotation("org.eclipse.core.resources.IContainer","findMember","(Ljava/lang/String;)Lorg/eclipse/core/resources/IResource;",
470 // false, NullnessAnnotation.CHECK_FOR_NULL);
471 // db.addMethodAnnotation("org.eclipse.core.resources.IContainer","findMember","(Ljava/lang/String;Z)Lorg/eclipse/core/resources/IResource;",
472 // false, NullnessAnnotation.CHECK_FOR_NULL);
473 // db.addMethodAnnotation("org.eclipse.core.resources.IContainer","findMember","(Lorg/eclipse/core/runtime/IPath;)Lorg/eclipse/core/resources/IResource;",
474 // false, NullnessAnnotation.CHECK_FOR_NULL);
475 // db.addMethodAnnotation("org.eclipse.core.resources.IContainer","findMember","(Lorg/eclipse/core/runtime/IPath;Z)Lorg/eclipse/core/resources/IResource;",
476 // false, NullnessAnnotation.CHECK_FOR_NULL);
493 // db.addMethodAnnotation("org.eclipse.core.resources.IContainer","findMember","(Ljava/lang/String;)Lorg/eclipse/core/resources/IResource;",
494 // false, NullnessAnnotation.CHECK_FOR_NULL);
495 // db.addMethodAnnotation("org.eclipse.core.resources.IContainer","findMember","(Ljava/lang/String;Z)Lorg/eclipse/core/resources/IResource;",
496 // false, NullnessAnnotation.CHECK_FOR_NULL);
497 // db.addMethodAnnotation("org.eclipse.core.resources.IContainer","findMember","(Lorg/eclipse/core/runtime/IPath;)Lorg/eclipse/core/resources/IResource;",
498 // false, NullnessAnnotation.CHECK_FOR_NULL);
499 // db.addMethodAnnotation("org.eclipse.core.resources.IContainer","findMember","(Lorg/eclipse/core/runtime/IPath;Z)Lorg/eclipse/core/resources/IResource;",
500 // false, NullnessAnnotation.CHECK_FOR_NULL);
477501
478502 db.addMethodAnnotation("org.eclipse.core.resources.IContainer","getDefaultCharset","(Z)Ljava/lang/String;",
479503 false, NullnessAnnotation.CHECK_FOR_NULL);
486510 db.addMethodAnnotation("org.eclipse.core.resources.IProject","getNature","(Ljava/lang/String;)Lorg/eclipse/core/resources/IProjectNature;",
487511 false, NullnessAnnotation.CHECK_FOR_NULL);
488512
489 // db.addMethodAnnotation("org.eclipse.core.resources.IProject","getWorkingLocation","(Ljava/lang/String;)Lorg/eclipse/core/runtime/IPath;",
490 // false, NullnessAnnotation.CHECK_FOR_NULL);
513 // db.addMethodAnnotation("org.eclipse.core.resources.IProject","getWorkingLocation","(Ljava/lang/String;)Lorg/eclipse/core/runtime/IPath;",
514 // false, NullnessAnnotation.CHECK_FOR_NULL);
491515
492516 db.addMethodAnnotation("org.eclipse.core.resources.IWorkspaceRoot","getContainerForLocation","(Lorg/eclipse/core/runtime/IPath;)Lorg/eclipse/core/resources/IContainer;",
493517 false, NullnessAnnotation.CHECK_FOR_NULL);
496520
497521 // override annotation from IResource: workspace root is always available
498522 // XXX seems not to work at all...
499 // db.addMethodAnnotation("org.eclipse.core.resources.IWorkspaceRoot","getLocation","()Lorg/eclipse/core/runtime/IPath;",
500 // false, NullnessAnnotation.NONNULL);
523 // db.addMethodAnnotation("org.eclipse.core.resources.IWorkspaceRoot","getLocation","()Lorg/eclipse/core/runtime/IPath;",
524 // false, NullnessAnnotation.NONNULL);
501525
502526 // override annotation from IResource: workspace root has no parent
503527 db.addMethodAnnotation("org.eclipse.core.resources.IWorkspaceRoot","getParent","()Lorg/eclipse/core/resources/IContainer;",
504528 false, NullnessAnnotation.CHECK_FOR_NULL);
505529
506530 // XXX too high rate of false positives, due the problem with IWorkspaceRoot.getLocation() above
507 // db.addMethodAnnotation("org.eclipse.core.resources.IResource","getLocation","()Lorg/eclipse/core/runtime/IPath;",
508 // false, NullnessAnnotation.CHECK_FOR_NULL);
531 // db.addMethodAnnotation("org.eclipse.core.resources.IResource","getLocation","()Lorg/eclipse/core/runtime/IPath;",
532 // false, NullnessAnnotation.CHECK_FOR_NULL);
509533 // XXX too high rate of false positives, due the problem with IWorkspaceRoot.getLocation() above
510 // db.addMethodAnnotation("org.eclipse.core.resources.IResource","getLocationURI","()Ljava/net/URI;",
511 // false, NullnessAnnotation.CHECK_FOR_NULL);
534 // db.addMethodAnnotation("org.eclipse.core.resources.IResource","getLocationURI","()Ljava/net/URI;",
535 // false, NullnessAnnotation.CHECK_FOR_NULL);
512536
513537 db.addMethodAnnotation("org.eclipse.core.resources.IResource","findMarker","(J)Lorg/eclipse/core/resources/IMarker;",
514538 false, NullnessAnnotation.CHECK_FOR_NULL);
515539 db.addMethodAnnotation("org.eclipse.core.resources.IResource","getFileExtension","()Ljava/lang/String;",
516540 false, NullnessAnnotation.CHECK_FOR_NULL);
517541 // only true for workspace root
518 // db.addMethodAnnotation("org.eclipse.core.resources.IResource","getParent","()Lorg/eclipse/core/resources/IContainer;",
519 // false, NullnessAnnotation.CHECK_FOR_NULL);
542 // db.addMethodAnnotation("org.eclipse.core.resources.IResource","getParent","()Lorg/eclipse/core/resources/IContainer;",
543 // false, NullnessAnnotation.CHECK_FOR_NULL);
520544
521545 db.addMethodAnnotation("org.eclipse.core.resources.IResource","getPersistentProperty","(Lorg/eclipse/core/runtime/QualifiedName;)Ljava/lang/String;",
522546 false, NullnessAnnotation.CHECK_FOR_NULL);
523547
524 // db.addMethodAnnotation("org.eclipse.core.resources.IResource","getProject","()Lorg/eclipse/core/resources/IProject;",
525 // false, NullnessAnnotation.CHECK_FOR_NULL);
526
527 // db.addMethodAnnotation("org.eclipse.core.resources.IResource","getRawLocation","()Lorg/eclipse/core/runtime/IPath;",
528 // false, NullnessAnnotation.CHECK_FOR_NULL);
529
530 // db.addMethodAnnotation("org.eclipse.core.resources.IResource","getResourceAttributes","()Lorg/eclipse/core/resources/ResourceAttributes;",
531 // false, NullnessAnnotation.CHECK_FOR_NULL);
548 // db.addMethodAnnotation("org.eclipse.core.resources.IResource","getProject","()Lorg/eclipse/core/resources/IProject;",
549 // false, NullnessAnnotation.CHECK_FOR_NULL);
550
551 // db.addMethodAnnotation("org.eclipse.core.resources.IResource","getRawLocation","()Lorg/eclipse/core/runtime/IPath;",
552 // false, NullnessAnnotation.CHECK_FOR_NULL);
553
554 // db.addMethodAnnotation("org.eclipse.core.resources.IResource","getResourceAttributes","()Lorg/eclipse/core/resources/ResourceAttributes;",
555 // false, NullnessAnnotation.CHECK_FOR_NULL);
532556
533557 db.addMethodAnnotation("org.eclipse.core.resources.IResource","getSessionProperty","(Lorg/eclipse/core/runtime/QualifiedName;)Ljava/lang/Object;",
534558 false, NullnessAnnotation.CHECK_FOR_NULL);
542566 false, NullnessAnnotation.CHECK_FOR_NULL);
543567
544568 // javadoc collision with IFile which claims to implement interface without returning null
545 // db.addMethodAnnotation("org.eclipse.core.resources.IStorage","getFullPath","()Lorg/eclipse/core/runtime/IPath;",
546 // false, NullnessAnnotation.CHECK_FOR_NULL);
569 // db.addMethodAnnotation("org.eclipse.core.resources.IStorage","getFullPath","()Lorg/eclipse/core/runtime/IPath;",
570 // false, NullnessAnnotation.CHECK_FOR_NULL);
547571
548572 // Too many false positives
549 // db.addMethodAnnotation("org.eclipse.core.resources.IMarker","getAttribute","(Ljava/lang/String;)Ljava/lang/Object;",
550 // false, NullnessAnnotation.CHECK_FOR_NULL);
573 // db.addMethodAnnotation("org.eclipse.core.resources.IMarker","getAttribute","(Ljava/lang/String;)Ljava/lang/Object;",
574 // false, NullnessAnnotation.CHECK_FOR_NULL);
551575
552576 db.addMethodAnnotation("org.eclipse.team.core.RepositoryProvider","getProvider","(Lorg/eclipse/core/resources/IProject;)Lorg/eclipse/team/core/RepositoryProvider;",
553577 false, NullnessAnnotation.CHECK_FOR_NULL);
557581 db.addMethodAnnotation("org.eclipse.swt.widgets.Display","getCurrent","()Lorg/eclipse/swt/widgets/Display;",
558582 false, NullnessAnnotation.CHECK_FOR_NULL);
559583
560 // db.addMethodAnnotation("org.eclipse.swt.widgets.Control","getParent","()Lorg/eclipse/swt/widgets/Composite;",
561 // false, NullnessAnnotation.CHECK_FOR_NULL);
584 // db.addMethodAnnotation("org.eclipse.swt.widgets.Control","getParent","()Lorg/eclipse/swt/widgets/Composite;",
585 // false, NullnessAnnotation.CHECK_FOR_NULL);
562586
563587 // Usually checked or used only if set before
564 // db.addMethodAnnotation("org.eclipse.swt.widgets.Widget","getData","()Ljava/lang/Object;",
565 // false, NullnessAnnotation.CHECK_FOR_NULL);
566 // db.addMethodAnnotation("org.eclipse.swt.widgets.Widget","getData","(Ljava/lang/String;)Ljava/lang/Object;",
567 // false, NullnessAnnotation.CHECK_FOR_NULL);
588 // db.addMethodAnnotation("org.eclipse.swt.widgets.Widget","getData","()Ljava/lang/Object;",
589 // false, NullnessAnnotation.CHECK_FOR_NULL);
590 // db.addMethodAnnotation("org.eclipse.swt.widgets.Widget","getData","(Ljava/lang/String;)Ljava/lang/Object;",
591 // false, NullnessAnnotation.CHECK_FOR_NULL);
568592
569593 // there is a way to ask selection before - so we can't just always warn
570 // db.addMethodAnnotation("org.eclipse.jface.viewers.IStructuredSelection","getFirstElement","()Ljava/lang/Object;",
571 // false, NullnessAnnotation.CHECK_FOR_NULL);
594 // db.addMethodAnnotation("org.eclipse.jface.viewers.IStructuredSelection","getFirstElement","()Ljava/lang/Object;",
595 // false, NullnessAnnotation.CHECK_FOR_NULL);
572596
573597 // too many false positives
574 // db.addMethodAnnotation("org.eclipse.jface.viewers.ISelectionProvider","getSelection","()Lorg/eclipse/jface/viewers/ISelection;",
575 // false, NullnessAnnotation.CHECK_FOR_NULL);
598 // db.addMethodAnnotation("org.eclipse.jface.viewers.ISelectionProvider","getSelection","()Lorg/eclipse/jface/viewers/ISelection;",
599 // false, NullnessAnnotation.CHECK_FOR_NULL);
576600
577601 db.addMethodAnnotation("org.eclipse.jface.viewers.ITreeContentProvider","getParent","(Ljava/lang/Object;)Ljava/lang/Object;",
578602 false, NullnessAnnotation.CHECK_FOR_NULL);
584608 false, NullnessAnnotation.CHECK_FOR_NULL);
585609
586610 // too many false positives
587 // db.addMethodAnnotation("org.eclipse.jface.viewers.ILabelProvider","getText","(Ljava/lang/Object;)Ljava/lang/String;",
588 // false, NullnessAnnotation.CHECK_FOR_NULL);
611 // db.addMethodAnnotation("org.eclipse.jface.viewers.ILabelProvider","getText","(Ljava/lang/Object;)Ljava/lang/String;",
612 // false, NullnessAnnotation.CHECK_FOR_NULL);
589613
590614 db.addMethodAnnotation("org.eclipse.jface.viewers.IFontProvider","getFont","(Ljava/lang/Object;)Lorg/eclipse/swt/graphics/Font;",
591615 false, NullnessAnnotation.CHECK_FOR_NULL);
617641 false, NullnessAnnotation.CHECK_FOR_NULL);
618642
619643 // too many false positives
620 // db.addMethodAnnotation("org.eclipse.jface.viewers.ITableLabelProvider","getColumnText","(Ljava/lang/Object;I)Ljava/lang/String;",
621 // false, NullnessAnnotation.CHECK_FOR_NULL);
644 // db.addMethodAnnotation("org.eclipse.jface.viewers.ITableLabelProvider","getColumnText","(Ljava/lang/Object;I)Ljava/lang/String;",
645 // false, NullnessAnnotation.CHECK_FOR_NULL);
622646
623647 db.addMethodAnnotation("org.eclipse.ui.IWorkbenchPage","findView","(Ljava/lang/String;)Lorg/eclipse/ui/IViewPart;",
624648 false, NullnessAnnotation.CHECK_FOR_NULL);
629653 db.addMethodAnnotation("org.eclipse.ui.IWorkbenchPage","findViewReference","(Ljava/lang/String;Ljava/lang/String;)Lorg/eclipse/ui/IViewReference;",
630654 false, NullnessAnnotation.CHECK_FOR_NULL);
631655
632 // Too many false positives if used from editor, which is active anyway
633 // db.addMethodAnnotation("org.eclipse.ui.IWorkbenchPage","getActiveEditor","()Lorg/eclipse/ui/IEditorPart;",
634 // false, NullnessAnnotation.CHECK_FOR_NULL);
656 // Too many false positives if used from editor, which is active anyway
657 // db.addMethodAnnotation("org.eclipse.ui.IWorkbenchPage","getActiveEditor","()Lorg/eclipse/ui/IEditorPart;",
658 // false, NullnessAnnotation.CHECK_FOR_NULL);
635659
636660 // too many false positives
637 // db.addMethodAnnotation("org.eclipse.ui.IWorkbenchPage","openEditor","(Lorg/eclipse/ui/IEditorInput;Ljava/lang/String;)Lorg/eclipse/ui/IEditorPart;",
638 // false, NullnessAnnotation.CHECK_FOR_NULL);
639 // db.addMethodAnnotation("org.eclipse.ui.IWorkbenchPage","openEditor","(Lorg/eclipse/ui/IEditorInput;Ljava/lang/String;ZI)Lorg/eclipse/ui/IEditorPart;",
640 // false, NullnessAnnotation.CHECK_FOR_NULL);
641
642 // db.addMethodAnnotation("org.eclipse.ui.IWorkbenchWindow","getActivePage","()Lorg/eclipse/ui/IWorkbenchPage;",
643 // false, NullnessAnnotation.CHECK_FOR_NULL);
661 // db.addMethodAnnotation("org.eclipse.ui.IWorkbenchPage","openEditor","(Lorg/eclipse/ui/IEditorInput;Ljava/lang/String;)Lorg/eclipse/ui/IEditorPart;",
662 // false, NullnessAnnotation.CHECK_FOR_NULL);
663 // db.addMethodAnnotation("org.eclipse.ui.IWorkbenchPage","openEditor","(Lorg/eclipse/ui/IEditorInput;Ljava/lang/String;ZI)Lorg/eclipse/ui/IEditorPart;",
664 // false, NullnessAnnotation.CHECK_FOR_NULL);
665
666 // db.addMethodAnnotation("org.eclipse.ui.IWorkbenchWindow","getActivePage","()Lorg/eclipse/ui/IWorkbenchPage;",
667 // false, NullnessAnnotation.CHECK_FOR_NULL);
644668
645669 // too much noise because if a class is used in UI context, there is an active window.
646 // db.addMethodAnnotation("org.eclipse.ui.IWorkbench","getActiveWorkbenchWindow","()Lorg/eclipse/ui/IWorkbenchWindow;",
647 // false, NullnessAnnotation.CHECK_FOR_NULL);
648
649 // db.addMethodAnnotation("org.eclipse.jface.wizard.IWizard","getContainer","()Lorg/eclipse/jface/wizard/IWizardContainer;",
650 // false, NullnessAnnotation.CHECK_FOR_NULL);
651 // db.addMethodAnnotation("org.eclipse.jface.wizard.IWizard","getDialogSettings","()Lorg/eclipse/jface/dialogs/IDialogSettings;",
652 // false, NullnessAnnotation.CHECK_FOR_NULL);
653 // db.addMethodAnnotation("org.eclipse.jface.wizard.IWizard","getNextPage","(Lorg/eclipse/jface/wizard/IWizardPage;)Lorg/eclipse/jface/wizard/IWizardPage;",
654 // false, NullnessAnnotation.CHECK_FOR_NULL);
655 // db.addMethodAnnotation("org.eclipse.jface.wizard.IWizard","getPreviousPage","(Lorg/eclipse/jface/wizard/IWizardPage;)Lorg/eclipse/jface/wizard/IWizardPage;",
656 // false, NullnessAnnotation.CHECK_FOR_NULL);
657 // db.addMethodAnnotation("org.eclipse.jface.wizard.IWizard","getWindowTitle","()Ljava/lang/String;",
658 // false, NullnessAnnotation.CHECK_FOR_NULL);
659 // db.addMethodAnnotation("org.eclipse.jface.wizard.IWizard","getPage","(Ljava/lang/String;)Lorg/eclipse/jface/wizard/IWizardPage;",
660 // false, NullnessAnnotation.CHECK_FOR_NULL);
661 //
662 // db.addMethodAnnotation("org.eclipse.jface.wizard.IWizardPage","getNextPage","()Lorg/eclipse/jface/wizard/IWizardPage;",
663 // false, NullnessAnnotation.CHECK_FOR_NULL);
664 // db.addMethodAnnotation("org.eclipse.jface.wizard.IWizardPage","getPreviousPage","()Lorg/eclipse/jface/wizard/IWizardPage;",
665 // false, NullnessAnnotation.CHECK_FOR_NULL);
666 // db.addMethodAnnotation("org.eclipse.jface.wizard.IWizardPage","getWizard","()Lorg/eclipse/jface/wizard/IWizard;",
667 // false, NullnessAnnotation.CHECK_FOR_NULL);
670 // db.addMethodAnnotation("org.eclipse.ui.IWorkbench","getActiveWorkbenchWindow","()Lorg/eclipse/ui/IWorkbenchWindow;",
671 // false, NullnessAnnotation.CHECK_FOR_NULL);
672
673 // db.addMethodAnnotation("org.eclipse.jface.wizard.IWizard","getContainer","()Lorg/eclipse/jface/wizard/IWizardContainer;",
674 // false, NullnessAnnotation.CHECK_FOR_NULL);
675 // db.addMethodAnnotation("org.eclipse.jface.wizard.IWizard","getDialogSettings","()Lorg/eclipse/jface/dialogs/IDialogSettings;",
676 // false, NullnessAnnotation.CHECK_FOR_NULL);
677 // db.addMethodAnnotation("org.eclipse.jface.wizard.IWizard","getNextPage","(Lorg/eclipse/jface/wizard/IWizardPage;)Lorg/eclipse/jface/wizard/IWizardPage;",
678 // false, NullnessAnnotation.CHECK_FOR_NULL);
679 // db.addMethodAnnotation("org.eclipse.jface.wizard.IWizard","getPreviousPage","(Lorg/eclipse/jface/wizard/IWizardPage;)Lorg/eclipse/jface/wizard/IWizardPage;",
680 // false, NullnessAnnotation.CHECK_FOR_NULL);
681 // db.addMethodAnnotation("org.eclipse.jface.wizard.IWizard","getWindowTitle","()Ljava/lang/String;",
682 // false, NullnessAnnotation.CHECK_FOR_NULL);
683 // db.addMethodAnnotation("org.eclipse.jface.wizard.IWizard","getPage","(Ljava/lang/String;)Lorg/eclipse/jface/wizard/IWizardPage;",
684 // false, NullnessAnnotation.CHECK_FOR_NULL);
685 //
686 // db.addMethodAnnotation("org.eclipse.jface.wizard.IWizardPage","getNextPage","()Lorg/eclipse/jface/wizard/IWizardPage;",
687 // false, NullnessAnnotation.CHECK_FOR_NULL);
688 // db.addMethodAnnotation("org.eclipse.jface.wizard.IWizardPage","getPreviousPage","()Lorg/eclipse/jface/wizard/IWizardPage;",
689 // false, NullnessAnnotation.CHECK_FOR_NULL);
690 // db.addMethodAnnotation("org.eclipse.jface.wizard.IWizardPage","getWizard","()Lorg/eclipse/jface/wizard/IWizard;",
691 // false, NullnessAnnotation.CHECK_FOR_NULL);
668692
669693 // usually statically assigned resources - so either it never worked or always ok. Too noisy
670 // db.addMethodAnnotation("org.eclipse.jface.resource.ImageRegistry","getDescriptor","(Ljava/lang/String;)Lorg/eclipse/jface/resource/ImageDescriptor;",
671 // false, NullnessAnnotation.CHECK_FOR_NULL);
672 // db.addMethodAnnotation("org.eclipse.jface.resource.ImageRegistry","get","(Ljava/lang/String;)Lorg/eclipse/swt/graphics/Image;",
673 // false, NullnessAnnotation.CHECK_FOR_NULL);
674 //
675 // db.addMethodAnnotation("org.eclipse.jface.resource.ColorRegistry","get","(Ljava/lang/String;)Lorg/eclipse/swt/graphics/Color;",
676 // false, NullnessAnnotation.CHECK_FOR_NULL);
677 // db.addMethodAnnotation("org.eclipse.jface.resource.ColorRegistry","getRGB","(Ljava/lang/String;)Lorg/eclipse/swt/graphics/RGB;",
678 // false, NullnessAnnotation.CHECK_FOR_NULL);
679 //
680 // db.addMethodAnnotation("org.eclipse.jface.resource.JFaceResources","getImage","(Ljava/lang/String;)Lorg/eclipse/swt/graphics/Image;",
681 // false, NullnessAnnotation.CHECK_FOR_NULL);
682
683 // db.addMethodAnnotation("org.eclipse.jface.action.IAction","getImageDescriptor","()Lorg/eclipse/jface/resource/ImageDescriptor;",
684 // false, NullnessAnnotation.CHECK_FOR_NULL);
694 // db.addMethodAnnotation("org.eclipse.jface.resource.ImageRegistry","getDescriptor","(Ljava/lang/String;)Lorg/eclipse/jface/resource/ImageDescriptor;",
695 // false, NullnessAnnotation.CHECK_FOR_NULL);
696 // db.addMethodAnnotation("org.eclipse.jface.resource.ImageRegistry","get","(Ljava/lang/String;)Lorg/eclipse/swt/graphics/Image;",
697 // false, NullnessAnnotation.CHECK_FOR_NULL);
698 //
699 // db.addMethodAnnotation("org.eclipse.jface.resource.ColorRegistry","get","(Ljava/lang/String;)Lorg/eclipse/swt/graphics/Color;",
700 // false, NullnessAnnotation.CHECK_FOR_NULL);
701 // db.addMethodAnnotation("org.eclipse.jface.resource.ColorRegistry","getRGB","(Ljava/lang/String;)Lorg/eclipse/swt/graphics/RGB;",
702 // false, NullnessAnnotation.CHECK_FOR_NULL);
703 //
704 // db.addMethodAnnotation("org.eclipse.jface.resource.JFaceResources","getImage","(Ljava/lang/String;)Lorg/eclipse/swt/graphics/Image;",
705 // false, NullnessAnnotation.CHECK_FOR_NULL);
706
707 // db.addMethodAnnotation("org.eclipse.jface.action.IAction","getImageDescriptor","()Lorg/eclipse/jface/resource/ImageDescriptor;",
708 // false, NullnessAnnotation.CHECK_FOR_NULL);
685709 }
686710 }
2020
2121 /**
2222 * Algorithm to perform a depth first search on a CFG.
23 *
23 *
2424 * @see CFG
2525 * @author David Hovemeyer
2626 */
2929
3030 /**
3131 * Constructor.
32 *
32 *
3333 * @param cfg
3434 * the CFG to perform the depth first search on
3535 */
4646 }
4747 }
4848
49 // vim:ts=4
2020
2121 /**
2222 * Dataflow analysis to compute dominator sets for a CFG.
23 *
23 *
2424 * @author David Hovemeyer
2525 * @see CFG
2626 * @see AbstractDominatorsAnalysis
2727 */
2828 public class DominatorsAnalysis extends AbstractDominatorsAnalysis {
29 private DepthFirstSearch dfs;
29 private final DepthFirstSearch dfs;
3030
3131 /**
3232 * Constructor.
33 *
33 *
3434 * @param cfg
3535 * the CFG to compute dominator relationships for
3636 * @param dfs
4343 this.dfs = dfs;
4444 }
4545
46 @Override
4647 public boolean isForwards() {
4748 return true;
4849 }
4950
51 @Override
5052 public BlockOrder getBlockOrder(CFG cfg) {
5153 return new ReversePostOrder(cfg, dfs);
5254 }
9193 // }
9294 }
9395
94 // vim:ts=4
3333
3434 /**
3535 * An edge of a control flow graph.
36 *
36 *
3737 * @author David Hovemeyer
3838 * @see BasicBlock
3939 * @see CFG
6565
6666 /**
6767 * Constructor.
68 *
68 *
6969 * @param source
7070 * source basic block
7171 * @param dest
110110
111111 /**
112112 * Return if given edge flag is set.
113 *
113 *
114114 * @param flag
115115 * the edge flag
116116 * @return true if the flag is set, false otherwise
128128
129129 @Override
130130 public boolean equals(Object o) {
131 if (o == null || this.getClass() != o.getClass())
131 if (o == null || this.getClass() != o.getClass()) {
132132 return false;
133 }
133134 Edge other = (Edge) o;
134135 return this.getSource() == other.getSource() && this.getTarget() == other.getTarget()
135136 && this.getType() == other.getType();
146147 @Override
147148 public int compareTo(Edge other) {
148149 int cmp = super.compareTo(other);
149 if (cmp != 0)
150 if (cmp != 0) {
150151 return cmp;
152 }
151153 return type - other.type;
152154 }
153155
163165 InstructionHandle sourceInstruction = source.getLastInstruction();
164166 InstructionHandle targetInstruction = target.getFirstInstruction();
165167
166 if (targetInstruction == null || sourceInstruction == null)
168 if (targetInstruction == null || sourceInstruction == null) {
167169 return false;
170 }
168171 return targetInstruction.getPosition() < sourceInstruction.getPosition();
169172
170173 }
178181
179182 InstructionHandle sourceInstruction = source.getLastInstruction();
180183
181 if (sourceInstruction == null)
184 if (sourceInstruction == null) {
182185 return false;
186 }
183187 return positions.contains(sourceInstruction.getPosition());
184188
185189 }
214218 buf.append(targetInstruction.getPosition());
215219 buf.append(']');
216220 } else if (source.isExceptionThrower()) {
217 if (type == FALL_THROUGH_EDGE)
221 if (type == FALL_THROUGH_EDGE) {
218222 buf.append(" [successful check]");
219 else {
223 } else {
220224 buf.append(" [failed check for ");
221225 buf.append(source.getExceptionThrower().getPosition());
222226 if (targetInstruction != null) {
273277 int stringToEdgeType(String s) {
274278 s = s.toUpperCase(Locale.ENGLISH);
275279
276 if (s.equals("FALL_THROUGH"))
280 if ("FALL_THROUGH".equals(s)) {
277281 return FALL_THROUGH_EDGE;
278 else if (s.equals("IFCMP"))
282 } else if ("IFCMP".equals(s)) {
279283 return IFCMP_EDGE;
280 else if (s.equals("SWITCH"))
284 } else if ("SWITCH".equals(s)) {
281285 return SWITCH_EDGE;
282 else if (s.equals("SWITCH_DEFAULT"))
286 } else if ("SWITCH_DEFAULT".equals(s)) {
283287 return SWITCH_DEFAULT_EDGE;
284 else if (s.equals("JSR"))
288 } else if ("JSR".equals(s)) {
285289 return JSR_EDGE;
286 else if (s.equals("RET"))
290 } else if ("RET".equals(s)) {
287291 return RET_EDGE;
288 else if (s.equals("GOTO"))
292 } else if ("GOTO".equals(s)) {
289293 return GOTO_EDGE;
290 else if (s.equals("RETURN"))
294 } else if ("RETURN".equals(s)) {
291295 return RETURN_EDGE;
292 else if (s.equals("UNHANDLED_EXCEPTION"))
296 } else if ("UNHANDLED_EXCEPTION".equals(s)) {
293297 return UNHANDLED_EXCEPTION_EDGE;
294 else if (s.equals("HANDLED_EXCEPTION"))
298 } else if ("HANDLED_EXCEPTION".equals(s)) {
295299 return HANDLED_EXCEPTION_EDGE;
296 else if (s.equals("START"))
300 } else if ("START".equals(s)) {
297301 return START_EDGE;
298 else if (s.equals("BACKEDGE_TARGET_EDGE"))
302 } else if ("BACKEDGE_TARGET_EDGE".equals(s)) {
299303 return BACKEDGE_TARGET_EDGE;
300 else if (s.equals("BACKEDGE_SOURCE_EDGE"))
304 } else if ("BACKEDGE_SOURCE_EDGE".equals(s)) {
301305 return BACKEDGE_SOURCE_EDGE;
302 else if (s.equals("EXIT_EDGE"))
306 } else if ("EXIT_EDGE".equals(s)) {
303307 return EXIT_EDGE;
304 else
308 } else {
305309 throw new IllegalArgumentException("Unknown edge type: " + s);
310 }
306311 }
307312 }
308313
309 // vim:ts=4
2020
2121 /**
2222 * Interface for choosing CFG Edges.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public interface EdgeChooser {
2727 /**
2828 * Return whether or not given Edge should be chosen.
29 *
29 *
3030 * @param edge
3131 * the Edge
3232 * @return true if the Edge should be chosen, false otherwise
2121 /**
2222 * Constants defining the type of control flow edges, as well as flags defining
2323 * additional information about the edges.
24 *
24 *
2525 * @see Edge
2626 */
2727 public interface EdgeTypes {
142142 public static final int EXPLICIT_EXCEPTIONS_FLAG = 2;
143143 }
144144
145 // vim:ts=3
3838 * to lists of CodeExceptionGen objects. This class also maps instructions which
3939 * are the start of exception handlers to the CodeExceptionGen object
4040 * representing the handler.
41 *
41 *
4242 * @author David Hovemeyer
4343 */
4444 public class ExceptionHandlerMap {
45 private IdentityHashMap<InstructionHandle, List<CodeExceptionGen>> codeToHandlerMap;
45 private final IdentityHashMap<InstructionHandle, List<CodeExceptionGen>> codeToHandlerMap;
4646
47 private IdentityHashMap<InstructionHandle, CodeExceptionGen> startInstructionToHandlerMap;
47 private final IdentityHashMap<InstructionHandle, CodeExceptionGen> startInstructionToHandlerMap;
4848
49 private TypeMerger merger;
49 private final TypeMerger merger;
5050 /**
5151 * Constructor.
52 *
52 *
5353 * @param methodGen
5454 * the method to build the map for
5555 */
6565 * specified to handle exceptions for the instruction whose handle is given.
6666 * Note that the handlers in the returned list are <b>in order of
6767 * priority</b>, as defined in the method's exception handler table.
68 *
68 *
6969 * @param handle
7070 * the handle of the instruction we want the exception handlers
7171 * for
7979 /**
8080 * If the given instruction is the start of an exception handler, get the
8181 * CodeExceptionGen object representing the handler.
82 *
82 *
8383 * @param start
8484 * the instruction
8585 * @return the CodeExceptionGen object, or null if the instruction is not
114114 // i.e., an ANY handler, or catch(Throwable...),
115115 // then no further (lower-priority)
116116 // handlers are reachable from the instruction.
117 if (Hierarchy.isUniversalExceptionHandler(exceptionHandler.getCatchType()))
117 if (Hierarchy.isUniversalExceptionHandler(exceptionHandler.getCatchType())) {
118118 break handlerLoop;
119 }
119120 }
120121 }
121122
124125 }
125126
126127 public static CodeExceptionGen merge(@CheckForNull TypeMerger m, CodeExceptionGen e1, CodeExceptionGen e2) {
127 if (e1 == null) return e2;
128 if (e2 == null) return e1;
129 if (m == null)
128 if (e1 == null) {
129 return e2;
130 }
131 if (e2 == null) {
130132 return e1;
133 }
134 if (m == null) {
135 return e1;
136 }
131137 if ( ! e1.getHandlerPC().equals( e2.getHandlerPC() ) ){
132138 // log error
133 return e1;
139 return e1;
134140 }
135141 try {
136142 Type t = m.mergeTypes(e1.getCatchType(), e2.getCatchType());
142148 }
143149 }
144150
145
151
146152 private void addExceptionHandler(CodeExceptionGen exceptionHandler) {
147153 InstructionHandle handlerPC = exceptionHandler.getHandlerPC();
148154 CodeExceptionGen existing = startInstructionToHandlerMap.get(handlerPC);
151157 }
152158 startInstructionToHandlerMap.put(handlerPC, exceptionHandler);
153159 }
154
160
155161 private void addHandler(InstructionHandle handle, CodeExceptionGen exceptionHandler) {
156162 List<CodeExceptionGen> handlerList = codeToHandlerMap.get(handle);
157163 if (handlerList == null) {
162168 }
163169 }
164170
165 // vim:ts=4
2727 import java.util.Set;
2828
2929 import javax.annotation.CheckForNull;
30 import javax.annotation.Nullable;
3031
3132 import org.apache.bcel.generic.Type;
3233
6263 private boolean complete = false;
6364
6465 public OpcodeStack.Item getSummary(XField field) {
65 if (field == null)
66 if (field == null) {
6667 return new OpcodeStack.Item();
68 }
6769
6870 OpcodeStack.Item result = summary.get(field);
6971 if (result == null || field.isVolatile()) {
8284 while (true) {
8385 XClass cx = Global.getAnalysisCache().getClassAnalysis(XClass.class, c);
8486 c = cx.getSuperclassDescriptor();
85 if (c == null)
87 if (c == null) {
8688 return false;
87 if (callsOverriddenMethodsFromConstructor(c))
89 }
90 if (callsOverriddenMethodsFromConstructor(c)) {
8891 return true;
92 }
8993 }
9094 } catch (CheckedAnalysisException e) {
9195 return false;
106110
107111 public Set<ProgramPoint> getCalledFromSuperConstructor(ClassDescriptor superClass, XMethod calledFromConstructor) {
108112
109 if (!callsOverriddenMethodsFromConstructor.contains(superClass))
113 if (!callsOverriddenMethodsFromConstructor.contains(superClass)) {
110114 return Collections.emptySet();
115 }
111116 for (Map.Entry<XMethod, Set<ProgramPoint>> e : selfMethodsCalledFromConstructor.entrySet()) {
112117 XMethod m = e.getKey();
113118 if (m.getName().equals(calledFromConstructor.getName())
116121 String sig2 = calledFromConstructor.getSignature();
117122 sig1 = sig1.substring(0, sig1.indexOf(')'));
118123 sig2 = sig2.substring(0, sig2.indexOf(')'));
119 if (sig1.equals(sig2))
124 if (sig1.equals(sig2)) {
120125 return e.getValue();
126 }
121127 }
122128 }
123129
126132 }
127133
128134 public void setFieldsWritten(XMethod method, Collection<XField> fields) {
129 if (fields.isEmpty())
130 return;
135 if (fields.isEmpty()) {
136 return;
137 }
131138 if (fields.size() == 1) {
132139 fieldsWritten.put(method, Collections.singleton(Util.first(fields)));
133140 return;
136143 fieldsWritten.put(method, Util.makeSmallHashSet(fields));
137144 }
138145
139 public Set<XField> getFieldsWritten(XMethod method) {
146 public Set<XField> getFieldsWritten(@Nullable XMethod method) {
140147 Set<XField> result = fieldsWritten.get(method);
141 if (result == null)
148 if (result == null) {
142149 return Collections.<XField> emptySet();
150 }
143151 return result;
144152 }
145153
146154 public boolean isWrittenOutsideOfConstructor(XField field) {
147 if (field.isFinal())
155 if (field.isFinal()) {
148156 return false;
149 if (writtenOutsideOfConstructor.contains(field))
157 }
158 if (writtenOutsideOfConstructor.contains(field)) {
150159 return true;
151 if (!AnalysisContext.currentAnalysisContext().unreadFieldsAvailable())
160 }
161 if (!AnalysisContext.currentAnalysisContext().unreadFieldsAvailable()) {
152162 return true;
163 }
153164 UnreadFieldsData unreadFields = AnalysisContext.currentAnalysisContext().getUnreadFieldsData();
154 if (unreadFields.isReflexive(field))
165 if (unreadFields.isReflexive(field)) {
155166 return true;
167 }
156168 return false;
157169 }
158170
227239 return complete;
228240 }
229241
230 /**
231 * @param method
232 * @param methodOperand
233 */
234242 public void sawSuperCall(XMethod from, XMethod constructorInSuperClass) {
235 if (constructorInSuperClass == null || from == null)
236 return;
237 if (constructorInSuperClass.getSignature().equals("()V"))
238 return;
243 if (constructorInSuperClass == null || from == null) {
244 return;
245 }
246 if ("()V".equals(constructorInSuperClass.getSignature())) {
247 return;
248 }
239249 nonVoidSuperConstructorsCalled.put(from, constructorInSuperClass);
240250
241251 }
2828 * Data source for source files which are stored in the filesystem.
2929 */
3030 public class FileSourceFileDataSource implements SourceFileDataSource {
31 private String fileName;
31 private final String fileName;
3232
3333 public FileSourceFileDataSource(String fileName) {
3434 this.fileName = fileName;
3535 }
3636
37 @Override
3738 public InputStream open() throws IOException {
3839 return new BufferedInputStream(new FileInputStream(fileName));
3940 }
4041
42 @Override
4143 public String getFullFileName() {
4244 return fileName;
4345 }
4446
45 public long getLastModified() {
47 @Override
48 public long getLastModified() {
4649 return new File(fileName).lastModified();
4750 }
4851 }
4952
50 // vim:ts=4
2121 /**
2222 * Abstract base class for forward dataflow analyses. Provides convenient
2323 * implementations for isForwards() and getBlockOrder() methods.
24 *
24 *
2525 * @author David Hovemeyer
2626 * @see Dataflow
2727 * @see DataflowAnalysis
2828 */
2929 public abstract class ForwardDataflowAnalysis<Fact> extends AbstractDataflowAnalysis<Fact> {
30 private DepthFirstSearch dfs;
30 private final DepthFirstSearch dfs;
3131
3232 public ForwardDataflowAnalysis(DepthFirstSearch dfs) {
33 if (dfs == null)
33 if (dfs == null) {
3434 throw new IllegalArgumentException();
35 }
3536 this.dfs = dfs;
3637 }
3738
3940 return dfs;
4041 }
4142
43 @Override
4244 public boolean isForwards() {
4345 return true;
4446 }
4547
48 @Override
4649 public BlockOrder getBlockOrder(CFG cfg) {
4750 return new ReversePostOrder(cfg, dfs);
4851 }
4952 }
5053
51 // vim:ts=4
110110 public Frame(int numLocals) {
111111 this.numLocals = numLocals;
112112 this.slotList = new ArrayList<ValueType>(numLocals + DEFAULT_STACK_CAPACITY);
113 for (int i = 0; i < numLocals; ++i)
113 for (int i = 0; i < numLocals; ++i) {
114114 slotList.add(null);
115 }
115116 }
116117
117118 /**
170171 * the ValueType to push
171172 */
172173 public void pushValue(ValueType value) {
173 if (VERIFY_INTEGRITY && value == null)
174 if (VERIFY_INTEGRITY && value == null) {
174175 throw new IllegalArgumentException();
175 if (!isValid())
176 }
177 if (!isValid()) {
176178 throw new IllegalStateException("accessing top or bottom frame");
179 }
177180 slotList.add(value);
178181 }
179182
185188 * if the Java operand stack is empty
186189 */
187190 public ValueType popValue() throws DataflowAnalysisException {
188 if (!isValid())
191 if (!isValid()) {
189192 throw new DataflowAnalysisException("accessing top or bottom frame");
190 if (slotList.size() == numLocals)
193 }
194 if (slotList.size() == numLocals) {
191195 throw new DataflowAnalysisException("operand stack empty");
196 }
192197 return slotList.remove(slotList.size() - 1);
193198 }
194199
199204 * if the Java operand stack is empty
200205 */
201206 public ValueType getTopValue() throws DataflowAnalysisException {
202 if (!isValid())
207 if (!isValid()) {
203208 throw new DataflowAnalysisException("accessing top or bottom frame");
209 }
204210 assert slotList.size() >= numLocals;
205 if (slotList.size() == numLocals)
211 if (slotList.size() == numLocals) {
206212 throw new DataflowAnalysisException("operand stack is empty");
213 }
207214 return slotList.get(slotList.size() - 1);
208215 }
209216
214221 */
215222 public void getTopStackWords(ValueType[] valueList) throws DataflowAnalysisException {
216223 int stackDepth = getStackDepth();
217 if (valueList.length > stackDepth)
224 if (valueList.length > stackDepth) {
218225 throw new DataflowAnalysisException("not enough values on stack");
226 }
219227 int numSlots = slotList.size();
220228 for (int i = numSlots - valueList.length, j = 0; i < numSlots; ++i, ++j) {
221229 valueList[j] = slotList.get(i);
230238 * 0)
231239 */
232240 public ValueType getStackValue(int loc) throws DataflowAnalysisException {
233 if (!isValid())
241 if (!isValid()) {
234242 throw new DataflowAnalysisException("Accessing TOP or BOTTOM frame!");
243 }
235244 int stackDepth = getStackDepth();
236 if (loc >= stackDepth)
245 if (loc >= stackDepth) {
237246 throw new DataflowAnalysisException("not enough values on stack: access=" + loc + ", avail=" + stackDepth);
238 if (loc < 0)
247 }
248 if (loc < 0) {
239249 throw new DataflowAnalysisException("can't get position " + loc + " of stack");
250 }
240251 int pos = slotList.size() - (loc + 1);
241252 return slotList.get(pos);
242253 }
250261 */
251262 public int getStackLocation(int loc) throws DataflowAnalysisException {
252263 int stackDepth = getStackDepth();
253 if (loc >= stackDepth)
264 if (loc >= stackDepth) {
254265 throw new DataflowAnalysisException("not enough values on stack: access=" + loc + ", avail=" + stackDepth);
266 }
255267 return slotList.size() - (loc + 1);
256268 }
257269
291303 */
292304 public int getInstanceStackLocation(Instruction ins, ConstantPoolGen cpg) throws DataflowAnalysisException {
293305 int numConsumed = ins.consumeStack(cpg);
294 if (numConsumed == Constants.UNPREDICTABLE)
306 if (numConsumed == Constants.UNPREDICTABLE) {
295307 throw new DataflowAnalysisException("Unpredictable stack consumption in " + ins);
308 }
296309 return numConsumed - 1;
297310 }
298311
312325 throw new DataflowAnalysisException("Accessing invalid frame at " + ins);
313326 }
314327 int numConsumed = ins.consumeStack(cpg);
315 if (numConsumed == Constants.UNPREDICTABLE)
328 if (numConsumed == Constants.UNPREDICTABLE) {
316329 throw new DataflowAnalysisException("Unpredictable stack consumption in " + ins);
317 if (numConsumed > getStackDepth())
330 }
331 if (numConsumed > getStackDepth()) {
318332 throw new DataflowAnalysisException("Stack underflow " + ins);
333 }
319334 return getNumSlots() - numConsumed;
320335 }
321336
348363 public int getNumArgumentsIncludingObjectInstance(InvokeInstruction ins, ConstantPoolGen cpg)
349364 throws DataflowAnalysisException {
350365 int numConsumed = ins.consumeStack(cpg);
351 if (numConsumed == Constants.UNPREDICTABLE)
366 if (numConsumed == Constants.UNPREDICTABLE) {
352367 throw new DataflowAnalysisException("Unpredictable stack consumption in " + ins);
368 }
353369 return numConsumed;
354370 }
355371
388404 */
389405 public ValueType getArgument(InvokeInstruction ins, ConstantPoolGen cpg, int i, SignatureParser sigParser)
390406 throws DataflowAnalysisException {
391 if (i >= sigParser.getNumParameters())
407 if (i >= sigParser.getNumParameters()) {
392408 throw new IllegalArgumentException("requesting parameter # " + i + " of " + sigParser);
409 }
393410 return getStackValue(sigParser.getSlotsFromTopOfStackForParameter(i));
394411 }
395412
405422 * @return slot containing the argument value
406423 */
407424 public int getArgumentSlot(int i, int numArguments) {
408 if (i >= numArguments)
425 if (i >= numArguments) {
409426 throw new IllegalArgumentException();
427 }
410428
411429 return (slotList.size() - numArguments) + i;
412430 }
425443 */
426444 public ValueType getOperand(StackConsumer ins, ConstantPoolGen cpg, int i) throws DataflowAnalysisException {
427445 int numOperands = ins.consumeStack(cpg);
428 if (numOperands == Constants.UNPREDICTABLE)
446 if (numOperands == Constants.UNPREDICTABLE) {
429447 throw new DataflowAnalysisException("Unpredictable stack consumption in " + ins);
448 }
430449 return getStackValue((numOperands - 1) - i);
431450 }
432451
452471
453472 for (int i = 0; i < sigParser.getNumParameters(); ++i) {
454473 ValueType value = getArgument(invokeInstruction, cpg, i, sigParser);
455 if (chooser.choose(value))
474 if (chooser.choose(value)) {
456475 chosenArgSet.set(i);
476 }
457477 }
458478
459479 return chosenArgSet;
464484 * the frame.
465485 */
466486 public void clearStack() {
467 if (!isValid())
487 if (!isValid()) {
468488 throw new IllegalStateException("accessing top or bottom frame");
489 }
469490 assert slotList.size() >= numLocals;
470 if (slotList.size() > numLocals)
491 if (slotList.size() > numLocals) {
471492 slotList.subList(numLocals, slotList.size()).clear();
493 }
472494 }
473495
474496 /**
493515 }
494516
495517 public boolean contains(ValueType value) {
496 if (!isValid())
518 if (!isValid()) {
497519 throw new IllegalStateException("accessing top or bottom frame");
498 for (ValueType v : slotList)
499 if (v.equals(value))
520 }
521 for (ValueType v : slotList) {
522 if (v.equals(value)) {
500523 return true;
524 }
525 }
501526 return false;
502527 }
503528
509534 * @return the value in the slot
510535 */
511536 public ValueType getValue(int n) {
512 if (!isValid())
537 if (!isValid()) {
513538 throw new IllegalStateException("accessing top or bottom frame");
539 }
514540 return slotList.get(n);
515541 }
516542
523549 * the value to set
524550 */
525551 public void setValue(int n, ValueType value) {
526 if (VERIFY_INTEGRITY && value == null)
552 if (VERIFY_INTEGRITY && value == null) {
527553 throw new IllegalArgumentException();
528 if (!isValid())
554 }
555 if (!isValid()) {
529556 throw new IllegalStateException("accessing top or bottom frame");
557 }
530558 slotList.set(n, value);
531559 }
532560
539567 * @return true if the frames are the same, false otherwise
540568 */
541569 public boolean sameAs(Frame<ValueType> other) {
542 if (isTop != other.isTop)
570 if (isTop != other.isTop) {
543571 return false;
544
545 if (isTop && other.isTop)
572 }
573
574 if (isTop && other.isTop) {
546575 return true;
547
548 if (isBottom != other.isBottom)
576 }
577
578 if (isBottom != other.isBottom) {
549579 return false;
550
551 if (isBottom && other.isBottom)
580 }
581
582 if (isBottom && other.isBottom) {
552583 return true;
553
554 if (getNumSlots() != other.getNumSlots())
584 }
585
586 if (getNumSlots() != other.getNumSlots()) {
555587 return false;
556
557 for (int i = 0; i < getNumSlots(); ++i)
558 if (!getValue(i).equals(other.getValue(i)))
588 }
589
590 for (int i = 0; i < getNumSlots(); ++i) {
591 if (!getValue(i).equals(other.getValue(i))) {
559592 return false;
593 }
594 }
560595
561596 return true;
562597 }
572607 if (true) {
573608 int size = slotList.size();
574609 if (size == other.slotList.size()) {
575 for (int i = 0; i < size; i++)
610 for (int i = 0; i < size; i++) {
576611 slotList.set(i, other.slotList.get(i));
612 }
577613 } else {
578614 slotList.clear();
579 for (ValueType v : other.slotList)
615 for (ValueType v : other.slotList) {
580616 slotList.add(v);
617 }
581618 }
582619 } else {
583620 slotList.clear();
602639 */
603640 @Override
604641 public String toString() {
605 if (isTop())
642 if (isTop()) {
606643 return "[TOP]";
607 if (isBottom())
644 }
645 if (isBottom()) {
608646 return "[BOTTOM]";
647 }
609648 StringBuilder buf = new StringBuilder();
610649 buf.append('[');
611650 int numSlots = getNumSlots();
616655 // the operand stack.
617656 int last = buf.length() - 1;
618657 if (last >= 0) {
619 if (buf.charAt(last) == ',')
658 if (buf.charAt(last) == ',') {
620659 buf.deleteCharAt(last);
660 }
621661 }
622662 buf.append('|');
623663 }
624664 String value = valueToString(getValue(i));
625 if (i == numSlots - 1 && value.endsWith(","))
665 if (i == numSlots - 1 && value.endsWith(",")) {
626666 value = value.substring(0, value.length() - 1);
667 }
627668 buf.append(value);
628669 // buf.append(' ');
629670 }
637678 * the values.
638679 */
639680 protected String valueToString(ValueType value) {
640 if (value == null)
681 if (value == null) {
641682 return "null";
683 }
642684 return value.toString();
643685 }
644686
647689 * stack slots
648690 */
649691 public Collection<ValueType> allSlots() {
650 if (slotList == null)
692 if (slotList == null) {
651693 return Collections.<ValueType> emptyList();
694 }
652695 return Collections.<ValueType> unmodifiableCollection(slotList);
653696 }
654697
669712
670713 }
671714
672 // vim:ts=4
2525 /**
2626 * A convenient base class for dataflow analysis classes which use Frames as
2727 * values.
28 *
28 *
2929 * @author David Hovemeyer
3030 * @see Frame
3131 * @see DataflowAnalysis
3232 */
3333 public abstract class FrameDataflowAnalysis<ValueType, FrameType extends Frame<ValueType>> extends
34 ForwardDataflowAnalysis<FrameType> {
34 ForwardDataflowAnalysis<FrameType> {
3535 public FrameDataflowAnalysis(DepthFirstSearch dfs) {
3636 super(dfs);
3737 }
3838
39 @Override
3940 public void copy(FrameType source, FrameType dest) {
4041 dest.copyFrom(source);
4142 }
4243
44 @Override
4345 public void makeFactTop(FrameType fact) {
4446 fact.setTop();
4547 }
4648
49 @Override
4750 public boolean isTop(FrameType fact) {
4851 return fact.isTop();
4952 }
5053
54 @Override
5155 public boolean same(FrameType fact1, FrameType fact2) {
5256 return fact1.sameAs(fact2);
5357 }
5660 * Get the dataflow fact representing the point just before given Location.
5761 * Note "before" is meant in the logical sense, so for backward analyses,
5862 * before means after the location in the control flow sense.
59 *
60 * @param location
61 * the location
63 *
6264 * @return the fact at the point just before the location
6365 */
6466
6971 for (Location l : cfg.locations()) {
7072 if (l.getHandle().getPosition() == pc) {
7173 FrameType fact = getFactAtLocation(l);
72 if (isFactValid(fact))
74 if (isFactValid(fact)) {
7375 mergeInto(fact, result);
76 }
7477 }
7578 }
7679 return result;
8083 * Get the dataflow fact representing the point just before given Location.
8184 * Note "before" is meant in the logical sense, so for backward analyses,
8285 * before means after the location in the control flow sense.
83 *
84 * @param location
85 * the location
86 *
8687 * @return the fact at the point just before the location
8788 */
8889
9091 FrameType result = createFact();
9192 makeFactTop(result);
9293
93 for (BasicBlock b : cfg.getBlocksContainingInstructionWithOffset(pc))
94 for (Location loc : cfg.getLocationsContainingInstructionWithOffset(pc)) {
95 BasicBlock b = loc.getBasicBlock();
96 BasicBlock b2 = null;
9497 if (b.getFirstInstruction() != null && b.getFirstInstruction().getPosition() == pc) {
95 BasicBlock b2 = cfg.getPredecessorWithEdgeType(b, EdgeTypes.FALL_THROUGH_EDGE);
98 b2 = cfg.getPredecessorWithEdgeType(b, EdgeTypes.FALL_THROUGH_EDGE);
99 }
100 if(b2 != null && b2.isExceptionThrower()) {
96101 for (Iterator<Edge> i = cfg.incomingEdgeIterator(b2); i.hasNext();) {
97102 Edge e = i.next();
98103 FrameType fact = getFactOnEdge(e);
99 if (isFactValid(fact))
104 if (isFactValid(fact)) {
100105 mergeInto(fact, result);
106 }
101107 }
102
108 } else {
109 FrameType fact = getFactAtLocation(loc);
110 if (isFactValid(fact)) {
111 mergeInto(fact, result);
112 }
103113 }
114 }
104115 return result;
105116 }
106117
124135 * the frame needs to be modified in a path-sensitive fashion. A typical
125136 * usage pattern is:
126137 * <p/>
127 *
138 *
128139 * <pre>
129140 * FrameType copy = null;
130141 * if (someCondition()) {
144155 * The advantage of using modifyFrame() is that new code can be added before
145156 * or after other places where the frame is modified, and the code will
146157 * remain correct.
147 *
158 *
148159 * @param orig
149160 * the original frame
150161 * @param copy
163174
164175 /**
165176 * Merge one frame into another.
166 *
177 *
167178 * @param other
168179 * the frame to merge with the result
169180 * @param result
209220
210221 /**
211222 * Merge the values contained in a given slot of two Frames.
212 *
223 *
213224 * @param otherFrame
214225 * a Frame
215226 * @param resultFrame
221232 protected abstract void mergeValues(FrameType otherFrame, FrameType resultFrame, int slot) throws DataflowAnalysisException;
222233 }
223234
224 // vim:ts=4
127127 * (RuntimeException or Error).
128128 */
129129 public static boolean isUncheckedException(ObjectType type) throws ClassNotFoundException {
130 if (type.equals(Type.THROWABLE) || type.equals(RUNTIME_EXCEPTION_TYPE) || type.equals(ERROR_TYPE))
130 if (type.equals(Type.THROWABLE) || type.equals(RUNTIME_EXCEPTION_TYPE) || type.equals(ERROR_TYPE)) {
131131 return true;
132 }
132133 ClassDescriptor c = DescriptorFactory.getClassDescriptor(type);
133134 Subtypes2 subtypes2 = Global.getAnalysisCache().getDatabase(Subtypes2.class);
134135 return subtypes2.isSubtype(c, RUNTIME_EXCEPTION, ERROR);
146147 * @return true if the method is a monitor wait, false if not
147148 */
148149 public static boolean isMonitorWait(String methodName, String methodSig) {
149 return methodName.equals("wait") && (methodSig.equals("()V") || methodSig.equals("(J)V") || methodSig.equals("(JI)V"));
150 return "wait".equals(methodName) && ("()V".equals(methodSig) || "(J)V".equals(methodSig) || "(JI)V".equals(methodSig));
150151 }
151152
152153 /**
160161 * @return true if the instruction is a monitor wait, false if not
161162 */
162163 public static boolean isMonitorWait(Instruction ins, ConstantPoolGen cpg) {
163 if (!(ins instanceof InvokeInstruction))
164 if (!(ins instanceof InvokeInstruction)) {
164165 return false;
165 if (ins.getOpcode() == Constants.INVOKESTATIC)
166 }
167 if (ins.getOpcode() == Constants.INVOKESTATIC) {
166168 return false;
169 }
167170
168171 InvokeInstruction inv = (InvokeInstruction) ins;
169172 String methodName = inv.getMethodName(cpg);
183186 * @return true if the method is a monitor notify, false if not
184187 */
185188 public static boolean isMonitorNotify(String methodName, String methodSig) {
186 return (methodName.equals("notify") || methodName.equals("notifyAll")) && methodSig.equals("()V");
189 return ("notify".equals(methodName) || "notifyAll".equals(methodName)) && "()V".equals(methodSig);
187190 }
188191
189192 /**
197200 * @return true if the instruction is a monitor wait, false if not
198201 */
199202 public static boolean isMonitorNotify(Instruction ins, ConstantPoolGen cpg) {
200 if (!(ins instanceof InvokeInstruction))
203 if (!(ins instanceof InvokeInstruction)) {
201204 return false;
202 if (ins.getOpcode() == Constants.INVOKESTATIC)
205 }
206 if (ins.getOpcode() == Constants.INVOKESTATIC) {
203207 return false;
208 }
204209
205210 InvokeInstruction inv = (InvokeInstruction) ins;
206211 String methodName = inv.getMethodName(cpg);
328333 short opcode = inv.getOpcode();
329334
330335 if (opcode == Constants.INVOKESTATIC) {
331 if (methodChooser == INSTANCE_METHOD)
336 if (methodChooser == INSTANCE_METHOD) {
332337 return null;
338 }
333339 } else {
334 if (methodChooser == STATIC_METHOD)
340 if (methodChooser == STATIC_METHOD) {
335341 return null;
342 }
336343 }
337344
338345 // Find the method
365372 JavaClassAndMethod findInvocationLeastUpperBound(JavaClass jClass, String methodName, String methodSig,
366373 JavaClassAndMethodChooser methodChooser, boolean invokeInterface) throws ClassNotFoundException {
367374 JavaClassAndMethod result = findMethod(jClass, methodName, methodSig, methodChooser);
368 if (result != null)
375 if (result != null) {
369376 return result;
370 if (invokeInterface)
377 }
378 if (invokeInterface) {
371379 for (JavaClass i : jClass.getInterfaces()) {
372380 result = findInvocationLeastUpperBound(i, methodName, methodSig, methodChooser, invokeInterface);
373 if (result != null)
381 if (result != null) {
374382 return null;
375 }
376 else {
383 }
384 }
385 } else {
377386 JavaClass sClass = jClass.getSuperClass();
378 if (sClass != null)
387 if (sClass != null) {
379388 return findInvocationLeastUpperBound(sClass, methodName, methodSig, methodChooser, invokeInterface);
389 }
380390 }
381391 return null;
382392
424434 System.out.println("Check " + javaClass.getClassName());
425435 }
426436 Method[] methodList = javaClass.getMethods();
427 for (Method method : methodList)
437 for (Method method : methodList) {
428438 if (method.getName().equals(methodName) && method.getSignature().equals(methodSig)) {
429439 JavaClassAndMethod m = new JavaClassAndMethod(javaClass, method);
430440 if (chooser.choose(m)) {
434444 return m;
435445 }
436446 }
447 }
437448 if (DEBUG_METHOD_LOOKUP) {
438449 System.out.println("\t==> NOT FOUND");
439450 }
490501 System.out.println("Check " + javaClass.getClassName());
491502 }
492503 Method[] methodList = javaClass.getMethods();
493 for (Method method : methodList)
504 for (Method method : methodList) {
494505 if (method.getName().equals(methodName) && method.getSignature().equals(methodSig)
495506 && accessFlagsAreConcrete(method.getAccessFlags())) {
496507 JavaClassAndMethod m = new JavaClassAndMethod(javaClass, method);
498509 return m;
499510
500511 }
512 }
501513 if (DEBUG_METHOD_LOOKUP) {
502514 System.out.println("\t==> NOT FOUND");
503515 }
529541 * JavaClassAndMethodChooser which accepts any method.
530542 */
531543 public static final JavaClassAndMethodChooser ANY_METHOD = new JavaClassAndMethodChooser() {
544 @Override
532545 public boolean choose(JavaClassAndMethod javaClassAndMethod) {
533546 return true;
534547 }
535548
549 @Override
536550 public boolean choose(XMethod method) {
537551 return true;
538552 }
549563 * native) methods.
550564 */
551565 public static final JavaClassAndMethodChooser CONCRETE_METHOD = new JavaClassAndMethodChooser() {
566 @Override
552567 public boolean choose(JavaClassAndMethod javaClassAndMethod) {
553568 Method method = javaClassAndMethod.getMethod();
554569 int accessFlags = method.getAccessFlags();
555570 return accessFlagsAreConcrete(accessFlags);
556571 }
557572
573 @Override
558574 public boolean choose(XMethod method) {
559575 return accessFlagsAreConcrete(method.getAccessFlags());
560576 }
564580 * JavaClassAndMethodChooser which accepts only static methods.
565581 */
566582 public static final JavaClassAndMethodChooser STATIC_METHOD = new JavaClassAndMethodChooser() {
583 @Override
567584 public boolean choose(JavaClassAndMethod javaClassAndMethod) {
568585 return javaClassAndMethod.getMethod().isStatic();
569586 }
570587
588 @Override
571589 public boolean choose(XMethod method) {
572590 return method.isStatic();
573591 }
577595 * JavaClassAndMethodChooser which accepts only instance methods.
578596 */
579597 public static final JavaClassAndMethodChooser INSTANCE_METHOD = new JavaClassAndMethodChooser() {
598 @Override
580599 public boolean choose(JavaClassAndMethod javaClassAndMethod) {
581600 return !javaClassAndMethod.getMethod().isStatic();
582601 }
583602
603 @Override
584604 public boolean choose(XMethod method) {
585605 return !method.isStatic();
586606 }
623643 JavaClassAndMethod m = null;
624644
625645 for (JavaClass cls : classList) {
626 if ((m = findMethod(cls, methodName, methodSig, chooser)) != null)
646 if ((m = findMethod(cls, methodName, methodSig, chooser)) != null) {
627647 break;
648 }
628649 }
629650
630651 return m;
770791 InvokeInstruction invokeInstruction, ConstantPoolGen cpg, boolean receiverTypeIsExact) throws ClassNotFoundException {
771792 HashSet<JavaClassAndMethod> result = new HashSet<JavaClassAndMethod>();
772793
773 if (invokeInstruction.getOpcode() == Constants.INVOKESTATIC)
794 if (invokeInstruction.getOpcode() == Constants.INVOKESTATIC) {
774795 throw new IllegalArgumentException();
796 }
775797
776798 String methodName = invokeInstruction.getName(cpg);
777799 String methodSig = invokeInstruction.getSignature(cpg);
781803 if (receiverType instanceof ArrayType) {
782804 JavaClass javaLangObject = AnalysisContext.currentAnalysisContext().lookupClass("java.lang.Object");
783805 JavaClassAndMethod classAndMethod = findMethod(javaLangObject, methodName, methodSig, INSTANCE_METHOD);
784 if (classAndMethod != null)
806 if (classAndMethod != null) {
785807 result.add(classAndMethod);
808 }
786809 return result;
787810 }
788811
816839 && !receiverTypeIsExact;
817840
818841 if (virtualCall) {
819 if (!receiverClassName.equals("java.lang.Object")) {
842 if (!"java.lang.Object".equals(receiverClassName)) {
820843
821844 // This is a true virtual call: assume that any concrete
822845 // subtype method may be called.
827850 result.add(new JavaClassAndMethod(concreteSubtypeMethod));
828851 }
829852 }
830 if (false && subTypeSet.size() > 500)
853 if (false && subTypeSet.size() > 500) {
831854 new RuntimeException(receiverClassName + " has " + subTypeSet.size() + " subclasses, " + result.size()
832855 + " of which implement " + methodName + methodSig + " " + invokeInstruction)
833 .printStackTrace(System.out);
856 .printStackTrace(System.out);
857 }
834858
835859 }
836860 }
893917 * could be found
894918 */
895919 public static XField findXField(String className, String fieldName, String fieldSig, boolean isStatic)
896 {
920 {
897921
898922 return XFactory.createXField(className, fieldName, fieldSig, isStatic);
899923 }
921945 XField xfield = findXField(className, fieldName, fieldSig, isStatic);
922946 short opcode = fins.getOpcode();
923947 if (xfield != null && xfield.isResolved()
924 && xfield.isStatic() == (opcode == Constants.GETSTATIC || opcode == Constants.PUTSTATIC))
948 && xfield.isStatic() == (opcode == Constants.GETSTATIC || opcode == Constants.PUTSTATIC)) {
925949 return xfield;
926 else
950 } else {
927951 return null;
952 }
928953 }
929954
930955 /**
966991
967992 }
968993
969 // vim:ts=4
1818
1919 package edu.umd.cs.findbugs.ba;
2020
21 import static edu.umd.cs.findbugs.ba.Hierarchy.DEBUG_METHOD_LOOKUP;
22 import static edu.umd.cs.findbugs.ba.Hierarchy.INSTANCE_METHOD;
23 import static edu.umd.cs.findbugs.ba.Hierarchy.STATIC_METHOD;
21 import static edu.umd.cs.findbugs.ba.Hierarchy.*;
2422
2523 import java.util.Collections;
2624 import java.util.HashSet;
5755 * hierarchy using the {@link org.apache.bcel.Repository} class. Callers should
5856 * generally expect to handle ClassNotFoundException for when referenced classes
5957 * can't be found.
60 *
58 *
6159 * @author William Pugh
6260 */
6361 public class Hierarchy2 {
6866 * Look up the method referenced by given InvokeInstruction. This method
6967 * does <em>not</em> look for implementations in super or subclasses
7068 * according to the virtual dispatch rules.
71 *
69 *
7270 * @param inv
7371 * the InvokeInstruction
7472 * @param cpg
8179 * the class
8280 */
8381 public static XMethod findExactMethod(InvokeInstruction inv, ConstantPoolGen cpg, JavaClassAndMethodChooser chooser)
84 {
82 {
8583 String className = inv.getClassName(cpg);
8684 String methodName = inv.getName(cpg);
8785 String methodSig = inv.getSignature(cpg);
9492
9593 private static @CheckForNull
9694 XMethod thisOrNothing(@CheckForNull XMethod m, JavaClassAndMethodChooser chooser) {
97 if (m == null)
95 if (m == null) {
9896 return null;
99 if (chooser.choose(m))
97 }
98 if (chooser.choose(m)) {
10099 return m;
100 }
101101 return null;
102102 }
103103
104104 public static @CheckForNull
105105 XMethod findInvocationLeastUpperBound(InvokeInstruction inv, ConstantPoolGen cpg, JavaClassAndMethodChooser methodChooser)
106 {
106 {
107107
108108 if (DEBUG_METHOD_LOOKUP) {
109109 System.out.println("Find prototype method for " + SignatureConverter.convertMethodSignature(inv, cpg));
112112 short opcode = inv.getOpcode();
113113
114114 if (opcode == Constants.INVOKESTATIC) {
115 if (methodChooser == INSTANCE_METHOD)
115 if (methodChooser == INSTANCE_METHOD) {
116116 return null;
117 }
117118 } else {
118 if (methodChooser == STATIC_METHOD)
119 if (methodChooser == STATIC_METHOD) {
119120 return null;
121 }
120122 }
121123
122124 // Find the method
163165 XMethod findInvocationLeastUpperBound(XClass jClass, String methodName, String methodSig, boolean invokeStatic,
164166 boolean invokeInterface) {
165167 XMethod result = findMethod(jClass.getClassDescriptor(), methodName, methodSig, invokeStatic);
166 if (result != null)
168 if (result != null) {
167169 return result;
170 }
168171 ClassDescriptor sClass = jClass.getSuperclassDescriptor();
169172 if (sClass != null) {
170173 result = findInvocationLeastUpperBound(sClass, methodName, methodSig, invokeStatic, invokeInterface);
171 if (result != null)
174 if (result != null) {
172175 return result;
176 }
173177 }
174178
175179 for (ClassDescriptor i : jClass.getInterfaceDescriptorList()) {
176180 result = findInvocationLeastUpperBound(i, methodName, methodSig, invokeStatic, invokeInterface);
177 if (result != null)
181 if (result != null) {
178182 return result;
183 }
179184 }
180185
181186 return null;
185190 XMethod findInvocationLeastUpperBound0(XClass jClass, String methodName, String methodSig, boolean invokeStatic,
186191 boolean invokeInterface) {
187192 XMethod result = findMethod(jClass.getClassDescriptor(), methodName, methodSig, invokeStatic);
188 if (result != null)
193 if (result != null) {
189194 return result;
190 if (invokeInterface)
195 }
196 if (invokeInterface) {
191197 for (ClassDescriptor i : jClass.getInterfaceDescriptorList()) {
192198 result = findInvocationLeastUpperBound(i, methodName, methodSig, invokeStatic, invokeInterface);
193 if (result != null)
199 if (result != null) {
194200 return result;
195 }
196 else {
201 }
202 }
203 } else {
197204 ClassDescriptor sClass = jClass.getSuperclassDescriptor();
198 if (sClass != null)
205 if (sClass != null) {
199206 return findInvocationLeastUpperBound(sClass, methodName, methodSig, invokeStatic, invokeInterface);
207 }
200208 }
201209 return null;
202210 }
212220
213221
214222 public static @CheckForNull XMethod findFirstSuperMethod(XMethod m) {
215
223
216224 try {
217225 @CheckForNull ClassDescriptor c = m.getClassDescriptor();
218226 XClass xc = getXClass(c);
220228 while (c != null) {
221229 xc = getXClass(c);
222230 XMethod xm = xc.findMatchingMethod(m.getMethodDescriptor());
223 if (xm != null)
231 if (xm != null) {
224232 return xm;
233 }
225234 c = xc.getSuperclassDescriptor();
226235 }
227236 } catch (CheckedAnalysisException e) {
229238 }
230239 return null;
231240 }
232
241
233242 private static void findSuperMethods(@CheckForNull ClassDescriptor c, XMethod m, Set<XMethod> accumulator) {
234 if (c == null)
243 if (c == null) {
235244 return;
245 }
236246 try {
237247 XClass xc = getXClass(c);
238248 XMethod xm = xc.findMatchingMethod(m.getMethodDescriptor());
239 if (xm != null && !accumulator.add(xm))
249 if (xm != null && !accumulator.add(xm)) {
240250 return;
251 }
241252 findSuperMethods(xc.getSuperclassDescriptor(), m, accumulator);
242 for (ClassDescriptor i : xc.getInterfaceDescriptorList())
253 for (ClassDescriptor i : xc.getInterfaceDescriptorList()) {
243254 findSuperMethods(i, m, accumulator);
244 if (!accumulator.add(m))
255 }
256 if (!accumulator.add(m)) {
245257 return;
258 }
246259
247260 } catch (CheckedAnalysisException e) {
248261 AnalysisContext.logError("Error finding super methods for " + m, e);
273286 /**
274287 * Resolve possible method call targets. This works for both static and
275288 * instance method calls.
276 *
289 *
277290 * @param invokeInstruction
278291 * the InvokeInstruction
279292 * @param typeFrame
328341 /**
329342 * Resolve possible instance method call targets. Assumes that invokevirtual
330343 * and invokeinterface methods may call any subtype of the receiver class.
331 *
344 *
332345 * @param receiverType
333346 * type of the receiver object
334347 * @param invokeInstruction
345358
346359 /**
347360 * Resolve possible instance method call targets.
348 *
361 *
349362 * @param receiverType
350363 * type of the receiver object
351364 * @param invokeInstruction
361374 public static Set<XMethod> resolveMethodCallTargets(ReferenceType receiverType, InvokeInstruction invokeInstruction,
362375 ConstantPoolGen cpg, boolean receiverTypeIsExact) throws ClassNotFoundException {
363376
364 if (invokeInstruction.getOpcode() == Constants.INVOKESTATIC)
377 if (invokeInstruction.getOpcode() == Constants.INVOKESTATIC) {
365378 throw new IllegalArgumentException();
379 }
366380
367381 String methodName = invokeInstruction.getName(cpg);
368382 String methodSig = invokeInstruction.getSignature(cpg);
369383
370384 // Array method calls aren't virtual.
371385 // They should just resolve to Object methods.
372 if (receiverType instanceof ArrayType)
386 if (receiverType instanceof ArrayType) {
373387 try {
374388 return Util.emptyOrNonnullSingleton(getXClass(objectDescriptor).findMethod(methodName, methodSig, false));
375389 } catch (CheckedAnalysisException e) {
376390 return Collections.<XMethod> emptySet();
377391 }
392 }
378393
379394 if (receiverType instanceof ObjectType) {
380395 // Get the receiver class.
388403
389404 }
390405
391 /**
392 * @param receiverClassName
393 * @param methodName
394 * @param methodSig
395 * @param receiverTypeIsExact
396 * @param invokeSpecial
397 * TODO
398 * @return
399 * @throws ClassNotFoundException
400 */
401406 public static Set<XMethod> resolveVirtualMethodCallTargets(String receiverClassName, String methodName, String methodSig,
402407 boolean receiverTypeIsExact, boolean invokeSpecial) throws ClassNotFoundException {
403408 ClassDescriptor receiverDesc = DescriptorFactory.createClassDescriptorFromDottedClassName(receiverClassName);
440445
441446 if (OPEN_WORLD_DEBUG) {
442447 System.out.println("OWD: " + receiverDesc + "." + methodName + methodSig);
443 if (upperBound != null)
448 if (upperBound != null) {
444449 System.out.println(" upper bound:" + upperBound);
450 }
445451 }
446452 // Is this a virtual call site?
447453 boolean virtualCall = (upperBound == null || !upperBound.isFinal()) && !receiverTypeIsExact && !invokeSpecial;
448454
449 if (virtualCall && !receiverDesc.getClassName().equals("java/lang/Object")) {
455 if (virtualCall && !"java/lang/Object".equals(receiverDesc.getClassName())) {
450456
451457 // This is a true virtual call: assume that any concrete
452458 // subtype method may be called.
454460 for (ClassDescriptor subtype : subTypeSet) {
455461 XMethod concreteSubtypeMethod = findMethod(subtype, methodName, methodSig, false);
456462 if (concreteSubtypeMethod != null && (OPEN_WORLD || !concreteSubtypeMethod.isAbstract())) {
457 if (OPEN_WORLD_DEBUG)
463 if (OPEN_WORLD_DEBUG) {
458464 System.out.println(" -> " + concreteSubtypeMethod);
465 }
459466
460467 result.add(concreteSubtypeMethod);
461468 }
467474
468475 /**
469476 * Find the declared exceptions for the method called by given instruction.
470 *
477 *
471478 * @param inv
472479 * the InvokeInstruction
473480 * @param cpg
481488 XMethod method = findInvocationLeastUpperBound(inv, cpg, inv instanceof INVOKESTATIC ? Hierarchy.STATIC_METHOD
482489 : Hierarchy.INSTANCE_METHOD);
483490
484 if (method == null)
491 if (method == null) {
485492 return null;
493 }
486494 String[] exceptions = method.getThrownExceptions();
487495
488 if (exceptions == null)
496 if (exceptions == null) {
489497 return new ObjectType[0];
498 }
490499
491500 ObjectType[] result = new ObjectType[exceptions.length];
492501 for (int i = 0; i < exceptions.length; ++i) {
497506
498507 }
499508
500 // vim:ts=4
2525 /**
2626 * Interface for querying nullness annotations on methods, fields, and
2727 * parameters.
28 *
28 *
2929 * @author David Hovemeyer
3030 */
3131 public interface INullnessAnnotationDatabase {
3232
3333 /**
3434 * Determine whether given parameter must be non-null.
35 *
35 *
3636 * @param m
3737 * a method
3838 * @param param
4444 /**
4545 * Get a resolved NullnessAnnotation on given XMethod, XField, or
4646 * XMethodParameter.
47 *
47 *
4848 * @param o
4949 * an XMethod, XField, or XMethodParameter
5050 * @param getMinimal
7171
7272 /**
7373 * Add a field annotation to the database.
74 *
74 *
7575 * @param cName
7676 * dotted class name
7777 * @param mName
8888
8989 /**
9090 * Add a method annotation to the database.
91 *
91 *
9292 * @param cName
9393 * dotted class name
9494 * @param mName
105105
106106 /**
107107 * Add a method parameter annotation to the database.
108 *
108 *
109109 * @param cName
110110 * dotted class name
111111 * @param mName
124124
125125 /**
126126 * Add a default annotation to the database.
127 *
127 *
128128 * @param target
129129 * one of AnnotationDatabase.METHOD, AnnotationDatabase.FIELD,
130130 * AnnotationDatabase.PARAMETER, or AnnotationDatabase.ANY
129129
130130 static public @Nonnull
131131 IncompatibleTypes getPriorityForAssumingCompatible(Type expectedType, Type actualType, boolean pointerEquality) {
132 if (expectedType.equals(actualType))
133 return SEEMS_OK;
134
135 if (!(expectedType instanceof ReferenceType))
136 return SEEMS_OK;
137 if (!(actualType instanceof ReferenceType))
138 return SEEMS_OK;
139
140 if (expectedType instanceof BasicType ^ actualType instanceof BasicType) {
141 return INCOMPATIBLE_CLASSES;
142 }
132 if (expectedType.equals(actualType)) {
133 return SEEMS_OK;
134 }
135
136 if (!(expectedType instanceof ReferenceType)) {
137 return SEEMS_OK;
138 }
139 if (!(actualType instanceof ReferenceType)) {
140 return SEEMS_OK;
141 }
142
143143 while (expectedType instanceof ArrayType && actualType instanceof ArrayType) {
144144 expectedType = ((ArrayType) expectedType).getElementType();
145145 actualType = ((ArrayType) actualType).getElementType();
149149 return PRIMATIVE_ARRAY_AND_OTHER_ARRAY;
150150 }
151151 if (expectedType instanceof BasicType && actualType instanceof BasicType) {
152 if (!expectedType.equals(actualType))
152 if (!expectedType.equals(actualType)) {
153153 return INCOMPATIBLE_PRIMATIVE_ARRAYS;
154 else
154 } else {
155155 return SEEMS_OK;
156 }
156157 }
157158 if (expectedType instanceof ArrayType) {
158159 return getPriorityForAssumingCompatibleWithArray(actualType);
160161 if (actualType instanceof ArrayType) {
161162 return getPriorityForAssumingCompatibleWithArray(expectedType);
162163 }
163 if (expectedType.equals(actualType))
164 return SEEMS_OK;
164 if (expectedType.equals(actualType)) {
165 return SEEMS_OK;
166 }
165167
166168 // For now, ignore the case where either reference is not
167169 // of an object type. (It could be either an array or null.)
168 if (!(expectedType instanceof ObjectType) || !(actualType instanceof ObjectType))
169 return SEEMS_OK;
170 if (!(expectedType instanceof ObjectType) || !(actualType instanceof ObjectType)) {
171 return SEEMS_OK;
172 }
170173
171174 return getPriorityForAssumingCompatible((ObjectType) expectedType, (ObjectType) actualType, pointerEquality);
172
173175 }
174176
175177 private static IncompatibleTypes getPriorityForAssumingCompatibleWithArray(Type rhsType) {
176 if (rhsType.equals(Type.OBJECT))
178 if (rhsType.equals(Type.OBJECT)) {
177179 return ARRAY_AND_OBJECT;
180 }
178181 String sig = rhsType.getSignature();
179 if (sig.equals("Ljava/io/Serializable;") || sig.equals("Ljava/lang/Cloneable;"))
180 return SEEMS_OK;
182 if ("Ljava/io/Serializable;".equals(sig) || "Ljava/lang/Cloneable;".equals(sig)) {
183 return SEEMS_OK;
184 }
181185 return ARRAY_AND_NON_ARRAY;
182186 }
183187
186190 IAnalysisCache cache = Global.getAnalysisCache();
187191 while (true) {
188192 XMethod result = xClass.findMethod(name, sig, isStatic);
189 if (result != null)
193 if (result != null) {
190194 return result;
191 if (isStatic)
195 }
196 if (isStatic) {
192197 throw new CheckedAnalysisException();
198 }
193199 ClassDescriptor superclassDescriptor = xClass.getSuperclassDescriptor();
194 if (superclassDescriptor == null)
200 if (superclassDescriptor == null) {
195201 throw new CheckedAnalysisException();
202 }
196203 xClass = cache.getClassAnalysis(XClass.class, superclassDescriptor);
197204 }
198205
200207
201208 static public @Nonnull
202209 IncompatibleTypes getPriorityForAssumingCompatible(ObjectType expectedType, ObjectType actualType, boolean pointerEquality) {
203 if (expectedType.equals(actualType))
204 return SEEMS_OK;
205
206 if (actualType.equals(Type.OBJECT))
210 if (expectedType.equals(actualType)) {
211 return SEEMS_OK;
212 }
213
214 if (actualType.equals(Type.OBJECT)) {
207215 return IncompatibleTypes.UNCHECKED;
208
209 if (expectedType.equals(Type.OBJECT))
216 }
217
218 if (expectedType.equals(Type.OBJECT)) {
210219 return IncompatibleTypes.SEEMS_OK;
220 }
211221
212222 try {
213223
214224 if (!Hierarchy.isSubtype(expectedType, actualType) && !Hierarchy.isSubtype(actualType, expectedType)) {
215225 if (Hierarchy.isSubtype(expectedType, GWT_JAVASCRIPTOBJECT_TYPE)
216 && Hierarchy.isSubtype(actualType, GWT_JAVASCRIPTOBJECT_TYPE))
226 && Hierarchy.isSubtype(actualType, GWT_JAVASCRIPTOBJECT_TYPE)) {
217227 return SEEMS_OK;
228 }
218229 // See if the types are related by inheritance.
219230 ClassDescriptor lhsDescriptor = DescriptorFactory.createClassDescriptorFromDottedClassName(expectedType
220231 .getClassName());
228239 && (Hierarchy.isSubtype(expectedType, COLLECTION_TYPE) || Hierarchy.isSubtype(expectedType, MAP_TYPE))) {
229240 List<? extends ReferenceType> lhsParameters = ((GenericObjectType) expectedType).getParameters();
230241 List<? extends ReferenceType> rhsParameters = ((GenericObjectType) actualType).getParameters();
231 if (lhsParameters != null && rhsParameters != null && lhsParameters.size() == rhsParameters.size())
242 if (lhsParameters != null && rhsParameters != null && lhsParameters.size() == rhsParameters.size()) {
232243 for (int i = 0; i < lhsParameters.size(); i++) {
233244 IncompatibleTypes r = getPriorityForAssumingCompatible(lhsParameters.get(i), rhsParameters.get(i),
234245 pointerEquality);
235 if (r.getPriority() <= Priorities.NORMAL_PRIORITY)
246 if (r.getPriority() <= Priorities.NORMAL_PRIORITY) {
236247 return r;
248 }
237249 }
250 }
238251
239252 }
240253
248261 return SEEMS_OK;
249262 }
250263
251 /**
252 * @param pointerEquality
253 * @param lhsDescriptor
254 * @param rhsDescriptor
255 * @throws CheckedAnalysisException
256 * @throws ClassNotFoundException
257 */
258264 public static IncompatibleTypes getPriorityForAssumingCompatible(boolean pointerEquality, ClassDescriptor lhsDescriptor,
259265 ClassDescriptor rhsDescriptor) throws CheckedAnalysisException, ClassNotFoundException {
260 if (lhsDescriptor.equals(rhsDescriptor))
261 return SEEMS_OK;
266 if (lhsDescriptor.equals(rhsDescriptor)) {
267 return SEEMS_OK;
268 }
262269
263270 AnalysisContext analysisContext = AnalysisContext.currentAnalysisContext();
264271 Subtypes2 subtypes2 = analysisContext.getSubtypes2();
271278 XMethod rhsEquals = getInvokedMethod(rhs, "equals", "(Ljava/lang/Object;)Z", false);
272279 String lhsClassName = lhsEquals.getClassName();
273280 if (lhsEquals.equals(rhsEquals)) {
274 if (lhsClassName.equals("java.lang.Enum"))
281 if ("java.lang.Enum".equals(lhsClassName)) {
275282 return INCOMPATIBLE_CLASSES;
276 if (!pointerEquality && !lhsClassName.equals("java.lang.Object"))
283 }
284 if (!pointerEquality && !"java.lang.Object".equals(lhsClassName)) {
277285 return SEEMS_OK;
286 }
278287 }
279288
280289 if ((subtypes2.isSubtype(lhsDescriptor, SET_DESCRIPTOR) && subtypes2.isSubtype(rhsDescriptor, SET_DESCRIPTOR)
281290 || subtypes2.isSubtype(lhsDescriptor, MAP_DESCRIPTOR) && subtypes2.isSubtype(rhsDescriptor, MAP_DESCRIPTOR) || subtypes2
282 .isSubtype(lhsDescriptor, LIST_DESCRIPTOR) && subtypes2.isSubtype(rhsDescriptor, LIST_DESCRIPTOR)))
283 return SEEMS_OK;
291 .isSubtype(lhsDescriptor, LIST_DESCRIPTOR) && subtypes2.isSubtype(rhsDescriptor, LIST_DESCRIPTOR))) {
292 return SEEMS_OK;
293 }
284294
285295 if (!lhs.isInterface() && !rhs.isInterface()) {
286296 // Both are class types, and therefore there is no possible
297307
298308 if (commonSubtypes.isEmpty()) {
299309 if (lhs.isInterface() && rhs.isInterface()) {
300 if (!subtypes2.hasKnownSubclasses(lhsDescriptor) || !subtypes2.hasKnownSubclasses(rhsDescriptor))
310 if (!subtypes2.hasKnownSubclasses(lhsDescriptor) || !subtypes2.hasKnownSubclasses(rhsDescriptor)) {
301311 return UNRELATED_INTERFACES_WITHOUT_IMPLEMENTATIONS;
312 }
302313 return UNRELATED_INTERFACES;
303314 }
304 if (lhs.isFinal() || rhs.isFinal())
315 if (lhs.isFinal() || rhs.isFinal()) {
305316 return UNRELATED_FINAL_CLASS_AND_INTERFACE;
317 }
306318 if (lhsDescriptor.getClassName().startsWith("java/util/")
307 || rhsDescriptor.getClassName().startsWith("java/util/"))
319 || rhsDescriptor.getClassName().startsWith("java/util/")) {
308320 return UNRELATED_UTIL_INTERFACE;
321 }
309322 return UNRELATED_CLASS_AND_INTERFACE;
310323 }
311324
313326 return SEEMS_OK;
314327 }
315328
316
317
318
319329 }
2121 import javax.annotation.Nonnull;
2222
2323 public class InnerClassAccess {
24 private String methodName;
24 private final String methodName;
2525
26 private String methodSig;
26 private final String methodSig;
2727
28 private @Nonnull XField field;
28 private @Nonnull
29 final XField field;
2930
30 private boolean isLoad;
31 private final boolean isLoad;
3132
3233 public InnerClassAccess(String methodName, String methodSig, @Nonnull XField field, boolean isLoad) {
3334 this.methodName = methodName;
5758 }
5859 }
5960
60 // vim:ts=4
140140 */
141141 private static int toInt(byte b) {
142142 int value = b & 0x7F;
143 if ((b & 0x80) != 0)
143 if ((b & 0x80) != 0) {
144144 value |= 0x80;
145 }
145146 return value;
146147 }
147148
152153 return (toInt(instructionList[index + 1]) << 8) | toInt(instructionList[index + 2]);
153154 }
154155
156 /*
155157 private static class LookupFailure extends RuntimeException {
156 /**
157 *
158 */
159158 private static final long serialVersionUID = 1L;
160159
161160 private final ClassNotFoundException exception;
168167 return exception;
169168 }
170169 }
170 */
171171
172172 /**
173173 * Callback to scan an access method to determine what field it accesses,
207207 this.accessCount = 0;
208208 }
209209
210 @Override
210211 public void handleInstruction(int opcode, int index) {
211212 switch (opcode) {
212213 case Constants.GETFIELD:
216217 case Constants.GETSTATIC:
217218 case Constants.PUTSTATIC:
218219 setField(getIndex(instructionList, index), true, opcode == Constants.GETSTATIC);
220 break;
221 default:
219222 break;
220223 }
221224 }
259262 String fieldSig = nameAndType.getSignature(cp);
260263
261264
262 XField xfield = Hierarchy.findXField(className, fieldName, fieldSig, isStatic);
263 if (xfield != null && xfield.isStatic() == isStatic && isValidAccessMethod(methodSig, xfield, isLoad)) {
264 access = new InnerClassAccess(methodName, methodSig, xfield, isLoad);
265 }
265 XField xfield = Hierarchy.findXField(className, fieldName, fieldSig, isStatic);
266 if (xfield != null && xfield.isStatic() == isStatic && isValidAccessMethod(methodSig, xfield, isLoad)) {
267 access = new InnerClassAccess(methodName, methodSig, xfield, isLoad);
268 }
266269
267270 }
268271
283286 // Get the method parameters and return type
284287 // (as they appear in the method signature).
285288 int paramsEnd = methodSig.indexOf(')');
286 if (paramsEnd < 0)
289 if (paramsEnd < 0) {
287290 return false;
291 }
288292 String methodParams = methodSig.substring(0, paramsEnd + 1);
289293 String methodReturnType = methodSig.substring(paramsEnd + 1);
290294
292296 String classSig = "L" + javaClass.getClassName().replace('.', '/') + ";";
293297 StringBuilder buf = new StringBuilder();
294298 buf.append('(');
295 if (!field.isStatic())
299 if (!field.isStatic()) {
296300 buf.append(classSig); // the OuterClass.this reference
297 if (!isLoad)
301 }
302 if (!isLoad) {
298303 buf.append(field.getSignature()); // the value being stored
304 }
299305 buf.append(')');
300306 String expectedMethodParams = buf.toString();
301307
310316 }
311317
312318 // Return type can be either the type of the field, or void.
313 if (!methodReturnType.equals("V") && !methodReturnType.equals(field.getSignature())) {
319 if (!"V".equals(methodReturnType) && !methodReturnType.equals(field.getSignature())) {
314320 if (DEBUG) {
315321 System.out.println("In " + javaClass.getClassName() + "." + methodName + " expected return type V or "
316322 + field.getSignature() + ", saw " + methodReturnType);
343349 Method[] methodList = javaClass.getMethods();
344350 for (Method method : methodList) {
345351 String methodName = method.getName();
346 if (!methodName.startsWith("access$"))
352 if (!methodName.startsWith("access$")) {
347353 continue;
354 }
348355
349356 Code code = method.getCode();
350 if (code == null)
357 if (code == null) {
351358 continue;
352
353 if (DEBUG)
359 }
360
361 if (DEBUG) {
354362 System.out.println("Analyzing " + className + "." + method.getName()
355363 + " as an inner-class access method...");
364 }
356365
357366 byte[] instructionList = code.getCode();
358367 String methodSig = method.getSignature();
359368 InstructionCallback callback = new InstructionCallback(javaClass, methodName, methodSig, instructionList);
360 try {
361 new BytecodeScanner().scan(instructionList, callback);
362 } catch (LookupFailure lf) {
363 throw lf.getException();
364 }
369 // try {
370 new BytecodeScanner().scan(instructionList, callback);
371 // } catch (LookupFailure lf) {
372 // throw lf.getException();
373 // }
365374 InnerClassAccess access = callback.getAccess();
366 if (DEBUG)
375 if (DEBUG) {
367376 System.out.println((access != null ? "IS" : "IS NOT") + " an inner-class access method");
368 if (access != null)
377 }
378 if (access != null) {
369379 map.put(methodName, access);
380 }
370381 }
371382 }
372383
373 if (map.size() == 0)
384 if (map.size() == 0) {
374385 map = Collections.emptyMap();
375 else
386 } else {
376387 map = new HashMap<String, InnerClassAccess>(map);
388 }
377389
378390 classToAccessMap.put(className, map);
379391 }
382394 }
383395
384396 }
385
386 // vim:ts=4
2323
2424 /**
2525 * Map BCEL InstructionHandles to some kind of value type.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class InstructionHandleMap<ValueType> {
30 private Object[] map;
30 private final Object[] map;
3131
3232 public InstructionHandleMap(MethodGen methodGen) {
3333 map = new Object[methodGen.getMethod().getCode().getLength()];
2626 * can be thrown to indicate that a method invocation requires more stack
2727 * operands than are available. AbstractFrameModelingVisitor will catch this
2828 * exception and rethrow it as a checked DataflowAnalysisException.
29 *
29 *
3030 * @author David Hovemeyer
3131 */
3232 public class InvalidBytecodeException extends RuntimeException {
3434
3535 /**
3636 * Constructor.
37 *
37 *
3838 * @param msg
3939 * reason for the exception
4040 */
4444
4545 /**
4646 * Constructor.
47 *
47 *
4848 * @param msg
4949 * reason for the exception
5050 * @param cause
5656
5757 /**
5858 * Constructor from method and instruction.
59 *
59 *
6060 * @param message
6161 * reason for the error
6262 * @param methodGen
7070
7171 /**
7272 * Constructor from method and instruction.
73 *
73 *
7474 * @param message
7575 * reason for the error
7676 * @param methodGen
3030 * A JavaClass and a Method belonging to the class. This is useful for answering
3131 * a method lookup query which must concretely identify both the class and the
3232 * method.
33 *
33 *
3434 * @author David Hovemeyer
3535 */
3636 public class JavaClassAndMethod {
4040
4141 /**
4242 * Constructor.
43 *
43 *
4444 * @param javaClass
4545 * the JavaClass
4646 * @param method
5353
5454 /**
5555 * Constructor.
56 *
56 *
5757 * @param method
5858 * an XMethod specifying a specific method in a specific class
5959 * @throws ClassNotFoundException
6161 public JavaClassAndMethod(XMethod method) throws ClassNotFoundException {
6262
6363 this.javaClass = Repository.lookupClass(method.getClassName());
64 for (Method m : javaClass.getMethods())
64 for (Method m : javaClass.getMethods()) {
6565 if (m.getName().equals(method.getName()) && m.getSignature().equals(method.getSignature())
6666 && m.isStatic() == method.isStatic()) {
6767 this.method = m;
6868 return;
6969 }
70 }
7071 throw new IllegalArgumentException("Can't find " + method);
7172 }
7273
9394
9495 /**
9596 * Get the MethodDescriptor that (hopefully) uniqely names this method.
96 *
97 *
9798 * @return the MethodDescriptor uniquely naming this method
9899 */
99100 public MethodDescriptor toMethodDescriptor() {
112113
113114 @Override
114115 public boolean equals(Object obj) {
115 if (obj == null || obj.getClass() != this.getClass())
116 if (obj == null || obj.getClass() != this.getClass()) {
116117 return false;
118 }
117119 JavaClassAndMethod other = (JavaClassAndMethod) obj;
118120 return javaClass.equals(other.javaClass) && method.equals(other.method);
119121 }
4242 */
4343 private static final boolean LINE_NUMBER_BUG = SystemProperties.getBoolean("lineNumberBug");
4444
45 private MethodGen methodGen;
45 private final MethodGen methodGen;
4646
47 private IdentityHashMap<InstructionHandle, LineNumber> lineNumberMap;
47 private final IdentityHashMap<InstructionHandle, LineNumber> lineNumberMap;
4848
4949 private boolean hasLineNumbers;
5050
5151 /**
5252 * Constructor.
53 *
53 *
5454 * @param methodGen
5555 * the method to summarize line numbers for
5656 */
8080 InstructionHandle handle = methodGen.getInstructionList().getStart();
8181 while (handle != null) {
8282 int bytecodeOffset = handle.getPosition();
83 if (bytecodeOffset < 0)
83 if (bytecodeOffset < 0) {
8484 throw new IllegalStateException("Bad bytecode offset: " + bytecodeOffset);
85 if (DEBUG)
85 }
86 if (DEBUG) {
8687 System.out.println("Looking for source line for bytecode offset " + bytecodeOffset);
88 }
8789 int sourceLine;
8890 try {
8991 sourceLine = table.getSourceLine(bytecodeOffset);
9092 } catch (ArrayIndexOutOfBoundsException e) {
91 if (LINE_NUMBER_BUG)
93 if (LINE_NUMBER_BUG) {
9294 throw e;
93 else
95 } else {
9496 sourceLine = -1;
97 }
9598 }
96 if (sourceLine >= 0)
99 if (sourceLine >= 0) {
97100 ++numGood;
101 }
98102 lineNumberMap.put(handle, new LineNumber(bytecodeOffset, sourceLine));
99103 handle = handle.getNext();
100104 ++numBytecodes;
101105 }
102106 hasLineNumbers = true;
103107
104 if (DEBUG)
108 if (DEBUG) {
105109 System.out.println("\t" + numGood + "/" + numBytecodes + " had valid line numbers");
110 }
106111 }
107112 }
108113
109114 private void checkTable(LineNumberTable table) {
110 if (DEBUG)
115 if (DEBUG) {
111116 System.out.println("line number table has length " + table.getTableLength());
117 }
112118 LineNumber[] entries = table.getLineNumberTable();
113119 int lastBytecode = -1;
114120 for (int i = 0; i < entries.length; ++i) {
115121 LineNumber ln = entries[i];
116 if (DEBUG)
122 if (DEBUG) {
117123 System.out.println("Entry " + i + ": pc=" + ln.getStartPC() + ", line=" + ln.getLineNumber());
124 }
118125 int pc = ln.getStartPC();
119 if (pc <= lastBytecode)
126 if (pc <= lastBytecode) {
120127 throw new IllegalStateException("LineNumberTable is not sorted");
128 }
121129 }
122130 }
123131
130138
131139 /**
132140 * Find the line number information for instruction whose handle is given.
133 *
141 *
134142 * @param handle
135143 * the InstructionHandle
136144 * @return the LineNumber object containing bytecode offset and source line
3333 /**
3434 * Dataflow analysis to find live stores of locals. This is just a backward
3535 * analysis to see which loads reach stores of the same local.
36 *
36 *
3737 * <p>
3838 * This analysis also computes which stores that were killed by a subsequent
3939 * store on any subsequent reachable path. (The FindDeadLocalStores detector
4040 * uses this information to reduce false positives.)
41 *
41 *
4242 * @author David Hovemeyer
4343 */
4444 public class LiveLocalStoreAnalysis extends BackwardDataflowAnalysis<BitSet> implements Debug {
45 private int topBit;
46
47 private int killedByStoreOffset;
45 private final int topBit;
46
47 private final int killedByStoreOffset;
4848
4949 public LiveLocalStoreAnalysis(MethodGen methodGen, ReverseDepthFirstSearch rdfs, DepthFirstSearch dfs) {
5050 super(rdfs, dfs);
5252 this.killedByStoreOffset = methodGen.getMaxLocals();
5353 }
5454
55 @Override
5556 public BitSet createFact() {
5657 return new BitSet();
5758 }
5859
60 @Override
5961 public void copy(BitSet source, BitSet dest) {
6062 dest.clear();
6163 dest.or(source);
6264 }
6365
66 @Override
6467 public void initEntryFact(BitSet result) throws DataflowAnalysisException {
6568 result.clear();
6669 }
6770
71 @Override
6872 public void makeFactTop(BitSet fact) {
6973 fact.clear();
7074 fact.set(topBit);
7175 }
7276
77 @Override
7378 public boolean same(BitSet fact1, BitSet fact2) {
7479 return fact1.equals(fact2);
7580 }
7681
82 @Override
7783 public void meetInto(BitSet fact, Edge edge, BitSet result) throws DataflowAnalysisException {
7884 verifyFact(fact);
7985 verifyFact(result);
94100 @Override
95101 public void transferInstruction(InstructionHandle handle, BasicBlock basicBlock, BitSet fact)
96102 throws DataflowAnalysisException {
97 if (!isFactValid(fact))
103 if (!isFactValid(fact)) {
98104 return;
105 }
99106
100107 Instruction ins = handle.getInstruction();
101108
119126 fact.clear(local + killedByStoreOffset);
120127 }
121128
122 if (!isFactValid(fact))
129 if (!isFactValid(fact)) {
123130 throw new IllegalStateException("Fact become invalid");
131 }
124132 }
125133
126134 @Override
134142 */
135143 private void verifyFact(BitSet fact) {
136144 if (VERIFY_INTEGRITY) {
137 if (isTop(fact) && fact.nextSetBit(0) < topBit)
145 if (isTop(fact) && fact.nextSetBit(0) < topBit) {
138146 throw new IllegalStateException();
147 }
139148 }
140149 }
141150
142151 @Override
143152 public String factToString(BitSet fact) {
144 if (isTop(fact))
153 if (isTop(fact)) {
145154 return "[TOP]";
155 }
146156 StringBuilder buf = new StringBuilder("[ ");
147157 boolean empty = true;
148158 for (int i = 0; i < killedByStoreOffset; i++) {
149159 boolean killedByStore = killedByStore(fact, i);
150160 boolean storeAlive = isStoreAlive(fact, i);
151 if (!storeAlive && !killedByStore)
161 if (!storeAlive && !killedByStore) {
152162 continue;
153 if (!empty)
163 }
164 if (!empty) {
154165 buf.append(", ");
166 }
155167 empty = false;
156168 buf.append(i);
157 if (storeAlive)
169 if (storeAlive) {
158170 buf.append("L");
159 if (killedByStore)
171 }
172 if (killedByStore) {
160173 buf.append("k");
174 }
161175 }
162176 buf.append("]");
163177 return buf.toString();
166180 /**
167181 * Return whether or not given fact is the special TOP value.
168182 */
183 @Override
169184 public boolean isTop(BitSet fact) {
170185 return fact.get(topBit);
171186 }
172187
173188 /**
174189 * Return whether or not a store of given local is alive.
175 *
190 *
176191 * @param fact
177192 * a dataflow fact created by this analysis
178193 * @param local
214229 // }
215230 }
216231
217 // vim:ts=4
2222
2323 /**
2424 * Dataflow class for LiveLocalStoreAnalysis.
25 *
25 *
2626 * @see LiveLocalStoreAnalysis
2727 * @author David Hovemeyer
2828 */
3232 }
3333 }
3434
35 // vim:ts=4
1818
1919 package edu.umd.cs.findbugs.ba;
2020
21 import java.util.Objects;
22
2123 import javax.annotation.Nonnull;
2224
2325 import org.apache.bcel.generic.InstructionHandle;
3436 * Location objects may be compared with each other using the equals() method,
3537 * and may be used as keys in tree and hash maps and sets. Note that
3638 * <em>it is only valid to compare Locations produced from the same CFG</em>.
37 *
39 *
3840 * @author David Hovemeyer
3941 * @see CFG
4042 */
4749
4850 /**
4951 * Constructor.
50 *
52 *
5153 * @param handle
5254 * the instruction
5355 * @param basicBlock
5456 * the basic block containing the instruction
5557 */
5658 public Location(@Nonnull InstructionHandle handle, @Nonnull BasicBlock basicBlock) {
57 if (handle == null)
58 throw new NullPointerException("handle cannot be null");
59 if (basicBlock == null)
60 throw new NullPointerException("basicBlock cannot be null");
59 Objects.requireNonNull(handle, "handle cannot be null");
60 Objects.requireNonNull(basicBlock, "basicBlock cannot be null");
6161 this.handle = handle;
6262 this.basicBlock = basicBlock;
6363 }
6464
6565 public static Location getFirstLocation(@Nonnull BasicBlock basicBlock) {
6666 InstructionHandle location = basicBlock.getFirstInstruction();
67 if (location == null)
67 if (location == null) {
6868 return null;
69 }
6970 return new Location(location, basicBlock);
7071 }
7172
7677 * basicBlock.getExceptionThrower(); if (lastInstruction == null)
7778 * lastInstruction = basicBlock.getFirstInstruction();
7879 */
79 if (lastInstruction == null)
80 if (lastInstruction == null) {
8081 return null;
82 }
8183 return new Location(lastInstruction, basicBlock);
8284 }
8385
113115 return !basicBlock.isEmpty() && handle == basicBlock.getLastInstruction();
114116 }
115117
118 @Override
116119 public int compareTo(Location other) {
117120 int pos = handle.getPosition() - other.handle.getPosition();
118121 return pos;
128131
129132 @Override
130133 public boolean equals(Object o) {
131 if (!(o instanceof Location))
134 if (!(o instanceof Location)) {
132135 return false;
136 }
133137 Location other = (Location) o;
134138 return basicBlock == other.basicBlock && handle == other.handle;
135139 }
148152 }
149153 }
150154
151 // vim:ts=4
3535 * Analysis to determine where particular values are locked in a method. The
3636 * dataflow values are maps of value numbers to the number of times those values
3737 * are locked.
38 *
38 *
3939 * @author David Hovemeyer
4040 * @see ValueNumberAnalysis
4141 */
4242 public class LockAnalysis extends ForwardDataflowAnalysis<LockSet> {
4343 private static final boolean DEBUG = SystemProperties.getBoolean("la.debug");
4444
45 private MethodGen methodGen;
45 private final MethodGen methodGen;
4646
47 private ValueNumberDataflow vnaDataflow;
47 private final ValueNumberDataflow vnaDataflow;
4848
49 private ValueNumberAnalysis vna;
49 private final ValueNumberAnalysis vna;
5050
51 private boolean isSynchronized;
51 private final boolean isSynchronized;
5252
53 private boolean isStatic;
53 private final boolean isStatic;
5454
5555 public LockAnalysis(MethodGen methodGen, ValueNumberDataflow vnaDataflow, DepthFirstSearch dfs) {
5656 super(dfs);
5959 this.vna = vnaDataflow.getAnalysis();
6060 this.isSynchronized = methodGen.isSynchronized();
6161 this.isStatic = methodGen.isStatic();
62 if (DEBUG)
62 if (DEBUG) {
6363 System.out.println("Analyzing Locks in " + methodGen.getClassName() + "." + methodGen.getName());
64 }
6465 }
6566
67 @Override
6668 public LockSet createFact() {
6769 return new LockSet();
6870 }
6971
72 @Override
7073 public void copy(LockSet source, LockSet dest) {
7174 dest.copyFrom(source);
7275 }
7376
77 @Override
7478 public void initEntryFact(LockSet result) {
7579 result.clear();
7680 result.setDefaultLockCount(0);
8488 }
8589 }
8690
91 @Override
8792 public void makeFactTop(LockSet fact) {
8893 fact.clear();
8994 fact.setDefaultLockCount(LockSet.TOP);
9095 }
9196
97 @Override
9298 public boolean isTop(LockSet fact) {
9399 return fact.isTop();
94100 }
95101
102 @Override
96103 public boolean same(LockSet fact1, LockSet fact2) {
97104 return fact1.sameAs(fact2);
98105 }
99106
107 @Override
100108 public void meetInto(LockSet fact, Edge edge, LockSet result) throws DataflowAnalysisException {
101109 result.meetWith(fact);
102110 }
119127 String sig = inv.getSignature(methodGen.getConstantPool());
120128 ValueNumberFrame frame = vnaDataflow.getFactAtLocation(new Location(handle, basicBlock));
121129
122 if (sig.equals("()V") && (name.equals("lock") || name.equals("lockInterruptibly")))
130 if ("()V".equals(sig) && ("lock".equals(name) || "lockInterruptibly".equals(name))) {
123131 modifyLock(frame, fact, 1);
124 else if (sig.equals("()V") && (name.equals("unlock")))
132 } else if ("()V".equals(sig) && ("unlock".equals(name))) {
125133 modifyLock(frame, fact, -1);
134 }
126135
127136 } else if ((ins instanceof ReturnInstruction) && isSynchronized && !isStatic) {
128137
139148
140149 private void lockOp(LockSet fact, int lockNumber, int delta) {
141150 int value = fact.getLockCount(lockNumber);
142 if (value < 0) // can't modify TOP or BOTTOM value
151 if (value < 0) {
143152 return;
153 }
144154 value += delta;
145 if (value < 0)
155 if (value < 0) {
146156 value = LockSet.BOTTOM;
147 if (DEBUG)
157 }
158 if (DEBUG) {
148159 System.out.println("Setting " + lockNumber + " to " + value + " in " + methodGen.getClassName() + "."
149160 + methodGen.getName());
161 }
150162 fact.setLockCount(lockNumber, value);
151163 }
152164
176188 // }
177189 }
178190
179 // vim:ts=4
3434 * Front-end for LockDataflow that can avoid doing unnecessary work (e.g.,
3535 * actually performing the lock dataflow) if the method analyzed does not
3636 * contain explicit monitorenter/monitorexit instructions.
37 *
37 *
3838 * <p>
3939 * Note that because LockSets use value numbers, ValueNumberAnalysis must be
4040 * performed for all methods that are synchronized or contain explicit
4141 * monitorenter/monitorexit instructions.
4242 * </p>
43 *
43 *
4444 * @see LockSet
4545 * @see LockDataflow
4646 * @see LockAnalysis
4747 * @author David Hovemeyer
4848 */
4949 public class LockChecker {
50 private MethodDescriptor methodDescriptor;
50 private final MethodDescriptor methodDescriptor;
5151
5252 private Method method;
5353
5555
5656 private ValueNumberDataflow vnaDataflow;
5757
58 private HashMap<Location, LockSet> cache;
58 private final HashMap<Location, LockSet> cache;
5959
6060 /**
6161 * Constructor.
6767
6868 /**
6969 * Execute dataflow analyses (only if required).
70 *
70 *
7171 * @throws CheckedAnalysisException
7272 */
7373 public void execute() throws CheckedAnalysisException {
7676 methodDescriptor.getClassDescriptor());
7777
7878 BitSet bytecodeSet = classContext.getBytecodeSet(method);
79 if (bytecodeSet == null)
79 if (bytecodeSet == null) {
8080 return;
81 }
8182 if (bytecodeSet.get(Constants.MONITORENTER) || bytecodeSet.get(Constants.MONITOREXIT)) {
8283 this.lockDataflow = classContext.getLockDataflow(method);
8384 } else if (method.isSynchronized()) {
8485 this.vnaDataflow = classContext.getValueNumberDataflow(method); // will
85 // need
86 // this
87 // later
86 // need
87 // this
88 // later
8889 }
8990 }
9091
9192 /**
9293 * Get LockSet at given Location.
93 *
94 *
9495 * @param location
9596 * the Location
9697 * @return the LockSet at that Location
9798 * @throws DataflowAnalysisException
9899 */
99100 public LockSet getFactAtLocation(Location location) throws DataflowAnalysisException {
100 if (lockDataflow != null)
101 if (lockDataflow != null) {
101102 return lockDataflow.getFactAtLocation(location);
102 else {
103 } else {
103104 LockSet lockSet = cache.get(location);
104105 if (lockSet == null) {
105106 lockSet = new LockSet();
2020
2121 /**
2222 * Dataflow class for LockAnalysis.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public class LockDataflow extends Dataflow<LockSet, LockAnalysis> {
2929 }
3030 }
3131
32 // vim:ts=4
2929 * Lock counts for values (as produced by ValueNumberAnalysis). A LockSet tells
3030 * us the lock counts for all values in a method, insofar as we can accurately
3131 * determine them.
32 *
32 *
3333 * @author David Hovemeyer
3434 * @see edu.umd.cs.findbugs.ba.vna.ValueNumberAnalysis
3535 */
7373
7474 /**
7575 * Get the lock count for given lock object.
76 *
76 *
7777 * @param valueNumber
7878 * value number of the lock object
7979 * @return the lock count for the lock object
8080 */
8181 public int getLockCount(int valueNumber) {
8282 int index = findIndex(valueNumber);
83 if (index < 0)
83 if (index < 0) {
8484 return defaultLockCount;
85 else
85 } else {
8686 return array[index + 1];
87 }
8788 }
8889
8990 public boolean isTop() {
9293
9394 /**
9495 * Set the lock count for a lock object.
95 *
96 *
9697 * @param valueNumber
9798 * value number of the lock object
9899 * @param lockCount
109110
110111 /**
111112 * Set the default lock count to return for nonexistent lock entries.
112 *
113 *
113114 * @param defaultLockCount
114115 * the default lock count value
115116 */
123124 public int getNumLockedObjects() {
124125 int result = 0;
125126 for (int i = 0; i < array.length; i += 2) {
126 if (array[i] == INVALID)
127 break;
128 if (array[i + 1] > 0)
127 if (array[i] == INVALID) {
128 break;
129 }
130 if (array[i + 1] > 0) {
129131 ++result;
132 }
130133 }
131134 return result;
132135 }
133136
134137 /**
135138 * Make this LockSet the same as the given one.
136 *
139 *
137140 * @param other
138141 * the LockSet to copy
139142 */
157160 /**
158161 * Meet this LockSet with another LockSet, storing the result in this
159162 * object.
160 *
163 *
161164 * @param other
162165 * the other LockSet
163166 */
164167 public void meetWith(LockSet other) {
165168 for (int i = 0; i < array.length; i += 2) {
166169 int valueNumber = array[i];
167 if (valueNumber < 0)
168 break;
170 if (valueNumber < 0) {
171 break;
172 }
169173
170174 int mine = array[i + 1];
171175 int his = other.getLockCount(valueNumber);
174178
175179 for (int i = 0; i < other.array.length; i += 2) {
176180 int valueNumber = other.array[i];
177 if (valueNumber < 0)
178 break;
181 if (valueNumber < 0) {
182 break;
183 }
179184
180185 int mine = getLockCount(valueNumber);
181186 int his = other.array[i + 1];
187192
188193 /**
189194 * Return whether or not this LockSet is the same as the one given.
190 *
195 *
191196 * @param other
192197 * the other LockSet
193198 */
198203 /**
199204 * Determine whether or not this lock set contains any locked values which
200205 * are method return values.
201 *
206 *
202207 * @param factory
203208 * the ValueNumberFactory that produced the lock values
204209 */
205210 public boolean containsReturnValue(ValueNumberFactory factory) {
206211 for (int i = 0; i < array.length; i += 2) {
207212 int valueNumber = array[i];
208 if (valueNumber < 0)
209 break;
213 if (valueNumber < 0) {
214 break;
215 }
210216 int lockCount = array[i + 1];
211 if (lockCount > 0 && factory.forNumber(valueNumber).hasFlag(ValueNumber.RETURN_VALUE))
217 if (lockCount > 0 && factory.forNumber(valueNumber).hasFlag(ValueNumber.RETURN_VALUE)) {
212218 return true;
219 }
213220 }
214221 return false;
215222 }
218225 * Destructively intersect this lock set with another. Note that this is
219226 * <em>not</em> a dataflow merge: we are interested in finding out which
220227 * locks are held in both sets, not in the exact lock counts.
221 *
228 *
222229 * @param other
223230 * the other LockSet
224231 */
225232 public void intersectWith(LockSet other) {
226233 for (int i = 0; i < array.length; i += 2) {
227234 int valueNumber = array[i];
228 if (valueNumber < 0)
229 break;
235 if (valueNumber < 0) {
236 break;
237 }
230238 int myLockCount = array[i + 1];
231 if (myLockCount <= 0)
239 if (myLockCount <= 0) {
232240 continue;
241 }
233242 int otherLockCount = other.getLockCount(valueNumber);
234243 if (otherLockCount <= 0) {
235244 /* This set holds the lock, but the other one doesn't. */
241250 /**
242251 * Return whether or not this lock set is empty, meaning that no locks have
243252 * a positive lock count.
244 *
253 *
245254 * @return true if no locks are held, false if at least one lock is held
246255 */
247256 public boolean isEmpty() {
248257 for (int i = 0; i < array.length; i += 2) {
249258 int valueNumber = array[i];
250 if (valueNumber < 0)
259 if (valueNumber < 0) {
251260 return true;
261 }
252262 int myLockCount = array[i + 1];
253 if (myLockCount > 0)
263 if (myLockCount > 0) {
254264 return false;
265 }
255266 }
256267 return true;
257268 }
259270 private boolean identicalSubset(LockSet other) {
260271 for (int i = 0; i < array.length; i += 2) {
261272 int valueNumber = array[i];
262 if (valueNumber < 0)
263 break;
273 if (valueNumber < 0) {
274 break;
275 }
264276 int mine = array[i + 1];
265277 int his = other.getLockCount(valueNumber);
266278 if (mine != his)
279 {
267280 return false;
268 // System.out.println("For value " + valueNumber + ", " + mine +
269 // "==" + his);
281 // System.out.println("For value " + valueNumber + ", " + mine +
282 // "==" + his);
283 }
270284 }
271285 return true;
272286 }
273287
274288 private static int mergeValues(int a, int b) {
275 if (a == TOP)
289 if (a == TOP) {
276290 return b;
277 else if (b == TOP)
291 } else if (b == TOP) {
278292 return a;
279 else if (a == BOTTOM || b == BOTTOM)
293 } else if (a == BOTTOM || b == BOTTOM) {
280294 return BOTTOM;
281 else if (a == b)
295 } else if (a == b) {
282296 return a;
283 else
297 } else {
284298 return BOTTOM;
299 }
285300 }
286301
287302 private int findIndex(int valueNumber) {
288303 for (int i = 0; i < array.length; i += 2) {
289304 int value = array[i];
290 if (value < 0)
305 if (value < 0) {
291306 return -(i + 1); // didn't find requested valueNumber - return
292 // first available slot
293 else if (value == valueNumber)
307 } else if (value == valueNumber)
308 {
294309 return i; // found requested valueNumber
310 }
295311 }
296312 return -(array.length + 1); // didn't find requested valueNumber, and
297 // array is full
313 // array is full
298314 }
299315
300316 private void addEntry(int negatedIndex, int valueNumber, int lockCount) {
335351 }
336352 for (int i = 0; i < array.length; i += 2) {
337353 int valueNumber = array[i];
338 if (valueNumber < 0)
354 if (valueNumber < 0) {
339355 continue;
356 }
340357 int lockCount = array[i + 1];
341 if (lockCount == 0)
358 if (lockCount == 0) {
342359 continue;
343 if (first)
360 }
361 if (first) {
344362 first = false;
345 else
363 } else {
346364 buf.append(',');
365 }
347366 buf.append(valueNumber);
348367 buf.append('=');
349 if (lockCount == TOP)
368 if (lockCount == TOP) {
350369 buf.append("TOP");
351 else if (lockCount == BOTTOM)
370 } else if (lockCount == BOTTOM) {
352371 buf.append("BOTTOM");
353 else
372 } else {
354373 buf.append(lockCount);
374 }
355375 }
356376 buf.append(']');
357377 return buf.toString();
362382 * @return a set of the locked value numbers
363383 */
364384 public Collection<ValueNumber> getLockedValueNumbers(ValueNumberFrame frame) {
365 if (frame == null)
385 if (frame == null) {
366386 throw new IllegalArgumentException("Null Frame");
387 }
367388 HashSet<ValueNumber> result = new HashSet<ValueNumber>();
368 for (ValueNumber v : frame.allSlots())
369 if (v != null && getLockCount(v.getNumber()) > 0)
389 for (ValueNumber v : frame.allSlots()) {
390 if (v != null && getLockCount(v.getNumber()) > 0) {
370391 result.add(v);
392 }
393 }
371394 return result;
372395 }
373396
379402 * 1); ll.setLockCount(69, 3); LockSet tmp = new LockSet();
380403 * tmp.copyFrom(ll); ll.meetWith(l); System.out.println(l + " merge with " +
381404 * tmp + " ==> " + ll);
382 *
405 *
383406 * LockSet dup = new LockSet(); dup.copyFrom(ll); System.out.println(ll +
384407 * " == " + dup + " ==> " + ll.sameAs(dup)); System.out.println(ll + " == "
385408 * + l + " ==> " + ll.sameAs(l)); }
386409 */
387410 }
388411
389 // vim:ts=4
2424
2525 /**
2626 * Class representing the set of opcodes used in a method.
27 *
27 *
2828 * @author David Hovemeyer
2929 */
3030 public class MethodBytecodeSet extends BitSet {
2222
2323 /**
2424 * Interface for choosing methods.
25 *
25 *
2626 * @author David Hovemeyer
2727 */
2828 public interface MethodChooser {
2929 /**
3030 * Determine whether or not given method should be chosen.
31 *
31 *
3232 * @param method
3333 * the Method
3434 * @return true if the method should be chosen, false if not
2828 /**
2929 * Compute a hash of the bytecode for given method. This can find methods which
3030 * have not been changed other than accessing different constant pool entries.
31 *
31 *
3232 * @author David Hovemeyer
3333 */
3434 public class MethodHash implements Comparable<MethodHash> {
5050
5151 /**
5252 * Constructor.
53 *
53 *
5454 * @param methodName
5555 * method name
5656 * @param methodSig
9191
9292 /**
9393 * Get the computed method hash.
94 *
94 *
9595 * @return the method hash
9696 */
9797 public byte[] getMethodHash() {
100100
101101 /**
102102 * Compute hash on given method.
103 *
103 *
104104 * @param method
105105 * the method
106106 * @return this object
116116 }
117117
118118 BytecodeScanner.Callback callback = new BytecodeScanner.Callback() {
119 @Override
119120 public void handleInstruction(int opcode, int index) {
120121 digest.update((byte) opcode);
121122 }
132133 /**
133134 * Return whether or not this method hash has the same value as the one
134135 * given.
135 *
136 *
136137 * @param other
137138 * another MethodHash
138139 * @return true if the hash values are the same, false if not
143144
144145 /*
145146 * (non-Javadoc)
146 *
147 *
147148 * @see java.lang.Comparable#compareTo(T)
148149 */
150 @Override
149151 public int compareTo(MethodHash other) {
150152 return MethodHash.compareHashes(this.hash, other.hash);
151153 }
152154
153155 @Override
154156 public boolean equals(Object o) {
155 if (o instanceof MethodHash)
157 if (o instanceof MethodHash) {
156158 return isSameHash((MethodHash) o);
159 }
157160 return false;
158161 }
159162
160163 @Override
161164 public int hashCode() {
162165 int result = 0;
163 for (byte b : hash)
166 for (byte b : hash) {
164167 result = result * 17 + b;
168 }
165169 return result;
166170 }
167171
169173 int pfxlen = Math.min(a.length, b.length);
170174 for (int i = 0; i < pfxlen; ++i) {
171175 int cmp = toUnsigned(a[i]) - toUnsigned(b[i]);
172 if (cmp != 0)
176 if (cmp != 0) {
173177 return cmp;
178 }
174179 }
175180 return a.length - b.length;
176181 }
177182
178183 /**
179184 * Convert a byte to an unsigned int.
180 *
185 *
181186 * @param b
182187 * a byte value
183188 * @return the unsigned integer value of the byte
2525
2626 /**
2727 * Used to signal a method not analyzed because it seemed unprofitable to do so
28 *
28 *
2929 * @author pugh
3030 */
3131 public class MethodUnprofitableException extends CFGBuilderException {
3535
3636 /**
3737 * Constructor.
38 *
38 *
3939 * @param method
4040 * the method that is unprofitable to analyze
4141 */
4646
4747 /**
4848 * Constructor.
49 *
49 *
5050 * @param jClass
5151 * the class containing the method that is unprofitable to
5252 * analyze
6060
6161 /**
6262 * Constructor.
63 *
63 *
6464 * @param methodDescriptor
6565 * the MethodDescriptor indicating the method it is unprofitable
6666 * to analyze
2525 /**
2626 * DataflowAnalysisException variant to report a class lookup failure that
2727 * caused dataflow analysis to abort.
28 *
28 *
2929 * @author David Hovemeyer
3030 */
3131 public class MissingClassException extends DataflowAnalysisException {
3232 private static final long serialVersionUID = 1L;
3333
3434 private @DottedClassName
35 final
3536 String className;
3637
3738 private MissingClassException(String className) {
4142
4243 /**
4344 * Constructor.
44 *
45 *
4546 * @param exception
4647 * the ClassNotFoundException that caused this exception
4748 */
5253
5354 /**
5455 * Get the name of the missing class.
55 *
56 *
5657 * @return name of the missing class, or null if the missing class name is
5758 * unknown
5859 */
6667
6768 /**
6869 * Get the ClassNotFoundException that caused this exception.
69 *
70 *
7071 * @return the ClassNotFoundException that caused this exception
7172 */
7273 public ClassNotFoundException getClassNotFoundException() {
4141 @CheckForNull
4242 public static NullnessAnnotation parse(@DottedClassName String className) {
4343 className = ClassName.toDottedClassName(className);
44 if (className.equals("com.google.common.base.Nullable")
45 || className.equals("org.eclipse.jdt.annotation.Nullable")
46 || className.equals("org.jetbrains.annotations.Nullable")) {
44 if ("com.google.common.base.Nullable".equals(className)
45 || "org.eclipse.jdt.annotation.Nullable".equals(className)
46 || "org.jetbrains.annotations.Nullable".equals(className)) {
4747 return CHECK_FOR_NULL;
4848 }
4949 // Unfortunately there are mixed case Nonnull and NonNull annotations (JSR305, FB and JDT)
4141 DefaultNullnessAnnotations.addDefaultNullnessAnnotations(this);
4242 }
4343
44 @Override
4445 public boolean parameterMustBeNonNull(XMethod m, int param) {
4546 if (param == 0) {
46 if (m.getName().equals("equals") && m.getSignature().equals("(Ljava/lang/Object;)Z") && !m.isStatic())
47 if ("equals".equals(m.getName()) && "(Ljava/lang/Object;)Z".equals(m.getSignature()) && !m.isStatic()) {
4748 return false;
48 else if (m.getName().equals("main") && m.getSignature().equals("([Ljava/lang/String;)V") && m.isStatic()
49 && m.isPublic())
49 } else if ("main".equals(m.getName()) && "([Ljava/lang/String;)V".equals(m.getSignature()) && m.isStatic()
50 && m.isPublic()) {
5051 return true;
51 else if (TypeQualifierNullnessAnnotationDatabase.assertsFirstParameterIsNonnull(m))
52 } else if (TypeQualifierNullnessAnnotationDatabase.assertsFirstParameterIsNonnull(m)) {
5253 return true;
53 else if (m.getName().equals("compareTo") && m.getSignature().endsWith(";)Z") && !m.isStatic())
54 } else if ("compareTo".equals(m.getName()) && m.getSignature().endsWith(";)Z") && !m.isStatic()) {
5455 return true;
56 }
5557 }
56 if (!anyAnnotations(NullnessAnnotation.NONNULL))
58 if (!anyAnnotations(NullnessAnnotation.NONNULL)) {
5759 return false;
60 }
5861 XMethodParameter xmp = new XMethodParameter(m, param);
5962 NullnessAnnotation resolvedAnnotation = getResolvedAnnotation(xmp, true);
6063
7679 // bug code for it
7780 int parameterNumber = mp.getParameterNumber();
7881 if (parameterNumber == 0) {
79 if (m.getName().equals("equals") && m.getSignature().equals("(Ljava/lang/Object;)Z") && !m.isStatic())
82 if ("equals".equals(m.getName()) && "(Ljava/lang/Object;)Z".equals(m.getSignature()) && !m.isStatic()) {
8083 return NullnessAnnotation.CHECK_FOR_NULL;
81 else if (m.getName().equals("main") && m.getSignature().equals("([Ljava/lang/String;)V") && m.isStatic()
82 && m.isPublic())
84 } else if ("main".equals(m.getName()) && "([Ljava/lang/String;)V".equals(m.getSignature()) && m.isStatic()
85 && m.isPublic()) {
8386 return NullnessAnnotation.NONNULL;
84 else if (TypeQualifierNullnessAnnotationDatabase.assertsFirstParameterIsNonnull(m))
87 } else if (TypeQualifierNullnessAnnotationDatabase.assertsFirstParameterIsNonnull(m)) {
8588 return NullnessAnnotation.NONNULL;
86 else if (m.getName().equals("compareTo") && m.getSignature().endsWith(";)Z") && !m.isStatic())
89 } else if ("compareTo".equals(m.getName()) && m.getSignature().endsWith(";)Z") && !m.isStatic()) {
8790 return NullnessAnnotation.NONNULL;
91 }
8892 }
8993 } else if (o instanceof XMethod) {
9094 XMethod m = (XMethod) o;
9195 String name = m.getName();
9296 String signature = m.getSignature();
9397 if (!m.isStatic()
94 && (name.equals("clone") && signature.equals("()Ljava/lang/Object;") || name.equals("toString")
95 && signature.equals("()Ljava/lang/String;") || m.isPrivate() && name.equals("readResolve")
96 && signature.equals("()Ljava/lang/Object;"))) {
98 && ("clone".equals(name) && "()Ljava/lang/Object;".equals(signature) || "toString".equals(name)
99 && "()Ljava/lang/String;".equals(signature) || m.isPrivate() && "readResolve".equals(name)
100 && "()Ljava/lang/Object;".equals(signature))) {
97101 NullnessAnnotation result = super.getDirectAnnotation(m);
98 if (result != null)
102 if (result != null) {
99103 return result;
104 }
100105 return NullnessAnnotation.NONNULL;
101106 }
102107
103108 } else if (o instanceof XField) {
104109 XField f = (XField) o;
105 if (f.getName().startsWith("this$"))
110 if (f.getName().startsWith("this$")) {
106111 return NullnessAnnotation.NONNULL;
112 }
107113 }
108114 NullnessAnnotation result = super.getResolvedAnnotation(o, getMinimal);
109115 return result;
112118 }
113119 }
114120
115
121
116122
117123 @Override
118124 public void addDefaultMethodAnnotation(String name, NullnessAnnotation annotation) {
4747 public static ObjectType getInstance(Class<?> c) {
4848 return getInstance(c.getName());
4949 }
50
51
50
51
5252 public static ObjectType getInstance(@DottedClassName String s) {
5353 if (FindBugs.DEBUG && s.startsWith("[")) {
5454 throw new IllegalArgumentException("Cannot create an ObjectType to represent an array type: " + s);
5555 }
56 if (s.endsWith(";"))
56 if (s.endsWith(";")) {
5757 throw new IllegalArgumentException(s);
58 if (s.indexOf("/") >= 0) {
58 }
59 if (s.indexOf('/') >= 0) {
5960 s = s.replace('/', '.');
6061 }
6162
6263 Map<String, ObjectType> map = instance.get();
6364 ObjectType result = map.get(s);
64 if (result != null)
65 if (result != null) {
6566 return result;
67 }
6668 result = ObjectType.getInstance(s);
6769 map.put(s, result);
6870 return result;
8686
8787 @Override
8888 public void sawOpcode(int seen) {
89 }
90
91 @Override
92 public void afterOpcode(int seen) {
8993 if(DEBUG) {
9094 System.out.printf("%3d: %8s %s%n", getPC(), OPCODE_NAMES[seen], getStack());
9195 }
92 if (getPC() == targetPC)
96 if (getPC() == targetPC) {
9397 throw new EarlyExitException(stack);
98 }
99 super.afterOpcode(seen);
94100 }
95101
96102 @Override
2424
2525 /**
2626 * A Path is a sequence of basic blocks.
27 *
27 *
2828 * @author David Hovemeyer
2929 */
3030 public class Path {
4949
5050 /**
5151 * Append given BasicBlock id to the path.
52 *
52 *
5353 * @param id
5454 * a BasicBlock id (label)
5555 */
6363 /**
6464 * Determine whether or not the id of the given BasicBlock appears anywhere
6565 * in the path.
66 *
66 *
6767 * @param blockId
6868 * the id (label) of a BasicBlock
6969 * @return true if the BasicBlock's id appears in the path, false if not
7979
8080 /**
8181 * Get the BasicBlock id at the given index in the path.
82 *
82 *
8383 * @param index
8484 * an index in the Path (0 is the first component)
8585 * @return the id of the BasicBlock at the given index
9191
9292 /**
9393 * Get the number of components (BasicBlock ids) in the Path.
94 *
94 *
9595 * @return number of components in the Path
9696 */
9797 public int getLength() {
100100
101101 /**
102102 * Return an exact copy of this Path.
103 *
103 *
104104 * @return an exact copy of this Path
105105 */
106106 public Path duplicate() {
111111
112112 /**
113113 * Make this Path identical to the given one.
114 *
114 *
115115 * @param other
116116 * a Path to which this object should be made identical
117117 */
124124
125125 /**
126126 * Accept a PathVisitor.
127 *
127 *
128128 * @param cfg
129129 * the control flow graph
130130 * @param visitor
140140 /**
141141 * Accept a PathVisitor, starting from a given BasicBlock and
142142 * InstructionHandle.
143 *
143 *
144144 * @param cfg
145145 * the control flow graph
146146 * @param visitor
205205
206206 /**
207207 * Determine whether or not given Path is a prefix of this one.
208 *
208 *
209209 * @param path
210210 * another Path
211211 * @return true if this Path is a prefix of the other Path, false otherwise
240240
241241 @Override
242242 public boolean equals(Object o) {
243 if (o == null || o.getClass() != this.getClass())
243 if (o == null || o.getClass() != this.getClass()) {
244244 return false;
245 }
245246 Path other = (Path) o;
246 if (this.length != other.length)
247 if (this.length != other.length) {
247248 return false;
249 }
248250 for (int i = 0; i < this.length; ++i) {
249 if (this.blockIdList[i] != other.blockIdList[i])
251 if (this.blockIdList[i] != other.blockIdList[i]) {
250252 return false;
253 }
251254 }
252255 return true;
253256 }
259262 StringBuilder buf = new StringBuilder();
260263 for (int i = 0; i < length; ++i) {
261264 int block = blockIdList[i];
262 if (block < SYMBOLS.length())
265 if (block < SYMBOLS.length()) {
263266 buf.append(SYMBOLS.charAt(block));
264 else
267 } else {
265268 buf.append("'" + block + "'");
269 }
266270 }
267271 return buf.toString();
268272 }
281285 }
282286 }
283287
284 // vim:ts=4
2222
2323 /**
2424 * Visit the BasicBlocks, InstructionHandles, and Edges along a Path.
25 *
25 *
2626 * @author David Hovemeyer
2727 */
2828 public interface PathVisitor {
2929 /**
3030 * Start to visit the given BasicBlock.
31 *
31 *
3232 * @param basicBlock
3333 * a BasicBlock in the Path being visited
3434 */
3636
3737 /**
3838 * Visit an InstructionHandle within the BasicBlock currently being visited.
39 *
39 *
4040 * @param handle
4141 * an InstructionHandle within the current BasicBlock
4242 */
4444
4545 /**
4646 * Visit an Edge connecting two BasicBlocks in the Path being visited.
47 *
47 *
4848 * @param edge
4949 * an Edge connecting two BasicBlocks in the Path being visited
5050 */
2020
2121 /**
2222 * Dataflow analysis to compute postdominator sets for a CFG.
23 *
23 *
2424 * @author David Hovemeyer
2525 * @see CFG
2626 * @see AbstractDominatorsAnalysis
3232
3333 /**
3434 * Constructor.
35 *
35 *
3636 * @param cfg
3737 * the CFG to compute dominator relationships for
3838 * @param rdfs
5050
5151 /**
5252 * Constructor.
53 *
53 *
5454 * @param cfg
5555 * the CFG to compute dominator relationships for
5656 * @param rdfs
6666 this.dfs = dfs;
6767 }
6868
69 @Override
6970 public boolean isForwards() {
7071 return false;
7172 }
7273
74 @Override
7375 public BlockOrder getBlockOrder(CFG cfg) {
7476 return new ReverseDFSOrder(cfg, rdfs, dfs);
7577 }
116118 // }
117119 }
118120
119 // vim:ts=4
2727 import org.apache.bcel.generic.MethodGen;
2828
2929 import edu.umd.cs.findbugs.SystemProperties;
30 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
3031 import edu.umd.cs.findbugs.ba.ch.Subtypes2;
3132 import edu.umd.cs.findbugs.ba.type.ExceptionSet;
3233 import edu.umd.cs.findbugs.ba.type.TypeDataflow;
3536 * Prune a CFG to remove infeasible exception edges. In order to determine what
3637 * kinds of exceptions can be thrown by explicit ATHROW instructions, type
3738 * analysis must first be performed on the unpruned CFG.
38 *
39 *
3940 * @author David Hovemeyer
4041 * @see CFG
4142 * @see edu.umd.cs.findbugs.ba.type.TypeAnalysis
6768 * successful.
6869 */
6970 private static class MarkedEdge {
70 private Edge edge;
71 private final Edge edge;
7172
72 private int flag;
73 private final int flag;
7374
7475 public MarkedEdge(Edge edge, int flag) {
7576 this.edge = edge;
8384 }
8485 }
8586
86 private CFG cfg;
87 private final CFG cfg;
8788
88 private TypeDataflow typeDataflow;
89 private final TypeDataflow typeDataflow;
8990
9091 private boolean cfgModified;
9192
9293 /**
9394 * Constructor.
94 *
95 *
9596 * @param cfg
9697 * the CFG to prune
9798 * @param methodGen
115116 * runtime exception is thrown, then the CFG may be partially modified and
116117 * should be considered invalid.
117118 */
119 @SuppressFBWarnings("ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD")
118120 public void execute() throws ClassNotFoundException {
119121 Set<Edge> deletedEdgeSet = new HashSet<Edge>();
120122 List<MarkedEdge> markedEdgeList = new LinkedList<MarkedEdge>();
123125 // mark edges to set properties of
124126 for (Iterator<Edge> i = cfg.edgeIterator(); i.hasNext();) {
125127 Edge edge = i.next();
126 if (!edge.isExceptionEdge())
128 if (!edge.isExceptionEdge()) {
127129 continue;
130 }
128131
129132 ExceptionSet exceptionSet = typeDataflow.getEdgeExceptionSet(edge);
130133 if (exceptionSet.isEmpty()) {
147150 boolean someExplicit = exceptionSet.containsExplicitExceptions();
148151
149152 int flags = 0;
150 if (someChecked)
153 if (someChecked) {
151154 flags |= CHECKED_EXCEPTIONS_FLAG;
152 if (someExplicit)
155 }
156 if (someExplicit) {
153157 flags |= EXPLICIT_EXCEPTIONS_FLAG;
158 }
154159
155160 markedEdgeList.add(new MarkedEdge(edge, flags));
156161 }
159164 // Remove deleted edges
160165 for (Edge edge : deletedEdgeSet) {
161166 cfg.removeEdge(edge);
162 if (STATS)
167 if (STATS) {
163168 ++numEdgesPruned;
169 }
164170 cfgModified = true;
165171 }
166172
178184 }
179185 }
180186
181 // vim:ts=4
4949 private static final String UNCONDITIONAL_THROWER_METHOD_NAMES = SystemProperties.getProperty(
5050 "findbugs.unconditionalThrower", " ").replace(',', '|');
5151
52 private MethodGen methodGen;
53
54 private CFG cfg;
55
56 private ConstantPoolGen cpg;
57
58 private TypeDataflow typeDataflow;
59
60 private AnalysisContext analysisContext;
52 private final MethodGen methodGen;
53
54 private final CFG cfg;
55
56 private final ConstantPoolGen cpg;
57
58 private final TypeDataflow typeDataflow;
59
60 private final AnalysisContext analysisContext;
6161
6262 private boolean cfgModified;
6363
9797
9898 public void execute() throws DataflowAnalysisException {
9999 AnalysisContext currentAnalysisContext = AnalysisContext.currentAnalysisContext();
100 if (currentAnalysisContext.getBoolProperty(AnalysisFeatures.CONSERVE_SPACE))
100 if (currentAnalysisContext.getBoolProperty(AnalysisFeatures.CONSERVE_SPACE)) {
101101 throw new IllegalStateException("This should not happen");
102 }
102103 boolean foundInexact = false;
103104 Set<Edge> deletedEdgeSet = new HashSet<Edge>();
104105 // TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
105106
106 if (DEBUG)
107 if (DEBUG) {
107108 System.out.println("PruneUnconditionalExceptionThrowerEdges: examining "
108109 + SignatureConverter.convertMethodSignature(methodGen));
110 }
109111
110112 for (Iterator<BasicBlock> i = cfg.blockIterator(); i.hasNext();) {
111113 BasicBlock basicBlock = i.next();
112 if (!basicBlock.isExceptionThrower())
114 if (!basicBlock.isExceptionThrower()) {
113115 continue;
116 }
114117
115118 InstructionHandle instructionHandle = basicBlock.getExceptionThrower();
116119 Instruction exceptionThrower = instructionHandle.getInstruction();
117 if (!(exceptionThrower instanceof InvokeInstruction))
120 if (!(exceptionThrower instanceof InvokeInstruction)) {
118121 continue;
122 }
119123
120124 InvokeInstruction inv = (InvokeInstruction) exceptionThrower;
121125 boolean foundThrower = false;
124128 XMethod primaryXMethod = XFactory.createXMethod(inv, cpg);
125129 final String methodName = primaryXMethod.getName();
126130 final boolean matches = unconditionalThrowerPattern.matcher(methodName).matches();
127 if (DEBUG)
131 if (DEBUG) {
128132 System.out.println("Checking '" + methodName + "' is " + matches);
133 }
129134
130135 if (matches) {
131 if (DEBUG)
136 if (DEBUG) {
132137 System.out.println("\tmatched for " + instructionHandle + " : " + primaryXMethod);
138 }
133139
134140 foundThrower = true;
135141 } else if (inv instanceof INVOKEINTERFACE) {
138144 foundThrower = isUnconditionalThrower(primaryXMethod);
139145 } else {
140146 String className = inv.getClassName(cpg);
141 if (DEBUG)
147 if (DEBUG) {
142148 System.out.println("\tlooking up method for " + instructionHandle + " : " + primaryXMethod);
149 }
143150
144151 Location loc = new Location(instructionHandle, basicBlock);
145152 TypeFrame typeFrame = typeDataflow.getFactAtLocation(loc);
147154 Set<XMethod> targetSet = null;
148155 try {
149156
150 if (className.startsWith("["))
157 if (className.startsWith("[")) {
151158 continue;
159 }
152160 String methodSig = inv.getSignature(cpg);
153 if (!methodSig.endsWith("V") && !methodSig.endsWith("Exception;")
154 && !methodSig.endsWith("Error;") && !methodSig.endsWith(")Ljava/lang/Object;"))
161 if (!methodSig.endsWith("V") && !methodSig.endsWith("Exception;")
162 && !methodSig.endsWith("Error;") && !methodSig.endsWith(")Ljava/lang/Object;")) {
155163 continue;
164 }
156165
157166 targetSet = Hierarchy2.resolveMethodCallTargets(inv, typeFrame, cpg);
158167
159168 for (XMethod xMethod : targetSet) {
160 if (DEBUG)
169 if (DEBUG) {
161170 System.out.println("\tFound " + xMethod);
171 }
162172
163173 // Ignore abstract and native methods
164 if (!(xMethod.isFinal() || xMethod.isStatic() || xMethod.isPrivate()))
165 try {
166 isExact = false;
167 XClass xClass = Global.getAnalysisCache().getClassAnalysis(XClass.class,
168 xMethod.getClassDescriptor());
169 if (xClass.isAbstract())
170 continue;
171 } catch (CheckedAnalysisException e) {
172 AnalysisContext.logError("Unable to resolve class for " + xMethod, e);
173 }
174174 boolean isUnconditionalThrower = isUnconditionalThrower(xMethod);
175175 if (isUnconditionalThrower) {
176 if (!(xMethod.isFinal() || xMethod.isStatic() || xMethod.isPrivate())) {
177 try {
178 isExact = false;
179 XClass xClass = Global.getAnalysisCache().getClassAnalysis(XClass.class,
180 xMethod.getClassDescriptor());
181 if (xClass.isAbstract()) {
182 continue;
183 }
184 } catch (CheckedAnalysisException e) {
185 AnalysisContext.logError("Unable to resolve class for " + xMethod, e);
186 }
187 }
176188 foundThrower = true;
177 if (DEBUG)
189 if (DEBUG) {
178190 System.out.println("Found thrower");
191 }
179192 } else {
180193 foundNonThrower = true;
181 if (DEBUG)
194 if (DEBUG) {
182195 System.out.println("Found non thrower");
196 }
183197 }
184198
185199 }
189203 }
190204 boolean newResult = foundThrower && !foundNonThrower;
191205 if (newResult) {
192 if (!isExact)
206 if (!isExact) {
193207 foundInexact = true;
208 }
194209 // Method always throws an unhandled exception
195210 // Remove the normal control flow edge from the CFG.
196211 Edge fallThrough = cfg.getOutgoingEdgeWithType(basicBlock, FALL_THROUGH_EDGE);
206221
207222 if (!deletedEdgeSet.isEmpty()) {
208223 cfgModified = true;
209 if (foundInexact)
224 if (foundInexact) {
210225 cfg.setFlag(CFG.FOUND_INEXACT_UNCONDITIONAL_THROWERS);
226 }
211227 // Remove all edges marked for deletion
212228 for (Edge edge : deletedEdgeSet) {
213229 cfg.removeEdge(edge);
216232 }
217233 }
218234
219 /**
220 * @param xMethod
221 * @return
222 */
223235 private boolean isUnconditionalThrower(XMethod xMethod) {
224236 return xMethod.isUnconditionalThrower() && !xMethod.isUnsupported() && !xMethod.isSynthetic();
225237 }
247259
248260 /**
249261 * Return whether or not the CFG was modified.
250 *
262 *
251263 * @return true if CFG was modified, false otherwise
252264 */
253265 public boolean wasCFGModified() {
255267 }
256268 }
257269
258 // vim:ts=4
5858
5959 @Override
6060 public void sawOpcode(int seen) {
61 if (seen != PUTFIELD)
61 if (seen != PUTFIELD) {
6262 return;
63 }
6364 XField xFieldOperand = getXFieldOperand();
64 if (xFieldOperand != null && xFieldOperand.equals(targetField) && stack.getStackItem(1).getRegisterNumber() == 0)
65 if (xFieldOperand != null && xFieldOperand.equals(targetField) && stack.getStackItem(1).getRegisterNumber() == 0) {
6566 putfields.put(getPC(), new OpcodeStack.Item(stack.getStackItem(0)));
67 }
6668
6769 }
6870
3131 * class hierarchy, based on the current class path.
3232 */
3333 public class RepositoryClassParser {
34 private ClassParser classParser;
34 private final ClassParser classParser;
3535
3636 /**
3737 * Constructor.
38 *
38 *
3939 * @param inputStream
4040 * the input stream from which to read the class file
4141 * @param fileName
4747
4848 /**
4949 * Constructor.
50 *
50 *
5151 * @param fileName
5252 * name of the class file
5353 */
5757
5858 /**
5959 * Constructor.
60 *
60 *
6161 * @param zipFile
6262 * name of a zip file containing the class
6363 * @param fileName
7070 /**
7171 * Parse the class file into a JavaClass object. If succesful, the new
7272 * JavaClass is entered into the Repository.
73 *
73 *
7474 * @return the parsed JavaClass
7575 * @throws IOException
7676 * if the class cannot be parsed
8282 }
8383 }
8484
85 // vim:ts=4
2929 * failure. However, it is important to report such lookup failures to the user.
3030 * So, classes that use the Repository should have a callback object to report
3131 * lookup failures to.
32 *
32 *
3333 * @author David Hovemeyer
3434 */
3535
3737 public interface RepositoryLookupFailureCallback extends IErrorLogger {
3838 }
3939
40 // vim:ts=4
2525 * A ResourceTracker is used with ResourceValueAnalysis to determine where in a
2626 * method a certain kind of resource is created, and to model the effect of
2727 * instructions on the state of that resource.
28 *
28 *
2929 * @author David Hovemeyer
3030 * @see ResourceValueAnalysis
3131 */
3333 /**
3434 * Determine if the given instruction is the site where a resource is
3535 * created.
36 *
36 *
3737 * @param basicBlock
3838 * basic block containing the instruction
3939 * @param handle
4949 /**
5050 * Determine if the given instruction is the site where a resource is
5151 * closed.
52 *
52 *
5353 * @param basicBlock
5454 * basic block containing the instruction
5555 * @param handle
6969 /**
7070 * Determine if the given instruction is the site where a resource is
7171 * closed.
72 *
72 *
7373 * @param basicBlock
7474 * basic block containing the instruction
7575 * @param handle
7676 * the instruction
7777 * @param cpg
7878 * the ConstantPoolGen for the method
79 * @param resource
80 * the resource, as returned by isResourceCreation()
81 * @param frame
82 * the ResourceValueFrame representing the stack prior to
83 * executing the instruction
8479 * @return true if the resource is closed here, false otherwise
8580 */
8681 public boolean mightCloseResource(BasicBlock basicBlock, InstructionHandle handle, ConstantPoolGen cpg)
8984 /**
9085 * Create a ResourceValueFrameModelingVisitor to model the effect of
9186 * instructions on the state of the resource.
92 *
87 *
9388 * @param resource
9489 * the resource we are tracking
9590 * @param cpg
10499 * types to be tracked with varying precision. For example, we might want to
105100 * ignore implicit exceptions for stream objects, but treat them as
106101 * significant for database resources.
107 *
102 *
108103 * @param resource
109104 * the resource being tracked
110105 * @return true if implicit exceptions are significant, false if they should
116111 * Determine whether the analysis should ignore given exception edge. This
117112 * allows the analysis to customize which kinds of exceptions are
118113 * significant.
119 *
114 *
120115 * @param edge
121116 * the exception edge
122117 * @param resource
131126 /**
132127 * Return if the given parameter slot contains the resource instance upon
133128 * entry to the method. This is for resources passed as parameters.
134 *
129 *
135130 * @param resource
136131 * the resource
137132 * @param slot
141136 public boolean isParamInstance(Resource resource, int slot);
142137 }
143138
144 // vim:ts=4
3535 }
3636
3737 public static ResourceValue merge(ResourceValue a, ResourceValue b) {
38 if (a == notInstance && b == notInstance)
38 if (a == notInstance && b == notInstance) {
3939 return notInstance;
40 else
40 } else {
4141 return instance;
42 }
4243 }
4344
4445 public boolean isInstance() {
5152 }
5253 }
5354
54 // vim:ts=4
3131
3232 @javax.annotation.ParametersAreNonnullByDefault
3333 public class ResourceValueAnalysis<Resource> extends FrameDataflowAnalysis<ResourceValue, ResourceValueFrame> implements
34 EdgeTypes {
34 EdgeTypes {
3535
3636 private static final boolean DEBUG = SystemProperties.getBoolean("dataflow.debug");
3737
38 private MethodGen methodGen;
39
40 private CFG cfg;
41
42 private ResourceTracker<Resource> resourceTracker;
43
44 private Resource resource;
45
46 private ResourceValueFrameModelingVisitor visitor;
47
48 private boolean ignoreImplicitExceptions;
38 private final MethodGen methodGen;
39
40 private final CFG cfg;
41
42 private final ResourceTracker<Resource> resourceTracker;
43
44 private final Resource resource;
45
46 private final ResourceValueFrameModelingVisitor visitor;
47
48 private final boolean ignoreImplicitExceptions;
4949
5050 public ResourceValueAnalysis(MethodGen methodGen, CFG cfg, DepthFirstSearch dfs, ResourceTracker<Resource> resourceTracker,
5151 Resource resource) {
6060 this.ignoreImplicitExceptions = resourceTracker.ignoreImplicitExceptions(resource);
6161 }
6262
63 @Override
6364 public ResourceValueFrame createFact() {
6465 ResourceValueFrame fact = new ResourceValueFrame(methodGen.getMaxLocals());
6566 fact.setTop();
6667 return fact;
6768 }
6869
70 @Override
6971 public void initEntryFact(ResourceValueFrame result) {
7072 result.setValid();
7173 result.clearStack();
7678 }
7779 }
7880
81 @Override
7982 public void meetInto(ResourceValueFrame fact, Edge edge, ResourceValueFrame result) throws DataflowAnalysisException {
8083 BasicBlock source = edge.getSource();
8184 BasicBlock dest = edge.getTarget();
8992 // and the resource tracker says to ignore implicit exceptions
9093 // for this resource, ignore it.
9194 if (AnalysisContext.currentAnalysisContext().getBoolProperty(AnalysisFeatures.ACCURATE_EXCEPTIONS)
92 && ignoreImplicitExceptions && !edge.isFlagSet(EXPLICIT_EXCEPTIONS_FLAG))
95 && ignoreImplicitExceptions && !edge.isFlagSet(EXPLICIT_EXCEPTIONS_FLAG)) {
9396 return;
97 }
9498
9599 // The ResourceTracker may veto the exception edge
96 if (resourceTracker.ignoreExceptionEdge(edge, resource, methodGen.getConstantPool()))
100 if (resourceTracker.ignoreExceptionEdge(edge, resource, methodGen.getConstantPool())) {
97101 return;
102 }
98103
99104 if (fact.getStatus() == ResourceValueFrame.OPEN) {
100105 // If status is OPEN, downgrade to OPEN_ON_EXCEPTION_PATH
109114 // closed anyway.
110115 InstructionHandle exceptionThrower = source.getExceptionThrower();
111116 BasicBlock fallThroughSuccessor = cfg.getSuccessorWithEdgeType(source, FALL_THROUGH_EDGE);
112 if (DEBUG && fallThroughSuccessor == null)
117 if (DEBUG && fallThroughSuccessor == null) {
113118 System.out.println("Null fall through successor!");
119 }
114120 if (fallThroughSuccessor != null
115121 && resourceTracker.isResourceClose(fallThroughSuccessor, exceptionThrower, methodGen.getConstantPool(),
116122 resource, fact)) {
117123 tmpFact = modifyFrame(fact, tmpFact);
118124 tmpFact.setStatus(ResourceValueFrame.CLOSED);
119 if (DEBUG)
125 if (DEBUG) {
120126 System.out.print("(failed attempt to close)");
127 }
121128 }
122129 }
123130
192199 }
193200 }
194201
195 if (tmpFact != null)
202 if (tmpFact != null) {
196203 fact = tmpFact;
204 }
197205
198206 mergeInto(fact, result);
199207 }
225233
226234 }
227235
228 // vim:ts=4
6868
6969 @Override
7070 public boolean sameAs(Frame<ResourceValue> other_) {
71 if (!super.sameAs(other_))
71 if (!super.sameAs(other_)) {
7272 return false;
73 }
7374
7475 ResourceValueFrame other = (ResourceValueFrame) other_;
7576 return this.status == other.status;
8384 }
8485
8586 private static final String[] statusList = { "(escaped)", "(open)", "(open_exception)", "(closed)", "(created)",
86 "(nonexistent)" };
87 "(nonexistent)" };
8788
8889 @Override
8990 public String toString() {
9293
9394 }
9495
95 // vim:ts=4
6161 // If the resource instance is stored in a field, then it escapes
6262 ResourceValueFrame frame = getFrame();
6363 ResourceValue topValue = frame.getTopValue();
64 if (topValue.equals(ResourceValue.instance()))
64 if (topValue.equals(ResourceValue.instance())) {
6565 frame.setStatus(ResourceValueFrame.ESCAPED);
66 }
6667 } catch (DataflowAnalysisException e) {
6768 throw new InvalidBytecodeException("Stack underflow", e);
6869 }
106107 * Override this to check for methods that it is legal to pass the instance
107108 * to without the instance escaping. By default, we consider all methods to
108109 * be possible escape routes.
109 *
110 *
110111 * @param inv
111112 * the InvokeInstruction to which the resource instance is passed
112113 * as an argument
132133 }
133134 }
134135
135 if (instanceArgNum >= 0 && instanceEscapes(inv, instanceArgNum))
136 if (instanceArgNum >= 0 && instanceEscapes(inv, instanceArgNum)) {
136137 frame.setStatus(ResourceValueFrame.ESCAPED);
138 }
137139
138140 handleNormalInstruction(inv);
139141 }
146148
147149 topValue = frame.getTopValue();
148150
149 if (topValue.equals(ResourceValue.instance()))
151 if (topValue.equals(ResourceValue.instance())) {
150152 frame.setStatus(ResourceValueFrame.ESCAPED);
153 }
151154 } catch (DataflowAnalysisException e) {
152155 AnalysisContext.logError("Analysis error", e);
153156 }
178181 try {
179182 ResourceValueFrame frame = getFrame();
180183 ResourceValue topValue = frame.getTopValue();
181 if (topValue.equals(ResourceValue.instance()))
184 if (topValue.equals(ResourceValue.instance())) {
182185 frame.setStatus(ResourceValueFrame.ESCAPED);
186 }
183187 } catch (DataflowAnalysisException e) {
184188 throw new InvalidBytecodeException("Stack underflow", e);
185189 }
189193
190194 }
191195
192 // vim:ts=4
6767 }
6868
6969 private static final int[][] mergeMatrix = {
70 // TOP EXIT UE EXIT_UE RETURNS
71 { TOP, }, // TOP
72 { EXIT, EXIT, }, // EXIT
73 { UE, EXIT_UE, UE, }, // UE
74 { EXIT_UE, EXIT_UE, EXIT_UE, EXIT_UE, }, // EXIT_UE
75 { RETURNS, RETURNS, RETURNS, RETURNS, RETURNS }, // RETURNS
70 // TOP EXIT UE EXIT_UE RETURNS
71 { TOP, }, // TOP
72 { EXIT, EXIT, }, // EXIT
73 { UE, EXIT_UE, UE, }, // UE
74 { EXIT_UE, EXIT_UE, EXIT_UE, EXIT_UE, }, // EXIT_UE
75 { RETURNS, RETURNS, RETURNS, RETURNS, RETURNS }, // RETURNS
7676 };
7777
7878 public void mergeWith(ReturnPath other) {
100100 }
101101 }
102102
103 // vim:ts=4
2525 super(dfs);
2626 }
2727
28 @Override
2829 public ReturnPath createFact() {
2930 return new ReturnPath(ReturnPath.TOP);
3031 }
3132
33 @Override
3234 public void copy(ReturnPath source, ReturnPath dest) {
3335 dest.copyFrom(source);
3436 }
3537
38 @Override
3639 public void initEntryFact(ReturnPath fact) {
3740 fact.setKind(ReturnPath.RETURNS);
3841 }
3942
43 @Override
4044 public void makeFactTop(ReturnPath fact) {
4145 fact.setKind(ReturnPath.TOP);
4246 }
4347
48 @Override
4449 public boolean isTop(ReturnPath fact) {
4550 return fact.getKind() == ReturnPath.TOP;
4651 }
4752
53 @Override
4854 public boolean same(ReturnPath fact1, ReturnPath fact2) {
4955 return fact1.sameAs(fact2);
5056 }
6066 return true;
6167 }
6268
69 @Override
6370 public void meetInto(ReturnPath fact, Edge edge, ReturnPath result) throws DataflowAnalysisException {
6471 switch (edge.getType()) {
6572 case UNHANDLED_EXCEPTION_EDGE:
96103 // }
97104 }
98105
99 // vim:ts=4
2424 * A BlockOrder for visiting the blocks of a CFG in the order they would be
2525 * visited in a depth first search of the reversed CFG. This is the most
2626 * efficient visitation order for backwards dataflow analyses.
27 *
27 *
2828 * @see BlockOrder
2929 * @see ReverseDepthFirstSearch
3030 * @see CFG
3636 private final DepthFirstSearch dfs;
3737
3838 public ReverseDFSComparator(ReverseDepthFirstSearch rdfs, DepthFirstSearch dfs) {
39 if (dfs == null)
39 if (dfs == null) {
4040 throw new IllegalArgumentException();
41 }
4142 this.dfs = dfs;
4243 }
4344
45 @Override
4446 public int compare(BasicBlock a, BasicBlock b) {
4547 return dfs.getFinishTime(a) - dfs.getFinishTime(b);
4648 }
5254
5355 /**
5456 * Constructor.
55 *
57 *
5658 * @param cfg
5759 * the CFG
5860 * @param rdfs
6769 }
6870 }
6971
70 // vim:ts=4
2121 /**
2222 * Algorithm to perform a reverse depth first search on a CFG. (I.e., depth
2323 * first search on reversed CFG.)
24 *
24 *
2525 * @see CFG
2626 * @author David Hovemeyer
2727 */
3030
3131 /**
3232 * Constructor.
33 *
33 *
3434 * @param cfg
3535 * the CFG to perform the reverse depth first search on
3636 */
4747 }
4848 }
4949
50 // vim:ts=4
2424 * A BlockOrder for visiting the blocks of a CFG in the reverse of the order in
2525 * which they are finished in a depth first search. This is the most efficient
2626 * visitation order for forward dataflow analyses.
27 *
27 *
2828 * @see BlockOrder
2929 * @see DepthFirstSearch
3030 * @see CFG
3636 * they would be finished by a depth first search.
3737 */
3838 private static class ReversePostfixComparator implements Comparator<BasicBlock> {
39 private DepthFirstSearch dfs;
39 private final DepthFirstSearch dfs;
4040
4141 public ReversePostfixComparator(DepthFirstSearch dfs) {
4242 this.dfs = dfs;
4343 }
4444
45 @Override
4546 public int compare(BasicBlock aa, BasicBlock bb) {
4647 return dfs.getFinishTime(bb) - dfs.getFinishTime(aa);
4748 }
4950
5051 /**
5152 * Constructor.
52 *
53 *
5354 * @param cfg
5455 * the CFG for the method
5556 * @param dfs
6061 }
6162 }
6263
63 // vim:ts=4
3434 * For a method signature, parseNext() must be called multiple times, and the
3535 * parens around the arguments must be skipped manually (by calling the skip()
3636 * method).
37 *
37 *
3838 * @author David Hovemeyer
3939 */
4040
4343
4444 /**
4545 * Constructor.
46 *
46 *
4747 * @param signature
4848 * the field or method signature to convert
4949 */
7171 * of the remaining part is "I", then this method will return "int", and the
7272 * "I" will be consumed. Arrays, reference types, and basic types are all
7373 * handled.
74 *
74 *
7575 * @return the parsed type string
7676 */
7777 public String parseNext() {
9090 }
9191 } else if (signature.startsWith("L")) {
9292 int semi = signature.indexOf(';');
93 if (semi < 0)
93 if (semi < 0) {
9494 throw new IllegalStateException("missing semicolon in signature " + signature);
95 }
9596 result.append(signature.substring(1, semi).replace('/', '.'));
9697 signature = signature.substring(semi + 1);
9798 } else {
135136 /**
136137 * Convenience method for generating a method signature in human readable
137138 * form.
138 *
139 *
139140 * @param javaClass
140141 * the class
141142 * @param method
148149 /**
149150 * Convenience method for generating a method signature in human readable
150151 * form.
151 *
152 *
152153 * @param methodGen
153154 * the method to produce a method signature for
154155 */
159160 /**
160161 * Convenience method for generating a method signature in human readable
161162 * form.
162 *
163 *
163164 * @param inv
164165 * an InvokeInstruction
165166 * @param cpg
172173 /**
173174 * Convenience method for generating a method signature in human readable
174175 * form.
175 *
176 *
176177 * @param className
177178 * name of the class containing the method
178179 * @param methodName
187188 /**
188189 * Convenience method for generating a method signature in human readable
189190 * form.
190 *
191 *
191192 * @param xmethod
192193 * an XMethod
193194 * @return the formatted version of that signature
202203 /**
203204 * Convenience method for generating a method signature in human readable
204205 * form.
205 *
206 *
206207 * @param methodDescriptor
207208 * a MethodDescriptor
208209 * @return the formatted version of that signature
215216 /**
216217 * Convenience method for generating a method signature in human readable
217218 * form.
218 *
219 *
219220 * @param className
220221 * name of the class containing the method
221222 * @param methodName
234235 args.append('(');
235236
236237 while (converter.getFirst() != ')') {
237 if (args.length() > 1)
238 if (args.length() > 1) {
238239 args.append(", ");
240 }
239241 args.append(shorten(pkgName, converter.parseNext()));
240242 }
241243 converter.skip();
255257 /**
256258 * Convenience method for converting a single signature component to
257259 * human-readable form.
258 *
260 *
259261 * @param signature
260262 * the signature
261263 */
267269 int index = typeName.lastIndexOf('.');
268270 if (index >= 0) {
269271 String otherPkg = typeName.substring(0, index);
270 if (otherPkg.equals(pkgName) || otherPkg.equals("java.lang"))
272 if (otherPkg.equals(pkgName) || "java.lang".equals(otherPkg)) {
271273 typeName = typeName.substring(index + 1);
274 }
272275 }
273276 return typeName;
274277 }
275278 }
276279
277 // vim:ts=4
3737 private int totalArgumentSize;
3838
3939 public int getTotalArgumentSize() {
40 if ( parameterOffset == null)
40 if ( parameterOffset == null) {
4141 getParameterOffset();
42 }
4243 return totalArgumentSize;
4344 }
4445
45 private @CheckForNull int parameterOffset[];
46
47 @Nonnull int[] getParameterOffset() {
48 if ( parameterOffset != null )
46 private @CheckForNull int parameterOffset[];
47
48 @Nonnull int[] getParameterOffset() {
49 if ( parameterOffset != null ) {
4950 return parameterOffset;
51 }
5052 ArrayList<Integer> offsets = new ArrayList<Integer>();
5153 Iterator<String> i = parameterSignatureIterator();
5254 int totalSize = 0;
5456 while (i.hasNext()) {
5557 String s = i.next();
5658
57 if (s.equals("D") || s.equals("J"))
59 if ("D".equals(s) || "J".equals(s)) {
5860 totalSize += 2;
59 else
61 } else {
6062 totalSize += 1;
63 }
6164 offsets.add(totalSize);
6265
6366 }
6467 totalArgumentSize = totalSize;
6568 int numParameters = offsets.size();
6669 parameterOffset = new int[numParameters];
67 for (int j = 0; j < numParameters; j++)
70 for (int j = 0; j < numParameters; j++) {
6871 parameterOffset[j] = offsets.get(j);
72 }
6973 return parameterOffset;
7074 }
7175
7882 private class ParameterSignatureIterator implements Iterator<String> {
7983 private int index = 1;
8084
85 @Override
8186 public boolean hasNext() {
8287 return index < signature.length() && signature.charAt(index) != ')';
8388 }
8489
90 @Override
8591 public String next() {
86 if (!hasNext())
92 if (!hasNext()) {
8793 throw new NoSuchElementException();
94 }
8895 StringBuilder result = new StringBuilder();
8996 boolean done;
9097 do {
105112
106113 case 'L':
107114 int semi = signature.indexOf(';', index + 1);
108 if (semi < 0)
115 if (semi < 0) {
109116 throw new IllegalStateException("Invalid method signature: " + signature);
117 }
110118 result.append(signature.substring(index, semi + 1));
111119 index = semi + 1;
112120 break;
126134 return result.toString();
127135 }
128136
137 @Override
129138 public void remove() {
130139 throw new UnsupportedOperationException();
131140 }
145154 * the method signature to be parsed
146155 */
147156 public SignatureParser(String signature) {
148 if (!signature.startsWith("("))
157 if (!signature.startsWith("(")) {
149158 throw new IllegalArgumentException("Bad method signature: " + signature);
159 }
150160 this.signature = signature;
151161
152162
168178 return new ParameterSignatureIterator();
169179 }
170180
171 public Iterable<String> parameterSignatures() {
172 return new Iterable<String>() {
173
174 public Iterator<String> iterator() {
175 return new ParameterSignatureIterator();
176 }};
177
181 public Iterable<String> parameterSignatures() {
182 return new Iterable<String>() {
183
184 @Override
185 public Iterator<String> iterator() {
186 return new ParameterSignatureIterator();
187 }};
188
178189 }
179190
180191 /**
184195 */
185196 public String getReturnTypeSignature() {
186197 int endOfParams = signature.lastIndexOf(')');
187 if (endOfParams < 0)
198 if (endOfParams < 0) {
188199 throw new IllegalArgumentException("Bad method signature: " + signature);
200 }
189201 return signature.substring(endOfParams + 1);
190202 }
191203
201213 public boolean hasReferenceParameters() {
202214 for (Iterator<String> i = parameterSignatureIterator(); i.hasNext();) {
203215 char c = i.next().charAt(0);
204 if (c == 'L' || c == '[')
216 if (c == 'L' || c == '[') {
205217 return true;
218 }
206219 }
207220 return false;
208221 }
211224 int count = 0;
212225 for (Iterator<String> i = parameterSignatureIterator(); i.hasNext();) {
213226 String p = i.next();
214 if (pos == count)
227 if (pos == count) {
215228 return p;
229 }
216230 ++count;
217231 }
218232 throw new IndexOutOfBoundsException("Asked for parameter " + pos + " of " + signature);
251265 * @return number of stack frame slots a value of the given type will occupy
252266 */
253267 public static int getNumSlotsForType(String sig) {
254 if (sig.equals("J") || sig.equals("D")) {
268 if ("J".equals(sig) || "D".equals(sig)) {
255269 return 2;
256270 } else {
257271 return 1;
271285 }
272286 }
273287
274 // vim:ts=4
3131 * <p>
3232 * FIXME: instead of storing the simple paths, should invoke a callback as each
3333 * simple path is produced. That would save memory.
34 *
34 *
3535 * @author David Hovemeyer
3636 * @see CFG
3737 */
3838 public class SimplePathEnumerator implements EdgeTypes, DFSEdgeTypes {
39 private CFG cfg;
39 private final CFG cfg;
4040
41 private DepthFirstSearch dfs;
41 private final DepthFirstSearch dfs;
4242
43 private int maxPaths;
43 private final int maxPaths;
4444
45 private int maxWork;
45 private final int maxWork;
4646
4747 private int work;
4848
49 private List<List<Edge>> pathList;
49 private final List<List<Edge>> pathList;
5050
5151 private static final boolean DEBUG = SystemProperties.getBoolean("spe.debug");
5252
5757
5858 /**
5959 * Constructor.
60 *
60 *
6161 * @param cfg
6262 * the control flow graph to enumerate simple paths of
6363 * @param maxPaths
7878
7979 /**
8080 * Constructor; max work is set to DEFAULT_MAX_WORK.
81 *
81 *
8282 * @param cfg
8383 * the control flow graph to enumerate simple paths of
8484 * @param maxPaths
9090
9191 /**
9292 * Enumerate the simple paths.
93 *
93 *
9494 * @return this object
9595 */
9696 public SimplePathEnumerator enumerate() {
9797 Iterator<Edge> entryOut = cfg.outgoingEdgeIterator(cfg.getEntry());
98 if (!entryOut.hasNext())
98 if (!entryOut.hasNext()) {
9999 throw new IllegalStateException();
100 }
100101 Edge entryEdge = entryOut.next();
101102
102103 LinkedList<Edge> init = new LinkedList<Edge>();
103104 init.add(entryEdge);
104105
105106 work(init);
106 if (DEBUG && work == maxWork)
107 if (DEBUG && work == maxWork) {
107108 System.out.println("**** Reached max work! ****");
109 }
108110
109111 return this;
110112 }
117119 }
118120
119121 private void work(LinkedList<Edge> partialPath) {
120 if (pathList.size() == maxPaths)
122 if (pathList.size() == maxPaths) {
121123 return;
124 }
122125
123126 Edge last = partialPath.getLast();
124127
135138 Edge outEdge = i.next();
136139
137140 // Ignore back edges and unhandled exception edges
138 if (dfs.getDFSEdgeType(outEdge) == BACK_EDGE || outEdge.getType() == UNHANDLED_EXCEPTION_EDGE)
141 if (dfs.getDFSEdgeType(outEdge) == BACK_EDGE || outEdge.getType() == UNHANDLED_EXCEPTION_EDGE) {
139142 continue;
143 }
140144
141145 // Add the edge to the current partial path, and recur
142146 partialPath.add(outEdge);
144148 partialPath.removeLast();
145149
146150 // Have we done the maximum amount of work?
147 if (work == maxWork)
151 if (work == maxWork) {
148152 return;
153 }
149154 ++work;
150155
151156 // Did we reach the maximum number of simple paths?
152 if (pathList.size() == maxPaths)
157 if (pathList.size() == maxPaths) {
153158 return;
159 }
154160 }
155161 }
156162 }
157163
158 // vim:ts=4
4040 * file.
4141 */
4242 private static class LineNumberMapBuilder {
43 private SourceFile sourceFile;
43 private final SourceFile sourceFile;
4444
4545 private int offset;
4646
9090
9191 private static final int DEFAULT_SIZE = 100;
9292
93 private SourceFileDataSource dataSource;
93 private final SourceFileDataSource dataSource;
9494
9595 private byte[] data;
9696
178178 System.err.println("SourceFile.getLineOffset: " + e.getMessage());
179179 return -1;
180180 }
181 if (line < 0 || line >= numLines)
181 if (line < 0 || line >= numLines) {
182182 return -1;
183 }
183184 return lineNumberMap[line];
184185 }
185186
186187 private synchronized void loadFileData() throws IOException {
187 if (data != null)
188 if (data != null) {
188189 return;
190 }
189191
190192 InputStream in = null;
191193
208210
209211 setData(out.toByteArray());
210212 } finally {
211 if (in != null)
213 if (in != null) {
212214 in.close();
215 }
213216 }
214217
215218 }
229232 }
230233 }
231234
232 // vim:ts=4
3838 public long getLastModified();
3939 }
4040
41 // vim:ts=4
9696 * A directory containing source files.
9797 */
9898 private static class DirectorySourceRepository implements SourceRepository {
99 private String baseDir;
99 private final String baseDir;
100100
101101 public DirectorySourceRepository(String baseDir) {
102102 this.baseDir = baseDir;
107107 return "DirectorySourceRepository:" + baseDir;
108108 }
109109
110 @Override
110111 public boolean contains(String fileName) {
111112 File file = new File(getFullFileName(fileName));
112113 boolean exists = file.exists();
113 if (DEBUG)
114 if (DEBUG) {
114115 System.out.println("Exists " + exists + " for " + file);
116 }
115117 return exists;
116118 }
117119
120 @Override
118121 public boolean isPlatformDependent() {
119122 return true;
120123 }
121124
125 @Override
122126 public SourceFileDataSource getDataSource(String fileName) {
123127 return new FileSourceFileDataSource(getFullFileName(fileName));
124128 }
138142 while (true) {
139143
140144 ZipEntry e = in.getNextEntry();
141 if (e == null)
145 if (e == null) {
142146 break;
147 }
143148 if (!e.isDirectory()) {
144149 String name = e.getName();
145150 long size = e.getSize();
146151
147 if (size > Integer.MAX_VALUE)
152 if (size > Integer.MAX_VALUE) {
148153 throw new IOException(name + " is too big at " + size + " bytes");
154 }
149155 ByteArrayOutputStream out;
150 if (size <= 0)
156 if (size <= 0) {
151157 out = new ByteArrayOutputStream();
152 else
158 } else {
153159 out = new ByteArrayOutputStream((int) size);
160 }
154161 GZIPOutputStream gOut = new GZIPOutputStream(out);
155162 IO.copy(in, gOut);
156163 gOut.close();
166173
167174 }
168175
169 /*
170 * (non-Javadoc)
171 *
172 * @see
173 * edu.umd.cs.findbugs.ba.SourceFinder.SourceRepository#contains(java
174 * .lang.String)
175 */
176 @Override
176177 public boolean contains(String fileName) {
177178 return contents.containsKey(fileName);
178179 }
179180
180 /*
181 * (non-Javadoc)
182 *
183 * @see
184 * edu.umd.cs.findbugs.ba.SourceFinder.SourceRepository#getDataSource
185 * (java.lang.String)
186 */
181 @Override
187182 public SourceFileDataSource getDataSource(final String fileName) {
188183 return new SourceFileDataSource() {
189184
185 @Override
190186 public String getFullFileName() {
191187 return fileName;
192188 }
193189
190 @Override
194191 public InputStream open() throws IOException {
195192 return new GZIPInputStream(new ByteArrayInputStream(contents.get(fileName)));
196193 }
197194
195 @Override
198196 public long getLastModified() {
199 Long when = lastModified.get(fileName);
200 if (when == null || when < 0)
201 return 0;
202 return when;
197 Long when = lastModified.get(fileName);
198 if (when == null || when < 0) {
199 return 0;
200 }
201 return when;
203202 }
204203 };
205204 }
206205
207 /*
208 * (non-Javadoc)
209 *
210 * @see
211 * edu.umd.cs.findbugs.ba.SourceFinder.SourceRepository#isPlatformDependent
212 * ()
213 */
206 @Override
214207 public boolean isPlatformDependent() {
215208 return false;
216209 }
220213 final BlockingSourceRepository r = new BlockingSourceRepository();
221214 Util.runInDameonThread(new Runnable() {
222215
216 @Override
223217 public void run() {
224218 InputStream in = null;
225219 try {
226220
227221 URLConnection connection = new URL(url).openConnection();
228222 in = connection.getInputStream();
229 if (getProject().isGuiAvaliable())
223 if (getProject().isGuiAvaliable()) {
230224 in = getProject().getGuiCallback().getProgressMonitorInputStream(in, connection.getContentLength(),
231225 "Downloading project source code...");
232
233 if (url.endsWith(".z0p.gz"))
226 }
227
228 if (url.endsWith(".z0p.gz")) {
234229 in = new GZIPInputStream(in);
230 }
235231
236232 r.setBase(new InMemorySourceRepository(new ZipInputStream(in)));
237233
253249 final BlockingSourceRepository r = new BlockingSourceRepository();
254250 Util.runInDameonThread(new Runnable() {
255251
252 @Override
256253 public void run() {
257254 InputStream in = null;
258255 OutputStream out = null;
304301 }
305302 }
306303
304 @Override
307305 public boolean contains(String fileName) {
308306 await();
309307 return base.contains(fileName);
310308 }
311309
310 @Override
312311 public SourceFileDataSource getDataSource(String fileName) {
313312 await();
314313 return base.getDataSource(fileName);
315314 }
316315
316 @Override
317317 public boolean isPlatformDependent() {
318318 await();
319319 return base.isPlatformDependent();
331331 this.zipFile = zipFile;
332332 }
333333
334 @Override
334335 public boolean contains(String fileName) {
335336 return zipFile.getEntry(fileName) != null;
336337 }
337338
339 @Override
338340 public boolean isPlatformDependent() {
339341 return false;
340342 }
341343
344 @Override
342345 public SourceFileDataSource getDataSource(String fileName) {
343346 return new ZipSourceFileDataSource(zipFile, fileName);
344347 }
384387 if (repos.startsWith("http:") || repos.startsWith("https:") || repos.startsWith("file:")) {
385388 String url = SystemProperties.rewriteURLAccordingToProperties(repos);
386389 repositoryList.add(makeInMemorySourceRepository(url));
387 } else
390 } else {
388391 repositoryList.add(new ZipSourceRepository(new ZipFile(repos)));
392 }
389393 } catch (IOException e) {
390394 // Ignored - we won't use this archive
391395 AnalysisContext.logError("Unable to load " + repos, e);
392396 }
393397 } else {
394398 File dir = new File(repos);
395 if (dir.canRead() && dir.isDirectory())
399 if (dir.canRead() && dir.isDirectory()) {
396400 repositoryList.add(new DirectorySourceRepository(repos));
397 else {
401 } else {
398402 AnalysisContext.logError("Unable to load " + repos);
399403
400404 }
460464 // Is the file in the cache already? Always cache it with the canonical
461465 // name
462466 SourceFile sourceFile = cache.get(canonicalName);
463 if (sourceFile != null)
467 if (sourceFile != null) {
464468 return sourceFile;
469 }
465470
466471 // Find this source file, add its data to the cache
467 if (DEBUG)
472 if (DEBUG) {
468473 System.out.println("Trying " + fileName + " in package " + packageName + "...");
474 }
469475 // Query each element of the source path to find the requested source
470476 // file
471477 for (SourceRepository repos : repositoryList) {
472 if (repos instanceof BlockingSourceRepository && !((BlockingSourceRepository) repos).isReady())
478 if (repos instanceof BlockingSourceRepository && !((BlockingSourceRepository) repos).isReady()) {
473479 continue;
480 }
474481 fileName = repos.isPlatformDependent() ? platformName : canonicalName;
475 if (DEBUG)
482 if (DEBUG) {
476483 System.out.println("Looking in " + repos + " for " + fileName);
484 }
477485 if (repos.contains(fileName)) {
478486 // Found it
479487 sourceFile = new SourceFile(repos.getDataSource(fileName));
480488 cache.put(canonicalName, sourceFile); // always cache with
481 // canonicalName
489 // canonicalName
482490 return sourceFile;
483491 }
484492 }
486494 throw new FileNotFoundException("Can't find source file " + fileName);
487495 }
488496
489 /**
490 * @param packageName
491 * @param fileName
492 * @return
493 */
494497 public static String getPlatformName(String packageName, String fileName) {
495498 String platformName = packageName.replace('.', File.separatorChar) + (packageName.length() > 0 ? File.separator : "")
496499 + fileName;
497500 return platformName;
498501 }
499
502
500503 public static String getPlatformName(SourceLineAnnotation source) {
501504 return getPlatformName(source.getPackageName(), getOrGuessSourceFile(source));
502505 }
503
506
504507 public static String getCanonicalName(SourceLineAnnotation source) {
505508 return getCanonicalName(source.getPackageName(), getOrGuessSourceFile(source));
506509 }
507 /**
508 * @param packageName
509 * @param fileName
510 * @return
511 */
510
512511 public static String getCanonicalName(String packageName, String fileName) {
513512 String canonicalName = packageName.replace('.', '/') + (packageName.length() > 0 ? "/" : "") + fileName;
514513 return canonicalName;
515514 }
516515
517516 public static String getOrGuessSourceFile(SourceLineAnnotation source) {
518 if (source.isSourceFileKnown())
517 if (source.isSourceFileKnown()) {
519518 return source.getSourceFile();
519 }
520520 String baseClassName = source.getClassName();
521521 int i = baseClassName.lastIndexOf('.');
522522 baseClassName = baseClassName.substring(i + 1);
523 int j = baseClassName.indexOf("$");
524 if (j >= 0)
523 int j = baseClassName.indexOf('$');
524 if (j >= 0) {
525525 baseClassName = baseClassName.substring(0, j);
526 }
526527 return baseClassName + ".java";
527528 }
529
528530 public boolean hasSourceFile(SourceLineAnnotation source) {
529531 return hasSourceFile(source.getPackageName(), getOrGuessSourceFile(source));
530532 }
551553 // Is the file in the cache already? Always cache it with the canonical
552554 // name
553555 SourceFile sourceFile = cache.get(canonicalName);
554 if (sourceFile != null)
556 if (sourceFile != null) {
555557 return true;
558 }
556559
557560 // Find this source file, add its data to the cache
558 if (DEBUG)
561 if (DEBUG) {
559562 System.out.println("Trying " + fileName + " in package " + packageName + "...");
563 }
560564 // Query each element of the source path to find the requested source
561565 // file
562566 for (SourceRepository repos : repositoryList) {
563 if (repos instanceof BlockingSourceRepository && !((BlockingSourceRepository) repos).isReady())
567 if (repos instanceof BlockingSourceRepository && !((BlockingSourceRepository) repos).isReady()) {
564568 continue;
569 }
565570 fileName = repos.isPlatformDependent() ? platformName : canonicalName;
566 if (DEBUG)
571 if (DEBUG) {
567572 System.out.println("Looking in " + repos + " for " + fileName);
573 }
568574 if (repos.contains(fileName)) {
569575 return true;
570576 }
573579 return false;
574580 }
575581
576
577
578 /**
579 * @param project
580 */
581582 private void setProject(Project project) {
582583 this.project = project;
583584 repositoryList = new LinkedList<SourceRepository>();
586587 }
587588 }
588589
589 // vim:ts=4
4040 * (items we don't get line number information for directly in classfiles), and
4141 * also source line information for methods that don't appear directly in
4242 * classfiles, such as abstract and native methods.
43 *
43 *
4444 * @author David Hovemeyer
4545 */
4646 public class SourceInfoMap {
6161
6262 /*
6363 * (non-Javadoc)
64 *
64 *
6565 * @see java.lang.Comparable#compareTo(T)
6666 */
67 @Override
6768 public int compareTo(FieldDescriptor o) {
6869 int cmp = className.compareTo(o.className);
69 if (cmp != 0)
70 if (cmp != 0) {
7071 return cmp;
72 }
7173 return fieldName.compareTo(o.fieldName);
7274 }
7375
7476 /*
7577 * (non-Javadoc)
76 *
78 *
7779 * @see java.lang.Object#hashCode()
7880 */
7981 @Override
8385
8486 /*
8587 * (non-Javadoc)
86 *
88 *
8789 * @see java.lang.Object#equals(java.lang.Object)
8890 */
8991 @Override
9092 public boolean equals(Object obj) {
91 if (obj == null || obj.getClass() != this.getClass())
93 if (obj == null || obj.getClass() != this.getClass()) {
9294 return false;
95 }
9396 FieldDescriptor other = (FieldDescriptor) obj;
9497 return className.equals(other.className) && fieldName.equals(other.fieldName);
9598 }
9699 }
97100
98101 static class MethodDescriptor implements Comparable<MethodDescriptor> {
99 private String className;
100
101 private String methodName;
102
103 private String methodSignature;
102 private final String className;
103
104 private final String methodName;
105
106 private final String methodSignature;
104107
105108 public MethodDescriptor(String className, String methodName, String methodSignature) {
106109 this.className = className;
115118
116119 /*
117120 * (non-Javadoc)
118 *
121 *
119122 * @see java.lang.Comparable#compareTo(T)
120123 */
124 @Override
121125 public int compareTo(MethodDescriptor o) {
122126 int cmp;
123 if ((cmp = className.compareTo(o.className)) != 0)
127 if ((cmp = className.compareTo(o.className)) != 0) {
124128 return cmp;
125 if ((cmp = methodName.compareTo(o.methodName)) != 0)
129 }
130 if ((cmp = methodName.compareTo(o.methodName)) != 0) {
126131 return cmp;
132 }
127133 return methodSignature.compareTo(o.methodSignature);
128134 }
129135
130136 /*
131137 * (non-Javadoc)
132 *
138 *
133139 * @see java.lang.Object#hashCode()
134140 */
135141 @Override
139145
140146 /*
141147 * (non-Javadoc)
142 *
148 *
143149 * @see java.lang.Object#equals(java.lang.Object)
144150 */
145151 @Override
146152 public boolean equals(Object obj) {
147 if (obj == null || obj.getClass() != this.getClass())
153 if (obj == null || obj.getClass() != this.getClass()) {
148154 return false;
155 }
149156 MethodDescriptor other = (MethodDescriptor) obj;
150157 return className.equals(other.className) && methodName.equals(other.methodName)
151158 && methodSignature.equals(other.methodSignature);
167174
168175 /**
169176 * Constructor for a range of lines.
170 *
177 *
171178 * @param start
172179 * start line in range
173180 * @param end
196203
197204 /*
198205 * (non-Javadoc)
199 *
206 *
200207 * @see java.lang.Object#toString()
201208 */
202209 @Override
207214
208215 private static final boolean DEBUG = SystemProperties.getBoolean("sourceinfo.debug");
209216
210 private Map<FieldDescriptor, SourceLineRange> fieldLineMap;
211
212 private Map<MethodDescriptor, SourceLineRange> methodLineMap;
213
214 private Map<String, SourceLineRange> classLineMap;
217 private final Map<FieldDescriptor, SourceLineRange> fieldLineMap;
218
219 private final Map<MethodDescriptor, SourceLineRange> methodLineMap;
220
221 private final Map<String, SourceLineRange> classLineMap;
215222
216223 public boolean fallBackToClassfile() {
217224 return isEmpty();
232239
233240 /**
234241 * Add a line number entry for a field.
235 *
242 *
236243 * @param className
237244 * name of class containing the field
238245 * @param fieldName
246253
247254 /**
248255 * Add a line number entry for a method.
249 *
256 *
250257 * @param className
251258 * name of class containing the method
252259 * @param methodName
262269
263270 /**
264271 * Add line number entry for a class.
265 *
272 *
266273 * @param className
267274 * name of class
268275 * @param range
274281
275282 /**
276283 * Look up the line number range for a field.
277 *
284 *
278285 * @param className
279286 * name of class containing the field
280287 * @param fieldName
289296
290297 /**
291298 * Look up the line number range for a method.
292 *
299 *
293300 * @param className
294301 * name of class containing the method
295302 * @param methodName
306313
307314 /**
308315 * Look up the line number range for a class.
309 *
316 *
310317 * @param className
311318 * name of the class
312319 * @return the line number range, or null if no line number is known for the
322329 /**
323330 * Read source info from given InputStream. The stream is guaranteed to be
324331 * closed.
325 *
332 *
326333 * @param inputStream
327334 * the InputStream
328335 * @throws IOException
341348 ++lineNumber;
342349
343350 if (lineNumber == 1) {
344 if (DEBUG)
351 if (DEBUG) {
345352 System.out.println("First line: " + line);
353 }
346354 // Try to parse the version number string from the first
347355 // line.
348356 // null means that the line does not appear to be a version
351359 if (version != null) {
352360 // Check to see if version is supported.
353361 // Only 1.0 supported for now.
354 if (!version.equals("1.0"))
362 if (!"1.0".equals(version)) {
355363 throw new IOException("Unsupported sourceInfo version " + version);
364 }
356365
357366 // Version looks good. Skip to next line of file.
358367 continue;
367376 // Line number for class
368377 SourceLineRange range = createRange(next, tokenizer.nextToken());
369378 classLineMap.put(className, range);
370 if (DEBUG)
379 if (DEBUG) {
371380 System.out.println("class:" + className + "," + range);
381 }
372382 } else if ((lparen = next.indexOf('(')) >= 0) {
373383 // Line number for method
374384 String methodName = next.substring(0, lparen);
375385 String methodSignature = next.substring(lparen);
376386
377 if (methodName.equals("init^"))
387 if ("init^".equals(methodName)) {
378388 methodName = "<init>";
379 else if (methodName.equals("clinit^"))
389 } else if ("clinit^".equals(methodName)) {
380390 methodName = "<clinit>";
391 }
381392
382393 SourceLineRange range = createRange(tokenizer.nextToken(), tokenizer.nextToken());
383394 methodLineMap.put(new MethodDescriptor(className, methodName, methodSignature), range);
384 if (DEBUG)
395 if (DEBUG) {
385396 System.out.println("method:" + methodName + methodSignature + "," + range);
397 }
386398 } else {
387399 // Line number for field
388400 String fieldName = next;
389401 SourceLineRange range = createRange(tokenizer.nextToken(), tokenizer.nextToken());
390402 fieldLineMap.put(new FieldDescriptor(className, fieldName), range);
391 if (DEBUG)
403 if (DEBUG) {
392404 System.out.println("field:" + className + "," + fieldName + "," + range);
405 }
393406 }
394407
395408 // Note: we could complain if there are more tokens,
410423
411424 /**
412425 * Parse the sourceInfo version string.
413 *
426 *
414427 * @param line
415428 * the first line of the sourceInfo file
416429 * @return the version number constant, or null if the line does not appear
419432 private static String parseVersionNumber(String line) {
420433 StringTokenizer tokenizer = new StringTokenizer(line, " \t");
421434
422 if (!expect(tokenizer, "sourceInfo") || !expect(tokenizer, "version") || !tokenizer.hasMoreTokens())
435 if (!expect(tokenizer, "sourceInfo") || !expect(tokenizer, "version") || !tokenizer.hasMoreTokens()) {
423436 return null;
437 }
424438
425439 return tokenizer.nextToken();
426440 }
428442 /**
429443 * Expect a particular token string to be returned by the given
430444 * StringTokenizer.
431 *
445 *
432446 * @param tokenizer
433447 * the StringTokenizer
434448 * @param token
436450 * @return true if the expected token was returned, false if not
437451 */
438452 private static boolean expect(StringTokenizer tokenizer, String token) {
439 if (!tokenizer.hasMoreTokens())
453 if (!tokenizer.hasMoreTokens()) {
440454 return false;
455 }
441456 String s = tokenizer.nextToken();
442 if (DEBUG)
457 if (DEBUG) {
443458 System.out.println("token=" + s);
459 }
444460 return s.equals(token);
445461 }
446462
2020
2121 /**
2222 * Dataflow fact to represent the depth of the Java operand stack.
23 *
23 *
2424 * @see StackDepthAnalysis
2525 */
2626 public class StackDepth {
2828
2929 /**
3030 * Constructor.
31 *
31 *
3232 * @param depth
3333 * the stack depth
3434 */
5252
5353 @Override
5454 public String toString() {
55 if (getDepth() == StackDepthAnalysis.TOP)
55 if (getDepth() == StackDepthAnalysis.TOP) {
5656 return "[TOP]";
57 else if (getDepth() == StackDepthAnalysis.BOTTOM)
57 } else if (getDepth() == StackDepthAnalysis.BOTTOM) {
5858 return "[BOTTOM]";
59 else
59 } else {
6060 return "[" + String.valueOf(depth) + "]";
61 }
6162 }
6263 }
6364
64 // vim:ts=4
2727 * A really simple forward dataflow analysis to find the depth of the Java
2828 * operand stack. This is more of a proof of concept for the dataflow analysis
2929 * framework than anything useful.
30 *
30 *
3131 * @see Dataflow
3232 * @see DataflowAnalysis
3333 */
3636
3737 public static final int BOTTOM = -2;
3838
39 private ConstantPoolGen cpg;
39 private final ConstantPoolGen cpg;
4040
4141 /**
4242 * Constructor.
43 *
43 *
4444 * @param cpg
4545 * the ConstantPoolGen of the method whose CFG we're performing
4646 * the analysis on
5252 this.cpg = cpg;
5353 }
5454
55 @Override
5556 public StackDepth createFact() {
5657 return new StackDepth(TOP);
5758 }
5859
60 @Override
5961 public void makeFactTop(StackDepth fact) {
6062 fact.setDepth(TOP);
6163 }
6264
65 @Override
6366 public boolean isTop(StackDepth fact) {
6467 return fact.getDepth() == TOP;
6568 }
7073 return depth != TOP && depth != BOTTOM;
7174 }
7275
76 @Override
7377 public void copy(StackDepth source, StackDepth dest) {
7478 dest.setDepth(source.getDepth());
7579 }
7680
81 @Override
7782 public void initEntryFact(StackDepth entryFact) {
7883 entryFact.setDepth(0); // stack depth == 0 at entry to CFG
7984 }
8085
86 @Override
8187 public boolean same(StackDepth fact1, StackDepth fact2) {
8288 return fact1.getDepth() == fact2.getDepth();
8389 }
8894 Instruction ins = handle.getInstruction();
8995 int produced = ins.produceStack(cpg);
9096 int consumed = ins.consumeStack(cpg);
91 if (produced == Constants.UNPREDICTABLE || consumed == Constants.UNPREDICTABLE)
97 if (produced == Constants.UNPREDICTABLE || consumed == Constants.UNPREDICTABLE) {
9298 throw new IllegalStateException("Unpredictable stack delta for instruction: " + handle);
99 }
93100 int depth = fact.getDepth();
94101 depth += (produced - consumed);
95 if (depth < 0)
102 if (depth < 0) {
96103 fact.setDepth(BOTTOM);
97 else
104 } else {
98105 fact.setDepth(depth);
106 }
99107 }
100108
109 @Override
101110 public void meetInto(StackDepth fact, Edge edge, StackDepth result) {
102111 int a = fact.getDepth();
103112 int b = result.getDepth();
104113 int combined;
105114
106 if (a == TOP)
115 if (a == TOP) {
107116 combined = b;
108 else if (b == TOP)
117 } else if (b == TOP) {
109118 combined = a;
110 else if (a == BOTTOM || b == BOTTOM || a != b)
119 } else if (a == BOTTOM || b == BOTTOM || a != b) {
111120 combined = BOTTOM;
112 else
121 } else {
113122 combined = a;
123 }
114124
115125 result.setDepth(combined);
116126 }
149159 // }
150160 }
151161
152 // vim:ts=4
2424 * The target of a branch instruction.
2525 */
2626 public class Target {
27 private InstructionHandle targetInstruction;
27 private final InstructionHandle targetInstruction;
2828
29 private @Edge.Type int edgeType;
29 private @Edge.Type
30 final int edgeType;
3031
3132 /**
3233 * Constructor.
33 *
34 *
3435 * @param targetInstruction
3536 * the handle of the target instruction
3637 * @param edgeType
3333 /**
3434 * Visitor to find all of the targets of an instruction whose InstructionHandle
3535 * is given. Note that we don't consider exception edges.
36 *
36 *
3737 * @author David Hovemeyer
3838 * @author Chadd Williams
3939 */
4040 public class TargetEnumeratingVisitor extends org.apache.bcel.generic.EmptyVisitor implements EdgeTypes {
4141
42 private InstructionHandle handle;
42 private final InstructionHandle handle;
4343
44 private ConstantPoolGen constPoolGen;
44 private final ConstantPoolGen constPoolGen;
4545
46 private LinkedList<Target> targetList;
46 private final LinkedList<Target> targetList;
4747
4848 private boolean isBranch, isReturn, isThrow, isExit;
4949
5050 /**
5151 * Constructor.
52 *
52 *
5353 * @param handle
5454 * the handle of the instruction whose targets should be
5555 * enumerated
105105 public void visitGotoInstruction(GotoInstruction ins) {
106106 isBranch = true;
107107 InstructionHandle target = ins.getTarget();
108 if (target == null)
108 if (target == null) {
109109 throw new IllegalStateException();
110 }
110111 targetList.add(new Target(target, GOTO_EDGE));
111112 }
112113
114115 public void visitIfInstruction(IfInstruction ins) {
115116 isBranch = true;
116117 InstructionHandle target = ins.getTarget();
117 if (target == null)
118 if (target == null) {
118119 throw new IllegalStateException();
120 }
119121 targetList.add(new Target(target, IFCMP_EDGE));
120122 InstructionHandle fallThrough = handle.getNext();
121123 targetList.add(new Target(fallThrough, FALL_THROUGH_EDGE));
158160 String methodName = ins.getName(constPoolGen);
159161 String methodSig = ins.getSignature(constPoolGen);
160162
161 if (className.equals("java.lang.System") && methodName.equals("exit") && methodSig.equals("(I)V"))
163 if ("java.lang.System".equals(className) && "exit".equals(methodName) && "(I)V".equals(methodSig)) {
162164 isExit = true;
165 }
163166 }
164167
165168 }
3232 private static final ClassDescriptor JUNIT3TESTCASE = DescriptorFactory.createClassDescriptor("junit/framework/TestCase");
3333
3434 public static boolean likelyTestCase(XMethod m) {
35 if (m.getAnnotation(JUNIT4TEST) != null)
35 if (m.getAnnotation(JUNIT4TEST) != null) {
3636 return true;
37 }
3738
3839 ClassDescriptor c = m.getClassDescriptor();
3940 if (m.getName().startsWith("test") || m.getName().startsWith("assert")) {
4041 Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
4142
4243 try {
43 if (subtypes2.isSubtype(c, JUNIT3TESTCASE))
44 if (subtypes2.isSubtype(c, JUNIT3TESTCASE)) {
4445 return true;
46 }
4547 } catch (ClassNotFoundException e) {
4648 AnalysisContext.reportMissingClass(e);
4749 }
4141 /**
4242 * A work-alike class to use instead of BCEL's ClassPath class. The main
4343 * difference is that URLClassPath can load classfiles from URLs.
44 *
44 *
4545 * @author David Hovemeyer
4646 */
4747 public class URLClassPath implements Serializable {
5454 /**
5555 * Open an input stream to read a resource in the codebase described by
5656 * this classpath entry.
57 *
57 *
5858 * @param resourceName
5959 * name of resource to load: e.g., "java/lang/Object.class"
6060 * @return an InputStream, or null if the resource wasn't found
9393
9494 /*
9595 * (non-Javadoc)
96 *
96 *
9797 * @see
9898 * edu.umd.cs.findbugs.URLClassPath.Entry#openStream(java.lang.String)
9999 */
100 @Override
100101 public InputStream openStream(String resourceName) throws IOException {
101102 ZipEntry zipEntry = zipFile.getEntry(resourceName);
102 if (zipEntry == null)
103 if (zipEntry == null) {
103104 return null;
105 }
104106 return zipFile.getInputStream(zipEntry);
105107 }
106108
107109 /*
108110 * (non-Javadoc)
109 *
111 *
110112 * @see edu.umd.cs.findbugs.URLClassPath.Entry#getURL()
111113 */
114 @Override
112115 public String getURL() {
113116 return zipFile.getName();
114117 }
115118
119 @Override
116120 public void close() {
117121 try {
118122 zipFile.close();
127131 * filesystem.
128132 */
129133 private static class LocalDirectoryEntry implements Entry {
130 private String dirName;
134 private final String dirName;
131135
132136 /**
133137 * Constructor.
134 *
138 *
135139 * @param dirName
136140 * name of the local directory
137141 * @throws IOException
139143 */
140144 public LocalDirectoryEntry(String dirName) throws IOException {
141145 this.dirName = dirName;
142 if (!(new File(dirName).isDirectory()))
146 if (!(new File(dirName).isDirectory())) {
143147 throw new IOException(dirName + " is not a directory");
144 }
145
146 /*
147 * (non-Javadoc)
148 *
148 }
149 }
150
151 /*
152 * (non-Javadoc)
153 *
149154 * @see
150155 * edu.umd.cs.findbugs.URLClassPath.Entry#openStream(java.lang.String)
151156 */
157 @Override
152158 public InputStream openStream(String resourceName) throws IOException {
153159 File file = new File(dirName, resourceName);
154 if (!file.exists())
160 if (!file.exists()) {
155161 return null;
162 }
156163 return new BufferedInputStream(new FileInputStream(file));
157164 }
158165
159166 /*
160167 * (non-Javadoc)
161 *
168 *
162169 * @see edu.umd.cs.findbugs.URLClassPath.Entry#getURL()
163170 */
171 @Override
164172 public String getURL() {
165173 return dirName;
166174 }
167175
176 @Override
168177 public void close() {
169178 // Nothing to do here
170179 }
176185 * jar URLs to specify individual files within the remote archive.
177186 */
178187 private static class RemoteArchiveEntry implements Entry {
179 private URL remoteArchiveURL;
188 private final URL remoteArchiveURL;
180189
181190 /**
182191 * Constructor.
183 *
192 *
184193 * @param remoteArchiveURL
185194 * the remote zip/jar file URL
186195 */
190199
191200 /*
192201 * (non-Javadoc)
193 *
202 *
194203 * @see
195204 * edu.umd.cs.findbugs.URLClassPath.Entry#openStream(java.lang.String)
196205 */
206 @Override
197207 public InputStream openStream(String resourceName) throws IOException {
198208 URL remoteFileURL = new URL("jar:" + remoteArchiveURL.toString() + "/" + resourceName);
199209 try {
205215
206216 /*
207217 * (non-Javadoc)
208 *
218 *
209219 * @see edu.umd.cs.findbugs.URLClassPath.Entry#getURL()
210220 */
221 @Override
211222 public String getURL() {
212223 return remoteArchiveURL.toString();
213224 }
214225
226 @Override
215227 public void close() {
216228 // Nothing to do
217229 }
222234 * Classpath entry class to load files from a remote directory URL.
223235 */
224236 private static class RemoteDirectoryEntry implements Entry {
225 private URL remoteDirURL;
237 private final URL remoteDirURL;
226238
227239 /**
228240 * Constructor.
229 *
241 *
230242 * @param remoteDirURL
231243 * URL of the remote directory; must end in "/"
232244 */
236248
237249 /*
238250 * (non-Javadoc)
239 *
251 *
240252 * @see
241253 * edu.umd.cs.findbugs.URLClassPath.Entry#openStream(java.lang.String)
242254 */
255 @Override
243256 public InputStream openStream(String resourceName) throws IOException {
244257 URL remoteFileURL = new URL(remoteDirURL.toString() + resourceName);
245258 try {
251264
252265 /*
253266 * (non-Javadoc)
254 *
267 *
255268 * @see edu.umd.cs.findbugs.URLClassPath.Entry#getURL()
256269 */
270 @Override
257271 public String getURL() {
258272 return remoteDirURL.toString();
259273 }
260274
275 @Override
261276 public void close() {
262277 // Nothing to do
263278 }
264279 }
265280
266281 // Fields
267 private List<Entry> entryList;
282 private final List<Entry> entryList;
268283
269284 /**
270285 * Constructor. Creates a classpath with no elements.
277292 * Add given filename/URL to the classpath. If no URL protocol is given, the
278293 * filename is assumed to be a local file or directory. Remote directories
279294 * must be specified with a "/" character at the end of the URL.
280 *
295 *
281296 * @param fileName
282297 * filename or URL of codebase (directory or archive file)
283298 * @throws IOException
294309 boolean isArchive = fileExtension != null && URLClassPath.isArchiveExtension(fileExtension);
295310
296311 Entry entry;
297 if (protocol.equals("file")) {
312 if ("file".equals(protocol)) {
298313 String localFileName = fileName.substring("file:".length());
299314
300 if (fileName.endsWith("/") || new File(localFileName).isDirectory())
315 if (fileName.endsWith("/") || new File(localFileName).isDirectory()) {
301316 entry = new LocalDirectoryEntry(localFileName);
302 else if (isArchive)
317 } else if (isArchive) {
303318 entry = new LocalArchiveEntry(localFileName);
304 else
319 } else {
305320 throw new IOException("Classpath entry " + fileName + " is not a directory or archive file");
321 }
306322 } else {
307 if (fileName.endsWith("/"))
323 if (fileName.endsWith("/")) {
308324 entry = new RemoteDirectoryEntry(new URL(fileName));
309 else if (isArchive)
325 } else if (isArchive) {
310326 entry = new RemoteArchiveEntry(new URL(fileName));
311 else
327 } else {
312328 throw new IOException("Classpath entry " + fileName + " is not a remote directory or archive file");
329 }
313330 }
314331
315332 entryList.add(entry);
317334
318335 /**
319336 * Return the classpath string.
320 *
337 *
321338 * @return the classpath string
322339 */
323340 public String getClassPath() {
324341 StringBuilder buf = new StringBuilder();
325342 for (Entry entry : entryList) {
326 if (buf.length() > 0)
343 if (buf.length() > 0) {
327344 buf.append(File.pathSeparator);
345 }
328346 buf.append(entry.getURL());
329347 }
330348 return buf.toString();
332350
333351 /**
334352 * Open a stream to read given resource.
335 *
353 *
336354 * @param resourceName
337355 * name of resource to load, e.g. "java/lang/Object.class"
338356 * @return input stream to read resource, or null if resource could not be
378396 return null;
379397 }
380398
381 private Set<String> classesThatCantBeFound = new HashSet<String>();
399 private final Set<String> classesThatCantBeFound = new HashSet<String>();
382400
383401 /**
384402 * Look up a class from the classpath.
385 *
403 *
386404 * @param className
387405 * name of class to look up
388406 * @return the JavaClass object for the class
436454
437455 /**
438456 * Get the URL protocol of given URL string.
439 *
457 *
440458 * @param urlString
441459 * the URL string
442460 * @return the protocol name ("http", "file", etc.), or null if there is no
447465 int firstColon = urlString.indexOf(':');
448466 if (firstColon >= 0) {
449467 String specifiedProtocol = urlString.substring(0, firstColon);
450 if (FindBugs.knownURLProtocolSet.contains(specifiedProtocol))
468 if (FindBugs.knownURLProtocolSet.contains(specifiedProtocol)) {
451469 protocol = specifiedProtocol;
470 }
452471 }
453472 return protocol;
454473 }
455474
456475 /**
457476 * Get the file extension of given fileName.
458 *
477 *
459478 * @return the file extension, or null if there is no file extension
460479 */
461480 public static String getFileExtension(String fileName) {
465484
466485 /**
467486 * Determine if given file extension indicates an archive file.
468 *
487 *
469488 * @param fileExtension
470489 * the file extension (e.g., ".jar")
471490 * @return true if the file extension indicates an archive, false otherwise
475494 }
476495 }
477496
478 // vim:ts=4
5050
5151 private static final long serialVersionUID = 1L;
5252
53 private Map<String, JavaClass> nameToClassMap;
54
55 private URLClassPath urlClassPath;
53 private final Map<String, JavaClass> nameToClassMap;
54
55 private final URLClassPath urlClassPath;
5656
5757 public URLClassPathRepository() {
5858 this.nameToClassMap = new HashMap<String, JavaClass>();
8888 * org.apache.bcel.util.Repository#storeClass(org.apache.bcel.classfile.
8989 * JavaClass)
9090 */
91 @Override
9192 public void storeClass(JavaClass javaClass) {
92 if (DEBUG)
93 if (DEBUG) {
9394 System.out.println("Storing class " + javaClass.getClassName() + " in repository");
95 }
9496 JavaClass previous = nameToClassMap.put(javaClass.getClassName(), javaClass);
9597 if (DEBUG && previous != null) {
9698 System.out.println("\t==> A previous class was evicted!");
9799 dumpStack();
98100 }
99101 Repository tmp = org.apache.bcel.Repository.getRepository();
100 if (tmp != null && tmp != this)
102 if (tmp != null && tmp != this) {
101103 throw new IllegalStateException("Wrong/multiple BCEL repository");
102 if (tmp == null)
104 }
105 if (tmp == null) {
103106 org.apache.bcel.Repository.setRepository(this);
107 }
104108 }
105109
106110 /*
110114 * org.apache.bcel.util.Repository#removeClass(org.apache.bcel.classfile
111115 * .JavaClass)
112116 */
117 @Override
113118 public void removeClass(JavaClass javaClass) {
114119 nameToClassMap.remove(javaClass.getClassName());
115120 if (DEBUG) {
127132 *
128133 * @see org.apache.bcel.util.Repository#findClass(java.lang.String)
129134 */
135 @Override
130136 public JavaClass findClass(/*@Nonnull*/ String className) {
131137 // Make sure we handle class names with slashes.
132138 // If we don't, we can get into serious trouble: a previously
143149 *
144150 * @see org.apache.bcel.util.Repository#loadClass(java.lang.String)
145151 */
152 @Override
146153 public JavaClass loadClass(/*@Nonnull*/ String className) throws ClassNotFoundException {
147 if (className == null)
154 if (className == null) {
148155 throw new IllegalArgumentException("className is null");
156 }
149157 // if (className.indexOf('/') >= 0) throw new IllegalStateException();
150158 JavaClass javaClass = findClass(className);
151159 if (javaClass == null) {
164172 *
165173 * @see org.apache.bcel.util.Repository#loadClass(java.lang.Class)
166174 */
167 public JavaClass loadClass(Class clazz) throws ClassNotFoundException {
175 @Override
176 public JavaClass loadClass(Class<?> clazz) throws ClassNotFoundException {
168177 return loadClass(clazz.getName());
169178 }
170179
173182 *
174183 * @see org.apache.bcel.util.Repository#clear()
175184 */
185 @Override
176186 public void clear() {
177187 if (DEBUG) {
178188 System.out.println("Clearing Repository!");
186196 *
187197 * @see org.apache.bcel.util.Repository#getClassPath()
188198 */
199 @Override
189200 public ClassPath getClassPath() {
190201 return new ClassPath(urlClassPath.getClassPath());
191202 }
192203 }
193204
194 // vim:ts=4
4040
4141 protected UnresolvedXField(@DottedClassName String className, String methodName, String methodSig, int accessFlags) {
4242 super(className, methodName, methodSig, accessFlags);
43 if (methodSig.charAt(0) == '(')
43 if (methodSig.charAt(0) == '(') {
4444 throw new IllegalArgumentException("Bad signature: " + methodSig);
45 }
4546 if (XFactory.DEBUG_UNRESOLVED) {
4647 System.out.println("Unresolved xmethod: " + this);
4748 }
4950
5051 protected UnresolvedXField(FieldDescriptor m) {
5152 super(m.getClassDescriptor().getDottedClassName(), m.getName(), m.getSignature(), m.isStatic() ? Constants.ACC_STATIC : 0);
52 if (m.getSignature().charAt(0) == '(')
53 if (m.getSignature().charAt(0) == '(') {
5354 throw new IllegalArgumentException("Bad signature: " + m.getSignature());
55 }
5456 if (XFactory.DEBUG_UNRESOLVED) {
5557 System.out.println("Unresolved xmethod: " + this);
5658 }
5860
5961 /*
6062 * (non-Javadoc)
61 *
63 *
6264 * @see java.lang.Comparable#compareTo(java.lang.Object)
6365 */
66 @Override
6467 public int compareTo(ComparableField o) {
6568 if (o instanceof XField) {
6669 return XFactory.compare((XField) this, (XField) o);
7174
7275 /*
7376 * (non-Javadoc)
74 *
77 *
7578 * @see
7679 * edu.umd.cs.findbugs.classfile.analysis.AnnotatedObject#getAnnotation(
7780 * edu.umd.cs.findbugs.classfile.ClassDescriptor)
7881 */
82 @Override
7983 public AnnotationValue getAnnotation(ClassDescriptor desc) {
8084 return null;
8185 }
8286
8387 /*
8488 * (non-Javadoc)
85 *
89 *
8690 * @see edu.umd.cs.findbugs.classfile.analysis.AnnotatedObject#
8791 * getAnnotationDescriptors()
8892 */
93 @Override
8994 public Collection<ClassDescriptor> getAnnotationDescriptors() {
9095 return Collections.<ClassDescriptor> emptyList();
9196 }
9297
9398 /*
9499 * (non-Javadoc)
95 *
100 *
96101 * @see
97102 * edu.umd.cs.findbugs.classfile.analysis.AnnotatedObject#getAnnotations()
98103 */
104 @Override
99105 public Collection<AnnotationValue> getAnnotations() {
100106 return Collections.<AnnotationValue> emptyList();
101107 }
102108
103109 /*
104110 * (non-Javadoc)
105 *
111 *
106112 * @see
107113 * edu.umd.cs.findbugs.classfile.analysis.AnnotatedObject#getContainingScope
108114 * ()
109115 */
116 @Override
110117 public AnnotatedObject getContainingScope() {
111118 // TODO Auto-generated method stub
112119 return AnalysisContext.currentXFactory().getXClass(
115122
116123 /*
117124 * (non-Javadoc)
118 *
125 *
119126 * @see
120127 * edu.umd.cs.findbugs.classfile.analysis.AnnotatedObject#getElementType()
121128 */
129 @Override
122130 public ElementType getElementType() {
123131 return ElementType.FIELD;
124132 }
125133
126134 /*
127135 * (non-Javadoc)
128 *
136 *
129137 * @see edu.umd.cs.findbugs.ba.AccessibleEntity#isDeprecated()
130138 */
139 @Override
131140 public boolean isDeprecated() {
132141 // TODO Auto-generated method stub
133142 return false;
134143 }
135144
145 @Override
136146 public @CheckForNull
137147 String getSourceSignature() {
138148 return null;
3838 *
3939 * @see edu.umd.cs.findbugs.ba.XMethod#isReturnTypeReferenceType()
4040 */
41 @Override
4142 public boolean isReturnTypeReferenceType() {
4243 SignatureParser parser = new SignatureParser(getSignature());
4344 String returnTypeSig = parser.getReturnTypeSignature();
4950 *
5051 * @see java.lang.Comparable#compareTo(java.lang.Object)
5152 */
53 @Override
5254 public int compareTo(ComparableMethod o) {
5355 if (o instanceof XMethod) {
5456 return XFactory.compare((XMethod) this, (XMethod) o);
5658 throw new ClassCastException("Don't know how to compare " + this.getClass().getName() + " to " + o.getClass().getName());
5759 }
5860
61 @Override
5962 public ElementType getElementType() {
60 if (getName().equals("<init>"))
63 if ("<init>".equals(getName())) {
6164 return ElementType.CONSTRUCTOR;
65 }
6266 return ElementType.METHOD;
6367 }
6468
69 @Override
6570 public @CheckForNull
6671 AnnotatedObject getContainingScope() {
6772 try {
7681 *
7782 * @see edu.umd.cs.findbugs.ba.XMethod#getThrownExceptions()
7883 */
84 @Override
7985 public String[] getThrownExceptions() {
8086
8187 return new String[0];
8692 *
8793 * @see edu.umd.cs.findbugs.ba.XMethod#isUnconditionalThrower()
8894 */
95 @Override
8996 public boolean isUnconditionalThrower() {
9097 return false;
9198 }
9299
100 @Override
93101 public boolean isUnsupported() {
94102 return false;
95103 }
99107 *
100108 * @see edu.umd.cs.findbugs.ba.XMethod#isAbstract()
101109 */
110 @Override
102111 public boolean isAbstract() {
103112 return false;
104113 }
108117 *
109118 * @see edu.umd.cs.findbugs.ba.AccessibleEntity#isSynthetic()
110119 */
120 @Override
111121 public boolean isSynthetic() {
112122 return false;
113123 }
117127 *
118128 * @see edu.umd.cs.findbugs.ba.AccessibleEntity#isDeprecated()
119129 */
130 @Override
120131 public boolean isDeprecated() {
121132 return false;
122133 }
126137 *
127138 * @see edu.umd.cs.findbugs.ba.XMethod#isVarArgs()
128139 */
140 @Override
129141 public boolean isVarArgs() {
130142 return false;
131143 }
135147 *
136148 * @see edu.umd.cs.findbugs.ba.XMethod#usesConcurrency()
137149 */
150 @Override
138151 public boolean usesConcurrency() {
139152 return false;
140153 }
141154
155 @Override
142156 public @CheckForNull
143157 String getSourceSignature() {
144158 return null;
149163 *
150164 * @see edu.umd.cs.findbugs.ba.XMethod#isStub()
151165 */
166 @Override
152167 public boolean isStub() {
153168 return false;
154169 }
155170
171 @Override
156172 public boolean isIdentity() {
157173 return false;
158174 }
165181 * @see edu.umd.cs.findbugs.ba.XMethod#addParameterAnnotation(int,
166182 * edu.umd.cs.findbugs.classfile.analysis.AnnotationValue)
167183 */
184 @Override
168185 public void addParameterAnnotation(int param, AnnotationValue annotationValue) {
169186 HashMap<Integer, Map<ClassDescriptor, AnnotationValue>> updatedAnnotations = new HashMap<Integer, Map<ClassDescriptor, AnnotationValue>>(
170187 methodParameterAnnotations);
179196
180197 }
181198
199 @Override
182200 public Collection<ClassDescriptor> getParameterAnnotationDescriptors(int param) {
183201 Map<ClassDescriptor, AnnotationValue> map = methodParameterAnnotations.get(param);
184 if (map == null)
202 if (map == null) {
185203 return Collections.<ClassDescriptor> emptySet();
204 }
186205 return map.keySet();
187206 }
188207
208 @Override
189209 public boolean hasParameterAnnotations() {
190210 return !methodParameterAnnotations.isEmpty();
191211 }
192
212
213 @Override
193214 public @Nullable
194215 AnnotationValue getParameterAnnotation(int param, ClassDescriptor desc) {
195216 Map<ClassDescriptor, AnnotationValue> map = methodParameterAnnotations.get(param);
196 if (map == null)
217 if (map == null) {
197218 return null;
219 }
198220 return map.get(desc);
199221 }
200222
223 @Override
201224 public Collection<AnnotationValue> getParameterAnnotations(int param) {
202225 Map<ClassDescriptor, AnnotationValue> map = methodParameterAnnotations.get(param);
203 if (map == null)
226 if (map == null) {
204227 return Collections.<AnnotationValue> emptySet();
228 }
205229 return map.values();
206230 }
207231
214238 * edu.umd.cs.findbugs.ba.XMethod#addAnnotation(edu.umd.cs.findbugs.classfile
215239 * .analysis.AnnotationValue)
216240 */
241 @Override
217242 public void addAnnotation(AnnotationValue annotationValue) {
218243 HashMap<ClassDescriptor, AnnotationValue> updatedAnnotations = new HashMap<ClassDescriptor, AnnotationValue>(
219244 methodAnnotations);
221246 methodAnnotations = updatedAnnotations;
222247 }
223248
249 @Override
224250 public Collection<ClassDescriptor> getAnnotationDescriptors() {
225251 return methodAnnotations.keySet();
226252 }
227253
254 @Override
228255 public AnnotationValue getAnnotation(ClassDescriptor desc) {
229256 return methodAnnotations.get(desc);
230257 }
231258
259 @Override
232260 public Collection<AnnotationValue> getAnnotations() {
233261 return methodAnnotations.values();
234262 }
238266 *
239267 * @see edu.umd.cs.findbugs.ba.XMethod#bridgeFrom()
240268 */
269 @Override
241270 public XMethod bridgeFrom() {
242271 return null;
243272 }
247276 *
248277 * @see edu.umd.cs.findbugs.ba.XMethod#bridgeTo()
249278 */
279 @Override
250280 public XMethod bridgeTo() {
251281 return null;
252282 }
254284 /* (non-Javadoc)
255285 * @see edu.umd.cs.findbugs.ba.XMethod#getAccessMethodFor()
256286 */
287 @Override
257288 public MethodDescriptor getAccessMethodForMethod() {
258289 return null;
259290 }
291 @Override
260292 public FieldDescriptor getAccessMethodForField() {
261293 return null;
262294 }
263295 /* (non-Javadoc)
264296 * @see edu.umd.cs.findbugs.ba.XMethod#isVariableSynthetic(int)
265297 */
298 @Override
266299 public boolean isVariableSynthetic(int param) {
267300 return false;
268301 }
269302
303 @Override
304 public boolean usesInvokeDynamic() {
305 return false;
306 }
307
270308
271309 }
7878 public @CheckForNull
7979 String getSource();
8080
81 @Override
8182 public Collection<ClassDescriptor> getAnnotationDescriptors();
8283
84 @Override
8385 public AnnotationValue getAnnotation(ClassDescriptor desc);
8486
8587 /**
6464
6565 /**
6666 * Factory methods for creating XMethod objects.
67 *
67 *
6868 * @author David Hovemeyer
6969 */
7070 public class XFactory {
7171 public static final boolean DEBUG_UNRESOLVED = SystemProperties.getBoolean("findbugs.xfactory.debugunresolved");
7272
73 private Set<ClassDescriptor> reflectiveClasses = new HashSet<ClassDescriptor>();
74
75 private Map<MethodDescriptor, XMethod> methods = new HashMap<MethodDescriptor, XMethod>();
76
77 private Map<FieldDescriptor, XField> fields = new HashMap<FieldDescriptor, XField>();
78
79 private Set<XMethod> calledMethods = new HashSet<XMethod>();
80
81 private Set<XField> emptyArrays = new HashSet<XField>();
82
83 private Set<String> calledMethodSignatures = new HashSet<String>();
84
85 private Set<MethodDescriptor> functionsThatMightBeMistakenForProcedures = new HashSet<MethodDescriptor>();
73 private final Set<ClassDescriptor> reflectiveClasses = new HashSet<ClassDescriptor>();
74
75 private final Map<MethodDescriptor, XMethod> methods = new HashMap<MethodDescriptor, XMethod>();
76
77 private final Map<FieldDescriptor, XField> fields = new HashMap<FieldDescriptor, XField>();
78
79 private final Set<XMethod> calledMethods = new HashSet<XMethod>();
80
81 private final Set<XField> emptyArrays = new HashSet<XField>();
82
83 private final Set<String> calledMethodSignatures = new HashSet<String>();
84
85 private final Set<MethodDescriptor> functionsThatMightBeMistakenForProcedures = new HashSet<MethodDescriptor>();
8686
8787 public void canonicalizeAll() {
8888 DescriptorFactory descriptorFactory = DescriptorFactory.instance();
89 for (XMethod m : methods.values())
89 for (XMethod m : methods.values()) {
9090 if (m instanceof MethodDescriptor) {
9191 descriptorFactory.canonicalize((MethodDescriptor) m);
9292 }
93 for (XField f : fields.values())
94 if (f instanceof FieldDescriptor)
93 }
94 for (XField f : fields.values()) {
95 if (f instanceof FieldDescriptor) {
9596 descriptorFactory.canonicalize((FieldDescriptor) f);
97 }
98 }
9699 }
97100
98101 /**
130133 }
131134
132135 public boolean isCalled(XMethod m) {
133 if (m.getName().equals("<clinit>"))
136 if ("<clinit>".equals(m.getName())) {
134137 return true;
138 }
135139 return calledMethods.contains(m);
136140 }
137141
138142 public Set<XMethod> getCalledMethods() {
139143 return calledMethods;
140144 }
141
145
142146 public void addFunctionThatMightBeMistakenForProcedures(MethodDescriptor m) {
143147 functionsThatMightBeMistakenForProcedures.add(m);
144148 }
159163 }
160164
161165 public boolean isCalledDirectlyOrIndirectly(XMethod m) {
162 if (isCalled(m))
166 if (isCalled(m)) {
163167 return true;
164 if (m.isStatic() || m.isPrivate() || m.getName().equals("<init>"))
168 }
169 if (m.isStatic() || m.isPrivate() || "<init>".equals(m.getName())) {
165170 return false;
171 }
166172 try {
167173 IAnalysisCache analysisCache = Global.getAnalysisCache();
168174 XClass clazz = analysisCache.getClassAnalysis(XClass.class, m.getClassDescriptor());
169 if (isCalledDirectlyOrIndirectly(clazz.getSuperclassDescriptor(), m))
175 if (isCalledDirectlyOrIndirectly(clazz.getSuperclassDescriptor(), m)) {
170176 return true;
171 for (ClassDescriptor i : clazz.getInterfaceDescriptorList())
172 if (isCalledDirectlyOrIndirectly(i, m))
177 }
178 for (ClassDescriptor i : clazz.getInterfaceDescriptorList()) {
179 if (isCalledDirectlyOrIndirectly(i, m)) {
173180 return true;
181 }
182 }
174183
175184 return false;
176185 } catch (edu.umd.cs.findbugs.classfile.MissingClassException e) {
185194 }
186195 }
187196
188 /**
189 * @param superclassDescriptor
190 * @param m
191 * @return
192 * @throws CheckedAnalysisException
193 */
194197 private boolean isCalledDirectlyOrIndirectly(@CheckForNull ClassDescriptor clazzDescriptor, XMethod m)
195198 throws CheckedAnalysisException {
196 if (clazzDescriptor == null)
199 if (clazzDescriptor == null) {
197200 return false;
201 }
198202 IAnalysisCache analysisCache = Global.getAnalysisCache();
199203 XClass clazz = analysisCache.getClassAnalysis(XClass.class, clazzDescriptor);
200204 XMethod m2 = clazz.findMethod(m.getName(), m.getSignature(), m.isStatic());
201 if (m2 != null && isCalled(m2))
205 if (m2 != null && isCalled(m2)) {
202206 return true;
203 if (isCalledDirectlyOrIndirectly(clazz.getSuperclassDescriptor(), m))
207 }
208 if (isCalledDirectlyOrIndirectly(clazz.getSuperclassDescriptor(), m)) {
204209 return true;
205 for (ClassDescriptor i : clazz.getInterfaceDescriptorList())
206 if (isCalledDirectlyOrIndirectly(i, m))
210 }
211 for (ClassDescriptor i : clazz.getInterfaceDescriptorList()) {
212 if (isCalledDirectlyOrIndirectly(i, m)) {
207213 return true;
214 }
215 }
208216
209217 return false;
210218
214222 return calledMethodSignatures.contains(getDetailedSignature(m));
215223 }
216224
217 /**
218 * @param m2
219 * @return
220 */
221225 private static String getDetailedSignature(XMethod m2) {
222226 return m2.getName() + m2.getSignature() + m2.isStatic();
223227 }
233237
234238 /**
235239 * Create an XMethod object from a BCEL Method.
236 *
240 *
237241 * @param className
238242 * the class to which the Method belongs
239243 * @param method
258262
259263 /**
260264 * Create an XMethod object from a BCEL Method.
261 *
265 *
262266 * @param javaClass
263267 * the class to which the Method belongs
264268 * @param method
267271 */
268272
269273 public static XMethod createXMethod(JavaClass javaClass, Method method) {
270 if (method == null)
274 if (method == null) {
271275 throw new NullPointerException("method must not be null");
276 }
272277 XMethod xmethod = createXMethod(javaClass.getClassName(), method);
273278 assert xmethod.isResolved();
274279 return xmethod;
316321 XFactory xFactory = AnalysisContext.currentXFactory();
317322
318323 XMethod m = xFactory.methods.get(desc);
319 if (m != null)
324 if (m != null) {
320325 return m;
326 }
321327 m = xFactory.resolveXMethod(desc);
322328 if (m instanceof MethodDescriptor) {
323329 xFactory.methods.put((MethodDescriptor) m, m);
324330 DescriptorFactory.instance().canonicalize((MethodDescriptor) m);
325 } else
331 } else {
326332 xFactory.methods.put(desc, m);
333 }
327334 return m;
328335 }
329336
331338 XFactory xFactory = AnalysisContext.currentXFactory();
332339 int count = 0;
333340 for (XMethod m : xFactory.methods.values()) {
334 if (m instanceof MethodInfo)
341 if (m instanceof MethodInfo) {
335342 count++;
343 }
336344 }
337345 System.out.printf("XFactory cached methods: %d/%d%n", count, xFactory.methods.size());
338346 DescriptorFactory.instance().profile();
344352 try {
345353 while (true) {
346354 XMethod m = methods.get(desc);
347 if (m != null)
355 if (m != null) {
348356 return m;
357 }
349358 XClass xClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, desc.getClassDescriptor());
350 if (xClass == null)
359 if (xClass == null) {
351360 break;
361 }
352362 ClassDescriptor superClass = xClass.getSuperclassDescriptor();
353 if (superClass == null)
363 if (superClass == null) {
354364 break;
365 }
355366 desc = DescriptorFactory.instance().getMethodDescriptor(superClass.getClassName(), desc.getName(),
356367 desc.getSignature(), desc.isStatic());
357368 }
361372 assert true;
362373 }
363374 UnresolvedXMethod xmethod = new UnresolvedXMethod(originalDescriptor);
364
375
365376
366377 ObligationPolicyDatabase database = Global.getAnalysisCache().getOptionalDatabase(ObligationPolicyDatabase.class);
367378
368 if (BuildObligationPolicyDatabase.INFER_CLOSE_METHODS && database != null
379 if (BuildObligationPolicyDatabase.INFER_CLOSE_METHODS && database != null
369380 && !xmethod.getClassName().startsWith("java")) {
370381 boolean methodHasCloseInName = false;
371382 String methodName = xmethod.getName();
375386
376387 for (int i = 0; i < xmethod.getNumParams(); i++) {
377388 Obligation obligationType = paramObligationTypes[i];
378 if (obligationType == null)
389 if (obligationType == null) {
379390 continue;
391 }
380392 if (methodHasCloseInName) {
381393 // Method has "close" in its name.
382394 // Assume that it deletes the obligation.
383395 ObligationPolicyDatabaseEntry entry = database.addParameterDeletesObligationDatabaseEntry(xmethod,
384396 obligationType, ObligationPolicyDatabaseEntryType.STRONG);
385
397
386398 } else {
387399
388400 /*
393405 * obligation. If strict checking is performed, // weak
394406 * entries are ignored.
395407 */
396 if (methodName.equals("<init>") || methodName.startsWith("access$") || xmethod.isStatic()
408 if ("<init>".equals(methodName) || methodName.startsWith("access$") || xmethod.isStatic()
397409 || methodName.toLowerCase().indexOf("close") >= 0
398410 || xmethod.getSignature().toLowerCase().indexOf("Closeable") >= 0) {
399411 ObligationPolicyDatabaseEntry entry = database.addParameterDeletesObligationDatabaseEntry(xmethod,
413425
414426 /**
415427 * Create an XField object
416 *
428 *
417429 * @param className
418430 * @param fieldName
419431 * @param fieldSignature
430442
431443 /**
432444 * Create an XField object
433 *
445 *
434446 * @param className
435447 * @param fieldName
436448 * @param fieldSignature
457469
458470 public static XField createReferencedXField(DismantleBytecode visitor) {
459471 int seen = visitor.getOpcode();
460 if (seen != Opcodes.GETFIELD && seen != Opcodes.GETSTATIC && seen != Opcodes.PUTFIELD && seen != Opcodes.PUTSTATIC)
472 if (seen != Opcodes.GETFIELD && seen != Opcodes.GETSTATIC && seen != Opcodes.PUTFIELD && seen != Opcodes.PUTSTATIC) {
461473 throw new IllegalArgumentException("Not at a field reference");
474 }
462475 return createXFieldUsingSlashedClassName(visitor.getClassConstantOperand(), visitor.getNameConstantOperand(),
463476 visitor.getSigConstantOperand(), visitor.getRefFieldIsStatic());
464477 }
479492
480493 /**
481494 * Create an XField object from a BCEL Field.
482 *
495 *
483496 * @param className
484497 * the name of the Java class containing the field
485498 * @param field
499512 * Get an XField object exactly matching given class, name, and signature.
500513 * May return an unresolved object (if the class can't be found, or does not
501514 * directly declare named field).
502 *
515 *
503516 * @param className
504517 * name of class containing the field
505518 * @param name
528541 XFactory xFactory = AnalysisContext.currentXFactory();
529542
530543 XField f = xFactory.fields.get(desc);
531 if (f == null)
544 if (f == null) {
532545 return new UnresolvedXField(desc);
546 }
533547 return f;
534548 }
535549
537551 XFactory xFactory = AnalysisContext.currentXFactory();
538552
539553 XField m = xFactory.fields.get(desc);
540 if (m != null)
554 if (m != null) {
541555 return m;
556 }
542557 m = xFactory.resolveXField(desc);
543558 xFactory.fields.put(desc, m);
544559 return m;
552567 try {
553568 while (!worklist.isEmpty()) {
554569 ClassDescriptor d = worklist.removeFirst();
555 if (!d.equals(originalClassDescriptor))
570 if (!d.equals(originalClassDescriptor)) {
556571 desc = DescriptorFactory.instance().getFieldDescriptor(d.getClassName(), desc.getName(), desc.getSignature(),
557572 desc.isStatic());
573 }
558574
559575 XField f = fields.get(desc);
560 if (f != null)
576 if (f != null) {
561577 return f;
578 }
562579 XClass xClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, d);
563 if (xClass == null)
580 if (xClass == null) {
564581 break;
582 }
565583 ClassDescriptor superClass = xClass.getSuperclassDescriptor();
566 if (superClass != null)
584 if (superClass != null) {
567585 worklist.add(superClass);
568 if (originalDescriptor.isStatic())
569 for (ClassDescriptor i : xClass.getInterfaceDescriptorList())
586 }
587 if (originalDescriptor.isStatic()) {
588 for (ClassDescriptor i : xClass.getInterfaceDescriptorList()) {
570589 worklist.add(i);
590 }
591 }
571592
572593 }
573594 } catch (CheckedAnalysisException e) {
578599
579600 /**
580601 * Create an XMethod object from an InvokeInstruction.
581 *
602 *
582603 * @param invokeInstruction
583604 * the InvokeInstruction
584605 * @param cpg
596617 /**
597618 * Create an XMethod object from the method currently being visited by the
598619 * given PreorderVisitor.
599 *
620 *
600621 * @param visitor
601622 * the PreorderVisitor
602623 * @return the XMethod representing the method currently being visited
611632 /**
612633 * Create an XField object from the field currently being visited by the
613634 * given PreorderVisitor.
614 *
635 *
615636 * @param visitor
616637 * the PreorderVisitor
617638 * @return the XField representing the method currently being visited
638659 /**
639660 * Get the XClass object providing information about the class named by the
640661 * given ClassDescriptor.
641 *
662 *
642663 * @param classDescriptor
643664 * a ClassDescriptor
644665 * @return an XClass object providing information about the class, or null
659680 * <em>All methods that implement XMethod or XField should
660681 * delegate to this method when implementing compareTo(Object)
661682 * if the right-hand object implements XField or XMethod.</em>
662 *
683 *
663684 * @param lhs
664685 * an XMethod or XField
665686 * @param rhs
2525 * Abstract representation of a field. Note that this is called "XField" to
2626 * distinguish it from BCEL's Field class. Also, you can think of the "X" as
2727 * expanding to "Instance" or "Static".
28 *
28 *
2929 * <p>
3030 * This interface and its implementations exist because Field objects in BCEL
3131 * are awkward to deal with. They are not Comparable, it is difficult to find
3232 * out what class they belong to, etc.
3333 * </p>
34 *
34 *
3535 * <p>
3636 * If the resolved() method returns true, then any information queried from this
3737 * object can be assumed to be accurate. If the resolved() method returns false,
5656 public FieldDescriptor getFieldDescriptor();
5757 }
5858
59 // vim:ts=4
8585 * method in a superclass. This method simply forwards the call to the
8686 * method it bridges to, which is a method with an identical name but possibly co-variant arguments and
8787 * return values.
88 *
89 * @return
9088 */
9189 public @CheckForNull
9290 XMethod bridgeTo();
121119 * @return true if method's return type is a reference type, false otherwise
122120 */
123121 public boolean isReturnTypeReferenceType();
122
123 /**
124 * Is this a bridge method?
125 */
126 public boolean isBridge();
124127
125128 /**
126129 * Get ClassDescriptors (annotation classes) of annotations applied directly
157160 * parameter
158161 */
159162 public Collection<AnnotationValue> getParameterAnnotations(int param);
160
163
161164 public boolean hasParameterAnnotations();
162165 /**
163166 * Get ClassDescriptors (annotation classes) of annotations applied directly
165168 *
166169 * @return ClassDescriptors of annotations applied directly to this method
167170 */
171 @Override
168172 public Collection<ClassDescriptor> getAnnotationDescriptors();
169173
170174 /**
175179 * @return AnnotationValue annotating the method, or null if method is not
176180 * annotated with this kind of annotation
177181 */
182 @Override
178183 public AnnotationValue getAnnotation(ClassDescriptor desc);
179184
180185 /**
182187 *
183188 * @return Collection of all AnnotationValues applied directly to the method
184189 */
190 @Override
185191 public Collection<AnnotationValue> getAnnotations();
186192
187193 /**
207213 */
208214 public void addAnnotation(AnnotationValue annotationValue);
209215
216 boolean usesInvokeDynamic();
217
210218
211219
212220 }
2424 public class XMethodParameter implements Comparable<XMethodParameter> {
2525 /**
2626 * Create a new Method parameter reference
27 *
27 *
2828 * @param m
2929 * the method of which this is a parameter to
3030 * @param p
4949
5050 @Override
5151 public boolean equals(Object o) {
52 if (!(o instanceof XMethodParameter))
52 if (!(o instanceof XMethodParameter)) {
5353 return false;
54 }
5455 XMethodParameter mp2 = (XMethodParameter) o;
5556 return parameter == mp2.parameter && method.equals(mp2.method);
5657 }
6061 return method.hashCode() + parameter;
6162 }
6263
64 @Override
6365 @SuppressWarnings("unchecked")
6466 public int compareTo(XMethodParameter mp2) {
6567 int result = method.compareTo(mp2.method);
66 if (result != 0)
68 if (result != 0) {
6769 return result;
70 }
6871 return parameter - mp2.parameter;
6972 }
7073
2828 * A source file data source for source files residing in Zip or Jar archives.
2929 */
3030 public class ZipSourceFileDataSource implements SourceFileDataSource {
31 private ZipFile zipFile;
31 private final ZipFile zipFile;
3232
33 private String entryName;
33 private final String entryName;
3434
35 private ZipEntry zipEntry;
35 private final ZipEntry zipEntry;
3636
3737 public ZipSourceFileDataSource(ZipFile zipFile, String entryName) {
3838 this.zipFile = zipFile;
4040 this.zipEntry = zipFile.getEntry(entryName);
4141 }
4242
43 @Override
4344 public InputStream open() throws IOException {
44 if (zipEntry == null)
45 if (zipEntry == null) {
4546 throw new FileNotFoundException("No zip entry for " + entryName);
47 }
4648 return zipFile.getInputStream(zipEntry);
4749 }
4850
51 @Override
4952 public String getFullFileName() {
5053 return entryName;
5154 }
5356 /* (non-Javadoc)
5457 * @see edu.umd.cs.findbugs.ba.SourceFileDataSource#getLastModified()
5558 */
59 @Override
5660 public long getLastModified() {
5761 long time = zipEntry.getTime();
58 if (time < 0)
62 if (time < 0) {
5963 return 0;
64 }
6065 return time;
6166 }
6267 }
6368
64 // vim:ts=4
2020
2121 /**
2222 * A Binding binds a name to a Variable.
23 *
23 *
2424 * @author David Hovemeyer
2525 * @see Variable
2626 */
3131
3232 /**
3333 * Constructor.
34 *
34 *
3535 * @param varName
3636 * the name of the variable
3737 * @param variable
3838 * the variable
3939 */
4040 public Binding(String varName, Variable variable) {
41 if (variable == null)
41 if (variable == null) {
4242 throw new IllegalArgumentException("No variable!");
43 }
4344 this.varName = varName;
4445 this.variable = variable;
4546 }
6869 }
6970 }
7071
71 // vim:ts=4
2222 * A set of Bindings, which are definitions of variables occuring in a
2323 * ByteCodePattern. BindingSets are immutable; to add a binding, a new cell is
2424 * allocated. (Are we CONSING yet?)
25 *
25 *
2626 * @author David Hovemeyer
2727 * @see Binding
2828 */
3333
3434 /**
3535 * Constructor; creates a new BindingSet as an extension of an existing one.
36 *
36 *
3737 * @param binding
3838 * a variable binding
3939 * @param parent
4646
4747 /**
4848 * Look for a Binding for given variable.
49 *
49 *
5050 * @param varName
5151 * name of the variable
5252 * @return the Binding, or null if no such Binding is present in the set
5353 */
5454 public Binding lookup(String varName) {
55 if (varName.equals(binding.getVarName()))
55 if (varName.equals(binding.getVarName())) {
5656 return binding;
57 }
5758 return parent != null ? parent.lookup(varName) : null;
5859 }
5960
6364 BindingSet cur = this;
6465 buf.append('[');
6566 while (cur != null) {
66 if (cur != this)
67 if (cur != this) {
6768 buf.append(", ");
69 }
6870 buf.append(cur.binding.toString());
6971 cur = cur.parent;
7072 }
7375 }
7476 }
7577
76 // vim:ts=4
2020
2121 /**
2222 * A ByteCodePattern is a pattern matching a sequence of bytecode instructions.
23 *
23 *
2424 * @author David Hovemeyer
2525 * @see PatternElement
2626 * @see PatternMatcher
3636
3737 /**
3838 * Add a PatternElement to the end of the pattern.
39 *
39 *
4040 * @param element
4141 * the PatternElement
4242 * @return this object
4343 */
4444 public ByteCodePattern add(PatternElement element) {
45 if (first != null)
45 if (first != null) {
4646 addInterElementWild();
47 }
4748 addElement(element);
4849 return this;
4950 }
5253 * Add a wildcard to match between 0 and given number of instructions. If
5354 * there is already a wildcard at the end of the current pattern, resets its
5455 * max value to that given.
55 *
56 *
5657 * @param numWild
5758 * maximum number of instructions to be matched by the wildcard
5859 */
5960 public ByteCodePattern addWild(int numWild) {
6061 Wild wild = isLastWild();
61 if (wild != null)
62 if (wild != null) {
6263 wild.setMinAndMax(0, numWild);
63 else
64 } else {
6465 addElement(new Wild(numWild));
66 }
6567 return this;
6668 }
6769
6870 /**
6971 * Set number of inter-element wildcards to create between explicit
7072 * PatternElements. By default, no implicit wildcards are created.
71 *
73 *
7274 * @param numWild
7375 * the number of wildcard instructions which may be matched
7476 * between explicit PatternElements
101103 }
102104
103105 private void addInterElementWild() {
104 if (interElementWild > 0 && isLastWild() == null)
106 if (interElementWild > 0 && isLastWild() == null) {
105107 addElement(new Wild(interElementWild));
108 }
106109 }
107110
108111 private void addElement(PatternElement element) {
116119 }
117120
118121 private Wild isLastWild() {
119 if (last != null && last instanceof Wild)
122 if (last != null && last instanceof Wild) {
120123 return (Wild) last;
121 else
124 } else {
122125 return null;
126 }
123127 }
124128 }
125129
126 // vim:ts=4
2525 import org.apache.bcel.generic.InstructionHandle;
2626
2727 public class ByteCodePatternMatch {
28 private BindingSet bindingSet;
28 private final BindingSet bindingSet;
2929
30 private PatternElementMatch lastElementMatch;
30 private final PatternElementMatch lastElementMatch;
3131
32 private LinkedList<PatternElementMatch> patternElementMatchList;
32 private final LinkedList<PatternElementMatch> patternElementMatchList;
3333
3434 @Override
3535 public String toString() {
7474 }
7575 }
7676
77 // vim:ts=4
2929 /**
3030 * Base class for Load and Store PatternElements. Handles some of the grunt work
3131 * of representing fields and extracting field values from the stack frame.
32 *
32 *
3333 * @author David Hovemeyer
3434 * @see Load
3535 * @see Store
3636 */
3737 public abstract class FieldAccess extends SingleInstruction implements org.apache.bcel.Constants {
38 private String fieldVarName;
38 private final String fieldVarName;
3939
40 private String valueVarName;
40 private final String valueVarName;
4141
4242 /**
4343 * Constructor.
44 *
44 *
4545 * @param fieldVarName
4646 * name of the variable to bind to the field
4747 * @param valueVarName
5656 /**
5757 * Check that the Variables determined for the field and the value
5858 * loaded/stored are consistent with previous variable definitions.
59 *
59 *
6060 * @param field
6161 * Variable representing the field
6262 * @param value
7070 // Ensure that the field and value variables are consistent with
7171 // previous definitions (if any)
7272 bindingSet = addOrCheckDefinition(fieldVarName, field, bindingSet);
73 if (bindingSet == null)
73 if (bindingSet == null) {
7474 return null;
75 }
7576 bindingSet = addOrCheckDefinition(valueVarName, value, bindingSet);
76 if (bindingSet == null)
77 if (bindingSet == null) {
7778 return null;
79 }
7880 return new MatchResult(this, bindingSet);
7981 }
8082
8183 /**
8284 * Return whether the given FieldInstruction accesses a long or double
8385 * field.
84 *
86 *
8587 * @param fieldIns
8688 * the FieldInstruction
8789 * @param cpg
9698 /**
9799 * Get a Variable representing the stack value which will either be stored
98100 * into or loaded from a field.
99 *
101 *
100102 * @param fieldIns
101103 * the FieldInstruction accessing the field
102104 * @param cpg
120122 }
121123 }
122124
123 // vim:ts=4
3333
3434 /**
3535 * Constructor for static fields.
36 *
36 *
3737 * @param className
3838 * the class name
3939 * @param fieldName
4747
4848 /**
4949 * Constructor for instance fields.
50 *
50 *
5151 * @param ref
5252 * ValueNumber of the object reference
5353 * @param className
9292 return fieldSig;
9393 }
9494
95 @Override
9596 public boolean sameAs(Variable other) {
96 if (!(other instanceof FieldVariable))
97 if (!(other instanceof FieldVariable)) {
9798 return false;
99 }
98100 FieldVariable otherField = (FieldVariable) other;
99 if (isStatic() != otherField.isStatic())
101 if (isStatic() != otherField.isStatic()) {
100102 return false;
103 }
101104 return (ref == null || ref.equals(otherField.ref)) && className.equals(otherField.className)
102105 && fieldName.equals(otherField.fieldName) && fieldSig.equals(otherField.fieldSig);
103106 }
112115 }
113116 }
114117
115 // vim:ts=4
4141
4242 // Instruction must be IFNULL or IFNONNULL.
4343 Instruction ins = handle.getInstruction();
44 if (!(ins instanceof IFNULL || ins instanceof IFNONNULL))
44 if (!(ins instanceof IFNULL || ins instanceof IFNONNULL)) {
4545 return null;
46 }
4647
4748 // Ensure reference used is consistent with previous uses of
4849 // same variable.
5758 }
5859 }
5960
60 // vim:ts=4
6969 * The special mode <code>ORDINARY_METHOD</code> is equivalent to
7070 * <code>INSTANCE|STATIC</code>. The special mode <code>ANY</code> is equivalent
7171 * to <code>INSTANCE|STATIC|CONSTRUCTOR</code>.
72 *
72 *
7373 * @author David Hovemeyer
7474 * @see PatternElement
7575 */
105105 }
106106
107107 private static class ExactStringMatcher implements StringMatcher {
108 private String value;
108 private final String value;
109109
110110 public ExactStringMatcher(String value) {
111111 this.value = value;
112112 }
113113
114 @Override
114115 public boolean match(String s) {
115116 return s.equals(value);
116117 }
117118 }
118119
119120 private static class RegexpStringMatcher implements StringMatcher {
120 private Pattern pattern;
121 private final Pattern pattern;
121122
122123 public RegexpStringMatcher(String re) {
123124 pattern = Pattern.compile(re);
124125 }
125126
127 @Override
126128 public boolean match(String s) {
127129 return pattern.matcher(s).matches();
128130 }
129131 }
130132
131133 private static class SubclassMatcher implements StringMatcher {
132 private String className;
134 private final String className;
133135
134136 public SubclassMatcher(String className) {
135137 this.className = className;
136138 }
137139
140 @Override
138141 public boolean match(String s) {
139142 try {
140143 return Hierarchy.isSubtype(s, className);
155158
156159 /**
157160 * Constructor.
158 *
161 *
159162 * @param className
160163 * the class name of the method; may be specified exactly, as a
161164 * regexp, or as a subtype match
182185
183186 private StringMatcher createMatcher(String s) {
184187 return s.startsWith("/") ? (StringMatcher) new RegexpStringMatcher(s.substring(1))
185 : (StringMatcher) new ExactStringMatcher(s);
188 : (StringMatcher) new ExactStringMatcher(s);
186189 }
187190
188191 @Override
191194
192195 // See if the instruction is an InvokeInstruction
193196 Instruction ins = handle.getInstruction();
194 if (!(ins instanceof InvokeInstruction))
197 if (!(ins instanceof InvokeInstruction)) {
195198 return null;
199 }
196200 InvokeInstruction inv = (InvokeInstruction) ins;
197201
198202 String methodName = inv.getMethodName(cpg);
199203 boolean isStatic = inv.getOpcode() == Constants.INVOKESTATIC;
200 boolean isCtor = methodName.equals("<init>");
204 boolean isCtor = "<init>".equals(methodName);
201205
202206 int actualMode = 0;
203207
204 if (isStatic)
208 if (isStatic) {
205209 actualMode |= STATIC;
206 if (isCtor)
210 }
211 if (isCtor) {
207212 actualMode |= CONSTRUCTOR;
208 if (!isStatic && !isCtor)
213 }
214 if (!isStatic && !isCtor) {
209215 actualMode |= INSTANCE;
216 }
210217
211218 // Intersection of actual and desired modes must be nonempty.
212 if ((actualMode & mode) == 0)
219 if ((actualMode & mode) == 0) {
213220 return null;
221 }
214222
215223 // Check class name, method name, and method signature.
216224 if (!methodNameMatcher.match(methodName) || !methodSigMatcher.match(inv.getSignature(cpg))
217 || !classNameMatcher.match(inv.getClassName(cpg)))
225 || !classNameMatcher.match(inv.getClassName(cpg))) {
218226 return null;
227 }
219228
220229 // It's a match!
221230 return new MatchResult(this, bindingSet);
238247 }
239248 }
240249
241 // vim:ts=4
3232 /**
3333 * A PatternElement representing a load from a field. Variables represent the
3434 * field and the result of the load.
35 *
35 *
3636 * @author David Hovemeyer
3737 * @see PatternElement
3838 */
4040
4141 /**
4242 * Constructor.
43 *
43 *
4444 * @param fieldVarName
4545 * the name of the field variable
4646 * @param resultVarName
6666 } else if (ins instanceof GETSTATIC) {
6767 fieldIns = (GETSTATIC) ins;
6868 field = new FieldVariable(fieldIns.getClassName(cpg), fieldIns.getFieldName(cpg), fieldIns.getSignature(cpg));
69 } else
69 } else {
7070 return null;
71 }
7172
7273 Variable result = snarfFieldValue(fieldIns, cpg, after);
7374
7576 }
7677 }
7778
78 // vim:ts=4
2727 this.valueNumber = valueNumber;
2828 }
2929
30 @Override
3031 public boolean sameAs(Variable other) {
31 if (!(other instanceof LocalVariable))
32 if (!(other instanceof LocalVariable)) {
3233 return false;
34 }
3335 LocalVariable otherLocal = (LocalVariable) other;
3436 return valueNumber.equals(otherLocal.valueNumber);
3537 }
4042 }
4143 }
4244
43 // vim:ts=4
2121 import edu.umd.cs.findbugs.ba.vna.ValueNumber;
2222
2323 public class LongOrDoubleLocalVariable implements Variable {
24 private ValueNumber topValue, nextValue;
24 private final ValueNumber topValue, nextValue;
2525
2626 public LongOrDoubleLocalVariable(ValueNumber topValue, ValueNumber nextValue) {
2727 this.topValue = topValue;
2828 this.nextValue = nextValue;
2929 }
3030
31 @Override
3132 public boolean sameAs(Variable other) {
32 if (!(other instanceof LongOrDoubleLocalVariable))
33 if (!(other instanceof LongOrDoubleLocalVariable)) {
3334 return false;
35 }
3436 LongOrDoubleLocalVariable otherLongOrDouble = (LongOrDoubleLocalVariable) other;
3537 return topValue.equals(otherLongOrDouble.topValue) && nextValue.equals(otherLongOrDouble.nextValue);
3638 }
3739 }
3840
39 // vim:ts=4
3535 * <p>
3636 * Note that the minOccur() and maxOccur() counts of the child PatternElements
3737 * are ignored. A MatchAny element always matches exactly one instruction.
38 *
38 *
3939 * @author David Hovemeyer
4040 * @see PatternElement
4141 */
4242 public class MatchAny extends PatternElement {
43 private PatternElement[] childList;
43 private final PatternElement[] childList;
4444
4545 /**
4646 * Constructor.
47 *
47 *
4848 * @param childList
4949 * list of child PatternElements
5050 */
6666 // Just forward this on to all children,
6767 // since it is the children that the PatternMatcher will ask
6868 // about edges.
69 for (PatternElement aChildList : childList)
69 for (PatternElement aChildList : childList) {
7070 aChildList.setAllowTrailingEdges(allowTrailingEdges);
71 }
7172
7273 return this;
7374 }
7879
7980 for (PatternElement child : childList) {
8081 MatchResult matchResult = child.match(handle, cpg, before, after, bindingSet);
81 if (matchResult != null)
82 if (matchResult != null) {
8283 return matchResult;
84 }
8385 }
8486
8587 return null;
104106 }
105107 }
106108
107 // vim:ts=4
2424 * reason we need this class is because some kinds of PatternElements, such as
2525 * MatchAny, may use it to indicate that a child PatternElement was the one that
2626 * actually matched the instruction.
27 *
27 *
2828 * @author David Hovemeyer
2929 * @see PatternElement
3030 * @see BindingSet
3131 */
3232 public class MatchResult {
33 private PatternElement patternElement;
33 private final PatternElement patternElement;
3434
35 private BindingSet bindingSet;
35 private final BindingSet bindingSet;
3636
3737 /**
3838 * Constructor.
39 *
39 *
4040 * @param patternElement
4141 * the PatternElement that matched the instruction
4242 * @param bindingSet
6363
6464 }
6565
66 // vim:ts=4
2828
2929 /**
3030 * A PatternElement for matching a MONITORENTER instruction.
31 *
31 *
3232 * @author DavidHovemeyer
3333 */
3434 public class Monitorenter extends OneVariableInstruction {
3535 /**
3636 * Constructor.
37 *
37 *
3838 * @param varName
3939 * name of the variable representing the reference to the object
4040 * being locked
4949
5050 // Instruction must be MONITORENTER.
5151 Instruction ins = handle.getInstruction();
52 if (!(ins instanceof MONITORENTER))
52 if (!(ins instanceof MONITORENTER)) {
5353 return null;
54 }
5455
5556 // Ensure the object being locked matches any previous
5657 // instructions which bound our variable name to a value.
5960 }
6061 }
6162
62 // vim:ts=4
2929 /**
3030 * A PatternElement which matches NEW instructions and binds the result to a
3131 * variable.
32 *
32 *
3333 * @author David Hovemeyer
3434 * @see PatternElement
3535 */
3636 public class New extends OneVariableInstruction {
3737 /**
3838 * Constructor.
39 *
39 *
4040 * @param resultVarName
4141 * name of the result of the NEW instruction
4242 */
4949 BindingSet bindingSet) throws DataflowAnalysisException {
5050
5151 Instruction ins = handle.getInstruction();
52 if (!(ins instanceof NEW))
52 if (!(ins instanceof NEW)) {
5353 return null;
54 }
5455
5556 LocalVariable result = new LocalVariable(after.getTopValue());
5657 return addOrCheckDefinition(result, bindingSet);
5758 }
5859 }
5960
60 // vim:ts=4
2121 /**
2222 * Abstract PatternElement subclass for matching single instructions which have
2323 * a single Variable.
24 *
24 *
2525 * @see PatternElement
2626 */
2727 public abstract class OneVariableInstruction extends SingleInstruction {
28 private String varName;
28 private final String varName;
2929
3030 /**
3131 * Constructor.
32 *
32 *
3333 * @param varName
3434 * the name of the Variable used in this instruction
3535 */
4040 /**
4141 * Add a variable definition to the given BindingSet, or if there is an
4242 * existing definition, make sure it is consistent with the new definition.
43 *
43 *
4444 * @param variable
4545 * the Variable which should be added or checked for consistency
4646 * @param bindingSet
5656
5757 }
5858
59 // vim:ts=4
2727
2828 /**
2929 * PatternElement to match instructions with a particular opcode.
30 *
30 *
3131 * @author David Hovemeyer
3232 * @see PatternElement
3333 */
3434 public class Opcode extends PatternElement {
35 private int opcode;
35 private final int opcode;
3636
3737 /**
3838 * Constructor.
39 *
39 *
4040 * @param opcode
4141 * the opcode to match
4242 */
4848 public MatchResult match(InstructionHandle handle, ConstantPoolGen cpg, ValueNumberFrame before, ValueNumberFrame after,
4949 BindingSet bindingSet) throws DataflowAnalysisException {
5050
51 if (handle.getInstruction().getOpcode() == opcode)
51 if (handle.getInstruction().getOpcode() == opcode) {
5252 return new MatchResult(this, bindingSet);
53 else
53 } else {
5454 return null;
55 }
5556
5657 }
5758
7172 }
7273 }
7374
74 // vim:ts=4
6161
6262 /**
6363 * Set a label for this PatternElement.
64 *
64 *
6565 * @param label
6666 * the label
6767 * @return this object
7373
7474 /**
7575 * Get the label of this PatternElement.
76 *
76 *
7777 * @return the label, or null if the PatternElement is not labeled
7878 */
7979 public String getLabel() {
109109 * matched. By default, trailing edges may be matched. When this value is
110110 * set to false, it ensures that the successor instruction must be in the
111111 * same basic block.
112 *
112 *
113113 * @param allowTrailingEdges
114114 * true if trailing edges may be matched, false if trailing edges
115115 * will never be matched
128128
129129 /**
130130 * Look up a variable definition in given BindingSet.
131 *
131 *
132132 * @param varName
133133 * the name of the variable
134134 * @param bindingSet
136136 * @return the Variable, or null if no Variable is bound to the name
137137 */
138138 public static Variable lookup(String varName, BindingSet bindingSet) {
139 if (bindingSet == null)
139 if (bindingSet == null) {
140140 return null;
141 }
141142 Binding binding = bindingSet.lookup(varName);
142143 return (binding != null) ? binding.getVariable() : null;
143144 }
145146 /**
146147 * Return whether or not this element matches the given instruction with the
147148 * given Bindings in effect.
148 *
149 *
149150 * @param handle
150151 * the instruction
151152 * @param cpg
168169
169170 /**
170171 * Return whether or not it is acceptable to take the given branch.
171 *
172 *
172173 * @param edge
173174 * the Edge representing the branch
174175 * @param source
192193 /**
193194 * Add a variable definition to the given BindingSet, or if there is an
194195 * existing definition, make sure it is consistent with the new definition.
195 *
196 *
196197 * @param varName
197198 * the name of the variable
198199 * @param variable
209210 bindingSet = new BindingSet(new Binding(varName, variable), bindingSet);
210211 } else {
211212 if (!existingVariable.sameAs(variable)) {
212 if (DEBUG)
213 if (DEBUG) {
213214 System.out.println("\tConflicting variable " + varName + ": " + variable + " != " + existingVariable);
215 }
214216 return null;
215217 }
216218 }
230232 }
231233 }
232234
233 // vim:ts=4
4141
4242 /**
4343 * Constructor.
44 *
44 *
4545 * @param patternElement
4646 * the PatternElement being matched
4747 * @param matchedInstruction
117117 PatternElementMatch cur = this, result = null;
118118 while (cur != null) {
119119 String elementLabel = cur.patternElement.getLabel();
120 if (elementLabel != null && elementLabel.equals(label))
120 if (elementLabel != null && elementLabel.equals(label)) {
121121 result = cur;
122 }
122123 cur = cur.prev;
123124 }
124125 return result;
131132 PatternElementMatch cur = this;
132133 while (cur != null) {
133134 String elementLabel = cur.patternElement.getLabel();
134 if (elementLabel != null && elementLabel.equals(label))
135 if (elementLabel != null && elementLabel.equals(label)) {
135136 return cur;
137 }
136138 cur = cur.prev;
137139 }
138140 return null;
166168
167169 @Override
168170 public boolean equals(Object o) {
169 if (!(o instanceof PatternElementMatch))
171 if (!(o instanceof PatternElementMatch)) {
170172 return false;
173 }
171174 PatternElementMatch lhs = this;
172175 PatternElementMatch rhs = (PatternElementMatch) o;
173176
174177 while (lhs != null && rhs != null) {
175178 if (lhs.patternElement != rhs.patternElement || lhs.matchedInstruction != rhs.matchedInstruction
176 || lhs.matchCount != rhs.matchCount)
179 || lhs.matchCount != rhs.matchCount) {
177180 return false;
181 }
178182
179183 lhs = lhs.prev;
180184 rhs = rhs.prev;
184188 }
185189 }
186190
187 // vim:ts=4
5050 * <p/>
5151 * <p>
5252 * This code is a hack and should probably be rewritten.
53 *
53 *
5454 * @author David Hovemeyer
5555 * @see ByteCodePattern
5656 */
5959
6060 private static final boolean SHOW_WILD = SystemProperties.getBoolean("bcp.showWild");
6161
62 private ByteCodePattern pattern;
63
64 private CFG cfg;
65
66 private ConstantPoolGen cpg;
67
68 private DepthFirstSearch dfs;
69
70 private ValueNumberDataflow vnaDataflow;
71
72 private DominatorsAnalysis domAnalysis;
73
74 private LinkedList<BasicBlock> workList;
75
76 private IdentityHashMap<BasicBlock, BasicBlock> visitedBlockMap;
77
78 private LinkedList<ByteCodePatternMatch> resultList;
62 private final ByteCodePattern pattern;
63
64 private final CFG cfg;
65
66 private final ConstantPoolGen cpg;
67
68 private final DepthFirstSearch dfs;
69
70 private final ValueNumberDataflow vnaDataflow;
71
72 private final DominatorsAnalysis domAnalysis;
73
74 private final LinkedList<BasicBlock> workList;
75
76 private final IdentityHashMap<BasicBlock, BasicBlock> visitedBlockMap;
77
78 private final LinkedList<ByteCodePatternMatch> resultList;
7979
8080 /**
8181 * Constructor.
82 *
82 *
8383 * @param pattern
8484 * the ByteCodePattern to look for examples of
8585 * @param classContext
8888 * the Method to analyze
8989 */
9090 public PatternMatcher(ByteCodePattern pattern, ClassContext classContext, Method method) throws CFGBuilderException,
91 DataflowAnalysisException {
91 DataflowAnalysisException {
9292 this.pattern = pattern;
9393 this.cfg = classContext.getCFG(method);
9494 this.cpg = classContext.getConstantPoolGen();
102102
103103 /**
104104 * Search for examples of the ByteCodePattern.
105 *
105 *
106106 * @return this object
107107 * @throws DataflowAnalysisException
108108 * if the ValueNumberAnalysis did not produce useful values for
127127 Iterator<BasicBlock> succIterator = cfg.successorIterator(basicBlock);
128128 while (succIterator.hasNext()) {
129129 BasicBlock succ = succIterator.next();
130 if (visitedBlockMap.get(succ) == null)
130 if (visitedBlockMap.get(succ) == null) {
131131 workList.addLast(succ);
132 }
132133 }
133134 }
134135
145146
146147 /**
147148 * Attempt to begin a match.
148 *
149 *
149150 * @param basicBlock
150151 * the basic block
151152 * @param instructionIterator
165166 * convenient methods to implement the various steps of the algorithm.
166167 */
167168 private class State {
168 private BasicBlock basicBlock;
169
170 private BasicBlock.InstructionIterator instructionIterator;
171
172 private PatternElement patternElement;
169 private final BasicBlock basicBlock;
170
171 private final BasicBlock.InstructionIterator instructionIterator;
172
173 private final PatternElement patternElement;
173174
174175 private int matchCount;
175176
190191
191192 /**
192193 * Constructor. Builds the start state.
193 *
194 *
194195 * @param basicBlock
195196 * the initial basic block
196197 * @param instructionIterator
260261 * Get a ByteCodePatternMatch representing the complete match.
261262 */
262263 public ByteCodePatternMatch getResult() {
263 if (!isComplete())
264 if (!isComplete()) {
264265 throw new IllegalStateException("match not complete!");
266 }
265267 return new ByteCodePatternMatch(bindingSet, currentMatch);
266268 }
267269
271273 * current element is not complete.
272274 */
273275 public State advanceToNextElement() {
274 if (!canFork || matchCount < patternElement.minOccur())
276 if (!canFork || matchCount < patternElement.minOccur()) {
275277 // Current element is not complete, or we already
276278 // forked at this point
277279 return null;
280 }
278281
279282 // Create state to advance to matching next pattern element
280283 // at current basic block and instruction.
308311 * Returns MatchResult if match succeeds, null otherwise.
309312 */
310313 public MatchResult matchNextInBasicBlock() throws DataflowAnalysisException {
311 if (!moreInstructionsInBasicBlock())
314 if (!moreInstructionsInBasicBlock()) {
312315 throw new IllegalStateException("At end of BB!");
316 }
313317
314318 // Move to location of next instruction to be matched
315319 Location location = new Location(instructionIterator.next(), basicBlock);
328332 * Get most recently matched instruction.
329333 */
330334 public InstructionHandle getLastMatchedInstruction() {
331 if (currentMatch == null)
335 if (currentMatch == null) {
332336 throw new IllegalStateException("no current match!");
337 }
333338 return currentMatch.getMatchedInstructionInstructionHandle();
334339 }
335340
336341 /**
337342 * Return a new State for continuing the overall pattern match in a
338343 * successor basic block.
339 *
344 *
340345 * @param edge
341346 * the Edge leading to the successor basic block
342347 * @param matchResult
349354 // This allows PatternElements to select particular control edges;
350355 // for example, only continue the pattern on the true branch
351356 // of an "if" comparison.
352 if (matchResult != null && !matchResult.getPatternElement().acceptBranch(edge, getLastMatchedInstruction()))
357 if (matchResult != null && !matchResult.getPatternElement().acceptBranch(edge, getLastMatchedInstruction())) {
353358 return null;
359 }
354360
355361 return new State(this, edge.getTarget(), edge.getTarget().instructionIterator(), patternElement, matchCount,
356362 currentMatch, bindingSet, canFork);
369375 * continue the match.
370376 */
371377 public Iterable<State> dominatedInstructionStateIterable() throws DataflowAnalysisException {
372 if (!lookForDominatedInstruction())
378 if (!lookForDominatedInstruction()) {
373379 throw new IllegalStateException();
380 }
374381 LinkedList<State> stateList = new LinkedList<State>();
375382
376383 State dup = this.duplicate();
399406 stateList.add(dup);
400407 dup = this.duplicate();
401408 }
402 } else if (next.equals(domInstruction))
409 } else if (next.equals(domInstruction)) {
403410 includeInstructions = true;
411 }
404412 }
405413 }
406414 }
422430 + location.getHandle() + " " + (bindingSet != null ? bindingSet.toString() : "[]") + "...");
423431 }
424432 MatchResult matchResult = patternElement.match(location.getHandle(), cpg, before, after, bindingSet);
425 if (debug)
433 if (debug) {
426434 debug("\t" + ((matchResult != null) ? " ==> MATCH" : " ==> NOT A MATCH"));
435 }
427436 if (matchResult != null) {
428437 // Successful match!
429438 // Update state to reflect that the match has occurred.
438447 }
439448
440449 private void debug(String s) {
441 if (!DEBUG)
450 if (!DEBUG) {
442451 throw new IllegalStateException("Only call if DEBUG is true");
452 }
443453 System.out.print(" ".substring(0, depth));
444454 System.out.println(s);
445455 }
460470 // Have we reached the end of the pattern?
461471 if (state.isComplete()) {
462472 // This is a complete match.
463 if (DEBUG)
473 if (DEBUG) {
464474 debug("FINISHED A MATCH!");
475 }
465476 resultList.add(state.getResult());
466477 return;
467478 }
482493
483494 // If we've reached the maximum number of occurrences for this
484495 // pattern element, then we can't continue.
485 if (!state.currentElementCanContinue())
496 if (!state.currentElementCanContinue()) {
486497 return;
498 }
487499
488500 MatchResult matchResult = null;
489501
492504 if (state.lookForDominatedInstruction()) {
493505 Iterable<State> dominatedInstructions = state.dominatedInstructionStateIterable();
494506 for (State s : dominatedInstructions) {
495 if (DEBUG)
507 if (DEBUG) {
496508 debug("trying " + s);
509 }
497510 work(s);
498511 }
499512 return;
503516 if (state.moreInstructionsInBasicBlock()) {
504517 // Try to match it.
505518 matchResult = state.matchNextInBasicBlock();
506 if (matchResult == null)
519 if (matchResult == null) {
507520 return;
521 }
508522 }
509523
510524 // Continue the match at each successor instruction,
520534 BitSet visitedSuccessorSet = new BitSet();
521535 while (i.hasNext()) {
522536 Edge edge = i.next();
523 if (dfs.getDFSEdgeType(edge) == BACK_EDGE)
537 if (dfs.getDFSEdgeType(edge) == BACK_EDGE) {
524538 continue;
539 }
525540
526541 BasicBlock destBlock = edge.getTarget();
527542 int destId = destBlock.getLabel();
528543
529544 // CFGs can have duplicate edges
530 if (visitedSuccessorSet.get(destId))
545 if (visitedSuccessorSet.get(destId)) {
531546 continue;
547 }
532548 visitedSuccessorSet.set(destId, true);
533549
534550 // See if we can continue matching in the successor basic
546562 }
547563 }
548564
549 // vim:ts=4
2626 * An abstract PatternElement subclass for pattern elements which must match
2727 * exactly one instruction and accept any kind of branch. (Subclasses may
2828 * override acceptBranch() to implement more selective handling of branches.)
29 *
29 *
3030 * @author David Hovemeyer
3131 * @see PatternElement
3232 */
4848
4949 }
5050
51 // vim:ts=4
3232 /**
3333 * A PatternElement representing a store to a field. Variables represent the
3434 * field and the value stored.
35 *
35 *
3636 * @author David Hovemeyer
3737 * @see PatternElement
3838 */
3939 public class Store extends FieldAccess {
4040 /**
4141 * Constructor.
42 *
42 *
4343 * @param fieldVarName
4444 * the name of the field variable
4545 * @param valueVarName
6666 } else if (ins instanceof PUTSTATIC) {
6767 fieldIns = (PUTSTATIC) ins;
6868 field = new FieldVariable(fieldIns.getClassName(cpg), fieldIns.getFieldName(cpg), fieldIns.getSignature(cpg));
69 } else
69 } else {
7070 return null;
71 }
7172
7273 Variable value = snarfFieldValue(fieldIns, cpg, before);
7374
7576 }
7677 }
7778
78 // vim:ts=4
3030 public boolean sameAs(Variable other);
3131 }
3232
33 // vim:ts=4
2828 /**
2929 * A wildcard PatternElement, which matches any kind of instruction
3030 * indiscriminately.
31 *
31 *
3232 * @author David Hovemeyer
3333 * @see PatternElement
3434 */
4747 /**
4848 * Constructor. Matches any number of instructions from 0 to the maximum
4949 * specified.
50 *
50 *
5151 * @param max
5252 * the maximum number of instructions the wildcard may match
5353 */
5858
5959 /**
6060 * Constructor.
61 *
61 *
6262 * @param min
6363 * minimum number of times the wildcard must match
6464 * @param max
7171
7272 /**
7373 * Set min and max values.
74 *
74 *
7575 * @param min
7676 * minimum number of times the wildcard must match
7777 * @param max
104104 }
105105 }
106106
107 // vim:ts=4
4444
4545 @Override
4646 public boolean equals(Object obj) {
47 if (obj == null || obj.getClass() != this.getClass())
47 if (obj == null || obj.getClass() != this.getClass()) {
4848 return false;
49 }
4950 Call other = (Call) obj;
5051 return this.className.equals(other.className) && this.methodName.equals(other.methodName)
5152 && this.methodSig.equals(other.methodSig);
2323 public class CallList {
2424 private boolean isTop, isBottom;
2525
26 private ArrayList<Call> callList;
26 private final ArrayList<Call> callList;
2727
2828 public CallList() {
2929 this.callList = new ArrayList<Call>();
9494 // Result is the common prefix
9595 int len = Math.min(a.size(), b.size());
9696 for (int i = 0; i < len; ++i) {
97 if (!a.get(i).equals(b.get(i)))
97 if (!a.get(i).equals(b.get(i))) {
9898 break;
99 }
99100 result.add(a.get(i));
100101 }
101102 }
104105
105106 @Override
106107 public boolean equals(Object obj) {
107 if (obj == null || obj.getClass() != this.getClass())
108 if (obj == null || obj.getClass() != this.getClass()) {
108109 return false;
110 }
109111 CallList other = (CallList) obj;
110112 return this.callList.equals(other.callList);
111113 }
119121 public String toString() {
120122 StringBuilder buf = new StringBuilder();
121123 for (Call call : callList) {
122 if (buf.length() > 0)
124 if (buf.length() > 0) {
123125 buf.append(',');
126 }
124127 buf.append(call.getMethodName());
125128 }
126129 return buf.toString();
3737 import edu.umd.cs.findbugs.ba.ReversePostOrder;
3838
3939 public class CallListAnalysis extends AbstractDataflowAnalysis<CallList> {
40 private DepthFirstSearch dfs;
40 private final DepthFirstSearch dfs;
4141
4242 // private ConstantPoolGen cpg;
43 private Map<InstructionHandle, Call> callMap;
43 private final Map<InstructionHandle, Call> callMap;
4444
4545 public CallListAnalysis(CFG cfg, DepthFirstSearch dfs, ConstantPoolGen cpg) {
4646 this.dfs = dfs;
6565 return callMap;
6666 }
6767
68 @Override
6869 public void initEntryFact(CallList fact) {
6970 fact.clear();
7071 }
7374 // fact.setTop();
7475 // }
7576
77 @Override
7678 public boolean isForwards() {
7779 return true;
7880 }
7981
82 @Override
8083 public BlockOrder getBlockOrder(CFG cfg) {
8184 return new ReversePostOrder(cfg, dfs);
8285 }
8386
87 @Override
8488 public void makeFactTop(CallList fact) {
8589 fact.setTop();
8690 }
8791
92 @Override
8893 public boolean isTop(CallList fact) {
8994 return fact.isTop();
9095 }
9196
97 @Override
9298 public CallList createFact() {
9399 return new CallList();
94100 }
95101
102 @Override
96103 public boolean same(CallList a, CallList b) {
97104 return a.equals(b);
98105 }
99106
107 @Override
100108 public void meetInto(CallList start, Edge edge, CallList result) throws DataflowAnalysisException {
101109 CallList merge = CallList.merge(start, result);
102110 result.copyFrom(merge);
103111 }
104112
113 @Override
105114 public void copy(CallList source, CallList dest) {
106115 dest.copyFrom(source);
107116 }
2222
2323 /**
2424 * Dataflow class for CallListAnalysis.
25 *
25 *
2626 * @author David Hovemeyer
2727 */
2828 public class CallListDataflow extends Dataflow<CallList, CallListAnalysis> {
2828 /**
2929 * Vertex class - represents a class or interface in the InheritanceGraph. Edges
3030 * connect subtypes to supertypes.
31 *
31 *
3232 * @author David Hovemeyer
3333 */
3434 class ClassVertex extends AbstractVertex<InheritanceEdge, ClassVertex> {
5454
5555 @Override
5656 public boolean equals(Object o) {
57 if (!(o instanceof ClassVertex))
57 if (!(o instanceof ClassVertex)) {
5858 return false;
59 }
5960 return classDescriptor.equals(((ClassVertex) o).classDescriptor);
6061 }
6162
8485
8586 /**
8687 * Factory method for resolved ClassVertex objects.
87 *
88 *
8889 * @param classDescriptor
8990 * ClassDescriptor naming the class or interface
9091 * @param xclass
9798
9899 /**
99100 * Factory method for ClassVertex objects representing missing classes.
100 *
101 *
101102 * @param classDescriptor
102103 * ClassDescriptor naming the missing class or interface
103104 * @param isInterface
177178
178179 /**
179180 * Set the ClassVertex representing the direct superclass.
180 *
181 *
181182 * @param target
182183 * ClassVertex representing the direct superclass.
183184 */
2323 /**
2424 * An edge in the InheritanceGraph. Source vertex is the subtype, target vertex
2525 * is a direct supertype.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class InheritanceEdge extends AbstractEdge<InheritanceEdge, ClassVertex> {
2222
2323 /**
2424 * Graph of inheritance relationships.
25 *
25 *
2626 * @author David Hovemeyer
2727 */
2828 public class InheritanceGraph extends AbstractGraph<InheritanceEdge, ClassVertex> {
2929
3030 /*
3131 * (non-Javadoc)
32 *
32 *
3333 * @see
3434 * edu.umd.cs.findbugs.graph.AbstractGraph#allocateEdge(edu.umd.cs.findbugs
3535 * .graph.AbstractVertex, edu.umd.cs.findbugs.graph.AbstractVertex)
4141
4242 /*
4343 * (non-Javadoc)
44 *
44 *
4545 * @see
4646 * edu.umd.cs.findbugs.graph.AbstractGraph#createEdge(edu.umd.cs.findbugs
4747 * .graph.AbstractVertex, edu.umd.cs.findbugs.graph.AbstractVertex)
2525
2626 /**
2727 * Visitor interface for traversals of the inheritance graph.
28 *
28 *
2929 * @see Subtypes2
3030 * @author David Hovemeyer
3131 */
3232 public interface InheritanceGraphVisitor {
3333 /**
3434 * Visit a class vertex in the inheritance graph.
35 *
35 *
3636 * @param classDescriptor
3737 * ClassDescriptor of the class vertex
3838 * @param xclass
4444
4545 /**
4646 * Visit an inheritance edge in the inheritance graph.
47 *
47 *
4848 * @param sourceDesc
4949 * ClassDescriptor of the source class (subtype)
5050 * @param source
2626 /**
2727 * Class representing the interprocedural call graph. Vertices represent
2828 * methods. Edges represent method calls.
29 *
29 *
3030 * @author David Hovemeyer
3131 */
3232 @Deprecated
3333 public class InterproceduralCallGraph extends AbstractGraph<InterproceduralCallGraphEdge, InterproceduralCallGraphVertex> {
3434
35 private Map<MethodDescriptor, InterproceduralCallGraphVertex> methodDescToVertexMap;
35 private final Map<MethodDescriptor, InterproceduralCallGraphVertex> methodDescToVertexMap;
3636
3737 /**
3838 * Constructor.
4343
4444 /*
4545 * (non-Javadoc)
46 *
46 *
4747 * @see
4848 * edu.umd.cs.findbugs.graph.AbstractGraph#addVertex(edu.umd.cs.findbugs
4949 * .graph.AbstractVertex)
5656
5757 /**
5858 * Look up vertex corresponding to given method.
59 *
59 *
6060 * @param methodDesc
6161 * a MethodDescriptor specifying a method
6262 * @return the InterproceduralCallGraphVertex representing that method, or
6868
6969 /*
7070 * (non-Javadoc)
71 *
71 *
7272 * @see
7373 * edu.umd.cs.findbugs.graph.AbstractGraph#allocateEdge(edu.umd.cs.findbugs
7474 * .graph.AbstractVertex, edu.umd.cs.findbugs.graph.AbstractVertex)
2323 /**
2424 * A class representing an edge in the interprocedural call graph; i.e., a call
2525 * from one method to another.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class InterproceduralCallGraphEdge extends AbstractEdge<InterproceduralCallGraphEdge, InterproceduralCallGraphVertex> {
3030
3131 /**
3232 * Constructor.
33 *
33 *
3434 * @param source
3535 * source vertex (caller)
3636 * @param target
2424 /**
2525 * Class representing a vertex in the interprocedural call graph; i.e., a
2626 * method.
27 *
27 *
2828 * @author David Hovemeyer
2929 */
3030 public class InterproceduralCallGraphVertex extends AbstractVertex<InterproceduralCallGraphEdge, InterproceduralCallGraphVertex> {
2828 * the
2929 * {@link Subtypes2#traverseSupertypes(ClassDescriptor, InheritanceGraphVisitor)}
3030 * method.
31 *
31 *
3232 * @author David Hovemeyer
3333 */
34 public abstract class OverriddenMethodsVisitor implements InheritanceGraphVisitor {
35 private XMethod xmethod;
34 public abstract class OverriddenMethodsVisitor implements SupertypeTraversalVisitor {
35 private final XMethod xmethod;
3636
3737 /**
3838 * Constructor.
39 *
39 *
4040 * @param xmethod
4141 * a derived method
4242 */
5454
5555 /*
5656 * (non-Javadoc)
57 *
57 *
5858 * @see
5959 * edu.umd.cs.findbugs.ba.ch.InheritanceGraphVisitor#visitClass(edu.umd.
6060 * cs.findbugs.classfile.ClassDescriptor, edu.umd.cs.findbugs.ba.XClass)
6161 */
62 @Override
6263 public boolean visitClass(ClassDescriptor classDescriptor, XClass xclass) {
6364 assert xclass != null;
64 String methodSignature;
65 String methodSignature = xmethod.getSignature();
6566 XMethod bridgedFrom = xmethod.bridgeFrom();
66 if (bridgedFrom != null && !classDescriptor.equals(xmethod.getClassDescriptor())) {
67 // See if this class has an overridden method
68
69 XMethod xm = xclass.findMethod(xmethod.getName(), methodSignature, false);
70
71 if (xm == null && bridgedFrom != null && !classDescriptor.equals(xmethod.getClassDescriptor())) {
6772 methodSignature = bridgedFrom.getSignature();
68 } else {
69 methodSignature = xmethod.getSignature();
70 }
71 // See if this class has an overridden method
72 XMethod xm = xclass.findMethod(xmethod.getName(), methodSignature, false);
73 if (xm == null && bridgedFrom != null && xclass.isInterface()) {
74 // if the method is bridged and the superclass is an interface,
75 // check the exact signature as well
76 xm = xclass.findMethod(xmethod.getName(), xmethod.getSignature(), false);
73 xm = xclass.findMethod(xmethod.getName(), methodSignature, false);
7774 }
7875
7976 if (xm != null) {
8683 }
8784 }
8885
89 /*
90 * (non-Javadoc)
91 *
92 * @see
93 * edu.umd.cs.findbugs.ba.ch.InheritanceGraphVisitor#visitEdge(edu.umd.cs
94 * .findbugs.classfile.ClassDescriptor, edu.umd.cs.findbugs.ba.XClass,
95 * edu.umd.cs.findbugs.classfile.ClassDescriptor,
96 * edu.umd.cs.findbugs.ba.XClass)
97 */
98 public boolean visitEdge(ClassDescriptor sourceDesc, XClass source, ClassDescriptor targetDesc, XClass target) {
99 return (target != null);
100 }
101
10286 /**
10387 * Downcall method: will be called for each method overridden by the derived
10488 * method object passed to the constructor. Note that this method will be
10589 * called <em>for</em> the original derived method, since this is useful for
10690 * some applications.
107 *
91 *
10892 * @param xmethod
10993 * a method which is overridden by the original derived method,
11094 * or is the original derived method
8484 * Object to record the results of a supertype search.
8585 */
8686 private static class SupertypeQueryResults {
87 private Set<ClassDescriptor> supertypeSet = new HashSet<ClassDescriptor>(4);
87 private final Set<ClassDescriptor> supertypeSet = new HashSet<ClassDescriptor>(4);
8888
8989 private boolean encounteredMissingClasses = false;
9090
147147
148148 public static boolean isJSP(JavaClass javaClass) {
149149 @DottedClassName String className = javaClass.getClassName();
150 if ( className.endsWith("_jsp") || className.endsWith("_tag"))
150 if ( className.endsWith("_jsp") || className.endsWith("_tag")) {
151151 return true;
152 for(Method m : javaClass.getMethods())
153 if (m.getName().startsWith("_jsp"))
152 }
153 for(Method m : javaClass.getMethods()) {
154 if (m.getName().startsWith("_jsp")) {
154155 return true;
155
156 for(Field f : javaClass.getFields())
157 if (f.getName().startsWith("_jsp"))
156 }
157 }
158
159 for(Field f : javaClass.getFields()) {
160 if (f.getName().startsWith("_jsp")) {
158161 return true;
162 }
163 }
159164 return Subtypes2.instanceOf(className, "javax.servlet.jsp.JspPage")
160165 || Subtypes2.instanceOf(className, "org.apache.jasper.runtime.HttpJspBase")
161166 || Subtypes2.instanceOf(className, "javax.servlet.jsp.tagext.SimpleTagSupport")
190195 }
191196
192197 public static boolean instanceOf(JavaClass subtype, @DottedClassName String dottedSupertype) {
193 if (subtype.getClassName().equals(dottedSupertype) || subtype.getSuperclassName().equals(dottedSupertype))
198 if (subtype.getClassName().equals(dottedSupertype) || subtype.getSuperclassName().equals(dottedSupertype)) {
194199 return true;
195 if (subtype.getSuperclassName().equals("java.lang.Object") && subtype.getInterfaceIndices().length == 0)
200 }
201 if ("java.lang.Object".equals(subtype.getSuperclassName()) && subtype.getInterfaceIndices().length == 0) {
196202 return false;
203 }
197204 Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
198205 ClassDescriptor subDescriptor = DescriptorFactory.createClassDescriptor(subtype);
199206 ClassDescriptor superDescriptor = DescriptorFactory.createClassDescriptorFromDottedClassName(dottedSupertype);
214221 */
215222 public void addApplicationClass(XClass appXClass) {
216223 for (XMethod m : appXClass.getXMethods()) {
217 if (m.isStub())
224 if (m.isStub()) {
218225 return;
226 }
219227 }
220228 ClassVertex vertex = addClassAndGetClassVertex(appXClass);
221229 vertex.markAsApplicationClass();
320328 if (type.equals(possibleSupertype)) {
321329 return true;
322330 }
323 if (possibleSupertype.equals(Type.OBJECT))
331 if (possibleSupertype.equals(Type.OBJECT)) {
324332 return true;
325 if (type.equals(Type.OBJECT))
333 }
334 if (type.equals(Type.OBJECT)) {
326335 return false;
336 }
327337
328338 boolean typeIsObjectType = (type instanceof ObjectType);
329339 boolean possibleSupertypeIsObjectType = (possibleSupertype instanceof ObjectType);
388398 }
389399 ClassDescriptor prevSubDesc, prevSuperDesc;
390400 boolean prevResult;
391
401
392402 public boolean isSubtype(ClassDescriptor subDesc, ClassDescriptor superDesc) throws ClassNotFoundException {
393 if (subDesc == prevSubDesc && prevSuperDesc == superDesc)
403 if (subDesc == prevSubDesc && prevSuperDesc == superDesc) {
394404 return prevResult;
405 }
395406 prevResult = isSubtype0(subDesc, superDesc);
396407 prevSubDesc = subDesc;
397408 prevSuperDesc = superDesc;
400411
401412 public boolean isSubtype(ClassDescriptor subDesc, ClassDescriptor... superDesc) throws ClassNotFoundException {
402413 for (ClassDescriptor s : superDesc) {
403 if (subDesc.equals(s))
414 if (subDesc.equals(s)) {
404415 return true;
416 }
405417 }
406418 XClass xclass = AnalysisContext.currentXFactory().getXClass(subDesc);
407419 if (xclass != null) {
408420 ClassDescriptor xSuper = xclass.getSuperclassDescriptor();
409421 for (ClassDescriptor s : superDesc) {
410 if (s.equals(xSuper))
422 if (s.equals(xSuper)) {
411423 return true;
424 }
412425 }
413426 }
414427 SupertypeQueryResults supertypeQueryResults = getSupertypeQueryResults(subDesc);
415 for (ClassDescriptor s : superDesc)
416 if (supertypeQueryResults.containsType(s))
428 for (ClassDescriptor s : superDesc) {
429 if (supertypeQueryResults.containsType(s)) {
417430 return true;
431 }
432 }
418433 return false;
419434 }
420
435
421436 public boolean isSubtype0(ClassDescriptor subDesc, ClassDescriptor superDesc) throws ClassNotFoundException {
422437 assert subDesc != null;
423438 assert superDesc != null;
424 if (subDesc.equals(superDesc))
439 if (subDesc.equals(superDesc)) {
425440 return true;
441 }
426442 String superName = superDesc.getClassName();
427 if (superName.equals("java/lang/Object"))
443 if ("java/lang/Object".equals(superName)) {
428444 return true;
445 }
429446 String subName = subDesc.getClassName();
430 if (subName.equals("java/lang/Object"))
447 if ("java/lang/Object".equals(subName)) {
431448 return false;
432
433
434 if (true) {
449 }
450
451 // if (true) {
452 // XXX call below causes 88% of all MissingClassException thrown (20000 on java* JDK7 classes)
435453 XClass xclass = AnalysisContext.currentXFactory().getXClass(subDesc);
436454 if (xclass != null) {
437455 ClassDescriptor xSuper = xclass.getSuperclassDescriptor();
438 if (superDesc.equals(xSuper))
456 if (superDesc.equals(xSuper)) {
439457 return true;
458 }
440459 ClassDescriptor[] interfaces = xclass.getInterfaceDescriptorList();
441460 if (interfaces.length == 0) {
442 if (xSuper == null)
461 if (xSuper == null) {
443462 return false;
444 if (xSuper.getClassName().equals("java/lang/Object"))
463 }
464 if ("java/lang/Object".equals(xSuper.getClassName())) {
445465 return false;
446 } else for (ClassDescriptor i : interfaces)
447 if (superDesc.equals(i))
448 return true;
449 }
450 }
466 }
467 } else {
468 for (ClassDescriptor i : interfaces) {
469 if (superDesc.equals(i)) {
470 return true;
471 }
472 }
473 }
474 }
475 // }
476
477 /*
451478 if (false) {
452 if (subName.equals("java/lang/Error") && superName.equals("java/lang/RuntimeException"))
453 System.out.println("huh");
479 if (subName.equals("java/lang/Error") && superName.equals("java/lang/RuntimeException")) {
480 System.out.println("huh");
481 }
454482 System.out.println("sub: " + subDesc);
455483 System.out.println("SUP: " + superDesc);
456484 System.out.println("CHECK: " + subDesc + " " + superDesc);
457 }
485 }
486 */
458487 SupertypeQueryResults supertypeQueryResults = getSupertypeQueryResults(subDesc);
488 // XXX call below causes 88% of all ClassNotFoundException thrown (20000 on java* JDK7 classes)
459489 return supertypeQueryResults.containsType(superDesc);
460490 }
461491
692722
693723 Set<ClassDescriptor> aSuperTypes = computeKnownSupertypes(aDesc);
694724 Set<ClassDescriptor> bSuperTypes = computeKnownSupertypes(bDesc);
695 if (bSuperTypes.contains(aDesc))
725 if (bSuperTypes.contains(aDesc)) {
696726 return a;
697 if (aSuperTypes.contains(bDesc))
727 }
728 if (aSuperTypes.contains(bDesc)) {
698729 return b;
730 }
699731 ArrayList<ClassVertex> aSuperList = getAllSuperclassVertices(aVertex);
700732 ArrayList<ClassVertex> bSuperList = getAllSuperclassVertices(bVertex);
701733
714746 aIndex--;
715747 bIndex--;
716748 }
717 if (lastCommonInBackwardsSearch == null)
749 if (lastCommonInBackwardsSearch == null) {
718750 firstCommonSupertype = Type.OBJECT;
719 else
751 } else {
720752 firstCommonSupertype = ObjectTypeFactory.getInstance(lastCommonInBackwardsSearch.getClassDescriptor()
721753 .toDottedClassName());
754 }
722755 if (firstCommonSupertype.equals(Type.OBJECT)) {
723756 // see if we can't do better
724757 ClassDescriptor objDesc = DescriptorFactory.getClassDescriptor(Type.OBJECT);
725758 aSuperTypes.retainAll(bSuperTypes);
726759 aSuperTypes.remove(objDesc);
727 for (ClassDescriptor c : aSuperTypes)
728 if (c.getPackageName().equals(aDesc.getPackageName()) || c.getPackageName().equals(bDesc.getPackageName()))
760 for (ClassDescriptor c : aSuperTypes) {
761 if (c.getPackageName().equals(aDesc.getPackageName()) || c.getPackageName().equals(bDesc.getPackageName())) {
729762 return ObjectTypeFactory.getInstance(c.toDottedClassName());
730
731 for (ClassDescriptor c : aSuperTypes)
763 }
764 }
765
766 for (ClassDescriptor c : aSuperTypes) {
732767 return ObjectTypeFactory.getInstance(c.toDottedClassName());
768 }
733769 }
734770
735771 return firstCommonSupertype;
850886 return result;
851887 }
852888
853
889
854890 /**
855891 * Get Collection of all XClass objects (resolved classes) seen so far.
856892 *
956992 }
957993 }
958994
995 /**
996 * Starting at the class or interface named by the given ClassDescriptor,
997 * traverse the inheritance graph depth first, visiting each class only
998 * once. This is much faster than traversing all paths in certain circumstances.
999 *
1000 * @param start
1001 * ClassDescriptor naming the class where the traversal should
1002 * start
1003 * @param visitor
1004 * an InheritanceGraphVisitor
1005 * @throws ClassNotFoundException
1006 * if the start vertex cannot be resolved
1007 */
1008 public void traverseSupertypesDepthFirst(ClassDescriptor start, SupertypeTraversalVisitor visitor) throws ClassNotFoundException {
1009 this.traverseSupertypesDepthFirstHelper(start, visitor, new HashSet<ClassDescriptor>());
1010 }
1011
1012 private void traverseSupertypesDepthFirstHelper(ClassDescriptor cur, SupertypeTraversalVisitor visitor,
1013 Set<ClassDescriptor> seen) throws ClassNotFoundException {
1014
1015 if (seen.contains(cur)) {
1016 return;
1017 }
1018 seen.add(cur);
1019
1020 ClassVertex vertex = resolveClassVertex(cur);
1021
1022 if (!vertex.isResolved()) {
1023 // Unknown class - so, we don't know its immediate supertypes
1024 return;
1025 }
1026
1027 if (!visitor.visitClass(vertex.getClassDescriptor(), vertex.getXClass())) {
1028 // Visitor doesn't want to continue on this path
1029 return;
1030 }
1031
1032 // Advance to direct superclass
1033 ClassDescriptor superclassDescriptor = vertex.getXClass().getSuperclassDescriptor();
1034 if (superclassDescriptor != null) {
1035 traverseSupertypesDepthFirstHelper(superclassDescriptor, visitor, seen);
1036 }
1037
1038 // Advance to directly-implemented interfaces
1039 for (ClassDescriptor ifaceDesc : vertex.getXClass().getInterfaceDescriptorList()) {
1040 traverseSupertypesDepthFirstHelper(ifaceDesc, visitor, seen);
1041 }
1042 }
1043
9591044 private void addToWorkList(LinkedList<SupertypeTraversalPath> workList, SupertypeTraversalPath curPath,
9601045 ClassDescriptor supertypeDescriptor) {
9611046 ClassVertex vertex = classDescriptorToVertexMap.get(supertypeDescriptor);
10301115 return new HashSet<ClassDescriptor>(result);
10311116 }
10321117
1033
1118
10341119 public boolean hasKnownSubclasses(ClassDescriptor classDescriptor) throws ClassNotFoundException {
1035
1120
10361121 ClassVertex startVertex = resolveClassVertex(classDescriptor);
1037 if (!startVertex.isInterface())
1122 if (!startVertex.isInterface()) {
10381123 return true;
1039
1124 }
1125
10401126 LinkedList<ClassVertex> workList = new LinkedList<ClassVertex>();
10411127
10421128 workList.addLast(startVertex);
10521138 }
10531139
10541140 // Add class to the result
1055 if (current.isResolved() && !current.isInterface())
1141 if (current.isResolved() && !current.isInterface()) {
10561142 return true;
1143 }
10571144
10581145 // Add all known subtype vertices to the work list
10591146 Iterator<InheritanceEdge> i = graph.incomingEdgeIterator(current);
11201207 * @return SupertypeQueryResults containing known supertypes of the class
11211208 */
11221209 private SupertypeQueryResults computeSupertypes(ClassDescriptor classDescriptor) // throws
1123 // ClassNotFoundException
1210 // ClassNotFoundException
11241211 {
11251212 if (DEBUG_QUERIES) {
11261213 System.out.println("Computing supertypes for " + classDescriptor.toDottedClassName());
11841271 return typeVertex;
11851272 }
11861273
1187 /**
1188 * @param classDescriptor
1189 * @return
1190 */
11911274 private ClassVertex optionallyResolveClassVertex(ClassDescriptor classDescriptor) {
11921275 ClassVertex typeVertex = classDescriptorToVertexMap.get(classDescriptor);
11931276 if (typeVertex == null) {
12241307
12251308 // Direct superclass
12261309 ClassDescriptor superclassDescriptor = xclass.getSuperclassDescriptor();
1227 if (superclassDescriptor != null)
1310 if (superclassDescriptor != null) {
12281311 addInheritanceEdge(vertex, superclassDescriptor, false, workList);
1312 }
12291313
12301314 // Directly implemented interfaces
12311315 for (ClassDescriptor ifaceDesc : xclass.getInterfaceDescriptorList()) {
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2007 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.ba.ch;
20
21 import edu.umd.cs.findbugs.ba.XClass;
22 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
23
24 /**
25 * Visitor interface for simple traversals of the inheritance graph.
26 *
27 * @see Subtypes2
28 * @author Tobias Baum
29 */
30 public interface SupertypeTraversalVisitor {
31 /**
32 * Visit a class vertex in the inheritance graph.
33 * Only called for classes that could be resolved, not called for java.lang.Object.
34 *
35 * @param classDescriptor
36 * ClassDescriptor of the class vertex
37 * @param xclass
38 * XClass object containing information about the class; never null
39 *
40 * @return true if traversal should continue, false otherwise
41 */
42 public boolean visitClass(ClassDescriptor classDescriptor, XClass xclass);
43
44 }
2222 /**
2323 * Abstract dataflow value representing a value which may or may not be a
2424 * constant.
25 *
25 *
2626 * @see edu.umd.cs.findbugs.ba.constant.ConstantAnalysis
2727 * @author David Hovemeyer
2828 */
3636
3737 /**
3838 * Constructor for a constant value.
39 *
39 *
4040 * @param value
4141 * the constant value; must be a String, Integer, etc.
4242 */
4949 }
5050 /**
5151 * Return whether or not this value is a constant.
52 *
52 *
5353 * @return true if the value is a constant, false if not
5454 */
5555 public boolean isConstant() {
5858
5959 /**
6060 * Return whether or not this value is a constant String.
61 *
61 *
6262 * @return true if the value is a constant String, false if not
6363 */
6464 public boolean isConstantString() {
6767
6868 /**
6969 * Get the constant String value of this value.
70 *
70 *
7171 * @return the constant String value
7272 */
7373 public String getConstantString() {
7676
7777 /**
7878 * Return whether or not this value is a constant int/Integer.
79 *
79 *
8080 * @return true if the value is a constant int/Integer, false if not
8181 */
8282 public boolean isConstantInteger() {
8585
8686 /**
8787 * Get the constant int value of this value.
88 *
88 *
8989 * @return the constant int value
9090 */
9191 public int getConstantInt() {
9494
9595 /**
9696 * Merge two Constants.
97 *
97 *
9898 * @param a
9999 * a StaticConstant
100100 * @param b
102102 * @return the merge (dataflow meet) of the two Constants
103103 */
104104 public static Constant merge(Constant a, Constant b) {
105 if (!a.isConstant() || !b.isConstant())
105 if (!a.isConstant() || !b.isConstant()) {
106106 return NOT_CONSTANT;
107 if (a.value.getClass() != b.value.getClass() || !a.value.equals(b.value))
107 }
108 if (a.value.getClass() != b.value.getClass() || !a.value.equals(b.value)) {
108109 return NOT_CONSTANT;
110 }
109111 return a;
110112 }
111113
112114 @Override
113115 public boolean equals(Object obj) {
114 if (obj == null || obj.getClass() != this.getClass())
116 if (obj == null || obj.getClass() != this.getClass()) {
115117 return false;
118 }
116119 Constant other = (Constant) obj;
117 if (other.value == this.value)
120 if (other.value == this.value) {
118121 return true;
119 else if (other.value == null || this.value == null)
122 } else if (other.value == null || this.value == null) {
120123 return false;
121 else
124 } else {
122125 return this.value.equals(other.value);
126 }
123127 }
124128
125129 @Override
2929
3030 /**
3131 * Dataflow analysis to find constant values.
32 *
32 *
3333 * @see edu.umd.cs.findbugs.ba.constant.Constant
3434 * @author David Hovemeyer
3535 */
3636 public class ConstantAnalysis extends FrameDataflowAnalysis<Constant, ConstantFrame> {
37 private MethodGen methodGen;
37 private final MethodGen methodGen;
3838
39 private ConstantFrameModelingVisitor visitor;
39 private final ConstantFrameModelingVisitor visitor;
4040
4141 public ConstantAnalysis(MethodGen methodGen, DepthFirstSearch dfs) {
4242 super(dfs);
4444 this.visitor = new ConstantFrameModelingVisitor(methodGen.getConstantPool());
4545 }
4646
47 @Override
4748 public ConstantFrame createFact() {
4849 return new ConstantFrame(methodGen.getMaxLocals());
4950 }
5051
52 @Override
5153 public void initEntryFact(ConstantFrame frame) {
5254 frame.setValid();
5355 frame.clearStack();
6466 visitor.analyzeInstruction(handle.getInstruction());
6567 }
6668
69 @Override
6770 public void meetInto(ConstantFrame fact, Edge edge, ConstantFrame result) throws DataflowAnalysisException {
6871
6972 if (fact.isValid()) {
7073 ConstantFrame tmpFact = null;
7174
7275 if (edge.isExceptionEdge()) {
73 tmpFact = modifyFrame(fact, tmpFact);
76 tmpFact = modifyFrame(fact, null);
7477 tmpFact.clearStack();
7578 tmpFact.pushValue(Constant.NOT_CONSTANT);
7679 }
2222
2323 /**
2424 * Dataflow class for ConstantAnalysis.
25 *
25 *
2626 * @see edu.umd.cs.findbugs.ba.Dataflow
2727 * @see edu.umd.cs.findbugs.ba.constant.ConstantAnalysis
2828 * @author David Hovemeyer
2121
2222 /**
2323 * Dataflow Frame for Constants.
24 *
24 *
2525 * @see edu.umd.cs.findbugs.ba.constant.Constant
2626 * @see edu.umd.cs.findbugs.ba.constant.ConstantAnalysis
2727 * @author David Hovemeyer
2929
3030 /**
3131 * Visitor to model the effect of bytecode instructions on ConstantFrames.
32 *
32 *
3333 * <p>
3434 * For now, only String constants are modeled. In the future we can add other
3535 * kinds of constants.
3636 * </p>
37 *
37 *
3838 * @see edu.umd.cs.findbugs.ba.constant.ConstantAnalysis
3939 * @author David Hovemeyer
4040 */
5656 int amount = obj.getIncrement();
5757 ConstantFrame f = getFrame();
5858 Constant c = f.getValue(v);
59 if (c.isConstantInteger())
59 if (c.isConstantInteger()) {
6060 f.setValue(v, new Constant(c.getConstantInt() + amount));
61 else
61 }
62 else {
6263 f.setValue(v, Constant.NOT_CONSTANT);
63 // System.out.println("after iinc: " + getFrame());
64 // System.out.println("after iinc: " + getFrame());
65 }
6466 }
6567
6668 @Override
8282 */
8383 public class UnconditionalValueDerefAnalysis extends BackwardDataflowAnalysis<UnconditionalValueDerefSet> {
8484
85 @Override
86 public String toString() {
87 return this.getClass().getSimpleName() + " of " + method;
88 }
89
9085 public static final boolean DEBUG = SystemProperties.getBoolean("fnd.derefs.debug");
9186
9287 public static final boolean ASSUME_NONZERO_TRIP_LOOPS = SystemProperties.getBoolean("fnd.derefs.nonzerotrip");
9893 public static final boolean CHECK_CALLS = SystemProperties.getBoolean("fnd.derefs.checkcalls", true);
9994
10095 public static final boolean DEBUG_CHECK_CALLS = SystemProperties.getBoolean("fnd.derefs.checkcalls.debug");
96
97 private static final int NULLCHECK1[] = { Opcodes.DUP, Opcodes.INVOKESPECIAL, Opcodes.ATHROW };
98
99 private static final int NULLCHECK2[] = { Opcodes.DUP, Opcodes.LDC, Opcodes.INVOKESPECIAL, Opcodes.ATHROW };
101100
102101 private final CFG cfg;
103102
118117 *
119118 * @param rdfs
120119 * the reverse depth-first-search (for the block order)
121 * @param dfs
122 * TODO
123120 * @param cfg
124121 * the CFG for the method
125 * @param method
126 * TODO
127122 * @param methodGen
128123 * the MethodGen for the method
129124 * @param vnaDataflow
142137 System.out.println("UnconditionalValueDerefAnalysis analysis " + methodGen.getClassName() + "." + methodGen.getName()
143138 + " : " + methodGen.getSignature());
144139 }
145
140 }
141
142 @Override
143 public String toString() {
144 return this.getClass().getSimpleName() + " of " + method;
146145 }
147146
148147 /**
156155 this.invDataflow = invDataflow;
157156 }
158157
159 /**
160 *
161 *
162 */
163158 public void setTypeDataflow(TypeDataflow typeDataflow) {
164159 this.typeDataflow = typeDataflow;
165160 }
166161
167 /*
168 * (non-Javadoc)
169 *
170 * @see
171 * edu.umd.cs.findbugs.ba.AbstractDataflowAnalysis#isFactValid(java.lang
172 * .Object)
173 */
174162 @Override
175163 public boolean isFactValid(UnconditionalValueDerefSet fact) {
176164 return !fact.isTop() && !fact.isBottom();
177165 }
178166
179 private static final int NULLCHECK1[] = { Opcodes.DUP, Opcodes.INVOKESPECIAL, Opcodes.ATHROW };
180
181 private static final int NULLCHECK2[] = { Opcodes.DUP, Opcodes.LDC, Opcodes.INVOKESPECIAL, Opcodes.ATHROW };
182
183167 private static boolean check(InstructionHandle h, int[] opcodes) {
184168 for (int opcode : opcodes) {
185 if (h == null)
169 if (h == null) {
186170 return false;
171 }
187172 short opcode2 = h.getInstruction().getOpcode();
188173 if (opcode == Constants.LDC) {
189174 switch (opcode2) {
197182 default:
198183 return false;
199184 }
200 } else if (opcode2 != opcode)
185 } else if (opcode2 != opcode) {
201186 return false;
187 }
202188 h = h.getNext();
203189 }
204190 return true;
205191 }
206192
207193 public static boolean isNullCheck(InstructionHandle h, ConstantPoolGen cpg) {
208 if (!(h.getInstruction() instanceof IFNONNULL))
194 if (!(h.getInstruction() instanceof IFNONNULL)) {
209195 return false;
196 }
210197 h = h.getNext();
211198 final Instruction newInstruction = h.getInstruction();
212 if (!(newInstruction instanceof NEW))
199 if (!(newInstruction instanceof NEW)) {
213200 return false;
201 }
214202 final ObjectType loadClassType = ((NEW) newInstruction).getLoadClassType(cpg);
215 if (!loadClassType.getClassName().equals("java.lang.NullPointerException"))
203 if (!"java.lang.NullPointerException".equals(loadClassType.getClassName())) {
216204 return false;
205 }
217206 h = h.getNext();
218207 return check(h, NULLCHECK1) || check(h, NULLCHECK2);
219208
229218
230219 public static boolean reportPotentialDereference(Location location, IsNullValueFrame invFrame)
231220 throws DataflowAnalysisException {
232 if (!invFrame.isValid())
221 if (!invFrame.isValid()) {
233222 return false;
223 }
234224 IsNullValue value = invFrame.getTopValue();
235 if (value.isDefinitelyNotNull())
225 if (value.isDefinitelyNotNull()) {
236226 return false;
237 if (value.isDefinitelyNull())
227 }
228 if (value.isDefinitelyNull()) {
238229 return false;
230 }
239231 return true;
240232 }
241233
242 /*
243 * (non-Javadoc)
244 *
245 * @see
246 * edu.umd.cs.findbugs.ba.AbstractDataflowAnalysis#transferInstruction(org
247 * .apache.bcel.generic.InstructionHandle,
248 * edu.umd.cs.findbugs.ba.BasicBlock, java.lang.Object)
249 */
250234 @Override
251235 public void transferInstruction(InstructionHandle handle, BasicBlock basicBlock, UnconditionalValueDerefSet fact)
252236 throws DataflowAnalysisException {
253237
254238 Instruction instruction = handle.getInstruction();
255 if (fact.isTop())
256 return;
239 if (fact.isTop()) {
240 return;
241 }
257242 Location location = new Location(handle, basicBlock);
258243
259244 // If this is a call to an assertion method,
263248 // returns normally.
264249 // TODO: at some point, evaluate whether we should revisit this
265250 if (isAssertion(handle) // || handle.getInstruction() instanceof ATHROW
266 ) {
267 if (DEBUG)
251 ) {
252 if (DEBUG) {
268253 System.out.println("MAKING BOTTOM0 AT: " + location);
254 }
269255 fact.clear();
270256 return;
271257 }
273259 // Get value number frame
274260 ValueNumberFrame vnaFrame = vnaDataflow.getFactAtLocation(location);
275261 if (!vnaFrame.isValid()) {
276 if (DEBUG)
262 if (DEBUG) {
277263 System.out.println("MAKING TOP1 AT: " + location);
264 }
278265 // Probably dead code.
279266 // Assume this location can't be reached.
280267 makeFactTop(fact);
309296 // Check to see if an instance value is dereferenced here
310297 checkInstance(location, vnaFrame, fact);
311298
312 if (false)
299 /*
300 if (false) {
313301 fact.cleanDerefSet(location, vnaFrame);
314
315 if (DEBUG && fact.isTop())
302 }*/
303
304 if (DEBUG && fact.isTop()) {
316305 System.out.println("MAKING TOP2 At: " + location);
306 }
317307
318308 }
319309
334324 ConstantPoolGen constantPool = methodGen.getConstantPool();
335325
336326 for (ValueNumber vn : checkUnconditionalDerefDatabase(location, vnaFrame, constantPool,
337 invDataflow.getFactAtLocation(location), typeDataflow))
327 invDataflow.getFactAtLocation(location), typeDataflow)) {
338328 fact.addDeref(vn, location);
329 }
339330 }
340331
341332 public static Set<ValueNumber> checkUnconditionalDerefDatabase(Location location, ValueNumberFrame vnaFrame,
342333 ConstantPoolGen constantPool, @CheckForNull IsNullValueFrame invFrame, TypeDataflow typeDataflow)
343 throws DataflowAnalysisException {
344 if (invFrame != null && !invFrame.isValid())
334 throws DataflowAnalysisException {
335 if (invFrame != null && !invFrame.isValid()) {
345336 return Collections.emptySet();
337 }
346338
347339 InvokeInstruction inv = (InvokeInstruction) location.getHandle().getInstruction();
348340
349341 SignatureParser sigParser = new SignatureParser(inv.getSignature(constantPool));
350342 int numParams = sigParser.getNumParameters();
351 if (numParams == 0 || !sigParser.hasReferenceParameters())
343 if (numParams == 0 || !sigParser.hasReferenceParameters()) {
352344 return Collections.emptySet();
345 }
353346 ParameterNullnessPropertyDatabase database = AnalysisContext.currentAnalysisContext()
354347 .getUnconditionalDerefParamDatabase();
355348 if (database == null) {
356 if (DEBUG_CHECK_CALLS)
349 if (DEBUG_CHECK_CALLS) {
357350 System.out.println("no database!");
351 }
358352 return Collections.emptySet();
359353 }
360354
361355 TypeFrame typeFrame = typeDataflow.getFactAtLocation(location);
362356 if (!typeFrame.isValid()) {
363 if (DEBUG_CHECK_CALLS)
357 if (DEBUG_CHECK_CALLS) {
364358 System.out.println("invalid type frame!");
359 }
365360 return Collections.emptySet();
366361 }
367362
368363 try {
369364 Set<XMethod> targetSet = Hierarchy2.resolveMethodCallTargets(inv, typeFrame, constantPool);
370365
371 if (targetSet.isEmpty())
366 if (targetSet.isEmpty()) {
372367 return Collections.emptySet();
373
374 if (DEBUG_CHECK_CALLS)
368 }
369
370 if (DEBUG_CHECK_CALLS) {
375371 System.out.println("target set size: " + targetSet.size());
372 }
376373 // Compute the intersection of all properties
377374 ParameterProperty derefParamSet = null;
378375 for (XMethod target : targetSet) {
379 if (target.isStub())
376 if (target.isStub()) {
380377 continue;
381 if (DEBUG_CHECK_CALLS)
378 }
379 if (DEBUG_CHECK_CALLS) {
382380 System.out.print("Checking: " + target + ": ");
381 }
383382
384383 ParameterProperty targetDerefParamSet = database.getProperty(target.getMethodDescriptor());
385384 if (targetDerefParamSet == null) {
386385 // Hmm...no information for this target.
387386 // assume it doesn't dereference anything
388 if (DEBUG_CHECK_CALLS)
387 if (DEBUG_CHECK_CALLS) {
389388 System.out.println("==> no information, assume no guaranteed dereferences");
389 }
390390 return Collections.emptySet();
391391 }
392392
402402 }
403403
404404 if (derefParamSet == null || derefParamSet.isEmpty()) {
405 if (DEBUG)
405 if (DEBUG) {
406406 System.out.println("** Nothing");
407 }
407408 return Collections.emptySet();
408409 }
409410 if (DEBUG_CHECK_CALLS) {
416417 continue;
417418 }
418419 int argSlot = vnaFrame.getStackLocation(sigParser.getSlotsFromTopOfStackForParameter(i));
419 if (invFrame != null && !reportDereference(invFrame, argSlot))
420 if (invFrame != null && !reportDereference(invFrame, argSlot)) {
420421 continue;
421 if (DEBUG_CHECK_CALLS)
422 }
423 if (DEBUG_CHECK_CALLS) {
422424 System.out.println(" dereference @ " + location.getHandle().getPosition() + " of parameter " + i);
425 }
423426
424427 requiredToBeNonnull.add(vnaFrame.getValue(argSlot));
425428 }
437440 * If this is a method call instruction, check to see if any of the
438441 * parameters are @NonNull, and treat them as dereferences.
439442 *
440 * @param thisMethod
441 * TODO
442443 * @param location
443444 * the Location of the instruction
444445 * @param vnaFrame
452453 UnconditionalValueDerefSet fact) throws DataflowAnalysisException {
453454 INullnessAnnotationDatabase database = AnalysisContext.currentAnalysisContext().getNullnessAnnotationDatabase();
454455
455 if (database.getResolvedAnnotation(thisMethod, true) != NullnessAnnotation.NONNULL)
456 return;
456 if (database.getResolvedAnnotation(thisMethod, true) != NullnessAnnotation.NONNULL) {
457 return;
458 }
457459 if (reportPotentialDereference(location, invDataflow.getFactAtLocation(location))) {
458460 ValueNumber vn = vnaFrame.getTopValue();
459461 fact.addDeref(vn, location);
480482
481483 XField field = XFactory.createXField(fieldIns, methodGen.getConstantPool());
482484 char firstChar = field.getSignature().charAt(0);
483 if (firstChar != 'L' && firstChar != '[')
484 return;
485 if (firstChar != 'L' && firstChar != '[') {
486 return;
487 }
485488 NullnessAnnotation resolvedAnnotation = database.getResolvedAnnotation(field, true);
486489 if (resolvedAnnotation == NullnessAnnotation.NONNULL) {
487490 IsNullValueFrame invFrame = invDataflow.getFactAtLocation(location);
488 if (!invFrame.isValid())
491 if (!invFrame.isValid()) {
489492 return;
493 }
490494 IsNullValue value = invFrame.getTopValue();
491495 if (reportDereference(value)) {
492496 ValueNumber vn = vnaFrame.getTopValue();
512516 ConstantPoolGen constantPool = methodGen.getConstantPool();
513517 Set<ValueNumber> nonNullParams = checkNonNullParams(location, vnaFrame, constantPool, method,
514518 invDataflow.getFactAtLocation(location));
515 for (ValueNumber vn : nonNullParams)
519 for (ValueNumber vn : nonNullParams) {
516520 fact.addDeref(vn, location);
521 }
517522 }
518523
519524 public static Set<ValueNumber> checkAllNonNullParams(Location location, ValueNumberFrame vnaFrame,
525530 }
526531 Set<ValueNumber> result1 = checkNonNullParams(location, vnaFrame, constantPool, method, invFrame);
527532 Set<ValueNumber> result2 = checkUnconditionalDerefDatabase(location, vnaFrame, constantPool, invFrame, typeDataflow);
528 if (result1.isEmpty())
533 if (result1.isEmpty()) {
529534 return result2;
530 if (result2.isEmpty())
535 }
536 if (result2.isEmpty()) {
531537 return result1;
538 }
532539 result1.addAll(result2);
533
534540 return result1;
535
536541 }
537542
538543 public static Set<ValueNumber> checkNonNullParams(Location location, ValueNumberFrame vnaFrame, ConstantPoolGen constantPool,
539544 @CheckForNull Method method, @CheckForNull IsNullValueFrame invFrame) throws DataflowAnalysisException {
540545
541 if (invFrame != null && !invFrame.isValid())
546 if (invFrame != null && !invFrame.isValid()) {
542547 return Collections.emptySet();
548 }
543549 INullnessAnnotationDatabase database = AnalysisContext.currentAnalysisContext().getNullnessAnnotationDatabase();
544550
545551 InvokeInstruction inv = (InvokeInstruction) location.getHandle().getInstruction();
552558 for (int i = 0; i < numParams; i++) {
553559 String parameterSignature = parameterIterator.next();
554560 char firstChar = parameterSignature.charAt(0);
555 if (firstChar != 'L' && firstChar != '[')
561 if (firstChar != 'L' && firstChar != '[') {
556562 continue;
563 }
557564 int offset = sigParser.getSlotsFromTopOfStackForParameter(i);
558565 if (invFrame != null) {
559566 int slot = invFrame.getStackLocation(offset);
560 if (!reportDereference(invFrame, slot))
567 if (!reportDereference(invFrame, slot)) {
561568 continue;
569 }
562570 }
563571 if (database.parameterMustBeNonNull(called, i)) {
564572 int catchSizeNPE = Util.getSizeOfSurroundingTryBlock(method, "java/lang/NullPointerException", location
566574 int catchSizeNFE = Util.getSizeOfSurroundingTryBlock(method, "java/lang/NumberFormatException", location
567575 .getHandle().getPosition());
568576 if (catchSizeNPE == Integer.MAX_VALUE
569 && (!called.getClassName().equals("java.lang.Integer") || catchSizeNFE == Integer.MAX_VALUE)) {
577 && (!"java.lang.Integer".equals(called.getClassName()) || catchSizeNFE == Integer.MAX_VALUE)) {
570578 // Get the corresponding value number
571579 ValueNumber vn = vnaFrame.getArgument(inv, constantPool, i, sigParser);
572580 result.add(vn);
596604 if (!location.isFirstInstructionInBasicBlock()) {
597605 return;
598606 }
599 if (invDataflow == null)
600 return;
607 if (invDataflow == null) {
608 return;
609 }
601610 BasicBlock fallThroughPredecessor = cfg.getPredecessorWithEdgeType(location.getBasicBlock(), EdgeTypes.FALL_THROUGH_EDGE);
602611 if (fallThroughPredecessor == null || !fallThroughPredecessor.isNullCheck()) {
603612 return;
609618 // Ignore dereferences of this
610619 if (!methodGen.isStatic()) {
611620 ValueNumber v = vnaFrame.getValue(0);
612 if (v.equals(vn))
621 if (v.equals(vn)) {
613622 return;
614 }
615 if (vn.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT))
616 return;
623 }
624 }
625 if (vn.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) {
626 return;
627 }
617628
618629 IsNullValueFrame startFact = null;
619630
620631 startFact = invDataflow.getStartFact(fallThroughPredecessor);
621632
622 if (!startFact.isValid())
623 return;
633 if (!startFact.isValid()) {
634 return;
635 }
624636
625637 int slot = startFact.getInstanceSlot(location.getHandle().getInstruction(), methodGen.getConstantPool());
626 if (!reportDereference(startFact, slot))
627 return;
638 if (!reportDereference(startFact, slot)) {
639 return;
640 }
628641 if (DEBUG) {
629642 System.out.println("FOUND GUARANTEED DEREFERENCE");
630643 System.out.println("Load: " + vnaFrame.getLoad(vn));
645658 }
646659
647660 private static boolean reportDereference(IsNullValue value) {
648 if (value.isDefinitelyNotNull())
661 if (value.isDefinitelyNotNull()) {
649662 return false;
650 if (value.isDefinitelyNull())
663 }
664 if (value.isDefinitelyNull()) {
651665 return false;
652 if (IGNORE_DEREF_OF_NCP && value.isNullOnComplicatedPath())
666 }
667 if (IGNORE_DEREF_OF_NCP && value.isNullOnComplicatedPath()) {
653668 return false;
669 }
654670 return true;
655671 }
656672
666682
667683 }
668684
669 /*
670 * (non-Javadoc)
671 *
672 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#copy(java.lang.Object,
673 * java.lang.Object)
674 */
685 @Override
675686 public void copy(UnconditionalValueDerefSet source, UnconditionalValueDerefSet dest) {
676687 dest.makeSameAs(source);
677688 }
678689
679 /*
680 * (non-Javadoc)
681 *
682 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#createFact()
683 */
690 @Override
684691 public UnconditionalValueDerefSet createFact() {
685692 return new UnconditionalValueDerefSet(vnaDataflow.getAnalysis().getNumValuesAllocated());
686693 }
687694
688 /*
689 * (non-Javadoc)
690 *
691 * @see
692 * edu.umd.cs.findbugs.ba.DataflowAnalysis#initEntryFact(java.lang.Object)
693 */
695 @Override
694696 public void initEntryFact(UnconditionalValueDerefSet result) throws DataflowAnalysisException {
695697 result.clear();
696698 }
703705 // result.setIsTop();
704706 // }
705707
706 /*
707 * (non-Javadoc)
708 *
709 * @see
710 * edu.umd.cs.findbugs.ba.DataflowAnalysis#makeFactTop(java.lang.Object)
711 */
708 @Override
712709 public void makeFactTop(UnconditionalValueDerefSet fact) {
713710 fact.setIsTop();
714711 }
715712
713 @Override
716714 public boolean isTop(UnconditionalValueDerefSet fact) {
717715 return fact.isTop();
718716 }
719717
720 /*
721 * (non-Javadoc)
722 *
723 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#meetInto(java.lang.Object,
724 * edu.umd.cs.findbugs.ba.Edge, java.lang.Object)
725 */
718 @Override
726719 public void meetInto(UnconditionalValueDerefSet fact, Edge edge, UnconditionalValueDerefSet result)
727720 throws DataflowAnalysisException {
728721 meetInto(fact, edge, result, false);
729722 }
730723
731 /*
732 * (non-Javadoc)
733 *
734 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#meetInto(java.lang.Object,
735 * edu.umd.cs.findbugs.ba.Edge, java.lang.Object)
736 */
737 public void meetInto(UnconditionalValueDerefSet fact, Edge edge, UnconditionalValueDerefSet result, boolean onlyEdge)
738 {
739
724 public void meetInto(UnconditionalValueDerefSet fact, Edge edge, UnconditionalValueDerefSet result, boolean onlyEdge) {
740725 if (isExceptionEdge(edge) && !onlyEdge) {
741 if (DEBUG)
726 if (DEBUG) {
742727 System.out.println("Skipping exception edge");
728 }
743729 return;
744730 }
745731
759745 Set<Integer> loopExitBranches = ClassContext.getLoopExitBranches(method, methodGen);
760746 assert loopExitBranches != null;
761747 boolean sourceIsTopOfLoop = edge.sourceIsTopOfLoop(loopExitBranches);
762 if (sourceIsTopOfLoop && edge.getType() == EdgeTypes.FALL_THROUGH_EDGE)
748 if (sourceIsTopOfLoop && edge.getType() == EdgeTypes.FALL_THROUGH_EDGE) {
763749 isBackEdge = true;
750 }
751 /*
764752 if (false && (edge.getType() == EdgeTypes.IFCMP_EDGE || sourceIsTopOfLoop)) {
765753 System.out.println("Meet into " + edge);
766754 System.out.println(" foo2: " + sourceIsTopOfLoop);
770758 System.out.println(" Initial fact: " + result);
771759 System.out.println(" Edge fact: " + fact);
772760 }
761 */
773762 if (result.isTop() || fact.isBottom()) {
774763 // Make result identical to other fact
775764 copy(fact, result);
776 if (ASSUME_NONZERO_TRIP_LOOPS && isBackEdge && !fact.isTop())
765 if (ASSUME_NONZERO_TRIP_LOOPS && isBackEdge && !fact.isTop()) {
777766 result.resultsFromBackEdge = true;
767 }
778768 } else if (ASSUME_NONZERO_TRIP_LOOPS && isBackEdge && !fact.isTop()) {
779769 result.unionWith(fact, vnaDataflow.getAnalysis().getFactory());
780770 result.resultsFromBackEdge = true;
791781 if (ASSUME_NONZERO_TRIP_LOOPS && result.resultsFromBackEdge) {
792782 result.backEdgeUpdateCount++;
793783 if (result.backEdgeUpdateCount < 10) {
794 if (DEBUG)
784 if (DEBUG) {
795785 System.out.println("\n Union update of " + System.identityHashCode(result) + " due to backedge info");
786 }
796787 result.unionWith(fact, vnaDataflow.getAnalysis().getFactory());
797788 return;
798789 }
807798 if (DEBUG && isBackEdge && edge.getType() == EdgeTypes.IFCMP_EDGE) {
808799 System.out.println(" result: " + result);
809800 }
810
811801 }
812802
813803 /**
832822
833823 if (blockValueNumberFrame.isValid() && targetValueNumberFrame.isValid()) {
834824 int slots = 0;
835 if (targetValueNumberFrame.getNumSlots() == blockValueNumberFrame.getNumSlots())
825 if (targetValueNumberFrame.getNumSlots() == blockValueNumberFrame.getNumSlots()) {
836826 slots = targetValueNumberFrame.getNumSlots();
837 else if (targetValueNumberFrame.getNumLocals() == blockValueNumberFrame.getNumLocals())
827 } else if (targetValueNumberFrame.getNumLocals() == blockValueNumberFrame.getNumLocals()) {
838828 slots = targetValueNumberFrame.getNumLocals();
829 }
839830
840831 if (slots > 0) {
841832 if (DEBUG) {
847838 for (int i = 0; i < slots; i++) {
848839 ValueNumber blockVN = blockValueNumberFrame.getValue(i);
849840 ValueNumber targetVN = targetValueNumberFrame.getValue(i);
850 if (blockVN.equals(targetVN))
841 if (blockVN.equals(targetVN)) {
851842 continue;
843 }
852844 fact.clearDerefSet(blockVN);
853 if (originalFact.isUnconditionallyDereferenced(targetVN))
845 if (originalFact.isUnconditionallyDereferenced(targetVN)) {
854846 fact.setDerefSet(blockVN, originalFact.getUnconditionalDerefLocationSet(targetVN));
847 }
855848
856849 } // for all slots
857850
858851 for (ValueNumber blockVN : blockValueNumberFrame.valueNumbersForLoads()) {
859852 AvailableLoad load = blockValueNumberFrame.getLoad(blockVN);
860 if (load == null)
853 if (load == null) {
861854 continue;
855 }
862856 ValueNumber[] targetVNs = targetValueNumberFrame.getAvailableLoad(load);
863 if (targetVNs != null)
864 for (ValueNumber targetVN : targetVNs)
857 if (targetVNs != null) {
858 for (ValueNumber targetVN : targetVNs) {
865859 if (targetVN.hasFlag(ValueNumber.PHI_NODE) && fact.isUnconditionallyDereferenced(targetVN)
866860 && !fact.isUnconditionallyDereferenced(blockVN)) {
867861 // Block VN is also dereferenced
868862 // unconditionally.
869863 AvailableLoad targetLoad = targetValueNumberFrame.getLoad(targetVN);
870 if (!load.equals(targetLoad))
864 if (!load.equals(targetLoad)) {
871865 continue;
866 }
872867 if (DEBUG) {
873868 System.out.println("** Copy vn derefs for " + load + " from " + targetVN + " --> " + blockVN);
874869 System.out.println("** block phi for " + System.identityHashCode(blockValueNumberFrame)
879874 fact.setDerefSet(blockVN, fact.getUnconditionalDerefLocationSet(targetVN));
880875
881876 }
877 }
878 }
882879 }
883880
884881 }
951948 private boolean isExceptionEdge(Edge edge) {
952949 boolean isExceptionEdge = edge.isExceptionEdge();
953950 if (isExceptionEdge) {
954 if (DEBUG)
951 if (DEBUG) {
955952 System.out.println("NOT Ignoring " + edge);
953 }
956954 return true; // false
957955 }
958 if (edge.getType() != EdgeTypes.FALL_THROUGH_EDGE)
956 if (edge.getType() != EdgeTypes.FALL_THROUGH_EDGE) {
959957 return false;
958 }
960959 InstructionHandle h = edge.getSource().getLastInstruction();
961 if (h != null && h.getInstruction() instanceof IFNONNULL && isNullCheck(h, methodGen.getConstantPool()))
960 if (h != null && h.getInstruction() instanceof IFNONNULL && isNullCheck(h, methodGen.getConstantPool())) {
962961 return true;
962 }
963963
964964 return false;
965965
966966 }
967967
968 /*
969 * (non-Javadoc)
970 *
971 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#same(java.lang.Object,
972 * java.lang.Object)
973 */
968 @Override
974969 public boolean same(UnconditionalValueDerefSet fact1, UnconditionalValueDerefSet fact2) {
975970 return fact1.resultsFromBackEdge || fact1.isSameAs(fact2);
976971 }
2323
2424 /**
2525 * Dataflow to find value numbers unconditionally dereferenced.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class UnconditionalValueDerefDataflow extends
30 AbstractDataflow<UnconditionalValueDerefSet, UnconditionalValueDerefAnalysis> {
30 AbstractDataflow<UnconditionalValueDerefSet, UnconditionalValueDerefAnalysis> {
3131
3232 /**
3333 * Constructor.
34 *
34 *
3535 * @param cfg
3636 * the control flow graph
3737 * @param analysis
3838
3939 /**
4040 * A set of values unconditionally dereferenced in the future.
41 *
41 *
4242 * @author David Hovemeyer
4343 */
4444 public class UnconditionalValueDerefSet {
4545 /** Number of distinct value numbers in method */
46 private int numValueNumbersInMethod;
46 private final int numValueNumbersInMethod;
4747
4848 /** Set of value numbers unconditionally dereferenced */
49 private BitSet valueNumbersUnconditionallyDereferenced;
49 private final BitSet valueNumbersUnconditionallyDereferenced;
5050
5151 /** Map of value numbers to locations */
52 private Map<ValueNumber, Set<Location>> derefLocationSetMap;
52 private final Map<ValueNumber, Set<Location>> derefLocationSetMap;
5353
5454 boolean resultsFromBackEdge = false;
5555
5959
6060 /**
6161 * Constructor.
62 *
62 *
6363 * @param numValueNumbersInMethod
6464 * number of distinct value numbers in method
6565 */
7272
7373 /**
7474 * Is this the bottom value?
75 *
75 *
7676 * @return true if this is the bottom value, false otherwise
7777 */
7878 public boolean isBottom() {
8989
9090 /**
9191 * Is this the top value?
92 *
92 *
9393 * @return true if this is the top value, false otherwise
9494 */
9595 public boolean isTop() {
116116
117117 /**
118118 * Make this dataflow fact the same as the given one.
119 *
119 *
120120 * @param source
121121 * another dataflow fact
122122 */
127127 lastUpdateTimestamp = source.lastUpdateTimestamp;
128128 // Copy dereference locations for each value number
129129 derefLocationSetMap.clear();
130 if (source.derefLocationSetMap.size() > 0)
130 if (source.derefLocationSetMap.size() > 0) {
131131 for (Map.Entry<ValueNumber, Set<Location>> sourceEntry : source.derefLocationSetMap.entrySet()) {
132132 Set<Location> derefLocationSet = Util.makeSmallHashSet(sourceEntry.getValue());
133133 derefLocationSetMap.put(sourceEntry.getKey(), derefLocationSet);
134134 }
135 }
135136 }
136137
137138 /**
138139 * Return whether or not this dataflow fact is identical to the one given.
139 *
140 *
140141 * @param otherFact
141142 * another dataflow fact
142143 * @return true if the other dataflow fact is identical to this one, false
150151 /**
151152 * Merge given dataflow fact into this one. We take the intersection of the
152153 * unconditional deref value number set, and union the deref locations.
153 *
154 *
154155 * @param fact
155156 * another dataflow fact
156157 * @param skipMe
177178 // For each unconditionally dereferenced value...
178179 for (int i = 0; i < numValueNumbersInMethod; i++) {
179180 ValueNumber vn = valueNumberFactory.forNumber(i);
180 if (vn.equals(skipMe))
181 if (vn.equals(skipMe)) {
181182 continue;
183 }
182184 Set<Location> factDerefLocationSet = fact.derefLocationSetMap.get(vn);
183185 if (valueNumbersUnconditionallyDereferenced.get(i)) {
184186 if (factDerefLocationSet != null && !factDerefLocationSet.isEmpty()) {
196198 // The value number is not in the fact:
197199 // remove its location set
198200 if (removed != null) {
199 if (UnconditionalValueDerefAnalysis.DEBUG)
201 if (UnconditionalValueDerefAnalysis.DEBUG) {
200202 System.out.println("Goodbye: " + removed);
203 }
201204 }
202205 }
203206 }
231234
232235 /**
233236 * Mark a value as being dereferenced at given Location.
234 *
237 *
235238 * @param vn
236239 * the value
237240 * @param location
250253 /**
251254 * Set a value as being unconditionally dereferenced at the given set of
252255 * locations.
253 *
256 *
254257 * @param vn
255258 * the value
256259 * @param derefSet
269272
270273 /**
271274 * Clear the set of dereferences for given ValueNumber
272 *
275 *
273276 * @param value
274277 * the ValueNumber
275278 */
283286
284287 /**
285288 * Get the set of dereference Locations for given value number.
286 *
289 *
287290 * @param vn
288291 * the value number
289292 * @return the set of dereference Locations
300303 /**
301304 * Return whether or not the given value number is unconditionally
302305 * dereferenced.
303 *
306 *
304307 * @param vn
305308 * the value number
306309 * @return true if the value is unconditionally dereferenced, false
313316 public Set<ValueNumber> getValueNumbersThatAreUnconditionallyDereferenced() {
314317 HashSet<ValueNumber> result = new HashSet<ValueNumber>();
315318 for (Map.Entry<ValueNumber, Set<Location>> e : derefLocationSetMap.entrySet()) {
316 if (!e.getValue().isEmpty())
319 if (!e.getValue().isEmpty()) {
317320 result.add(e.getKey());
321 }
318322 }
319323 return result;
320324 }
333337 * Get the set of Locations where given value is guaranteed to be
334338 * dereferenced. (I.e., if non-implicit-exception control paths are
335339 * followed, one of these locations will be reached).
336 *
340 *
337341 * @param vn
338342 * the value
339343 * @return set of Locations, one of which will definitely be reached if
349353
350354 /*
351355 * (non-Javadoc)
352 *
356 *
353357 * @see java.lang.Object#toString()
354358 */
355359 @Override
375379 }
376380 buf.append('{');
377381 buf.append(i);
378 if (valueNumbersUnconditionallyDereferenced.get(i))
382 if (valueNumbersUnconditionallyDereferenced.get(i)) {
379383 buf.append(':');
380 else
384 } else {
381385 buf.append('?');
386 }
382387 TreeSet<Location> derefLocationSet = new TreeSet<Location>();
383388 derefLocationSet.addAll(getDerefLocationSet(i));
384389 boolean firstLoc = true;
416421 valueNumbers.addAll(vnaFrame.valueNumbersForLoads());
417422
418423 if (UnconditionalValueDerefAnalysis.DEBUG) {
419 for (ValueNumber v : getValueNumbersThatAreUnconditionallyDereferenced())
424 for (ValueNumber v : getValueNumbersThatAreUnconditionallyDereferenced()) {
420425 if (!valueNumbers.contains(v)) {
421426 System.out.println("\nWhy is " + v + " unconditionally dereferenced in #" + System.identityHashCode(this));
422427 System.out.println("VN: " + vnaFrame);
424429 System.out.println("Location: " + location);
425430 System.out.println();
426431 }
432 }
427433
428434 }
429435 retainOnlyTheseValueNumbers(valueNumbers);
7171
7272 @Override
7373 public boolean equals(Object o) {
74 if (!(o instanceof GenericObjectType))
74 if (!(o instanceof GenericObjectType)) {
7575 return false;
76 if (!super.equals(o))
76 }
77 if (!super.equals(o)) {
7778 return false;
79 }
7880 GenericObjectType that = (GenericObjectType) o;
7981 return Util.nullSafeEquals(this.parameters, that.parameters) && Util.nullSafeEquals(this.variable, that.variable)
8082 && Util.nullSafeEquals(this.extension, that.extension);
8183 }
8284
8385 public Type getUpperBound() {
84 if ("+".equals(variable))
86 if ("+".equals(variable)) {
8587 return extension;
88 }
8689 return this;
8790 }
8891
110113 return GenericUtilities.TypeCategory.PARAMETERIZED;
111114
112115 } else if (!hasParameters() && variable != null && extension == null) {
113 if (variable.equals("*"))
116 if ("*".equals(variable)) {
114117 return GenericUtilities.TypeCategory.WILDCARD;
115 else
118 } else {
116119 return GenericUtilities.TypeCategory.TYPE_VARIABLE;
120 }
117121
118122 } else if (!hasParameters() && variable != null && extension != null) {
119 if (variable.equals("+"))
123 if ("+".equals(variable)) {
120124 return GenericUtilities.TypeCategory.WILDCARD_EXTENDS;
121 else if (variable.equals("-"))
125 } else if ("-".equals(variable)) {
122126 return GenericUtilities.TypeCategory.WILDCARD_SUPER;
127 }
123128
124129 }
125130 // this should never happen
149154 * @return the type parameter at index
150155 */
151156 public ReferenceType getParameterAt(int index) {
152 if (index < getNumParameters())
157 if (index < getNumParameters()) {
153158 return parameters.get(index);
154 else
159 } else {
155160 throw new IndexOutOfBoundsException("The index " + index + " is too large for " + this);
161 }
156162 }
157163
158164 public @CheckForNull
159165 List<? extends ReferenceType> getParameters() {
160 if (parameters == null)
166 if (parameters == null) {
161167 return null;
168 }
162169 return Collections.unmodifiableList(parameters);
163170 }
164171
201208 super(DescriptorFactory.canonicalizeString(class_name));
202209 variable = null;
203210 extension = null;
204 if (parameters == null || parameters.size() == 0)
211 if (parameters == null || parameters.size() == 0) {
205212 throw new IllegalStateException("argument 'parameters' must contain at least 1 parameter");
213 }
206214 this.parameters = parameters;
207215 }
208216
246254 }
247255
248256 public String getGenericParametersAsString() {
249 if (getTypeCategory() != GenericUtilities.TypeCategory.PARAMETERIZED)
257 if (getTypeCategory() != GenericUtilities.TypeCategory.PARAMETERIZED) {
250258 throw new IllegalStateException(toString() + " doesn't have generic parameters");
259 }
251260 String baseStringValue = super.toString();
252261 String fullStringValue = toString();
253262 return fullStringValue.substring(baseStringValue.length());
3535 /**
3636 * A simple class to parse method signatures that include generic information.
3737 * <p>
38 *
38 *
3939 * Modified from edu.umd.cs.findbugs.ba.SignatureParser
40 *
40 *
4141 * @author Nat Ayewah
4242 */
4343 public class GenericSignatureParser {
4444 private class ParameterSignatureIterator implements Iterator<String> {
4545 private int index = 1;
4646
47 @Override
4748 public boolean hasNext() {
4849 return index < signature.length() && signature.charAt(index) != ')' && signature.charAt(index) != '^';
4950 }
5051
52 @Override
5153 public String next() {
52 if (!hasNext())
54 if (!hasNext()) {
5355 throw new NoSuchElementException();
56 }
5457 StringBuilder result = new StringBuilder();
5558 boolean done;
5659 do {
8083 char c = signature.charAt(i);
8184 switch (c) {
8285 case ';':
83 if (leftCount == 0)
86 if (leftCount == 0) {
8487 break loop;
88 }
8589 break;
8690 case '<':
8791 leftCount++;
118122 return result.toString();
119123 }
120124
125 @Override
121126 public void remove() {
122127 throw new UnsupportedOperationException();
123128 }
128133 /**
129134 * Parses a generic method signature of the form:
130135 * <code>(argument_signature)return_type_signature</code>
131 *
136 *
132137 * @param signature
133138 * the method signature to be parsed
134139 */
137142 // method definitions
138143 int s = signature.indexOf('(');
139144 String sig = signature;
140 if (s > 0)
145 if (s > 0) {
141146 sig = sig.substring(s);
142 else if (s < 0 || sig.indexOf(':') >= 0 || sig.startsWith("(V)"))
147 } else if (s < 0 || sig.indexOf(':') >= 0 || sig.startsWith("(V)")) {
143148 throw new IllegalArgumentException("Bad method signature: " + signature);
149 }
144150 this.signature = sig;
145151 }
146152
147153 /**
148154 * Get an Iterator over signatures of the method parameters.
149 *
155 *
150156 * @return Iterator which returns the parameter type signatures in order
151157 */
152158 public Iterator<String> parameterSignatureIterator() {
155161
156162 /**
157163 * Get the method return type signature.
158 *
164 *
159165 * @return the method return type signature
160166 */
161167 public String getReturnTypeSignature() {
162168 int endOfParams = signature.lastIndexOf(')');
163 if (endOfParams < 0)
169 if (endOfParams < 0) {
164170 throw new IllegalArgumentException("Bad method signature: " + signature);
171 }
165172 return signature.substring(endOfParams + 1);
166173 }
167174
168175 /**
169176 * Get the number of parameters in the signature.
170 *
177 *
171178 * @return the number of parameters
172179 */
173180 public int getNumParameters() {
181188
182189 /**
183190 * Get the number of parameters passed to method invocation.
184 *
191 *
185192 * @param inv
186193 * @param cpg
187194 * @return int number of parameters
214221 System.out.println(sig.getSignature());
215222 }
216223 return null; // we've seen two inconsistent
217 // signatures
224 // signatures
218225 }
219226 continue;
220227 }
221228
222229 genericSignature = sig.getSignature();
223 if (compareSignatures(target.getSignature(), genericSignature))
230 if (compareSignatures(target.getSignature(), genericSignature)) {
224231 parser = new GenericSignatureParser(genericSignature);
232 }
225233 }
226234 }
227235 Iterator<String> iter = parser == null ? null : parser.parameterSignatureIterator();
239247 GenericSignatureParser plainParser = new GenericSignatureParser(plainSignature);
240248 GenericSignatureParser genericParser = new GenericSignatureParser(genericSignature);
241249
242 if (plainParser.getNumParameters() != genericParser.getNumParameters())
250 if (plainParser.getNumParameters() != genericParser.getNumParameters()) {
243251 return false;
252 }
244253
245254 return true;
246255 }
256265 System.out.println(s);
257266 Type t = GenericUtilities.getType(s);
258267 System.out.println("-~- " + t);
259 if (t instanceof ObjectType)
268 if (t instanceof ObjectType) {
260269 System.out.println("-~- " + ((ObjectType) t).toString());
261 if (t != null)
270 }
271 if (t != null) {
262272 System.out.println("-~- " + t.getClass());
273 }
263274 }
264275 System.out.println(parser.getNumParameters() + " parameter(s)");
265276
8484 b.append("<");
8585 boolean first = true;
8686 for (Type t : obj.parameters) {
87 if (!first)
87 if (!first) {
8888 b.append(",");
89 }
8990 first = false;
9091 b.append(GenericUtilities.getString(t));
9192 }
183184 * @see GenericUtilities.TypeCategory
184185 */
185186 public static final TypeCategory getTypeCategory(Type type) {
186 if (type instanceof GenericObjectType)
187 if (type instanceof GenericObjectType) {
187188 return ((GenericObjectType) type).getTypeCategory();
188
189 if (type instanceof ObjectType || type instanceof NullType)
189 }
190
191 if (type instanceof ObjectType || type instanceof NullType) {
190192 return TypeCategory.PLAIN_OBJECT_TYPE;
191
192 if (type instanceof ArrayType)
193 }
194
195 if (type instanceof ArrayType) {
193196 return TypeCategory.ARRAY_TYPE;
197 }
194198
195199 throw new IllegalArgumentException("Not a reference type: " + type);
196200 }
203207 * Get String representation of a Type including Generic information
204208 */
205209 public static final String getString(Type type) {
206 if (type instanceof GenericObjectType)
210 if (type instanceof GenericObjectType) {
207211 return ((GenericObjectType) type).toString(true);
208 else if (type instanceof ArrayType)
212 } else if (type instanceof ArrayType) {
209213 return TypeCategory.asString((ArrayType) type);
210 else
214 } else {
211215 return type.toString();
216 }
212217 }
213218
214219 static String stripAngleBrackets(String s) {
215 if (s.indexOf('<') == -1)
220 if (s.indexOf('<') == -1) {
216221 return s;
222 }
217223 StringBuilder result = new StringBuilder(s.length());
218224 int nesting = 0;
219225 boolean seenLeftBracket = false;
222228 if (c == '<') {
223229 nesting++;
224230 seenLeftBracket = true;
225 } else if (c == '>')
231 } else if (c == '>') {
226232 nesting--;
227 else if (nesting == 0) {
228 if (seenLeftBracket && c == '.')
233 } else if (nesting == 0) {
234 if (seenLeftBracket && c == '.') {
229235 result.append('$');
230 else
236 } else {
231237 result.append(c);
238 }
232239 }
233240 }
234241 return result.toString();
251258 public static @CheckForNull
252259 Type getType(String signature) {
253260 try {
254 // ensure signature only has one type
255 if (new GenericSignatureParser("(" + signature + ")V").getNumParameters() != 1)
256 throw new IllegalArgumentException("the following signature does not " + "contain exactly one type: " + signature);
257 int index = 0;
258
259 if (signature.startsWith("L")) {
260 index = lastMatchedLeftAngleBracket(signature);
261 if (index < 0)
262 return Type.getType(stripAngleBrackets(signature));
263
264 String typeParameters = signature.substring(index + 1, nextUnmatchedRightAngleBracket(signature, index + 1));
265 List<ReferenceType> parameters = GenericUtilities.getTypeParameters(typeParameters);
266 if (parameters == null)
261 // ensure signature only has one type
262 if (new GenericSignatureParser("(" + signature + ")V").getNumParameters() != 1) {
263 throw new IllegalArgumentException("the following signature does not " + "contain exactly one type: " + signature);
264 }
265 int index = 0;
266
267 if (signature.startsWith("L")) {
268 index = lastMatchedLeftAngleBracket(signature);
269 if (index < 0) {
270 return Type.getType(stripAngleBrackets(signature));
271 }
272
273 String typeParameters = signature.substring(index + 1, nextUnmatchedRightAngleBracket(signature, index + 1));
274 List<ReferenceType> parameters = GenericUtilities.getTypeParameters(typeParameters);
275 if (parameters == null) {
276 return null;
277 }
278 String baseType = removeMatchedAngleBrackets(signature.substring(1, index)).replace('.', '$');
279 return new GenericObjectType(baseType, parameters);
280
281 } else if (signature.startsWith("T")) {
282 int i = signature.indexOf(';');
283 if (i > 0) {
284 String var = signature.substring(1, i);
285 if (var.indexOf('<') == -1) {
286 return new GenericObjectType(var);
287 }
288 }
289 // can't handle type variables
267290 return null;
268 String baseType = removeMatchedAngleBrackets(signature.substring(1, index)).replace('.', '$');
269 return new GenericObjectType(baseType, parameters);
270
271 } else if (signature.startsWith("T")) {
272 // can't handle type variables
273 return null;
274
275 } else if (signature.startsWith("[")) {
276 index++;
277 while (signature.charAt(index) == '[')
291
292 } else if (signature.startsWith("[")) {
278293 index++;
279 Type componentType = getType(signature.substring(index));
280 if (componentType == null)
281 return null;
282 return new ArrayType(componentType, index);
283
284 } else if (signature.startsWith("*")) {
285 return new GenericObjectType("*");
286
287 } else if (signature.startsWith("+") || signature.startsWith("-")) {
288 Type baseType = getType(signature.substring(1));
289 if (baseType == null)
290 return null;
291 return new GenericObjectType(signature.substring(0, 1), (ReferenceType) baseType);
292
293 } else
294 // assert signature contains no generic information
295 return Type.getType(signature);
294 while (signature.charAt(index) == '[') {
295 index++;
296 }
297 Type componentType = getType(signature.substring(index));
298 if (componentType == null) {
299 return null;
300 }
301 return new ArrayType(componentType, index);
302
303 } else if (signature.startsWith("*")) {
304 return new GenericObjectType("*");
305
306 } else if (signature.startsWith("+") || signature.startsWith("-")) {
307 Type baseType = getType(signature.substring(1));
308 if (baseType == null) {
309 return null;
310 }
311 return new GenericObjectType(signature.substring(0, 1), (ReferenceType) baseType);
312
313 } else {
314 // assert signature contains no generic information
315 return Type.getType(signature);
316 }
296317 } catch (IllegalStateException e) {
297318 AnalysisContext.logError("Error parsing signature " + signature, e);
298319 return null;
300321 }
301322
302323 public static ObjectType merge(@CheckForNull Type t1, ObjectType t2) {
303 if (t1 instanceof GenericObjectType)
324 if (t1 instanceof GenericObjectType) {
304325 return merge((GenericObjectType) t1, t2);
326 }
305327 return t2;
306328 }
307329
308330 public static Type merge(@CheckForNull GenericObjectType t1, Type t2) {
309 if (t1 == null)
331 if (t1 == null) {
310332 return t2;
311 if (t2 instanceof ObjectType)
333 }
334 if (t2 instanceof ObjectType) {
312335 return merge(t1,(ObjectType) t2);
313 if (t2 instanceof NullType)
336 }
337 if (t2 instanceof NullType) {
314338 return t1;
339 }
315340 return t2;
316341 }
317342
318343 public static ObjectType merge(@CheckForNull GenericObjectType t1, ObjectType t2) {
319 if (t1 == null || t2 instanceof GenericObjectType)
344 if (t1 == null || t2 instanceof GenericObjectType) {
320345 return t2;
346 }
321347 List<? extends ReferenceType> parameters = t1.getParameters();
322 if (parameters == null)
348 if (parameters == null) {
323349 return t2;
350 }
324351 return new GenericObjectType(t2.getClassName(), parameters);
325352 }
326353
327354 public static String removeMatchedAngleBrackets(String s) {
328355 int first = s.indexOf('<');
329 if (first < 0)
356 if (first < 0) {
330357 return s;
358 }
331359 StringBuilder result = new StringBuilder(s.substring(0, first));
332360 int pos = first;
333361 int nesting = 0;
334362 while (pos < s.length()) {
335363 char c = s.charAt(pos++);
336 if (c == '<')
364 if (c == '<') {
337365 nesting++;
338 else if (c == '>')
366 } else if (c == '>') {
339367 nesting--;
340 else if (nesting == 0)
368 } else if (nesting == 0) {
341369 result.append(c);
370 }
342371 }
343372 return result.toString();
344373
349378 int pos = startingAt;
350379
351380 while (true) {
352 if (pos < 0)
381 if (pos < 0) {
353382 return -1;
383 }
354384 char c = s.charAt(pos);
355385 if (c == '>') {
356 if (nesting == 0)
386 if (nesting == 0) {
357387 return pos;
388 }
358389 nesting--;
359 } else if (c == '<')
390 } else if (c == '<') {
360391 nesting++;
392 }
361393 pos++;
362394 }
363395 }
367399 int pos = s.length() - 2;
368400
369401 while (true) {
370 if (pos < 0)
402 if (pos < 0) {
371403 return -1;
404 }
372405 char c = s.charAt(pos);
373406 if (c == '<') {
374407 nesting--;
375 if (nesting == 0)
408 if (nesting == 0) {
376409 return pos;
377 } else if (c == '>')
410 }
411 } else if (c == '>') {
378412 nesting++;
379 else if (nesting == 0)
413 } else if (nesting == 0) {
380414 return -1;
415 }
381416 pos--;
382417 }
383418 }
399434 while (iter.hasNext()) {
400435 String parameterString = iter.next();
401436 ReferenceType t = (ReferenceType) getType(parameterString);
402 if (t == null)
437 if (t == null) {
403438 return null;
439 }
404440 types.add(t);
405441 }
406442 return types;
408444
409445 public static final List<String> split(String signature, boolean skipInitialAngleBracket) {
410446 List<String> result = new ArrayList<String>();
411 if (signature.charAt(0) != '<')
447 if (signature.charAt(0) != '<') {
412448 skipInitialAngleBracket = false;
449 }
413450 int depth = 0;
414451 int start = 0;
415 for (int pos = start; pos < signature.length(); pos++)
452 for (int pos = start; pos < signature.length(); pos++) {
416453 switch (signature.charAt(pos)) {
417454 case '<':
418455 depth++;
425462 }
426463 break;
427464 case ';':
428 if (depth > 0)
465 if (depth > 0) {
429466 break;
467 }
430468 String substring = signature.substring(start, pos + 1);
431469 result.add(substring);
432470 start = pos + 1;
433 }
434 if (depth != 0)
471 break;
472 default:
473 break;
474 }
475 }
476 if (depth != 0) {
435477 throw new IllegalArgumentException("Unbalanced signature: " + signature);
478 }
436479 return result;
437480 }
438481
2929 public class FieldSet {
3030 private boolean isTop, isBottom;
3131
32 private Set<XField> fieldSet;
32 private final Set<XField> fieldSet;
3333
3434 public FieldSet() {
3535 fieldSet = new HashSet<XField>();
6767 }
6868
6969 public void addField(XField field) {
70 if (!isValid())
70 if (!isValid()) {
7171 throw new IllegalStateException();
72 }
7273 fieldSet.add(field);
7374 }
7475
7778 }
7879
7980 public void mergeWith(FieldSet other) {
80 if (other.isTop() || this.isBottom())
81 if (other.isTop() || this.isBottom()) {
8182 return;
83 }
8284
8385 if (other.isBottom() || this.isTop()) {
8486 this.copyFrom(other);
101103
102104 public boolean isIntersectionNonEmpty(FieldSet other) {
103105 for (XField field : fieldSet) {
104 if (other.fieldSet.contains(field))
106 if (other.fieldSet.contains(field)) {
105107 return true;
108 }
106109 }
107110 return false;
108111 }
109112
110113 @Override
111114 public String toString() {
112 if (isTop)
115 if (isTop) {
113116 return "TOP";
114 else if (isBottom)
117 } else if (isBottom) {
115118 return "BOTTOM";
116 else
119 } else {
117120 return fieldSet.toString();
121 }
118122 }
119123 }
4141 * @author David Hovemeyer
4242 */
4343 public abstract class FieldSetAnalysis extends ForwardDataflowAnalysis<FieldSet> {
44 private ConstantPoolGen cpg;
44 private final ConstantPoolGen cpg;
4545
46 private Map<InstructionHandle, XField> instructionToFieldMap;
46 private final Map<InstructionHandle, XField> instructionToFieldMap;
4747
4848 public FieldSetAnalysis(DepthFirstSearch dfs, ConstantPoolGen cpg) {
4949 super(dfs);
5555 return cpg;
5656 }
5757
58 @Override
5859 public void makeFactTop(FieldSet fact) {
5960 fact.setTop();
6061 }
6162
63 @Override
6264 public boolean isTop(FieldSet fact) {
6365 return fact.isTop();
6466 }
6567
68 @Override
6669 public void initEntryFact(FieldSet result) throws DataflowAnalysisException {
6770 result.clear();
6871 }
7174 // makeFactTop(result);
7275 // }
7376
77 @Override
7478 public void meetInto(FieldSet fact, Edge edge, FieldSet result) throws DataflowAnalysisException {
7579 result.mergeWith(fact);
7680 }
7781
82 @Override
7883 public boolean same(FieldSet fact1, FieldSet fact2) {
7984 return fact1.sameAs(fact2);
8085 }
8186
87 @Override
8288 public FieldSet createFact() {
8389 return new FieldSet();
8490 }
8894 return fact.isValid();
8995 }
9096
97 @Override
9198 public void copy(FieldSet source, FieldSet dest) {
9299 dest.copyFrom(source);
93100 }
95102 @Override
96103 public void transferInstruction(InstructionHandle handle, BasicBlock basicBlock, FieldSet fact)
97104 throws DataflowAnalysisException {
98 if (!isFactValid(fact))
105 if (!isFactValid(fact)) {
99106 return;
107 }
100108
101109 handleInstruction(handle, basicBlock, fact);
102
110
103111 }
104112
105113 private void handleInstruction(InstructionHandle handle, BasicBlock basicBlock, FieldSet fact)
106 {
114 {
107115 Instruction ins = handle.getInstruction();
108116 short opcode = ins.getOpcode();
109117 XField field;
133141 // possible fields
134142 fact.setBottom();
135143 break;
144 default:
145 break;
136146 }
137147 }
138148
3333
3434 /*
3535 * (non-Javadoc)
36 *
36 *
3737 * @see
3838 * edu.umd.cs.findbugs.ba.heap.FieldSetAnalysis#sawLoad(edu.umd.cs.findbugs
3939 * .ba.heap.FieldSet, edu.umd.cs.findbugs.ba.XField)
4545
4646 /*
4747 * (non-Javadoc)
48 *
48 *
4949 * @see
5050 * edu.umd.cs.findbugs.ba.heap.FieldSetAnalysis#sawStore(edu.umd.cs.findbugs
5151 * .ba.heap.FieldSet, edu.umd.cs.findbugs.ba.XField)
3131
3232 /**
3333 * Interprocedural field property database.
34 *
34 *
3535 * @author David Hovemeyer
3636 */
3737 public abstract class FieldPropertyDatabase<Property> extends PropertyDatabase<FieldDescriptor, Property> {
3838
3939 /*
4040 * (non-Javadoc)
41 *
41 *
4242 * @see
4343 * edu.umd.cs.findbugs.ba.interproc.PropertyDatabase#parseKey(java.lang.
4444 * String)
6666
6767 /*
6868 * (non-Javadoc)
69 *
69 *
7070 * @see
7171 * edu.umd.cs.findbugs.ba.interproc.PropertyDatabase#writeKey(java.io.Writer
7272 * , KeyType)
3131 /**
3232 * A MethodPropertyDatabase keeps track of properties of methods. This is useful
3333 * for implementing interprocedural analyses.
34 *
34 *
3535 * @author David Hovemeyer
3636 */
3737 public abstract class MethodPropertyDatabase<Property> extends PropertyDatabase<MethodDescriptor, Property> {
3939 @Override
4040 protected MethodDescriptor parseKey(String methodStr) throws PropertyDatabaseFormatException {
4141 String[] tuple = methodStr.split(",");
42 if (tuple.length != 4)
42 if (tuple.length != 4) {
4343 throw new PropertyDatabaseFormatException("Invalid method tuple: " + methodStr);
44 }
4445
4546 try {
4647 int accessFlags = Integer.parseInt(tuple[3]);
2323 /**
2424 * Method property recording which parameters are have some property
2525 * (originally, which were required to be nonnull, now made more generic)
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class ParameterProperty {
5151
5252 /**
5353 * Get the non-null param bitset.
54 *
54 *
5555 * @return the non-null param bitset
5656 */
5757 public int getParamsWithProperty() {
6161 public Iterable<Integer> iterable() {
6262 return new Iterable<Integer>() {
6363
64 @Override
6465 public Iterator<Integer> iterator() {
6566 return new Iterator<Integer>() {
6667 int nextInt = 0;
6970 }
7071
7172 private void advanceNextInt() {
72 while (!hasProperty(nextInt) && nextInt < 32)
73 while (!hasProperty(nextInt) && nextInt < 32) {
7374 nextInt++;
74 if (nextInt >= 32)
75 }
76 if (nextInt >= 32) {
7577 nextInt = -1;
76 }
77
78 }
79 }
80
81 @Override
7882 public boolean hasNext() {
7983 return nextInt >= 0;
8084 }
8185
86 @Override
8287 public Integer next() {
8388 int result = nextInt;
8489 nextInt++;
8691 return result;
8792 }
8893
94 @Override
8995 public void remove() {
9096 throw new UnsupportedOperationException();
9197
98104
99105 /**
100106 * Set the non-null param bitset.
101 *
107 *
102108 * @param nonNullParamSet
103109 * the non-null param bitset
104110 */
108114
109115 /**
110116 * Set the non-null param set from given BitSet.
111 *
117 *
112118 * @param nonNullSet
113119 * BitSet indicating which parameters are non-null
114120 */
120126
121127 /**
122128 * Set whether or not a parameter might be non-null.
123 *
129 *
124130 * @param param
125131 * the parameter index
126132 * @param hasProperty
127133 * true if the parameter might be non-null, false otherwise
128134 */
129135 public void setParamWithProperty(int param, boolean hasProperty) {
130 if (param < 0 || param > 31)
136 if (param < 0 || param > 31) {
131137 return;
138 }
132139 if (hasProperty) {
133140 bits |= (1 << param);
134141 } else {
138145
139146 /**
140147 * Return whether or not a parameter might be non-null.
141 *
148 *
142149 * @param param
143150 * the parameter index
144151 * @return true if the parameter might be non-null, false otherwise
145152 */
146153 public boolean hasProperty(int param) {
147 if (param < 0 || param > 31)
154 if (param < 0 || param > 31) {
148155 return false;
149 else
156 } else {
150157 return (bits & (1 << param)) != 0;
158 }
151159 }
152160
153161 /**
154162 * Given a bitset of null arguments passed to the method represented by this
155163 * property, return a bitset indicating which null arguments correspond to
156164 * an non-null param.
157 *
165 *
158166 * @param nullArgSet
159167 * bitset of null arguments
160168 * @return bitset intersecting null arguments and non-null params
169177
170178 public BitSet getAsBitSet() {
171179 BitSet result = new BitSet();
172 if (isEmpty())
180 if (isEmpty()) {
173181 return result;
182 }
174183 for (int i = 0; i < 32; ++i) {
175184 result.set(i, hasProperty(i));
176185 }
179188
180189 /**
181190 * Return whether or not the set of non-null parameters is empty.
182 *
191 *
183192 * @return true if the set is empty, false if it contains at least one
184193 * parameter
185194 */
194203 buf.append('{');
195204 for (int i = 0; i < 32; ++i) {
196205 if (hasProperty(i)) {
197 if (buf.length() > 1)
206 if (buf.length() > 1) {
198207 buf.append(',');
208 }
199209 buf.append(i);
200210 }
201211 }
207217 /**
208218 * Intersect this set with the given set. Useful for summarizing the
209219 * properties of multiple methods.
210 *
220 *
211221 * @param targetDerefParamSet
212222 * another set
213223 */
217227
218228 /**
219229 * Make this object the same as the given one.
220 *
230 *
221231 * @param other
222232 * another ParameterNullnessProperty
223233 */
5252 * @author David Hovemeyer
5353 */
5454 public abstract class PropertyDatabase<KeyType extends FieldOrMethodDescriptor, ValueType> {
55 private Map<KeyType, ValueType> propertyMap;
55 private final Map<KeyType, ValueType> propertyMap;
5656
5757 /**
5858 * Constructor. Creates an empty property database.
143143 String line;
144144 while ((line = reader.readLine()) != null) {
145145 line = line.trim();
146 if (line.equals(""))
146 if ("".equals(line)) {
147147 continue;
148 }
148149 int bar = line.indexOf('|');
149150 if (bar < 0) {
150151 throw new PropertyDatabaseFormatException("Invalid property database: missing separator");
156157 }
157158 } finally {
158159 try {
159 if (reader != null)
160 if (reader != null) {
160161 reader.close();
162 }
161163 } catch (IOException e) {
162164 // Ignore
163165 }
221223 AnalysisContext.currentAnalysisContext().setMissingClassWarningsSuppressed(missingClassWarningsSuppressed);
222224
223225 try {
224 if (writer != null)
226 if (writer != null) {
225227 writer.close();
228 }
226229 } catch (IOException e) {
227230 // Ignore
228231 }
3636 import edu.umd.cs.findbugs.ba.SignatureParser;
3737 import edu.umd.cs.findbugs.ba.XFactory;
3838 import edu.umd.cs.findbugs.ba.XMethod;
39 import edu.umd.cs.findbugs.ba.ch.InheritanceGraphVisitor;
4039 import edu.umd.cs.findbugs.ba.ch.OverriddenMethodsVisitor;
40 import edu.umd.cs.findbugs.ba.ch.SupertypeTraversalVisitor;
4141 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
4242 import edu.umd.cs.findbugs.classfile.Global;
4343 import edu.umd.cs.findbugs.classfile.IAnalysisCache;
121121 // Instance method - must consider type qualifiers inherited
122122 // from superclasses
123123
124 InheritanceGraphVisitor visitor = new OverriddenMethodsVisitor(xmethod) {
124 SupertypeTraversalVisitor visitor = new OverriddenMethodsVisitor(xmethod) {
125125 /*
126126 * (non-Javadoc)
127127 *
138138
139139 try {
140140 AnalysisContext.currentAnalysisContext().getSubtypes2()
141 .traverseSupertypes(xmethod.getClassDescriptor(), visitor);
141 .traverseSupertypesDepthFirst(xmethod.getClassDescriptor(), visitor);
142142 } catch (ClassNotFoundException e) {
143143 AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportMissingClass(e);
144144 return Collections.<TypeQualifierValue<?>> emptySet();
145145 } catch (UncheckedAnalysisException e) {
146146 AnalysisContext.currentAnalysisContext().getLookupFailureCallback()
147 .logError("Error getting relevant type qualifiers for " + xmethod.toString(), e);
147 .logError("Error getting relevant type qualifiers for " + xmethod.toString(), e);
148148 return Collections.<TypeQualifierValue<?>> emptySet();
149149 }
150150 }
2222
2323 /**
2424 * Dataflow class for BackwardTypeQualifierDataflowAnalysis.
25 *
25 *
2626 * @author David Hovemeyer
2727 */
2828 public class BackwardTypeQualifierDataflow extends TypeQualifierDataflow<BackwardTypeQualifierDataflowAnalysis> {
2929
3030 /**
3131 * Constructor.
32 *
32 *
3333 * @param cfg
3434 * CFG for analyzed method
3535 * @param analysis
138138 }
139139 }
140140
141 @Override
141142 public BlockOrder getBlockOrder(CFG cfg1) {
142143 return new ReverseDFSOrder(cfg1, rdfs, dfs);
143144 }
144145
146 @Override
145147 public boolean isForwards() {
146148 return false;
147149 }
212214 XMethod calledMethod = XFactory.createXMethod(inv, cpg);
213215
214216 SignatureParser sigParser = new SignatureParser(calledMethod.getSignature());
215 if (sigParser.getNumParameters() == 0)
216 return;
217 if (sigParser.getNumParameters() == 0) {
218 return;
219 }
217220 ValueNumberFrame vnaFrame = vnaDataflow.getFactAtLocation(location);
218221
219222 if (!vnaFrame.isValid()) {
223226 return;
224227 }
225228
226 if (TypeQualifierDataflowAnalysis.isIdentifyFunctionForTypeQualifiers(calledMethod))
227 return;
229 if (TypeQualifierDataflowAnalysis.isIdentifyFunctionForTypeQualifiers(calledMethod)) {
230 return;
231 }
228232
229233 for (int param = 0; param < calledMethod.getNumParams(); param++) {
230234 TypeQualifierAnnotation tqa = TypeQualifierApplications.getEffectiveTypeQualifierAnnotation(calledMethod, param,
4242 * @author David Hovemeyer
4343 */
4444 public class BackwardTypeQualifierDataflowFactory extends
45 TypeQualifierDataflowFactory<BackwardTypeQualifierDataflowAnalysis, BackwardTypeQualifierDataflow> {
45 TypeQualifierDataflowFactory<BackwardTypeQualifierDataflowAnalysis, BackwardTypeQualifierDataflow> {
4646
4747 /**
4848 * Constructor.
104104 // back to the entry of the method.
105105 // This will contain the effective type qualifier
106106 // annotations on the method parameters.
107 if (xmethod.isIdentity())
107 if (xmethod.isIdentity()) {
108108 return;
109 }
109110
110111 BasicBlock entry = dataflow.getCFG().getEntry();
111112 TypeQualifierValueSet entryFact = dataflow.getAnalysis().getResultFact(entry);
5656 */
5757 public Collection<TypeQualifierValue<?>> getDirectlyRelevantTypeQualifiers(MethodDescriptor m) {
5858 Collection<TypeQualifierValue<?>> result = methodToDirectlyRelevantQualifiersMap.get(m);
59 if (result != null)
59 if (result != null) {
6060 return result;
61 }
6162 return Collections.<TypeQualifierValue<?>> emptyList();
6263 }
6364
2727
2828 /**
2929 * FindBugs-specific default-annotation annotations. I.e.:
30 *
30 *
3131 * <pre>
3232 * {@literal @DefaultAnnotationForParameters(Nonnull.class)}
3333 * public class MyClass {
3434 * ...
3535 * }
3636 * </pre>
37 *
37 *
3838 * @author David Hovemeyer
3939 */
4040 @Deprecated
4343 /** Default annotation for all element types. */
4444 public static final ClassDescriptor DEFAULT_ANNOTATION = DescriptorFactory.instance().getClassDescriptor(
4545 DefaultAnnotation.class);
46
46
4747
4848 /** Default annotation for fields. */
4949 public static final ClassDescriptor DEFAULT_ANNOTATION_FOR_FIELDS = DescriptorFactory.instance()
5656 /** Default annotation for parameters. */
5757 public static final ClassDescriptor DEFAULT_ANNOTATION_FOR_PARAMETERS = DescriptorFactory.instance()
5858 .getClassDescriptor(DefaultAnnotationForParameters.class);
59
59
6060
6161 }
2222
2323 /**
2424 * Flow value type for type qualifier dataflow analysis.
25 *
25 *
2626 * @author David Hovemeyer
2727 */
2828 public enum FlowValue {
6464 // Unknown
6565 //
6666 private static final FlowValue[][] mergeMatrix = {
67 // TOP ALWAYS NEVER UNKNOWN
68 /* TOP */{ TOP, },
69 /* ALWAYS */{ ALWAYS, ALWAYS, },
70 /* NEVER */{ NEVER, UNKNOWN, NEVER, },
71 /* UNKNOWN */{ UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN }, };
67 // TOP ALWAYS NEVER UNKNOWN
68 /* TOP */{ TOP, },
69 /* ALWAYS */{ ALWAYS, ALWAYS, },
70 /* NEVER */{ NEVER, UNKNOWN, NEVER, },
71 /* UNKNOWN */{ UNKNOWN, UNKNOWN, UNKNOWN, UNKNOWN }, };
7272
7373 public static final FlowValue meet(FlowValue a, FlowValue b) {
7474 int aIndex = a.ordinal();
8585
8686 /**
8787 * Determine whether given flow values conflict.
88 * @param typeQualifierValue TODO
8988 * @param forward
9089 * a forwards flow value
9190 * @param backward
9291 * a backwards flow value
93 *
92 *
9493 * @return true if values conflict, false otherwise
9594 */
9695 public static boolean valuesConflict(boolean strictChecking, FlowValue forward, FlowValue backward) {
9796 if (forward == TOP || backward == TOP || backward == UNKNOWN || forward == backward) {
9897 return false;
9998 }
100
101 if (strictChecking)
99
100 if (strictChecking) {
102101 return true;
103
102 }
103
104104 return (forward == ALWAYS && backward == NEVER) || (forward == NEVER && backward == ALWAYS);
105105 }
106106
107107 /**
108108 * Convert a When value to a FlowValue value.
109 *
109 *
110110 * @param when
111111 * a When value
112112 * @return the corresponding FlowValue
128128
129129 /**
130130 * Determine whether given backwards FlowValue conflicts with given source.
131 *
131 *
132132 * @param backwardsFlowValue
133133 * a backwards FlowValue
134134 * @param source
2222
2323 /**
2424 * Dataflow object for ForwardTypeQualifierDataflowAnalysis.
25 *
25 *
2626 * @author David Hovemeyer
2727 */
2828 public class ForwardTypeQualifierDataflow extends TypeQualifierDataflow<ForwardTypeQualifierDataflowAnalysis> {
2929
3030 /**
3131 * Constructor.
32 *
32 *
3333 * @param cfg
3434 * CFG of analyzed method
3535 * @param analysis
8181 this.dfs = dfs;
8282 }
8383
84 @Override
8485 public BlockOrder getBlockOrder(CFG cfg1) {
8586 return new ReversePostOrder(cfg1, dfs);
8687 }
8788
89 @Override
8890 public boolean isForwards() {
8991 return true;
9092 }
147149 When w;
148150 if (typeQualifierValue.canValidate(constantValue)) {
149151 w = typeQualifierValue.validate(constantValue);
150 } else if (typeQualifierValue.isStrictQualifier())
151 return;
152 else
152 } else if (typeQualifierValue.isStrictQualifier()) {
153 return;
154 } else {
153155 w = When.UNKNOWN;
156 }
154157
155158 registerTopOfStackSource(SourceSinkType.CONSTANT_VALUE, location, w, false, constantValue);
156159 }
174177 }
175178
176179 XMethod calledXMethod = XFactory.createXMethod(inv, cpg);
177 if (TypeQualifierDataflowAnalysis.isIdentifyFunctionForTypeQualifiers(calledXMethod))
178 return;
180 if (TypeQualifierDataflowAnalysis.isIdentifyFunctionForTypeQualifiers(calledXMethod)) {
181 return;
182 }
179183
180184 if (calledXMethod.isResolved()) {
181185 TypeQualifierAnnotation tqa = TypeQualifierApplications.getEffectiveTypeQualifierAnnotation(calledXMethod,
210214
211215 private void registerTopOfStackSource(SourceSinkType sourceSinkType, Location location, When when, boolean interproc,
212216 @CheckForNull Object constantValue) throws DataflowAnalysisException {
213 if (when == When.UNKNOWN && !typeQualifierValue.isStrictQualifier())
214 return;
217 if (when == When.UNKNOWN && !typeQualifierValue.isStrictQualifier()) {
218 return;
219 }
215220 ValueNumberFrame vnaFrameAfterInstruction = vnaDataflow.getFactAfterLocation(location);
216221 if (vnaFrameAfterInstruction.isValid()) {
217222 ValueNumber tosValue = vnaFrameAfterInstruction.getTopValue();
4949 * @author David Hovemeyer
5050 */
5151 public class ForwardTypeQualifierDataflowFactory extends
52 TypeQualifierDataflowFactory<ForwardTypeQualifierDataflowAnalysis, ForwardTypeQualifierDataflow> {
52 TypeQualifierDataflowFactory<ForwardTypeQualifierDataflowAnalysis, ForwardTypeQualifierDataflow> {
5353
5454 /**
5555 * Constructor.
2323
2424 /**
2525 * ClassDescriptors for JSR-305 nullness annotations.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public abstract class JSR305NullnessAnnotations {
2121 /**
2222 * TypeQualifierAnnotationLookupResult summarizing TypeQualifierAnnotation(s)
2323 * relevant for a method parameter.
24 *
24 *
2525 * @author David Hovemeyer
2626 */
2727 class ParameterAnnotationLookupResult extends TypeQualifierAnnotationLookupResult {
2828 /*
2929 * (non-Javadoc)
30 *
30 *
3131 * @see
3232 * edu.umd.cs.findbugs.ba.jsr305.TypeQualifierAnnotationLookupResult#combine
3333 * (edu.umd.cs.findbugs.ba.jsr305.TypeQualifierAnnotation,
2323 /**
2424 * Accumulate relevant return type annotations for a given method by traversing
2525 * its supertypes.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 class ReturnTypeAnnotationAccumulator extends AbstractMethodAnnotationAccumulator {
30 private TypeQualifierAnnotationLookupResult result;
30 private final TypeQualifierAnnotationLookupResult result;
3131
3232 private boolean overrides = false;
3333
3434 /**
3535 * Constructor.
36 *
36 *
3737 * @param typeQualifierValue
3838 * TypeQualifierValue specifying the kind of annotation we want
3939 * to look up
4040 * @param xmethod
4141 * method whose effective return type annotation we want
4242 */
43 public ReturnTypeAnnotationAccumulator(TypeQualifierValue typeQualifierValue, XMethod xmethod) {
43 public ReturnTypeAnnotationAccumulator(TypeQualifierValue<?> typeQualifierValue, XMethod xmethod) {
4444 super(typeQualifierValue, xmethod);
4545 this.result = new ReturnTypeAnnotationLookupResult();
4646 }
2121 /**
2222 * TypeQualifierAnnotationLookupResult summarizing TypeQualifierAnnotation(s)
2323 * relevant for a method return type.
24 *
24 *
2525 * @author David Hovemeyer
2626 */
2727 class ReturnTypeAnnotationLookupResult extends TypeQualifierAnnotationLookupResult {
2828 /*
2929 * (non-Javadoc)
30 *
30 *
3131 * @see
3232 * edu.umd.cs.findbugs.ba.jsr305.TypeQualifierAnnotationLookupResult#combine
3333 * (edu.umd.cs.findbugs.ba.jsr305.TypeQualifierAnnotation,
2525
2626 /**
2727 * Information about a source or sink in the type qualifier dataflow analysis.
28 *
28 *
2929 * @author David Hovemeyer
3030 */
3131 public class SourceSinkInfo implements Comparable<SourceSinkInfo> {
4747
4848 /**
4949 * Constructor.
50 *
50 *
5151 * @param type
5252 * type of the source or sink
5353 * @param location
130130 * Set the SourceSinkInfo as having been created based on the results of
131131 * type qualifiers computed for a called method (and not explicitly
132132 * annotating the called method).
133 *
133 *
134134 * @param interproc
135135 * true if the SourceSinkInfo results from computed type
136136 * qualifiers for a called method, false otherwise
143143 * Return whether or not the SourceSinkInfo was created based on the results
144144 * of type qualifiers computed for a called method (and not explicitly
145145 * annotating the called method).
146 *
146 *
147147 * @return true if the SourceSinkInfo results from computed type qualifiers
148148 * for a called method, false otherwise
149149 */
153153
154154 /*
155155 * (non-Javadoc)
156 *
156 *
157157 * @see java.lang.Comparable#compareTo(java.lang.Object)
158158 */
159 @Override
159160 public int compareTo(SourceSinkInfo o) {
160161 return this.location.compareTo(o.location);
161162 }
185186
186187 @Override
187188 public boolean equals(Object obj) {
188 if (this == obj)
189 if (this == obj) {
189190 return true;
190 if (obj == null)
191 return false;
192 if (getClass() != obj.getClass())
193 return false;
191 }
192 if (obj == null) {
193 return false;
194 }
195 if (getClass() != obj.getClass()) {
196 return false;
197 }
194198 SourceSinkInfo other = (SourceSinkInfo) obj;
195199 if (constantValue == null) {
196 if (other.constantValue != null)
200 if (other.constantValue != null) {
197201 return false;
198 } else if (!constantValue.equals(other.constantValue))
199 return false;
200 if (interproc != other.interproc)
201 return false;
202 if (local != other.local)
203 return false;
202 }
203 } else if (!constantValue.equals(other.constantValue)) {
204 return false;
205 }
206 if (interproc != other.interproc) {
207 return false;
208 }
209 if (local != other.local) {
210 return false;
211 }
204212 if (location == null) {
205 if (other.location != null)
213 if (other.location != null) {
206214 return false;
207 } else if (!location.equals(other.location))
208 return false;
209 if (parameter != other.parameter)
210 return false;
211 if (type != other.type)
212 return false;
215 }
216 } else if (!location.equals(other.location)) {
217 return false;
218 }
219 if (parameter != other.parameter) {
220 return false;
221 }
222 if (type != other.type) {
223 return false;
224 }
213225 if (vn == null) {
214 if (other.vn != null)
226 if (other.vn != null) {
215227 return false;
216 } else if (!vn.equals(other.vn))
217 return false;
218 if (when != other.when)
219 return false;
228 }
229 } else if (!vn.equals(other.vn)) {
230 return false;
231 }
232 if (when != other.when) {
233 return false;
234 }
220235 return true;
221236 }
222237
223238 /*
224239 * (non-Javadoc)
225 *
240 *
226241 * @see java.lang.Object#toString()
227242 */
228243 @Override
9292 private static final When TOP = null;
9393
9494 private static final When[][] combineReturnValueMatrix = {
95 // ALWAYS UNKNOWN MAYBE NEVER
96 /* ALWAYS */{ When.ALWAYS, },
97 /* UNKNOWN */{ When.ALWAYS, When.UNKNOWN, },
98 /* MAYBE */{ When.ALWAYS, When.UNKNOWN, When.MAYBE, },
99 /* NEVER */{ TOP, TOP, When.NEVER, When.NEVER }, };
95 // ALWAYS UNKNOWN MAYBE NEVER
96 /* ALWAYS */{ When.ALWAYS, },
97 /* UNKNOWN */{ When.ALWAYS, When.UNKNOWN, },
98 /* MAYBE */{ When.ALWAYS, When.UNKNOWN, When.MAYBE, },
99 /* NEVER */{ TOP, TOP, When.NEVER, When.NEVER }, };
100100
101101 private static final When[][] combineParameterMatrix = {
102 // ALWAYS UNKNOWN MAYBE NEVER
103 /* ALWAYS */{ When.ALWAYS, },
104 /* UNKNOWN */{ When.UNKNOWN, When.UNKNOWN, },
105 /* MAYBE */{ When.MAYBE, When.MAYBE, When.MAYBE, },
106 /* NEVER */{ When.MAYBE, When.UNKNOWN, When.MAYBE, When.NEVER }, };
102 // ALWAYS UNKNOWN MAYBE NEVER
103 /* ALWAYS */{ When.ALWAYS, },
104 /* UNKNOWN */{ When.UNKNOWN, When.UNKNOWN, },
105 /* MAYBE */{ When.MAYBE, When.MAYBE, When.MAYBE, },
106 /* NEVER */{ When.MAYBE, When.UNKNOWN, When.MAYBE, When.NEVER }, };
107107
108108 /**
109109 * Combine return type annotations.
168168 TypeQualifierAnnotation getValue(TypeQualifierValue<?> desc, When when) {
169169 DualKeyHashMap<TypeQualifierValue<?>, When, TypeQualifierAnnotation> map = instance.get();
170170 TypeQualifierAnnotation result = map.get(desc, when);
171 if (result != null)
171 if (result != null) {
172172 return result;
173 }
173174 result = new TypeQualifierAnnotation(desc, when);
174175 map.put(desc, when, result);
175176 return result;
182183
183184 @Override
184185 public boolean equals(Object o) {
185 if (!(o instanceof TypeQualifierAnnotation))
186 if (!(o instanceof TypeQualifierAnnotation)) {
186187 return false;
188 }
187189 TypeQualifierAnnotation other = (TypeQualifierAnnotation) o;
188190 return typeQualifier.equals(other.typeQualifier) && when.equals(other.when);
189191 }
3333 * on the annotated entity. This object makes it possible to report such
3434 * conflicts, while still providing a convenient interface for getting the
3535 * "effective" TypeQualifierAnnotation.
36 *
36 *
3737 * @author David Hovemeyer
3838 */
3939 public class TypeQualifierAnnotationLookupResult {
4141 * Partial result of looking up a TypeQualifierAnnotation.
4242 */
4343 public static class PartialResult {
44 private AnnotatedObject annotatedObject;
44 private final AnnotatedObject annotatedObject;
4545
46 private TypeQualifierAnnotation typeQualifierAnnotation;
46 private final TypeQualifierAnnotation typeQualifierAnnotation;
4747
4848 PartialResult(AnnotatedObject annotatedObject, TypeQualifierAnnotation typeQualifierAnnotation) {
4949 this.annotatedObject = annotatedObject;
6666
6767 /*
6868 * (non-Javadoc)
69 *
69 *
7070 * @see java.lang.Object#toString()
7171 */
7272 @Override
7575 }
7676 }
7777
78 private List<PartialResult> partialResultList;
78 private final List<PartialResult> partialResultList;
7979
8080 TypeQualifierAnnotationLookupResult() {
8181 this.partialResultList = new LinkedList<PartialResult>();
8787
8888 /**
8989 * Get the effective TypeQualifierAnnotation.
90 *
90 *
9191 * @return the effective TypeQualifierAnnotation, or null if no effective
9292 * TypeQualifierAnnotation can be found
9393 */
111111 /**
112112 * Subclasses must override this method to combine TypeQualifierAnnotations
113113 * found in multiple superclasses.
114 *
114 *
115115 * @param a
116116 * a TypeQualifierAnnotation
117117 * @param b
126126
127127 /*
128128 * (non-Javadoc)
129 *
129 *
130130 * @see java.lang.Object#toString()
131131 */
132132 @Override
146146 */
147147 private static Collection<AnnotationValue> getDirectAnnotation(AnnotatedObject m) {
148148 Collection<AnnotationValue> result = getDirectObjectAnnotations().get(m);
149 if (result != null)
149 if (result != null) {
150150 return result;
151 if (m.getAnnotationDescriptors().isEmpty())
151 }
152 if (m.getAnnotationDescriptors().isEmpty()) {
152153 return Collections.<AnnotationValue> emptyList();
154 }
153155 result = TypeQualifierResolver.resolveTypeQualifiers(m.getAnnotations());
154 if (result.size() == 0)
156 if (result.size() == 0) {
155157 result = Collections.<AnnotationValue> emptyList();
158 }
156159 getDirectObjectAnnotations().put(m, result);
157160 return result;
158161 }
173176 if (map == null) {
174177 int n = m.getNumParams();
175178 if (m.isVarArgs())
179 {
176180 n--; // ignore annotations on varargs parameters
181 }
177182 map = new HashMap<Integer, Collection<AnnotationValue>>(n + 2);
178183 for (int i = 0; i < n; i++) {
179184 Collection<AnnotationValue> a = TypeQualifierResolver.resolveTypeQualifiers(m.getParameterAnnotations(i));
180 if (!a.isEmpty())
185 if (!a.isEmpty()) {
181186 map.put(i, a);
182 }
183 if (map.isEmpty())
187 }
188 }
189 if (map.isEmpty()) {
184190 map = Collections.emptyMap();
191 }
185192 directParameterAnnotations.put(m, map);
186193 }
187194
188195 Collection<AnnotationValue> result = map.get(parameter);
189 if (result != null)
196 if (result != null) {
190197 return result;
198 }
191199 return Collections.emptyList();
192200 }
193201
204212 */
205213 public static void getDirectApplications(Set<TypeQualifierAnnotation> result, XMethod o, int parameter) {
206214 Collection<AnnotationValue> values = getDirectAnnotation(o, parameter);
207 for (AnnotationValue v : values)
215 for (AnnotationValue v : values) {
208216 constructTypeQualifierAnnotation(result, v);
217 }
209218
210219 }
211220
221230 * ElementType representing kind of annotated object
222231 */
223232 public static void getDirectApplications(Set<TypeQualifierAnnotation> result, AnnotatedObject o, ElementType e) {
224 if (!o.getElementType().equals(e))
233 if (!o.getElementType().equals(e)) {
225234 return;
235 }
226236 Collection<AnnotationValue> values = getDirectAnnotation(o);
227 for (AnnotationValue v : values)
237 for (AnnotationValue v : values) {
228238 constructTypeQualifierAnnotation(result, v);
239 }
229240
230241 }
231242
276287 private static void getApplicableScopedApplications(Set<TypeQualifierAnnotation> result, AnnotatedObject o, ElementType e) {
277288 if (!o.isSynthetic()) {
278289 AnnotatedObject outer = o.getContainingScope();
279 if (outer != null)
290 if (outer != null) {
280291 getApplicableScopedApplications(result, outer, e);
292 }
281293 }
282294 getDirectApplications(result, o, e);
283295 }
433445 break;
434446 default:
435447 // ignore
448 break;
436449 }
437450
438451 // Try out default JDT (Eclipse) annotations
486499 if (!(obj instanceof Type)) {
487500 if (DEBUG_DEFAULT_ANNOTATION) {
488501 System.out
489 .println("Found a non-Type value in value array of " + defaultAnnotation.toString() + " annotation");
502 .println("Found a non-Type value in value array of " + defaultAnnotation.toString() + " annotation");
490503 }
491504 continue;
492505 }
506519 AnnotationValue annotation = new AnnotationValue(typeDesc);
507520 Collection<AnnotationValue> resolvedTypeQualifiers = TypeQualifierResolver.resolveTypeQualifiers(annotation);
508521 TypeQualifierAnnotation tqa = extractAnnotation(resolvedTypeQualifiers, typeQualifierValue);
509 if (tqa != null)
522 if (tqa != null) {
510523 return tqa;
524 }
511525
512526 }
513527
563577 final AnnotatedObject o2 = o;
564578 if (CHECK_EXCLUSIVE && tqa == null && typeQualifierValue.isExclusiveQualifier()) {
565579 tqa = computeExclusiveQualifier(typeQualifierValue, new ComputeEffectiveTypeQualifierAnnotation() {
580 @Override
566581 public TypeQualifierAnnotation compute(TypeQualifierValue<?> tqv) {
567582 return computeEffectiveTypeQualifierAnnotation(tqv, o2);
568583 }
604619
605620 // If it's an instance method, check for an inherited annotation
606621 if (tqa == null && (o instanceof XMethod) && !((XMethod) o).isStatic() && !((XMethod) o).isPrivate()
607 && !((XMethod) o).getName().equals("<init>")) {
622 && !"<init>".equals(((XMethod) o).getName())) {
608623 tqa = getInheritedTypeQualifierAnnotation((XMethod) o, typeQualifierValue);
609624 }
610625
670685
671686 ReturnTypeAnnotationAccumulator accumulator = new ReturnTypeAnnotationAccumulator(typeQualifierValue, o);
672687 try {
673 AnalysisContext.currentAnalysisContext().getSubtypes2().traverseSupertypes(o.getClassDescriptor(), accumulator);
688 AnalysisContext.currentAnalysisContext().getSubtypes2().traverseSupertypesDepthFirst(o.getClassDescriptor(), accumulator);
674689 TypeQualifierAnnotation result = accumulator.getResult().getEffectiveTypeQualifierAnnotation();
675 if (result == null && accumulator.overrides())
690 if (result == null && accumulator.overrides()) {
676691 return TypeQualifierAnnotation.OVERRIDES_BUT_NO_ANNOTATION;
692 }
677693 return result;
678694 } catch (ClassNotFoundException e) {
679695 AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportMissingClass(e);
696712 TypeQualifierValue<?> typeQualifierValue, boolean stopAtClassScope) {
697713
698714 if (o.isSynthetic())
715 {
699716 return null; // synthetic objects don't get default annotations
717 }
700718
701719 ElementType elementType = o.getElementType();
702720 while (true) {
703721 o = o.getContainingScope();
704 if (o == null)
722 if (o == null) {
705723 return null;
706 if (stopAtClassScope && o instanceof XClass)
724 }
725 if (stopAtClassScope && o instanceof XClass) {
707726 return null;
727 }
708728 TypeQualifierAnnotation result;
709729
710730 // Check direct applications of the type qualifier
714734 if (result != null) {
715735 // Great - found an outer scope with a relevant annotation
716736 assert false : "I don't think we should be looking here";
717 return result;
737 return result;
718738 }
719739
720740 // Check default annotations
747767
748768 if (CHECK_EXCLUSIVE && tqa == null && typeQualifierValue.isExclusiveQualifier()) {
749769 tqa = computeExclusiveQualifier(typeQualifierValue, new ComputeEffectiveTypeQualifierAnnotation() {
770 @Override
750771 public TypeQualifierAnnotation compute(TypeQualifierValue<?> tqv) {
751772 return computeEffectiveTypeQualifierAnnotation(tqv, xmethod, parameter);
752773 }
815836 }
816837 }
817838 else {
818 // Check direct application
819 if (DEBUG) {
820 System.out.print(" (1) Checking direct application...");
821 }
822 tqa = getDirectTypeQualifierAnnotation(xmethod, parameter, typeQualifierValue);
823 if (DEBUG) {
824 System.out.println(tqa != null ? "FOUND" : "none");
825 }
826
827 // If it's an instance method, check for inherited annotation
828 if (tqa == null && !xmethod.isStatic() && !xmethod.isPrivate() && !xmethod.getName().equals("<init>")) {
839 // Check direct application
829840 if (DEBUG) {
830 System.out.print(" (2) Checking inherited...");
831 }
832 tqa = getInheritedTypeQualifierAnnotation(xmethod, parameter, typeQualifierValue);
841 System.out.print(" (1) Checking direct application...");
842 }
843 tqa = getDirectTypeQualifierAnnotation(xmethod, parameter, typeQualifierValue);
833844 if (DEBUG) {
834 if (tqa == TypeQualifierAnnotation.OVERRIDES_BUT_NO_ANNOTATION)
835 System.out.println("Overrides, no annotation inherited");
836 else if (tqa != null)
837 System.out.println("Inherited " + tqa.when);
838 else
839 System.out.println("Nothing inherited");
840 }
841 }
842 boolean overriddenMethod = false;
843 if (tqa == TypeQualifierAnnotation.OVERRIDES_BUT_NO_ANNOTATION) {
844 overriddenMethod = true;
845 tqa = null;
846 }
847 // Check for default (outer scope) annotation
848 if (tqa == null) {
849 if (xmethod.isVariableSynthetic((xmethod.isStatic() ? 0 : 1) + parameter)) {
845 System.out.println(tqa != null ? "FOUND" : "none");
846 }
847
848 // If it's an instance method, check for inherited annotation
849 if (tqa == null && !xmethod.isStatic() && !xmethod.isPrivate() && !"<init>".equals(xmethod.getName())) {
850850 if (DEBUG) {
851 System.out.print(" (3) Skipping default for synthetic parameter");
851 System.out.print(" (2) Checking inherited...");
852852 }
853 } else {
853 tqa = getInheritedTypeQualifierAnnotation(xmethod, parameter, typeQualifierValue);
854854 if (DEBUG) {
855 System.out.print(" (3) Checking default...");
855 if (tqa == TypeQualifierAnnotation.OVERRIDES_BUT_NO_ANNOTATION) {
856 System.out.println("Overrides, no annotation inherited");
857 } else if (tqa != null) {
858 System.out.println("Inherited " + tqa.when);
859 } else {
860 System.out.println("Nothing inherited");
861 }
856862 }
857
858 tqa = getDefaultTypeQualifierAnnotationForParameters(xmethod, typeQualifierValue, overriddenMethod);
859
860 if (DEBUG) {
861 System.out.println(tqa != null ? "FOUND" : "none");
863 }
864 boolean overriddenMethod = false;
865 if (tqa == TypeQualifierAnnotation.OVERRIDES_BUT_NO_ANNOTATION) {
866 overriddenMethod = true;
867 tqa = null;
868 }
869 // Check for default (outer scope) annotation
870 if (tqa == null) {
871 if (xmethod.isVariableSynthetic((xmethod.isStatic() ? 0 : 1) + parameter)) {
872 if (DEBUG) {
873 System.out.print(" (3) Skipping default for synthetic parameter");
874 }
875 } else {
876 if (DEBUG) {
877 System.out.print(" (3) Checking default...");
878 }
879
880 tqa = getDefaultTypeQualifierAnnotationForParameters(xmethod, typeQualifierValue, overriddenMethod);
881
882 if (DEBUG) {
883 System.out.println(tqa != null ? "FOUND" : "none");
884 }
862885 }
863886 }
864 }
865887 }
866888
867889 // Cache answer
869891 map.put(xmethod, parameter, result);
870892
871893 if (DEBUG) {
872 if (result == null)
894 if (result == null) {
873895 System.out.println(" => Answer: no annotation on parameter " + parameter + " of " + xmethod);
874 else
896 } else {
875897 System.out.println(" => Answer: " + result.when + " on parameter " + parameter + " of " + xmethod);
898 }
876899 }
877900 }
878901
900923 public static @CheckForNull @CheckReturnValue
901924 TypeQualifierAnnotation getDirectTypeQualifierAnnotation(XMethod xmethod, int parameter, TypeQualifierValue<?> typeQualifierValue) {
902925 XMethod bridge = xmethod.bridgeTo();
903 if (bridge != null)
926 if (bridge != null) {
904927 xmethod = bridge;
928 }
905929 Set<TypeQualifierAnnotation> applications = new HashSet<TypeQualifierAnnotation>();
906930 getDirectApplications(applications, xmethod, parameter);
907931 if (DEBUG_METHOD != null && DEBUG_METHOD.equals(xmethod.getName())) {
931955
932956 ParameterAnnotationAccumulator accumulator = new ParameterAnnotationAccumulator(typeQualifierValue, xmethod, parameter);
933957 try {
934 AnalysisContext.currentAnalysisContext().getSubtypes2().traverseSupertypes(xmethod.getClassDescriptor(), accumulator);
958 AnalysisContext.currentAnalysisContext().getSubtypes2().traverseSupertypesDepthFirst(xmethod.getClassDescriptor(), accumulator);
935959 TypeQualifierAnnotation result = accumulator.getResult().getEffectiveTypeQualifierAnnotation();
936 if (result == null && accumulator.overrides())
960 if (result == null && accumulator.overrides()) {
937961 return TypeQualifierAnnotation.OVERRIDES_BUT_NO_ANNOTATION;
962 }
938963 return result;
939964 } catch (ClassNotFoundException e) {
940965 AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportMissingClass(e);
951976 * @param typeQualifierValue
952977 * the kind of TypeQualifierValue we are looking for
953978 * @param stopAtMethodScope
954 * TODO
955979 * @return the default (outer scope) TypeQualifierAnnotation on the
956980 * parameter, or null if there is no default TypeQualifierAnnotation
957981 */
960984 TypeQualifierValue<?> typeQualifierValue, boolean stopAtMethodScope) {
961985
962986 if (xmethod.isSynthetic())
987 {
963988 return null; // synthetic methods don't get default annotations
989 }
964990 // System.out.println("Looking for default " + typeQualifierValue +
965991 // " annotation of parameters of " + xmethod);
966 if (xmethod.getName().equals("<init>") && xmethod.getClassDescriptor().isAnonymousClass())
992 if ("<init>".equals(xmethod.getName()) && xmethod.getClassDescriptor().isAnonymousClass())
993 {
967994 return null; // constructors for anonymous inner classes don't get
968 // default annotations
995 // default annotations
996 }
969997
970998 /** private methods don't inherit from class or package scope */
971 if (xmethod.isPrivate())
999 if (xmethod.isPrivate()) {
9721000 stopAtMethodScope = true;
1001 }
9731002
9741003 boolean stopAtClassScope = false;
9751004
976 if (!xmethod.isPublic() && !xmethod.isProtected() && (xmethod.isStatic() || xmethod.getName().equals("<init>"))) {
1005 if (!xmethod.isPublic() && !xmethod.isProtected() && (xmethod.isStatic() || "<init>".equals(xmethod.getName()))) {
9771006 try {
9781007 XClass xclass = Global.getAnalysisCache().getClassAnalysis(XClass.class, xmethod.getClassDescriptor());
9791008 stopAtClassScope = xclass.isPrivate();
9841013
9851014 AnnotatedObject o = xmethod;
9861015 while (true) {
987 if (o == null)
1016 if (o == null) {
9881017 return null;
989
990 if (stopAtMethodScope && o instanceof XClass)
1018 }
1019
1020 if (stopAtMethodScope && o instanceof XClass) {
9911021 return null;
1022 }
9921023 // Check for direct type qualifier annotation
9931024 Set<TypeQualifierAnnotation> applications = new HashSet<TypeQualifierAnnotation>();
9941025 getDirectApplications(applications, o, ElementType.PARAMETER);
9961027 if (tqa != null) {
9971028 // Found matching annotation in outer scope
9981029 assert false : "I think this code is dead; it shouldn't find anything";
999 return tqa;
1030 return tqa;
10001031 }
10011032 // Check for default annotation
10021033 tqa = getDefaultAnnotation(o, typeQualifierValue, ElementType.PARAMETER);
10031034 if (tqa != null) {
1004 if (DEBUG)
1035 if (DEBUG) {
10051036 System.out.println("Found default of " + tqa + " @ " + o);
1037 }
10061038 return tqa;
10071039 }
1008 if (stopAtClassScope && o instanceof XClass)
1040 if (stopAtClassScope && o instanceof XClass) {
10091041 return null;
1010
1042 }
1043
10111044 o = o.getContainingScope();
1012
1045
10131046 }
10141047
10151048 }
2323
2424 /**
2525 * Dataflow class for TypeQualifierDataflowAnalysis.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class TypeQualifierDataflow<AnalysisType extends TypeQualifierDataflowAnalysis> extends
30 Dataflow<TypeQualifierValueSet, AnalysisType> {
30 Dataflow<TypeQualifierValueSet, AnalysisType> {
3131
3232 /**
3333 * Constructor.
34 *
34 *
3535 * @param cfg
3636 * control flow graph (CFG) of analyzed method
3737 * @param analysis
5151 public abstract class TypeQualifierDataflowAnalysis extends AbstractDataflowAnalysis<TypeQualifierValueSet> {
5252
5353 static String primitiveType (String simpleClass) {
54 if (simpleClass.equals("Integer"))
54 if ("Integer".equals(simpleClass)) {
5555 return "int";
56 }
5657 return simpleClass.toLowerCase();
5758 }
5859
5960 static boolean isIdentifyFunctionForTypeQualifiers(XMethod m) {
6061 String className = m.getClassName();
61 if (!className.startsWith("java.lang"))
62 if (!className.startsWith("java.lang")) {
6263 return false;
64 }
6365 String methodName = m.getName();
6466
6567 if (m.isStatic()) {
66 if (!methodName.equals("valueOf"))
68 if (!"valueOf".equals(methodName)) {
6769 return false;
70 }
6871 String signature = m.getSignature();
69 if (signature.charAt(2) != ')')
72 if (signature.charAt(2) != ')') {
7073 return false;
74 }
7175 } else {
7276 String simpleClassName = ClassName.extractSimpleName(className);
7377
74 if (!methodName.equals(primitiveType(simpleClassName) + "Value"))
78 if (!methodName.equals(primitiveType(simpleClassName) + "Value")) {
7579 return false;
80 }
7681 String signature = m.getSignature();
77 if (signature.charAt(1) != ')')
82 if (signature.charAt(1) != ')') {
7883 return false;
84 }
7985 }
8086 return true;
8187 }
8995
9096 protected final ValueNumberDataflow vnaDataflow;
9197
92 protected final TypeQualifierValue typeQualifierValue;
98 protected final TypeQualifierValue<?> typeQualifierValue;
9399
94100 protected final ConstantPoolGen cpg;
95101
111117 * the TypeQualifierValue we want the dataflow analysis to check
112118 */
113119 public TypeQualifierDataflowAnalysis(XMethod xmethod, CFG cfg, ValueNumberDataflow vnaDataflow, ConstantPoolGen cpg,
114 TypeQualifierValue typeQualifierValue) {
120 TypeQualifierValue<?> typeQualifierValue) {
115121 this.xmethod = xmethod;
116122 this.cfg = cfg;
117123 this.vnaDataflow = vnaDataflow;
126132 * @see
127133 * edu.umd.cs.findbugs.ba.DataflowAnalysis#initEntryFact(java.lang.Object)
128134 */
135 @Override
129136 public void initEntryFact(TypeQualifierValueSet result) throws DataflowAnalysisException {
130137 result.makeValid();
131138 }
148155 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#copy(java.lang.Object,
149156 * java.lang.Object)
150157 */
158 @Override
151159 public void copy(TypeQualifierValueSet source, TypeQualifierValueSet dest) {
152160 dest.makeSameAs(source);
153161 }
157165 *
158166 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#createFact()
159167 */
168 @Override
160169 public TypeQualifierValueSet createFact() {
161170 return new TypeQualifierValueSet(typeQualifierValue);
162171 }
166175 *
167176 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#isTop(java.lang.Object)
168177 */
178 @Override
169179 public boolean isTop(TypeQualifierValueSet fact) {
170180 return fact.isTop();
171181 }
176186 * @see
177187 * edu.umd.cs.findbugs.ba.DataflowAnalysis#makeFactTop(java.lang.Object)
178188 */
189 @Override
179190 public void makeFactTop(TypeQualifierValueSet fact) {
180191 fact.setTop();
181192 }
186197 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#meetInto(java.lang.Object,
187198 * edu.umd.cs.findbugs.ba.Edge, java.lang.Object)
188199 */
200 @Override
189201 public void meetInto(TypeQualifierValueSet fact, Edge edge, TypeQualifierValueSet result) throws DataflowAnalysisException {
190202 if (fact.isTop() || result.isBottom()) {
191203 // result does not change
207219 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#same(java.lang.Object,
208220 * java.lang.Object)
209221 */
222 @Override
210223 public boolean same(TypeQualifierValueSet fact1, TypeQualifierValueSet fact2) {
211224 return fact1.equals(fact2);
212225 }
8989 public static Collection<AnnotationValue> resolveTypeQualifierDefaults(Collection<AnnotationValue> values,
9090 ElementType elementType) {
9191 LinkedList<AnnotationValue> result = new LinkedList<AnnotationValue>();
92 for (AnnotationValue value : values)
92 for (AnnotationValue value : values) {
9393 resolveTypeQualifierDefaults(value, elementType, result);
94 }
9495 return result;
9596 }
9697
133134
134135 XClass c = Global.getAnalysisCache().getClassAnalysis(XClass.class, annotationClass);
135136 if (c.getAnnotationDescriptors().contains(typeQualifierNickname)) {
136 for (ClassDescriptor d : c.getAnnotationDescriptors())
137 if (!d.equals(typeQualifierNickname))
137 for (ClassDescriptor d : c.getAnnotationDescriptors()) {
138 if (!d.equals(typeQualifierNickname)) {
138139 resolveTypeQualifierNicknames(c.getAnnotation(d), result, onStack);
140 }
141 }
139142 } else if (c.getAnnotationDescriptors().contains(typeQualifier)) {
140143 result.add(value);
141144 }
155158
156159 public static void logMissingAnnotationClass(MissingClassException e) {
157160 ClassDescriptor c = e.getClassDescriptor();
158 if (c.getClassName().startsWith("javax.annotation"))
161 if (c.getClassName().startsWith("javax.annotation")) {
159162 AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportMissingClass(c);
163 }
160164 }
161165
162166 /**
170174 * @return Collection of resolved type qualifier AnnotationValues
171175 */
172176 public static Collection<AnnotationValue> resolveTypeQualifiers(Collection<AnnotationValue> values) {
173 if (values.isEmpty())
177 if (values.isEmpty()) {
174178 return Collections.emptyList();
179 }
175180 LinkedList<AnnotationValue> result = new LinkedList<AnnotationValue>();
176181 LinkedList<ClassDescriptor> onStack = new LinkedList<ClassDescriptor>();
177 for (AnnotationValue value : values)
182 for (AnnotationValue value : values) {
178183 resolveTypeQualifierNicknames(value, result, onStack);
184 }
179185 return result;
180186 }
181187
199205 try {
200206 XClass c = Global.getAnalysisCache().getClassAnalysis(XClass.class, value.getAnnotationClass());
201207 AnnotationValue defaultAnnotation = c.getAnnotation(typeQualifierDefault);
202 if (defaultAnnotation == null)
208 if (defaultAnnotation == null) {
203209 return;
204 for (Object o : (Object[]) defaultAnnotation.getValue("value"))
210 }
211 for (Object o : (Object[]) defaultAnnotation.getValue("value")) {
205212 if (o instanceof EnumValue) {
206213 EnumValue e = (EnumValue) o;
207214 if (e.desc.equals(elementTypeDescriptor) && e.value.equals(defaultFor.name())) {
208 for (ClassDescriptor d : c.getAnnotationDescriptors())
209 if (!d.equals(typeQualifierDefault))
215 for (ClassDescriptor d : c.getAnnotationDescriptors()) {
216 if (!d.equals(typeQualifierDefault)) {
210217 resolveTypeQualifierNicknames(c.getAnnotation(d), result, new LinkedList<ClassDescriptor>());
218 }
219 }
211220 break;
212221 }
213222 }
223 }
214224
215225 } catch (MissingClassException e) {
216226 logMissingAnnotationClass(e);
112112 isStrict1 = true;
113113 }
114114 for (XMethod xmethod : xclass.getXMethods()) {
115 if (xmethod.getName().equals("value") && xmethod.getSignature().startsWith("()")) {
115 if ("value".equals(xmethod.getName()) && xmethod.getSignature().startsWith("()")) {
116116 isExhaustive1 = xmethod.getAnnotation(EXHAUSTIVE_ANNOTATION) != null;
117117 if (isExhaustive1) {
118118 // exhaustive qualifiers are automatically exclusive
133133 this.isStrict = isStrict1;
134134 this.isExclusive = isExclusive1;
135135 this.isExhaustive = isExhaustive1;
136
137
136
137
138138 if (xclass != null) {
139139 ClassDescriptor checkerName = DescriptorFactory.createClassDescriptor(typeQualifier.getClassName() + "$Checker");
140140
149149 // found it.
150150 SecurityManager m = System.getSecurityManager();
151151 if (m == null) {
152 if (DEBUG_CLASSLOADING) System.out.println("Setting ValidationSecurityManager");
152 if (DEBUG_CLASSLOADING) {
153 System.out.println("Setting ValidationSecurityManager");
154 }
153155 System.setSecurityManager(ValidationSecurityManager.INSTANCE);
154156 }
155157
164166
165167 InvocationHandler handler = new InvocationHandler() {
166168
169 @Override
167170 public Object invoke(Object arg0, Method arg1, Object[] arg2) throws Throwable {
168 if (arg1.getName() == "value")
171 if ("value".equals(arg1.getName())) {
169172 return TypeQualifierValue.this.value;
173 }
170174 throw new UnsupportedOperationException("Can't handle " + arg1);
171175 }
172176 };
189193 else if (DEBUG_CLASSLOADING) {
190194 SecurityManager m = System.getSecurityManager();
191195 if (m == null) {
192 if (DEBUG_CLASSLOADING) System.out.println("Setting ValidationSecurityManager");
196 if (DEBUG_CLASSLOADING) {
197 System.out.println("Setting ValidationSecurityManager");
198 }
193199 System.setSecurityManager(ValidationSecurityManager.INSTANCE);
194200 }
195201 }
201207
202208 private static <A extends Annotation> TypeQualifierValidator<A> getValidator(
203209 Class<? extends TypeQualifierValidator<A>> checkerClass)
204 throws InstantiationException, IllegalAccessException {
210 throws InstantiationException, IllegalAccessException {
205211 return checkerClass.newInstance();
206212 }
207213
208214 @SuppressWarnings("unchecked")
209215 private static <A> Class<A> getQualifierClass(ClassDescriptor typeQualifier) throws ClassNotFoundException {
210216 @DottedClassName String className = typeQualifier.getDottedClassName();
211 if (DEBUG_CLASSLOADING)
217 if (DEBUG_CLASSLOADING) {
212218 System.out.println("Getting qualifier class for " + className);
213 if (className.startsWith("javax.annotation"))
219 }
220 if (className.startsWith("javax.annotation")) {
214221 return (Class<A>) Class.forName(className);
222 }
215223 try {
216224 Global.getAnalysisCache().getClassAnalysis(ClassData.class, typeQualifier);
217225 } catch (CheckedAnalysisException e) {
218226 throw new ClassNotFoundException("No class data found for " + className);
219227 }
220
228
221229 ValidatorClassLoader validatorLoader = ValidatorClassLoader.INSTANCE;
222230 return (Class<A>) validatorLoader.loadClass(typeQualifier.getDottedClassName());
223231 }
251259 }
252260
253261 public boolean canValidate(@CheckForNull Object constantValue) {
254 if (validator == null)
262 if (validator == null) {
255263 return false;
264 }
256265 return true;
257266 }
258267
259268 public When validate(@CheckForNull Object constantValue) {
260 if (validator == null)
269 if (validator == null) {
261270 throw new IllegalStateException("No validator");
271 }
262272 IAnalysisCache analysisCache = Global.getAnalysisCache();
263273 Profiler profiler = analysisCache.getProfiler();
264274 profiler.start(validator.getClass());
287297 TypeQualifierValue<?> getValue(ClassDescriptor desc, @CheckForNull Object value) {
288298 DualKeyHashMap<ClassDescriptor, Object, TypeQualifierValue<?>> map = instance.get().typeQualifierMap;
289299 TypeQualifierValue<?> result = map.get(desc, value);
290 if (result != null)
300 if (result != null) {
291301 return result;
302 }
292303 result = new TypeQualifierValue(desc, value);
293304 map.put(desc, value, result);
294305 instance.get().allKnownTypeQualifiers.add(result);
397408 @Override
398409 public int hashCode() {
399410 int result = typeQualifier.hashCode();
400 if (value != null)
411 if (value != null) {
401412 result += 37 * value.hashCode();
413 }
402414 return result;
403415 }
404416
405417 @Override
406418 public boolean equals(Object o) {
407 if (!(o instanceof TypeQualifierValue))
419 if (!(o instanceof TypeQualifierValue)) {
408420 return false;
421 }
409422 TypeQualifierValue<?> other = (TypeQualifierValue<?>) o;
410423 return typeQualifier.equals(other.typeQualifier) && Util.nullSafeEquals(value, other.value);
411424 }
5050
5151 final boolean isStrict;
5252
53 public TypeQualifierValueSet(TypeQualifierValue typeQualifierValue) {
53 public TypeQualifierValueSet(TypeQualifierValue<?> typeQualifierValue) {
5454 this.valueMap = new HashMap<ValueNumber, FlowValue>(3);
5555 this.whereAlways = new HashMap<ValueNumber, Set<SourceSinkInfo>>(3);
5656 this.whereNever = new HashMap<ValueNumber, Set<SourceSinkInfo>>(3);
7171 break;
7272 case NEVER:
7373 addSourceSinkInfo(whereNever, vn, sourceSinkInfo);
74 }
75 } else {
74 break;
75 default:
76 break;
77 }
78 }/* else {
7679
7780 if (flowValue.isYes()) {
7881 addSourceSinkInfo(whereAlways, vn, sourceSinkInfo);
8184 if (flowValue.isNo()) {
8285 addSourceSinkInfo(whereNever, vn, sourceSinkInfo);
8386 }
84 }
85 }
86
87 private void setValue(ValueNumber vn, FlowValue flowValue) {
88 if (flowValue == FlowValue.TOP)
89 pruneValue(vn);
90 else
91 valueMap.put(vn, flowValue);
87 }*/
88 }
89
90 private void setValue(ValueNumber vn, FlowValue flowValue) {
91 if (flowValue == FlowValue.TOP) {
92 pruneValue(vn);
93 } else {
94 valueMap.put(vn, flowValue);
95 }
9296 }
9397
9498 static <K, V> void copyMapValue(Map<K, V> map, K from, K to) {
95 if (!map.containsKey(from))
99 if (!map.containsKey(from)) {
96100 return;
101 }
97102 map.put(to, map.get(from));
98103 }
99104
100105 void copyInfo(ValueNumber from, ValueNumber to) {
101 if (state == State.TOP)
106 if (state == State.TOP) {
102107 return;
108 }
103109 copyMapValue(valueMap, from, to);
104110 copyMapValue(whereAlways, from, to);
105111 copyMapValue(whereNever, from, to);
108114 private static void addSourceSinkInfo(Map<ValueNumber, Set<SourceSinkInfo>> sourceSinkInfoSetMap, ValueNumber vn,
109115 SourceSinkInfo sourceSinkInfo) {
110116 Set<SourceSinkInfo> sourceSinkInfoSet = sourceSinkInfoSetMap.get(vn);
111 if (sourceSinkInfoSet == null) {
112 sourceSinkInfoSet = new HashSet<SourceSinkInfo>(3);
113 sourceSinkInfoSetMap.put(vn, sourceSinkInfoSet);
114 }
117 if (sourceSinkInfoSet == null) {
118 sourceSinkInfoSet = new HashSet<SourceSinkInfo>(3);
119 sourceSinkInfoSetMap.put(vn, sourceSinkInfoSet);
120 }
115121 sourceSinkInfoSet.add(sourceSinkInfo);
116122 }
117123
142148 private static Set<? extends SourceSinkInfo> getSourceSinkInfoSet(Map<ValueNumber, Set<SourceSinkInfo>> sourceSinkInfoSetMap,
143149 ValueNumber vn) {
144150 Set<SourceSinkInfo> sourceSinkInfoSet = sourceSinkInfoSetMap.get(vn);
145 if (sourceSinkInfoSet == null || sourceSinkInfoSet.isEmpty())
151 if (sourceSinkInfoSet == null || sourceSinkInfoSet.isEmpty()) {
146152 return Collections.emptySet();
153 }
147154
148155 return sourceSinkInfoSet;
149 }
156 }
150157 private static Set<SourceSinkInfo> getOrCreateSourceSinkInfoSet(Map<ValueNumber, Set<SourceSinkInfo>> sourceSinkInfoSetMap,
151158 ValueNumber vn) {
152159 Set<SourceSinkInfo> sourceSinkInfoSet = sourceSinkInfoSetMap.get(vn);
320327 StringBuilder buf = new StringBuilder();
321328
322329 buf.append("{");
323 boolean first = true;
330 // boolean first = true;
324331
325332 for (ValueNumber vn : interesting) {
326333 FlowValue value = getValue(vn);
327 if (value == FlowValue.TOP || /* !isStrict && */ value == FlowValue.UNKNOWN) continue;
334 if (value == FlowValue.TOP || /* !isStrict && */ value == FlowValue.UNKNOWN) {
335 continue;
336 }
328337 if (buf.length() > 1) {
329338 buf.append(", ");
330339 }
351360 Set<? extends SourceSinkInfo> never = getSourceSinkInfoSet(whereNever, vn);
352361 if (value != FlowValue.UNKNOWN || !always.equals(never)) {
353362 buf.append("[");
354 if (!always.isEmpty())
363 if (!always.isEmpty()) {
355364 appendSourceSinkInfos(buf, "YES=", always);
356 if (!always.isEmpty() && !never.isEmpty())
365 }
366 if (!always.isEmpty() && !never.isEmpty()) {
357367 buf.append(",");
358 if (!never.isEmpty())
368 }
369 if (!never.isEmpty()) {
359370 appendSourceSinkInfos(buf, "NO=", never);
371 }
360372 buf.append("]");
361373 }
362374 }
2929
3030
3131 static final ValidationSecurityManager INSTANCE = new ValidationSecurityManager();
32 final static ValidatorClassLoader VALIDATOR_LOADER = ValidatorClassLoader.INSTANCE;
3233
33
34
34
3535 static {
36 if (TypeQualifierValue.DEBUG_CLASSLOADING)
36 if (TypeQualifierValue.DEBUG_CLASSLOADING) {
3737 new RuntimeException("Creating ValidationSecurityManager #").printStackTrace();
38 }
3839
3940 }
4041 public static <A extends Annotation> When sandboxedValidation(A proxy, TypeQualifierValidator<A> v, @CheckForNull
41 Object constantValue) {
42 if (performingValidation.get())
42 Object constantValue) {
43 if (performingValidation.get()) {
4344 throw new IllegalStateException("recursive validation");
45 }
4446
4547 try {
4648 performingValidation.set(Boolean.TRUE);
47 if (TypeQualifierValue.DEBUG_CLASSLOADING)
49 if (TypeQualifierValue.DEBUG_CLASSLOADING) {
4850 System.out.println("Performing validation in thread " + Thread.currentThread().getName());
51 }
4952 try {
5053 When result = v.forConstantValue(proxy, constantValue);
51 if (!performingValidation.get())
54 if (!performingValidation.get()) {
5255 throw new IllegalStateException("performingValidation not set when validation completes");
56 }
5357 return result;
5458 } catch (ClassCastException e) {
5559 Class<? extends Annotation> c = proxy.getClass();
5660 System.out.println(c.getName() + " extends " + c.getSuperclass().getName());
57 for(Class<?> i : c.getInterfaces())
58 System.out.println(" " + i.getName());
61 for(Class<?> i : c.getInterfaces()) {
62 System.out.println(" " + i.getName());
63 }
5964 throw e;
6065 }
61
66
6267 } finally {
6368 performingValidation.set(Boolean.FALSE);
64 if (TypeQualifierValue.DEBUG_CLASSLOADING)
69 if (TypeQualifierValue.DEBUG_CLASSLOADING) {
6570 System.out.println("Validation finished in thread " + Thread.currentThread().getName());
66
71 }
72
6773 }
6874 }
6975
7076
7177 @Override
7278 public void checkPermission(Permission perm) {
73 if (TypeQualifierValue.DEBUG_CLASSLOADING)
79 if (TypeQualifierValue.DEBUG_CLASSLOADING) {
7480 System.out.println("Checking for " + perm + " permission in thread " + Thread.currentThread().getName());
81 }
7582 if (performingValidation.get() && inValidation()) {
7683 SecurityException e = new SecurityException("No permissions granted while performing JSR-305 validation");
77 if (TypeQualifierValue.DEBUG_CLASSLOADING)
84 if (TypeQualifierValue.DEBUG_CLASSLOADING) {
7885 e.printStackTrace(System.out);
86 }
7987 throw e;
8088 }
8189
8391
8492 @Override
8593 public void checkPermission(Permission perm, Object context) {
86 if (TypeQualifierValue.DEBUG_CLASSLOADING)
94 if (TypeQualifierValue.DEBUG_CLASSLOADING) {
8795 System.out.println("Checking for " + perm + " permission with content in thread " + Thread.currentThread().getName());
88
96 }
97
8998 if (performingValidation.get() && inValidation()) {
9099 SecurityException e = new SecurityException("No permissions granted while performing JSR-305 validation");
91 if (TypeQualifierValue.DEBUG_CLASSLOADING)
100 if (TypeQualifierValue.DEBUG_CLASSLOADING) {
92101 e.printStackTrace(System.out);
102 }
93103 throw e;
94104 }
95105 }
99109 private boolean inValidation() {
100110 for (Class<?> c : getClassContext()) {
101111 if (TypeQualifierValidator.class.isAssignableFrom(c)
102 || c.getClassLoader() == ValidatorClassLoader.INSTANCE)
112 || c.getClassLoader() == VALIDATOR_LOADER) {
103113 return true;
114 }
104115 }
105116 return false;
106117 }
114125 };
115126
116127
117 }
128 }
2727 class ValidatorClassLoader extends ClassLoader {
2828
2929 static {
30 if (TypeQualifierValue.DEBUG_CLASSLOADING)
30 if (TypeQualifierValue.DEBUG_CLASSLOADING) {
3131 new RuntimeException("Initialising ValidatorClassLoader").printStackTrace();
32 }
3233
3334 }
3435 final static ValidatorClassLoader INSTANCE = new ValidatorClassLoader();
3536
3637 ValidatorClassLoader() {
3738 super(ClassLoader.getSystemClassLoader().getParent());
38 if (TypeQualifierValue.DEBUG_CLASSLOADING)
39 if (TypeQualifierValue.DEBUG_CLASSLOADING) {
3940 new RuntimeException("Creating ValidatorClassLoader #").printStackTrace();
41 }
4042 }
41
43
4244 @Override
4345 protected Class<?> loadClass(String name, boolean resolve)
4446 throws ClassNotFoundException {
45
47
4648 if (TypeQualifierValue.DEBUG_CLASSLOADING) {
47 if (resolve)
49 if (resolve) {
4850 System.out.println("Loading and resolving class for " + name);
49 else System.out.println("Loading class for " + name);
51 } else {
52 System.out.println("Loading class for " + name);
53 }
5054 }
51
55
5256 return super.loadClass(name, resolve);
5357 }
5458 @Override
5559 public Class<?> findClass(@DottedClassName String name) throws ClassNotFoundException {
56 if (TypeQualifierValue.DEBUG_CLASSLOADING)
60 if (TypeQualifierValue.DEBUG_CLASSLOADING) {
5761 System.out.println("Looking for class data for " + name);
58
59 if (name.startsWith("javax.annotation"))
60 return Class.forName(name);
61
62 }
63
64 if (name.startsWith("javax.annotation")) {
65 return Class.forName(name);
66 }
67
6268 try {
63 byte[] b = TypeQualifierValue.loadClassData(name);
69 byte[] b = TypeQualifierValue.loadClassData(name);
6470 return findClass(name, b);
6571 } catch (CheckedAnalysisException e) {
66 if (TypeQualifierValue.DEBUG_CLASSLOADING)
72 if (TypeQualifierValue.DEBUG_CLASSLOADING) {
6773 e.printStackTrace();
74 }
6875 return super.findClass(name);
6976 } catch (RuntimeException e) {
70 if (TypeQualifierValue.DEBUG_CLASSLOADING)
77 if (TypeQualifierValue.DEBUG_CLASSLOADING) {
7178 e.printStackTrace();
79 }
7280 throw e;
7381 }
7482 }
7583
76
84
7785 private Class<?> findClass(@DottedClassName String name, byte [] b) {
7886 try {
79 if (TypeQualifierValue.DEBUG_CLASSLOADING)
87 if (TypeQualifierValue.DEBUG_CLASSLOADING) {
8088 System.out.println("Loading " + b.length + " bytes for class " + name);
89 }
8190 Class<?> result = defineClass(name, b, 0, b.length);
8291 super.resolveClass(result);
83 if (TypeQualifierValue.DEBUG_CLASSLOADING)
92 if (TypeQualifierValue.DEBUG_CLASSLOADING) {
8493 System.out.println("defined class " + name);
94 }
8595 return result;
8696 } catch (RuntimeException e) {
8797 e.printStackTrace();
90100
91101
92102 }
93
94
103
104
95105
96106 }
6666
6767 public static UsagesRequiringNonNullValues getAnalysis(ClassContext classContext, Method method) {
6868 XMethod thisMethod = classContext.getXClass().findMethod(method.getName(), method.getSignature(), method.isStatic());
69 if (DEBUG)
69 if (DEBUG) {
7070 System.out.println(thisMethod);
71 }
7172 UsagesRequiringNonNullValues derefs = new UsagesRequiringNonNullValues();
7273 try {
7374
9697 InstructionHandle exceptionThrowerHandle = basicBlock.getExceptionThrower();
9798 Instruction exceptionThrower = exceptionThrowerHandle.getInstruction();
9899 ValueNumberFrame vnaFrame = vna.getStartFact(basicBlock);
99 if (!vnaFrame.isValid())
100 if (!vnaFrame.isValid()) {
100101 continue;
102 }
101103 ValueNumber valueNumber = vnaFrame.getInstance(exceptionThrower, cpg);
102104
103105 Location location = new Location(exceptionThrowerHandle, basicBlock);
104 if (valueNumberForThis != valueNumber)
106 if (valueNumberForThis != valueNumber) {
105107 derefs.add(location, valueNumber, PointerUsageRequiringNonNullValue.getPointerDereference());
108 }
106109
107110 }
108111 }
121124
122125 // Check nonnull annotations
123126
124 for (int j = 0; j < numParams; j++)
127 for (int j = 0; j < numParams; j++) {
125128 if (db.parameterMustBeNonNull(m, j)) {
126129 int slot = sigParser.getSlotsFromTopOfStackForParameter(j);
127130 ValueNumber valueNumber = valueNumberFrame.getStackValue(slot);
128 if (valueNumberForThis != valueNumber)
131 if (valueNumberForThis != valueNumber) {
129132 derefs.add(location, valueNumber,
130133 PointerUsageRequiringNonNullValue.getPassedAsNonNullParameter(m, j));
131 }
134 }
135 }
136 }
132137
133138 // Check actual targets
134139 try {
143148 break;
144149 }
145150 BitSet foo = property.getAsBitSet();
146 if (unconditionallyDereferencedNullArgSet == null)
151 if (unconditionallyDereferencedNullArgSet == null) {
147152 unconditionallyDereferencedNullArgSet = foo;
148 else
149 unconditionallyDereferencedNullArgSet.intersects(foo);
150 if (unconditionallyDereferencedNullArgSet.isEmpty())
153 } else {
154 unconditionallyDereferencedNullArgSet.and(foo);
155 }
156 if (unconditionallyDereferencedNullArgSet.isEmpty()) {
151157 break;
158 }
152159 }
153160
154161 if (unconditionallyDereferencedNullArgSet != null && !unconditionallyDereferencedNullArgSet.isEmpty()
155 && valueNumberFrame.isValid())
162 && valueNumberFrame.isValid()) {
156163 for (int j = unconditionallyDereferencedNullArgSet.nextSetBit(0); j >= 0; j = unconditionallyDereferencedNullArgSet
157164 .nextSetBit(j + 1)) {
158165 int slot = sigParser.getSlotsFromTopOfStackForParameter(j);
159166 ValueNumber valueNumber = valueNumberFrame.getStackValue(slot);
160 if (valueNumberForThis != valueNumber)
167 if (valueNumberForThis != valueNumber) {
161168 derefs.add(location, valueNumber,
162169 PointerUsageRequiringNonNullValue.getPassedAsNonNullParameter(m, j));
163 }
170 }
171 }
172 }
164173
165174 } catch (ClassNotFoundException e) {
166175 AnalysisContext.reportMissingClass(e);
171180 derefs.add(location, valueNumber, PointerUsageRequiringNonNullValue.getPointerNullChecked());
172181 } else if (ins instanceof ARETURN && methodAnnotation == NullnessAnnotation.NONNULL) {
173182 ValueNumber valueNumber = valueNumberFrame.getTopValue();
174 if (valueNumberForThis != valueNumber)
183 if (valueNumberForThis != valueNumber) {
175184 derefs.add(location, valueNumber,
176185 PointerUsageRequiringNonNullValue.getReturnFromNonNullMethod(thisMethod));
186 }
177187
178188 } else if (ins instanceof PUTFIELD || ins instanceof PUTSTATIC) {
179189 FieldInstruction inf = (FieldInstruction) ins;
182192 .getResolvedAnnotation(field, false);
183193 if (annotation == NullnessAnnotation.NONNULL) {
184194 ValueNumber valueNumber = valueNumberFrame.getTopValue();
185 if (valueNumberForThis != valueNumber)
195 if (valueNumberForThis != valueNumber) {
186196 derefs.add(location, valueNumber, PointerUsageRequiringNonNullValue.getStoredIntoNonNullField(field));
197 }
187198 }
188199
189200 }
2929 * A control decision which resulted in information being gained about whether a
3030 * particular value is null or non-null on the IFCMP_EDGE and FALL_THROUGH_EDGE
3131 * branches.
32 *
32 *
3333 * @see IsNullValue
3434 * @see IsNullValueFrame
3535 * @see IsNullValueAnalysis
4646
4747 /**
4848 * Constructor.
49 *
49 *
5050 * @param value
5151 * the ValueNumber for which we have new information; null if no
5252 * new information
7575
7676 @Override
7777 public boolean equals(Object o) {
78 if (!(o instanceof IsNullConditionDecision))
78 if (!(o instanceof IsNullConditionDecision)) {
7979 return false;
80 }
8081 IsNullConditionDecision other = (IsNullConditionDecision) o;
8182 return Util.nullSafeEquals(value, other.value) && Util.nullSafeEquals(ifcmpDecision, other.ifcmpDecision)
8283 && Util.nullSafeEquals(fallThroughDecision, other.fallThroughDecision);
100101 * Determine whether or not the given edge is feasible. An edge may be
101102 * infeasible if the comparison is redundant (i.e., can only be determined
102103 * one way)
103 *
104 *
104105 * @param edgeType
105106 * the type of edge; must be IFCMP_EDGE or FALL_THROUGH_EDGE
106107 * @return true if the edge is feasible, false if infeasible
112113
113114 /**
114115 * Get the decision reached about the value on outgoing edge of given type.
115 *
116 *
116117 * @param edgeType
117118 * the type of edge; must be IFCMP_EDGE or FALL_THROUGH_EDGE
118119 * @return the IsNullValue representing the decision, or null if the edge is
142143 }
143144 }
144145
145 // vim:ts=4
9797 private static final int FLAG_MASK = EXCEPTION | PARAM | RETURN_VAL | FIELD_VAL | READLINE_VAL;
9898
9999 private static final int[][] mergeMatrix = {
100 // NULL, CHECKED_NULL, NN, CHECKED_NN, NO_KABOOM_NN, NSP,
101 // NN_UNKNOWN, NCP2, NCP3
102 { NULL }, // NULL
103 { NULL, CHECKED_NULL, }, // CHECKED_NULL
104 { NSP, NSP, NN }, // NN
105 { NSP, NSP, NN, CHECKED_NN, }, // CHECKED_NN
106 { NSP, NSP, NN, NN, NO_KABOOM_NN }, // NO_KABOOM_NN
107 { NSP, NSP, NSP, NSP, NSP, NSP }, // NSP
108 { NSP, NSP, NN_UNKNOWN, NN_UNKNOWN, NN_UNKNOWN, NSP, NN_UNKNOWN, }, // NN_UNKNOWN
109 { NSP, NSP, NCP2, NCP2, NCP2, NSP, NCP2, NCP2, }, // NCP2
110 { NSP, NSP, NCP3, NCP3, NCP3, NSP, NCP3, NCP3, NCP3 } // NCP3
100 // NULL, CHECKED_NULL, NN, CHECKED_NN, NO_KABOOM_NN, NSP,
101 // NN_UNKNOWN, NCP2, NCP3
102 { NULL }, // NULL
103 { NULL, CHECKED_NULL, }, // CHECKED_NULL
104 { NSP, NSP, NN }, // NN
105 { NSP, NSP, NN, CHECKED_NN, }, // CHECKED_NN
106 { NSP, NSP, NN, NN, NO_KABOOM_NN }, // NO_KABOOM_NN
107 { NSP, NSP, NSP, NSP, NSP, NSP }, // NSP
108 { NSP, NSP, NN_UNKNOWN, NN_UNKNOWN, NN_UNKNOWN, NSP, NN_UNKNOWN, }, // NN_UNKNOWN
109 { NSP, NSP, NCP2, NCP2, NCP2, NSP, NCP2, NCP2, }, // NCP2
110 { NSP, NSP, NCP3, NCP3, NCP3, NSP, NCP3, NCP3, NCP3 } // NCP3
111111 };
112112
113113 private static final IsNullValue[][] instanceByFlagsList = createInstanceByFlagList();
114114
115115 private static IsNullValue[][] createInstanceByFlagList() {
116116 final int max = FLAG_MASK >>> FLAG_SHIFT;
117 IsNullValue[][] result = new IsNullValue[max + 1][];
118 for (int i = 0; i <= max; ++i) {
119 final int flags = i << FLAG_SHIFT;
120 result[i] = new IsNullValue[] { new IsNullValue(NULL | flags), new IsNullValue(CHECKED_NULL | flags),
121 new IsNullValue(NN | flags), new IsNullValue(CHECKED_NN | flags),
122 null, // NO_KABOOM_NN values must be allocated dynamically
123 new IsNullValue(NSP | flags), new IsNullValue(NN_UNKNOWN | flags), new IsNullValue(NCP2 | flags),
124 new IsNullValue(NCP3 | flags), };
125 }
126
127 return result;
117 IsNullValue[][] result = new IsNullValue[max + 1][];
118 for (int i = 0; i <= max; ++i) {
119 final int flags = i << FLAG_SHIFT;
120 result[i] = new IsNullValue[] { new IsNullValue(NULL | flags), new IsNullValue(CHECKED_NULL | flags),
121 new IsNullValue(NN | flags), new IsNullValue(CHECKED_NN | flags),
122 null, // NO_KABOOM_NN values must be allocated dynamically
123 new IsNullValue(NSP | flags), new IsNullValue(NN_UNKNOWN | flags), new IsNullValue(NCP2 | flags),
124 new IsNullValue(NCP3 | flags), };
125 }
126
127 return result;
128128 }
129129
130130 // Fields
135135 private IsNullValue(int kind) {
136136 this.kind = kind;
137137 locationOfKaBoom = null;
138 if (VERIFY_INTEGRITY)
138 if (VERIFY_INTEGRITY) {
139139 checkNoKaboomNNLocation();
140 }
140141 }
141142
142143 private IsNullValue(int kind, Location ins) {
143144 this.kind = kind;
144145 locationOfKaBoom = ins;
145 if (VERIFY_INTEGRITY)
146 if (VERIFY_INTEGRITY) {
146147 checkNoKaboomNNLocation();
148 }
147149 }
148150
149151 private void checkNoKaboomNNLocation() {
154156
155157 @Override
156158 public boolean equals(Object o) {
157 if (o == null || this.getClass() != o.getClass())
159 if (o == null || this.getClass() != o.getClass()) {
158160 return false;
161 }
159162 IsNullValue other = (IsNullValue) o;
160 if (kind != other.kind)
163 if (kind != other.kind) {
161164 return false;
162 if (locationOfKaBoom == other.locationOfKaBoom)
165 }
166 if (locationOfKaBoom == other.locationOfKaBoom) {
163167 return true;
164 if (locationOfKaBoom == null || other.locationOfKaBoom == null)
168 }
169 if (locationOfKaBoom == null || other.locationOfKaBoom == null) {
165170 return false;
171 }
166172 return locationOfKaBoom.equals(other.locationOfKaBoom);
167173 }
168174
169175 @Override
170176 public int hashCode() {
171177 int hashCode = kind;
172 if (locationOfKaBoom != null)
178 if (locationOfKaBoom != null) {
173179 hashCode += locationOfKaBoom.hashCode();
180 }
174181 return hashCode;
175182 }
176183
230237 return getBaseKind() == NO_KABOOM_NN;
231238 }
232239
240 /*
233241 private IsNullValue toBaseValue() {
234242 return instanceByFlagsList[0][getBaseKind()];
235243 }
244 */
236245
237246 /**
238247 * Convert to an exception path value.
239248 */
240249 public IsNullValue toExceptionValue() {
241 if (getBaseKind() == NO_KABOOM_NN)
250 if (getBaseKind() == NO_KABOOM_NN) {
242251 return new IsNullValue(kind | EXCEPTION, locationOfKaBoom);
252 }
243253 return instanceByFlagsList[(getFlags() | EXCEPTION) >> FLAG_SHIFT][getBaseKind()];
244254 }
245255
258268 }
259269
260270 int flag = RETURN_VAL;
261 if (methodInvoked.getName().equals("readLine") && methodInvoked.getSignature().equals("()Ljava/lang/String;"))
271 if ("readLine".equals(methodInvoked.getName()) && "()Ljava/lang/String;".equals(methodInvoked.getSignature())) {
262272 flag = READLINE_VAL;
263 if (getBaseKind() == NO_KABOOM_NN)
273 }
274 if (getBaseKind() == NO_KABOOM_NN) {
264275 return new IsNullValue(kind | flag, locationOfKaBoom);
276 }
265277 return instanceByFlagsList[(getFlags() | flag) >> FLAG_SHIFT][getBaseKind()];
266278 }
267279
273285 * TODO
274286 */
275287 public IsNullValue markInformationAsComingFromFieldValue(XField field) {
276 if (getBaseKind() == NO_KABOOM_NN)
288 if (getBaseKind() == NO_KABOOM_NN) {
277289 return new IsNullValue(kind | FIELD_VAL, locationOfKaBoom);
290 }
278291 return instanceByFlagsList[(getFlags() | FIELD_VAL) >> FLAG_SHIFT][getBaseKind()];
279292 }
280293
314327 * would have occurred if it were null.
315328 */
316329 public static IsNullValue noKaboomNonNullValue(@Nonnull Location ins) {
317 if (ins == null)
330 if (ins == null) {
318331 throw new NullPointerException("ins cannot be null");
332 }
319333 return new IsNullValue(NO_KABOOM_NN, ins);
320334 }
321335
383397 * Merge two values.
384398 */
385399 public static IsNullValue merge(IsNullValue a, IsNullValue b) {
386 if (a == b)
400 if (a == b) {
387401 return a;
388 if (a.equals(b))
402 }
403 if (a.equals(b)) {
389404 return a;
405 }
390406 int aKind = a.kind & 0xff;
391407 int bKind = b.kind & 0xff;
392408 int aFlags = a.getFlags();
394410
395411 int combinedFlags = aFlags & bFlags;
396412
397 if (!(a.isNullOnSomePath() || a.isDefinitelyNull()) && b.isException())
413 if (!(a.isNullOnSomePath() || a.isDefinitelyNull()) && b.isException()) {
398414 combinedFlags |= EXCEPTION;
399 else if (!(b.isNullOnSomePath() || b.isDefinitelyNull()) && a.isException())
415 } else if (!(b.isNullOnSomePath() || b.isDefinitelyNull()) && a.isException()) {
400416 combinedFlags |= EXCEPTION;
417 }
401418
402419 // Left hand value should be >=, since it is used
403420 // as the first dimension of the matrix to index.
487504 String pfx = "";
488505 if (DEBUG_EXCEPTION) {
489506 int flags = getFlags();
490 if (flags == 0)
507 if (flags == 0) {
491508 pfx = "_";
492 else {
493 if ((flags & EXCEPTION) != 0)
509 } else {
510 if ((flags & EXCEPTION) != 0) {
494511 pfx += "e";
495 if ((flags & PARAM) != 0)
512 }
513 if ((flags & PARAM) != 0) {
496514 pfx += "p";
497 if ((flags & RETURN_VAL) != 0)
515 }
516 if ((flags & RETURN_VAL) != 0) {
498517 pfx += "r";
499 if ((flags & FIELD_VAL) != 0)
518 }
519 if ((flags & FIELD_VAL) != 0) {
500520 pfx += "f";
521 }
501522 }
502523 }
503524 if (DEBUG_KABOOM && locationOfKaBoom == null) {
541562 if (NCP_EXTRA_BRANCH) {
542563 // Experimental: track two distinct kinds of "null on complex path"
543564 // values.
544 if (value.isNullOnSomePath())
565 if (value.isNullOnSomePath()) {
545566 value = nullOnComplexPathValue();
546 else if (value.equals(nullOnComplexPathValue()))
567 } else if (value.equals(nullOnComplexPathValue())) {
547568 value = nullOnComplexPathValue3();
569 }
548570
549571 } else {
550572 // Downgrade "null on simple path" values to
551573 // "null on complex path".
552 if (value.isNullOnSomePath())
574 if (value.isNullOnSomePath()) {
553575 value = nullOnComplexPathValue();
576 }
554577 }
555578 return value;
556579 }
557580 }
558
559 // vim:ts=4
6969 * @see IsNullValueFrameModelingVisitor
7070 */
7171 public class IsNullValueAnalysis extends FrameDataflowAnalysis<IsNullValue, IsNullValueFrame> implements EdgeTypes,
72 IsNullValueAnalysisFeatures {
72 IsNullValueAnalysisFeatures {
7373 static final boolean DEBUG = SystemProperties.getBoolean("inva.debug");
7474
7575 static {
76 if (DEBUG)
76 if (DEBUG) {
7777 System.out.println("inva.debug enabled");
78 }
7879 }
7980
8081 private final MethodGen methodGen;
139140 state = PointerEqualityCheckState.START;
140141 break;
141142 case START:
142 if (ins instanceof org.apache.bcel.generic.ALOAD)
143 if (ins instanceof org.apache.bcel.generic.ALOAD) {
143144 state = PointerEqualityCheckState.SAW1;
144 else
145 } else {
145146 return null;
147 }
146148 break;
147149 case SAW1:
148 if (ins instanceof org.apache.bcel.generic.ALOAD)
150 if (ins instanceof org.apache.bcel.generic.ALOAD) {
149151 state = PointerEqualityCheckState.SAW2;
150 else
152 } else {
151153 return null;
154 }
152155 break;
153156 case SAW2:
154157 if (ins instanceof org.apache.bcel.generic.IF_ACMPNE) {
155158 state = PointerEqualityCheckState.IFEQUAL;
156159 target = ((IF_ACMPNE) ins).getIndex() + loc.getHandle().getPosition();
157160 test = loc;
158 } else
161 } else {
159162 return null;
163 }
160164 break;
161165 case IFEQUAL:
162166 if (ins instanceof org.apache.bcel.generic.ReturnInstruction || ins instanceof ATHROW) {
174178 } catch (DataflowAnalysisException e) {
175179 return null;
176180 }
177 } else
181 } else {
178182 return null;
179
183 }
180184 }
181185 }
182186 return null;
184188
185189 private @CheckForNull
186190 ValueNumber getKnownNonnullDueToPointerDisequality(ValueNumber knownNull, int pc) {
187 if (pointerEqualityCheck == null || pc < pointerEqualityCheck.firstValuePC)
191 if (pointerEqualityCheck == null || pc < pointerEqualityCheck.firstValuePC) {
188192 return null;
189 if (pointerEqualityCheck.reg1.equals(knownNull))
193 }
194 if (pointerEqualityCheck.reg1.equals(knownNull)) {
190195 return pointerEqualityCheck.reg2;
191 if (pointerEqualityCheck.reg2.equals(knownNull))
196 }
197 if (pointerEqualityCheck.reg2.equals(knownNull)) {
192198 return pointerEqualityCheck.reg1;
199 }
193200 return null;
194201 }
195202
213220 return classAndMethod;
214221 }
215222
223 @Override
216224 public IsNullValueFrame createFact() {
217225 return new IsNullValueFrame(methodGen.getMaxLocals(), trackValueNumbers);
218226 }
219227
228 @Override
220229 public void initEntryFact(IsNullValueFrame result) {
221230 if (cachedEntryFact == null) {
222231
233242 for (int i = 0; i < numLocals; ++i) {
234243 cachedEntryFact.setValue(i, IsNullValue.nonReportingNotNullValue());
235244 }
236 if (paramShift == 1)
245 if (paramShift == 1) {
237246 cachedEntryFact.setValue(0, IsNullValue.nonNullValue());
247 }
238248
239249 int slot = paramShift;
240250 for (int paramIndex = 0; paramIndex < argumentTypes.length; paramIndex++) {
242252
243253 XMethodParameter methodParameter = new XMethodParameter(xm, paramIndex);
244254 NullnessAnnotation n = db.getResolvedAnnotation(methodParameter, false);
245 if (n == NullnessAnnotation.CHECK_FOR_NULL)
255 if (n == NullnessAnnotation.CHECK_FOR_NULL) {
246256 // Parameter declared @CheckForNull
247257 value = IsNullValue.parameterMarkedAsMightBeNull(methodParameter);
248 else if (n == NullnessAnnotation.NONNULL)
258 } else if (n == NullnessAnnotation.NONNULL) {
249259 // Parameter declared @NonNull
250260 // TODO: label this so we don't report defensive programming
251 value = false ? IsNullValue.nonNullValue() : IsNullValue.parameterMarkedAsNonnull(methodParameter);
252 else
261 // value = false ? IsNullValue.nonNullValue() : IsNullValue.parameterMarkedAsNonnull(methodParameter);
262 value = IsNullValue.parameterMarkedAsNonnull(methodParameter);
263 } else {
253264 // Don't know; use default value, normally non-reporting
254265 // nonnull
255266 value = IsNullValue.nonReportingNotNullValue();
267 }
256268
257269 cachedEntryFact.setValue(slot, value);
258270
272284 if (end == null) {
273285 ValueNumberFrame vnaFrameAfter = vnaDataflow.getFactAfterLocation(Location.getLastLocation(basicBlock));
274286 // purge stale information
275 if (!vnaFrameAfter.isTop())
287 if (!vnaFrameAfter.isTop()) {
276288 result.cleanStaleKnowledge(vnaFrameAfter);
289 }
277290 }
278291
279292 }
287300 throws DataflowAnalysisException {
288301 // Determine if this basic block ends in a redundant branch.
289302 if (end == null) {
290 if (lastFrame == null)
303 if (lastFrame == null) {
291304 result.setDecision(null);
292 else {
305 } else {
293306 IsNullConditionDecision decision = getDecision(basicBlock, lastFrame);
294307 result.setDecision(decision);
295308 }
302315 public void transferInstruction(InstructionHandle handle, BasicBlock basicBlock, IsNullValueFrame fact)
303316 throws DataflowAnalysisException {
304317
305 if (!fact.isValid())
318 if (!fact.isValid()) {
306319 return;
320 }
307321
308322 // If this is the last instruction in the block,
309323 // save the result immediately before the instruction.
322336 Instruction ins = handle.getInstruction();
323337 visitor.analyzeInstruction(ins);
324338
325 if (!fact.isValid())
339 if (!fact.isValid()) {
326340 return;
341 }
327342
328343 // Special case:
329344 // The instruction may have produced previously seen values
333348 // Also, make a note of any newly-produced null values.
334349
335350 int numProduced = ins.produceStack(methodGen.getConstantPool());
336 if (numProduced == Constants.UNPREDICTABLE)
351 if (numProduced == Constants.UNPREDICTABLE) {
337352 throw new DataflowAnalysisException("Unpredictable stack production", methodGen, handle);
353 }
338354
339355 int start = fact.getNumSlots() - numProduced;
340356 Location location = new Location(handle, basicBlock);
341357 ValueNumberFrame vnaFrameAfter = vnaDataflow.getFactAfterLocation(location);
342358 if (!vnaFrameAfter.isValid()) {
343359 assert false : "Invalid VNA after location " + location + " in " + SignatureConverter.convertMethodSignature(methodGen);
344 return;
360 return;
345361 }
346362 for (int i = start; i < fact.getNumSlots(); ++i) {
347363 ValueNumber value = vnaFrameAfter.getValue(i);
361377 if (visitor.getSlotContainingNewNullValue() >= 0) {
362378 ValueNumber newNullValue = vnaFrameAfter.getValue(visitor.getSlotContainingNewNullValue());
363379 addLocationWhereValueBecomesNull(new LocationWhereValueBecomesNull(location, newNullValue// ,
364 // handle
365 ));
380 // handle
381 ));
366382 }
367383
368384 }
371387
372388 static {
373389 nullComparisonInstructionSet.set(Constants.IFNULL);
374
375390 nullComparisonInstructionSet.set(Constants.IFNONNULL);
376391 nullComparisonInstructionSet.set(Constants.IF_ACMPEQ);
377392 nullComparisonInstructionSet.set(Constants.IF_ACMPNE);
378393 }
379394
395 @Override
380396 public void meetInto(IsNullValueFrame fact, Edge edge, IsNullValueFrame result) throws DataflowAnalysisException {
381397 meetInto(fact, edge, result, true);
382398 }
390406 if (!NO_SPLIT_DOWNGRADE_NSP) {
391407 // Downgrade NSP to DNR on non-exception control splits
392408 if (!edge.isExceptionEdge() && cfg.getNumNonExceptionSucessors(edge.getSource()) > 1) {
393 tmpFact = modifyFrame(fact, tmpFact);
409 tmpFact = modifyFrame(fact, null);
394410 tmpFact.downgradeOnControlSplit();
395411 }
396412 }
417433 ObjectType catchType = handler.getCatchType();
418434 if (catchType != null) {
419435 String catchClass = catchType.getClassName();
420 if (catchClass.equals("java.lang.CloneNotSupportedException")
421 || catchClass.equals("java.lang.InterruptedException")) {
436 if ("java.lang.CloneNotSupportedException".equals(catchClass)
437 || "java.lang.InterruptedException".equals(catchClass)) {
422438 for (int i = 0; i < tmpFact.getNumSlots(); ++i) {
423439 IsNullValue value = tmpFact.getValue(i);
424 if (value.isDefinitelyNull() || value.isNullOnSomePath())
440 if (value.isDefinitelyNull() || value.isNullOnSomePath()) {
425441 tmpFact.setValue(i, IsNullValue.nullOnComplexPathValue());
442 }
426443 }
427444 }
428445 }
487504 addLocationWhereValueBecomesNull(new LocationWhereValueBecomesNull(atIf, valueTested));
488505 ValueNumber knownNonnull = getKnownNonnullDueToPointerDisequality(valueTested, atIf
489506 .getHandle().getPosition());
490 if (knownNonnull != null)
507 if (knownNonnull != null) {
491508 tmpFact = replaceValues(fact, tmpFact, knownNonnull, prevVnaFrame, targetVnaFrame,
492509 IsNullValue.checkedNonNullValue());
510 }
493511 }
494512 }
495
496513 }
497514 }
498515 }
499516 } // if (edgeType == IFCMP_EDGE || edgeType ==
500 // FALL_THROUGH_EDGE)
517 // FALL_THROUGH_EDGE)
501518
502519 // If this is a fall-through edge from a null check,
503520 // then we know the value checked is not null.
504521 if (sourceBlock.isNullCheck() && edgeType == FALL_THROUGH_EDGE) {
505522 ValueNumberFrame vnaFrame = vnaDataflow.getStartFact(destBlock);
506 if (vnaFrame == null)
523 if (vnaFrame == null) {
507524 throw new IllegalStateException("no vna frame at block entry?");
525 }
508526
509527 Instruction firstInDest = edge.getTarget().getFirstInstruction().getInstruction();
510528
534552 tmpFact = replaceValues(fact, tmpFact, replaceMe, vnaFrame, targetVnaFrame, noKaboomNonNullValue);
535553 }
536554 } // if (sourceBlock.isNullCheck() && edgeType ==
537 // FALL_THROUGH_EDGE)
555 // FALL_THROUGH_EDGE)
538556
539557 if (propagatePhiNodeInformation && targetVnaFrame.phiNodeForLoads) {
540 if (DEBUG)
558 if (DEBUG) {
541559 System.out.println("Is phi node for loads");
560 }
542561 for (ValueNumber v : fact.getKnownValues()) {
543562 AvailableLoad loadForV = sourceVnaFrame.getLoad(v);
544563 if (DEBUG) {
546565 }
547566 if (loadForV != null) {
548567 ValueNumber[] matchingValueNumbers = targetVnaFrame.getAvailableLoad(loadForV);
549 if (matchingValueNumbers != null)
568 if (matchingValueNumbers != null) {
550569 for (ValueNumber v2 : matchingValueNumbers) {
551570 tmpFact = modifyFrame(fact, tmpFact);
552571 tmpFact.useNewValueNumberForLoad(v, v2);
553 if (DEBUG)
572 if (DEBUG) {
554573 System.out.println("For " + loadForV + " switch from " + v + " to " + v2);
574 }
555575 }
576 }
556577 }
557
558578 }
559579 }
560580 }
561 if (tmpFact != null)
581 if (tmpFact != null) {
562582 fact = tmpFact;
583 }
563584 } // if (fact.isValid())
564585
565586 if (DEBUG) {
574595 }
575596 }
576597
577 /*
578 * (non-Javadoc)
579 *
580 * @see
581 * edu.umd.cs.findbugs.ba.FrameDataflowAnalysis#mergeInto(edu.umd.cs.findbugs
582 * .ba.Frame, edu.umd.cs.findbugs.ba.Frame)
583 */
584598 @Override
585599 protected void mergeInto(IsNullValueFrame other, IsNullValueFrame result) throws DataflowAnalysisException {
586 if (other.isTop())
600 if (other.isTop()) {
587601 return;
602 }
588603 if (result.isTop()) {
589604 result.copyFrom(other);
590605 return;
597612
598613 }
599614
600 /*
601 * (non-Javadoc)
602 *
603 * @see edu.umd.cs.findbugs.ba.AbstractDataflowAnalysis#startIteration()
604 */
605615 @Override
606616 public void startIteration() {
607617 // At the beginning of each iteration, clear the set of locations
645655 assert lastFrame != null;
646656
647657 final InstructionHandle lastInSourceHandle = basicBlock.getLastInstruction();
648 if (lastInSourceHandle == null)
658 if (lastInSourceHandle == null) {
649659 return null; // doesn't end in null comparison
660 }
650661
651662 final short lastInSourceOpcode = lastInSourceHandle.getInstruction().getOpcode();
652663 if (lastInSourceOpcode == Constants.IFEQ || lastInSourceOpcode == Constants.IFNE) {
653664 // check for instanceof check
654665 InstructionHandle prev = lastInSourceHandle.getPrev();
655 if (prev == null)
666 if (prev == null) {
656667 return null;
668 }
657669 short secondToLastOpcode = prev.getInstruction().getOpcode();
658670 // System.out.println("Second last opcode: " +
659671 // Constants.OPCODE_NAMES[secondToLastOpcode]);
660 if (secondToLastOpcode != Constants.INSTANCEOF)
672 if (secondToLastOpcode != Constants.INSTANCEOF) {
661673 return null;
662 if (instanceOfFrame == null)
674 }
675 if (instanceOfFrame == null) {
663676 return null;
677 }
664678 IsNullValue tos = instanceOfFrame.getTopValue();
665679 boolean isNotInstanceOf = (lastInSourceOpcode != Constants.IFNE);
666680 Location atInstanceOf = new Location(prev, basicBlock);
672686
673687 if (tos.isDefinitelyNull()) {
674688 // Predetermined comparison - one branch is infeasible
675 if (isNotInstanceOf)
689 if (isNotInstanceOf) {
676690 ifcmpDecision = tos;
677 else
691 } else {
678692 // ifnonnull
679693 fallThroughDecision = tos;
694 }
680695 } else if (tos.isDefinitelyNotNull()) {
681696 return null;
682697 } else {
684699 ifcmpDecision = isNotInstanceOf ? tos : IsNullValue.pathSensitiveNonNullValue();
685700 fallThroughDecision = isNotInstanceOf ? IsNullValue.pathSensitiveNonNullValue() : tos;
686701 }
687 if (DEBUG)
702 if (DEBUG) {
688703 System.out.println("Checking..." + tos + " -> " + ifcmpDecision + " or " + fallThroughDecision);
689
704 }
690705 return new IsNullConditionDecision(instanceOfVnaFrame.getTopValue(), ifcmpDecision, fallThroughDecision);
691
692 }
693 if (!nullComparisonInstructionSet.get(lastInSourceOpcode))
706 }
707
708 if (!nullComparisonInstructionSet.get(lastInSourceOpcode)) {
694709 return null; // doesn't end in null comparison
710 }
695711
696712 Location atIf = new Location(lastInSourceHandle, basicBlock);
697713 ValueNumberFrame prevVnaFrame = vnaDataflow.getFactAtLocation(atIf);
725741 // Redundant comparison: both values are null, only one branch
726742 // is feasible
727743 value = null; // no value will be replaced - just want to
728 // indicate that one of the branches is infeasible
729 if (cmpeq)
744 // indicate that one of the branches is infeasible
745 if (cmpeq) {
730746 ifcmpDecision = IsNullValue.pathSensitiveNullValue();
731 else
747 } else {
732748 // cmpne
733749 fallThroughDecision = IsNullValue.pathSensitiveNullValue();
750 }
734751 } else if (tosNull || nextToTosNull) {
735 if (tosNull)
752 if (tosNull) {
736753 return handleIfNull(nextToTos, prevVnaFrame.getStackValue(1), cmpeq);
754 }
737755
738756 assert nextToTosNull;
739757 return handleIfNull(tos, prevVnaFrame.getStackValue(0), cmpeq);
778796
779797 if (tos.isDefinitelyNull()) {
780798 // Predetermined comparison - one branch is infeasible
781 if (ifnull)
799 if (ifnull) {
782800 ifcmpDecision = IsNullValue.pathSensitiveNullValue();
783 else
801 } else {
784802 // ifnonnull
785803 fallThroughDecision = IsNullValue.pathSensitiveNullValue();
804 }
786805 } else if (tos.isDefinitelyNotNull()) {
787806 // Predetermined comparison - one branch is infeasible
788 if (ifnull)
807 if (ifnull) {
789808 fallThroughDecision = tos.wouldHaveBeenAKaboom() ? tos : IsNullValue.pathSensitiveNonNullValue();
790 else
809 } else {
791810 // ifnonnull
792811 ifcmpDecision = tos.wouldHaveBeenAKaboom() ? tos : IsNullValue.pathSensitiveNonNullValue();
812 }
793813 } else {
794814 // As far as we know, both branches feasible
795815 ifcmpDecision = ifnull ? IsNullValue.pathSensitiveNullValue() : IsNullValue.pathSensitiveNonNullValue();
821841 private IsNullValueFrame replaceValues(IsNullValueFrame origFrame, IsNullValueFrame frame, ValueNumber replaceMe,
822842 ValueNumberFrame prevVnaFrame, ValueNumberFrame targetVnaFrame, IsNullValue replacementValue) {
823843
824 if (!targetVnaFrame.isValid())
844 if (!targetVnaFrame.isValid()) {
825845 throw new IllegalArgumentException("Invalid frame in " + methodGen.getClassName() + "." + methodGen.getName() + " : "
826846 + methodGen.getSignature());
847 }
827848 // If required, make a copy of the frame
828849 frame = modifyFrame(origFrame, frame);
829850
842863 if (DEBUG && loadForV != null) {
843864 System.out.println("For " + replaceMe + " availableLoad is " + loadForV);
844865 ValueNumber[] matchingValueNumbers = targetVnaFrame.getAvailableLoad(loadForV);
845 if (matchingValueNumbers != null)
846 for (ValueNumber v2 : matchingValueNumbers)
866 if (matchingValueNumbers != null) {
867 for (ValueNumber v2 : matchingValueNumbers) {
847868 System.out.println(" matches " + v2);
869 }
870 }
848871 }
849872 if (loadForV != null) {
850873 ValueNumber[] matchingValueNumbers = targetVnaFrame.getAvailableLoad(loadForV);
851 if (matchingValueNumbers != null)
852 for (ValueNumber v2 : matchingValueNumbers)
874 if (matchingValueNumbers != null) {
875 for (ValueNumber v2 : matchingValueNumbers) {
853876 if (!replaceMe.equals(v2)) {
854877 frame.setKnownValue(v2, replacementValue);
855 if (DEBUG)
878 if (DEBUG) {
856879 System.out.println("For " + loadForV + " switch from " + replaceMe + " to " + v2);
880 }
857881 }
882 }
883 }
858884 }
859885 frame.setKnownValue(replaceMe, replacementValue);
860886 }
875901 if (prevVnaFrame.getValue(i).equals(replaceMe)) {
876902 ValueNumber corresponding = targetVnaFrame.getValue(i);
877903 for (int j = 0; j < targetNumSlots; ++j) {
878 if (targetVnaFrame.getValue(j).equals(corresponding))
904 if (targetVnaFrame.getValue(j).equals(corresponding)) {
879905 frame.setValue(j, replacementValue);
906 }
880907 }
881908 }
882909 }
901928 }
902929
903930 }
904
905 // vim:ts=4
2525
2626 /**
2727 * Knobs for null value analysis.
28 *
28 *
2929 * @author David Hovemeyer
3030 */
3131 public interface IsNullValueAnalysisFeatures {
5959
6060 @Override
6161 public boolean equals(Object obj) {
62 if (this == obj)
62 if (this == obj) {
6363 return true;
64 if (!(obj instanceof PointerEqualityInfo))
64 }
65 if (!(obj instanceof PointerEqualityInfo)) {
6566 return false;
67 }
6668 PointerEqualityInfo other = (PointerEqualityInfo) obj;
6769 return this.addr1.equals(other.addr1) && this.addr2.equals(other.addr2) && this.areEqual == other.areEqual;
6870 }
8890 }
8991
9092 public void cleanStaleKnowledge(ValueNumberFrame vnaFrameAfter) {
91 if (vnaFrameAfter.isTop() && !isTop())
93 if (vnaFrameAfter.isTop() && !isTop()) {
9294 throw new IllegalArgumentException("VNA frame is top");
93 if (!trackValueNumbers)
95 }
96 if (!trackValueNumbers) {
9497 return;
95 if (!ValueNumberAnalysisFeatures.REDUNDANT_LOAD_ELIMINATION)
98 }
99 if (!ValueNumberAnalysisFeatures.REDUNDANT_LOAD_ELIMINATION) {
96100 return;
101 }
97102 for (Iterator<ValueNumber> i = knownValueMap.keySet().iterator(); i.hasNext();) {
98103 ValueNumber v = i.next();
99104 if (vnaFrameAfter.getLoad(v) == null) {
100 if (IsNullValueAnalysis.DEBUG)
105 if (IsNullValueAnalysis.DEBUG) {
101106 System.out.println("PURGING " + v);
107 }
102108 i.remove();
103109 }
104110 }
115121 }
116122
117123 public void toExceptionValues() {
118 for (int i = 0; i < getNumSlots(); ++i)
124 for (int i = 0; i < getNumSlots(); ++i) {
119125 setValue(i, getValue(i).toExceptionValue());
126 }
120127
121128 if (trackValueNumbers) {
122129 Map<ValueNumber, IsNullValue> replaceMap = new HashMap<ValueNumber, IsNullValue>();
138145
139146 public void setKnownValue(@Nonnull ValueNumber valueNumber, @Nonnull IsNullValue knownValue) {
140147 assert trackValueNumbers;
141 if (valueNumber == null || knownValue == null)
148 if (valueNumber == null || knownValue == null) {
142149 throw new NullPointerException();
150 }
143151 knownValueMap.put(valueNumber, knownValue);
144152 if (IsNullValueAnalysis.DEBUG) {
145153 System.out.println("Updated information for " + valueNumber);
148156 }
149157
150158 public void useNewValueNumberForLoad(ValueNumber oldValueNumber, ValueNumber newValueNumber) {
151 if (oldValueNumber == null || newValueNumber == null)
159 if (oldValueNumber == null || newValueNumber == null) {
152160 throw new NullPointerException();
153 if (newValueNumber.equals(oldValueNumber) || !trackValueNumbers)
161 }
162 if (newValueNumber.equals(oldValueNumber) || !trackValueNumbers) {
154163 return;
164 }
155165 IsNullValue isNullValue = knownValueMap.get(oldValueNumber);
156166 if (isNullValue != null) {
157167 knownValueMap.put(newValueNumber, isNullValue);
231241
232242 @Override
233243 public boolean sameAs(Frame<IsNullValue> other) {
234 if (!(other instanceof IsNullValueFrame))
244 if (!(other instanceof IsNullValueFrame)) {
235245 return false;
236 if (!super.sameAs(other))
246 }
247 if (!super.sameAs(other)) {
237248 return false;
249 }
238250 IsNullValueFrame o2 = (IsNullValueFrame) other;
239 if (!Util.nullSafeEquals(decision, o2.decision))
251 if (!Util.nullSafeEquals(decision, o2.decision)) {
240252 return false;
241 if (trackValueNumbers && !Util.nullSafeEquals(knownValueMap, o2.knownValueMap))
253 }
254 if (trackValueNumbers && !Util.nullSafeEquals(knownValueMap, o2.knownValueMap)) {
242255 return false;
256 }
243257
244258 return true;
245259 }
291305 }
292306 }
293307
294 // vim:ts=4
9797 */
9898 @Override
9999 public void analyzeInstruction(Instruction ins) throws DataflowAnalysisException {
100 if (!getFrame().isValid())
101 return;
100 if (!getFrame().isValid()) {
101 return;
102 }
102103 slotContainingNewNullValue = -1;
103104 super.analyzeInstruction(ins);
104 if (!getFrame().isValid())
105 return;
105 if (!getFrame().isValid()) {
106 return;
107 }
106108
107109 if (!NO_ASSERT_HACK) {
108110 if (assertionMethods.isAssertionHandle(getLocation().getHandle(), cpg)) {
115117 }
116118 for (Map.Entry<ValueNumber, IsNullValue> e : frame.getKnownValueMapEntrySet()) {
117119 IsNullValue value = e.getValue();
118 if (value.isDefinitelyNull() || value.isNullOnSomePath())
120 if (value.isDefinitelyNull() || value.isNullOnSomePath()) {
119121 e.setValue(IsNullValue.nonReportingNotNullValue());
122 }
120123
121124 }
122125 }
173176
174177 Location location = getLocation();
175178
176 if (trackValueNumbers)
179 if (trackValueNumbers) {
177180 try {
178181 ValueNumberFrame vnaFrame = vnaDataflow.getFactAtLocation(location);
179182 Set<ValueNumber> nonnullParameters = UnconditionalValueDerefAnalysis.checkAllNonNullParams(location, vnaFrame,
207210 } catch (DataflowAnalysisException e) {
208211 AnalysisContext.logError("Error looking up nonnull parameters for invoked method", e);
209212 }
210 // Determine if we are going to model the return value of this call.
213 }
214 // Determine if we are going to model the return value of this call.
211215 boolean modelCallReturnValue = MODEL_NONNULL_RETURN && returnType instanceof ReferenceType;
212216
213217 if (!modelCallReturnValue) {
226230 if (targetSet.isEmpty()) {
227231 XMethod calledMethod = XFactory.createXMethod(obj, getCPG());
228232 result = getReturnValueNullness(calledMethod);
229 } else for (XMethod calledMethod : targetSet) {
230 IsNullValue pushValue = getReturnValueNullness(calledMethod);
231 if (result == null)
232 result = pushValue;
233 else
234 result = IsNullValue.merge(result, pushValue);
233 } else {
234 for (XMethod calledMethod : targetSet) {
235 IsNullValue pushValue = getReturnValueNullness(calledMethod);
236 if (result == null) {
237 result = pushValue;
238 } else {
239 result = IsNullValue.merge(result, pushValue);
240 }
241 }
235242 }
236243 } catch (DataflowAnalysisException e) {
237244 result = IsNullValue.nonReportingNotNullValue();
246253
247254 public IsNullValue getReturnValueNullness(XMethod calledMethod) {
248255 IsNullValue pushValue;
249 if (IsNullValueAnalysis.DEBUG)
256 if (IsNullValueAnalysis.DEBUG) {
250257 System.out.println("Check " + calledMethod + " for null return...");
258 }
251259 NullnessAnnotation annotation = AnalysisContext.currentAnalysisContext().getNullnessAnnotationDatabase()
252260 .getResolvedAnnotation(calledMethod, false);
253261 Boolean alwaysNonNull = AnalysisContext.currentAnalysisContext().getReturnValueNullnessPropertyDatabase()
316324 }
317325 super.visitPUTFIELD(obj);
318326 XField field = XFactory.createXField(obj, cpg);
319 if (nullValueStored != null && ValueNumberAnalysisFeatures.REDUNDANT_LOAD_ELIMINATION)
327 if (nullValueStored != null && ValueNumberAnalysisFeatures.REDUNDANT_LOAD_ELIMINATION) {
320328 try {
321329 ValueNumberFrame vnaFrameBefore = vnaDataflow.getFactAtLocation(getLocation());
322330 ValueNumber refValue = vnaFrameBefore.getStackValue(1);
323331 AvailableLoad load = new AvailableLoad(refValue, field);
324332 ValueNumberFrame vnaFrameAfter = vnaDataflow.getFactAfterLocation(getLocation());
325333 ValueNumber[] newValueNumbersForField = vnaFrameAfter.getAvailableLoad(load);
326 if (newValueNumbersForField != null && trackValueNumbers)
327 for (ValueNumber v : newValueNumbersForField)
334 if (newValueNumbersForField != null && trackValueNumbers) {
335 for (ValueNumber v : newValueNumbersForField) {
328336 getFrame().setKnownValue(v, nullValueStored);
337 }
338 }
329339 } catch (DataflowAnalysisException e) {
330340 AnalysisContext.logError("Oops", e);
331341 }
342 }
332343 }
333344
334345 @Override
384395 return;
385396 }
386397 }
387 if (field.getClassName().equals("java.util.logging.Level") && field.getName().equals("SEVERE")
388 || field.getClassName().equals("org.apache.log4j.Level")
389 && (field.getName().equals("ERROR") || field.getName().equals("FATAL")))
398 if ("java.util.logging.Level".equals(field.getClassName()) && "SEVERE".equals(field.getName())
399 || "org.apache.log4j.Level".equals(field.getClassName())
400 && ("ERROR".equals(field.getName()) || "FATAL".equals(field.getName()))) {
390401 getFrame().toExceptionValues();
402 }
391403
392404 if (field.getName().startsWith("class$")) {
393405 produce(IsNullValue.nonNullValue());
507519
508520 }
509521
510 // vim:ts=4
2323
2424 /**
2525 * A Location where a particular value number becomes null.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class LocationWhereValueBecomesNull implements Comparable<LocationWhereValueBecomesNull> {
30 private Location location;
30 private final Location location;
3131
32 private ValueNumber valueNumber;
32 private final ValueNumber valueNumber;
3333
3434 /**
3535 * Constructor.
36 *
36 *
3737 * @param location
3838 * the Location where a value becomes null
3939 * @param valueNumber
6060
6161 /*
6262 * (non-Javadoc)
63 *
63 *
6464 * @see java.lang.Comparable#compareTo(java.lang.Object)
6565 */
66 @Override
6667 public int compareTo(LocationWhereValueBecomesNull o) {
6768 int cmp = this.location.compareTo(o.location);
6869 if (cmp != 0) {
7475
7576 /*
7677 * (non-Javadoc)
77 *
78 *
7879 * @see java.lang.Object#equals(java.lang.Object)
7980 */
8081 @Override
8889
8990 /*
9091 * (non-Javadoc)
91 *
92 *
9293 * @see java.lang.Object#hashCode()
9394 */
9495 @Override
2323
2424 /**
2525 * MethodPropertyDatabase for keeping track of which methods may return null.
26 *
26 *
2727 * @author David Hovemeyer
2828 * @deprecated
2929 */
2121 import edu.umd.cs.findbugs.ba.JavaClassAndMethod;
2222
2323 public class NonNullParamViolation {
24 private JavaClassAndMethod classAndMethod;
24 private final JavaClassAndMethod classAndMethod;
2525
26 private int param;
26 private final int param;
2727
2828 public NonNullParamViolation(JavaClassAndMethod classAndMethod, int param) {
2929 this.classAndMethod = classAndMethod;
6262 import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow;
6363 import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;
6464 import edu.umd.cs.findbugs.ba.vna.ValueNumberSourceInfo;
65 import edu.umd.cs.findbugs.bcel.generic.NullnessConversationInstruction;
6665 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
6766 import edu.umd.cs.findbugs.classfile.Global;
6867 import edu.umd.cs.findbugs.log.Profiler;
108107 private final AssertionMethods assertionMethods;
109108
110109 static {
111 if (DEBUG)
110 if (DEBUG) {
112111 System.out.println("fnd.debug enabled");
112 }
113113 }
114114
115115 /**
209209 case Constants.IFNONNULL:
210210 analyzeIfNullBranch(basicBlock, lastHandle);
211211 break;
212 default:
213 break;
212214 }
213215 }
214216 }
250252 Map<ValueNumber, SortedSet<Location>> bugEdgeLocationMap = new HashMap<ValueNumber, SortedSet<Location>>();
251253
252254 checkEdges(cfg, nullValueGuaranteedDerefMap, bugEdgeLocationMap);
253
255
254256 Map<ValueNumber, SortedSet<Location>> bugLocationMap = bugEdgeLocationMap;
255257 bugLocationMap.putAll(bugStatementLocationMap);
256
258
257259 // For each value number that is null somewhere in the
258260 // method, collect the set of locations where it becomes null.
259261 // FIXME: we may see some locations that are not guaranteed to be
263265 reportBugs(nullValueGuaranteedDerefMap, npeIfStatementCovered, bugLocationMap, nullValueAssignmentMap);
264266 }
265267
266 /**
267 * @param locationWhereValueBecomesNullSet
268 * @return
269 */
270268 public Map<ValueNumber, Set<Location>> findNullAssignments(Set<LocationWhereValueBecomesNull> locationWhereValueBecomesNullSet) {
271269 Map<ValueNumber, Set<Location>> nullValueAssignmentMap = new HashMap<ValueNumber, Set<Location>>();
272270 for (LocationWhereValueBecomesNull lwvbn : locationWhereValueBecomesNullSet) {
273 if (DEBUG_DEREFS)
271 if (DEBUG_DEREFS) {
274272 System.out.println("OOO " + lwvbn);
273 }
275274 Set<Location> locationSet = nullValueAssignmentMap.get(lwvbn.getValueNumber());
276275 if (locationSet == null) {
277276 locationSet = new HashSet<Location>(4);
278277 nullValueAssignmentMap.put(lwvbn.getValueNumber(), locationSet);
279278 }
280279 locationSet.add(lwvbn.getLocation());
281 if (DEBUG_DEREFS)
280 if (DEBUG_DEREFS) {
282281 System.out.println(lwvbn.getValueNumber() + " becomes null at " + lwvbn.getLocation());
282 }
283283 }
284284 return nullValueAssignmentMap;
285285 }
286286
287 /**
288 * @param nullValueGuaranteedDerefMap
289 * @param npeIfStatementCovered
290 * @param bugLocationMap
291 * @param nullValueAssignmentMap
292 * @throws CFGBuilderException
293 * @throws DataflowAnalysisException
294 */
295287 public void reportBugs(Map<ValueNumber, NullValueUnconditionalDeref> nullValueGuaranteedDerefMap,
296288 HashSet<ValueNumber> npeIfStatementCovered, Map<ValueNumber, SortedSet<Location>> bugLocationMap,
297289 Map<ValueNumber, Set<Location>> nullValueAssignmentMap) throws CFGBuilderException, DataflowAnalysisException {
311303 }
312304 }
313305 // TODO: figure out why this is failing
314 if (false)
315 assert false : "No assigned NullLocationSet for " + valueNumber + " in " + nullValueAssignmentMap.keySet()
316 + " while analyzing " + classContext.getJavaClass().getClassName() + "." + method.getName();
306 // if (false) {
307 // assert false : "No assigned NullLocationSet for " + valueNumber + " in " + nullValueAssignmentMap.keySet()
308 // + " while analyzing " + classContext.getJavaClass().getClassName() + "." + method.getName();
309 // }
317310 assignedNullLocationSet = Collections.<Location> emptySet();
318311 }
319312 SortedSet<Location> knownNullAndDoomedAt = bugLocationMap.get(valueNumber);
323316 for (Location loc : derefLocationSet) {
324317 variableAnnotation = ValueNumberSourceInfo.findAnnotationFromValueNumber(method, loc, valueNumber,
325318 vnaDataflow.getFactAtLocation(loc), "VALUE_OF");
326 if (variableAnnotation != null)
319 if (variableAnnotation != null) {
327320 break;
328 }
329 if (variableAnnotation == null)
321 }
322 }
323 if (variableAnnotation == null) {
330324 for (Location loc : knownNullAndDoomedAt) {
331325 variableAnnotation = ValueNumberSourceInfo.findAnnotationFromValueNumber(method, loc, valueNumber,
332326 vnaDataflow.getFactAtLocation(loc), "VALUE_OF");
333 if (variableAnnotation != null)
327 if (variableAnnotation != null) {
334328 break;
329 }
335330 }
336 if (variableAnnotation == null)
331 }
332 if (variableAnnotation == null) {
337333 for (Location loc : assignedNullLocationSet) {
338334 variableAnnotation = ValueNumberSourceInfo.findAnnotationFromValueNumber(method, loc, valueNumber,
339335 vnaDataflow.getFactAtLocation(loc), "VALUE_OF");
340 if (variableAnnotation != null)
336 if (variableAnnotation != null) {
341337 break;
338 }
342339 }
340 }
343341
344342 } catch (DataflowAnalysisException e2) {
345343 }
346 if (variableAnnotation == null)
344 if (variableAnnotation == null) {
347345 variableAnnotation = new LocalVariableAnnotation("?", -1, derefLocationSet.iterator().next().getHandle()
348346 .getPosition());
347 }
349348
350349 if (PRUNE_GUARANTEED_DEREFERENCES) {
351350 PostDominatorsAnalysis postDomAnalysis = classContext.getNonExceptionPostDominatorsAnalysis(method);
363362 }
364363 }
365364
366 /**
367 * @param cfg
368 * @param nullValueGuaranteedDerefMap
369 * @param bugEdgeLocationMap
370 * @throws DataflowAnalysisException
371 */
372365 public void checkEdges(CFG cfg, Map<ValueNumber, NullValueUnconditionalDeref> nullValueGuaranteedDerefMap,
373366 Map<ValueNumber, SortedSet<Location>> bugEdgeLocationMap) throws DataflowAnalysisException {
374367 // Check every non-exception control edge
377370
378371 UnconditionalValueDerefSet uvdFact = uvdDataflow.getFactOnEdge(edge);
379372
380 if (uvdFact.isEmpty())
373 if (uvdFact.isEmpty()) {
381374 continue;
382
375 }
376
383377 if (edge.isExceptionEdge()) {
384378 if (DEBUG_DEREFS) {
385379 System.out.println("On exception edge " + edge.formatAsString(false));
399393 Location location = null;
400394 if (edge.isExceptionEdge()) {
401395 BasicBlock b = cfg.getSuccessorWithEdgeType(source, EdgeTypes.FALL_THROUGH_EDGE);
402 if (b != null)
396 if (b != null) {
403397 location = new Location(source.getExceptionThrower(), b);
404 } else
398 }
399 } else {
405400 location = Location.getLastLocation(source);
401 }
406402
407403 if (location != null) {
408404 Instruction in = location.getHandle().getInstruction();
409405 if (assertionMethods.isAssertionInstruction(in, classContext.getConstantPoolGen())) {
410 if (DEBUG_DEREFS)
406 if (DEBUG_DEREFS) {
411407 System.out.println("Skipping because it is an assertion method ");
408 }
412409 continue;
413410 }
414411
475472 * location in the CFG.
476473 *
477474 * @param thisLocation
478 * TODO
479475 * @param knownNullAndDoomedAt
480 * TODO
481476 * @param nullValueGuaranteedDerefMap
482477 * map to be populated with null values and where they are
483478 * derefed
488483 * @param derefSet
489484 * set of unconditionally derefed values at this location
490485 * @param isEdge
491 * TODO
492486 */
493487 private void checkForUnconditionallyDereferencedNullValues(Location thisLocation,
494488 Map<ValueNumber, SortedSet<Location>> knownNullAndDoomedAt,
508502 }
509503
510504 int slots;
511 if (vnaFrame.getNumSlots() == invFrame.getNumSlots())
505 if (vnaFrame.getNumSlots() == invFrame.getNumSlots()) {
512506 slots = vnaFrame.getNumSlots();
513 else
507 } else {
514508 slots = vnaFrame.getNumLocals();
515
509 /*
516510 if (false) {
517511 InstructionHandle handle = thisLocation.getHandle();
518512 if (handle != null && handle.getInstruction() instanceof NullnessConversationInstruction) {
538532 AnalysisContext.logError("huh", e);
539533 }
540534 }
541
535 }
536 */
542537 }
543538
544539 // See if there are any definitely-null values in the frame
593588 * dereferenced.
594589 *
595590 * @param thisLocation
596 * TODO
597591 * @param bugLocations
598 * TODO
599592 * @param nullValueGuaranteedDerefMap
600593 * map of null values to sets of Locations where they are derefed
601594 * @param derefSet
646639 */
647640 private void examineRedundantBranches() {
648641 for (RedundantBranch redundantBranch : redundantBranchList) {
649 if (DEBUG)
642 if (DEBUG) {
650643 System.out.println("Redundant branch: " + redundantBranch);
644 }
651645 int lineNumber = redundantBranch.lineNumber;
652646
653647 // The source to bytecode compiler may sometimes duplicate blocks of
664658
665659 boolean reportIt = true;
666660
667 if (lineMentionedMultipleTimes.get(lineNumber) && confused)
661 if (lineMentionedMultipleTimes.get(lineNumber) && confused) {
668662 reportIt = false;
669 else if (redundantBranch.location.getBasicBlock().isInJSRSubroutine() /*
670 * occurs
671 * in
672 * a
673 * JSR
674 */
675 && confused)
663 } else if (redundantBranch.location.getBasicBlock().isInJSRSubroutine() /*
664 * occurs
665 * in
666 * a
667 * JSR
668 */
669 && confused) {
676670 reportIt = false;
677 else {
671 } else {
678672 int pc = redundantBranch.location.getHandle().getPosition();
679673 for (CodeException e : method.getCode().getExceptionTable()) {
680674 if (e.getCatchType() == 0 && e.getStartPC() != e.getHandlerPC() && e.getEndPC() <= pc
681 && pc <= e.getEndPC() + 5)
675 && pc <= e.getEndPC() + 5) {
682676 reportIt = false;
677 }
683678 }
684679 }
685680
698693 // Probably dead code due to pruning infeasible exception edges.
699694 return;
700695 }
701 if (frame.getStackDepth() < 2)
696 if (frame.getStackDepth() < 2) {
702697 throw new DataflowAnalysisException("Stack underflow at " + lastHandle);
698 }
703699
704700 // Find the line number.
705701 int lineNumber = getLineNumber(method, lastHandle);
706 if (lineNumber < 0)
707 return;
702 if (lineNumber < 0) {
703 return;
704 }
708705
709706 int numSlots = frame.getNumSlots();
710707 IsNullValue top = frame.getValue(numSlots - 1);
716713
717714 if (definitelySame || definitelyDifferent) {
718715 if (definitelySame) {
719 if (DEBUG)
716 if (DEBUG) {
720717 System.out.println("Line " + lineNumber + " always same");
718 }
721719 definitelySameBranchSet.set(lineNumber);
722720 }
723721 if (definitelyDifferent) {
724 if (DEBUG)
722 if (DEBUG) {
725723 System.out.println("Line " + lineNumber + " always different");
724 }
726725 definitelyDifferentBranchSet.set(lineNumber);
727726 }
728727
735734 Edge infeasibleEdge = invDataflow.getCFG().getOutgoingEdgeWithType(basicBlock, infeasibleEdgeType);
736735 redundantBranch.setInfeasibleEdge(infeasibleEdge);
737736
738 if (DEBUG)
737 if (DEBUG) {
739738 System.out.println("Adding redundant branch: " + redundantBranch);
739 }
740740 redundantBranchList.add(redundantBranch);
741741 } else {
742 if (DEBUG)
742 if (DEBUG) {
743743 System.out.println("Line " + lineNumber + " undetermined");
744 }
744745 undeterminedBranchSet.set(lineNumber);
745746 }
746747 }
747748
748 // This is called for both IFNULL and IFNONNULL instructions.
749 /** This is called for both IFNULL and IFNONNULL instructions. */
749750 private void analyzeIfNullBranch(BasicBlock basicBlock, InstructionHandle lastHandle) throws DataflowAnalysisException {
750751
751752 Location location = new Location(lastHandle, basicBlock);
761762
762763 // Find the line number.
763764 int lineNumber = getLineNumber(method, lastHandle);
764 if (lineNumber < 0)
765 return;
765 if (lineNumber < 0) {
766 return;
767 }
766768
767769 if (!(top.isDefinitelyNull() || top.isDefinitelyNotNull())) {
768 if (DEBUG)
770 if (DEBUG) {
769771 System.out.println("Line " + lineNumber + " undetermined");
772 }
770773 undeterminedBranchSet.set(lineNumber);
771774 return;
772775 }
775778 // or always not taken.
776779 short opcode = lastHandle.getInstruction().getOpcode();
777780 boolean definitelySame = top.isDefinitelyNull();
778 if (opcode != Constants.IFNULL)
781 if (opcode != Constants.IFNULL) {
779782 definitelySame = !definitelySame;
783 }
780784
781785 if (definitelySame) {
782 if (DEBUG)
786 if (DEBUG) {
783787 System.out.println("Line " + lineNumber + " always same");
788 }
784789 definitelySameBranchSet.set(lineNumber);
785790 } else {
786 if (DEBUG)
791 if (DEBUG) {
787792 System.out.println("Line " + lineNumber + " always different");
793 }
788794 definitelyDifferentBranchSet.set(lineNumber);
789795 }
790796
797803 Edge infeasibleEdge = invDataflow.getCFG().getOutgoingEdgeWithType(basicBlock, infeasibleEdgeType);
798804 redundantBranch.setInfeasibleEdge(infeasibleEdge);
799805
800 if (DEBUG)
806 if (DEBUG) {
801807 System.out.println("Adding redundant branch: " + redundantBranch);
808 }
802809 redundantBranchList.add(redundantBranch);
803810 }
804811
805812 private void analyzeNullCheck(IsNullValueDataflow invDataflow, BasicBlock basicBlock) throws DataflowAnalysisException,
806 CFGBuilderException {
813 CFGBuilderException {
807814 // Look for null checks where the value checked is definitely
808815 // null or null on some path.
809816
812819
813820 // Get the stack values at entry to the null check.
814821 IsNullValueFrame frame = invDataflow.getStartFact(basicBlock);
815 if (!frame.isValid())
816 return;
822 if (!frame.isValid()) {
823 return;
824 }
817825
818826 // Could the reference be null?
819827 IsNullValue refValue = frame.getInstance(exceptionThrower, classContext.getConstantPoolGen());
821829 System.out.println("For basic block " + basicBlock + " value is " + refValue);
822830 }
823831 if (refValue.isDefinitelyNotNull())
824 return;
825 if (false && !refValue.mightBeNull())
826 return;
827
828 if (!refValue.isDefinitelyNull())
829 return;
832 {
833 return;
834 // if (false && !refValue.mightBeNull())
835 // return;
836 }
837
838 if (!refValue.isDefinitelyNull()) {
839 return;
840 }
830841 // Get the value number
831842 ValueNumberFrame vnaFrame = classContext.getValueNumberDataflow(method).getStartFact(basicBlock);
832 if (!vnaFrame.isValid())
833 return;
843 if (!vnaFrame.isValid()) {
844 return;
845 }
834846 ValueNumber valueNumber = vnaFrame.getInstance(exceptionThrower, classContext.getConstantPoolGen());
835847 Location location = new Location(exceptionThrowerHandle, basicBlock);
836 if (DEBUG)
848 if (DEBUG) {
837849 System.out.println("Warning: VN " + valueNumber + " invf: " + frame + " @ " + location);
850 }
838851
839852 boolean isConsistent = true;
840853 Iterator<BasicBlock> bbIter = invDataflow.getCFG().blockIterator();
844857 while (bbIter.hasNext()) {
845858 BasicBlock bb = bbIter.next();
846859
847 if (!bb.isNullCheck())
860 if (!bb.isNullCheck()) {
848861 continue;
862 }
849863
850864 InstructionHandle eth = bb.getExceptionThrower();
851 if (eth == exceptionThrowerHandle)
865 if (eth == exceptionThrowerHandle) {
852866 continue;
853 if (eth.getInstruction().getOpcode() != exceptionThrower.getOpcode())
867 }
868 if (eth.getInstruction().getOpcode() != exceptionThrower.getOpcode()) {
854869 continue;
870 }
855871
856872 int ePosition = eth.getPosition();
857873 if (ePosition == position || table != null && line == table.getSourceLine(ePosition)) {
858874
859875 IsNullValueFrame frame2 = invDataflow.getStartFact(bb);
860 if (!frame2.isValid())
876 if (!frame2.isValid()) {
861877 continue;
878 }
862879 // apparent clone
863880 IsNullValue rv = frame2.getInstance(eth.getInstruction(), classContext.getConstantPoolGen());
864 if (!rv.equals(refValue))
881 if (!rv.equals(refValue)) {
865882 isConsistent = false;
883 }
866884 }
867885
868886 }
904922 }
905923
906924 /**
907 * @param method
908 * TODO
909 * @param location
910 * @param valueNumber
911 * @param vnaFrame
912 * @return the annotation
913925 * @deprecated Use
914926 * {@link ValueNumberSourceInfo#findRequiredAnnotationFromValueNumber(Method,Location,ValueNumber,ValueNumberFrame, String)}
915927 * instead
922934
923935 private static int getLineNumber(Method method, InstructionHandle handle) {
924936 LineNumberTable table = method.getCode().getLineNumberTable();
925 if (table == null)
937 if (table == null) {
926938 return -1;
939 }
927940 return table.getSourceLine(handle.getPosition());
928941 }
929942 }
1717
1818 private boolean alwaysReadlineValue;
1919
20 private Set<Location> derefLocationSet;
20 private final Set<Location> derefLocationSet;
2121
2222 public NullValueUnconditionalDeref() {
2323 this.alwaysOnExceptionPath = true;
3535 if (!isNullValue.isException()) {
3636 alwaysOnExceptionPath = false;
3737 }
38 if (!isNullValue.isFieldValue())
38 if (!isNullValue.isFieldValue()) {
3939 alwaysFieldValue = false;
40 }
4041 if (!isNullValue.isReturnValue()) {
4142 alwaysMethodReturnValue = false;
4243 }
2525 /**
2626 * Method property database storing which method parameters might be
2727 * unconditionally dereferenced.
28 *
28 *
2929 * @author David Hovemeyer
3030 */
3131 public class ParameterNullnessPropertyDatabase extends MethodPropertyDatabase<ParameterProperty> {
3232
3333 /*
3434 * (non-Javadoc)
35 *
35 *
3636 * @see
3737 * edu.umd.cs.findbugs.ba.interproc.MethodPropertyDatabase#decodeProperty
3838 * (java.lang.String)
5151
5252 /*
5353 * (non-Javadoc)
54 *
54 *
5555 * @see
5656 * edu.umd.cs.findbugs.ba.interproc.MethodPropertyDatabase#encodeProperty
5757 * (Property)
3737
3838 /**
3939 * Constructor.
40 *
40 *
4141 * @param location
4242 * Location of ref comparison
4343 * @param lineNumber
5656
5757 /**
5858 * Constructor.
59 *
59 *
6060 * @param location
6161 * Location of ref comparison
6262 * @param lineNumber
7373
7474 /**
7575 * Set the edge which has been determined to be infeasible.
76 *
76 *
7777 * @param infeasibleEdge
7878 * The infeasibleEdge to set.
7979 */
2828 * <li>It is not possible to return normally (i.e., an exception is guaranteed
2929 * to be thrown)</li>
3030 * </ul>
31 *
31 *
3232 * @author David Hovemeyer
3333 */
3434 public class ReturnPathType {
6060
6161 /**
6262 * Make this dataflow fact an exact copy of the other one.
63 *
63 *
6464 * @param other
6565 * another dataflow fact
6666 */
8484
8585 /**
8686 * Set whether or not it is possible to return normally.
87 *
87 *
8888 * @param canReturnNormally
8989 * true if the method can return normally at this location, false
9090 * otherwise
9595
9696 /**
9797 * Merge this fact with given fact.
98 *
98 *
9999 * @param fact
100100 * another dataflow fact
101101 */
118118
119119 /**
120120 * Determine whether this dataflow fact is identical to another one.
121 *
121 *
122122 * @param other
123123 * another dataflow fact
124124 * @return true if the two dataflow facts are identical, false if they are
138138
139139 /*
140140 * (non-Javadoc)
141 *
141 *
142142 * @see java.lang.Object#toString()
143143 */
144144 @Override
1818
1919 package edu.umd.cs.findbugs.ba.npe;
2020
21 import java.util.HashMap;
22
2321 import org.apache.bcel.generic.InstructionHandle;
2422
2523 import edu.umd.cs.findbugs.ba.BasicAbstractDataflowAnalysis;
3533 /**
3634 * A dataflow analysis to determine, at each location in a method's CFG, whether
3735 * or not it is possible to return normally at that location.
38 *
36 *
3937 * @author David Hovemeyer
4038 */
4139 public class ReturnPathTypeAnalysis extends BasicAbstractDataflowAnalysis<ReturnPathType> {
42 private CFG cfg;
40 private final CFG cfg;
4341
44 private DepthFirstSearch dfs;
42 private final DepthFirstSearch dfs;
4543
46 private ReverseDepthFirstSearch rdfs;
44 private final ReverseDepthFirstSearch rdfs;
4745
4846 /**
4947 * Constructor.
50 *
48 *
5149 * @param cfg
5250 * the method's CFG
5351 * @param rdfs
6159 this.rdfs = rdfs;
6260 }
6361
64 /*
65 * (non-Javadoc)
66 *
67 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#copy(java.lang.Object,
68 * java.lang.Object)
69 */
62 @Override
7063 public void copy(ReturnPathType source, ReturnPathType dest) {
7164 dest.copyFrom(source);
7265 }
7366
74 /*
75 * (non-Javadoc)
76 *
77 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#createFact()
78 */
67 @Override
7968 public ReturnPathType createFact() {
8069 ReturnPathType fact = new ReturnPathType();
8170 fact.setTop();
8271 return fact;
8372 }
8473
85 /*
86 * (non-Javadoc)
87 *
88 * @see
89 * edu.umd.cs.findbugs.ba.DataflowAnalysis#edgeTransfer(edu.umd.cs.findbugs
90 * .ba.Edge, java.lang.Object)
91 */
9274 @Override
9375 public void edgeTransfer(Edge edge, ReturnPathType fact) {
9476 // The edges leading into the exit block create the "seed" values
10183 }
10284 }
10385
104 /*
105 * (non-Javadoc)
106 *
107 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#finishIteration()
108 */
10986 @Override
11087 public void finishIteration() {
11188 // nothing to do
11289 }
11390
114 /*
115 * (non-Javadoc)
116 *
117 * @see
118 * edu.umd.cs.findbugs.ba.DataflowAnalysis#getBlockOrder(edu.umd.cs.findbugs
119 * .ba.CFG)
120 */
91 @Override
12192 public BlockOrder getBlockOrder(CFG cfg) {
12293 return new ReverseDFSOrder(cfg, rdfs, dfs);
12394 }
12495
125 /*
126 * (non-Javadoc)
127 *
128 * @see
129 * edu.umd.cs.findbugs.ba.DataflowAnalysis#getLastUpdateTimestamp(java.lang
130 * .Object)
131 */
13296 @Override
13397 public int getLastUpdateTimestamp(ReturnPathType fact) {
13498 return 0;
13599 }
136100
137 /**
101 /*
138102 * Look up a dataflow value in given map, creating a new (top) fact if no
139103 * fact currently exists.
140 *
104 *
141105 * @param map
142106 * the map
143107 * @param block
144108 * BasicBlock used as key in map
145109 * @return the dataflow fact for the block
146 */
110 *
147111 private ReturnPathType getOrCreateFact(HashMap<BasicBlock, ReturnPathType> map, BasicBlock block) {
148112 ReturnPathType returnPathType = map.get(block);
149113 if (returnPathType == null) {
152116 }
153117 return returnPathType;
154118 }
119 */
155120
156 /*
157 * (non-Javadoc)
158 *
159 * @see
160 * edu.umd.cs.findbugs.ba.DataflowAnalysis#initEntryFact(java.lang.Object)
161 */
121 @Override
162122 public void initEntryFact(ReturnPathType result) throws DataflowAnalysisException {
163123 // By default, we assume that paths reaching the exit block
164124 // can return normally. If we traverse exception edges which
168128 result.setCanReturnNormally(true);
169129 }
170130
171 /*
172 * (non-Javadoc)
173 *
174 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#isForwards()
175 */
131 @Override
176132 public boolean isForwards() {
177133 return false;
178134 }
179135
180 /*
181 * (non-Javadoc)
182 *
183 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#isTop(java.lang.Object)
184 */
136 @Override
185137 public boolean isTop(ReturnPathType fact) {
186138 return fact.isTop();
187139 }
188140
189 /*
190 * (non-Javadoc)
191 *
192 * @see
193 * edu.umd.cs.findbugs.ba.DataflowAnalysis#makeFactTop(java.lang.Object)
194 */
141 @Override
195142 public void makeFactTop(ReturnPathType fact) {
196143 fact.setTop();
197144 }
198145
199 /*
200 * (non-Javadoc)
201 *
202 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#meetInto(java.lang.Object,
203 * edu.umd.cs.findbugs.ba.Edge, java.lang.Object)
204 */
146 @Override
205147 public void meetInto(ReturnPathType fact, Edge edge, ReturnPathType result) throws DataflowAnalysisException {
206148 result.mergeWith(fact);
207149 }
208150
209 /*
210 * (non-Javadoc)
211 *
212 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#same(java.lang.Object,
213 * java.lang.Object)
214 */
151 @Override
215152 public boolean same(ReturnPathType fact1, ReturnPathType fact2) {
216153 return fact1.sameAs(fact2);
217154 }
218155
219 /*
220 * (non-Javadoc)
221 *
222 * @see
223 * edu.umd.cs.findbugs.ba.DataflowAnalysis#setLastUpdateTimestamp(java.lang
224 * .Object, int)
225 */
226156 @Override
227157 public void setLastUpdateTimestamp(ReturnPathType fact, int timestamp) {
228158 // ignore
229159 }
230160
231 /*
232 * (non-Javadoc)
233 *
234 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#startIteration()
235 */
236161 @Override
237162 public void startIteration() {
238163 // nothing to do
239164 }
240165
241 /*
242 * (non-Javadoc)
243 *
244 * @see
245 * edu.umd.cs.findbugs.ba.DataflowAnalysis#transfer(edu.umd.cs.findbugs.
246 * ba.BasicBlock, org.apache.bcel.generic.InstructionHandle,
247 * java.lang.Object, java.lang.Object)
248 */
166 @Override
249167 public void transfer(BasicBlock basicBlock, InstructionHandle end, ReturnPathType start, ReturnPathType result)
250168 throws DataflowAnalysisException {
251169 // just copy the start fact
2323
2424 /**
2525 * Dataflow class for ReturnPathTypeAnalysis.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class ReturnPathTypeDataflow extends Dataflow<ReturnPathType, ReturnPathTypeAnalysis> {
3030
3131 /**
3232 * Constructor.
33 *
33 *
3434 * @param cfg
3535 * CFG of the method being analyzed
3636 * @param analysis
2323 /**
2424 * Method property database storing which methods return values guaranteed to be
2525 * nonnull
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class ReturnValueNullnessPropertyDatabase extends MethodPropertyDatabase<Boolean> {
3030
3131 /*
3232 * (non-Javadoc)
33 *
33 *
3434 * @see
3535 * edu.umd.cs.findbugs.ba.interproc.MethodPropertyDatabase#decodeProperty
3636 * (java.lang.String)
4444
4545 /*
4646 * (non-Javadoc)
47 *
47 *
4848 * @see
4949 * edu.umd.cs.findbugs.ba.interproc.MethodPropertyDatabase#encodeProperty
5050 * (Property)
6969 this.nonnullTypeQualifierValue = TypeQualifierValue.getValue(javax.annotation.Nonnull.class, null);
7070 }
7171
72 /*
73 * (non-Javadoc)
74 *
75 * @see
76 * edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#getResolvedAnnotation
77 * (java.lang.Object, boolean)
78 */
72 @Override
7973 public NullnessAnnotation getResolvedAnnotation(Object o, boolean getMinimal) {
8074 Profiler profiler = Global.getAnalysisCache().getProfiler();
8175 profiler.start(this.getClass());
9993
10094 NullnessAnnotation result = toNullnessAnnotation(tqa);
10195 if (DEBUG) {
102 if (result == null)
96 if (result == null) {
10397 System.out.println(" ===> not found");
104 else
98 } else {
10599 System.out.println(" ===> " + tqa + "/" + result.toString() );
100 }
106101 }
107102 return result;
108103 } finally {
116111 try {
117112 TypeQualifierAnnotation tqa
118113 = TypeQualifierApplications.getInheritedTypeQualifierAnnotation(m,
119 parameter, nonnullTypeQualifierValue);
114 parameter, nonnullTypeQualifierValue);
120115 NullnessAnnotation result = toNullnessAnnotation(tqa);
121116 return result;
122117 } finally {
157152
158153 NullnessAnnotation result = toNullnessAnnotation(tqa);
159154 if (DEBUG) {
160 if (result == null)
155 if (result == null) {
161156 System.out.println(" ===> not found");
162 else
157 } else {
163158 System.out.println(" ===> " + tqa + "/" + result.toString() );
159 }
164160 }
165161 return result;
166162 } finally {
167163 profiler.end(this.getClass());
168164 }
169165 }
170
166
171167 public static boolean assertsFirstParameterIsNonnull(XMethod m) {
172 return (m.getName().equalsIgnoreCase("checkNonNull")
173 || m.getName().equalsIgnoreCase("checkNotNull")
168 return ("checkNonNull".equalsIgnoreCase(m.getName())
169 || "checkNotNull".equalsIgnoreCase(m.getName())
174170 // JDK 7 java.util.Objects.requireNonNull(Object)
175 || m.getName().equals("requireNonNull")
171 || "requireNonNull".equals(m.getName())
176172 // org.eclipse.core.runtime.Assert(Object)
177 || m.getName().equalsIgnoreCase("isNotNull")
178 || m.getName().equalsIgnoreCase("assertNotNull"))
179 && m.getSignature().startsWith("(Ljava/lang/Object;");
180 }
181
182 /*
183 * (non-Javadoc)
184 *
185 * @see
186 * edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#parameterMustBeNonNull
187 * (edu.umd.cs.findbugs.ba.XMethod, int)
188 */
173 || "isNotNull".equalsIgnoreCase(m.getName())
174 || "assertNotNull".equalsIgnoreCase(m.getName()))
175 && m.getSignature().startsWith("(Ljava/lang/Object;");
176 }
177
178 @Override
189179 public boolean parameterMustBeNonNull(XMethod m, int param) {
190180 if (DEBUG) {
191181 System.out.print("Checking " + m + " param " + param + " for @Nonnull...");
196186 if (tqa == null && param == 0) {
197187 String name = m.getName();
198188 String signature = m.getSignature();
199 if (name.equals("main") && signature.equals("([Ljava/lang/String;)V") && m.isStatic() && m.isPublic())
189 if ("main".equals(name) && "([Ljava/lang/String;)V".equals(signature) && m.isStatic() && m.isPublic()) {
200190 return true;
201 else if (assertsFirstParameterIsNonnull(m))
191 } else if (assertsFirstParameterIsNonnull(m)) {
202192 return true;
203 else if (name.equals("compareTo") && signature.substring(signature.indexOf(";") + 1).equals(")Z") && !m.isStatic())
193 } else if ("compareTo".equals(name) && ")Z".equals(signature.substring(signature.indexOf(';') + 1)) && !m.isStatic()) {
204194 return true;
195 }
205196 }
206197 boolean answer = (tqa != null) && tqa.when == When.ALWAYS;
207198
247238 private static final ClassDescriptor RETURN_VALUES_ARE_NONNULL_BY_DEFAULT = DescriptorFactory
248239 .createClassDescriptor(edu.umd.cs.findbugs.annotations.ReturnValuesAreNonnullByDefault.class);
249240
250 /*
251 * (non-Javadoc)
252 *
253 * @see
254 * edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addDefaultAnnotation
255 * (java.lang.String, java.lang.String,
256 * edu.umd.cs.findbugs.ba.NullnessAnnotation)
257 */
241 @Override
258242 public void addDefaultAnnotation(Target target, String c, NullnessAnnotation n) {
259243 if (DEBUG) {
260244 System.out.println("addDefaultAnnotation: target=" + target + ", c=" + c + ", n=" + n);
312296 xclass.addAnnotation(annotationValue);
313297 }
314298
315 // /* (non-Javadoc)
316 // * @see
317 // edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addDefaultMethodAnnotation(java.lang.String,
318 // edu.umd.cs.findbugs.ba.NullnessAnnotation)
319 // */
320 // public void addDefaultMethodAnnotation(String name, NullnessAnnotation
321 // annotation) {
322 // }
323
324 /*
325 * (non-Javadoc)
326 *
327 * @see
328 * edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addFieldAnnotation
329 * (java.lang.String, java.lang.String, java.lang.String, boolean,
330 * edu.umd.cs.findbugs.ba.NullnessAnnotation)
331 */
299 @Override
332300 public void addFieldAnnotation(String cName, String mName, String mSig, boolean isStatic, NullnessAnnotation annotation) {
333301 if (DEBUG) {
334302 System.out.println("addFieldAnnotation: annotate " + cName + "." + mName + " with " + annotation);
376344 }
377345 XMethod xmethod = xclass.findMethod(mName, sig, isStatic);
378346
379 if (xmethod == null)
347 if (xmethod == null) {
380348 xmethod = XFactory.createXMethod(cName, mName, sig, isStatic);
349 }
381350 return xmethod;
382351
383352 }
384353
385 /*
386 * (non-Javadoc)
387 *
388 * @see
389 * edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#addMethodAnnotation
390 * (java.lang.String, java.lang.String, java.lang.String, boolean,
391 * edu.umd.cs.findbugs.ba.NullnessAnnotation)
392 */
354 @Override
393355 public void addMethodAnnotation(String cName, String mName, String sig, boolean isStatic, NullnessAnnotation annotation) {
394356 if (DEBUG) {
395357 System.out.println("addMethodAnnotation: annotate " + cName + "." + mName + " with " + annotation);
396358 }
397359 XMethod xmethod = getXMethod(cName, mName, sig, isStatic);
398 if (xmethod == null)
399 return;
360 if (xmethod == null) {
361 return;
362 }
400363 // Get JSR-305 nullness annotation type
401364 ClassDescriptor nullnessAnnotationType = getNullnessAnnotationClassDescriptor(annotation);
402365
407370 xmethod.addAnnotation(annotationValue);
408371 }
409372
410 /*
411 * (non-Javadoc)
412 *
413 * @see edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#
414 * addMethodParameterAnnotation(java.lang.String, java.lang.String,
415 * java.lang.String, boolean, int,
416 * edu.umd.cs.findbugs.ba.NullnessAnnotation)
417 */
373 @Override
418374 public void addMethodParameterAnnotation(@DottedClassName String cName, String mName, String sig, boolean isStatic,
419375 int param, NullnessAnnotation annotation) {
420376 if (DEBUG) {
422378 + annotation);
423379 }
424380 XMethod xmethod = getXMethod(cName, mName, sig, isStatic);
425 if (xmethod == null)
426 return;
381 if (xmethod == null) {
382 return;
383 }
427384 // Get JSR-305 nullness annotation type
428385 ClassDescriptor nullnessAnnotationType = getNullnessAnnotationClassDescriptor(annotation);
429386
431388 AnnotationValue annotationValue = new AnnotationValue(nullnessAnnotationType);
432389
433390 if (!xmethod.getClassName().equals(cName)) {
434 if (SystemProperties.ASSERTIONS_ENABLED)
391 if (SystemProperties.ASSERTIONS_ENABLED) {
435392 AnalysisContext.logError("Could not fully resolve method " + cName + "." + mName + sig + " to apply annotation "
436393 + annotation);
394 }
437395 return;
438396 }
439397
448406 xmethod.addParameterAnnotation(param, annotationValue);
449407 }
450408
451 /*
452 * (non-Javadoc)
453 *
454 * @see
455 * edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase#loadAuxiliaryAnnotations
456 * ()
457 */
409 @Override
458410 public void loadAuxiliaryAnnotations() {
459411 DefaultNullnessAnnotations.addDefaultNullnessAnnotations(this);
460412 }
468420 * @return corresponding NullnessAnnotation
469421 */
470422 private @CheckForNull NullnessAnnotation toNullnessAnnotation(@CheckForNull TypeQualifierAnnotation tqa) {
471 if (tqa == null || tqa == TypeQualifierAnnotation.OVERRIDES_BUT_NO_ANNOTATION)
423 if (tqa == null || tqa == TypeQualifierAnnotation.OVERRIDES_BUT_NO_ANNOTATION) {
472424 return null;
425 }
473426 if (tqa.when == null) {
474427 new NullPointerException("TGA value with null when field").printStackTrace();
475428 return null;
476429 }
477
478
479
430
431
432
480433 switch (tqa.when) {
481434 case ALWAYS:
482435 return NullnessAnnotation.NONNULL;
5757 @Override
5858 public String toString() {
5959 StringBuilder buf = new StringBuilder();
60 for (Map.Entry<Integer, Collection<Pair>> e : map.asMap().entrySet())
60 for (Map.Entry<Integer, Collection<Pair>> e : map.asMap().entrySet()) {
6161 buf.append(e).append("\n");
62 }
6263 return buf.toString();
6364 }
6465
6566 public void add(Location loc, ValueNumber vn, PointerUsageRequiringNonNullValue usage) {
6667 Pair p = new Pair(vn, usage);
67 if (DerefFinder.DEBUG)
68 if (DerefFinder.DEBUG) {
6869 System.out.println("At " + loc + " adding dereference " + p);
70 }
6971
7072 map.add(loc.getHandle().getPosition(), p);
7173 }
7577 // PointerUsageRequiringNonNullValue secondBest = null;
7678 MergeTree mergeTree = vnaDataflow.getAnalysis().getMergeTree();
7779 for (Pair p : map.get(loc.getHandle().getPosition())) {
78 if (p.vn.equals(vn))
80 if (p.vn.equals(vn)) {
7981 return p.pu;
80 if (!p.vn.hasFlag(ValueNumber.PHI_NODE))
82 }
83 if (!p.vn.hasFlag(ValueNumber.PHI_NODE)) {
8184 continue;
85 }
8286 BitSet inputs = mergeTree.getTransitiveInputSet(p.vn);
83 if (inputs.get(vn.getNumber()))
87 if (inputs.get(vn.getNumber())) {
8488 return p.pu;
89 }
8590 }
8691 return null;
8792 }
8484
8585 static final ClassDescriptor WILL_CLOSE = DescriptorFactory.createClassDescriptor(WillClose.class);
8686 public Collection<ObligationPolicyDatabaseAction> getActions(BasicBlock block, InstructionHandle handle) {
87 Collection<ObligationPolicyDatabaseAction> actionList = actionCache.get(handle);
87 Collection<ObligationPolicyDatabaseAction> actionList = actionCache.get(handle);
8888 if (actionList == null) {
8989 Instruction ins = handle.getInstruction();
9090 actionList = Collections.emptyList();
113113 if (actionList.isEmpty()) {
114114
115115 try {
116 TypeFrame factAtLocation = null;
117 SignatureParser sigParser = new SignatureParser(signature);
118 // int startIndex = 0;
119 // if (!xmethod.isStatic())
120 // startIndex = 1;
121 Iterator<String> signatureIterator = sigParser.parameterSignatureIterator();
122 int parameters = sigParser.getNumParameters();
123 for (int i = 0; i < parameters; i++) {
124 String sig = signatureIterator.next();
125 Collection<ClassDescriptor> annotations = invokedMethod.getParameterAnnotationDescriptors(i);
126 if (annotations.contains(WILL_CLOSE) || sig.equals("Ljava/io/Closeable;") || methodName.startsWith("close")) {
127 // closing this value
128 if (factAtLocation == null)
129 factAtLocation = typeDataflow.getFactAtLocation( new Location(handle, block));
130
131 Type argumentType = factAtLocation.getArgument(inv, cpg, i, sigParser);
132 if (argumentType instanceof ObjectType) {
133 Obligation obligation = database.getFactory().getObligationByType((ObjectType) argumentType);
134 if (obligation != null)
135 actionList.add(new ObligationPolicyDatabaseAction(ObligationPolicyDatabaseActionType.DEL, obligation));
116 TypeFrame factAtLocation = null;
117 SignatureParser sigParser = new SignatureParser(signature);
118 // int startIndex = 0;
119 // if (!xmethod.isStatic())
120 // startIndex = 1;
121 Iterator<String> signatureIterator = sigParser.parameterSignatureIterator();
122 int parameters = sigParser.getNumParameters();
123 for (int i = 0; i < parameters; i++) {
124 String sig = signatureIterator.next();
125 Collection<ClassDescriptor> annotations = invokedMethod.getParameterAnnotationDescriptors(i);
126 if (annotations.contains(WILL_CLOSE) || "Ljava/io/Closeable;".equals(sig) || methodName.startsWith("close")) {
127 // closing this value
128 if (factAtLocation == null) {
129 factAtLocation = typeDataflow.getFactAtLocation( new Location(handle, block));
130 }
131
132 Type argumentType = factAtLocation.getArgument(inv, cpg, i, sigParser);
133 if (argumentType instanceof ObjectType) {
134 Obligation obligation = database.getFactory().getObligationByType((ObjectType) argumentType);
135 if (obligation != null) {
136 actionList.add(new ObligationPolicyDatabaseAction(ObligationPolicyDatabaseActionType.DEL, obligation));
137 }
138
139 }
136140
137141 }
138
139142 }
140 }
141143
142144 } catch (CheckedAnalysisException e) {
143145 AnalysisContext.logError("Error checking " + invokedMethod, e);
161163 ObligationFactory factory = database.getFactory();
162164 Obligation obligation = factory.getObligationByType((ObjectType) tosType);
163165 if (obligation != null) {
164 if (obligation.getClassName().equals("java.sql.ResultSet")) {
166 if ("java.sql.ResultSet".equals(obligation.getClassName())) {
165167 ObjectType sType = ObjectTypeFactory.getInstance(java.sql.Statement.class);
166168 Obligation sObligation = factory.getObligationByType(sType);
167169 actionList = Arrays.asList(
168170 new ObligationPolicyDatabaseAction(ObligationPolicyDatabaseActionType.DEL, obligation),
169171 new ObligationPolicyDatabaseAction(ObligationPolicyDatabaseActionType.DEL, sObligation));
170 } else
171 actionList = Collections.singleton(new ObligationPolicyDatabaseAction(ObligationPolicyDatabaseActionType.DEL,
172 obligation));
172 } else {
173 actionList = Collections.singleton(new ObligationPolicyDatabaseAction(ObligationPolicyDatabaseActionType.DEL,
174 obligation));
175 }
173176
174177 }
175178 }
3232 /**
3333 * An ObligationPolicyDatabaseEntry which creates or deletes an obligation based
3434 * on a call to a specified method.
35 *
35 *
3636 * @author David Hovemeyer
3737 */
3838 public class MatchMethodEntry implements ObligationPolicyDatabaseEntry {
5050 private final Obligation[] obligations;
5151
5252 private final ObligationPolicyDatabaseEntryType entryType;
53
53
54 @Override
5455 public Collection<Obligation> getAllObligations() {
5556 return Arrays.asList(obligations);
5657 }
5758
5859 /**
5960 * Constructor. Creates an entry which matches the given XMethod.
60 *
61 *
6162 * @param xmethod
6263 * an XMethod
6364 * @param action
8283
8384 /**
8485 * Constructor.
85 *
86 *
8687 * @param receiverType
8788 * TypeMatcher to match the receiver type (or class containing
8889 * static method)
111112 this.entryType = entryType;
112113 }
113114
115 @Override
114116 public ObligationPolicyDatabaseEntryType getEntryType() {
115117 return entryType;
116118 }
117119
120 @Override
118121 public boolean getActions(ReferenceType receiverType, String methodName, String signature, boolean isStatic,
119122 Collection<ObligationPolicyDatabaseAction> actionList) {
120123 if (this.methodName.matches(methodName) && this.signature.matches(signature) && this.isStatic == isStatic
121124 && this.receiverType.matches(receiverType)) {
122 for (Obligation o : obligations)
125 for (Obligation o : obligations) {
123126 actionList.add(new ObligationPolicyDatabaseAction(action, o));
127 }
124128 return true;
125129 }
126130 return false;
2626 /**
2727 * An obligation that must be cleaned up by error handling code. Examples
2828 * include open streams and database connections.
29 *
29 *
3030 * <p>
3131 * See Weimer and Necula, <a href="http://doi.acm.org/10.1145/1028976.1029011"
3232 * >Finding and preventing run-time error handling mistakes</a>, OOPSLA 2004.
3333 * </p>
34 *
34 *
3535 * @author David Hovemeyer
3636 */
3737 public class Obligation {
7777 }
7878 }
7979
80 // vim:ts=4
2323 /**
2424 * Exception to indicate that ObligationAnalysis has detected a situation in
2525 * which an obligation is either acquired or released inside a loop.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class ObligationAcquiredOrReleasedInLoopException extends DataflowAnalysisException {
30 private Obligation obligation;
30 private final Obligation obligation;
3131
3232 public ObligationAcquiredOrReleasedInLoopException(Obligation obligation) {
3333 super("Obligation " + obligation + " acquired or released in loop");
5656 /**
5757 * Dataflow analysis to track obligations (i/o streams and other resources which
5858 * must be closed).
59 *
59 *
6060 * <p>
6161 * See Weimer and Necula, <a href="http://doi.acm.org/10.1145/1028976.1029011"
6262 * >Finding and preventing run-time error handling mistakes</a>, OOPSLA 2004.
6363 * </p>
64 *
64 *
6565 * @author David Hovemeyer
6666 */
6767 public class ObligationAnalysis extends ForwardDataflowAnalysis<StateSet> {
7070
7171 private static final boolean DEBUG_NULL_CHECK = SystemProperties.getBoolean("oa.debug.nullcheck");
7272
73 private XMethod xmethod;
74
75 private ObligationFactory factory;
76
77 private ObligationPolicyDatabase database;
78
79 private TypeDataflow typeDataflow;
80
81 private IsNullValueDataflow invDataflow;
82
83 private IErrorLogger errorLogger;
84
85 private InstructionActionCache actionCache;
73 private final XMethod xmethod;
74
75 private final ObligationFactory factory;
76
77 private final ObligationPolicyDatabase database;
78
79 private final TypeDataflow typeDataflow;
80
81 private final IsNullValueDataflow invDataflow;
82
83 private final IErrorLogger errorLogger;
84
85 private final InstructionActionCache actionCache;
8686
8787 private StateSet cachedEntryFact;
8888
9090
9191 /**
9292 * Constructor.
93 *
93 *
9494 * @param dfs
9595 * a DepthFirstSearch on the method to be analyzed
9696 * @param xmethod
122122 return actionCache;
123123 }
124124
125 @Override
125126 public StateSet createFact() {
126127 return new StateSet(factory);
127128 }
134135 @Override
135136 public void transferInstruction(InstructionHandle handle, BasicBlock basicBlock, StateSet fact)
136137 throws DataflowAnalysisException {
137 Collection<ObligationPolicyDatabaseAction> actionList = actionCache.getActions(basicBlock, handle);
138 if (actionList.isEmpty())
139 return;
138 Collection<ObligationPolicyDatabaseAction> actionList = actionCache.getActions(basicBlock, handle);
139 if (actionList.isEmpty()) {
140 return;
141 }
140142 if (DEBUG) {
141143 System.out.println("Applying actions at " + handle + " to " + fact);
142144 }
151153 }
152154 }
153155
154 /*
155 * (non-Javadoc)
156 *
157 * @see
158 * edu.umd.cs.findbugs.ba.AbstractDataflowAnalysis#transfer(edu.umd.cs.findbugs
159 * .ba.BasicBlock, org.apache.bcel.generic.InstructionHandle,
160 * java.lang.Object, java.lang.Object)
161 */
162156 @Override
163157 public void transfer(BasicBlock basicBlock, @CheckForNull InstructionHandle end, StateSet start, StateSet result)
164158 throws DataflowAnalysisException {
212206 if (isPossibleIfComparison(edge)) {
213207 Obligation comparedObligation = comparesObligationTypeToNull(edge);
214208 if (comparedObligation != null/*
215 * && comparedObligation.equals(
216 * possiblyLeakedObligation)
217 */) {
209 * && comparedObligation.equals(
210 * possiblyLeakedObligation)
211 */) {
218212 if (DEBUG_NULL_CHECK) {
219213 System.out.println("Deleting " + comparedObligation.toString() + " on edge from comparision "
220214 + edge.getSource().getLastInstruction());
249243 case Constants.IF_ACMPEQ:
250244 case Constants.IF_ACMPNE:
251245 type = acmpNullCheck(opcode, edge, last, sourceBlock);
246 break;
247 default:
252248 break;
253249 }
254250
324320 return type;
325321 }
326322
327 /*
328 * (non-Javadoc)
329 *
330 * @see
331 * edu.umd.cs.findbugs.ba.DataflowAnalysis#copy(edu.umd.cs.findbugs.ba.obl
332 * .StateSet, edu.umd.cs.findbugs.ba.obl.StateSet)
333 */
323 @Override
334324 public void copy(StateSet src, StateSet dest) {
335325 dest.copyFrom(src);
336326 }
337327
338 /*
339 * (non-Javadoc)
340 *
341 * @see
342 * edu.umd.cs.findbugs.ba.DataflowAnalysis#initEntryFact(edu.umd.cs.findbugs
343 * .ba.obl.StateSet)
344 */
328 @Override
345329 public void initEntryFact(StateSet fact) throws DataflowAnalysisException {
346330 if (cachedEntryFact == null) {
347331 cachedEntryFact = new StateSet(factory);
374358
375359 /*
376360 * (non-Javadoc)
377 *
361 *
378362 * @see
379363 * edu.umd.cs.findbugs.ba.DataflowAnalysis#makeFactTop(edu.umd.cs.findbugs
380364 * .ba.obl.StateSet)
381365 */
366 @Override
382367 public void makeFactTop(StateSet fact) {
383368 fact.setTop();
384369 }
385370
371 @Override
386372 public boolean isTop(StateSet fact) {
387373 return fact.isTop();
388374 }
389375
390 /*
391 * (non-Javadoc)
392 *
393 * @see
394 * edu.umd.cs.findbugs.ba.DataflowAnalysis#same(edu.umd.cs.findbugs.ba.obl
395 * .StateSet, edu.umd.cs.findbugs.ba.obl.StateSet)
396 */
376 @Override
397377 public boolean same(StateSet a, StateSet b) {
398378 return a.equals(b);
399379 }
400380
401 /*
402 * (non-Javadoc)
403 *
404 * @see
405 * edu.umd.cs.findbugs.ba.DataflowAnalysis#meetInto(edu.umd.cs.findbugs.
406 * ba.obl.StateSet, edu.umd.cs.findbugs.ba.Edge,
407 * edu.umd.cs.findbugs.ba.obl.StateSet)
408 */
381 @Override
409382 public void meetInto(StateSet fact, Edge edge, StateSet result) throws DataflowAnalysisException {
410383 final StateSet inputFact = fact;
411384
423396
424397 // Handle easy top and bottom cases
425398 if (inputFact.isTop() || result.isBottom() ) {
426 // Nothing to do
399 // Nothing to do
427400 } else if (inputFact.isBottom() || result.isTop() || result.isEmpty()) {
428401 copy(inputFact, result);
429 } else if (inputFact.isOnExceptionPath()
430 && !result.isOnExceptionPath()) {
402 } else if (inputFact.isOnExceptionPath()
403 && !result.isOnExceptionPath()) {
431404 if (DEBUG) {
432405 System.out.println("Ignoring " + inputFact + " in favor of " + result);
433406 BasicBlock from = edge.getSource();
435408 System.out.printf(" edge %s -> %s%n", from, to);
436409 }
437410 // Nothing to do
438 } else if(!inputFact.isOnExceptionPath() && !inputFact.isEmpty()
439 && result.isOnExceptionPath()) {
440 if (DEBUG)
411 } else if(!inputFact.isOnExceptionPath() && !inputFact.isEmpty()
412 && result.isOnExceptionPath()) {
413 if (DEBUG) {
441414 System.out.println("overwriting " + result + " with " + inputFact);
415 }
442416 copy(inputFact, result);
443417 } else {
444418 // We will destructively replace the state map of the result fact
489463 }
490464 }
491465
492 // vim:ts=4
2323
2424 /**
2525 * Dataflow class for ObligationAnalysis.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class ObligationDataflow extends Dataflow<StateSet, ObligationAnalysis> {
4242 * ObligationAnalysis.
4343 */
4444 public class ObligationFactory {
45 private Map<String, Obligation> classNameToObligationMap;
45 private final Map<String, Obligation> classNameToObligationMap;
4646
47 private Set<String> slashedClassNames = new HashSet<String>();
47 private final Set<String> slashedClassNames = new HashSet<String>();
4848
4949 // // XXX: this is just for debugging.
5050 // static ObligationFactory lastInstance;
6060 }
6161
6262 public boolean signatureInvolvesObligations(String sig) {
63 for (String c : slashedClassNames)
64 if (sig.indexOf(c) >= 0)
63 sig = sig.replaceAll("java/io/File", "java/io/");
64 for (String c : slashedClassNames) {
65 if (sig.indexOf(c) >= 0) {
6566 return true;
67 }
68 }
6669 return false;
6770 }
6871
6972 /**
7073 * Determine whether class named by given ClassDescriptor is an Obligation
7174 * type.
72 *
75 *
7376 * @param classDescriptor
7477 * a class
7578 * @return true if the class is an Obligation type, false otherwise
8588
8689 /**
8790 * Get an Iterator over known Obligation types.
88 *
91 *
8992 * @return Iterator over known Obligation types
9093 */
9194 public Iterator<Obligation> obligationIterator() {
9699 * Look up an Obligation by type. This returns the first Obligation that is
97100 * a supertype of the type given (meaning that the given type could be an
98101 * instance of the returned Obligation).
99 *
102 *
100103 * @param type
101104 * a type
102105 * @return an Obligation that is a supertype of the given type, or null if
107110 Obligation getObligationByType(ObjectType type) throws ClassNotFoundException {
108111 for (Iterator<Obligation> i = obligationIterator(); i.hasNext();) {
109112 Obligation obligation = i.next();
110 if (Hierarchy.isSubtype(type, obligation.getType()))
113 if (Hierarchy.isSubtype(type, obligation.getType())) {
111114 return obligation;
115 }
112116 }
113117 return null;
114118 }
117121 * Look up an Obligation by type. This returns the first Obligation that is
118122 * a supertype of the type given (meaning that the given type could be an
119123 * instance of the returned Obligation).
120 *
124 *
121125 * @param classDescriptor
122126 * a ClassDescriptor naming a class type
123127 * @return an Obligation that is a supertype of the given type, or null if
136140 /**
137141 * Get array of Obligation types corresponding to the parameters of the
138142 * given method.
139 *
143 *
140144 * @param xmethod
141145 * a method
142146 * @return array of Obligation types for each of the method's parameters; a
171175
172176 public Obligation getObligationById(int id) {
173177 for (Obligation obligation : classNameToObligationMap.values()) {
174 if (obligation.getId() == id)
178 if (obligation.getId() == id) {
175179 return obligation;
180 }
176181 }
177182 return null;
178183 }
186191 }
187192 }
188193
189 // vim:ts=4
3232
3333 /**
3434 * Policy database which defines which methods create and remove obligations.
35 *
35 *
3636 * <p>
3737 * See Weimer and Necula, <a href="http://doi.acm.org/10.1145/1028976.1029011"
3838 * >Finding and preventing run-time error handling mistakes</a>, OOPSLA 2004.
3939 * </p>
40 *
40 *
4141 * @author David Hovemeyer
4242 */
4343 public class ObligationPolicyDatabase {
4444 public static final boolean DEBUG = SystemProperties.getBoolean("oa.debug.db");
4545
46 private ObligationFactory factory;
46 private final ObligationFactory factory;
4747
48 private LinkedList<ObligationPolicyDatabaseEntry> entryList;
49
50 private HashSet<Obligation> allObligations = new HashSet<Obligation>();
48 private final LinkedList<ObligationPolicyDatabaseEntry> entryList;
49
50 private final HashSet<Obligation> allObligations = new HashSet<Obligation>();
5151
5252 private boolean strictChecking;
5353
6060 public ObligationFactory getFactory() {
6161 return factory;
6262 }
63
63
6464 public Set<Obligation> getAllObligations() {
6565 return allObligations;
6666 }
6767
6868 public void addEntry(ObligationPolicyDatabaseEntry entry) {
69 if (DEBUG)
69 if (DEBUG) {
7070 System.out.println("Adding entry " + entry);
71 }
7172 allObligations.addAll(entry.getAllObligations());
7273 entryList.add(entry);
7374 }
7576 /**
7677 * Add an appropriate policy database entry for parameters marked with the
7778 * WillClose annotation.
78 *
79 *
7980 * @param xmethod
8081 * a method
8182 * @param obligation
9293 addEntry(entry);
9394 return entry;
9495 }
95
96
9697 public void setStrictChecking(boolean strictChecking) {
97 if (DEBUG)
98 if (DEBUG) {
9899 System.out.println("Setting strict checking to " + strictChecking );
100 }
99101 this.strictChecking = strictChecking;
100102 }
101103
113115 boolean matched = entry.getActions(receiverType, methodName, signature, isStatic, actionList);
114116
115117 if (DEBUG) {
116 if (matched)
118 if (matched)
119 {
117120 System.out.println(" Entry " + entry + " ==> MATCH");
118 // else
119 // System.out.println(" ==> no match");
121 // else
122 // System.out.println(" ==> no match");
123 }
120124 }
121125 }
122126 if (DEBUG) {
129133 }
130134 }
131135
132 // vim:ts=4
2323 /**
2424 * An action applied by an entry in the ObligationPolicyDatabase. Adds or
2525 * removes an obligation.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class ObligationPolicyDatabaseAction {
2525 /**
2626 * Interface defining an entry in the ObligationPolicyDatabase. Checks called
2727 * methods to see what actions they apply.
28 *
28 *
2929 * @author David Hovemeyer
3030 */
3131 public interface ObligationPolicyDatabaseEntry {
3737 /**
3838 * Get the ObligationPolicyDatabaseActions that should be applied when the
3939 * method described by the parameters is called.
40 *
40 *
4141 * @param receiverType
4242 * receiver type of called method
4343 * @param methodName
5454 */
5555 public boolean getActions(ReferenceType receiverType, String methodName, String signature, boolean isStatic,
5656 Collection<ObligationPolicyDatabaseAction> actionList);
57
57
5858 public Collection<Obligation> getAllObligations();
5959 }
2020
2121 /**
2222 * Types of ObligationPolicyDatabaseEntries.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public enum ObligationPolicyDatabaseEntryType {
2222
2323 /**
2424 * A multiset of obligations that must be cleaned up by error-handling code.
25 *
25 *
2626 * <p>
2727 * See Weimer and Necula, <a href="http://doi.acm.org/10.1145/1028976.1029011"
2828 * >Finding and preventing run-time error handling mistakes</a>, OOPSLA 2004.
2929 * </p>
30 *
30 *
3131 * @author David Hovemeyer
3232 */
3333 public class ObligationSet {
4747 invalidate();
4848 }
4949
50
50
5151 public boolean isEmpty() {
52 for(short s : countList)
53 if (s > 0)
52 for(short s : countList) {
53 if (s > 0) {
5454 return false;
55 }
56 }
5557 return true;
5658 }
5759 public void add(Obligation obligation) {
6365 invalidate();
6466 int count = countList[obligation.getId()];
6567 if (count > 0)
68 {
6669 countList[obligation.getId()]--; // = (short)(count - 1);
70 }
6771 }
6872
6973 public int getCount(int id) {
106110
107111 @Override
108112 public boolean equals(Object o) {
109 if (o == null || o.getClass() != this.getClass())
113 if (o == null || o.getClass() != this.getClass()) {
110114 return false;
115 }
111116
112117 ObligationSet other = (ObligationSet) o;
113118
114119 if (!Arrays.equals(this.countList, other.countList)
115 /* || !Arrays.equals(this.whereCreated, other.whereCreated) */) {
120 /* || !Arrays.equals(this.whereCreated, other.whereCreated) */) {
116121 return false;
117122 }
118123
129134 buf.append("{");
130135 int count = 0;
131136 for (int i = 0; i < countList.length; ++i) {
132 if (countList[i] == 0)
137 if (countList[i] == 0) {
133138 continue;
134 if (count > 0)
139 }
140 if (count > 0) {
135141 buf.append(",");
142 }
136143 buf.append(factory.getObligationById(i).toString());
137144 buf.append(" x ");
138145 buf.append(countList[i]);
173180 }
174181 }
175182
176 // vim:ts=4
2323 /**
2424 * Error-handling obligation analysis state. This is a set of obligations and a
2525 * program path on which they are outstanding (not cleaned up).
26 *
26 *
2727 * <p>
2828 * See Weimer and Necula, <a href="http://doi.acm.org/10.1145/1028976.1029011"
2929 * >Finding and preventing run-time error handling mistakes</a>, OOPSLA 2004.
3030 * </p>
31 *
31 *
3232 * @author David Hovemeyer
3333 */
3434 public class State {
9292 }
9393 }
9494
95 // vim:ts=4
3131 /**
3232 * A dataflow fact used in ObligationAnalysis. It is a set of State objects,
3333 * plus the additional capability to represent top and bottom elements.
34 *
34 *
3535 * <p>
3636 * Invariant: no StateSet may contain more than one State with the same
3737 * ObligationSet.
3838 * </p>
39 *
39 *
4040 * <p>
4141 * See Weimer and Necula, <a href="http://doi.acm.org/10.1145/1028976.1029011"
4242 * >Finding and preventing run-time error handling mistakes</a>, OOPSLA 2004.
4343 * </p>
44 *
44 *
4545 * @author David Hovemeyer
4646 */
4747 public class StateSet {
4848 private boolean isTop;
4949
5050 private boolean isBottom;
51
51
5252 private boolean onExceptionPath;
5353
5454 private Map<ObligationSet, State> stateMap;
55
55
5656 public boolean isEmpty() {
5757 return stateMap.isEmpty();
5858 }
104104
105105 /**
106106 * Return an Iterator over the States in the StateSet.
107 *
107 *
108108 * @return an Iterator over the States in the StateSet
109109 */
110110 public Iterator<State> stateIterator() {
113113
114114 /**
115115 * Get Set of all ObligationsSets in this StateSet.
116 *
116 *
117117 * @return Set of all ObligationsSets in this StateSet
118118 */
119119 public Set<ObligationSet> getAllObligationSets() {
123123 /**
124124 * Get the State which has the given ObligationSet. Returns null if there is
125125 * no such state.
126 *
126 *
127127 * @param obligationSet
128128 * we want to get the State with this ObligationSet
129129 * @return the State with the given ObligationSet, or null if there is no
150150
151151 /**
152152 * Make this StateSet an exact copy of the given StateSet.
153 *
153 *
154154 * @param other
155155 * a StateSet; this StateSet will be made identical to it
156156 */
167167
168168 /**
169169 * Return an exact deep copy of this StateSet.
170 *
170 *
171171 * @return an exact deep copy of this StateSet
172172 */
173173 public StateSet duplicate() {
178178
179179 /**
180180 * Add an obligation to every State in the StateSet.
181 *
181 *
182182 * @param obligation
183183 * the obligation to add
184184 * @param basicBlockId
191191 State s = new State(factory);
192192 s.getObligationSet().add(obligation);
193193 updatedStateMap.put(s.getObligationSet(), s);
194 } else for (State state : stateMap.values()) {
195 checkCircularity(state, obligation, basicBlockId);
196 state.getObligationSet().add(obligation);
197 updatedStateMap.put(state.getObligationSet(), state);
198
194 } else {
195 for (State state : stateMap.values()) {
196 checkCircularity(state, obligation, basicBlockId);
197 state.getObligationSet().add(obligation);
198 updatedStateMap.put(state.getObligationSet(), state);
199
200 }
199201 }
200202 replaceMap(updatedStateMap);
201203 }
202204
203205 /**
204206 * Remove an Obligation from every State in the StateSet.
205 *
207 *
206208 * @param obligation
207209 * the obligation to remove
208210 * @param basicBlockId
218220 checkCircularity(state, obligation, basicBlockId);
219221 ObligationSet obligationSet = state.getObligationSet();
220222 obligationSet.remove(obligation);
221 if (!obligationSet.isEmpty())
222 updatedStateMap.put(obligationSet, state);
223 if (!obligationSet.isEmpty()) {
224 updatedStateMap.put(obligationSet, state);
225 }
223226 }
224227 replaceMap(updatedStateMap);
225228 }
227230 /**
228231 * Bail out of the analysis is an obligation is acquired or released in a
229232 * loop.
230 *
233 *
231234 * @param state
232235 * a State to which an obligation is being added or removed
233236 * @param obligation
244247
245248 /**
246249 * Replace the map of ObligationSets to States with the given one.
247 *
250 *
248251 * @param stateMap
249252 * enw map of ObligationSets to States
250253 */
254257
255258 /**
256259 * Get all States that have Paths which are prefixes of the given Path.
257 *
260 *
258261 * @param path
259262 * a Path
260263 * @return Collection of States that have Paths which are prefixes of the
272275
273276 @Override
274277 public boolean equals(Object o) {
275 if (o == null || o.getClass() != this.getClass())
278 if (o == null || o.getClass() != this.getClass()) {
276279 return false;
280 }
277281 StateSet other = (StateSet) o;
278282 return this.isTop == other.isTop && this.isBottom == other.isBottom
279283 && this.onExceptionPath == other.onExceptionPath && this.stateMap.equals(other.stateMap);
286290
287291 @Override
288292 public String toString() {
289 if (isTop)
293 if (isTop) {
290294 return "TOP";
291 else if (isBottom)
295 } else if (isBottom) {
292296 return "BOTTOM";
293 else {
297 } else {
294298 StringBuilder buf = new StringBuilder();
295299 buf.append(stateMap);
296
297 if (onExceptionPath)
300
301 if (onExceptionPath) {
298302 buf.append(" On exception path");
303 }
299304 return buf.toString();
300305 }
301306 }
309314 }
310315 }
311316
312 // vim:ts=4
2222
2323 /**
2424 * Special "bottom" type. It is the zero element for the type merge operation.
25 *
25 *
2626 * @author David Hovemeyer
2727 * @see TypeAnalysis
2828 * @see TypeFrame
5858 }
5959 }
6060
61 // vim:ts=4
2424 * Special type used to represent the "extra" part of a double value. We say
2525 * that when a double is stored, local <i>n</i> will have type double, and local
2626 * <i>n+1</i> will have this type.
27 *
27 *
2828 * @author David Hovemeyer
2929 * @see TypeAnalysis
3030 * @see TypeFrame
5757 }
5858 }
5959
60 // vim:ts=4
3333 */
3434 private static final long serialVersionUID = 1L;
3535
36 private ExceptionSet exceptionSet;
36 private final ExceptionSet exceptionSet;
3737
3838 /**
3939 * Constructor.
40 *
40 *
4141 * @param className
4242 * the class name
4343 * @param exceptionSet
5050
5151 /**
5252 * Initialize object from an exception set.
53 *
53 *
5454 * @param exceptionSet
5555 * the exception set
5656 * @return a Type that is a supertype of all of the exceptions in the
5858 */
5959 public static Type fromExceptionSet(ExceptionSet exceptionSet) throws ClassNotFoundException {
6060 Type commonSupertype = exceptionSet.getCommonSupertype();
61 if (commonSupertype.getType() != T_OBJECT)
61 if (commonSupertype.getType() != T_OBJECT) {
6262 return commonSupertype;
63 }
6364
6465 ObjectType exceptionSupertype = (ObjectType) commonSupertype;
6566
6667 String className = exceptionSupertype.getClassName();
67 if (className.equals("java.lang.Throwable"))
68 if ("java.lang.Throwable".equals(className)) {
6869 return exceptionSupertype;
70 }
6971 return new ExceptionObjectType(className, exceptionSet);
7072 }
7173
8183
8284 @Override
8385 public boolean equals(Object o) {
84 if (o == null)
86 if (o == null) {
8587 return false;
86 if (o.getClass() != this.getClass())
88 }
89 if (o.getClass() != this.getClass()) {
8790 return false;
91 }
8892
8993 ExceptionObjectType other = (ExceptionObjectType) o;
9094 return getSignature().equals(other.getSignature()) && exceptionSet.equals(other.exceptionSet);
9296
9397 /**
9498 * Return the exception set.
95 *
99 *
96100 * @return the ExceptionSet
97101 */
98102 public ExceptionSet getExceptionSet() {
105109 buf.append("<exception:");
106110 boolean first = true;
107111 for (ExceptionSet.ThrownExceptionIterator i = exceptionSet.iterator(); i.hasNext();) {
108 if (first)
112 if (first) {
109113 first = false;
110 else
114 } else {
111115 buf.append(',');
116 }
112117 buf.append(i.next().toString());
113118 }
114119 buf.append(">");
116121 }
117122 }
118123
119 // vim:ts=4
6666 findNext();
6767 }
6868
69 @Override
6970 public boolean hasNext() {
70 if (last == next)
71 if (last == next) {
7172 findNext();
73 }
7274 return next < factory.getNumTypes();
7375 }
7476
77 @Override
7578 public ObjectType next() {
76 if (!hasNext())
79 if (!hasNext()) {
7780 throw new NoSuchElementException();
81 }
7882 ObjectType result = factory.getType(next);
7983 last = next;
8084 return result;
8488 return explicitSet.get(last);
8589 }
8690
91 @Override
8792 public void remove() {
8893 exceptionSet.clear(last);
8994 explicitSet.clear(last);
9499 private void findNext() {
95100 ++next;
96101 while (next < factory.getNumTypes()) {
97 if (exceptionSet.get(next))
102 if (exceptionSet.get(next)) {
98103 break;
104 }
99105 ++next;
100106 }
101107 }
135141
136142 @Override
137143 public boolean equals(Object o) {
138 if (o == null)
144 if (o == null) {
139145 return false;
140 if (o.getClass() != this.getClass())
146 }
147 if (o.getClass() != this.getClass()) {
141148 return false;
149 }
142150
143151 ExceptionSet other = (ExceptionSet) o;
144152 return exceptionSet.equals(other.exceptionSet) && explicitSet.equals(other.explicitSet)
150158 * in the set. Returns the special TOP type if the set is empty.
151159 */
152160 public Type getCommonSupertype() throws ClassNotFoundException {
153 if (commonSupertype != null)
161 if (commonSupertype != null) {
154162 return commonSupertype;
163 }
155164
156165 if (isEmpty()) {
157166 // This probably means that we're looking at an
204213 * @return true if it is
205214 */
206215 public boolean isSingleton(String exceptionName) {
207 if (size != 1)
216 if (size != 1) {
208217 return false;
218 }
209219 ObjectType e = iterator().next();
210220 return e.toString().equals(exceptionName);
211221
242252 */
243253 public void add(ObjectType type, boolean explicit) {
244254 int index = factory.getIndexOfType(type);
245 if (!exceptionSet.get(index))
255 if (!exceptionSet.get(index)) {
246256 ++size;
257 }
247258 exceptionSet.set(index);
248 if (explicit)
259 if (explicit) {
249260 explicitSet.set(index);
261 }
250262
251263 commonSupertype = null;
252264 }
268280 private int countBits(BitSet bitSet) {
269281 int count = 0;
270282 for (int i = 0; i < factory.getNumTypes(); ++i) {
271 if (bitSet.get(i))
283 if (bitSet.get(i)) {
272284 ++count;
285 }
273286 }
274287 return count;
275288 }
307320 public boolean containsCheckedExceptions() throws ClassNotFoundException {
308321 for (ThrownExceptionIterator i = iterator(); i.hasNext();) {
309322 ObjectType type = i.next();
310 if (!Hierarchy.isUncheckedException(type))
323 if (!Hierarchy.isUncheckedException(type)) {
311324 return true;
325 }
312326 }
313327 return false;
314328 }
319333 public boolean containsExplicitExceptions() {
320334 for (ThrownExceptionIterator i = iterator(); i.hasNext();) {
321335 i.next();
322 if (i.isExplicit())
336 if (i.isExplicit()) {
323337 return true;
338 }
324339 }
325340 return false;
326341 }
332347 boolean first = true;
333348 for (ThrownExceptionIterator i = iterator(); i.hasNext();) {
334349 ObjectType type = i.next();
335 if (first)
350 if (first) {
336351 first = false;
337 else
352 } else {
338353 buf.append(',');
354 }
339355 boolean implicit = !i.isExplicit();
340 if (implicit)
356 if (implicit) {
341357 buf.append('[');
358 }
342359 buf.append(type.toString());
343 if (implicit)
360 if (implicit) {
344361 buf.append(']');
362 }
345363 }
346364 buf.append('}');
347365 return buf.toString();
352370 }
353371 }
354372
355 // vim:ts=4
3030 */
3131 private static final long serialVersionUID = 1L;
3232
33 private HashMap<ObjectType, Integer> typeIndexMap;
33 private final HashMap<ObjectType, Integer> typeIndexMap;
3434
35 private ArrayList<ObjectType> typeList;
35 private final ArrayList<ObjectType> typeList;
3636
3737 public ExceptionSetFactory() {
3838 this.typeIndexMap = new HashMap<ObjectType, Integer>();
6262 }
6363 }
6464
65 // vim:ts=3
2121 /**
2222 * Extended type codes used by StackAndLocalTypes and StackAndLocalTypeAnalysis
2323 * for typing locals and stack values used in Java bytecode.
24 *
24 *
2525 * @author David Hovemeyer
2626 * @see TypeFrame
2727 * @see TypeAnalysis
7878 public static final byte T_AVAIL_TYPE = 100;
7979 }
8080
81 // vim:ts=4
3333 * Field property storing the types of values stored in a field. The idea is
3434 * that we may be able to determine a more precise type for values loaded from
3535 * the field than the field type alone would indicate.
36 *
36 *
3737 * @author David Hovemeyer
3838 */
3939 public class FieldStoreType {
40 private HashSet<String> typeSignatureSet;
40 private final HashSet<String> typeSignatureSet;
4141
4242 private ReferenceType loadType;
4343
6969 try {
7070 String signature = i.next();
7171 Type type = Type.getType(signature);
72 if (!(type instanceof ReferenceType))
72 if (!(type instanceof ReferenceType)) {
7373 continue;
74 }
7475
7576 // FIXME: this will mangle interface types, since
7677 // getFirstCommonSuperclass() ignores interfaces.
99100 }
100101
101102 try {
102 if (leastSupertype != null && Hierarchy.isSubtype(leastSupertype, fieldType))
103 if (leastSupertype != null && Hierarchy.isSubtype(leastSupertype, fieldType)) {
103104 loadType = leastSupertype;
105 }
104106 } catch (ClassNotFoundException e) {
105107 AnalysisContext.reportMissingClass(e);
106108 }
107109
108 if (loadType == null)
110 if (loadType == null) {
109111 loadType = fieldType;
112 }
110113 }
111114 }
4848 continue;
4949 }
5050 ReferenceType storeType = type.getLoadType((ReferenceType) fieldType);
51 if (storeType.equals(fieldType))
51 if (storeType.equals(fieldType)) {
5252 removeProperty(f);
53 }
5354 }
5455 }
5556
2424 * Special type used to represent the "extra" part of a long value. We say that
2525 * when a long is stored, local <i>n</i> will have type long, and local
2626 * <i>n+1</i> will have this type.
27 *
27 *
2828 * @author David Hovemeyer
2929 * @see TypeAnalysis
3030 * @see TypeFrame
5757 }
5858 }
5959
60 // vim:ts=4
2525 * the lattice than any object type, but lower than the overall Top type. It
2626 * represents the type of the null value, which may logically be merged with any
2727 * object type without loss of information.
28 *
28 *
2929 * @author David Hovemeyer
3030 * @see TypeAnalysis
3131 * @see TypeFrame
5656 }
5757 }
5858
59 // vim:ts=4
6262 this.exceptionSetFactory = exceptionSetFactory;
6363 }
6464
65 @Override
6566 public Type mergeTypes(Type a, Type b) throws DataflowAnalysisException {
66 if (a == null) return b;
67 if (b == null) return a;
67 if (a == null) {
68 return b;
69 }
70 if (b == null) {
71 return a;
72 }
6873 byte aType = a.getType(), bType = b.getType();
6974
70 if (aType == T_TOP) // Top is the identity element
75 if (aType == T_TOP) {
7176 return b;
72 else if (bType == T_TOP) // Top is the identity element
77 } else if (bType == T_TOP) {
7378 return a;
74 else if (aType == T_BOTTOM || bType == T_BOTTOM) // Bottom meet anything
75 // is bottom
79 } else if (aType == T_BOTTOM || bType == T_BOTTOM) {
80 // is bottom
7681 return BottomType.instance();
77 else if (isReferenceType(aType) && isReferenceType(bType)) { // Two
78 // object
79 // types!
82 } else if (isReferenceType(aType) && isReferenceType(bType)) { // Two
83 // object
84 // types!
8085 // Handle the Null type, which serves as a special "top"
8186 // value for reference types.
82 if (aType == T_NULL)
87 if (aType == T_NULL) {
8388 return b;
84 else if (bType == T_NULL)
89 } else if (bType == T_NULL) {
8590 return a;
91 }
8692
8793 ReferenceType aRef = (ReferenceType) a;
8894 ReferenceType bRef = (ReferenceType) b;
8995 return mergeReferenceTypes(aRef, bRef);
90 } else if (isReferenceType(aType) || isReferenceType(bType)) // Object
91 // meet
92 // non-object
93 // is
94 // bottom
96 } else if (isReferenceType(aType) || isReferenceType(bType)) {
97 // meet
98 // non-object
99 // is
100 // bottom
95101 return BottomType.instance();
96 else if (aType == bType) // Same non-object type?
102 } else if (aType == bType) {
97103 return a;
98 else if (isIntegerType(aType) && isIntegerType(bType)) // Two different
99 // integer types
100 // - use T_INT
104 } else if (isIntegerType(aType) && isIntegerType(bType)) {
105 // integer types
106 // - use T_INT
101107 return Type.INT;
102 else
108 } else {
103109 // Default - types are incompatible
104110 return BottomType.instance();
111 }
105112 }
106113
107114 /**
133140 }
134141
135142 private static void updateExceptionSet(ExceptionSet exceptionSet, ObjectType type) {
136 if (type instanceof ExceptionObjectType)
143 if (type instanceof ExceptionObjectType) {
137144 exceptionSet.addAll(((ExceptionObjectType) type).getExceptionSet());
138 else
145 } else {
139146 exceptionSet.addExplicit(type);
147 }
140148 }
141149
142150 /**
151159 * @return the merged Type
152160 */
153161 protected ReferenceType mergeReferenceTypes(ReferenceType aRef, ReferenceType bRef) throws DataflowAnalysisException {
154 if (aRef.equals(bRef))
162 if (aRef.equals(bRef)) {
155163 return aRef;
164 }
156165 byte aType = aRef.getType();
157166 byte bType = bRef.getType();
158167 try {
162171 if (isObjectType(aType) && isObjectType(bType)
163172 && ((aType == T_EXCEPTION || isThrowable(aRef)) && (bType == T_EXCEPTION || isThrowable(bRef)))) {
164173 ExceptionSet union = exceptionSetFactory.createExceptionSet();
165 if (aType == T_OBJECT && aRef.getSignature().equals("Ljava/lang/Throwable;"))
174 if (aType == T_OBJECT && "Ljava/lang/Throwable;".equals(aRef.getSignature())) {
166175 return aRef;
167 if (bType == T_OBJECT && bRef.getSignature().equals("Ljava/lang/Throwable;"))
176 }
177 if (bType == T_OBJECT && "Ljava/lang/Throwable;".equals(bRef.getSignature())) {
168178 return bRef;
179 }
169180
170181 updateExceptionSet(union, (ObjectType) aRef);
171182 updateExceptionSet(union, (ObjectType) bRef);
172183
173184 Type t = ExceptionObjectType.fromExceptionSet(union);
174 if (t instanceof ReferenceType)
185 if (t instanceof ReferenceType) {
175186 return (ReferenceType) t;
187 }
176188 }
177189
178190 if (aRef instanceof GenericObjectType && bRef instanceof GenericObjectType
186198 List<? extends ReferenceType> bP = bG.getParameters();
187199 assert aP != null;
188200 assert bP != null;
189 if (aP.size() != bP.size())
201 if (aP.size() != bP.size()) {
190202 break;
203 }
191204 ArrayList<ReferenceType> result = new ArrayList<ReferenceType>(aP.size());
192 for (int i = 0; i < aP.size(); i++)
205 for (int i = 0; i < aP.size(); i++) {
193206 result.add(mergeReferenceTypes(aP.get(i), bP.get(i)));
207 }
194208
195209 GenericObjectType rOT = GenericUtilities.getType(aG.getClassName(), result);
196210 return rOT;
200214 }
201215
202216 }
203 if (aRef instanceof GenericObjectType)
217 if (aRef instanceof GenericObjectType) {
204218 aRef = ((GenericObjectType) aRef).getObjectType();
205 if (bRef instanceof GenericObjectType)
219 }
220 if (bRef instanceof GenericObjectType) {
206221 bRef = ((GenericObjectType) bRef).getObjectType();
222 }
207223
208224 if (Subtypes2.ENABLE_SUBTYPES2_FOR_COMMON_SUPERCLASS_QUERIES) {
209225 return AnalysisContext.currentAnalysisContext().getSubtypes2().getFirstCommonSuperclass(aRef, bRef);
217233 }
218234
219235 private boolean isThrowable(ReferenceType ref) /*
220 * throws
221 * ClassNotFoundException
222 */{
236 * throws
237 * ClassNotFoundException
238 */{
223239 try {
224240
225 Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
226 return subtypes2.isSubtype(ref, Type.THROWABLE);
241 Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
242 return subtypes2.isSubtype(ref, Type.THROWABLE);
227243
228244 } catch (ClassNotFoundException e) {
229245 // We'll just assume that it's not an exception type.
234250
235251 }
236252
237 // vim:ts=4
3030 * <li>which exceptions are explicit (declared or explicitly thrown) and which
3131 * are implicit (result of failed runtime checks)
3232 * </ul>
33 *
33 *
3434 * @author David Hovemeyer
3535 * @see ExceptionSet
3636 * @see TypeAnalysis
3737 */
3838 public class ThrownException {
39 private ObjectType type;
39 private final ObjectType type;
4040
4141 private boolean explicit;
4242
4343 /**
4444 * Constructor.
45 *
45 *
4646 * @param type
4747 * type of exception
4848 * @param explicit
8888
8989 @Override
9090 public boolean equals(Object o) {
91 if (o == null)
91 if (o == null) {
9292 return false;
93 if (o.getClass() != this.getClass())
93 }
94 if (o.getClass() != this.getClass()) {
9495 return false;
96 }
9597
9698 ThrownException other = (ThrownException) o;
9799 return this.type.equals(other.type) && this.explicit == other.explicit;
98100 }
99101 }
100102
101 // vim:ts=4
2222
2323 /**
2424 * Special "top" type. It is the identity element for the type merge operation.
25 *
25 *
2626 * @author David Hovemeyer
2727 * @see TypeAnalysis
2828 * @see TypeFrame
5858 }
5959 }
6060
61 // vim:ts=4
7676 * exception edge in the CFG. This information can be used to prune infeasible
7777 * exception edges, and mark exception edges which propagate only implicit
7878 * exceptions.
79 *
79 *
8080 * @author David Hovemeyer
8181 * @see Dataflow
8282 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis
9898 * recomputed for the block.
9999 */
100100 private class CachedExceptionSet {
101 private TypeFrame result;
102
103 private ExceptionSet exceptionSet;
104
105 private Map<Edge, ExceptionSet> edgeExceptionMap;
101 private final TypeFrame result;
102
103 private final ExceptionSet exceptionSet;
104
105 private final Map<Edge, ExceptionSet> edgeExceptionMap;
106106
107107 public CachedExceptionSet(TypeFrame result, ExceptionSet exceptionSet) {
108108 this.result = result;
166166
167167 protected CFG cfg;
168168
169 private TypeMerger typeMerger;
170
171 private TypeFrameModelingVisitor visitor;
172
173 private Map<BasicBlock, CachedExceptionSet> thrownExceptionSetMap;
174
175 private RepositoryLookupFailureCallback lookupFailureCallback;
176
177 private ExceptionSetFactory exceptionSetFactory;
169 private final TypeMerger typeMerger;
170
171 private final TypeFrameModelingVisitor visitor;
172
173 private final Map<BasicBlock, CachedExceptionSet> thrownExceptionSetMap;
174
175 private final RepositoryLookupFailureCallback lookupFailureCallback;
176
177 private final ExceptionSetFactory exceptionSetFactory;
178178
179179 private ValueNumberDataflow valueNumberDataflow;
180180
181 private Map<BasicBlock, InstanceOfCheck> instanceOfCheckMap;
181 private final Map<BasicBlock, InstanceOfCheck> instanceOfCheckMap;
182182
183183 /**
184184 * Constructor.
185 *
185 *
186186 * @param method
187187 * TODO
188188 * @param methodGen
207207 super(dfs);
208208 this.method = method;
209209 Code code = method.getCode();
210 if (code == null)
210 if (code == null) {
211211 throw new IllegalArgumentException(method.getName() + " has no code");
212 }
212213 for (Attribute a : code.getAttributes()) {
213 if (a instanceof LocalVariableTypeTable)
214 if (a instanceof LocalVariableTypeTable) {
214215 visitor.setLocalTypeTable((LocalVariableTypeTable) a);
216 }
215217 }
216218 this.methodGen = methodGen;
217219 this.cfg = cfg;
228230
229231 /**
230232 * Constructor.
231 *
233 *
232234 * @param method
233235 * TODO
234236 * @param methodGen
248250 RepositoryLookupFailureCallback lookupFailureCallback, ExceptionSetFactory exceptionSetFactory) {
249251 this(method, methodGen, cfg, dfs, typeMerger, new TypeFrameModelingVisitor(methodGen.getConstantPool(), typeMerger),
250252 lookupFailureCallback, exceptionSetFactory);
251 if (TypeFrameModelingVisitor.DEBUG)
253 if (TypeFrameModelingVisitor.DEBUG) {
252254 System.out.println(methodGen.getClassName() + "." + methodGen.getName() + " " + methodGen.getSignature());
255 }
253256 }
254257
255258 /**
256259 * Constructor which uses StandardTypeMerger.
257 *
260 *
258261 * @param method
259262 * TODO
260263 * @param methodGen
278281 * Set the ValueNumberDataflow for the method being analyzed. This is
279282 * optional; if set, it will be used to make instanceof instructions more
280283 * precise.
281 *
284 *
282285 * @param valueNumberDataflow
283286 * the ValueNumberDataflow
284287 */
290293 /**
291294 * Set the FieldStoreTypeDatabase. This can be used to get more accurate
292295 * types for values loaded from fields.
293 *
296 *
294297 * @param database
295298 * the FieldStoreTypeDatabase
296299 */
301304 /**
302305 * Get the set of exceptions that can be thrown on given edge. This should
303306 * only be called after the analysis completes.
304 *
307 *
305308 * @param edge
306309 * the Edge
307310 * @return the ExceptionSet
311314 return cachedExceptionSet.getEdgeExceptionSet(edge);
312315 }
313316
317 @Override
314318 public TypeFrame createFact() {
315319 return new TypeFrame(methodGen.getMaxLocals());
316320 }
317321
322 @Override
318323 public void initEntryFact(TypeFrame result) {
319324 // Make the frame valid
320325 result.setValid();
325330 result.clearStack();
326331
327332 // Add local for "this" pointer, if present
328 if (!methodGen.isStatic())
333 if (!methodGen.isStatic()) {
329334 result.setValue(slot++, ObjectTypeFactory.getInstance(methodGen.getClassName()));
335 }
330336
331337 // [Added: Support for Generics]
332338 // Get a parser that reads the generic signature of the method and
354360 // replace with a generic version of the type
355361 try {
356362 Type t = GenericUtilities.getType(s);
357 if (t != null)
363 if (t != null) {
358364 argType = t;
365 }
359366 } catch (RuntimeException e) {
360367 } // degrade gracefully
361368 }
366373
367374 // Set remaining locals to BOTTOM; this will cause any
368375 // uses of them to be flagged
369 while (slot < methodGen.getMaxLocals())
376 while (slot < methodGen.getMaxLocals()) {
370377 result.setValue(slot++, TypeFrame.getBottomType());
378 }
371379 }
372380
373381 @Override
397405 visitor.analyzeInstruction(handle.getInstruction());
398406 }
399407
400 /*
401 * (non-Javadoc)
402 *
403 * @see
404 * edu.umd.cs.findbugs.ba.AbstractDataflowAnalysis#transfer(edu.umd.cs.findbugs
405 * .ba.BasicBlock, org.apache.bcel.generic.InstructionHandle,
406 * java.lang.Object, java.lang.Object)
407 */
408408 @Override
409409 public void transfer(BasicBlock basicBlock, @CheckForNull InstructionHandle end, TypeFrame start, TypeFrame result)
410410 throws DataflowAnalysisException {
433433
434434 // Do nothing if we're not computing propagated exceptions
435435 if (!(FORCE_ACCURATE_EXCEPTIONS || AnalysisContext.currentAnalysisContext().getBoolProperty(
436 AnalysisFeatures.ACCURATE_EXCEPTIONS)))
436 AnalysisFeatures.ACCURATE_EXCEPTIONS))) {
437437 return;
438 }
438439
439440 // Also, nothing to do if the block is not an exception thrower
440 if (!basicBlock.isExceptionThrower())
441 if (!basicBlock.isExceptionThrower()) {
441442 return;
443 }
442444
443445 // If cached results are up to date, don't recompute.
444446 CachedExceptionSet cachedExceptionSet = getCachedExceptionSet(basicBlock);
445 if (cachedExceptionSet.isUpToDate(result))
447 if (cachedExceptionSet.isUpToDate(result)) {
446448 return;
449 }
447450
448451 // Figure out what exceptions can be thrown out
449452 // of the basic block, and mark each exception edge
480483 // In the process, this will remove exceptions from
481484 // the thrown exception set.
482485 ExceptionSet thrownExceptionSet = cachedExceptionSet.getExceptionSet();
483 if (!thrownExceptionSet.isEmpty())
486 if (!thrownExceptionSet.isEmpty()) {
484487 thrownExceptionSet = thrownExceptionSet.duplicate();
488 }
485489 for (Iterator<Edge> i = cfg.outgoingEdgeIterator(basicBlock); i.hasNext();) {
486490 Edge edge = i.next();
487 if (edge.isExceptionEdge())
491 if (edge.isExceptionEdge()) {
488492 cachedExceptionSet.setEdgeExceptionSet(edge, computeEdgeExceptionSet(edge, thrownExceptionSet));
489 }
490 }
491
493 }
494 }
495 }
496
497 @Override
492498 public void meetInto(TypeFrame fact, Edge edge, TypeFrame result) throws DataflowAnalysisException {
493499 BasicBlock basicBlock = edge.getTarget();
494500
497503
498504 // Handling an exception?
499505 if (basicBlock.isExceptionHandler()) {
500 tmpFact = modifyFrame(fact, tmpFact);
506 tmpFact = modifyFrame(fact, null);
501507
502508 // Special case: when merging predecessor facts for entry to
503509 // an exception handler, we clear the stack and push a
530536 // pick a type conservatively using the handler catch type.
531537 catchType = exceptionGen.getCatchType();
532538 if (catchType == null)
539 {
533540 catchType = Type.THROWABLE; // handle catches anything
534 // throwable
541 // throwable
542 }
535543 }
536544
537545 tmpFact.pushValue(catchType);
568576
569577 ValueNumber instanceOfValueNumber = check.getValueNumber();
570578 ValueNumberFrame vnaFrame = valueNumberDataflow.getStartFact(edge.getTarget());
571 if (!vnaFrame.isValid())
579 if (!vnaFrame.isValid()) {
572580 return tmpFact;
581 }
573582
574583 Type instanceOfType = check.getType();
575 if (!(instanceOfType instanceof ReferenceType || instanceOfType instanceof NullType))
584 if (!(instanceOfType instanceof ReferenceType || instanceOfType instanceof NullType)) {
576585 return tmpFact;
586 }
577587
578588 short branchOpcode = edge.getSource().getLastInstruction().getInstruction().getOpcode();
579589
588598 // Successful instanceof check.
589599
590600 for (int i = 0; i < numSlots; ++i) {
591 if (!vnaFrame.getValue(i).equals(instanceOfValueNumber))
601 if (!vnaFrame.getValue(i).equals(instanceOfValueNumber)) {
592602 continue;
603 }
593604
594605 Type checkedType = fact.getValue(i);
595 if (!(checkedType instanceof ReferenceType))
606 if (!(checkedType instanceof ReferenceType)) {
596607 continue;
608 }
597609
598610 // Only refine the type if the cast is feasible: i.e., a
599611 // downcast.
600612 // Otherwise, just set it to TOP.
601613 try {
602614 boolean guaranteed = Hierarchy.isSubtype((ReferenceType) checkedType, (ReferenceType) instanceOfType);
603 if (guaranteed)
615 if (guaranteed) {
604616 continue;
617 }
605618
606619 boolean feasibleCheck = instanceOfType.equals(NullType.instance())
607620 || Hierarchy.isSubtype((ReferenceType) instanceOfType, (ReferenceType) checkedType);
609622 if (!feasibleCheck && instanceOfType instanceof ObjectType && checkedType instanceof ObjectType) {
610623 double v = Analyze.deepInstanceOf(((ObjectType) instanceOfType).getClassName(),
611624 ((ObjectType) checkedType).getClassName());
612 if (v > 0.0)
625 if (v > 0.0) {
613626 feasibleCheck = true;
627 }
614628 }
615629 tmpFact = modifyFrame(fact, tmpFact);
616630 if (feasibleCheck) {
627641 } else if (!instanceOfType.equals(NullType.instance())) {
628642
629643 for (int i = 0; i < numSlots; ++i) {
630 if (!vnaFrame.getValue(i).equals(instanceOfValueNumber))
644 if (!vnaFrame.getValue(i).equals(instanceOfValueNumber)) {
631645 continue;
646 }
632647
633648 Type checkedType = fact.getValue(i);
634 if (!(checkedType instanceof ReferenceType))
649 if (!(checkedType instanceof ReferenceType)) {
635650 continue;
651 }
636652 try {
637653 boolean guaranteed = Hierarchy.isSubtype((ReferenceType) checkedType, (ReferenceType) instanceOfType);
638 if (!guaranteed)
654 if (!guaranteed) {
639655 continue;
656 }
640657 tmpFact = modifyFrame(fact, tmpFact);
641658 tmpFact.setTop();
642659 return tmpFact;
671688 * Get the cached set of exceptions that can be thrown from given basic
672689 * block. If this information hasn't been computed yet, then an empty
673690 * exception set is returned.
674 *
691 *
675692 * @param basicBlock
676693 * the block to get the cached exception set for
677694 * @return the CachedExceptionSet for the block
700717 * Compute the set of exceptions that can be thrown from the given basic
701718 * block. This should only be called if the existing cached exception set is
702719 * out of date.
703 *
720 *
704721 * @param basicBlock
705722 * the basic block
706723 * @param result
711728 private CachedExceptionSet computeBlockExceptionSet(BasicBlock basicBlock, TypeFrame result) throws DataflowAnalysisException {
712729
713730 ExceptionSet exceptionSet = computeThrownExceptionTypes(basicBlock);
714
731
715732
716733 TypeFrame copyOfResult = createFact();
717734 copy(result, copyOfResult);
728745 * exception edge. This method should be called for each outgoing exception
729746 * edge in sequence, so the caught exceptions can be removed from the thrown
730747 * exception set as needed.
731 *
748 *
732749 * @param edge
733750 * the exception edge
734751 * @param thrownExceptionSet
738755 */
739756 private ExceptionSet computeEdgeExceptionSet(Edge edge, ExceptionSet thrownExceptionSet) {
740757
741 if (thrownExceptionSet.isEmpty())
758 if (thrownExceptionSet.isEmpty()) {
742759 return thrownExceptionSet;
760 }
743761 ExceptionSet result = exceptionSetFactory.createExceptionSet();
744762
745763 if (edge.getType() == UNHANDLED_EXCEPTION_EDGE) {
768786 ObjectType thrownType = i.next();
769787 boolean explicit = i.isExplicit();
770788
771 if (DEBUG)
789 if (DEBUG) {
772790 System.out.println("\texception type " + thrownType + ", catch type " + catchType);
791 }
773792
774793 try {
775794 if (Hierarchy.isSubtype(thrownType, catchType)) {
779798 // And it will definitely be caught
780799 i.remove();
781800
782 if (DEBUG)
801 if (DEBUG) {
783802 System.out.println("\tException is subtype of catch type: " + "will definitely catch");
803 }
784804 } else if (Hierarchy.isSubtype(catchType, thrownType)) {
785805 // Exception possibly thrown along this edge
786806 result.add(thrownType, explicit);
787807
788 if (DEBUG)
808 if (DEBUG) {
789809 System.out.println("\tException is supertype of catch type: " + "might catch");
810 }
790811 }
791812 } catch (ClassNotFoundException e) {
792813 // As a special case, if a class hierarchy lookup
805826 /**
806827 * Compute the set of exception types that can be thrown by given basic
807828 * block.
808 *
829 *
809830 * @param basicBlock
810831 * the basic block
811832 * @return the set of exceptions that can be thrown by the block
812833 */
813834 private ExceptionSet computeThrownExceptionTypes(BasicBlock basicBlock) throws
814 DataflowAnalysisException {
835 DataflowAnalysisException {
815836
816837 ExceptionSet exceptionTypeSet = exceptionSetFactory.createExceptionSet();
817838 InstructionHandle pei = basicBlock.getExceptionThrower();
893914 // Couldn't find declared exceptions,
894915 // so conservatively assume it could thrown any checked
895916 // exception.
896 if (DEBUG)
917 if (DEBUG) {
897918 System.out.println("Couldn't find declared exceptions for "
898919 + SignatureConverter.convertMethodSignature(inv, cpg));
920 }
899921 exceptionTypeSet.addExplicit(Hierarchy.EXCEPTION_TYPE);
900922 } else {
901923 for (ObjectType aDeclaredExceptionList : declaredExceptionList) {
906928 exceptionTypeSet.addImplicit(Hierarchy.RUNTIME_EXCEPTION_TYPE);
907929 }
908930
909 if (DEBUG)
931 if (DEBUG) {
910932 System.out.println(pei + " can throw " + exceptionTypeSet);
933 }
911934
912935 return exceptionTypeSet;
913936 }
942965 // driver.execute(argv[0]);
943966 // }
944967 }
945
946 // vim:ts=3
3434 * @see TypeAnalysis
3535 */
3636 public class TypeFrame extends Frame<Type> {
37 private BitSet exactTypeSet;
37 private final BitSet exactTypeSet;
3838
3939 /**
4040 * Constructor.
163163
164164 @Override
165165 public String toString() {
166 if (isTop())
166 if (isTop()) {
167167 return "[TOP]";
168 if (isBottom())
168 }
169 if (isBottom()) {
169170 return "[BOTTOM]";
171 }
170172 StringBuilder buf = new StringBuilder();
171173 buf.append('[');
172174 int numSlots = getNumSlots();
178180 // the operand stack.
179181 int last = buf.length() - 1;
180182 if (last >= 0) {
181 if (buf.charAt(last) == ',')
183 if (buf.charAt(last) == ',') {
182184 buf.deleteCharAt(last);
185 }
183186 }
184187 buf.append(" | ");
185188 }
186 if (isExact(i))
189 if (isExact(i)) {
187190 buf.append("!");
191 }
188192 String value = valueToString(getValue(i));
189 if (i == numSlots - 1 && value.endsWith(","))
193 if (i == numSlots - 1 && value.endsWith(",")) {
190194 value = value.substring(0, value.length() - 1);
195 }
191196 buf.append(value);
192197 // buf.append(' ');
193198 }
197202
198203 }
199204
200 // vim:ts=4
9999 * examining
100100 * @param typeMerger
101101 * TODO
102 * @param typesComputerFromGenerics
103 * TODO
104102 */
105103 public TypeFrameModelingVisitor(ConstantPoolGen cpg, TypeMerger typeMerger) {
106104 super(cpg);
122120
123121 public void setLocalTypeTable(LocalVariableTypeTable localTypeTable) {
124122 this.localTypeTable = localTypeTable;
125 if (localTypeTable == null)
123 if (localTypeTable == null) {
126124 genericLocalVariables = null;
127 else {
125 } else {
128126 genericLocalVariables = new BitSet();
129127 for(LocalVariable lv : localTypeTable.getLocalVariableTypeTable()) {
130 if (lv.getSignature().indexOf('<') > 0)
128 if (lv.getSignature().indexOf('<') > 0) {
131129 genericLocalVariables.set(lv.getIndex());
130 }
132131
133132 }
134133 }
216215 TypeFrame frame = getFrame();
217216
218217 int numWordsConsumed = ins.consumeStack(cpg);
219 if (numWordsConsumed == Constants.UNPREDICTABLE)
218 if (numWordsConsumed == Constants.UNPREDICTABLE) {
220219 throw new InvalidBytecodeException("Unpredictable stack consumption for " + ins);
220 }
221 if (numWordsConsumed > frame.getStackDepth()) {
222 throw new InvalidBytecodeException("Stack underflow for " + ins + ", " + numWordsConsumed + " needed, " + frame.getStackDepth() + " avail, frame is " + frame);
223 }
221224 try {
222225 while (numWordsConsumed-- > 0) {
223226 frame.popValue();
233236 * method ensures that we push two types for each double or long value.
234237 */
235238 protected void pushValue(Type type) {
236 if (type.getType() == T_VOID)
239 if (type.getType() == T_VOID) {
237240 throw new IllegalArgumentException("Can't push void");
241 }
238242 TypeFrame frame = getFrame();
239243 if (type.getType() == T_LONG) {
240244 frame.pushValue(Type.LONG);
242246 } else if (type.getType() == T_DOUBLE) {
243247 frame.pushValue(Type.DOUBLE);
244248 frame.pushValue(TypeFrame.getDoubleExtraType());
245 } else
249 } else {
246250 frame.pushValue(type);
251 }
247252 }
248253
249254 /**
252257 protected void pushReturnType(InvokeInstruction ins) {
253258 ConstantPoolGen cpg = getCPG();
254259 Type type = ins.getType(cpg);
255 if (type.getType() != T_VOID)
260 if (type.getType() != T_VOID) {
256261 pushValue(type);
262 }
257263 }
258264
259265 /**
263269 @Override
264270 public void modelNormalInstruction(Instruction ins, int numWordsConsumed, int numWordsProduced) {
265271 if (VERIFY_INTEGRITY) {
266 if (numWordsProduced > 0)
272 if (numWordsProduced > 0) {
267273 throw new InvalidBytecodeException("missing visitor method for " + ins);
274 }
268275 }
269276 super.modelNormalInstruction(ins, numWordsConsumed, numWordsProduced);
270277 }
349356 Type loadType = obj.getFieldType(cpg);
350357
351358 XField xfield = Hierarchy.findXField(obj, getCPG());
352 if (xfield != null)
359 if (xfield != null) {
353360 loadType = getType(xfield);
361 }
354362
355363
356364 pushValue(loadType);
357365 }
358366
359 /**
360 * @param xfield
361 * @return
362 */
363367 public static Type getType(XField xfield) {
364368 Type t = Type.getType(xfield.getSignature());
365 if (!(t instanceof ReferenceType))
369 if (!(t instanceof ReferenceType)) {
366370 return t;
371 }
367372 ReferenceType loadType = (ReferenceType) t;
368373
369374 // Check the field store type database to see if we can
389394 if (xfield.isFinal() && summary.isNull()) {
390395 return TypeFrame.getNullType();
391396 }
392 if (!summary.getSignature().equals("Ljava/lang/Object;")) {
397 if (!"Ljava/lang/Object;".equals(summary.getSignature())) {
393398 loadType = (ReferenceType) Type.getType(summary
394399 .getSignature());
395400 }
412417 String methodName = obj.getMethodName(cpg);
413418 String signature = obj.getSignature(cpg);
414419 String className = obj.getClassName(cpg);
415 if (methodName.equals("asList") && className.equals("java.util.Arrays")
416 && signature.equals("([Ljava/lang/Object;)Ljava/util/List;")) {
420 if ("asList".equals(methodName) && "java.util.Arrays".equals(className)
421 && "([Ljava/lang/Object;)Ljava/util/List;".equals(signature)) {
417422 consumeStack(obj);
418423 Type returnType = Type.getType("Ljava/util/Arrays$ArrayList;");
419424 pushValue(returnType);
445450 List<? extends ReferenceType> parameters = genericMapType.getParameters();
446451 if (parameters != null && parameters.size() == expectedParameters) {
447452 ReferenceType resultType = parameters.get(index);
448 if (resultType instanceof GenericObjectType)
453 if (resultType instanceof GenericObjectType) {
449454 resultType = ((GenericObjectType) resultType).produce();
455 }
450456 typesComputedFromGenerics.add(resultType);
451457 frame.popValue();
452458 frame.pushValue(resultType);
467473 if (mapType instanceof GenericObjectType) {
468474 GenericObjectType genericMapType = (GenericObjectType) mapType;
469475 List<? extends ReferenceType> parameters = genericMapType.getParameters();
470 if (parameters == null)
476 if (parameters == null) {
471477 return false;
478 }
472479 if (parameters.size() == expectedNumberOfTypeParameters) {
473480 ReferenceType keyType = parameters.get(index);
474481 frame.popValue();
497504 String className = obj.getClassName(cpg);
498505
499506 String returnValueSignature = new SignatureParser(signature).getReturnTypeSignature();
500 if (returnValueSignature.equals("V")) {
507 if ("V".equals(returnValueSignature)) {
501508 consumeStack(obj);
502509 return;
503510 }
504511
505 if (methodName.equals("isInstance")) {
506 if (className.equals("java.lang.Class") && valueNumberDataflow != null) {
512 if ("isInstance".equals(methodName)) {
513 if ("java.lang.Class".equals(className) && valueNumberDataflow != null) {
507514 // Record the value number of the value checked by this
508515 // instruction,
509516 // and the type the value was compared to.
514521 if (stackValue.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) {
515522 String c = valueNumberDataflow.getClassName(stackValue);
516523 if (c != null) {
517 if (c.charAt(0) != '[' && !c.endsWith(";"))
524 if (c.charAt(0) != '[' && !c.endsWith(";")) {
518525 c = "L" + c.replace('.', '/') + ";";
526 }
519527 Type type = Type.getType(c);
520528 if (type instanceof ReferenceType) {
521529 instanceOfValueNumber = vnaFrame.getTopValue();
539547 return;
540548 }
541549
542 if (methodName.equals("cast") && className.equals("java.lang.Class")) {
550 if ("cast".equals(methodName) && "java.lang.Class".equals(className)) {
543551 try {
544552 Type resultType = frame.popValue();
545553 frame.popValue();
551559 return;
552560 }
553561
554 mapGetCheck: if (methodName.equals("get") && signature.equals("(Ljava/lang/Object;)Ljava/lang/Object;")
562 mapGetCheck: if ("get".equals(methodName) && "(Ljava/lang/Object;)Ljava/lang/Object;".equals(signature)
555563 && className.endsWith("Map") && Subtypes2.instanceOf(className, "java.util.Map")
556564 && frame.getStackDepth() >= 2) {
557565 try {
559567 if (mapType instanceof GenericObjectType) {
560568 GenericObjectType genericMapType = (GenericObjectType) mapType;
561569 List<? extends ReferenceType> parameters = genericMapType.getParameters();
562 if (parameters == null || parameters.size() != 2)
570 if (parameters == null || parameters.size() != 2) {
563571 break mapGetCheck;
572 }
564573
565574 ClassDescriptor c = DescriptorFactory.getClassDescriptor(genericMapType);
566 if (!Subtypes2.instanceOf(c, Map.class))
575 if (!Subtypes2.instanceOf(c, Map.class)) {
567576 break mapGetCheck;
568 if (!isStraightGenericMap(c))
577 }
578 if (!isStraightGenericMap(c)) {
569579 break mapGetCheck;
580 }
570581
571582 ReferenceType valueType = parameters.get(1);
572583 consumeStack(obj);
580591
581592 }
582593
583 if (className.equals("java.util.Map$Entry"))
584 if (methodName.equals("getKey") && getResultTypeFromGenericType(frame, 0, 2) || methodName.equals("getValue")
585 && getResultTypeFromGenericType(frame, 1, 2))
594 if ("java.util.Map$Entry".equals(className)) {
595 if ("getKey".equals(methodName) && getResultTypeFromGenericType(frame, 0, 2) || "getValue".equals(methodName)
596 && getResultTypeFromGenericType(frame, 1, 2)) {
586597 return;
587
588 if (methodName.equals("entrySet") && signature.equals("()Ljava/util/Set;") && className.startsWith("java.util")
598 }
599 }
600
601 if ("entrySet".equals(methodName) && "()Ljava/util/Set;".equals(signature) && className.startsWith("java.util")
589602 && className.endsWith("Map")) {
590603 Type argType;
591604 try {
599612 if (argType instanceof GenericObjectType) {
600613 GenericObjectType genericArgType = (GenericObjectType) argType;
601614 List<? extends ReferenceType> parameters = genericArgType.getParameters();
602 if (parameters != null && parameters.size() == 2)
615 if (parameters != null && parameters.size() == 2) {
603616 mapType = GenericUtilities.getType("java.util.Map$Entry", parameters);
617 }
604618 }
605619 GenericObjectType entrySetType = GenericUtilities.getType("java.util.Set", Collections.singletonList(mapType));
606620 frame.pushValue(entrySetType);
607621 return;
608622
609623 }
610 if (className.startsWith("java.util") && className.endsWith("Map"))
611 if (methodName.equals("keySet") && signature.equals("()Ljava/util/Set;")
612 && handleGetMapView(frame, "java.util.Set", 0, 2) || methodName.equals("values")
613 && signature.equals("()Ljava/util/Collection;") && handleGetMapView(frame, "java.util.Collection", 1, 2))
624 if (className.startsWith("java.util") && className.endsWith("Map")) {
625 if ("keySet".equals(methodName) && "()Ljava/util/Set;".equals(signature)
626 && handleGetMapView(frame, "java.util.Set", 0, 2) || "values".equals(methodName)
627 && "()Ljava/util/Collection;".equals(signature) && handleGetMapView(frame, "java.util.Collection", 1, 2)) {
614628 return;
615
616 if (methodName.equals("iterator") && signature.equals("()Ljava/util/Iterator;") && className.startsWith("java.util")
617 && handleGetMapView(frame, "java.util.Iterator", 0, 1))
629 }
630 }
631
632 if ("iterator".equals(methodName) && "()Ljava/util/Iterator;".equals(signature) && className.startsWith("java.util")
633 && handleGetMapView(frame, "java.util.Iterator", 0, 1)) {
618634 return;
619 if (className.equals("java.util.Iterator") && methodName.equals("next") && signature.equals("()Ljava/lang/Object;")
620 && getResultTypeFromGenericType(frame, 0, 1))
635 }
636 if ("java.util.Iterator".equals(className) && "next".equals(methodName) && "()Ljava/lang/Object;".equals(signature)
637 && getResultTypeFromGenericType(frame, 0, 1)) {
621638 return;
622
623 if (methodName.equals("initCause") && signature.equals("(Ljava/lang/Throwable;)Ljava/lang/Throwable;")
639 }
640
641 if ("initCause".equals(methodName) && "(Ljava/lang/Throwable;)Ljava/lang/Throwable;".equals(signature)
624642 && (className.endsWith("Exception")
625 || className.endsWith("Error"))) {
643 || className.endsWith("Error"))) {
626644 try {
627645
628646 frame.popValue();
631649 AnalysisContext.logError("Ooops", e);
632650 }
633651 }
634 if (handleToArray(obj))
652 if (handleToArray(obj)) {
635653 return;
654 }
636655 Type result = TopType.instance();
637656 try {
638657 Set<XMethod> targets = Hierarchy2.resolveMethodCallTargets(obj, frame, cpg);
645664 String sourceSignature = m.getSourceSignature();
646665 if (DEBUG) {
647666 System.out.println(" Call target: " + m);
648 if (sourceSignature != null)
667 if (sourceSignature != null) {
649668 System.out.println(" source signature: " + sourceSignature);
669 }
650670 }
651671 boolean foundSomething = false;
652672 XMethod m2 = m.bridgeTo();
653 if (m2 != null)
673 if (m2 != null) {
654674 m = m2;
675 }
655676 if (sourceSignature != null && !sourceSignature.equals(m.getSignature())) {
656677 GenericSignatureParser p = new GenericSignatureParser(sourceSignature);
657678 String rv = p.getReturnTypeSignature();
682703 }
683704 if (!foundSomething) {
684705 result = TopType.instance();
685 if (DEBUG)
706 if (DEBUG) {
686707 System.out.println(" giving up");
708 }
687709 break;
688710 }
689711 }
697719 }
698720
699721 consumeStack(obj);
700 if (result instanceof TopType)
722 if (result instanceof TopType) {
701723 pushReturnType(obj);
702 else
724 } else {
703725 pushValue(result);
726 }
704727 }
705728
706729 public static final Pattern mapSignaturePattern = Pattern.compile("<(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*):L[^;]*;(\\p{javaJavaIdentifierStart}\\p{javaJavaIdentifierPart}*):L[^;]*;>.*Ljava/util/(\\p{javaJavaIdentifierStart}(\\p{javaJavaIdentifierPart}|/)*)?Map<T\\1;T\\2;>;.*");
707730 public static boolean isStraightGenericMap(ClassDescriptor c) {
708 if (c.matches(Map.class))
731 if (c.matches(Map.class)) {
709732 return true;
733 }
710734 XClass xc;
711735 try {
712736 xc = c.getXClass();
714738 return false;
715739 }
716740 String sourceSignature = xc.getSourceSignature();
717 if (sourceSignature == null)
741 if (sourceSignature == null) {
718742 return false;
743 }
719744 if (sourceSignature.startsWith("<")) {
720745 Matcher matcher = mapSignaturePattern.matcher(sourceSignature);
721746 if (!matcher.matches()) {
733758 private Type merge(Type prevType, Type newType) throws DataflowAnalysisException {
734759 if (prevType.equals(TopType.instance())) {
735760
736 if (DEBUG)
761 if (DEBUG) {
737762 System.out.println("Got " + newType);
763 }
738764 return newType;
739765 } else if (prevType.equals(newType)) {
740766 return prevType;
755781 return false;
756782 }
757783 Type topValue = frame.getTopValue();
758 if (obj.getName(getCPG()).equals("toArray")) {
784 if ("toArray".equals(obj.getName(getCPG()))) {
759785 ReferenceType target = obj.getReferenceType(getCPG());
760786 String signature = obj.getSignature(getCPG());
761 if (signature.equals("([Ljava/lang/Object;)[Ljava/lang/Object;") && Subtypes2.isCollection(target)) {
787 if ("([Ljava/lang/Object;)[Ljava/lang/Object;".equals(signature) && Subtypes2.isCollection(target)) {
762788
763789 boolean topIsExact = frame.isExact(frame.getStackLocation(0));
764790 Type resultType = frame.popValue();
766792 frame.pushValue(resultType);
767793 frame.setExact(frame.getStackLocation(0), topIsExact);
768794 return true;
769 } else if (signature.equals("()[Ljava/lang/Object;") && Subtypes2.isCollection(target)
770 && !topValue.getSignature().equals("Ljava/util/Arrays$ArrayList;")) {
795 } else if ("()[Ljava/lang/Object;".equals(signature) && Subtypes2.isCollection(target)
796 && !"Ljava/util/Arrays$ArrayList;".equals(topValue.getSignature())) {
771797 consumeStack(obj);
772798 pushReturnType(obj);
773799 frame.setExact(frame.getStackLocation(0), true);
785811
786812 @CheckForNull
787813 GenericObjectType getLocalVariable(int index, int pos) {
788 if (genericLocalVariables == null || !genericLocalVariables.get(index))
814 if (genericLocalVariables == null || !genericLocalVariables.get(index)) {
789815 return null;
790 for (LocalVariable local : localTypeTable.getLocalVariableTypeTable())
816 }
817 for (LocalVariable local : localTypeTable.getLocalVariableTypeTable()) {
791818 if (local.getIndex() == index && local.getStartPC() <= pos
792819 && pos < +local.getStartPC() + local.getLength()) {
793820 String signature = local.getSignature();
794 if (signature.indexOf('<') < 0) continue;
821 if (signature.indexOf('<') < 0) {
822 continue;
823 }
795824 Type t;
796825 try {
797826 t = GenericUtilities.getType(signature);
798 if (t instanceof GenericObjectType)
827 if (t instanceof GenericObjectType) {
799828 return (GenericObjectType) t;
829 }
800830 } catch (RuntimeException e) {
801831 AnalysisContext.logError("Bad signature " + signature
802832 + " for " + local.getName(), e);
804834 }
805835 return null;
806836 }
837 }
807838 return null;
808839 }
809840
818849 Type value = frame.popValue();
819850 int index = obj.getIndex();
820851 if (value instanceof ReferenceType && !(value instanceof GenericObjectType)) {
821 GenericObjectType gType = getLocalVariable(index,
852 GenericObjectType gType = getLocalVariable(index,
822853 getLocation().getHandle().getPosition());
823854 value = GenericUtilities.merge(gType, value);
824855 }
825856 frame.setValue(index, value);
826857 frame.setExact(index, isExact);
827 } else
858 } else {
828859 super.handleStoreInstruction(obj);
860 }
829861
830862 } catch (DataflowAnalysisException e) {
831863 throw new InvalidBytecodeException(
841873 @Override
842874 public void handleLoadInstruction(LoadInstruction obj) {
843875 int numProduced = obj.produceStack(cpg);
844 if (numProduced == Constants.UNPREDICTABLE)
876 if (numProduced == Constants.UNPREDICTABLE) {
845877 throw new InvalidBytecodeException("Unpredictable stack production");
878 }
846879
847880 if (numProduced != 1) {
848881 super.handleLoadInstruction(obj);
858891 }
859892 boolean isExact = frame.isExact(index);
860893 frame.pushValue(value);
861 if (isExact)
894 if (isExact) {
862895 setTopOfStackIsExact();
896 }
863897 }
864898
865899 @Override
867901
868902 try {
869903 Type t = getFrame().popValue();
870 if (t instanceof NullType)
904 if (t instanceof NullType) {
871905 pushValue(t);
872 else
906 } else {
873907 pushValue(obj.getType(getCPG()));
908 }
874909 } catch (DataflowAnalysisException e) {
875910 throw new InvalidBytecodeException("Stack underflow for " + obj + ": " + e.getMessage());
876911 }
11711206 boolean isExact = isTopOfStackExact();
11721207 Type value = frame.popValue();
11731208 frame.pushValue(value);
1174 if (isExact)
1209 if (isExact) {
11751210 setTopOfStackIsExact();
1211 }
11761212 frame.pushValue(value);
1177 if (isExact)
1213 if (isExact) {
11781214 setTopOfStackIsExact();
1215 }
11791216 } catch (DataflowAnalysisException e) {
11801217 throw new InvalidBytecodeException(e.toString());
11811218 }
14281465
14291466 @Override
14301467 public void visitIFEQ(IFEQ obj) {
1431 if (previousWasEffectiveInstanceOf)
1468 if (previousWasEffectiveInstanceOf) {
14321469 instanceOfFollowedByBranch = true;
1470 }
14331471 super.visitIFEQ(obj);
14341472 }
14351473
14361474 @Override
14371475 public void visitIFGT(IFGT obj) {
1438 if (previousWasEffectiveInstanceOf)
1476 if (previousWasEffectiveInstanceOf) {
14391477 instanceOfFollowedByBranch = true;
1478 }
14401479 super.visitIFGT(obj);
14411480 }
14421481
14431482 @Override
14441483 public void visitIFLE(IFLE obj) {
1445 if (previousWasEffectiveInstanceOf)
1484 if (previousWasEffectiveInstanceOf) {
14461485 instanceOfFollowedByBranch = true;
1486 }
14471487 super.visitIFLE(obj);
14481488 }
14491489
14501490 @Override
14511491 public void visitIFNE(IFNE obj) {
1452 if (previousWasEffectiveInstanceOf)
1492 if (previousWasEffectiveInstanceOf) {
14531493 instanceOfFollowedByBranch = true;
1494 }
14541495 super.visitIFNE(obj);
14551496 }
14561497
14591500 }
14601501 }
14611502
1462 // vim:ts=4
2525 /**
2626 * Interface for object which merges types for TypeAnalysis. By allowing
2727 * multiple implementations, we allow creation of specialized type systems.
28 *
28 *
2929 * @author David Hovemeyer
3030 * @see TypeAnalysis
3131 */
3232 public interface TypeMerger {
3333 /**
3434 * Merge type Types.
35 *
35 *
3636 * @param a
3737 * a Type
3838 * @param b
4242 public Type mergeTypes(Type a, Type b) throws DataflowAnalysisException;
4343 }
4444
45 // vim:ts=4
3131 * <p>
3232 * AvailableLoad objects may be used as keys in both hash and tree sets and
3333 * maps.
34 *
34 *
3535 * @author David Hovemeyer
3636 * @see ValueNumberAnalysis
3737 */
4242
4343 /**
4444 * Constructor from static field.
45 *
45 *
4646 * @param staticField
4747 * the StaticField
4848 */
5353
5454 /**
5555 * Constructor from object reference and instance field.
56 *
56 *
5757 * @param reference
5858 * the ValueNumber of the object reference
5959 * @param field
6060 * the InstanceField
6161 */
6262 public AvailableLoad(ValueNumber reference, XField field) {
63 if (reference == null)
63 if (reference == null) {
6464 throw new IllegalArgumentException();
65 }
6566 this.reference = reference;
6667 this.field = field;
6768 }
6869
6970 /**
7071 * Get the ValueNumber of the object reference.
71 *
72 *
7273 * @return the ValueNumber, or null if this is a an available static field
7374 * load
7475 */
7778 }
7879
7980 public boolean matchesReference(ValueNumber v) {
80 if (v == reference)
81 if (v == reference) {
8182 return true;
82 if (reference == null)
83 }
84 if (reference == null) {
8385 return false;
86 }
8487 return reference.equals(v);
8588 }
8689
8790 /**
8891 * Get the field for which a load is available.
89 *
92 *
9093 * @return the XField
9194 */
9295 public XField getField() {
9396 return field;
9497 }
9598
99 @Override
96100 @SuppressWarnings("unchecked")
97101 public int compareTo(AvailableLoad other) {
98102 int cmp = field.compareTo(other.field);
99 if (cmp != 0)
103 if (cmp != 0) {
100104 return cmp;
101 else if (reference == other.reference)
105 } else if (reference == other.reference) {
102106 return 0;
103 else if (reference == null)
107 } else if (reference == null) {
104108 return -1;
105 else if (other.reference == null)
109 } else if (other.reference == null) {
106110 return 1;
107 else
111 } else {
108112 return reference.compareTo(other.reference);
113 }
109114 }
110115
111116 @Override
115120
116121 @Override
117122 public boolean equals(Object o) {
118 if (o == null || this.getClass() != o.getClass())
123 if (o == null || this.getClass() != o.getClass()) {
119124 return false;
125 }
120126 AvailableLoad other = (AvailableLoad) o;
121127 return (reference == other.reference || (reference != null && other.reference != null && reference
122128 .equals(other.reference))) && field.equals(other.field);
128134 }
129135 }
130136
131 // vim:ts=4
3333 * fields are loaded/stored by the overall method. The main purpose is for doing
3434 * redundant load elimination and forward substitution more efficiently, but it
3535 * might be useful in other situations.
36 *
36 *
3737 * @author David Hovemeyer
3838 */
3939 public class LoadedFieldSet {
5656
5757 // Fields
5858 // private MethodGen methodGen;
59 private Map<XField, LoadStoreCount> loadStoreCountMap;
59 private final Map<XField, LoadStoreCount> loadStoreCountMap;
6060
61 private Map<InstructionHandle, XField> handleToFieldMap;
61 private final Map<InstructionHandle, XField> handleToFieldMap;
6262
63 private BitSet loadHandleSet;
63 private final BitSet loadHandleSet;
6464
6565 /**
6666 * Constructor. Constructs an empty object.
67 *
67 *
6868 * @param methodGen
6969 * the method being analyzed for loads/stores
7070 */
7878 /**
7979 * Get the number of times given field is loaded and stored within the
8080 * method.
81 *
81 *
8282 * @param field
8383 * the field
8484 * @return the load/store count object
9494
9595 /**
9696 * Add a load of given field at given instruction.
97 *
97 *
9898 * @param handle
9999 * the instruction
100100 * @param field
108108
109109 /**
110110 * Add a store of given field at given instruction.
111 *
111 *
112112 * @param handle
113113 * the instruction
114114 * @param field
121121
122122 /**
123123 * Get the field loaded or stored at given instruction, if any.
124 *
124 *
125125 * @param handle
126126 * the instruction
127127 * @return the field loaded or stored at the instruction, or null if the
134134 /**
135135 * Return whether or not the given field is loaded by any instruction in the
136136 * method.
137 *
137 *
138138 * @param field
139139 * the field
140140 * @return true if the field is loaded somewhere in the method, false if it
146146
147147 /**
148148 * Return whether or not the given instruction is a load.
149 *
149 *
150150 * @param handle
151151 * the instruction
152152 * @return true if the instruction is a load, false if not
156156 }
157157 }
158158
159 // vim:ts=4
2222 import java.util.HashMap;
2323 import java.util.LinkedList;
2424 import java.util.Map;
25 import java.util.Map.Entry;
2526
2627 import edu.umd.cs.findbugs.SystemProperties;
2728
2829 /**
2930 * Data structure to keep track of which input ValueNumbers were combined to
3031 * produce which other output ValueNumbers.
31 *
32 *
3233 * @author David Hovemeyer
3334 */
3435 public class MergeTree {
3536 public static final boolean DEBUG = SystemProperties.getBoolean("vna.merge.debug");
3637
37 private ValueNumberFactory factory;
38 private final ValueNumberFactory factory;
3839
39 private Map<ValueNumber, BitSet> outputToInputMap;
40 private final Map<ValueNumber, BitSet> outputToInputMap;
4041
4142 /**
4243 * Constructor.
43 *
44 *
4445 * @param factory
4546 * the ValueNumberFactory
4647 */
5152
5253 /**
5354 * Map an input ValueNumber to an output ValueNumber.
54 *
55 *
5556 * @param input
5657 * the input ValueNumber
5758 * @param output
6970 /**
7071 * Get the set of input ValueNumbers which directly contributed to the given
7172 * output ValueNumber.
72 *
73 *
7374 * @param output
7475 * the output ValueNumber
7576 * @return the set of direct input ValueNumbers
8990 /**
9091 * Get the transitive set of input ValueNumbers which contributed (directly
9192 * or indirectly) to the given output ValueNumber.
92 *
93 *
9394 * @param output
9495 * the output ValueNumber
9596 * @return the transitive set of input ValueNumbers
115116 System.out.println("\tInput set is " + inputSet);
116117 }
117118 result.or(inputSet);
118 for (int i = 0; i < factory.getNumValuesAllocated(); ++i) {
119 if (inputSet.get(i) && !visited.get(i)) {
119 for (int i = inputSet.nextSetBit(0); i >= 0; i = inputSet.nextSetBit(i+1)) {
120 if (!visited.get(i)) {
120121 if (DEBUG) {
121122 System.out.println("\tExplore: " + i);
122123 }
130131
131132 return result;
132133 }
134
135 public BitSet getTransitiveOutputSet(int input) {
136 BitSet visited = new BitSet();
137 BitSet result = new BitSet();
138
139 LinkedList<Integer> workList = new LinkedList<Integer>();
140 workList.addLast(input);
141 while (!workList.isEmpty()) {
142 Integer valueNumber = workList.removeFirst();
143 visited.set(valueNumber);
144 BitSet outputSet = getOutputSet(valueNumber);
145 result.or(outputSet);
146 for (int i = outputSet.nextSetBit(0); i >= 0; i = outputSet.nextSetBit(i+1)) {
147 if (!visited.get(i)) {
148 workList.addLast(i);
149 }
150 }
151 }
152 return result;
153 }
154
155 private BitSet getOutputSet(int valueNumber) {
156 BitSet result = new BitSet();
157 for(Entry<ValueNumber, BitSet> entry : outputToInputMap.entrySet()) {
158 if(entry.getValue().get(valueNumber)) {
159 result.set(entry.getKey().getNumber());
160 }
161 }
162 return result;
163 }
133164 }
3131 * ValueNumberFactory} are unique, so reference equality may be used to
3232 * determine whether or not two value numbers are the same. In general,
3333 * ValueNumbers from different factories cannot be compared.
34 *
34 *
3535 * @author David Hovemeyer
3636 * @see ValueNumberAnalysis
3737 */
4141 static int valueNumbersCreated = 0;
4242
4343 static int valueNumbersReused = 0;
44
44
4545 public static int mergeFlags(int flags1, int flags2) {
46 if (flags1 == -1) return flags2;
47 if (flags2 == -1) return flags1;
46 if (flags1 == -1) {
47 return flags2;
48 }
49 if (flags2 == -1) {
50 return flags1;
51 }
4852 return flags1 & flags2;
4953 }
5054
6771 static {
6872 Util.runLogAtShutdown(new Runnable() {
6973
74 @Override
7075 public void run() {
7176 System.out.println("Value number statistics: " + valueNumbersCreated + " created, " + valueNumbersReused
7277 + " reused");
8186 final int number;
8287
8388 /**
84 * Flags representing meta information about the value. When value numbers are merged,
89 * Flags representing meta information about the value. When value numbers are merged,
8590 * their flags should be the flags common to both.
8691 */
8792 final int flags;
101106
102107 /**
103108 * Constructor.
104 *
109 *
105110 * @param number
106111 * the value number
107112 */
129134
130135 @Override
131136 public String toString() {
132 if (flags != 0)
137 if (flags != 0) {
133138 return number + "(" + flags + "),";
139 }
134140 return number + ",";
135141 }
136142
147153 return false;
148154 }
149155
156 @Override
150157 public int compareTo(ValueNumber other) {
151158 int result = number - other.number;
152 if (result != 0)
159 if (result != 0) {
153160 return result;
161 }
154162 return flags - other.flags;
155163
156164 }
157165 }
158166
159 // vim:ts=4
1818
1919 package edu.umd.cs.findbugs.ba.vna;
2020
21 import java.util.BitSet;
2221 import java.util.HashMap;
2322 import java.util.IdentityHashMap;
2423 import java.util.Iterator;
2524
2625 import javax.annotation.CheckForNull;
2726
27 import org.apache.bcel.generic.INVOKEDYNAMIC;
28 import org.apache.bcel.generic.Instruction;
2829 import org.apache.bcel.generic.InstructionHandle;
30 import org.apache.bcel.generic.InvokeInstruction;
2931 import org.apache.bcel.generic.MethodGen;
3032
3133 import edu.umd.cs.findbugs.SystemProperties;
8789
8890 int numLocals = methodGen.getMaxLocals();
8991 this.entryLocalValueList = new ValueNumber[numLocals];
90 for (int i = 0; i < numLocals; ++i)
92 for (int i = 0; i < numLocals; ++i) {
9193 this.entryLocalValueList[i] = factory.createFreshValue();
94 }
9295
9396 this.exceptionHandlerValueNumberMap = new IdentityHashMap<BasicBlock, ValueNumber>();
9497
9598 // For non-static methods, keep track of which value represents the
9699 // "this" reference
97 if (!methodGen.isStatic())
100 if (!methodGen.isStatic()) {
98101 this.thisValue = entryLocalValueList[0];
102 }
99103
100104 this.factAtLocationMap = new HashMap<Location, ValueNumberFrame>();
101105 this.factAfterLocationMap = new HashMap<Location, ValueNumberFrame>();
102 if (DEBUG)
106 if (DEBUG) {
103107 System.out.println("VNA Analysis " + methodGen.getClassName() + "." + methodGen.getName() + " : "
104108 + methodGen.getSignature());
109 }
105110
106111 }
107112
154159 * @return the ValueNumber assigned to that parameter
155160 */
156161 public ValueNumber getEntryValueForParameter(int param) {
157
162
158163 SignatureParser sigParser = new SignatureParser(methodGen.getSignature());
159164 int p = 0;
160165 int slotOffset = methodGen.isStatic() ? 0 : 1;
161166
162167 for ( String paramSig : sigParser.parameterSignatures()) {
163 if (p == param)
168 if (p == param) {
164169 return getEntryValue(slotOffset);
170 }
165171 param++;
166172 slotOffset += SignatureParser.getNumSlotsForType(paramSig);
167173 }
168
174
169175 throw new IllegalStateException();
170176 }
171177
178 @Override
172179 public ValueNumberFrame createFact() {
173180 return new ValueNumberFrame(methodGen.getMaxLocals());
174181 }
175182
183 @Override
176184 public void initEntryFact(ValueNumberFrame result) {
177185 // Change the frame from TOP to something valid.
178186 result.setValid();
180188 // At entry to the method, each local has (as far as we know) a unique
181189 // value.
182190 int numSlots = result.getNumSlots();
183 for (int i = 0; i < numSlots; ++i)
191 for (int i = 0; i < numSlots; ++i) {
184192 result.setValue(i, entryLocalValueList[i]);
193 }
194 }
195
196 @Override
197 public void transfer(BasicBlock basicBlock, InstructionHandle end, ValueNumberFrame start, ValueNumberFrame result)
198 throws DataflowAnalysisException {
199 if(basicBlock.isExceptionThrower() && isFactValid(start)) {
200 /* If exceptionThrower is invoke instruction then it's possible that
201 * it was partially executed before an exception occurred
202 * So we have to kill available loads when control is transferred to the catch block
203 */
204 InstructionHandle handle = basicBlock.getExceptionThrower();
205 Instruction inst = handle.getInstruction();
206 if(inst instanceof InvokeInstruction || inst instanceof INVOKEDYNAMIC) {
207 copy(start, result);
208 visitor.setFrameAndLocation(result, new Location(handle, basicBlock));
209 visitor.setHandle(handle);
210 visitor.visitInvokeOnException(inst);
211 return;
212 }
213 }
214 super.transfer(basicBlock, end, start, result);
185215 }
186216
187217 @Override
193223 ValueNumberFrame atLocation = getFactAtLocation(location);
194224 copy(fact, atLocation);
195225
196 visitor.setFrameAndLocation(fact, new Location(handle, basicBlock));
226 visitor.setFrameAndLocation(fact, location);
197227 visitor.setHandle(handle);
198228 visitor.analyzeInstruction(handle.getInstruction());
199229
201231 copy(fact, afterLocation);
202232 }
203233
234 @Override
204235 public void meetInto(ValueNumberFrame fact, Edge edge, ValueNumberFrame result) throws DataflowAnalysisException {
205236 if (edge.getTarget().isExceptionHandler() && fact.isValid()) {
206237 // Special case: when merging predecessor facts for entry to
236267 resultFrame.setValue(slot, value);
237268 }
238269
239 private ValueNumber mergeValues(ValueNumberFrame frame, int slot, ValueNumber mine, ValueNumber other)
240 {
270 private ValueNumber mergeValues(ValueNumberFrame frame, int slot, ValueNumber mine, ValueNumber other) {
241271
242272 // Merging slot values:
243273 // - Merging identical values results in no change
253283 // I believe (but haven't proved) that this technique is a dumb way
254284 // of computing SSA.
255285
256 if (mine != frame.getValue(slot))
286 if (mine != frame.getValue(slot)) {
257287 throw new IllegalStateException();
258
259 if (mine.equals(other))
288 }
289
290 if (mine.equals(other)) {
260291 return mine;
292 }
261293
262294 ValueNumber mergedValue = frame.getMergedValue(slot);
263295 if (mergedValue == null) {
288320
289321 @Override
290322 public ValueNumberFrame getFactAfterLocation(Location location) {
291 if (TRACE)
323 if (TRACE) {
292324 System.out.println("getting fact after " + location);
325 }
293326 ValueNumberFrame fact = factAfterLocationMap.get(location);
294327 if (fact == null) {
295 if (TRACE)
328 if (TRACE) {
296329 System.out
297 .println("Initialized fact after " + location + " @ "
298 + Integer.toHexString(System.identityHashCode(location)) + " in "
299 + Integer.toHexString(System.identityHashCode(this)) + " : "
300 + factAfterLocationMap.containsKey(location));
330 .println("Initialized fact after " + location + " @ "
331 + Integer.toHexString(System.identityHashCode(location)) + " in "
332 + Integer.toHexString(System.identityHashCode(this)) + " : "
333 + factAfterLocationMap.containsKey(location));
334 }
301335
302336 fact = createFact();
303337 makeFactTop(fact);
316350 }
317351
318352 // These fields are used by the compactValueNumbers() method.
353 /*
319354 private static class ValueCompacter {
320355 public final BitSet valuesUsed;
321356
332367 // specify value numbers which are not actually used (and thus can
333368 // be purged.)
334369 discovered = new int[origNumValuesAllocated];
335 for (int i = 0; i < discovered.length; ++i)
370 for (int i = 0; i < discovered.length; ++i) {
336371 discovered[i] = -1;
372 }
337373 }
338374
339375 public boolean isUsed(int number) {
348384 return numValuesUsed++;
349385 }
350386 }
387 */
351388
352389 /**
353390 * Compact the value numbers assigned. This should be done only after the
366403 */
367404 @Deprecated
368405 public void compactValueNumbers(Dataflow<ValueNumberFrame, ValueNumberAnalysis> dataflow) {
369 if (true)
406 if (true) {
370407 throw new UnsupportedOperationException();
408 }
409 /*
371410 ValueCompacter compacter = new ValueCompacter(factory.getNumValuesAllocated());
372411
373412 // We can get all extant Frames by looking at the values in
388427
389428 int after = factory.getNumValuesAllocated();
390429
391 if (DEBUG && after < before && before > 0)
430 if (DEBUG && after < before && before > 0) {
392431 System.out.println("Value compaction: " + after + "/" + before + " (" + ((after * 100) / before) + "%)");
393
432 }
433 */
394434 }
395435
396436 /**
397437 * Mark value numbers in a value number frame for compaction.
398 */
438 *
399439 private static void markFrameValues(ValueNumberFrame frame, ValueCompacter compacter) {
400440 // We don't need to do anything for top and bottom frames.
401 if (!frame.isValid())
441 if (!frame.isValid()) {
402442 return;
443 }
403444
404445 for (int j = 0; j < frame.getNumSlots(); ++j) {
405446 ValueNumber value = frame.getValue(j);
411452 }
412453 }
413454 }
455 */
414456
415457 // /**
416458 // * Test driver.
450492 return factory.getClassName(v);
451493 }
452494 }
453
454 // vim:ts=4
4646 public static final boolean RLE_DEBUG = SystemProperties.getBoolean("vna.rle.debug");
4747 }
4848
49 // vim:ts=4
3030 * A cache mapping instructions and input values to the output values they
3131 * produce. We must always produce the same output given identical input, or
3232 * else value number analysis will not terminate.
33 *
33 *
3434 * @author David Hovemeyer
3535 * @see ValueNumberAnalysis
3636 */
5757
5858 @Override
5959 public boolean equals(Object o) {
60 if (!(o instanceof Entry))
60 if (!(o instanceof Entry)) {
6161 return false;
62 }
6263 Entry other = (Entry) o;
63 if (handle.getPosition() != other.handle.getPosition())
64 if (handle.getPosition() != other.handle.getPosition()) {
6465 return false;
66 }
6567 ValueNumber[] myList = inputValueList;
6668 ValueNumber[] otherList = other.inputValueList;
67 if (myList.length != otherList.length)
69 if (myList.length != otherList.length) {
6870 return false;
69 for (int i = 0; i < myList.length; ++i)
70 if (!myList[i].equals(otherList[i]))
71 }
72 for (int i = 0; i < myList.length; ++i) {
73 if (!myList[i].equals(otherList[i])) {
7174 return false;
75 }
76 }
7277 return true;
7378 }
7479
101106 /**
102107 * Map of entries to output values.
103108 */
104 private HashMap<Entry, ValueNumber[]> entryToOutputMap = new HashMap<Entry, ValueNumber[]>();
109 private final HashMap<Entry, ValueNumber[]> entryToOutputMap = new HashMap<Entry, ValueNumber[]>();
105110
106111 /**
107112 * Look up cached output values for given entry.
108 *
113 *
109114 * @param entry
110115 * the entry
111116 * @return the list of output values, or null if there is no matching entry
112117 * in the cache
113118 */
114119 public ValueNumber[] lookupOutputValues(Entry entry) {
115 if (DEBUG)
120 if (DEBUG) {
116121 System.out.println("VN cache lookup: " + entry);
122 }
117123 ValueNumber[] result = entryToOutputMap.get(entry);
118 if (DEBUG)
124 if (DEBUG) {
119125 System.out.println(" result ==> " + Arrays.toString(result));
126 }
120127 return result;
121128 }
122129
123130 /**
124131 * Add output values for given entry. Assumes that lookupOutputValues() has
125132 * determined that the entry is not in the cache.
126 *
133 *
127134 * @param entry
128135 * the entry
129136 * @param outputValueList
132139 */
133140 public void addOutputValues(Entry entry, ValueNumber[] outputValueList) {
134141 ValueNumber[] old = entryToOutputMap.put(entry, outputValueList);
135 if (old != null)
142 if (old != null) {
136143 throw new IllegalStateException("overwriting output values for entry!");
144 }
137145 }
138146
139147 }
140148
141 // vim:ts=4
3838 /**
3939 * Build map of value numbers to param indices. The first parameter has
4040 * index 0, the second has index 1, etc.
41 *
41 *
4242 * @param method
4343 * the method analyzed by the ValueNumberAnalysis
4444 * @return the value number to parameter index map
5050 /**
5151 * Build map of value numbers to param indices. The first parameter has
5252 * index 0, the second has index 1, etc.
53 *
53 *
5454 * @param methodSignature
5555 * signature of the method analyzed by the ValueNumberAnalysis
5656 * @param isStatic
7979 }
8080 }
8181
82 // vim:ts=4
3030 /**
3131 * Factory for ValueNumbers. A single Factory must be used to create all of the
3232 * ValueNumbers for a method.
33 *
33 *
3434 * @author David Hovemeyer
3535 * @see ValueNumber
3636 */
4040 */
4141 private ArrayList<ValueNumber> allocatedValueList = new ArrayList<ValueNumber>();
4242
43 private HashMap<String, ValueNumber> classObjectValueMap = new HashMap<String, ValueNumber>();
43 private final HashMap<String, ValueNumber> classObjectValueMap = new HashMap<String, ValueNumber>();
4444
4545 /**
4646 * Create a fresh (unique) value number.
6161 * Return a previously allocated value.
6262 */
6363 public ValueNumber forNumber(int number) {
64 if (number >= getNumValuesAllocated())
64 if (number >= getNumValuesAllocated()) {
6565 throw new IllegalArgumentException("Value " + number + " has not been allocated");
66 }
6667 return allocatedValueList.get(number);
6768 }
6869
7576
7677 /**
7778 * Compact the value numbers produced by this factory.
78 *
79 *
7980 * @param map
8081 * array mapping old numbers to new numbers
8182 * @param numValuesAllocated
8384 */
8485 @Deprecated
8586 public void compact(int[] map, int numValuesAllocated) {
86 if (true)
87 if (true) {
8788 throw new UnsupportedOperationException();
89 }
8890 ArrayList<ValueNumber> oldList = this.allocatedValueList;
8991 ArrayList<ValueNumber> newList = new ArrayList<ValueNumber>(Collections.<ValueNumber> nCopies(numValuesAllocated, null));
9092
103105
104106 /**
105107 * Get the ValueNumber for given class's Class object.
106 *
108 *
107109 * @param className
108110 * the class
109111 */
122124 public @CheckForNull
123125 @DottedClassName
124126 String getClassName(ValueNumber v) {
125 if (!v.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT))
127 if (!v.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) {
126128 throw new IllegalArgumentException("Not a value number for a constant class");
129 }
127130 for (Map.Entry<String, ValueNumber> e : classObjectValueMap.entrySet()) {
128 if (e.getValue().equals(v))
131 if (e.getValue().equals(v)) {
129132 return e.getKey();
133 }
130134 }
131135 return null;
132136 }
133137
134138 }
135139
136 // vim:ts=4
2626 import java.util.HashSet;
2727 import java.util.Iterator;
2828 import java.util.Map;
29 import java.util.Map.Entry;
30 import java.util.Objects;
2931 import java.util.Set;
3032
3133 import javax.annotation.CheckForNull;
4042 /**
4143 * A dataflow value representing a Java stack frame with value number
4244 * information.
43 *
45 *
4446 * @author David Hovemeyer
4547 * @see ValueNumber
4648 * @see ValueNumberAnalysis
5658 private Map<ValueNumber, AvailableLoad> previouslyKnownAs;
5759
5860 public boolean phiNodeForLoads;
61
62 private static final boolean USE_WRITTEN_OUTSIDE_OF_CONSTRUCTOR = true;
63
64 static int constructedUnmodifiableMap;
65
66 static int reusedMap;
67
68 static int createdEmptyMap;
69
70 static int madeImmutableMutable;
71
72 static int reusedMutableMap;
73
74 static {
75 Util.runLogAtShutdown(new Runnable() {
76
77 @Override
78 public void run() {
79 System.err.println("Getting updatable previously known as:");
80 System.err.println(" " + createdEmptyMap + " created empty map");
81 System.err.println(" " + madeImmutableMutable + " made immutable map mutable");
82 System.err.println(" " + reusedMutableMap + " reused mutable map");
83 System.err.println("Copying map:");
84 System.err.println(" " + constructedUnmodifiableMap + " made mutable map unmodifiable");
85 System.err.println(" " + reusedMap + " reused immutable map");
86 System.err.println();
87
88 }
89 });
90 }
5991
6092 public ValueNumberFrame(int numLocals) {
6193 super(numLocals);
71103 for (Map.Entry<AvailableLoad, ValueNumber[]> e : getAvailableLoadMap().entrySet()) {
72104 buf.append(e.getKey());
73105 buf.append("=");
74 for (ValueNumber v : e.getValue())
106 for (ValueNumber v : e.getValue()) {
75107 buf.append(v).append(",");
108 }
76109 buf.append("; ");
77110 }
78111
82115
83116 public @CheckForNull
84117 AvailableLoad getLoad(ValueNumber v) {
85 if (!REDUNDANT_LOAD_ELIMINATION)
118 if (!REDUNDANT_LOAD_ELIMINATION) {
86119 return null;
120 }
87121 for (Map.Entry<AvailableLoad, ValueNumber[]> e : getAvailableLoadMap().entrySet()) {
88122 ValueNumber[] values = e.getValue();
89 if (values != null)
90 for (ValueNumber v2 : values)
91 if (v.equals(v2))
123 if (values != null) {
124 for (ValueNumber v2 : values) {
125 if (v.equals(v2)) {
92126 return e.getKey();
127 }
128 }
129 }
93130 }
94131 return null;
95132 }
96133
97134 /**
98135 * Look for an available load.
99 *
136 *
100137 * @param availableLoad
101138 * the AvailableLoad (reference and field)
102139 * @return the value(s) available, or null if no matching entry is found
107144
108145 /**
109146 * Add an available load.
110 *
147 *
111148 * @param availableLoad
112149 * the AvailableLoad (reference and field)
113150 * @param value
114151 * the value(s) loaded
115152 */
116153 public void addAvailableLoad(AvailableLoad availableLoad, @Nonnull ValueNumber[] value) {
117 if (value == null)
118 throw new IllegalStateException();
154 Objects.requireNonNull(value);
119155 getUpdateableAvailableLoadMap().put(availableLoad, value);
120156
121157 for (ValueNumber v : value) {
128164 }
129165
130166 private static <K, V> void removeAllKeys(Map<K, V> map, Iterable<K> removeMe) {
131 for (K k : removeMe)
167 for (K k : removeMe) {
132168 map.remove(k);
169 }
133170 }
134171
135172 /**
136173 * Kill all loads of given field.
137 *
174 *
138175 * @param field
139176 * the field
140177 */
141178 public void killLoadsOfField(XField field) {
142 if (!REDUNDANT_LOAD_ELIMINATION)
179 if (!REDUNDANT_LOAD_ELIMINATION) {
143180 return;
181 }
144182 HashSet<AvailableLoad> killMe = new HashSet<AvailableLoad>();
145183 for (AvailableLoad availableLoad : getAvailableLoadMap().keySet()) {
146184 if (availableLoad.getField().equals(field)) {
147 if (RLE_DEBUG)
185 if (RLE_DEBUG) {
148186 System.out.println("KILLING Load of " + availableLoad + " in " + this);
187 }
149188 killMe.add(availableLoad);
150189 }
151190 }
152191 killAvailableLoads(killMe);
153192 }
154
155 private static boolean USE_WRITTEN_OUTSIDE_OF_CONSTRUCTOR = true;
156193
157194 /**
158195 * Kill all loads. This conservatively handles method calls where we don't
159196 * really know what fields might be assigned.
160197 */
161198 public void killAllLoads() {
162 if (!REDUNDANT_LOAD_ELIMINATION)
199 killAllLoads(false);
200 }
201
202 public void killAllLoads(boolean primitiveOnly) {
203 if (!REDUNDANT_LOAD_ELIMINATION) {
163204 return;
205 }
164206 FieldSummary fieldSummary = AnalysisContext.currentAnalysisContext().getFieldSummary();
165207 HashSet<AvailableLoad> killMe = new HashSet<AvailableLoad>();
166208 for (AvailableLoad availableLoad : getAvailableLoadMap().keySet()) {
167209 XField field = availableLoad.getField();
168 if (field.isVolatile() || !field.isFinal()
169 && (!USE_WRITTEN_OUTSIDE_OF_CONSTRUCTOR || fieldSummary.isWrittenOutsideOfConstructor(field))) {
170 if (RLE_DEBUG)
210 if ((!primitiveOnly || !field.isReferenceType()) && (field.isVolatile() || !field.isFinal()
211 && (!USE_WRITTEN_OUTSIDE_OF_CONSTRUCTOR || fieldSummary.isWrittenOutsideOfConstructor(field)))) {
212 if (RLE_DEBUG) {
171213 System.out.println("KILLING load of " + availableLoad + " in " + this);
214 }
172215 killMe.add(availableLoad);
173216 }
174217 }
177220 }
178221
179222 public void killAllLoadsExceptFor(@CheckForNull ValueNumber v) {
180 if (!REDUNDANT_LOAD_ELIMINATION)
223 if (!REDUNDANT_LOAD_ELIMINATION) {
181224 return;
225 }
182226 AvailableLoad myLoad = getLoad(v);
183227 HashSet<AvailableLoad> killMe = new HashSet<AvailableLoad>();
184228 for (AvailableLoad availableLoad : getAvailableLoadMap().keySet()) {
185229 if (!availableLoad.getField().isFinal() && !availableLoad.equals(myLoad)) {
186 if (RLE_DEBUG)
230 if (RLE_DEBUG) {
187231 System.out.println("KILLING load of " + availableLoad + " in " + this);
232 }
188233 killMe.add(availableLoad);
189234 }
190235 }
196241 * really know what fields might be assigned.
197242 */
198243 public void killAllLoadsOf(@CheckForNull ValueNumber v) {
199 if (!REDUNDANT_LOAD_ELIMINATION)
244 if (!REDUNDANT_LOAD_ELIMINATION) {
200245 return;
246 }
201247 FieldSummary fieldSummary = AnalysisContext.currentAnalysisContext().getFieldSummary();
202248
203249 HashSet<AvailableLoad> killMe = new HashSet<AvailableLoad>();
204250 for (AvailableLoad availableLoad : getAvailableLoadMap().keySet()) {
205 if (availableLoad.getReference() != v)
251 if (availableLoad.getReference() != v) {
206252 continue;
253 }
207254 XField field = availableLoad.getField();
208255 if (!field.isFinal() && (!USE_WRITTEN_OUTSIDE_OF_CONSTRUCTOR || fieldSummary.isWrittenOutsideOfConstructor(field))) {
209 if (RLE_DEBUG)
256 if (RLE_DEBUG) {
210257 System.out.println("Killing load of " + availableLoad + " in " + this);
258 }
211259 killMe.add(availableLoad);
212260 }
213261 }
214262 killAvailableLoads(killMe);
215
216263 }
217264
218265 public void killLoadsOf(Set<XField> fieldsToKill) {
219 if (!REDUNDANT_LOAD_ELIMINATION)
266 if (!REDUNDANT_LOAD_ELIMINATION) {
220267 return;
268 }
221269 HashSet<AvailableLoad> killMe = new HashSet<AvailableLoad>();
222270 for (AvailableLoad availableLoad : getAvailableLoadMap().keySet()) {
223271
224 if (fieldsToKill.contains(availableLoad.getField()))
272 if (fieldsToKill.contains(availableLoad.getField())) {
225273 killMe.add(availableLoad);
274 }
226275
227276 }
228277 killAvailableLoads(killMe);
229
230278 }
231279
232280 public void killLoadsWithSimilarName(String className, String methodName) {
233 if (!REDUNDANT_LOAD_ELIMINATION)
281 if (!REDUNDANT_LOAD_ELIMINATION) {
234282 return;
283 }
235284 String packageName = extractPackageName(className);
236285
237286 HashSet<AvailableLoad> killMe = new HashSet<AvailableLoad>();
240289 XField field = availableLoad.getField();
241290 String fieldPackageName = extractPackageName(field.getClassName());
242291 if (packageName.equals(fieldPackageName) && field.isStatic()
243 && methodName.toLowerCase().indexOf(field.getName().toLowerCase()) >= 0)
292 && methodName.toLowerCase().indexOf(field.getName().toLowerCase()) >= 0) {
244293 killMe.add(availableLoad);
294 }
245295
246296 }
247297 killAvailableLoads(killMe);
248298 }
249299
250 /**
251 * @param killMe
252 */
253300 private void killAvailableLoads(HashSet<AvailableLoad> killMe) {
254 if (killMe.size() > 0)
301 if (killMe.size() > 0) {
255302 removeAllKeys(getUpdateableAvailableLoadMap(), killMe);
256 }
257
258 /**
259 * @param className
260 * @return
261 */
303 }
304 }
305
262306 private String extractPackageName(String className) {
263307 return className.substring(className.lastIndexOf('.') + 1);
264308 }
281325 AvailableLoad load = e.getKey();
282326 ValueNumber[] myVN = e.getValue();
283327 ValueNumber[] otherVN = other.getAvailableLoadMap().get(load);
328 /*
284329 if (false && this.phiNodeForLoads && myVN != null && myVN.length == 1
285 && myVN[0].hasFlag(ValueNumber.PHI_NODE))
330 && myVN[0].hasFlag(ValueNumber.PHI_NODE)) {
286331 continue;
332 }
333 */
287334 if (!Arrays.equals(myVN, otherVN)) {
288335
289336 ValueNumber phi = getMergedLoads().get(load);
292339 for (ValueNumber vn : myVN) {
293340 flags = ValueNumber.mergeFlags(flags, vn.getFlags());
294341 }
295 if (otherVN != null)
342 if (otherVN != null) {
296343 for (ValueNumber vn : otherVN) {
297344 flags = ValueNumber.mergeFlags(flags, vn.getFlags());
298345 }
299 if (flags == -1)
346 }
347 if (flags == -1) {
300348 flags = ValueNumber.PHI_NODE;
301 else
349 } else {
302350 flags |= ValueNumber.PHI_NODE;
303
351 }
352
304353 phi = factory.createFreshValue(flags);
305354
306355 getUpdateableMergedLoads().put(load, phi);
307356 for (ValueNumber vn : myVN) {
308357 mergeTree.mapInputToOutput(vn, phi);
309358 }
310 if (otherVN != null)
359 if (otherVN != null) {
311360 for (ValueNumber vn : otherVN) {
312361 mergeTree.mapInputToOutput(vn, phi);
313362 }
314
315 if (RLE_DEBUG)
363 }
364
365 if (RLE_DEBUG) {
316366 System.out.println("Creating phi node " + phi + " for " + load + " from " + Arrays.toString(myVN)
317367 + " x " + Arrays.toString(otherVN) + " in " + System.identityHashCode(this));
368 }
318369 changed = true;
319370 e.setValue(new ValueNumber[] { phi });
320371 } else {
321 if (RLE_DEBUG)
372 if (RLE_DEBUG) {
322373 System.out.println("Reusing phi node : " + phi + " for " + load + " from "
323374 + Arrays.toString(myVN) + " x " + Arrays.toString(otherVN) + " in "
324375 + System.identityHashCode(this));
325 if (myVN.length != 1 || !myVN[0].equals(phi))
376 }
377 if (myVN.length != 1 || !myVN[0].equals(phi)) {
326378 e.setValue(new ValueNumber[] { phi });
379 }
327380 }
328381
329382 }
332385 }
333386 Map<ValueNumber, AvailableLoad> previouslyKnownAsOther = other.getPreviouslyKnownAs();
334387 if (getPreviouslyKnownAs() != previouslyKnownAsOther && previouslyKnownAsOther.size() != 0) {
335 if (getPreviouslyKnownAs().size() == 0)
388 if (getPreviouslyKnownAs().size() == 0) {
336389 assignPreviouslyKnownAs(other);
337 else
390 } else {
338391 getUpdateablePreviouslyKnownAs().putAll(previouslyKnownAsOther);
339 }
340 if (changed)
392 }
393 }
394 if (changed) {
341395 this.phiNodeForLoads = true;
396 }
342397 if (changed && RLE_DEBUG) {
343398 System.out.println(s);
344399 System.out.println(" Result is " + this.availableLoadMapAsString());
357412
358413 @Override
359414 public void copyFrom(Frame<ValueNumber> other) {
360 if (!(other instanceof ValueNumberFrame))
415 if (!(other instanceof ValueNumberFrame)) {
361416 throw new IllegalArgumentException();
417 }
362418 // If merged value list hasn't been created yet, create it.
363419 if (mergedValueList == null && other.isValid()) {
364420 // This is where this frame gets its size.
365421 // It will have the same size as long as it remains valid.
366422 mergedValueList = new ArrayList<ValueNumber>(other.getNumSlots());
367423 int numSlots = other.getNumSlots();
368 for (int i = 0; i < numSlots; ++i)
424 for (int i = 0; i < numSlots; ++i) {
369425 mergedValueList.add(null);
426 }
370427 }
371428
372429 if (REDUNDANT_LOAD_ELIMINATION) {
403460 }
404461 }
405462
406 static int constructedUnmodifiableMap;
407
408 static int reusedMap;
409
410463 @Override
411464 public String toString() {
412465 String frameValues = super.toString();
419472 while (i.hasNext()) {
420473 AvailableLoad key = i.next();
421474 ValueNumber[] value = getAvailableLoadMap().get(key);
422 if (first)
475 if (first) {
423476 first = false;
424 else
477 } else {
425478 buf.append(',');
479 }
426480 buf.append(key + "=" + valueToString(value));
427481 }
428482
429483 buf.append(" #");
430484 buf.append(System.identityHashCode(this));
431 if (phiNodeForLoads)
485 if (phiNodeForLoads) {
432486 buf.append(" phi");
487 }
433488 return buf.toString();
434489 } else {
435490 return frameValues;
441496 buf.append('[');
442497 boolean first = true;
443498 for (ValueNumber aValueNumberList : valueNumberList) {
444 if (first)
499 if (first) {
445500 first = false;
446 else
501 } else {
447502 buf.append(',');
503 }
448504 buf.append(aValueNumberList.getNumber());
449505 }
450506 buf.append(']');
452508 }
453509
454510 public boolean fuzzyMatch(ValueNumber v1, ValueNumber v2) {
455 if (REDUNDANT_LOAD_ELIMINATION)
511 if (REDUNDANT_LOAD_ELIMINATION) {
456512 return v1.equals(v2) || fromMatchingLoads(v1, v2) || haveMatchingFlags(v1, v2);
457 else
513 } else {
458514 return v1.equals(v2);
515 }
459516 }
460517
461518 public boolean veryFuzzyMatch(ValueNumber v1, ValueNumber v2) {
462 if (REDUNDANT_LOAD_ELIMINATION)
519 if (REDUNDANT_LOAD_ELIMINATION) {
463520 return v1.equals(v2) || fromMatchingFields(v1, v2) || haveMatchingFlags(v1, v2);
464 else
521 } else {
465522 return v1.equals(v2);
523 }
466524 }
467525
468526 public boolean fromMatchingLoads(ValueNumber v1, ValueNumber v2) {
469527 AvailableLoad load1 = getLoad(v1);
470 if (load1 == null)
528 if (load1 == null) {
471529 load1 = getPreviouslyKnownAs().get(v1);
472 if (load1 == null)
530 }
531 if (load1 == null) {
473532 return false;
533 }
474534 AvailableLoad load2 = getLoad(v2);
475 if (load2 == null)
535 if (load2 == null) {
476536 load2 = getPreviouslyKnownAs().get(v2);
477 if (load2 == null)
537 }
538 if (load2 == null) {
478539 return false;
540 }
479541 return load1.equals(load2);
480542 }
481543
482544 public boolean fromMatchingFields(ValueNumber v1, ValueNumber v2) {
483545 AvailableLoad load1 = getLoad(v1);
484 if (load1 == null)
546 if (load1 == null) {
485547 load1 = getPreviouslyKnownAs().get(v1);
486 if (load1 == null)
548 }
549 if (load1 == null) {
487550 return false;
551 }
488552 AvailableLoad load2 = getLoad(v2);
489 if (load2 == null)
553 if (load2 == null) {
490554 load2 = getPreviouslyKnownAs().get(v2);
491 if (load2 == null)
555 }
556 if (load2 == null) {
492557 return false;
493 if (load1.equals(load2))
558 }
559 if (load1.equals(load2)) {
494560 return true;
561 }
495562 if (load1.getField().equals(load2.getField())) {
496563 ValueNumber source1 = load1.getReference();
497564 ValueNumber source2 = load2.getReference();
498 if (!this.contains(source1))
565 if (!this.contains(source1)) {
499566 return true;
500 if (!this.contains(source2))
567 }
568 if (!this.contains(source2)) {
501569 return true;
570 }
502571 }
503572 return false;
504573 }
505574
506575 /**
507 * @param v1
508 * @param v2
509576 * @return true if v1 and v2 have a flag in common
510577 */
511578 public boolean haveMatchingFlags(ValueNumber v1, ValueNumber v2) {
516583
517584 public Collection<ValueNumber> valueNumbersForLoads() {
518585 HashSet<ValueNumber> result = new HashSet<ValueNumber>();
519 if (REDUNDANT_LOAD_ELIMINATION)
586 if (REDUNDANT_LOAD_ELIMINATION) {
520587 for (Map.Entry<AvailableLoad, ValueNumber[]> e : getAvailableLoadMap().entrySet()) {
521 if (e.getValue() != null)
522 for (ValueNumber v2 : e.getValue())
588 if (e.getValue() != null) {
589 for (ValueNumber v2 : e.getValue()) {
523590 result.add(v2);
524 }
591 }
592 }
593 }
594 }
525595
526596 return result;
527597 }
528598
529 /**
530 * @param availableLoadMap
531 * The availableLoadMap to set.
532 */
533599 private void setAvailableLoadMap(Map<AvailableLoad, ValueNumber[]> availableLoadMap) {
534600 this.availableLoadMap = availableLoadMap;
535601 }
536602
537 /**
538 * @return Returns the availableLoadMap.
539 */
540603 private Map<AvailableLoad, ValueNumber[]> getAvailableLoadMap() {
541604 return availableLoadMap;
542605 }
550613 return availableLoadMap;
551614 }
552615
553 /**
554 * @param mergedLoads
555 * The mergedLoads to set.
556 */
557616 private void setMergedLoads(Map<AvailableLoad, ValueNumber> mergedLoads) {
558617 this.mergedLoads = mergedLoads;
559618 }
560619
561 /**
562 * @return Returns the mergedLoads.
563 */
564620 private Map<AvailableLoad, ValueNumber> getMergedLoads() {
565621 return mergedLoads;
566622 }
567623
568624 private Map<AvailableLoad, ValueNumber> getUpdateableMergedLoads() {
569 if (!(mergedLoads instanceof HashMap))
625 if (!(mergedLoads instanceof HashMap)) {
570626 mergedLoads = new HashMap<AvailableLoad, ValueNumber>();
627 }
571628
572629 return mergedLoads;
573630 }
574631
575 /**
576 * @param previouslyKnownAs
577 * The previouslyKnownAs to set.
578 */
579632 private void setPreviouslyKnownAs(Map<ValueNumber, AvailableLoad> previouslyKnownAs) {
580633 this.previouslyKnownAs = previouslyKnownAs;
581634 }
582635
583 /**
584 * @return Returns the previouslyKnownAs.
585 */
586636 private Map<ValueNumber, AvailableLoad> getPreviouslyKnownAs() {
587637 return previouslyKnownAs;
588 }
589
590 static int createdEmptyMap;
591
592 static int madeImmutableMutable;
593
594 static int reusedMutableMap;
595 static {
596 Util.runLogAtShutdown(new Runnable() {
597
598 public void run() {
599 System.err.println("Getting updatable previously known as:");
600 System.err.println(" " + createdEmptyMap + " created empty map");
601 System.err.println(" " + madeImmutableMutable + " made immutable map mutable");
602 System.err.println(" " + reusedMutableMap + " reused mutable map");
603 System.err.println("Copying map:");
604 System.err.println(" " + constructedUnmodifiableMap + " made mutable map unmodifiable");
605 System.err.println(" " + reusedMap + " reused immutable map");
606 System.err.println();
607
608 }
609 });
610638 }
611639
612640 private Map<ValueNumber, AvailableLoad> getUpdateablePreviouslyKnownAs() {
618646 tmp.putAll(previouslyKnownAs);
619647 previouslyKnownAs = tmp;
620648 madeImmutableMutable++;
621 } else
649 } else {
622650 reusedMutableMap++;
651 }
623652
624653 return previouslyKnownAs;
625654 }
626655
656 @Override
657 public boolean sameAs(Frame<ValueNumber> other) {
658 if(!super.sameAs(other)) {
659 return false;
660 }
661 if (isTop() && other.isTop() || isBottom() && other.isBottom()) {
662 return true;
663 }
664 ValueNumberFrame o = (ValueNumberFrame)other;
665 if(availableLoadMap.size() != o.availableLoadMap.size()) {
666 return false;
667 }
668 for(Entry<AvailableLoad, ValueNumber[]> entry : availableLoadMap.entrySet()) {
669 ValueNumber[] oValue = o.availableLoadMap.get(entry.getKey());
670 if(!Arrays.equals(entry.getValue(), oValue)) {
671 return false;
672 }
673 }
674 return true;
675 }
676
677 public boolean hasAvailableLoads() {
678 return !getAvailableLoadMap().isEmpty();
679 }
627680 }
628
629 // vim:ts=4
3131 import org.apache.bcel.generic.GETFIELD;
3232 import org.apache.bcel.generic.GETSTATIC;
3333 import org.apache.bcel.generic.IINC;
34 import org.apache.bcel.generic.INVOKEDYNAMIC;
3435 import org.apache.bcel.generic.INVOKEINTERFACE;
35 import org.apache.bcel.generic.INVOKESPECIAL;
3636 import org.apache.bcel.generic.INVOKESTATIC;
3737 import org.apache.bcel.generic.INVOKEVIRTUAL;
3838 import org.apache.bcel.generic.Instruction;
4141 import org.apache.bcel.generic.LDC;
4242 import org.apache.bcel.generic.MONITORENTER;
4343 import org.apache.bcel.generic.MethodGen;
44 import org.apache.bcel.generic.ObjectType;
4445 import org.apache.bcel.generic.PUTFIELD;
4546 import org.apache.bcel.generic.PUTSTATIC;
4647
5556 import edu.umd.cs.findbugs.ba.RepositoryLookupFailureCallback;
5657 import edu.umd.cs.findbugs.ba.XField;
5758 import edu.umd.cs.findbugs.ba.XMethod;
59 import edu.umd.cs.findbugs.classfile.Global;
60 import edu.umd.cs.findbugs.detect.FindNoSideEffectMethods.MethodSideEffectStatus;
61 import edu.umd.cs.findbugs.detect.FindNoSideEffectMethods.NoSideEffectMethodsDatabase;
5862
5963 /**
6064 * Visitor which models the effects of bytecode instructions on value numbers of
6165 * values in the operand stack frames.
62 *
66 *
6367 * @see ValueNumber
6468 * @see ValueNumberFrame
6569 * @see ValueNumberAnalysis
6670 * @author David Hovemeyer
6771 */
6872 public class ValueNumberFrameModelingVisitor extends AbstractFrameModelingVisitor<ValueNumber, ValueNumberFrame> implements
69 Debug, ValueNumberAnalysisFeatures {
73 Debug, ValueNumberAnalysisFeatures {
7074
7175 /*
7276 * ----------------------------------------------------------------------
7478 * ----------------------------------------------------------------------
7579 */
7680
77 private MethodGen methodGen;
81 private final MethodGen methodGen;
7882
7983 ValueNumberFactory factory;
8084
81 private ValueNumberCache cache;
82
83 private LoadedFieldSet loadedFieldSet;
84
85 private HashMap<Object, ValueNumber> constantValueMap;
86
87 private HashMap<ValueNumber, String> stringConstantMap;
85 private final ValueNumberCache cache;
86
87 private final LoadedFieldSet loadedFieldSet;
88
89 private final HashMap<Object, ValueNumber> constantValueMap;
90
91 private final HashMap<ValueNumber, String> stringConstantMap;
8892
8993 private InstructionHandle handle;
94
95 private static final ValueNumber[] EMPTY_INPUT_VALUE_LIST = new ValueNumber[0];
9096
9197 /*
9298 * ----------------------------------------------------------------------
96102
97103 /**
98104 * Constructor.
99 *
105 *
100106 * @param methodGen
101107 * the method being analyzed
102108 * @param factory
142148 /**
143149 * Determine whether redundant load elimination should be performed for the
144150 * heap location referenced by the current instruction.
145 *
151 *
146152 * @return true if we should do redundant load elimination for the current
147153 * instruction, false if not
148154 */
149155 private boolean doRedundantLoadElimination() {
150 if (!REDUNDANT_LOAD_ELIMINATION)
156 if (!REDUNDANT_LOAD_ELIMINATION) {
151157 return false;
158 }
152159
153160 XField xfield = loadedFieldSet.getField(handle);
154 if (xfield == null)
161 if (xfield == null) {
155162 return false;
156
157 if (!xfield.isReferenceType())
163 }
164
165 if(xfield.getSignature().equals("D") || xfield.getSignature().equals("J")) {
166 // TODO: support two-slot fields
158167 return false;
168 }
159169
160170 // Don't do redundant load elimination for fields that
161171 // are loaded in only one place.
162 if (false && loadedFieldSet.getLoadStoreCount(xfield).getLoadCount() <= 1)
172 /*
173 if (false && loadedFieldSet.getLoadStoreCount(xfield).getLoadCount() <= 1){
163174 return false;
164
175 }
176 */
165177 return true;
166178 }
167179
168180 /**
169181 * Determine whether forward substitution should be performed for the heap
170182 * location referenced by the current instruction.
171 *
183 *
172184 * @return true if we should do forward substitution for the current
173185 * instruction, false if not
174186 */
175187 private boolean doForwardSubstitution() {
176 if (!REDUNDANT_LOAD_ELIMINATION)
188 if (!REDUNDANT_LOAD_ELIMINATION) {
177189 return false;
190 }
178191
179192 XField xfield = loadedFieldSet.getField(handle);
180 if (xfield == null)
193 if (xfield == null) {
181194 return false;
182
183 if (!xfield.isReferenceType())
195 }
196
197 if(xfield.getSignature().equals("D") || xfield.getSignature().equals("J")) {
184198 return false;
199 }
185200
186201 // Don't do forward substitution for fields that
187202 // are never read.
188 if (!loadedFieldSet.isLoaded(xfield))
203 if (!loadedFieldSet.isLoaded(xfield)) {
189204 return false;
205 }
190206
191207 return true;
192208 }
195211 int numConsumed = ins.consumeStack(getCPG());
196212 int numProduced = ins.produceStack(getCPG());
197213
198 if (numConsumed == Constants.UNPREDICTABLE)
214 if (numConsumed == Constants.UNPREDICTABLE) {
199215 throw new InvalidBytecodeException("Unpredictable stack consumption for " + ins);
200 if (numProduced == Constants.UNPREDICTABLE)
216 }
217 if (numProduced == Constants.UNPREDICTABLE) {
201218 throw new InvalidBytecodeException("Unpredictable stack production for " + ins);
219 }
202220
203221 if (consumedValueList.length != numConsumed) {
204222 throw new IllegalStateException("Wrong number of values consumed for " + ins + ": expected " + numConsumed + ", got "
218236 public void modelNormalInstruction(Instruction ins, int numWordsConsumed, int numWordsProduced) {
219237
220238 int flags = 0;
221 if (ins instanceof InvokeInstruction)
239 if (ins instanceof InvokeInstruction) {
222240 flags = ValueNumber.RETURN_VALUE;
223 else if (ins instanceof ArrayInstruction)
241 } else if (ins instanceof ArrayInstruction) {
224242 flags = ValueNumber.ARRAY_VALUE;
225 else if (ins instanceof ConstantPushInstruction)
243 } else if (ins instanceof ConstantPushInstruction) {
226244 flags = ValueNumber.CONSTANT_VALUE;
245 }
227246
228247 // Get the input operands to this instruction.
229248 ValueNumber[] inputValueList = popInputValues(numWordsConsumed);
245264 public void visitGETFIELD(GETFIELD obj) {
246265 XField xfield = Hierarchy.findXField(obj, getCPG());
247266 if (xfield != null) {
248 if (xfield.isVolatile())
267 if (xfield.isVolatile()) {
249268 getFrame().killAllLoads();
269 }
250270
251271 if (doRedundantLoadElimination()) {
252272 loadInstanceField(xfield, obj);
260280 @Override
261281 public void visitPUTFIELD(PUTFIELD obj) {
262282 if (doForwardSubstitution()) {
263 XField xfield = Hierarchy.findXField(obj, getCPG());
264 if (xfield != null) {
265 storeInstanceField(xfield, obj, false);
266 return;
267 }
268
283 XField xfield = Hierarchy.findXField(obj, getCPG());
284 if (xfield != null) {
285 storeInstanceField(xfield, obj, false);
286 return;
287 }
288
269289 }
270290 handleNormalInstruction(obj);
271291 }
272
273 private static final ValueNumber[] EMPTY_INPUT_VALUE_LIST = new ValueNumber[0];
274292
275293 @Override
276294 public void visitGETSTATIC(GETSTATIC obj) {
284302 System.out.println("GETSTATIC of " + fieldName + " : " + fieldSig);
285303 }
286304 // Is this an access of a Class object?
287 if (fieldName.startsWith("class$") && fieldSig.equals("Ljava/lang/Class;")) {
305 if (fieldName.startsWith("class$") && "Ljava/lang/Class;".equals(fieldSig)) {
288306 String className = fieldName.substring("class$".length()).replace('$', '.');
289 if (RLE_DEBUG)
307 if (RLE_DEBUG) {
290308 System.out.println("[found load of class object " + className + "]");
309 }
291310 ValueNumber value = factory.getClassObjectValue(className);
292311 frame.pushValue(value);
293312 return;
295314
296315 XField xfield = Hierarchy.findXField(obj, getCPG());
297316 if (xfield != null) {
298 if (xfield.isVolatile())
317 if (xfield.isVolatile()) {
299318 getFrame().killAllLoads();
319 }
300320 if (doRedundantLoadElimination()) {
301321 loadStaticField(xfield, obj);
302322 return;
329349 String methodName = obj.getName(cpg);
330350 String methodSig = obj.getSignature(cpg);
331351
332 if ((methodName.equals("forName") && targetClassName.equals("java.lang.Class") || methodName.equals("class$"))
333 && methodSig.equals("(Ljava/lang/String;)Ljava/lang/Class;")) {
352 if (("forName".equals(methodName) && "java.lang.Class".equals(targetClassName) || "class$".equals(methodName))
353 && "(Ljava/lang/String;)Ljava/lang/Class;".equals(methodSig)) {
334354 // Access of a Class object
335355 ValueNumberFrame frame = getFrame();
336356 try {
338358 String className = stringConstantMap.get(arg);
339359 if (className != null) {
340360 frame.popValue();
341 if (RLE_DEBUG)
361 if (RLE_DEBUG) {
342362 System.out.println("[found access of class object " + className + "]");
363 }
343364 frame.pushValue(factory.getClassObjectValue(className));
344365 return;
345366 }
353374 if (loadedFieldSet.instructionIsLoad(handle)) {
354375 // Load via inner-class accessor
355376 if (doRedundantLoadElimination()) {
356 if (xfield.isStatic())
377 if (xfield.isStatic()) {
357378 loadStaticField(xfield, obj);
358 else
379 } else {
359380 loadInstanceField(xfield, obj);
381 }
360382 return;
361383 }
362384 } else {
366388 // return the value stored.
367389 boolean pushValue = !methodSig.endsWith(")V");
368390
369 if (xfield.isStatic())
391 if (xfield.isStatic()) {
370392 storeStaticField(xfield, obj, pushValue);
371 else
393 } else {
372394 storeInstanceField(xfield, obj, pushValue);
395 }
373396
374397 return;
375398 }
376399 }
377 } else {
378 // Don't know what this method invocation is doing.
379 // Kill all loads.
380 killLoadsOfObjectsPassed(obj);
381 getFrame().killAllLoadsOf(null);
382400 }
383 } else {
384 // Don't know what this method invocation is doing.
385 // Kill all loads.
386 killLoadsOfObjectsPassed(obj);
387 getFrame().killAllLoadsOf(null);
388 }
389 }
390
401 }
402 }
391403 handleNormalInstruction(obj);
404 }
405
406 private void killLoadsOfObjectsPassed(INVOKEDYNAMIC ins) {
407 try {
408
409 int passed = getNumWordsConsumed(ins);
410 ValueNumber[] arguments = allocateValueNumberArray(passed);
411 getFrame().getTopStackWords(arguments);
412 for (ValueNumber v : arguments) {
413 getFrame().killAllLoadsOf(v);
414 }
415
416 } catch (DataflowAnalysisException e) {
417 AnalysisContext.logError("Error in killLoadsOfObjectsPassed", e);
418 }
392419 }
393420
394421 private void killLoadsOfObjectsPassed(InvokeInstruction ins) {
395422 try {
396423
397424 XMethod called = Hierarchy2.findExactMethod(ins, methodGen.getConstantPool(), Hierarchy.ANY_METHOD);
425 if (called != null ) {
426 NoSideEffectMethodsDatabase nse = Global.getAnalysisCache().getOptionalDatabase(NoSideEffectMethodsDatabase.class);
427 if(nse != null && !nse.is(called.getMethodDescriptor(), MethodSideEffectStatus.SE, MethodSideEffectStatus.OBJ)) {
428 return;
429 }
430 }
398431 FieldSummary fieldSummary = AnalysisContext.currentAnalysisContext().getFieldSummary();
399432 Set<XField> touched = fieldSummary.getFieldsWritten(called);
400433 if (!touched.isEmpty()) {
406439 ValueNumber[] arguments = allocateValueNumberArray(passed);
407440 getFrame().killLoadsWithSimilarName(ins.getClassName(cpg), ins.getMethodName(cpg));
408441 getFrame().getTopStackWords(arguments);
409 for (ValueNumber v : arguments)
442 for (ValueNumber v : arguments) {
410443 getFrame().killAllLoadsOf(v);
444 }
445
446 // Too many false-positives for primitives without transitive FieldSummary analysis,
447 // so currently we simply kill any writable primitive on any method call
448 // TODO: implement transitive FieldSummary
449 getFrame().killAllLoads(true);
411450
412451 } catch (DataflowAnalysisException e) {
413452 AnalysisContext.logError("Error in killLoadsOfObjectsPassed", e);
428467 handleNormalInstruction(obj);
429468 }
430469
431 @Override
432 public void visitINVOKESPECIAL(INVOKESPECIAL obj) {
433 // Don't know what this method invocation is doing.
434 // Kill all loads.
435 killLoadsOfObjectsPassed(obj);
436 handleNormalInstruction(obj);
437 }
438
439 @Override
440 public void visitINVOKEINTERFACE(INVOKEINTERFACE obj) {
441 // Don't know what this method invocation is doing.
442 // Kill all loads.
443 if (obj.getMethodName(cpg).equals("lock"))
470 public void visitInvokeOnException(Instruction obj) {
471 if(!REDUNDANT_LOAD_ELIMINATION || !getFrame().hasAvailableLoads()) {
472 return;
473 }
474 if(obj instanceof INVOKEDYNAMIC) {
475 killLoadsOfObjectsPassed((INVOKEDYNAMIC) obj);
476 return;
477 }
478 InvokeInstruction inv = (InvokeInstruction) obj;
479 if ((inv instanceof INVOKEINTERFACE || inv instanceof INVOKEVIRTUAL)
480 && inv.getMethodName(cpg).toLowerCase().indexOf("lock") >= 0) {
481 // Don't know what this method invocation is doing.
482 // Kill all loads.
444483 getFrame().killAllLoads();
445 else
446 killLoadsOfObjectsPassed(obj);
447 handleNormalInstruction(obj);
484 return;
485 }
486 if(inv instanceof INVOKEVIRTUAL && "cast".equals(inv.getMethodName(cpg)) && "java.lang.Class".equals(inv.getClassName(cpg))) {
487 // No-op
488 return;
489 }
490 if(inv instanceof INVOKESTATIC) {
491 String methodName = inv.getName(cpg);
492 if (("forName".equals(methodName) && "java.lang.Class".equals(inv.getClassName(cpg)) || "class$".equals(methodName))
493 && "(Ljava/lang/String;)Ljava/lang/Class;".equals(inv.getSignature(cpg))
494 || (Hierarchy.isInnerClassAccess((INVOKESTATIC) inv, cpg) && loadedFieldSet.getField(handle) != null)) {
495 return;
496 }
497 }
498
499 killLoadsOfObjectsPassed(inv);
500 if(inv instanceof INVOKESTATIC) {
501 getFrame().killAllLoadsOf(null);
502 }
448503 }
449504
450505 @Override
451506 public void visitINVOKEVIRTUAL(INVOKEVIRTUAL obj) {
452507
453 if (obj.getMethodName(cpg).equals("cast") && obj.getClassName(cpg).equals("java.lang.Class")) {
508 if ("cast".equals(obj.getMethodName(cpg)) && "java.lang.Class".equals(obj.getClassName(cpg))) {
454509 // treat as no-op
455510 try {
456511 ValueNumberFrame frame = getFrame();
462517 }
463518 return;
464519 }
465 // Don't know what this method invocation is doing.
466 // Kill all loads.
467 if (obj.getMethodName(cpg).toLowerCase().indexOf("lock") >= 0)
468 getFrame().killAllLoads();
469 else
470 killLoadsOfObjectsPassed(obj);
471520 handleNormalInstruction(obj);
472521 }
473522
497546 ConstantClass constantClass = (ConstantClass) constantValue;
498547 String className = constantClass.getBytes(cpg.getConstantPool());
499548 value = factory.getClassObjectValue(className);
549 } else if (constantValue instanceof ObjectType) {
550 ObjectType objectType = (ObjectType) constantValue;
551 String className = objectType.getClassName();
552 value = factory.getClassObjectValue(className);
500553 } else {
501554 value = constantValueMap.get(constantValue);
502555 if (value == null) {
575628 */
576629 private void pushOutputValues(ValueNumber[] outputValueList) {
577630 ValueNumberFrame frame = getFrame();
578 for (ValueNumber aOutputValueList : outputValueList)
631 for (ValueNumber aOutputValueList : outputValueList) {
579632 frame.pushValue(aOutputValueList);
633 }
580634 }
581635
582636 /**
595649 ValueNumber freshValue = factory.createFreshValue(flags);
596650 outputValueList[i] = freshValue;
597651 }
652 /*
598653 if (false && RLE_DEBUG) {
599654 System.out.println("<<cache fill for " + handle.getPosition() + ": " + vlts(inputValueList) + " ==> "
600655 + vlts(outputValueList) + ">>");
601656 }
657 */
602658 cache.addOutputValues(entry, outputValueList);
603 } else if (false && RLE_DEBUG) {
659 } /* else if (false && RLE_DEBUG) {
604660 System.out.println("<<cache hit for " + handle.getPosition() + ": " + vlts(inputValueList) + " ==> "
605661 + vlts(outputValueList) + ">>");
606 }
662 } */
607663 return outputValueList;
608664 }
609665
610666 /**
611667 * Creates a new empty array (if needed) with given size.
612 *
668 *
613669 * @param size
614670 * array size
615671 * @return if size is zero, returns {@link #EMPTY_INPUT_VALUE_LIST}
624680 private static String vlts(ValueNumber[] vl) {
625681 StringBuilder buf = new StringBuilder();
626682 for (ValueNumber aVl : vl) {
627 if (buf.length() > 0)
683 if (buf.length() > 0) {
628684 buf.append(',');
685 }
629686 buf.append(aVl.getNumber());
630687 }
631688 return buf.toString();
633690
634691 /**
635692 * Load an instance field.
636 *
693 *
637694 * @param instanceField
638695 * the field
639696 * @param obj
650707 ValueNumber reference = frame.popValue();
651708
652709 AvailableLoad availableLoad = new AvailableLoad(reference, instanceField);
653 if (RLE_DEBUG)
710 if (RLE_DEBUG) {
654711 System.out.println("[getfield of " + availableLoad + "]");
712 }
655713 ValueNumber[] loadedValue = frame.getAvailableLoad(availableLoad);
656714
657715 if (loadedValue == null) {
683741
684742 /**
685743 * Load a static field.
686 *
744 *
687745 * @param staticField
688746 * the field
689747 * @param obj
706764
707765 frame.addAvailableLoad(availableLoad, loadedValue);
708766
709 if (RLE_DEBUG)
767 if (RLE_DEBUG) {
710768 System.out.println("[making load of " + staticField + " available]");
769 }
711770 } else {
712 if (RLE_DEBUG)
771 if (RLE_DEBUG) {
713772 System.out.println("[found available load of " + staticField + "]");
773 }
714774 }
715775
716776 if (VERIFY_INTEGRITY) {
722782
723783 /**
724784 * Store an instance field.
725 *
785 *
726786 * @param instanceField
727787 * the field
728788 * @param obj
748808 ValueNumber[] storedValue = allocateValueNumberArray(inputValueList.length - 1);
749809 System.arraycopy(inputValueList, 1, storedValue, 0, inputValueList.length - 1);
750810
751 if (pushStoredValue)
811 if (pushStoredValue) {
752812 pushOutputValues(storedValue);
813 }
753814
754815 // Kill all previous loads of the same field,
755816 // in case there is aliasing we don't know about
758819 // Forward substitution
759820 frame.addAvailableLoad(new AvailableLoad(reference, instanceField), storedValue);
760821
761 if (RLE_DEBUG)
822 if (RLE_DEBUG) {
762823 System.out.println("[making store of " + instanceField + " available]");
824 }
763825
764826 if (VERIFY_INTEGRITY) {
765827 /*
771833
772834 /**
773835 * Store a static field.
774 *
836 *
775837 * @param staticField
776838 * the static field
777839 * @param obj
792854 int numWordsConsumed = getNumWordsConsumed(obj);
793855 ValueNumber[] inputValueList = popInputValues(numWordsConsumed);
794856
795 if (pushStoredValue)
857 if (pushStoredValue) {
796858 pushOutputValues(inputValueList);
859 }
797860
798861 // Kill loads of this field
799862 frame.killLoadsOfField(staticField);
801864 // Make load available
802865 frame.addAvailableLoad(availableLoad, inputValueList);
803866
804 if (RLE_DEBUG)
867 if (RLE_DEBUG) {
805868 System.out.println("[making store of " + staticField + " available]");
869 }
806870
807871 if (VERIFY_INTEGRITY) {
808872 checkConsumedAndProducedValues(obj, inputValueList, pushStoredValue ? inputValueList : EMPTY_INPUT_VALUE_LIST);
811875
812876 }
813877
814 // vim:ts=4
3838 /**
3939 * Helper methods to find out information about the source of the value
4040 * represented by a given ValueNumber.
41 *
41 *
4242 * @author Bill Pugh
4343 * @author David Hovemeyer
4444 */
6363 }
6464 LocalVariableAnnotation ann = ValueNumberSourceInfo.findLocalAnnotationFromValueNumber(method, location, valueNumber,
6565 vnaFrame);
66 if (ann != null && partialRole != null)
66 if (ann != null && partialRole != null) {
6767 ann.setDescription("LOCAL_VARIABLE_" + partialRole);
68 }
6869
6970 if (ann != null && ann.isSignificant()) {
7071 return ann;
7172 }
7273 FieldAnnotation field = ValueNumberSourceInfo.findFieldAnnotationFromValueNumber(method, location, valueNumber, vnaFrame);
7374 if (field != null) {
74 if (partialRole != null)
75 if (partialRole != null) {
7576 field.setDescription("FIELD_" + partialRole);
77 }
7678
7779 return field;
7880 }
79 if (ann != null)
81 if (ann != null) {
8082 return ann;
83 }
8184 return null;
8285 }
8386
9497 BugAnnotation findRequiredAnnotationFromValueNumber(Method method, Location location, ValueNumber valueNumber,
9598 ValueNumberFrame vnaFrame, @CheckForNull String partialRole) {
9699 BugAnnotation result = findAnnotationFromValueNumber(method, location, valueNumber, vnaFrame, partialRole);
97 if (result != null)
100 if (result != null) {
98101 return result;
102 }
99103 return new LocalVariableAnnotation("?", -1, location.getHandle().getPosition());
100104 }
101105
102106 public static LocalVariableAnnotation findLocalAnnotationFromValueNumber(Method method, Location location,
103107 ValueNumber valueNumber, ValueNumberFrame vnaFrame) {
104108
105 if (vnaFrame == null || vnaFrame.isBottom() || vnaFrame.isTop())
109 if (vnaFrame == null || vnaFrame.isBottom() || vnaFrame.isTop()) {
106110 return null;
111 }
107112
108113 LocalVariableAnnotation localAnnotation = null;
109114 for (int i = 0; i < vnaFrame.getNumLocals(); i++) {
110115 if (valueNumber.equals(vnaFrame.getValue(i))) {
111116 InstructionHandle handle = location.getHandle();
112117 InstructionHandle prev = handle.getPrev();
113 if (prev == null)
118 if (prev == null) {
114119 continue;
120 }
115121 int position1 = prev.getPosition();
116122 int position2 = handle.getPosition();
117123 localAnnotation = LocalVariableAnnotation.getLocalVariableAnnotation(method, i, position1, position2);
118 if (localAnnotation != null)
124 if (localAnnotation != null) {
119125 return localAnnotation;
126 }
120127 }
121128 }
122129 return null;
125132 public static FieldAnnotation findFieldAnnotationFromValueNumber(Method method, Location location, ValueNumber valueNumber,
126133 ValueNumberFrame vnaFrame) {
127134 XField field = ValueNumberSourceInfo.findXFieldFromValueNumber(method, location, valueNumber, vnaFrame);
128 if (field == null)
135 if (field == null) {
129136 return null;
137 }
130138 return FieldAnnotation.fromXField(field);
131139 }
132140
133141 public static XField findXFieldFromValueNumber(Method method, Location location, ValueNumber valueNumber,
134142 ValueNumberFrame vnaFrame) {
135 if (vnaFrame == null || vnaFrame.isBottom() || vnaFrame.isTop())
143 if (vnaFrame == null || vnaFrame.isBottom() || vnaFrame.isTop()) {
136144 return null;
145 }
137146
138147 AvailableLoad load = vnaFrame.getLoad(valueNumber);
139148 if (load != null) {
154163 public BugAnnotation getFromValueNumber(ClassContext classContext, Method method, Location location, int stackPos)
155164 throws DataflowAnalysisException, CFGBuilderException {
156165 ValueNumberFrame vnaFrame = classContext.getValueNumberDataflow(method).getFactAtLocation(location);
157 if (!vnaFrame.isValid())
166 if (!vnaFrame.isValid()) {
158167 return null;
168 }
159169 ValueNumber valueNumber = vnaFrame.getStackValue(stackPos);
160 if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT))
170 if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) {
161171 return null;
172 }
162173 BugAnnotation variableAnnotation = findAnnotationFromValueNumber(method, location, valueNumber, vnaFrame, "VALUE_OF");
163174
164175 return variableAnnotation;
2626 /**
2727 * Base class for Detectors which want to perform a preorder traversal of the
2828 * classfile including visiting Annotations.
29 *
29 *
3030 * @see AnnotationVisitor
3131 */
3232 abstract public class AnnotationDetector extends AnnotationVisitor implements Detector, UseAnnotationDatabase {
3333 private ClassContext classContext;
3434
35 @Override
3536 public void visitClassContext(ClassContext classContext) {
3637 this.classContext = classContext;
3738 classContext.getJavaClass().accept(this);
3940
4041 /**
4142 * Get the ClassContext of the class currently being visited.
42 *
43 *
4344 * @return the current ClassContext
4445 */
4546 public ClassContext getClassContext() {
4647 return classContext;
4748 }
4849
50 @Override
4951 public void report() {
5052 }
5153 }
5254
53 // vim:ts=4
3939
4040 /**
4141 * Utility methods for detectors and analyses using BCEL.
42 *
42 *
4343 * @author David Hovemeyer
4444 */
4545 public abstract class BCELUtil {
4646 /**
4747 * Construct a MethodDescriptor from JavaClass and method.
48 *
48 *
4949 * @param jclass
5050 * a JavaClass
5151 * @param method
6060 /**
6161 * Get a MethodDescriptor describing the method called by given
6262 * InvokeInstruction.
63 *
63 *
6464 * @param inv
6565 * the InvokeInstruction
6666 * @param cpg
7979 /**
8080 * Get FieldDescriptor describing the field accessed by given
8181 * FieldInstruction.
82 *
82 *
8383 * @param fins
8484 * a FieldInstruction
8585 * @param cpg
9797
9898 /**
9999 * Construct a ClassDescriptor from a JavaClass.
100 *
100 *
101101 * @param jclass
102102 * a JavaClass
103103 * @return a ClassDescriptor identifying that JavaClass
120120
121121 /**
122122 * Get a ClassDescriptor for the class described by given ObjectType object.
123 *
123 *
124124 * @param type
125125 * an ObjectType
126126 * @return a ClassDescriptor for the class described by the ObjectType
136136 * Throw a ClassNotFoundException to indicate that class named by given
137137 * ClassDescriptor cannot be found. The exception message is formatted in a
138138 * way that can be decoded by ClassNotFoundExceptionParser.
139 *
139 *
140140 * @param classDescriptor
141141 * ClassDescriptor naming a class that cannot be found
142142 * @throws ClassNotFoundException
153153 public static ObjectType getObjectTypeInstance(@DottedClassName String className) {
154154 return ObjectType.getInstance(className);
155155 }
156
156
157157 public static ObjectType getObjectTypeInstance(Class<?> clazz) {
158158 return getObjectTypeInstance(clazz.getName());
159159 }
160
160
161161 public static boolean isSynthetic(FieldOrMethod m) {
162 if (m.isSynthetic())
162 if (m.isSynthetic()) {
163163 return true;
164 }
164165
165 for(Attribute a : m.getAttributes())
166 if (a instanceof Synthetic)
166 for(Attribute a : m.getAttributes()) {
167 if (a instanceof Synthetic) {
167168 return true;
169 }
170 }
168171 return false;
169172 }
170173 public static boolean isSynthetic(JavaClass j) {
171 if (j.isSynthetic())
174 if (j.isSynthetic()) {
172175 return true;
176 }
173177
174 for(Attribute a : j.getAttributes())
175 if (a instanceof Synthetic)
178 for(Attribute a : j.getAttributes()) {
179 if (a instanceof Synthetic) {
176180 return true;
181 }
182 }
177183 return false;
178184 }
179185 public static boolean isSynthetic(FieldGenOrMethodGen m) {
180 if (m.isSynthetic())
186 if (m.isSynthetic()) {
181187 return true;
182 for(Attribute a : m.getAttributes())
183 if (a instanceof Synthetic)
188 }
189 for(Attribute a : m.getAttributes()) {
190 if (a instanceof Synthetic) {
184191 return true;
192 }
193 }
185194 return false;
186195 }
187196 }
3333
3434 /**
3535 * Base class for detectors that analyze CFG (and/or use CFG-based analyses).
36 *
36 *
3737 * @author David Hovemeyer
3838 */
3939 public abstract class CFGDetector implements Detector2 {
4040
4141 /*
4242 * (non-Javadoc)
43 *
43 *
4444 * @see edu.umd.cs.findbugs.Detector2#finishPass()
4545 */
46 @Override
4647 public void finishPass() {
4748 }
4849
4950 /*
5051 * (non-Javadoc)
51 *
52 *
5253 * @see edu.umd.cs.findbugs.Detector2#getDetectorClassName()
5354 */
55 @Override
5456 public String getDetectorClassName() {
5557 return getClass().getName();
5658 }
5961 protected Method method;
6062 /*
6163 * (non-Javadoc)
62 *
64 *
6365 * @see
6466 * edu.umd.cs.findbugs.Detector2#visitClass(edu.umd.cs.findbugs.classfile
6567 * .ClassDescriptor)
6668 */
69 @Override
6770 public void visitClass(ClassDescriptor classDescriptor) throws CheckedAnalysisException {
6871 IAnalysisCache analysisCache = Global.getAnalysisCache();
6972
9396 /**
9497 * Visit the CFG (control flow graph) of a method to be analyzed. Should be
9598 * overridded by subclasses.
96 *
99 *
97100 * @param methodDescriptor
98101 * @param cfg
99102 * @throws CheckedAnalysisException
5454
5555 @Override
5656 public final void visitCode(Code obj) {
57 if (!shouldVisitCode(obj))
57 if (!shouldVisitCode(obj)) {
5858 return;
59 }
5960 stack = new OpcodeStack();
6061 stack.resetForMethodEntry(this);
6162 super.visitCode(obj);
6869 return !stack.isTop();
6970 }
7071
72 /**
73 * <p>Note that stack might be TOP when this method is called.</p>
74 * @see #sawOpcode(int)
75 */
7176 @Override
7277 public void afterOpcode(int seen) {
7378 stack.sawOpcode(this, seen);
7479 }
7580
81 /**
82 * <p>By default, this method will not be called when
83 * stack is TOP. To change this behavior, override {@code #beforeOpcode(int)}
84 * and change to return true even if stack is TOP.</p>
85 * <p>see <a href="http://findbugs-tutorials.googlecode.com/files/uffr-talk.pdf">Using FindBugs for Research</a> to learn lattice and what TOP means.</p>
86 * @see #beforeOpcode(int)
87 */
7688 @Override
7789 abstract public void sawOpcode(int seen);
7890
2525 /**
2626 * Base class for Detectors which want to perform a preorder traversal of the
2727 * classfile.
28 *
28 *
2929 * @see PreorderVisitor
3030 */
3131 abstract public class PreorderDetector extends PreorderVisitor implements Detector {
3232 private ClassContext classContext;
3333
34 @Override
3435 public void visitClassContext(ClassContext classContext) {
3536 this.classContext = classContext;
3637 classContext.getJavaClass().accept(this);
3839
3940 /**
4041 * Get the ClassContext of the class currently being visited.
41 *
42 *
4243 * @return the current ClassContext
4344 */
4445 public ClassContext getClassContext() {
4546 return classContext;
4647 }
4748
49 @Override
4850 public void report() {
4951 }
5052 }
5153
52 // vim:ts=4
33
44 /** A synthetic instruction that converts a reference to a boolean value,
55 * translating any nonnull value to 1 (true), and null value to 0 (false).
6 *
6 *
77 */
88 public class NONNULL2Z extends NullnessConversationInstruction {
99
1111 super(org.apache.bcel.Constants.IMPDEP2);
1212 }
1313
14
14
1515
1616 }
33
44 /** A synthetic instruction that converts a reference to a boolean value,
55 * translating null to 1 (true), and any nonnull value to 0 (false).
6 *
6 *
77 */
88 public class NULL2Z extends NullnessConversationInstruction {
99
1111 public NULL2Z() {
1212 super(org.apache.bcel.Constants.IMPDEP1);
1313 }
14
15
14
15
1616
1717 }
00 /*
11 * FindBugs - Find Bugs in Java programs
22 * Copyright (C) 2003-2008 University of Maryland
3 *
3 *
44 * This library is free software; you can redistribute it and/or
55 * modify it under the terms of the GNU Lesser General Public
66 * License as published by the Free Software Foundation; either
77 * version 2.1 of the License, or (at your option) any later version.
8 *
8 *
99 * This library is distributed in the hope that it will be useful,
1010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1111 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1212 * Lesser General Public License for more details.
13 *
13 *
1414 * You should have received a copy of the GNU Lesser General Public
1515 * License along with this library; if not, write to the Free Software
1616 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2424 import org.apache.bcel.generic.Visitor;
2525
2626 /** A synthetic instruction that converts a the nullness of a reference to a boolean value.
27 *
27 *
2828 */
2929 public abstract class NullnessConversationInstruction extends ConversionInstruction {
3030
4545 public int produceStack( ConstantPoolGen cpg ) {
4646 return 1;
4747 }
48
48
4949 @Override
5050 public int hashCode() {
5151 return this.getClass().hashCode();
3939 @Override
4040 public void reportBug(@Nonnull BugInstance bugInstance) {
4141 int rank = bugInstance.getBugRank();
42 if (rank <= maxRank)
42 if (rank <= maxRank) {
4343 getDelegate().reportBug(bugInstance);
44 }
4445
4546 }
4647
6565 URL u;
6666
6767 if (adjustmentSource.startsWith("file:") || adjustmentSource.startsWith("http:")
68 || adjustmentSource.startsWith("https:"))
68 || adjustmentSource.startsWith("https:")) {
6969 u = new URL(adjustmentSource);
70 else {
70 } else {
7171 u = plugin.getPlugin().getResource(adjustmentSource);
72 if (u == null)
72 if (u == null) {
7373 u = DetectorFactoryCollection.getCoreResource(adjustmentSource);
74 }
7475
7576 }
7677 if (u != null) {
8990 * @throws IOException
9091 */
9192 private void processPackageList(@WillClose Reader rawIn) throws IOException {
92 try {
93 BufferedReader in = new BufferedReader(rawIn);
94 while (true) {
95 String s = in.readLine();
96 if (s == null)
97 break;
98 s = s.trim();
99 if (s.length() == 0)
100 continue;
101 String packageName = s.substring(1).trim();
102 if (s.charAt(0) == '+') {
103 check.add(packageName);
104 dontCheck.remove(packageName);
105 } else if (s.charAt(0) == '-') {
106 dontCheck.add(packageName);
107 check.remove(packageName);
108 } else
109 throw new IllegalArgumentException("Can't parse " + category + " filter line: " + s);
110 }
93 try (BufferedReader in = new BufferedReader(rawIn)) {
94 String s;
95 while ((s = in.readLine()) != null) {
96 s = s.trim();
97 if (s.length() == 0) {
98 continue;
99 }
100 String packageName = s.substring(1).trim();
101 if (s.charAt(0) == '+') {
102 check.add(packageName);
103 dontCheck.remove(packageName);
104 } else if (s.charAt(0) == '-') {
105 dontCheck.add(packageName);
106 check.remove(packageName);
107 } else {
108 throw new IllegalArgumentException("Can't parse " + category + " filter line: " + s);
109 }
110 }
111111 } finally {
112112 rawIn.close();
113113 }
120120 getDelegate().reportBug(bugInstance);
121121 return;
122122 }
123 if (check.isEmpty())
123 if (check.isEmpty()) {
124124 return;
125 }
125126
126127 ClassAnnotation c = bugInstance.getPrimaryClass();
127128 @DottedClassName
135136 return;
136137 }
137138 int i = packageName.lastIndexOf('.');
138 if (i < 0)
139 if (i < 0) {
139140 return;
141 }
140142 packageName = packageName.substring(0, i);
141143 }
142144
4343 public class UTF8 {
4444
4545 /**
46 *
46 *
4747 */
4848 private static final String UTF_8 = "UTF-8";
4949 public static final Charset charset;
5353 }
5454
5555 public static PrintStream printStream(OutputStream out) {
56 return printStream(out, false);
56 return printStream(out, false);
5757 }
5858 public static PrintStream printStream(OutputStream out, boolean autoflush) {
5959 try {
6060 return new PrintStream(out,autoflush, UTF_8);
6161 } catch (UnsupportedEncodingException e) {
62 throw new AssertionError("UTF-8 not supported");
62 throw new AssertionError("UTF-8 not supported");
6363 }
6464 }
6565 public static Writer writer(OutputStream out) {
2121 /**
2222 * Common superclass for all checked exceptions that can be thrown while
2323 * performing some kind of analysis.
24 *
24 *
2525 * @author David Hovemeyer
2626 */
2727 public class CheckedAnalysisException extends Exception {
3737
3838 /**
3939 * Constructor.
40 *
40 *
4141 * @param msg
4242 * message
4343 */
4747
4848 /**
4949 * Constructor.
50 *
50 *
5151 * @param msg
5252 * message
5353 * @param cause
7474 *
7575 * @see java.lang.Comparable#compareTo(java.lang.Object)
7676 */
77 @Override
7778 public int compareTo(ClassDescriptor o) {
7879 return className.compareTo(o.className);
7980 }
112113 return ClassName.toDottedClassName(className);
113114 }
114115
115 public XClass getXClass() throws CheckedAnalysisException {
116 public XClass getXClass() throws CheckedAnalysisException {
116117 return Global.getAnalysisCache().getClassAnalysis(XClass.class, this);
117118 }
118119 /**
135136 }
136137
137138 public String getSignature() {
138 if (isArray())
139 if (isArray()) {
139140 return className;
141 }
140142 return "L" + className + ";";
141143 }
142144
2121 /**
2222 * Exception to indicate that the class name defined in a class file does not
2323 * match its expected class name (as indicated by its resource name).
24 *
24 *
2525 * @author David Hovemeyer
2626 */
2727 public class ClassNameMismatchException extends InvalidClassFileFormatException {
28 private ClassDescriptor loadedClassDescriptor;
28 private final ClassDescriptor loadedClassDescriptor;
2929
3030 /**
3131 * Constructor.
32 *
32 *
3333 * @param expectedClassDescriptor
3434 * class descriptor we were expected based on the resource name
3535 * @param loadedClassDescriptor
6767 this.fieldDescriptorMap = new HashMap<FieldDescriptor, FieldDescriptor>();
6868 }
6969
70 private MapCache<String, String> stringCache = new MapCache<String, String>(10000);
70 private final MapCache<String, String> stringCache = new MapCache<String, String>(10000);
7171
7272 public static String canonicalizeString(@CheckForNull String s) {
73 if (s == null)
73 if (s == null) {
7474 return s;
75 }
7576 DescriptorFactory df = instanceThreadLocal.get();
7677 String cached = df.stringCache.get(s);
77 if (cached != null)
78 if (cached != null) {
7879 return cached;
80 }
7981 df.stringCache.put(s, s);
8082 return s;
8183 }
166168 */
167169 public MethodDescriptor getMethodDescriptor(@SlashedClassName String className, String name, String signature,
168170 boolean isStatic) {
169 if (className == null)
171 if (className == null) {
170172 throw new NullPointerException("className must be nonnull");
173 }
171174 MethodDescriptor methodDescriptor = new MethodDescriptor(className, name, signature, isStatic);
172175 MethodDescriptor existing = methodDescriptorMap.get(methodDescriptor);
173176 if (existing == null) {
184187 int bad = 0;
185188 for (Map.Entry<MethodDescriptor, MethodDescriptor> e : methodDescriptorMap.entrySet()) {
186189 total++;
187 if (e.getKey() instanceof MethodInfo)
190 if (e.getKey() instanceof MethodInfo) {
188191 keys++;
189 if (e.getValue() instanceof MethodInfo)
192 }
193 if (e.getValue() instanceof MethodInfo) {
190194 values++;
195 }
191196 }
192197 System.out.printf("Descriptor factory: %d/%d/%d%n", keys, values, total);
193198
308313
309314 public static ClassDescriptor createClassDescriptorFromSignature(String signature) {
310315 int length = signature.length();
311 if (length == 0)
316 if (length == 0) {
312317 throw new IllegalArgumentException("Empty signature");
313 if (signature.charAt(0) == 'L' && signature.endsWith(";"))
318 }
319 if (signature.charAt(0) == 'L' && signature.endsWith(";")) {
314320 signature = signature.substring(1, signature.length() - 1);
321 }
315322 return createClassDescriptor(signature);
316323 }
317324
318325 public static ClassDescriptor createClassOrObjectDescriptorFromSignature(String signature) {
319 if (signature.charAt(0) == '[')
326 if (signature.charAt(0) == '[') {
320327 return createClassDescriptor("java/lang/Object");
328 }
321329 return createClassDescriptorFromSignature(signature);
322330 }
323331
330338 }
331339
332340 public static ClassDescriptor[] createClassDescriptor(String[] classNames) {
333 if (classNames.length == 0)
341 if (classNames.length == 0) {
334342 return ClassDescriptor.EMPTY_ARRAY;
343 }
335344 ClassDescriptor[] result = new ClassDescriptor[classNames.length];
336 for (int i = 0; i < classNames.length; i++)
345 for (int i = 0; i < classNames.length; i++) {
337346 result[i] = createClassDescriptor(classNames[i]);
347 }
338348 return result;
339349 }
340350
2323
2424 /**
2525 * Descriptor uniquely identifying a field in a class.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class FieldDescriptor extends FieldOrMethodDescriptor implements ComparableField {
3030
3131 /**
3232 * Constructor.
33 *
33 *
3434 * @param className
3535 * the name of the class the field belongs to
3636 * @param fieldName
4949 return (isStatic() ? "static " : "") + getClassDescriptor().getDottedClassName() + "." + getName() + " " + getSignature();
5050 }
5151
52 @Override
5253 public int compareTo(ComparableField o) {
5354 return FieldOrMethodDescriptor.compareTo(this, (FieldDescriptor)o);
5455 }
6061 }
6162 return false;
6263 }
63
64
6465 }
7171 /**
7272 * @return a ClassDescriptor for the method's class
7373 */
74 @Override
7475 public ClassDescriptor getClassDescriptor() {
7576 return DescriptorFactory.createClassDescriptor(slashedClassName);
7677 }
7879 /**
7980 * @return Returns the method name
8081 */
82 @Override
8183 public String getName() {
8284 return name;
8385 }
8587 /**
8688 * @return Returns the method signature
8789 */
90 @Override
8891 public String getSignature() {
8992 return signature;
9093 }
9295 /**
9396 * @return Returns true if method is static, false if not
9497 */
98 @Override
9599 public boolean isStatic() {
96100 return isStatic;
97101 }
115119
116120
117121 protected boolean haveEqualFields(FieldOrMethodDescriptor other) {
118 return this.isStatic == other.isStatic && this.slashedClassName.equals(other.slashedClassName)
119 && this.name.equals(other.name) && this.signature.equals(other.signature);
122 return this.isStatic == other.isStatic && this.slashedClassName.equals(other.slashedClassName)
123 && this.name.equals(other.name) && this.signature.equals(other.signature);
120124 }
121125
122126 /*
143147 }
144148
145149 public static int compareTo(FieldOrMethodDescriptor thas, FieldOrMethodDescriptor that) {
146 int result = thas.slashedClassName.compareTo(that.slashedClassName);
147 if (result != 0)
150 int result = thas.slashedClassName.compareTo(that.slashedClassName);
151 if (result != 0) {
148152 return result;
153 }
149154 result = thas.name.compareTo(that.name);
150 if (result != 0)
155 if (result != 0) {
151156 return result;
157 }
152158 result = thas.signature.compareTo(that.signature);
153 if (result != 0)
159 if (result != 0) {
154160 return result;
161 }
155162 result = (thas.isStatic ? 1 : 0) - (that.isStatic ? 1 : 0);
156163 return result;
157164 }
2121 /**
2222 * Static methods for accessing objects that are global to an analysis session.
2323 * Hopefully, this will be limited to the analysis cache.
24 *
24 *
2525 * @author David Hovemeyer
2626 */
2727 public abstract class Global {
3030 /**
3131 * Remove the analysis cache for the current thread. This should be called
3232 * after all analysis is complete
33 *
33 *
3434 */
3535 public static void removeAnalysisCacheForCurrentThread() {
3636 analysisCacheThreadLocal.remove();
3939 /**
4040 * Set the analysis cache for the current thread. This should be called
4141 * before any detectors or analyses that need the cache are used.
42 *
42 *
4343 * @param analysisCache
4444 * the analysis cache to set for the current thread
4545 */
4949
5050 /**
5151 * Get the analysis cache for the current thread.
52 *
52 *
5353 * @return the analysis cache for the current thread
5454 */
5555 public static IAnalysisCache getAnalysisCache() {
2727 /**
2828 * The analysis cache performs analyses on classes and methods and caches the
2929 * results.
30 *
30 *
3131 * @author David Hovemeyer
3232 */
3333 public interface IAnalysisCache {
3535 /**
3636 * Register the given class analysis engine as producing the analysis result
3737 * type whose Class is given.
38 *
38 *
3939 * @param <E>
4040 * analysis result type
4141 * @param analysisResultType
4848 /**
4949 * Register the given method analysis engine as producing the analysis
5050 * result type whose Class is given.
51 *
51 *
5252 * @param <E>
5353 * analysis result type
5454 * @param analysisResultType
6060
6161 /**
6262 * Get an analysis of the given class.
63 *
63 *
6464 * @param <E>
6565 * the type of the analysis (e.g., FoobarAnalysis)
6666 * @param analysisClass
7878 /**
7979 * See if the cache contains a cached class analysis result for given class
8080 * descriptor.
81 *
81 *
8282 * @param analysisClass
8383 * analysis result class
8484 * @param classDescriptor
9090
9191 /**
9292 * Get an analysis of the given method.
93 *
93 *
9494 * @param <E>
9595 * the type of the analysis (e.g., FoobarAnalysis)
9696 * @param analysisClass
109109 * Eagerly put a method analysis object in the cache. This can be necessary
110110 * if an method analysis engine invokes other analysis engines that might
111111 * recursively require the analysis being produced.
112 *
112 *
113113 * @param <E>
114114 * the type of the analysis (e.g., FoobarAnalysis)
115115 * @param analysisClass
125125 * Purge all analysis results for given method. This can be called when a
126126 * CFG is pruned and we want to compute more accurate analysis results on
127127 * the new CFG.
128 *
128 *
129129 * @param methodDescriptor
130130 * method whose analysis results should be purged
131131 */
143143
144144 /**
145145 * Register a database factory.
146 *
146 *
147147 * @param <E>
148148 * type of database
149149 * @param databaseClass
155155
156156 /**
157157 * Get a database.
158 *
158 *
159159 * <em>Note</em>: an unchecked analysis exception will be thrown if the
160160 * database cannot be instantiated. Since instantiation of most kinds of
161161 * databases simply involves creating an object (and not opening a file or
162162 * other failure-prone operation), throwing a CheckedAnalysisException
163163 * creates too great of an exception-handling burden on analyses and
164164 * detectors which use databases.
165 *
165 *
166166 * @param <E>
167167 * type of database
168168 * @param databaseClass
176176 /**
177177 * Eagerly install a database. This avoids the need to register a database
178178 * factory.
179 *
179 *
180180 * @param <E>
181181 * type of database
182182 * @param databaseClass
188188
189189 /**
190190 * Get the classpath from which classes are loaded.
191 *
191 *
192192 * @return the classpath
193193 */
194194 public IClassPath getClassPath();
195195
196196 /**
197197 * Get the error logger.
198 *
198 *
199199 * @return the error logger
200200 */
201201 public IErrorLogger getErrorLogger();
207207
208208 /**
209209 * Get the analysis profiler instance, never null
210 *
211 * @return
212210 */
213211 public Profiler getProfiler();
214212 }
2020
2121 /**
2222 * An engine for analyzing classes or methods.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public interface IAnalysisEngine<DescriptorType, ResultType> {
2727 /**
2828 * Perform an analysis on class or method named by given descriptor.
29 *
29 *
3030 * @param analysisCache
3131 * the analysis cache
3232 * @param descriptor
3737
3838 /**
3939 * Register the analysis engine with given analysis cache.
40 *
40 *
4141 * @param analysisCache
4242 * the analysis cache
4343 */
2121 /**
2222 * Interface for a registrar class that registers analysis engines with an
2323 * analysis cache.
24 *
24 *
2525 * @author David Hovemeyer
2626 */
2727 public interface IAnalysisEngineRegistrar {
2828 /**
2929 * Register analysis engines with given analysis cache.
30 *
30 *
3131 * @param analysisCache
3232 * the analysis cache
3333 */
2020
2121 /**
2222 * Engine for performing an analysis on classes.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public interface IClassAnalysisEngine<ResultType> extends IAnalysisEngine<ClassDescriptor, ResultType> {
3030 * recomputed. Unless some correctness criterion prevents analysis results
3131 * from being recomputed, analysis engines should return true (allowing the
3232 * cache to be kept to a manageable size).
33 *
33 *
3434 * @return true if analysis results produced by this engine can be
3535 * recomputed, false if for some reason the analysis results must be
3636 * retained indefinitely
2222 * Observer for classes being visited in some manner. If the observe wishes to
2323 * load the actual class data (or some analysis of the class) it must explicitly
2424 * load it from the analysis cache.
25 *
25 *
2626 * @author David Hovemeyer
2727 */
2828 public interface IClassObserver {
2929 /**
3030 * Observe a class being visited.
31 *
31 *
3232 * @param classDescriptor
3333 * class being visited
3434 */
2828 * <li>Builds a list of application class descriptors</li>
2929 * <li>Adds system codebases</li>
3030 * </ul>
31 *
31 *
3232 * @author David Hovemeyer
3333 */
3434 public interface IClassPathBuilder {
3535 /**
3636 * Add a project codebase.
37 *
37 *
3838 * @param locator
3939 * locator for project codebase
4040 * @param isApplication
4646 /**
4747 * Set whether or not nested archives should be scanned. This should be
4848 * called before the build() method is called.
49 *
49 *
5050 * @param scanNestedArchives
5151 * true if nested archives should be scanned, false otherwise
5252 */
5454
5555 /**
5656 * Build the classpath.
57 *
57 *
5858 * @param classPath
5959 * IClassPath object to build
6060 * @param progress
6464 * @throws InterruptedException
6565 */
6666 public void build(IClassPath classPath, IClassPathBuilderProgress progress) throws CheckedAnalysisException, IOException,
67 InterruptedException;
67 InterruptedException;
6868
6969 /**
7070 * Get the list of application classes discovered while scanning the
7171 * classpath.
72 *
72 *
7373 * @return list of application classes
7474 */
7575 public List<ClassDescriptor> getAppClassList();
2020
2121 /**
2222 * Progress callback interface for classpath construction.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public interface IClassPathBuilderProgress {
2121 /**
2222 * Interface for a basic code base in which we can look up resources but not
2323 * necessarily scan for the list of all resources.
24 *
24 *
2525 * @author David Hovemeyer
2626 */
2727 public interface ICodeBase {
28
28
2929 enum Discovered {
3030
3131 /** Codebase was explicitly specified. */
4545 }
4646 /**
4747 * Get the codebase locator describing the location of this codebase.
48 *
48 *
4949 * @return the ICodeBaseLocator
5050 */
5151 public ICodeBaseLocator getCodeBaseLocator();
5252
5353 /**
5454 * Look up a resource in this code base.
55 *
55 *
5656 * @param resourceName
5757 * name of the resource to look up
5858 * @return ICodeBaseEntry representing the resource or null if the resource
6262
6363 /**
6464 * Designate this code base as an application codebase.
65 *
65 *
6666 * @param isAppCodeBase
6767 * true if this is an application codebase, false if not
6868 */
7070
7171 /**
7272 * Return whether or not this codebase is an application codebase.
73 *
73 *
7474 * @return true if this is an application codebase, false if not
7575 */
7676 public boolean isApplicationCodeBase();
7777
7878 /**
7979 * Set how this codebase was discovered.
80 *
80 *
8181 * @param howDiscovered
8282 * one of the constants SPECIFIED, NESTED, IN_JAR_MANIFEST, or
8383 * IN_SYSTEM_CLASSPATH
8686
8787 /**
8888 * Return how this codebase was discovered.
89 *
89 *
9090 * @return one of the constants SPECIFIED, NESTED, IN_JAR_MANIFEST, or
9191 * IN_SYSTEM_CLASSPATH
9292 */
9494
9595 /**
9696 * Return whether or not this code base contains any source files.
97 *
97 *
9898 * @return true if the code base contains source file(s), false if it does
9999 * not contain source files
100100 */
102102
103103 /**
104104 * Get the filesystem pathname of this codebase.
105 *
105 *
106106 * @return the filesystem pathname of this codebase, or null if this
107107 * codebase is not accessible via the filesystem
108108 */
111111 /**
112112 * Set timestamp indicating the most recent time when any of the files in
113113 * the codebase were modified.
114 *
114 *
115115 * @param lastModifiedTime
116116 * timestamp when any codebase files were most-recently modified
117117 */
122122 * the codebase were modified. This information is only likely to be
123123 * accurate if an ICodeBaseIterator has been used to scan the resources in
124124 * the codebase (scannable codebases only, obviously).
125 *
125 *
126126 * @return timestamp when any codebase files were most-recently modified, -1
127127 * if unknown
128128 */
2323
2424 /**
2525 * Object representing a resource in a code base.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public interface ICodeBaseEntry {
3030 /**
3131 * Get the name of the resource.
32 *
32 *
3333 * @return the name of the resource
3434 */
3535 public String getResourceName();
3737 /**
3838 * Get the number of bytes in the resource. Returns &lt;0 if the number of
3939 * bytes is not known.
40 *
40 *
4141 * @return number of bytes in the resource, or &lt;0 if not known.
4242 */
4343 public int getNumBytes();
4444
4545 /**
4646 * Open an input stream reading from the resource.
47 *
47 *
4848 * @return InputStream reading from the resource.
4949 * @throws IOException
5050 * if an error occurs reading from the resource
5353
5454 /**
5555 * Get the codebase this codebase entry belongs to.
56 *
56 *
5757 * @return the codebase this codebase entry belongs to
5858 */
5959 public ICodeBase getCodeBase();
6363 * codebase entry. Do not call this method unless
6464 * ClassDescriptor.isClassResource() returns true. This method may require
6565 * the class data to be loaded in order to determine the class.
66 *
66 *
6767 * @return ClassDescriptor of this entry
6868 * @throws ResourceNotFoundException, InvalidClassFileFormatException
6969 * if the codebase entry does not reference a valid classfile
7575
7676 /**
7777 * Override the resource name of this codebase entry.
78 *
78 *
7979 * @param resourceName
8080 * the new resource name
8181 */
2222 * Iterator over the resources in an IScannableCodeBase. Note that some of the
2323 * methods can throw InterruptedException. This occurs when the analysis is
2424 * canceled by interrupting the analysis thread.
25 *
25 *
2626 * <p>
2727 * Note that the close() method must be called when done with an
2828 * ICodeBaseIterator object.
2929 * </p>
30 *
30 *
3131 * @author David Hovemeyer
3232 */
3333 public interface ICodeBaseIterator {
3434 /**
3535 * Return true if there is another resource to be scanned, false otherwise.
36 *
36 *
3737 * @return true if there is another resource to be scanned, false otherwise
3838 */
3939 public boolean hasNext() throws InterruptedException;
4040
4141 /**
4242 * Get the ICodeBaseEntry representing the next resource in the code base.
43 *
43 *
4444 * @return the ICodeBaseEntry representing the next resource in the code
4545 * base
4646 * @throws InterruptedException
2222
2323 /**
2424 * Specify the location of a codebase.
25 *
25 *
2626 * @author David Hovemeyer
2727 */
2828 public interface ICodeBaseLocator {
2929 /**
3030 * Get the codebase object.
31 *
31 *
3232 * @return the codebase object
3333 */
3434 public ICodeBase openCodeBase() throws IOException, ResourceNotFoundException;
3737 * Get the codebase locator describing the location of a relative codebase.
3838 * This method is useful for getting the location of a codebase referred to
3939 * in the Class-Path attribute of a Jar manifest.
40 *
40 *
4141 * @param relativePath
4242 * the path of a relative codebase
4343 * @return codebase locator of the relative codebase whose path is given
4949 * codebase locators that refer to the same codebase should produce the same
5050 * string representation. So, this string can serve as a key identifying the
5151 * codebase in a map.
52 *
52 *
5353 * @return a string representation of the codebase
5454 */
55 @Override
5556 public String toString();
5657 }
2020
2121 /**
2222 * Interface for a database factory.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public interface IDatabaseFactory<T> {
2020
2121 /**
2222 * Interface for objects that log various kinds of analysis errors.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public interface IErrorLogger {
2727 /**
2828 * Called to report a class lookup failure.
29 *
29 *
3030 * @param ex
3131 * a ClassNotFoundException resulting from the class lookup
3232 * failure
3535
3636 /**
3737 * Called to report a class lookup failure.
38 *
38 *
3939 * @param classDescriptor
4040 * ClassDescriptor of a missing class
4141 */
4343
4444 /**
4545 * Log an error that occurs while performing analysis.
46 *
46 *
4747 * @param message
4848 * the error message
4949 */
5151
5252 /**
5353 * Log an error that occurs while performing analysis.
54 *
54 *
5555 * @param message
5656 * the error message
5757 * @param e
6161
6262 /**
6363 * Report that we skipped some analysis of a method
64 *
64 *
6565 * @param method
6666 * the method we skipped
6767 */
2020
2121 /**
2222 * Engine for performing an analysis on methods.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public interface IMethodAnalysisEngine<ResultType> extends IAnalysisEngine<MethodDescriptor, ResultType> {
2121 /**
2222 * A scannable code base: in addition to looking up a named resource, scannable
2323 * code bases can also enumerate the names of the resources they contain.
24 *
24 *
2525 * @author David Hovemeyer
2626 */
2727 public interface IScannableCodeBase extends ICodeBase {
3131
3232 /**
3333 * Get an iterator over the resources in the this code base.
34 *
34 *
3535 * @return ICodeBaseIterator over the resources in the code base
3636 */
3737 public ICodeBaseIterator iterator() throws InterruptedException;
2222 * @author David Hovemeyer
2323 */
2424 public class InvalidClassFileFormatException extends CheckedAnalysisException {
25 private ClassDescriptor classDescriptor;
25 private final ClassDescriptor classDescriptor;
2626
27 private ICodeBaseEntry codeBaseEntry;
27 private final ICodeBaseEntry codeBaseEntry;
2828
2929 public InvalidClassFileFormatException(ClassDescriptor classDescriptor, ICodeBaseEntry codeBaseEntry) {
3030 super("Invalid classfile format");
6161
6262 /*
6363 * (non-Javadoc)
64 *
64 *
6565 * @see java.lang.Throwable#getMessage()
6666 */
6767 @Override
1818
1919 package edu.umd.cs.findbugs.classfile;
2020
21 import org.apache.bcel.generic.ConstantPoolGen;
22 import org.apache.bcel.generic.INVOKESTATIC;
23 import org.apache.bcel.generic.InvokeInstruction;
24
2125 import edu.umd.cs.findbugs.ba.ComparableMethod;
2226 import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName;
27 import edu.umd.cs.findbugs.util.ClassName;
2328
2429 /**
2530 * Descriptor uniquely identifying a method in a class.
26 *
31 *
2732 * @author David Hovemeyer
2833 */
2934 public class MethodDescriptor extends FieldOrMethodDescriptor implements ComparableMethod {
3035
3136 /**
3237 * Constructor.
33 *
38 *
3439 * @param className
3540 * name of the class containing the method, in VM format (e.g.,
3641 * "java/lang/String")
4550 super(className, methodName, methodSignature, isStatic);
4651 }
4752
53 public MethodDescriptor(@SlashedClassName String className, String methodName, String methodSignature) {
54 super(className, methodName, methodSignature, false);
55 }
56
57 public MethodDescriptor(InvokeInstruction iins, ConstantPoolGen cpg) {
58 super(ClassName.toSlashedClassName(iins.getClassName(cpg)), iins.getMethodName(cpg), iins.getSignature(cpg), iins instanceof INVOKESTATIC);
59 }
60
61 @Override
4862 public int compareTo(ComparableMethod o) {
4963 return FieldOrMethodDescriptor.compareTo(this, (MethodDescriptor)o);
5064 }
51
65
5266 @Override
5367 public final boolean equals(Object obj) {
5468 if (obj instanceof MethodDescriptor) {
2121 /**
2222 * CheckedAnalysisException subtype to indicate that a required class was
2323 * missing.
24 *
24 *
2525 * @author David Hovemeyer
2626 */
2727 public class MissingClassException extends ResourceNotFoundException {
28 private ClassDescriptor classDescriptor;
28 private final ClassDescriptor classDescriptor;
2929
3030 /**
3131 * Constructor.
32 *
32 *
3333 * @param classDescriptor
3434 * missing class
3535 */
4040
4141 /**
4242 * Constructor.
43 *
43 *
4444 * @param classDescriptor
4545 * missing class
4646 * @param cause
2323 */
2424 public abstract class RecomputableClassAnalysisEngine<ResultType> implements IClassAnalysisEngine<ResultType> {
2525
26 @Override
2627 public boolean canRecompute() {
2728 // can be recomputed
2829 return true;
2626 /**
2727 * A generic database factory that tries to create the database by (in order of
2828 * preference)
29 *
29 *
3030 * <ol>
3131 * <li>Invoking a static <b>create</b> method</li>
3232 * <li>Invoking a no-arg constructor
3333 * </ol>
34 *
34 *
3535 * @author David Hovemeyer
3636 */
3737 public class ReflectionDatabaseFactory<E> implements IDatabaseFactory<E> {
38 private Class<E> databaseClass;
38 private final Class<E> databaseClass;
3939
4040 public ReflectionDatabaseFactory(Class<E> databaseClass) {
4141 this.databaseClass = databaseClass;
4343
4444 /*
4545 * (non-Javadoc)
46 *
46 *
4747 * @see edu.umd.cs.findbugs.classfile.IDatabaseFactory#createDatabase()
4848 */
49 @Override
4950 public E createDatabase() throws CheckedAnalysisException {
5051 E database;
5152
6465
6566 /**
6667 * Try to create the database using a static create() method.
67 *
68 *
6869 * @return the database, or null if there is no static create() method
6970 * @throws CheckedAnalysisException
7071 */
9596
9697 /**
9798 * Try to create the database using a no-arg constructor.
98 *
99 *
99100 * @return the database, or null if there is no no-arg constructor
100101 * @throws CheckedAnalysisException
101102 */
120121
121122 /*
122123 * (non-Javadoc)
123 *
124 *
124125 * @see
125126 * edu.umd.cs.findbugs.classfile.IDatabaseFactory#registerWith(edu.umd.cs
126127 * .findbugs.classfile.IAnalysisCache)
127128 */
129 @Override
128130 public void registerWith(IAnalysisCache analysisCache) {
129131 analysisCache.registerDatabaseFactory(databaseClass, this);
130132 }
2020
2121 /**
2222 * Exception to indicate that a resource was not found.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public class ResourceNotFoundException extends CheckedAnalysisException {
27 private String resourceName;
27 private final String resourceName;
2828
2929 public static final String MESSAGE_PREFIX = "Resource not found: ";
3030
3131 /**
3232 * Constructor.
33 *
33 *
3434 * @param resourceName
3535 * name of the missing resource
3636 */
4141
4242 /**
4343 * Constructor.
44 *
44 *
4545 * @param resourceName
4646 * name of the missing resource
4747 * @param cause
5454
5555 /**
5656 * Get the name of the resource that was not found.
57 *
57 *
5858 * @return the name of the resource that was not found
5959 */
6060 public String getResourceName() {
2020
2121 /**
2222 * Common base class for unchecked analysis exceptions.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public class UncheckedAnalysisException extends RuntimeException {
2727 /**
2828 * Constructor.
29 *
29 *
3030 * @param message
3131 * message describing the exception
3232 */
3636
3737 /**
3838 * Constructor.
39 *
39 *
4040 * @param message
4141 * message describing the exception
4242 * @param cause
2727
2828 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
2929 import edu.umd.cs.findbugs.classfile.DescriptorFactory;
30 import edu.umd.cs.findbugs.classfile.engine.asm.FindBugsASM;
3031
3132 /**
3233 * The "raw" version of an annotation appearing in a class file.
33 *
34 *
3435 * @author William Pugh
3536 */
3637 public class AnnotationValue {
3738 private final ClassDescriptor annotationClass;
3839
39 private Map<String, Object> valueMap = new HashMap<String, Object>(4);
40
41 private Map<String, Object> typeMap = new HashMap<String, Object>(4);
40 private final Map<String, Object> valueMap = new HashMap<String, Object>(4);
41
42 private final Map<String, Object> typeMap = new HashMap<String, Object>(4);
4243
4344 /**
4445 * Constructor.
45 *
46 *
4647 * @param annotationClass
4748 * the annotation class
4849 */
5253
5354 /**
5455 * Constructor.
55 *
56 *
5657 * @param annotationClass
5758 * JVM signature of the annotation class
5859 */
7273 * "http://asm.objectweb.org/current/doc/javadoc/user/org/objectweb/asm/AnnotationVisitor.html"
7374 * >AnnotationVisitor Javadoc</a> for information on what the object
7475 * returned could be.
75 *
76 *
7677 * @param name
7778 * name of annotation element
7879 * @return the element value (primitive value, String value, enum value,
8485
8586 /**
8687 * Get a descriptor specifying the type of an annotation element.
87 *
88 *
8889 * @param name
8990 * name of annotation element
9091 * @return descriptor specifying the type of the annotation element
99100 }
100101
101102 private static String canonicalString(String s) {
102 if (s.equals("value"))
103 if ("value".equals(s)) {
103104 return "value";
105 }
104106 return s;
105107 }
106108
108110 * Get an AnnotationVisitor which can populate this AnnotationValue object.
109111 */
110112 public AnnotationVisitor getAnnotationVisitor() {
111 return new AnnotationVisitor() {
113 return new AnnotationVisitor(FindBugsASM.ASM_VERSION) {
114 @Override
112115 public void visit(String name, Object value) {
113116 name = canonicalString(name);
114117 valueMap.put(name, value);
116119
117120 /*
118121 * (non-Javadoc)
119 *
122 *
120123 * @see
121124 * org.objectweb.asm.AnnotationVisitor#visitAnnotation(java.lang
122125 * .String, java.lang.String)
123126 */
127 @Override
124128 public AnnotationVisitor visitAnnotation(String name, String desc) {
125129 name = canonicalString(name);
126130 AnnotationValue newValue = new AnnotationValue(desc);
131135
132136 /*
133137 * (non-Javadoc)
134 *
138 *
135139 * @see
136140 * org.objectweb.asm.AnnotationVisitor#visitArray(java.lang.String)
137141 */
142 @Override
138143 public AnnotationVisitor visitArray(String name) {
139144 name = canonicalString(name);
140145 return new AnnotationArrayVisitor(name);
142147
143148 /*
144149 * (non-Javadoc)
145 *
150 *
146151 * @see org.objectweb.asm.AnnotationVisitor#visitEnd()
147152 */
153 @Override
148154 public void visitEnd() {
149155
150156 }
151157
152158 /*
153159 * (non-Javadoc)
154 *
160 *
155161 * @see
156162 * org.objectweb.asm.AnnotationVisitor#visitEnum(java.lang.String,
157163 * java.lang.String, java.lang.String)
158164 */
165 @Override
159166 public void visitEnum(String name, String desc, String value) {
160167 name = canonicalString(name);
161168 valueMap.put(name, new EnumValue(desc, value));
165172 };
166173 }
167174
168 private final class AnnotationArrayVisitor implements AnnotationVisitor {
175 private final class AnnotationArrayVisitor extends AnnotationVisitor {
169176 /**
170177 *
171178 */
183190 * @param result
184191 */
185192 private AnnotationArrayVisitor(String name) {
193 super(FindBugsASM.ASM_VERSION);
186194 name = canonicalString(name);
187195 this.name = name;
188196 this.outerList = null;
189197 }
190198
191199 private AnnotationArrayVisitor(List<Object> outerList) {
200 super(FindBugsASM.ASM_VERSION);
192201 this.name = null;
193202 this.outerList = outerList;
194203 }
195204
205 @Override
196206 public void visit(String name, Object value) {
197207 result.add(value);
198208 }
199209
210 @Override
200211 public AnnotationVisitor visitAnnotation(String name, String desc) {
201212 AnnotationValue newValue = new AnnotationValue(desc);
202213 result.add(newValue);
203214 return newValue.getAnnotationVisitor();
204215 }
205216
217 @Override
206218 public AnnotationVisitor visitArray(String name) {
207219 return new AnnotationArrayVisitor(result);
208220 }
209221
222 @Override
210223 public void visitEnd() {
211 if (name != null)
224 if (name != null) {
212225 valueMap.put(name, result.toArray());
213 else
226 } else {
214227 outerList.add(result.toArray());
215 }
216
228 }
229 }
230
231 @Override
217232 public void visitEnum(String name, String desc, String value) {
218233 result.add(new EnumValue(desc, value));
219234
2626
2727 /**
2828 * The data (bytes) of a class.
29 *
29 *
3030 * @author David Hovemeyer
3131 */
3232 public class ClassData {
3838
3939 /**
4040 * Constructor.
41 *
41 *
4242 * @param classDescriptor
4343 * descriptor for the class
4444 * @param data
7373
7474 /**
7575 * Open an InputStream on the class data.
76 *
76 *
7777 * @return InputStream reading from the class data
7878 */
7979 public InputStream getInputStream() {
8080 return new ByteArrayInputStream(data);
8181 }
8282
83
83
8484 @Override
8585 public String toString() {
8686 return codeBaseEntry + ":" + classDescriptor;
3030
3131 import javax.annotation.CheckForNull;
3232
33 import edu.umd.cs.findbugs.SystemProperties;
3334 import edu.umd.cs.findbugs.ba.AnalysisContext;
3435 import edu.umd.cs.findbugs.ba.XClass;
3536 import edu.umd.cs.findbugs.ba.XField;
5354 *
5455 * @author David Hovemeyer
5556 */
56 public class ClassInfo extends ClassNameAndSuperclassInfo implements XClass, AnnotatedObject {
57 public class ClassInfo extends ClassNameAndSuperclassInfo implements XClass {
58
59 private final static boolean DEBUG = SystemProperties.getBoolean("ci.debug");
5760 private final FieldInfo[] xFields;
5861
5962 private final MethodInfo[] xMethods;
7174 private final boolean usesConcurrency;
7275
7376 private final boolean hasStubs;
77
78 @CheckForNull
79 AnnotatedObject containingScope;
80
81 private boolean containingScopeCached;
7482
7583 public static class Builder extends ClassNameAndSuperclassInfo.Builder {
7684 private List<FieldInfo> fieldInfoList = new LinkedList<FieldInfo>();
93101 boolean usesConcurrency;
94102
95103 boolean hasStubs;
104
105 private static String arguments(String signature) {
106 int i = signature.indexOf('(');
107 if (i == -1) {
108 return signature;
109 }
110 return signature.substring(0,i+1);
111 }
96112
97113 @Override
98114 public ClassInfo build() {
99115 AnalysisContext context = AnalysisContext.currentAnalysisContext();
100116 FieldInfo fields[];
101117 MethodInfo methods[];
102 if (fieldInfoList.size() == 0)
118 if (fieldInfoList.size() == 0) {
103119 fields = FieldInfo.EMPTY_ARRAY;
104 else
120 } else {
105121 fields = fieldInfoList.toArray(new FieldInfo[fieldInfoList.size()]);
122 }
123
124
125 for(MethodInfo m : methodInfoList) {
126 if (m.isBridge() && !bridgedSignatures.containsKey(m)) {
127
128 if (DEBUG) {
129 System.out.println("Have bridge method:" + m);
130 }
131 for(MethodInfo to : methodInfoList) {
132 if (m != to) {
133 if (!to.isBridge()
134 && m.getName().equals(to.getName())
135 && arguments( m.getSignature()).equals(arguments(to.getSignature()))) {
136 if (DEBUG) {
137 System.out.println(" to method:" + to);
138 }
139 bridgedSignatures.put(m, to.getSignature());
140 }
141
142 }
143 } // end for(MethodInfo to
144 } // end if (m.isBridge()
145 } // end for(MethodInfo m : methodInfoList)
146
147
106148
107149 for (Map.Entry<MethodInfo, String> e : bridgedSignatures.entrySet()) {
108150 MethodInfo method = e.getKey();
109151 String signature = e.getValue();
110 for (MethodInfo m : methodInfoList)
152 for (MethodInfo m : methodInfoList) {
111153 if (m.getName().equals(method.getName()) && m.getSignature().equals(signature)) {
154 if (DEBUG) {
155 System.out.println("Found bridge method:");
156 System.out.println(" " + method);
157 if (!method.getAnnotations().isEmpty()) {
158 System.out.println(" " + method.getAnnotations());
159 }
160 System.out.println(" " + m);
161 if (!m.getAnnotations().isEmpty()) {
162 System.out.println(" " + m.getAnnotations());
163 }
164 }
112165 context.setBridgeMethod(method, m);
113166
114167 }
115
116 }
117
118 if (methodInfoList.size() == 0)
168 }
169
170 } // end for
171
172 if (methodInfoList.size() == 0) {
119173 methods = MethodInfo.EMPTY_ARRAY;
120 else
174 } else {
121175 methods = methodInfoList.toArray(new MethodInfo[methodInfoList.size()]);
176 }
122177
123178 return new ClassInfo(classDescriptor, classSourceSignature, superclassDescriptor, interfaceDescriptorList,
124179 codeBaseEntry, accessFlags, source, majorVersion, minorVersion, referencedClassDescriptorList,
130185 this.source = source;
131186 }
132187
133 /**
134 * @return Returns the classDescriptor.
135 */
136188 public ClassDescriptor getClassDescriptor() {
137189 return classDescriptor;
138190 }
146198 classAnnotations.put(annotationClass, value);
147199 }
148200
149 /**
150 * @param fieldDescriptorList
151 * The fieldDescriptorList to set.
152 */
153201 public void setFieldDescriptorList(FieldInfo[] fieldDescriptorList) {
154202 this.fieldInfoList = Arrays.asList(fieldDescriptorList);
155203 }
158206 fieldInfoList.add(field);
159207 }
160208
161 /**
162 * @param methodDescriptorList
163 * The methodDescriptorList to set.
164 */
165209 public void setMethodDescriptorList(MethodInfo[] methodDescriptorList) {
166210 this.methodInfoList = Arrays.asList(methodDescriptorList);
167211 }
170214 methodInfoList.add(method);
171215 }
172216
173 public void addBridgeMethodDescriptor(MethodInfo method, String bridgedSignature) {
217 public void addBridgeMethodDescriptor(MethodInfo from, String bridgedSignature) {
174218 if (bridgedSignature != null) {
175 bridgedSignatures.put(method, bridgedSignature);
176 }
177 addMethodDescriptor(method);
178 }
179
180 /**
181 * @param immediateEnclosingClass
182 * The immediateEnclosingClass to set.
183 */
219 bridgedSignatures.put(from, bridgedSignature);
220 }
221 addMethodDescriptor(from);
222 }
223
184224 public void setImmediateEnclosingClass(ClassDescriptor immediateEnclosingClass) {
185225 this.immediateEnclosingClass = immediateEnclosingClass;
186226 }
204244 final MultiMap<MethodInfo, MethodInfo> multiMap = SelfMethodCalls.getSelfCalls(getClassDescriptor(), map);
205245 OutEdges2<MethodInfo> edges1 = new OutEdges2<MethodInfo>() {
206246
247 @Override
207248 public Collection<MethodInfo> getOutEdges(MethodInfo method) {
208249 return multiMap.get(method);
209250 }
210251
252 @Override
211253 public int score(MethodInfo e) {
212254 return e.getMethodCallCount();
213255 }
232274 * @param referencedClassDescriptorList
233275 * ClassDescriptors of all classes/interfaces referenced by the
234276 * class
235 * @param calledClassDescriptors
236 * TODO
237277 * @param fieldDescriptorList
238278 * FieldDescriptors of fields defined in the class
239279 * @param methodInfoList
240280 * MethodDescriptors of methods defined in the class
241 * @param usesConcurrency
242 * TODO
243 * @param hasStubs
244 * TODO
245281 */
246282 private ClassInfo(ClassDescriptor classDescriptor, String classSourceSignature, ClassDescriptor superclassDescriptor,
247283 ClassDescriptor[] interfaceDescriptorList, ICodeBaseEntry codeBaseEntry, int accessFlags, String source,
253289 referencedClassDescriptorList, calledClassDescriptors, majorVersion, minorVersion);
254290 this.source = source;
255291 this.classSourceSignature = classSourceSignature;
256 if (fieldDescriptorList.length == 0)
292 if (fieldDescriptorList.length == 0) {
257293 fieldDescriptorList = FieldInfo.EMPTY_ARRAY;
294 }
258295 this.xFields = fieldDescriptorList;
259296 this.xMethods = methodInfoList;
260297 this.immediateEnclosingClass = immediateEnclosingClass;
262299 this.usesConcurrency = usesConcurrency;
263300 this.hasStubs = hasStubs;
264301 this.methodsInCallOrder = computeMethodsInCallOrder();
302 /*
265303 if (false) {
266304 System.out.println("Methods in call order for " + classDescriptor);
267305 for (MethodInfo m : methodsInCallOrder) {
269307 }
270308 System.out.println();
271309 }
272 }
273
274 /**
275 * @return Returns the fieldDescriptorList.
276 */
310 */
311 }
312
313 @Override
277314 public List<? extends XField> getXFields() {
278315 return Arrays.asList(xFields);
279316 }
280317
281 /**
282 * @return Returns the methodDescriptorList.
283 */
318 @Override
284319 public List<? extends XMethod> getXMethods() {
285320 return Arrays.asList(xMethods);
286321 }
287322
288 /**
289 * @return Returns the methodDescriptorList.
290 */
291323 public List<? extends XMethod> getXMethodsInCallOrder() {
292324 return Arrays.asList(methodsInCallOrder);
293325 }
294326
295 /*
296 * (non-Javadoc)
297 *
298 * @see edu.umd.cs.findbugs.ba.XClass#findMethod(java.lang.String,
299 * java.lang.String, boolean)
300 */
327 @Override
301328 public XMethod findMethod(String methodName, String methodSig, boolean isStatic) {
302329 int hash = FieldOrMethodDescriptor.getNameSigHashCode(methodName, methodSig);
303 for (MethodInfo mInfo : xMethods)
330 for (MethodInfo mInfo : xMethods) {
304331 if (mInfo.getNameSigHashCode() == hash && mInfo.getName().equals(methodName)
305 && mInfo.getSignature().equals(methodSig) && mInfo.isStatic() == isStatic)
332 && mInfo.getSignature().equals(methodSig) && mInfo.isStatic() == isStatic) {
306333 return mInfo;
334 }
335 }
307336 return null;
308337 }
309338
310 /*
311 * (non-Javadoc)
312 *
313 * @see
314 * edu.umd.cs.findbugs.ba.XClass#findMethod(edu.umd.cs.findbugs.classfile
315 * .MethodDescriptor)
316 */
339 @Override
317340 public XMethod findMethod(MethodDescriptor descriptor) {
318341 if (!descriptor.getClassDescriptor().equals(this)) {
319342 throw new IllegalArgumentException();
321344 return findMatchingMethod(descriptor);
322345 }
323346
347 @Override
324348 public XMethod findMatchingMethod(MethodDescriptor descriptor) {
325349 return findMethod(descriptor.getName(), descriptor.getSignature(), descriptor.isStatic());
326350 }
327351
328 /*
329 * (non-Javadoc)
330 *
331 * @see edu.umd.cs.findbugs.ba.XClass#findField(java.lang.String,
332 * java.lang.String, boolean)
333 */
352 @Override
334353 public XField findField(String name, String signature, boolean isStatic) {
335354 int hash = FieldOrMethodDescriptor.getNameSigHashCode(name, signature);
336 for (FieldInfo fInfo : xFields)
355 for (FieldInfo fInfo : xFields) {
337356 if (fInfo.getNameSigHashCode() == hash && fInfo.getName().equals(name) && fInfo.getSignature().equals(signature)
338 && fInfo.isStatic() == isStatic)
357 && fInfo.isStatic() == isStatic) {
339358 return fInfo;
359 }
360 }
340361 try {
341 if (getSuperclassDescriptor() == null)
362 if (getSuperclassDescriptor() == null) {
342363 return null;
364 }
343365 XClass superClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, getSuperclassDescriptor());
344366 XField result = superClass.findField(name, signature, isStatic);
345 if (result != null)
367 if (result != null) {
346368 return result;
347 if (!isStatic)
369 }
370 if (!isStatic) {
348371 return null;
372 }
349373 ClassDescriptor[] interfaces = getInterfaceDescriptorList();
350374 for (ClassDescriptor implementedInterface : interfaces) {
351375 superClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, implementedInterface);
352376 result = superClass.findField(name, signature, isStatic);
353 if (result != null)
377 if (result != null) {
354378 return result;
379 }
355380 }
356381 return null;
357382 } catch (CheckedAnalysisException e) {
359384 }
360385 }
361386
362 /**
363 * @return Returns the immediateEnclosingClass.
364 */
387 @Override
365388 public ClassDescriptor getImmediateEnclosingClass() {
366389 return immediateEnclosingClass;
367390 }
368391
369 /*
370 * (non-Javadoc)
371 *
372 * @see edu.umd.cs.findbugs.ba.AccessibleEntity#getPackageName()
373 */
374392 @Override
375393 public String getPackageName() {
376394 String dottedClassName = getClassDescriptor().toDottedClassName();
382400 }
383401 }
384402
385 /*
386 * (non-Javadoc)
387 *
388 * @see edu.umd.cs.findbugs.ba.AccessibleEntity#getPackageName()
389 */
390
391403 public String getSlashedPackageName() {
392404 String slashedClassName = getClassDescriptor().getClassName();
393405 int lastSlash = slashedClassName.lastIndexOf('/');
398410 }
399411 }
400412
413 @Override
401414 public Collection<ClassDescriptor> getAnnotationDescriptors() {
402415 return classAnnotations.keySet();
403416 }
404417
418 @Override
405419 public Collection<AnnotationValue> getAnnotations() {
406420 return classAnnotations.values();
407421 }
408422
423 @Override
409424 public AnnotationValue getAnnotation(ClassDescriptor desc) {
410425 return classAnnotations.get(desc);
411426 }
427442 classAnnotations = Util.immutableMap(updatedMap);
428443 }
429444
445 @Override
430446 public ElementType getElementType() {
431 if (getClassName().endsWith("package-info"))
447 if (getClassName().endsWith("package-info")) {
432448 return ElementType.PACKAGE;
433 else if (isAnnotation())
449 } else if (isAnnotation()) {
434450 return ElementType.ANNOTATION_TYPE;
451 }
435452 return ElementType.TYPE;
436453
437454 }
438455
456 @Override
439457 public @CheckForNull
440458 String getSource() {
441459 return source;
442460 }
443461
444 @CheckForNull
445 AnnotatedObject containingScope;
446
447 private boolean containingScopeCached = false;
448
462 @Override
449463 public @CheckForNull
450464 AnnotatedObject getContainingScope() {
451465 if (!containingScopeCached) {
471485 }
472486 }
473487
474 /*
475 * (non-Javadoc)
476 *
477 * @see edu.umd.cs.findbugs.ba.XClass#getSourceSignature()
478 */
488 @Override
479489 public String getSourceSignature() {
480490 return classSourceSignature;
481491 }
482492
493 @Override
483494 public boolean usesConcurrency() {
484495 return usesConcurrency;
485496 }
486497
498 @Override
487499 public boolean hasStubs() {
488500 return hasStubs;
489501 }
122122 * The referencedClassDescriptorList to set.
123123 */
124124 public void setReferencedClassDescriptors(Collection<ClassDescriptor> referencedClassDescriptorList) {
125 if (referencedClassDescriptorList.size() == 0)
125 if (referencedClassDescriptorList.size() == 0) {
126126 this.referencedClassDescriptorList = Collections.emptyList();
127 else
127 } else {
128128 this.referencedClassDescriptorList = new ArrayList<ClassDescriptor>(referencedClassDescriptorList);
129 }
129130 }
130131
131132 public void setCalledClassDescriptors(Collection<ClassDescriptor> calledClassDescriptorList) {
132 if (calledClassDescriptorList.size() == 0)
133 if (calledClassDescriptorList.size() == 0) {
133134 this.calledClassDescriptors = Collections.emptySet();
134 else
135 } else {
135136 this.calledClassDescriptors = new HashSet<ClassDescriptor>(calledClassDescriptorList);
137 }
136138 }
137139 }
138140
147149 this.interfaceDescriptorList = interfaceDescriptorList;
148150 this.codeBaseEntry = codeBaseEntry;
149151 this.accessFlags = accessFlags;
150 if (calledClassDescriptors == null)
152 if (calledClassDescriptors == null) {
151153 throw new NullPointerException("calledClassDescriptors must not be null");
154 }
152155 this.calledClassDescriptors = calledClassDescriptors;
153156 this.majorVersion = majorVersion;
154157 this.minorVersion = minorVersion;
2323
2424 /**
2525 * Represents an enumeration value used with an application of an annotation.
26 *
26 *
2727 * @author William Pugh
2828 * @author David Hovemeyer
2929 */
118118 return checkFlag(Constants.ACC_SYNCHRONIZED);
119119 }
120120
121 @Override
121122 public boolean isDeprecated() {
122123 return checkFlag(Opcodes.ACC_DEPRECATED);
123124 }
124125
126 @Override
125127 public @DottedClassName
126128 String getClassName() {
127129 return getClassDescriptor().toDottedClassName();
128130 }
129131
132 @Override
130133 public @DottedClassName
131134 String getPackageName() {
132135 return getClassDescriptor().getPackageName();
133136 }
134137
138 @Override
135139 public String getSourceSignature() {
136140 return fieldSourceSignature;
137141 }
138142
139143 /*
140144 * (non-Javadoc)
141 *
145 *
142146 * @see java.lang.Comparable#compareTo(java.lang.Object)
143147 */
144148 @Override
156160
157161 /*
158162 * (non-Javadoc)
159 *
163 *
160164 * @see edu.umd.cs.findbugs.ba.AccessibleEntity#getAccessFlags()
161165 */
166 @Override
162167 public int getAccessFlags() {
163168 return accessFlags;
164169 }
165170
166171 /*
167172 * (non-Javadoc)
168 *
173 *
169174 * @see edu.umd.cs.findbugs.ba.AccessibleEntity#isFinal()
170175 */
176 @Override
171177 public boolean isFinal() {
172178 return checkFlag(Constants.ACC_FINAL);
173179 }
174180
175181 /*
176182 * (non-Javadoc)
177 *
183 *
178184 * @see edu.umd.cs.findbugs.ba.AccessibleEntity#isPrivate()
179185 */
186 @Override
180187 public boolean isPrivate() {
181188 return checkFlag(Constants.ACC_PRIVATE);
182189 }
183190
184191 /*
185192 * (non-Javadoc)
186 *
193 *
187194 * @see edu.umd.cs.findbugs.ba.AccessibleEntity#isProtected()
188195 */
196 @Override
189197 public boolean isProtected() {
190198 return checkFlag(Constants.ACC_PROTECTED);
191199 }
192200
193201 /*
194202 * (non-Javadoc)
195 *
203 *
196204 * @see edu.umd.cs.findbugs.ba.AccessibleEntity#isPublic()
197205 */
206 @Override
198207 public boolean isPublic() {
199208 return checkFlag(Constants.ACC_PUBLIC);
200209 }
201210
202211 /*
203212 * (non-Javadoc)
204 *
213 *
205214 * @see edu.umd.cs.findbugs.ba.AccessibleEntity#isResolved()
206215 */
216 @Override
207217 public boolean isResolved() {
208218 return this.isResolved;
209219 }
210220
211221 /*
212222 * (non-Javadoc)
213 *
223 *
214224 * @see edu.umd.cs.findbugs.ba.XField#isReferenceType()
215225 */
226 @Override
216227 public boolean isReferenceType() {
217228 return getSignature().startsWith("L") || getSignature().startsWith("[");
218229 }
219230
220231 /*
221232 * (non-Javadoc)
222 *
233 *
223234 * @see edu.umd.cs.findbugs.ba.XField#isVolatile()
224235 */
236 @Override
225237 public boolean isVolatile() {
226238 return checkFlag(Constants.ACC_VOLATILE);
227239 }
228240
241 @Override
229242 public boolean isSynthetic() {
230243 return checkFlag(Constants.ACC_SYNTHETIC);
231244 }
232245
246 @Override
233247 public Collection<ClassDescriptor> getAnnotationDescriptors() {
234248 return fieldAnnotations.keySet();
235249 }
236250
251 @Override
237252 public AnnotationValue getAnnotation(ClassDescriptor desc) {
238253 return fieldAnnotations.get(desc);
239254 }
240255
256 @Override
241257 public Collection<AnnotationValue> getAnnotations() {
242258 return fieldAnnotations.values();
243259 }
247263 * that might not be directly evident in the code. It's not a great idea in
248264 * general, but we can get away with it as long as it's done early enough
249265 * (i.e., before anyone asks what annotations this field has.)
250 *
266 *
251267 * @param annotationValue
252268 * an AnnotationValue representing a field annotation
253269 */
261277
262278 /*
263279 * (non-Javadoc)
264 *
280 *
265281 * @see edu.umd.cs.findbugs.ba.XField#getFieldDescriptor()
266282 */
283 @Override
267284 public FieldDescriptor getFieldDescriptor() {
268285 return this;
269286 }
271288 /**
272289 * Create a FieldInfo object to represent an unresolved field.
273290 * <em>Don't call this directly - use XFactory instead.</em>
274 *
291 *
275292 * @param className
276293 * name of class containing the field
277294 * @param name
285302 public static FieldInfo createUnresolvedFieldInfo(String className, String name, String signature, boolean isStatic) {
286303 className = ClassName.toSlashedClassName(className);
287304 return new FieldInfo(className, name, signature, null, // without seeing
288 // the definition
289 // we don't know
290 // if it has a
291 // generic type
305 // the definition
306 // we don't know
307 // if it has a
308 // generic type
292309 isStatic ? Constants.ACC_STATIC : 0, new HashMap<ClassDescriptor, AnnotationValue>(), false);
293310 }
294311
312 @Override
295313 public ElementType getElementType() {
296314 return ElementType.FIELD;
297315 }
298316
317 @Override
299318 public @CheckForNull
300319 AnnotatedObject getContainingScope() {
301320 try {
5757 public static final MethodInfo[] EMPTY_ARRAY = new MethodInfo[0];
5858
5959 public static MethodInfo[] newArray(int sz) {
60 if (sz == 0)
60 if (sz == 0) {
6161 return EMPTY_ARRAY;
62 }
6263 return new MethodInfo[sz];
6364 }
6465
8990
9091 boolean isIdentity;
9192
93 boolean usesInvokeDynamic;
94
9295 int methodCallCount;
9396
9497 MethodDescriptor accessMethodForMethod;
99102 final Map<Integer, Map<ClassDescriptor, AnnotationValue>> methodParameterAnnotations = new HashMap<Integer, Map<ClassDescriptor, AnnotationValue>>(
100103 4);
101104
105 @Override
106 public String toString() {
107 return "builder for " + className + "." + methodName + methodSignature;
108 }
102109 public Builder(@SlashedClassName String className, String methodName, String methodSignature, int accessFlags) {
103110 this.className = className;
104111 this.methodName = methodName;
118125 }
119126
120127 public void setVariableHasName(int p) {
121 if (p < 64)
128 if (p < 64) {
122129 variableHasName |= 1 << p;
130 }
123131 }
124132
125133 public void setVariableIsSynthetic(int p) {
126 if (p < 64)
134 if (p < 64) {
127135 variableIsSynthetic |= 1 << p;
136 }
128137 }
129138
130139 public void setUsesConcurrency() {
176185 }
177186
178187 public MethodInfo build() {
179 if (variableHasName != 0)
188 if (variableHasName != 0) {
180189 variableIsSynthetic |= (~variableHasName);
190 }
181191 return new MethodInfo(className, methodName, methodSignature, methodSourceSignature, accessFlags,
182192 isUnconditionalThrower, isUnsupported, usesConcurrency, hasBackBranch, isStub, isIdentity,
183 methodCallCount, exceptions, accessMethodForMethod, accessMethodForField,
184 methodAnnotations, methodParameterAnnotations, variableIsSynthetic);
193 usesInvokeDynamic, methodCallCount, exceptions, accessMethodForMethod,
194 accessMethodForField, methodAnnotations, methodParameterAnnotations, variableIsSynthetic);
185195 }
186196
187197 public void setIsUnconditionalThrower() {
201211 this.methodCallCount = methodCallCount;
202212
203213 }
214
215 /**
216 *
217 */
218 public void setUsesInvokeDynamic() {
219 usesInvokeDynamic = true;
220 }
204221 }
205222
206223 final int accessFlags;
225242 Map<Integer, Map<ClassDescriptor, AnnotationValue>> methodParameterAnnotations;
226243
227244 public static class MethodInfoDatabase {
228 final IdentityHashMap<MethodInfo, Void> unconditionalThrowers = new IdentityHashMap<MethodInfo, Void>();
229 final IdentityHashMap<MethodInfo, Void> unsupportedMethods = new IdentityHashMap<MethodInfo, Void>();
230 final IdentityHashMap<MethodInfo, MethodDescriptor> accessMethodForMethod = new IdentityHashMap<MethodInfo, MethodDescriptor>();
231 final IdentityHashMap<MethodInfo, FieldDescriptor> accessMethodForField = new IdentityHashMap<MethodInfo, FieldDescriptor>();
232 final IdentityHashMap<MethodInfo, Void> identityMethods = new IdentityHashMap<MethodInfo, Void>();
233
234 }
235
236 /**
245 final IdentityHashMap<MethodInfo, Void> unconditionalThrowers = new IdentityHashMap<MethodInfo, Void>();
246 final IdentityHashMap<MethodInfo, Void> unsupportedMethods = new IdentityHashMap<MethodInfo, Void>();
247 final IdentityHashMap<MethodInfo, MethodDescriptor> accessMethodForMethod = new IdentityHashMap<MethodInfo, MethodDescriptor>();
248 final IdentityHashMap<MethodInfo, FieldDescriptor> accessMethodForField = new IdentityHashMap<MethodInfo, FieldDescriptor>();
249 final IdentityHashMap<MethodInfo, Void> identityMethods = new IdentityHashMap<MethodInfo, Void>();
250 final IdentityHashMap<MethodInfo, Void> invokeDynamicMethods = new IdentityHashMap<MethodInfo, Void>();
251
252 }
253
254 /**
237255 * @return Returns the database.
238256 */
239257 static MethodInfoDatabase getDatabase() {
257275
258276 static IdentityHashMap<MethodInfo, Void> getIdentitymethods() {
259277 return getDatabase().identityMethods;
278 }
279 static public IdentityHashMap<MethodInfo, Void> getInvokeDynamicMethods() {
280 return getDatabase().invokeDynamicMethods;
260281 }
261282
262283 MethodInfo(@SlashedClassName String className, String methodName, String methodSignature, String methodSourceSignature,
263284 int accessFlags, boolean isUnconditionalThrower, boolean isUnsupported, boolean usesConcurrency,
264285 boolean hasBackBranch, boolean isStub, boolean isIdentity,
265 int methodCallCount, @CheckForNull String[] exceptions, @CheckForNull MethodDescriptor accessMethodForMethod,
286 boolean usesInvokeDynamic, int methodCallCount, @CheckForNull String[] exceptions,
287 @CheckForNull MethodDescriptor accessMethodForMethod,
266288 @CheckForNull FieldDescriptor accessMethodForField,
267 Map<ClassDescriptor, AnnotationValue> methodAnnotations,
268 Map<Integer, Map<ClassDescriptor, AnnotationValue>> methodParameterAnnotations, long variableIsSynthetic) {
289 Map<ClassDescriptor, AnnotationValue> methodAnnotations, Map<Integer, Map<ClassDescriptor, AnnotationValue>> methodParameterAnnotations, long variableIsSynthetic) {
269290 super(className, methodName, methodSignature, (accessFlags & Constants.ACC_STATIC) != 0);
270291 this.accessFlags = accessFlags;
271292 this.exceptions = exceptions;
272 if (exceptions != null)
273 for (int i = 0; i < exceptions.length; i++)
293 if (exceptions != null) {
294 for (int i = 0; i < exceptions.length; i++) {
274295 exceptions[i] = DescriptorFactory.canonicalizeString(exceptions[i]);
296 }
297 }
275298 this.methodSourceSignature = DescriptorFactory.canonicalizeString(methodSourceSignature);
276299 this.methodAnnotations = Util.immutableMap(methodAnnotations);
277300 this.methodParameterAnnotations = Util.immutableMap(methodParameterAnnotations);
278 if (isUnconditionalThrower)
301 if (isUnconditionalThrower) {
279302 getUnconditionalthrowers().put(this, null);
280 if (isUnsupported)
303 }
304 if (isUnsupported) {
281305 getUnconditionalthrowers().put(this, null);
282 if (accessMethodForMethod != null)
306 }
307 if (accessMethodForMethod != null) {
283308 getAccessmethodformethod().put(this, accessMethodForMethod);
284 if (accessMethodForField!= null)
309 }
310 if (accessMethodForField!= null) {
285311 getAccessmethodforfield().put(this, accessMethodForField);
312 }
286313 if (isIdentity) {
287314 getIdentitymethods().put(this, null);
315 }
316 if (usesInvokeDynamic) {
317 getInvokeDynamicMethods().put(this, null);
288318 }
289319
290320 this.usesConcurrency = usesConcurrency;
294324 this.variableIsSynthetic = variableIsSynthetic;
295325 }
296326
327 @Override
297328 public @CheckForNull
298329 String[] getThrownExceptions() {
299330 return exceptions;
301332
302333
303334
335 @Override
304336 public boolean isUnconditionalThrower() {
305337 return getUnconditionalthrowers().containsKey(this);
306338 }
307339
340 @Override
308341 public boolean isIdentity() {
309342 return getIdentitymethods().containsKey(this);
310343 }
311344
345 @Override
346 public boolean usesInvokeDynamic() {
347 return getInvokeDynamicMethods().containsKey(this);
348 }
349
350
351 @Override
312352 public boolean isUnsupported() {
313353 return getUnsupportedmethods().containsKey(this);
314354 }
315355
356 @Override
316357 public int getNumParams() {
317358 return new SignatureParser(getSignature()).getNumParameters();
318359 }
319360
361 @Override
320362 public boolean isVariableSynthetic(int param) {
321 if (param >= 64) return false;
363 if (param >= 64) {
364 return false;
365 }
322366 return (variableIsSynthetic & (1 << param)) != 0;
323367 }
324368
330374 return (accessFlags & flag) != 0;
331375 }
332376
377 @Override
333378 public boolean isNative() {
334379 return checkFlag(Constants.ACC_NATIVE);
335380 }
336381
382 @Override
337383 public boolean isAbstract() {
338384 return checkFlag(Constants.ACC_ABSTRACT);
339385 }
340386
387 @Override
341388 public boolean isSynchronized() {
342389 return checkFlag(Constants.ACC_SYNCHRONIZED);
343390 }
344391
392 @Override
393 public boolean isBridge() {
394 return checkFlag(Constants.ACC_BRIDGE);
395 }
396
345397 /*
346398 * (non-Javadoc)
347399 *
348400 * @see edu.umd.cs.findbugs.ba.XMethod#isReturnTypeReferenceType()
349401 */
402 @Override
350403 public boolean isReturnTypeReferenceType() {
351404 SignatureParser parser = new SignatureParser(getSignature());
352405 String returnTypeSig = parser.getReturnTypeSignature();
353406 return SignatureParser.isReferenceType(returnTypeSig);
354407 }
355408
409 @Override
356410 public @DottedClassName
357411 String getClassName() {
358412 return getClassDescriptor().toDottedClassName();
359413 }
360414
415 @Override
361416 public @DottedClassName
362417 String getPackageName() {
363418 return getClassDescriptor().getPackageName();
364419 }
365420
421 @Override
366422 public String getSourceSignature() {
367423 return methodSourceSignature;
368424 }
378434 return FieldOrMethodDescriptor.compareTo(this, (MethodDescriptor) rhs);
379435 }
380436
381 if (rhs instanceof XMethod) {
437 if (rhs instanceof XMethod) {
382438 return XFactory.compare((XMethod) this, (XMethod) rhs);
383439 }
384440
390446 *
391447 * @see edu.umd.cs.findbugs.ba.AccessibleEntity#getAccessFlags()
392448 */
449 @Override
393450 public int getAccessFlags() {
394451 return accessFlags;
395452 }
399456 *
400457 * @see edu.umd.cs.findbugs.ba.AccessibleEntity#isFinal()
401458 */
459 @Override
402460 public boolean isFinal() {
403461 return checkFlag(Constants.ACC_FINAL);
404462 }
408466 *
409467 * @see edu.umd.cs.findbugs.ba.AccessibleEntity#isPrivate()
410468 */
469 @Override
411470 public boolean isPrivate() {
412471 return checkFlag(Constants.ACC_PRIVATE);
413472 }
414473
474 @Override
415475 public boolean isDeprecated() {
416476 return checkFlag(Opcodes.ACC_DEPRECATED);
417477 }
421481 *
422482 * @see edu.umd.cs.findbugs.ba.AccessibleEntity#isProtected()
423483 */
484 @Override
424485 public boolean isProtected() {
425486 return checkFlag(Constants.ACC_PROTECTED);
426487 }
430491 *
431492 * @see edu.umd.cs.findbugs.ba.AccessibleEntity#isPublic()
432493 */
494 @Override
433495 public boolean isPublic() {
434496 return checkFlag(Constants.ACC_PUBLIC);
435497 }
436498
499 @Override
437500 public boolean isSynthetic() {
438501 return checkFlag(Constants.ACC_SYNTHETIC);
439502 }
443506 *
444507 * @see edu.umd.cs.findbugs.ba.AccessibleEntity#isResolved()
445508 */
509 @Override
446510 public boolean isResolved() {
447511 return true;
448512 }
449513
514 @Override
450515 public Collection<ClassDescriptor> getParameterAnnotationDescriptors(int param) {
451516 Map<ClassDescriptor, AnnotationValue> map = methodParameterAnnotations.get(param);
452 if (map == null)
517 if (map == null) {
453518 return Collections.<ClassDescriptor> emptySet();
519 }
454520 return map.keySet();
455521 }
456522
523 @Override
457524 public boolean hasParameterAnnotations() {
458525 return !methodParameterAnnotations.isEmpty();
459526 }
460
527
528 @Override
461529 public @Nullable
462530 AnnotationValue getParameterAnnotation(int param, ClassDescriptor desc) {
463531 Map<ClassDescriptor, AnnotationValue> map = methodParameterAnnotations.get(param);
464 if (map == null)
532 if (map == null) {
465533 return null;
534 }
466535 return map.get(desc);
467536 }
468537
538 @Override
469539 public Collection<AnnotationValue> getParameterAnnotations(int param) {
470540 Map<ClassDescriptor, AnnotationValue> map = methodParameterAnnotations.get(param);
471 if (map == null)
541 if (map == null) {
472542 return Collections.<AnnotationValue> emptySet();
543 }
473544 return map.values();
474545 }
475546
547 @Override
476548 public Collection<ClassDescriptor> getAnnotationDescriptors() {
477549 return methodAnnotations.keySet();
478550 }
479551
552 @Override
480553 public AnnotationValue getAnnotation(ClassDescriptor desc) {
481554 return methodAnnotations.get(desc);
482555 }
483556
557 @Override
484558 public Collection<AnnotationValue> getAnnotations() {
485 return methodAnnotations.values();
559
560 Collection<AnnotationValue> result = methodAnnotations.values();
561 if (result.isEmpty() && isBridge()) {
562 XMethod to = bridgeTo();
563 if (to != null) {
564 result = to.getAnnotations();
565 }
566 }
567 return result;
486568 }
487569
488570 /**
494576 * @param annotationValue
495577 * an AnnotationValue representing a method annotation
496578 */
579 @Override
497580 public void addAnnotation(AnnotationValue annotationValue) {
498581 HashMap<ClassDescriptor, AnnotationValue> updatedAnnotations = new HashMap<ClassDescriptor, AnnotationValue>(
499582 methodAnnotations);
510593 * @param annotationValue
511594 * an AnnotationValue representing a parameter annotation
512595 */
596 @Override
513597 public void addParameterAnnotation(int param, AnnotationValue annotationValue) {
514598 HashMap<Integer, Map<ClassDescriptor, AnnotationValue>> updatedAnnotations = new HashMap<Integer, Map<ClassDescriptor, AnnotationValue>>(
515599 methodParameterAnnotations);
529613 *
530614 * @see edu.umd.cs.findbugs.ba.XMethod#getMethodDescriptor()
531615 */
616 @Override
532617 public MethodDescriptor getMethodDescriptor() {
533618 return this;
534619 }
535620
621 @Override
536622 public ElementType getElementType() {
537 if (getName().equals("<init>"))
623 if ("<init>".equals(getName())) {
538624 return ElementType.CONSTRUCTOR;
625 }
539626 return ElementType.METHOD;
540627 }
541628
629 @Override
542630 public @CheckForNull
543631 AnnotatedObject getContainingScope() {
544632 try {
553641 *
554642 * @see edu.umd.cs.findbugs.ba.XMethod#isVarArgs()
555643 */
644 @Override
556645 public boolean isVarArgs() {
557646 return checkFlag(Constants.ACC_VARARGS);
558647 }
562651 *
563652 * @see edu.umd.cs.findbugs.ba.XMethod#usesConcurrency()
564653 */
654 @Override
565655 public boolean usesConcurrency() {
566656 return usesConcurrency;
567657 }
570660 public boolean hasBackBranch() {
571661 return hasBackBranch;
572662 }
663 @Override
573664 public boolean isStub() {
574665 return isStub;
575666 }
576667
668 @Override
577669 public @CheckForNull
578670 MethodDescriptor getAccessMethodForMethod() {
579671 return getAccessmethodformethod().get(this);
580672 }
673 @Override
581674 public @CheckForNull
582675 FieldDescriptor getAccessMethodForField() {
583676 return getAccessmethodforfield().get(this);
588681 *
589682 * @see edu.umd.cs.findbugs.ba.XMethod#bridgeFrom()
590683 */
684 @Override
591685 public XMethod bridgeFrom() {
592686 return AnalysisContext.currentAnalysisContext().getBridgeFrom(this);
593687 }
597691 *
598692 * @see edu.umd.cs.findbugs.ba.XMethod#bridgeTo()
599693 */
694 @Override
600695 public XMethod bridgeTo() {
601696 return AnalysisContext.currentAnalysisContext().getBridgeTo(this);
602697
603698 }
604699
700 @Override
605701 public XMethod resolveAccessMethodForMethod() {
606702 MethodDescriptor access = getAccessMethodForMethod();
607 if (access != null)
703 if (access != null) {
608704 return XFactory.createXMethod(access);
705 }
609706 return this;
610707 }
611708 }
2121 import org.objectweb.asm.Attribute;
2222 import org.objectweb.asm.FieldVisitor;
2323
24 import edu.umd.cs.findbugs.classfile.engine.asm.FindBugsASM;
25
2426 /**
2527 * @author pwilliam
2628 */
27 public abstract class AbstractFieldAnnotationVisitor implements FieldVisitor {
29 public abstract class AbstractFieldAnnotationVisitor extends FieldVisitor {
30
31 public AbstractFieldAnnotationVisitor() {
32 super(FindBugsASM.ASM_VERSION);
33 }
2834
2935 /*
3036 * (non-Javadoc)
31 *
37 *
3238 * @see
3339 * org.objectweb.asm.FieldVisitor#visitAttribute(org.objectweb.asm.Attribute
3440 * )
3541 */
42 @Override
3643 public void visitAttribute(Attribute attr) {
3744 // TODO Auto-generated method stub
3845
2323 import org.objectweb.asm.Label;
2424 import org.objectweb.asm.MethodVisitor;
2525
26 import edu.umd.cs.findbugs.classfile.engine.asm.FindBugsASM;
27
2628 /**
2729 * @author pwilliam
2830 */
29 public abstract class AbstractMethodVisitor implements MethodVisitor {
31 public abstract class AbstractMethodVisitor extends MethodVisitor {
32
33
34 public AbstractMethodVisitor() {
35 super(FindBugsASM.ASM_VERSION);
36 }
3037
3138 public void visitSomeInsn() {
3239 }
3340
3441 /*
3542 * (non-Javadoc)
36 *
43 *
3744 * @see org.objectweb.asm.MethodVisitor#visitAnnotationDefault()
3845 */
46 @Override
3947 public AnnotationVisitor visitAnnotationDefault() {
4048 // TODO Auto-generated method stub
4149 return null;
4351
4452 /*
4553 * (non-Javadoc)
46 *
54 *
4755 * @see
4856 * org.objectweb.asm.MethodVisitor#visitAttribute(org.objectweb.asm.Attribute
4957 * )
5058 */
59 @Override
5160 public void visitAttribute(Attribute attr) {
5261 // TODO Auto-generated method stub
5362 }
5463
5564 /*
5665 * (non-Javadoc)
57 *
66 *
5867 * @see org.objectweb.asm.MethodVisitor#visitCode()
5968 */
69 @Override
6070 public void visitCode() {
6171 // TODO Auto-generated method stub
6272
6474
6575 /*
6676 * (non-Javadoc)
67 *
77 *
6878 * @see org.objectweb.asm.MethodVisitor#visitFieldInsn(int,
6979 * java.lang.String, java.lang.String, java.lang.String)
7080 */
81 @Override
7182 public void visitFieldInsn(int opcode, String owner, String name, String desc) {
7283 visitSomeInsn();
7384
7586
7687 /*
7788 * (non-Javadoc)
78 *
89 *
7990 * @see org.objectweb.asm.MethodVisitor#visitFrame(int, int,
8091 * java.lang.Object[], int, java.lang.Object[])
8192 */
93 @Override
8294 public void visitFrame(int type, int local, Object[] local2, int stack, Object[] stack2) {
8395 // TODO Auto-generated method stub
8496
8698
8799 /*
88100 * (non-Javadoc)
89 *
101 *
90102 * @see org.objectweb.asm.MethodVisitor#visitIincInsn(int, int)
91103 */
104 @Override
92105 public void visitIincInsn(int var, int increment) {
93106 visitSomeInsn();
94107
96109
97110 /*
98111 * (non-Javadoc)
99 *
112 *
100113 * @see org.objectweb.asm.MethodVisitor#visitInsn(int)
101114 */
115 @Override
102116 public void visitInsn(int opcode) {
103117 visitSomeInsn();
104118
106120
107121 /*
108122 * (non-Javadoc)
109 *
123 *
110124 * @see org.objectweb.asm.MethodVisitor#visitIntInsn(int, int)
111125 */
126 @Override
112127 public void visitIntInsn(int opcode, int operand) {
113128 visitSomeInsn();
114129
116131
117132 /*
118133 * (non-Javadoc)
119 *
134 *
120135 * @see org.objectweb.asm.MethodVisitor#visitJumpInsn(int,
121136 * org.objectweb.asm.Label)
122137 */
138 @Override
123139 public void visitJumpInsn(int opcode, Label label) {
124140 visitSomeInsn();
125141
127143
128144 /*
129145 * (non-Javadoc)
130 *
146 *
131147 * @see org.objectweb.asm.MethodVisitor#visitLabel(org.objectweb.asm.Label)
132148 */
149 @Override
133150 public void visitLabel(Label label) {
134151 // TODO Auto-generated method stub
135152
137154
138155 /*
139156 * (non-Javadoc)
140 *
157 *
141158 * @see org.objectweb.asm.MethodVisitor#visitLdcInsn(java.lang.Object)
142159 */
160 @Override
143161 public void visitLdcInsn(Object cst) {
144162 visitSomeInsn();
145163
147165
148166 /*
149167 * (non-Javadoc)
150 *
168 *
151169 * @see org.objectweb.asm.MethodVisitor#visitLineNumber(int,
152170 * org.objectweb.asm.Label)
153171 */
172 @Override
154173 public void visitLineNumber(int line, Label start) {
155174 // TODO Auto-generated method stub
156175
158177
159178 /*
160179 * (non-Javadoc)
161 *
180 *
162181 * @see org.objectweb.asm.MethodVisitor#visitLocalVariable(java.lang.String,
163182 * java.lang.String, java.lang.String, org.objectweb.asm.Label,
164183 * org.objectweb.asm.Label, int)
165184 */
185 @Override
166186 public void visitLocalVariable(String name, String desc, String signature, Label start, Label end, int index) {
167187 // TODO Auto-generated method stub
168188
170190
171191 /*
172192 * (non-Javadoc)
173 *
193 *
174194 * @see
175195 * org.objectweb.asm.MethodVisitor#visitLookupSwitchInsn(org.objectweb.asm
176196 * .Label, int[], org.objectweb.asm.Label[])
177197 */
198 @Override
178199 public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
179200 visitSomeInsn();
180201
182203
183204 /*
184205 * (non-Javadoc)
185 *
206 *
186207 * @see org.objectweb.asm.MethodVisitor#visitMaxs(int, int)
187208 */
209 @Override
188210 public void visitMaxs(int maxStack, int maxLocals) {
189211 // TODO Auto-generated method stub
190212
192214
193215 /*
194216 * (non-Javadoc)
195 *
217 *
196218 * @see org.objectweb.asm.MethodVisitor#visitMethodInsn(int,
197219 * java.lang.String, java.lang.String, java.lang.String)
198220 */
199 public void visitMethodInsn(int opcode, String owner, String name, String desc) {
200 visitSomeInsn();
201
202 }
203
204 /*
205 * (non-Javadoc)
206 *
221 @Override
222 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
223 visitSomeInsn();
224
225 }
226
227 /*
228 * (non-Javadoc)
229 *
207230 * @see
208231 * org.objectweb.asm.MethodVisitor#visitMultiANewArrayInsn(java.lang.String,
209232 * int)
210233 */
234 @Override
211235 public void visitMultiANewArrayInsn(String desc, int dims) {
212236 visitSomeInsn();
213237
215239
216240 /*
217241 * (non-Javadoc)
218 *
242 *
219243 * @see org.objectweb.asm.MethodVisitor#visitTableSwitchInsn(int, int,
220244 * org.objectweb.asm.Label, org.objectweb.asm.Label[])
221245 */
222 public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
223 // TODO Auto-generated method stub
224
225 }
226
227 /*
228 * (non-Javadoc)
229 *
246 @Override
247 public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
248 // TODO Auto-generated method stub
249
250 }
251
252 /*
253 * (non-Javadoc)
254 *
230255 * @see
231256 * org.objectweb.asm.MethodVisitor#visitTryCatchBlock(org.objectweb.asm.
232257 * Label, org.objectweb.asm.Label, org.objectweb.asm.Label,
233258 * java.lang.String)
234259 */
260 @Override
235261 public void visitTryCatchBlock(Label start, Label end, Label handler, String type) {
236262 // TODO Auto-generated method stub
237263
239265
240266 /*
241267 * (non-Javadoc)
242 *
268 *
243269 * @see org.objectweb.asm.MethodVisitor#visitTypeInsn(int, java.lang.String)
244270 */
271 @Override
245272 public void visitTypeInsn(int opcode, String type) {
246273 visitSomeInsn();
247274
249276
250277 /*
251278 * (non-Javadoc)
252 *
279 *
253280 * @see org.objectweb.asm.MethodVisitor#visitVarInsn(int, int)
254281 */
282 @Override
255283 public void visitVarInsn(int opcode, int var) {
256284 visitSomeInsn();
257285
4040 */
4141 public class ClassDataAnalysisEngine extends RecomputableClassAnalysisEngine<ClassData> {
4242
43 @Override
4344 public ClassData analyze(IAnalysisCache analysisCache, ClassDescriptor descriptor) throws CheckedAnalysisException {
4445
4546 // Compute the resource name
8081 return new ClassData(descriptor, codeBaseEntry, data);
8182 }
8283
84 @Override
8385 public void registerWith(IAnalysisCache analysisCache) {
8486 analysisCache.registerClassAnalysisEngine(ClassData.class, this);
8587 }
9294 this.descriptor = descriptor;
9395 }
9496
97 @Override
9598 public String getResourceName() {
9699 return descriptor.toResourceName();
97100 }
98101
102 @Override
99103 public int getNumBytes() {
100104 return -1;
101105 }
102106
107 @Override
103108 public InputStream openResource() throws IOException {
104109 InputStream stream = getClass().getClassLoader().getResourceAsStream(descriptor.toResourceName());
105110 if(stream == null){
108113 return stream;
109114 }
110115
116 @Override
111117 public ICodeBase getCodeBase() {
112118 return null;
113119 }
114120
121 @Override
115122 public ClassDescriptor getClassDescriptor() {
116123 return descriptor;
117124 }
118125
126 @Override
119127 public void overrideResourceName(String resourceName) {
120128 //
121129 }
3131 /**
3232 * Analysis engine to produce the ClassInfo for a loaded class. We parse just
3333 * enough information from the classfile to get the needed information.
34 *
34 *
3535 * @author David Hovemeyer
3636 */
3737 public class ClassInfoAnalysisEngine implements IClassAnalysisEngine<XClass> {
4444
4545 /*
4646 * (non-Javadoc)
47 *
47 *
4848 * @see
4949 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
5050 * .classfile.IAnalysisCache, java.lang.Object)
5151 */
52 @Override
5253 public ClassInfo analyze(IAnalysisCache analysisCache, ClassDescriptor descriptor) throws CheckedAnalysisException {
5354
54 if (descriptor instanceof ClassInfo)
55 if (descriptor instanceof ClassInfo) {
5556 return (ClassInfo) descriptor;
57 }
5658 ClassData classData;
5759 try {
5860 classData = analysisCache.getClassAnalysis(ClassData.class, descriptor);
5961 } catch (edu.umd.cs.findbugs.classfile.MissingClassException e) {
60 if (!descriptor.getSimpleName().equals("package-info"))
62 if (!"package-info".equals(descriptor.getSimpleName())) {
6163 throw e;
64 }
6265
6366 ClassInfo.Builder builder = new ClassInfo.Builder();
6467 builder.setClassDescriptor(descriptor);
8386
8487 /*
8588 * (non-Javadoc)
86 *
89 *
8790 * @see
8891 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#registerWith(edu.umd.cs
8992 * .findbugs.classfile.IAnalysisCache)
9093 */
94 @Override
9195 public void registerWith(IAnalysisCache analysisCache) {
9296 analysisCache.registerClassAnalysisEngine(XClass.class, this);
9397 }
9498
9599 /*
96100 * (non-Javadoc)
97 *
101 *
98102 * @see edu.umd.cs.findbugs.classfile.IAnalysisEngine#canRecompute()
99103 */
104 @Override
100105 public boolean canRecompute() {
101106 // ClassInfo objects serve as XClass objects,
102107 // which we want interned. So, they are never purged from the cache.
3232 /**
3333 * Analysis engine to produce the ClassInfo for a loaded class. We parse just
3434 * enough information from the classfile to get the needed information.
35 *
35 *
3636 * @author David Hovemeyer
3737 */
3838 public class ClassNameAndSuperclassInfoAnalysisEngine implements IClassAnalysisEngine<ClassNameAndSuperclassInfo> {
3939
4040 /*
4141 * (non-Javadoc)
42 *
42 *
4343 * @see
4444 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4545 * .classfile.IAnalysisCache, java.lang.Object)
4646 */
47 @Override
4748 public ClassNameAndSuperclassInfo analyze(IAnalysisCache analysisCache, ClassDescriptor descriptor)
4849 throws CheckedAnalysisException {
4950 // Get InputStream reading from class data
6465
6566 /*
6667 * (non-Javadoc)
67 *
68 *
6869 * @see
6970 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#registerWith(edu.umd.cs
7071 * .findbugs.classfile.IAnalysisCache)
7172 */
73 @Override
7274 public void registerWith(IAnalysisCache analysisCache) {
7375 analysisCache.registerClassAnalysisEngine(ClassNameAndSuperclassInfo.class, this);
7476 }
7577
7678 /*
7779 * (non-Javadoc)
78 *
80 *
7981 * @see edu.umd.cs.findbugs.classfile.IAnalysisEngine#canRecompute()
8082 */
83 @Override
8184 public boolean canRecompute() {
8285 // ClassInfo objects serve as XClass objects,
8386 // which we want interned. So, they are never purged from the cache.
2828
2929 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
3030 import edu.umd.cs.findbugs.classfile.DescriptorFactory;
31 import edu.umd.cs.findbugs.classfile.FieldDescriptor;
3231 import edu.umd.cs.findbugs.classfile.IClassConstants;
3332 import edu.umd.cs.findbugs.classfile.ICodeBaseEntry;
3433 import edu.umd.cs.findbugs.classfile.InvalidClassFileFormatException;
35 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
3634 import edu.umd.cs.findbugs.classfile.analysis.ClassInfo;
3735 import edu.umd.cs.findbugs.classfile.analysis.ClassNameAndSuperclassInfo;
3836 import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName;
39 import edu.umd.cs.findbugs.io.IO;
4037 import edu.umd.cs.findbugs.util.ClassName;
4138
4239 /**
6966
7067 private Constant[] constantPool;
7168
72 private ClassDescriptor immediateEnclosingClass;
69 // private ClassDescriptor immediateEnclosingClass;
7370
7471 /**
7572 * Constructor.
8784 this.codeBaseEntry = codeBaseEntry;
8885 }
8986
90 /*
91 * (non-Javadoc)
92 *
93 * @see
94 * edu.umd.cs.findbugs.classfile.engine.ClassParserInterface#parse(edu.umd
95 * .cs.findbugs.classfile.analysis.ClassNameAndSuperclassInfo.Builder)
96 */
87 @Override
9788 public void parse(ClassNameAndSuperclassInfo.Builder builder) throws InvalidClassFileFormatException {
9889 try {
9990 int magic = in.readInt();
100 if (magic != 0xcafebabe)
91 if (magic != 0xcafebabe) {
10192 throw new InvalidClassFileFormatException("Classfile header isn't 0xCAFEBABE", expectedClassDescriptor,
10293 codeBaseEntry);
94 }
10395 int major_version = in.readUnsignedShort();
10496 int minor_version = in.readUnsignedShort();
10597 int constant_pool_count = in.readUnsignedShort();
128120 throw new InvalidClassFileFormatException(expectedClassDescriptor, codeBaseEntry);
129121 }
130122 ClassDescriptor[] interfaceDescriptorList;
131 if (interfaces_count == 0)
123 if (interfaces_count == 0) {
132124 interfaceDescriptorList = ClassDescriptor.EMPTY_ARRAY;
133 else {
125 } else {
134126 interfaceDescriptorList = new ClassDescriptor[interfaces_count];
135 for (int i = 0; i < interfaceDescriptorList.length; i++)
127 for (int i = 0; i < interfaceDescriptorList.length; i++) {
136128 interfaceDescriptorList[i] = getClassDescriptor(in.readUnsignedShort());
129 }
137130 }
138131 // Extract all references to other classes,
139132 // both CONSTANT_Class entries and also referenced method
152145 }
153146 }
154147
155 /*
156 * (non-Javadoc)
157 *
158 * @see
159 * edu.umd.cs.findbugs.classfile.engine.ClassParserInterface#parse(edu.umd
160 * .cs.findbugs.classfile.analysis.ClassInfo.Builder)
161 */
148 @Override
162149 public void parse(ClassInfo.Builder builder) throws InvalidClassFileFormatException {
163150 throw new UnsupportedOperationException("Need to use a ClassParserUsingASM to build ClassInfo");
164151 }
197184 return referencedClassSet;
198185 }
199186
200 /**
201 * @param referencedClassSet
202 * @param signature
203 */
204187 public static void extractReferencedClassesFromSignature(Set<ClassDescriptor> referencedClassSet, String signature) {
205188 while (signature.length() > 0) {
206189 int start = signature.indexOf('L');
227210 // D: double
228211 // i: 2-byte constant pool index
229212 private static final String[] CONSTANT_FORMAT_MAP = { null,
230 "8", // 1:CONSTANT_Utf8
231 null, // 2:
232 "I", // 3: CONSTANT_Integer
233 "F", // 4: CONSTANT_Float
234 "L", // 5: CONSTANT_Long
235 "D", // 6: CONSTANT_Double
236 "i", // 7: CONSTANT_Class
237 "i", // 8: CONSTANT_String
238 "ii", // 9: CONSTANT_Fieldref
239 "ii", // 10: CONSTANT_Methodref
240 "ii", // 11: CONSTANT_InterfaceMethodref
241 "ii", // 12: CONSTANT_NameAndType
242 null, // 13:
243 null, // 14:
244 "bi", // 15: CONSTANT_MethodHandle
245 "i", // 16: CONSTANT_MethodType
246 null, // 17:
247 "ii", // 18: CONSTANT_InvokeDynamic
213 "8", // 1:CONSTANT_Utf8
214 null, // 2:
215 "I", // 3: CONSTANT_Integer
216 "F", // 4: CONSTANT_Float
217 "L", // 5: CONSTANT_Long
218 "D", // 6: CONSTANT_Double
219 "i", // 7: CONSTANT_Class
220 "i", // 8: CONSTANT_String
221 "ii", // 9: CONSTANT_Fieldref
222 "ii", // 10: CONSTANT_Methodref
223 "ii", // 11: CONSTANT_InterfaceMethodref
224 "ii", // 12: CONSTANT_NameAndType
225 null, // 13:
226 null, // 14:
227 "bi", // 15: CONSTANT_MethodHandle
228 "i", // 16: CONSTANT_MethodType
229 null, // 17:
230 "ii", // 18: CONSTANT_InvokeDynamic
248231
249232 };
250233
276259 data[i] = in.readInt();
277260 break;
278261 case 'F':
279 data[i] = new Float(in.readFloat());
262 data[i] = Float.valueOf(in.readFloat());
280263 break;
281264 case 'L':
282265 data[i] = in.readLong();
283266 break;
284267 case 'D':
285 data[i] = new Double(in.readDouble());
268 data[i] = Double.valueOf(in.readDouble());
286269 break;
287270 case 'i':
288271 data[i] = in.readUnsignedShort();
355338 /**
356339 * Check that a constant pool index is valid.
357340 *
358 * @param expectedClassDescriptor
359 * class descriptor
360 * @param constantPool
361 * the constant pool
362341 * @param index
363342 * the index to check
364343 * @throws InvalidClassFileFormatException
398377 * @return the FieldDescriptor
399378 * @throws IOException
400379 * @throws InvalidClassFileFormatException
401 */
380 *
402381 private FieldDescriptor readField(ClassDescriptor thisClassDescriptor) throws IOException, InvalidClassFileFormatException {
403382 return readFieldOrMethod(thisClassDescriptor, new FieldOrMethodDescriptorCreator<FieldDescriptor>() {
404 /*
405 * (non-Javadoc)
406 *
407 * @see edu.umd.cs.findbugs.classfile.engine.ClassParser.
408 * FieldOrMethodDescriptorCreator#create(java.lang.String,
409 * java.lang.String, java.lang.String, int)
410 */
383 @Override
411384 public FieldDescriptor create(String className, String name, String signature, int accessFlags) {
412385 return DescriptorFactory.instance().getFieldDescriptor(className, name, signature,
413386 (accessFlags & IClassConstants.ACC_STATIC) != 0);
414387 }
415388 });
416 }
389 }*/
417390
418391 /**
419392 * Read method_info, read method descriptor.
420393 *
421 * @param thisClassDescriptor
422 * @return
423 * @throws IOException
424 * @throws InvalidClassFileFormatException
425 */
426394 private MethodDescriptor readMethod(ClassDescriptor thisClassDescriptor) throws InvalidClassFileFormatException, IOException {
427395 return readFieldOrMethod(thisClassDescriptor, new FieldOrMethodDescriptorCreator<MethodDescriptor>() {
428 /*
429 * (non-Javadoc)
430 *
431 * @see edu.umd.cs.findbugs.classfile.engine.ClassParser.
432 * FieldOrMethodDescriptorCreator#create(java.lang.String,
433 * java.lang.String, java.lang.String, int)
434 */
396
397 @Override
435398 public MethodDescriptor create(String className, String name, String signature, int accessFlags) {
436399 return DescriptorFactory.instance().getMethodDescriptor(className, name, signature,
437400 (accessFlags & IClassConstants.ACC_STATIC) != 0);
438401 }
439402 });
440 }
403 }*/
441404
442405 /**
443406 * Read field_info or method_info. They have the same format.
451414 * @return the parsed descriptor
452415 * @throws IOException
453416 * @throws InvalidClassFileFormatException
454 */
417 *
455418 private <E> E readFieldOrMethod(ClassDescriptor thisClassDescriptor, FieldOrMethodDescriptorCreator<E> creator)
456419 throws IOException, InvalidClassFileFormatException {
457420 int access_flags = in.readUnsignedShort();
469432 }
470433
471434 return creator.create(thisClassDescriptor.getClassName(), name, signature, access_flags);
472 }
435 }*/
473436
474437 /**
475438 * Read an attribute.
476439 *
477440 * @throws IOException
478441 * @throws InvalidClassFileFormatException
479 */
442 *
480443 private void readAttribute() throws IOException, InvalidClassFileFormatException {
481444 int attribute_name_index = in.readUnsignedShort();
482445 String attrName = getUtf8String(attribute_name_index);
491454 } else {
492455 IO.skipFully(in, attribute_length);
493456 }
494 }
457 }*/
495458
496459 /**
497460 * Read an InnerClasses attribute.
500463 * length of attribute (excluding first 6 bytes)
501464 * @throws InvalidClassFileFormatException
502465 * @throws IOException
503 */
466 *
504467 private void readInnerClassesAttribute(int attribute_length) throws InvalidClassFileFormatException, IOException {
505468 int number_of_classes = in.readUnsignedShort();
506469 if (attribute_length != number_of_classes * 8) {
513476 int inner_name_index = in.readUnsignedShort();
514477 int inner_class_access_flags = in.readUnsignedShort();
515478
516 if (outer_class_info_index != 0) {
517 // Record which class this class is a member of.
518 this.immediateEnclosingClass = getClassDescriptor(outer_class_info_index);
519 }
520 }
521 }
479 // if (outer_class_info_index != 0) {
480 // // Record which class this class is a member of.
481 // this.immediateEnclosingClass = getClassDescriptor(outer_class_info_index);
482 // }
483 }
484 }*/
522485
523486 /**
524487 * Get the signature from a CONSTANT_NameAndType.
2626 * Interface implemented by ClassParsers - objects that take the raw bytes of a
2727 * classfile and parse the important symbolic information to produce
2828 * ClassNameAndSuperclassInfo and ClassInfo objects.
29 *
29 *
3030 * @author Bill Pugh
3131 * @author David Hovemeyer
3232 */
3535 /**
3636 * Parse the class data into a ClassNameAndSuperclassInfo object containing
3737 * (some of) the class's symbolic information.
38 *
38 *
3939 * @param classInfo
4040 * a ClassNameAndSuperclassInfo object to be filled in with (some
4141 * of) the class's symbolic information
4646 /**
4747 * Parse the class data into a ClassInfo object containing (some of) the
4848 * class's symbolic information.
49 *
49 *
5050 * @param classInfo
5151 * a ClassInfo object to be filled in with (some of) the class's
5252 * symbolic information
2929 import org.objectweb.asm.ClassReader;
3030 import org.objectweb.asm.ClassVisitor;
3131 import org.objectweb.asm.FieldVisitor;
32 import org.objectweb.asm.Handle;
3233 import org.objectweb.asm.Label;
3334 import org.objectweb.asm.MethodVisitor;
3435 import org.objectweb.asm.Opcodes;
4445 import edu.umd.cs.findbugs.classfile.analysis.ClassNameAndSuperclassInfo;
4546 import edu.umd.cs.findbugs.classfile.analysis.FieldInfo;
4647 import edu.umd.cs.findbugs.classfile.analysis.MethodInfo;
48 import edu.umd.cs.findbugs.classfile.engine.asm.FindBugsASM;
4749 import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName;
4850 import edu.umd.cs.findbugs.util.ClassName;
4951
7072 private @SlashedClassName
7173 String slashedClassName;
7274
73 private final ClassDescriptor expectedClassDescriptor;
75 // private final ClassDescriptor expectedClassDescriptor;
7476
7577 private final ICodeBaseEntry codeBaseEntry;
7678
7779
80
81 /**
82 * @author pugh
83 */
84 private final class ClassParserMethodVisitor extends AbstractMethodVisitor {
85 /**
86 *
87 */
88 private final TreeSet<ClassDescriptor> calledClassSet;
89
90 /**
91 *
92 */
93 private final edu.umd.cs.findbugs.classfile.analysis.MethodInfo.Builder mBuilder;
94
95 /**
96 *
97 */
98 private final String methodName;
99
100 /**
101 *
102 */
103 private final int access;
104
105 /**
106 *
107 */
108 private final String methodDesc;
109
110 /**
111 *
112 */
113 private final edu.umd.cs.findbugs.classfile.analysis.ClassNameAndSuperclassInfo.Builder cBuilder;
114
115 boolean sawReturn;
116
117 boolean sawNormalThrow = false;
118
119 boolean sawUnsupportedThrow = false;
120
121 boolean sawSystemExit = false;
122
123 boolean sawBranch = false;
124
125 boolean sawBackBranch = false;
126
127 int methodCallCount = 0;
128
129 int fieldInstructionCount = 0;
130
131 boolean sawStubThrow = false;
132
133 boolean justSawInitializationOfUnsupportedOperationException;
134
135 boolean isBridge;
136
137 String bridgedMethodSignature;
138
139 IdentityMethodState identityState =
140 IdentityMethodState.INITIAL;
141
142 ParameterLoadState parameterLoadState = ParameterLoadState.OTHER;
143
144 int parameterForLoadState;
145
146 StubState stubState = StubState.INITIAL;
147
148 boolean isAccessMethod;
149
150 String accessOwner, accessName, accessDesc;
151
152 boolean accessForField;
153
154 boolean accessIsStatic;
155
156 HashSet<Label> labelsSeen = new HashSet<Label>();
157
158 /**
159 * @param calledClassSet
160 * @param mBuilder
161 * @param methodName
162 * @param access
163 * @param methodDesc
164 * @param cBuilder
165 */
166 private ClassParserMethodVisitor(TreeSet<ClassDescriptor> calledClassSet,
167 edu.umd.cs.findbugs.classfile.analysis.MethodInfo.Builder mBuilder, String methodName, int access,
168 String methodDesc, edu.umd.cs.findbugs.classfile.analysis.ClassNameAndSuperclassInfo.Builder cBuilder) {
169 this.calledClassSet = calledClassSet;
170 this.mBuilder = mBuilder;
171 this.methodName = methodName;
172 this.access = access;
173 this.methodDesc = methodDesc;
174 this.cBuilder = cBuilder;
175 sawReturn = (access & Opcodes.ACC_NATIVE) != 0;
176 isBridge = (access & Opcodes.ACC_SYNTHETIC) != 0 && (access & Opcodes.ACC_BRIDGE) != 0;
177 isAccessMethod = methodName.startsWith("access$");
178 }
179
180 boolean isStatic() {
181 return (access & Opcodes.ACC_STATIC) != 0;
182 }
183
184 @Override
185 public
186 void visitLocalVariable(String name,
187 String desc,
188 String signature,
189 Label start,
190 Label end,
191 int index) {
192 mBuilder.setVariableHasName(index);
193
194 }
195
196 @Override
197 public void visitLdcInsn(Object cst) {
198 if (cst.equals("Stub!")) {
199 stubState = StubState.LOADED_STUB;
200 } else {
201 stubState = StubState.INITIAL;
202 }
203 identityState = IdentityMethodState.NOT;
204 }
205
206 @Override
207 public void visitInsn(int opcode) {
208 switch (opcode) {
209 case Constants.MONITORENTER:
210 mBuilder.setUsesConcurrency();
211 break;
212 case Constants.ARETURN:
213 case Constants.IRETURN:
214 case Constants.LRETURN:
215 case Constants.DRETURN:
216 case Constants.FRETURN:
217 if (identityState == IdentityMethodState.LOADED_PARAMETER) {
218 mBuilder.setIsIdentity();
219 }
220 sawReturn = true;
221 break;
222 case Constants.RETURN:
223 sawReturn = true;
224 break;
225 case Constants.ATHROW:
226 if (stubState == StubState.INITIALIZE_RUNTIME) {
227 sawStubThrow = true;
228 } else if (justSawInitializationOfUnsupportedOperationException) {
229 sawUnsupportedThrow = true;
230 } else {
231 sawNormalThrow = true;
232 }
233 break;
234 default:
235 break;
236 }
237
238 resetState();
239 }
240
241 public void resetState() {
242 stubState = StubState.INITIAL;
243 }
244
245 @Override
246 public void visitSomeInsn() {
247 identityState = IdentityMethodState.NOT;
248 parameterLoadState = ParameterLoadState.OTHER;
249 resetState();
250 }
251
252 @Override
253 public void visitVarInsn(int opcode, int var) {
254
255 boolean match = false;
256 if (parameterLoadState == ParameterLoadState.OTHER && !isStatic() && var == 0) {
257 parameterLoadState = ParameterLoadState.LOADED_THIS;
258
259 match = true;
260 }
261 else if (parameterLoadState == ParameterLoadState.LOADED_THIS && var > 0){
262 parameterLoadState = ParameterLoadState.LOADED_THIS_AND_PARAMETER;
263 parameterForLoadState = var;
264 match = true;
265 }
266
267 if (identityState == IdentityMethodState.INITIAL) {
268 match = true;
269 if (var > 0 || isStatic()) {
270 identityState = IdentityMethodState.LOADED_PARAMETER;
271 } else {
272 identityState = IdentityMethodState.NOT;
273 }
274
275 }
276 if (!match) {
277 visitSomeInsn();
278 }
279 }
280
281 @Override
282 public void visitFieldInsn(int opcode,
283 String owner,
284 String name,
285 String desc) {
286 if (opcode == Opcodes.PUTFIELD && parameterLoadState == ParameterLoadState.LOADED_THIS_AND_PARAMETER
287 && owner.equals(slashedClassName) && name.startsWith("this$")) {
288 mBuilder.setVariableIsSynthetic(parameterForLoadState);
289 }
290 fieldInstructionCount++;
291
292 if (isAccessMethod && this.accessOwner == null) {
293 this.accessOwner = owner;
294 this.accessName = name;
295 this.accessDesc = desc;
296 this.accessIsStatic = opcode == Opcodes.GETSTATIC || opcode == Opcodes.PUTSTATIC;
297 this.accessForField = true;
298 }
299 visitSomeInsn();
300 }
301
302 @Override
303 public org.objectweb.asm.AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
304 AnnotationValue value = new AnnotationValue(desc);
305 mBuilder.addAnnotation(desc, value);
306 return value.getAnnotationVisitor();
307 }
308
309 @Override
310 public void visitInvokeDynamicInsn(String name, String desc, Handle bsm,
311 Object... bsmArgs) {
312 mBuilder.setUsesInvokeDynamic();
313 }
314
315 @Override
316 public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) {
317 identityState = IdentityMethodState.NOT;
318 methodCallCount++;
319 if (isAccessMethod && this.accessOwner == null) {
320 this.accessOwner = owner;
321 this.accessName = name;
322 this.accessDesc = desc;
323 this.accessIsStatic = opcode == Opcodes.INVOKESTATIC;
324 this.accessForField = false;
325 }
326 if (stubState == StubState.LOADED_STUB && opcode == Opcodes.INVOKESPECIAL
327 && "java/lang/RuntimeException".equals(owner) && "<init>".equals(name)) {
328 stubState = StubState.INITIALIZE_RUNTIME;
329 } else {
330 stubState = StubState.INITIAL;
331 }
332 if (owner.startsWith("java/util/concurrent")) {
333 mBuilder.setUsesConcurrency();
334 }
335 if (opcode == Opcodes.INVOKEINTERFACE) {
336 return;
337 }
338
339 if (owner.charAt(0) == '[' && owner.charAt(owner.length() - 1) != ';') {
340 // primitive array
341 return;
342 }
343 if (opcode == Opcodes.INVOKESTATIC && "java/lang/System".equals(owner) && "exit".equals(name)
344 && !sawReturn) {
345 sawSystemExit = true;
346 }
347 justSawInitializationOfUnsupportedOperationException = opcode == Opcodes.INVOKESPECIAL
348 && "java/lang/UnsupportedOperationException".equals(owner) && "<init>".equals(name);
349
350 if (isBridge && bridgedMethodSignature == null) {
351 switch (opcode) {
352 case Opcodes.INVOKEVIRTUAL:
353 case Opcodes.INVOKESPECIAL:
354 case Opcodes.INVOKESTATIC:
355 case Opcodes.INVOKEINTERFACE:
356 if (desc != null && name.equals(methodName)) {
357 bridgedMethodSignature = desc;
358 }
359 break;
360 default:
361 break;
362 }
363 }
364
365 // System.out.println("Call from " +
366 // ClassParserUsingASM.this.slashedClassName +
367 // " to " + owner + " : " + desc);
368 if (desc == null || desc.indexOf('[') == -1 && desc.indexOf('L') == -1) {
369 return;
370 }
371 if (ClassParserUsingASM.this.slashedClassName.equals(owner)) {
372 return;
373 }
374 ClassDescriptor classDescriptor = DescriptorFactory.instance().getClassDescriptor(owner);
375 calledClassSet.add(classDescriptor);
376
377 }
378
379 private void sawBranchTo(Label label) {
380 sawBranch = true;
381 if (labelsSeen.contains(label)) {
382 sawBackBranch = true;
383 }
384 }
385
386 @Override
387 public void visitJumpInsn(int opcode, Label label) {
388 sawBranchTo(label);
389 identityState = IdentityMethodState.NOT;
390 super.visitJumpInsn(opcode, label);
391 }
392
393 @Override
394 public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
395 sawBranchTo(dflt);
396 for (Label lbl : labels) {
397 sawBranchTo(lbl);
398 }
399 identityState = IdentityMethodState.NOT;
400 super.visitLookupSwitchInsn(dflt, keys, labels);
401 }
402
403 @Override
404 public void visitTableSwitchInsn(int min, int max, Label dflt, Label... labels) {
405 sawBranchTo(dflt);
406 for (Label lbl : labels) {
407 sawBranchTo(lbl);
408 }
409 identityState = IdentityMethodState.NOT;
410 super.visitTableSwitchInsn(min, max, dflt, labels);
411 }
412
413 @Override
414 public void visitLabel(Label label) {
415 labelsSeen.add(label);
416 super.visitLabel(label);
417
418 }
419
420 @Override
421 public void visitEnd() {
422 labelsSeen.clear();
423 if (isAccessMethod && accessOwner != null) {
424 if (!accessForField && methodCallCount == 1) {
425 mBuilder.setAccessMethodForMethod(accessOwner, accessName, accessDesc, accessIsStatic);
426 } else if(accessForField && fieldInstructionCount == 1) {
427 boolean isSetter = methodDesc.endsWith(")V");
428 int numArg = new SignatureParser(methodDesc).getNumParameters();
429 int expected = 0;
430 if (!accessIsStatic) {
431 expected++;
432 }
433 if (isSetter) {
434 expected++;
435 }
436 boolean OK;
437 if (isSetter) {
438 OK = methodDesc.substring(1).startsWith(ClassName.toSignature(accessOwner) + accessDesc);
439 } else {
440 OK = methodDesc.substring(1).startsWith(ClassName.toSignature(accessOwner));
441 }
442 if (numArg == expected && OK) {
443 mBuilder.setAccessMethodForField(accessOwner, accessName, accessDesc, accessIsStatic);
444 }
445 }
446 }
447 if (sawBackBranch) {
448 mBuilder.setHasBackBranch();
449 }
450 boolean sawThrow = sawNormalThrow | sawUnsupportedThrow | sawStubThrow;
451 if (sawThrow && !sawReturn || sawSystemExit && !sawBranch) {
452
453 mBuilder.setIsUnconditionalThrower();
454 if (!sawReturn && !sawNormalThrow) {
455 if (sawUnsupportedThrow) {
456 mBuilder.setUnsupported();
457 }
458 if (sawStubThrow) {
459 mBuilder.addAccessFlags(Constants.ACC_SYNTHETIC);
460 mBuilder.setIsStub();
461
462 }
463 }
464 // else
465 // System.out.println(slashedClassName+"."+methodName+methodDesc
466 // + " is thrower");
467 }
468 mBuilder.setNumberMethodCalls(methodCallCount);
469 MethodInfo methodInfo = mBuilder.build();
470 Builder classBuilder = (ClassInfo.Builder) cBuilder;
471 if (isBridge && bridgedMethodSignature != null && !bridgedMethodSignature.equals(methodDesc)) {
472 classBuilder.addBridgeMethodDescriptor(methodInfo, bridgedMethodSignature);
473 } else {
474 classBuilder.addMethodDescriptor(methodInfo);
475 }
476
477 if (methodInfo.usesConcurrency()) {
478 classBuilder.setUsesConcurrency();
479 }
480 if (methodInfo.isStub()) {
481 classBuilder.setHasStubs();
482 }
483 }
484
485 @Override
486 public org.objectweb.asm.AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
487 boolean visible) {
488 AnnotationValue value = new AnnotationValue(desc);
489 mBuilder.addParameterAnnotation(parameter, desc, value);
490 return value.getAnnotationVisitor();
491 }
492 }
78493
79494 enum StubState {
80495 INITIAL, LOADED_STUB, INITIALIZE_RUNTIME
89504 public ClassParserUsingASM(ClassReader classReader, @CheckForNull ClassDescriptor expectedClassDescriptor,
90505 ICodeBaseEntry codeBaseEntry) {
91506 this.classReader = classReader;
92 this.expectedClassDescriptor = expectedClassDescriptor;
507 // this.expectedClassDescriptor = expectedClassDescriptor;
93508 this.codeBaseEntry = codeBaseEntry;
94509 }
95510
96 /*
97 * (non-Javadoc)
98 *
99 * @see
100 * edu.umd.cs.findbugs.classfile.engine.ClassParserInterface#parse(edu.umd
101 * .cs.findbugs.classfile.analysis.ClassNameAndSuperclassInfo.Builder)
102 */
511 @Override
103512 public void parse(final ClassNameAndSuperclassInfo.Builder cBuilder) throws InvalidClassFileFormatException {
104513
105514 cBuilder.setCodeBaseEntry(codeBaseEntry);
106515
107516 final TreeSet<ClassDescriptor> calledClassSet = new TreeSet<ClassDescriptor>();
108517
109 classReader.accept(new ClassVisitor() {
110
111 boolean isInnerClass = false;
112
518 classReader.accept(new ClassVisitor(FindBugsASM.ASM_VERSION) {
519
520 // boolean isInnerClass = false;
521
522 @Override
113523 public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
114524 ClassParserUsingASM.this.slashedClassName = name;
115525 cBuilder.setClassfileVersion(version >>> 16, version & 0xffff);
116526 cBuilder.setAccessFlags(access);
117527 cBuilder.setClassDescriptor(DescriptorFactory.createClassDescriptor(name));
118528 cBuilder.setInterfaceDescriptorList(DescriptorFactory.createClassDescriptor(interfaces));
119 if (superName != null)
529 if (superName != null) {
120530 cBuilder.setSuperclassDescriptor(DescriptorFactory.createClassDescriptor(superName));
531 }
121532 if (cBuilder instanceof ClassInfo.Builder) {
122533 ((ClassInfo.Builder) cBuilder).setSourceSignature(signature);
123534 }
124535 }
125536
537 @Override
126538 public org.objectweb.asm.AnnotationVisitor visitAnnotation(String desc, boolean isVisible) {
127539 if (cBuilder instanceof ClassInfo.Builder) {
128540 AnnotationValue value = new AnnotationValue(desc);
132544 return null;
133545 }
134546
547 @Override
135548 public void visitAttribute(Attribute arg0) {
136 // TODO Auto-generated method stub
137
138 }
139
549 //
550 }
551
552 @Override
140553 public void visitEnd() {
141 // TODO Auto-generated method stub
142
143 }
144
554 //
555 }
556
557 @Override
145558 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
146 if (name.equals("this$0"))
147 isInnerClass = true;
148
149 if (desc == null)
559 // if (name.equals("this$0"))
560 // isInnerClass = true;
561
562 if (desc == null) {
150563 throw new NullPointerException("Description cannot be null");
564 }
151565 if (cBuilder instanceof ClassInfo.Builder) {
152566 final ClassInfo.Builder cBuilder2 = (ClassInfo.Builder) cBuilder;
153 if ((access & Opcodes.ACC_VOLATILE) != 0 || desc.contains("util/concurrent"))
567 if ((access & Opcodes.ACC_VOLATILE) != 0 || desc.contains("util/concurrent")) {
154568 cBuilder2.setUsesConcurrency();
569 }
155570 final FieldInfo.Builder fBuilder = new FieldInfo.Builder(slashedClassName, name, desc, access);
156571 fBuilder.setSourceSignature(signature);
157572 return new AbstractFieldAnnotationVisitor() {
158573
574 @Override
159575 public org.objectweb.asm.AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
160576 AnnotationValue value = new AnnotationValue(desc);
161577 fBuilder.addAnnotation(desc, value);
162578 return value.getAnnotationVisitor();
163579 }
164580
581 @Override
165582 public void visitEnd() {
166583 cBuilder2.addFieldDescriptor(fBuilder.build());
167584
173590 return null;
174591 }
175592
593 @Override
176594 public void visitInnerClass(String name, String outerName, String innerName, int access) {
177595 if (name.equals(slashedClassName) && outerName != null) {
178596 if (cBuilder instanceof ClassInfo.Builder) {
185603
186604 }
187605
606 @Override
188607 public MethodVisitor visitMethod(final int access, final String methodName, final String methodDesc,
189608 String signature, String[] exceptions) {
190609 if (cBuilder instanceof ClassInfo.Builder) {
191610 final MethodInfo.Builder mBuilder = new MethodInfo.Builder(slashedClassName, methodName, methodDesc, access);
192611 mBuilder.setSourceSignature(signature);
193612 mBuilder.setThrownExceptions(exceptions);
194 if ((access & Opcodes.ACC_SYNCHRONIZED) != 0)
613 if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) {
195614 mBuilder.setUsesConcurrency();
196
197 return new AbstractMethodVisitor() {
198
199 boolean sawReturn = (access & Opcodes.ACC_NATIVE) != 0;
200
201 boolean sawNormalThrow = false;
202
203 boolean sawUnsupportedThrow = false;
204
205 boolean sawSystemExit = false;
206
207 boolean sawBranch = false;
208
209 boolean sawBackBranch = false;
210
211 int methodCallCount = 0;
212 int fieldInstructionCount = 0;
213
214 boolean sawStubThrow = false;
215
216 boolean justSawInitializationOfUnsupportedOperationException;
217
218 boolean isBridge = (access & Opcodes.ACC_SYNTHETIC) != 0 && (access & Opcodes.ACC_BRIDGE) != 0;
219
220 String bridgedMethodSignature;
221
222 IdentityMethodState identityState =
223 IdentityMethodState.INITIAL;
224
225 ParameterLoadState parameterLoadState = ParameterLoadState.OTHER;
226
227 int parameterForLoadState;
228
229 StubState stubState = StubState.INITIAL;
230
231 boolean isAccessMethod = methodName.startsWith("access$");
232
233 String accessOwner, accessName, accessDesc;
234
235 boolean accessForField;
236 boolean accessIsStatic;
237
238 HashSet<Label> labelsSeen = new HashSet<Label>();
239
240 boolean isStatic() {
241 return (access & Opcodes.ACC_STATIC) != 0;
242 }
243
244 @Override
245 public
246 void visitLocalVariable(String name,
247 String desc,
248 String signature,
249 Label start,
250 Label end,
251 int index) {
252 mBuilder.setVariableHasName(index);
253
254 }
255
256 @Override
257 public void visitLdcInsn(Object cst) {
258 if (cst.equals("Stub!"))
259 stubState = StubState.LOADED_STUB;
260 else
261 stubState = StubState.INITIAL;
262 identityState = IdentityMethodState.NOT;
263 }
264
265 @Override
266 public void visitInsn(int opcode) {
267 switch (opcode) {
268 case Constants.MONITORENTER:
269 mBuilder.setUsesConcurrency();
270 break;
271 case Constants.ARETURN:
272 case Constants.IRETURN:
273 case Constants.LRETURN:
274 case Constants.DRETURN:
275 case Constants.FRETURN:
276 if (identityState == IdentityMethodState.LOADED_PARAMETER)
277 mBuilder.setIsIdentity();
278 sawReturn = true;
279 break;
280 case Constants.RETURN:
281 sawReturn = true;
282 break;
283 case Constants.ATHROW:
284 if (stubState == StubState.INITIALIZE_RUNTIME)
285 sawStubThrow = true;
286 else if (justSawInitializationOfUnsupportedOperationException)
287 sawUnsupportedThrow = true;
288 else
289 sawNormalThrow = true;
290 break;
291 }
292
293 resetState();
294 }
295
296 public void resetState() {
297 stubState = StubState.INITIAL;
298 }
299
300 @Override
301 public void visitSomeInsn() {
302 identityState = IdentityMethodState.NOT;
303 parameterLoadState = ParameterLoadState.OTHER;
304 resetState();
305 }
306
307 @Override
308 public void visitVarInsn(int opcode, int var) {
309
310 boolean match = false;
311 if (parameterLoadState == ParameterLoadState.OTHER && !isStatic() && var == 0) {
312 parameterLoadState = ParameterLoadState.LOADED_THIS;
313
314 match = true;
315 }
316 else if (parameterLoadState == ParameterLoadState.LOADED_THIS && var > 0){
317 parameterLoadState = ParameterLoadState.LOADED_THIS_AND_PARAMETER;
318 parameterForLoadState = var;
319 match = true;
320 }
321
322 if (identityState == IdentityMethodState.INITIAL) {
323 match = true;
324 if (var > 0 || isStatic())
325 identityState = IdentityMethodState.LOADED_PARAMETER;
326 else
327 identityState = IdentityMethodState.NOT;
328
329 }
330 if (!match)
331 visitSomeInsn();
332 }
333
334 @Override
335 public void visitFieldInsn(int opcode,
336 String owner,
337 String name,
338 String desc) {
339 if (opcode == Opcodes.PUTFIELD && parameterLoadState == ParameterLoadState.LOADED_THIS_AND_PARAMETER
340 && owner.equals(slashedClassName) && name.startsWith("this$")) {
341 mBuilder.setVariableIsSynthetic(parameterForLoadState);
342 }
343 fieldInstructionCount++;
344
345 if (isAccessMethod && this.accessOwner == null) {
346 this.accessOwner = owner;
347 this.accessName = name;
348 this.accessDesc = desc;
349 this.accessIsStatic = opcode == Opcodes.GETSTATIC || opcode == Opcodes.PUTSTATIC;
350 this.accessForField = true;
351 }
352 visitSomeInsn();
353 }
354
355 public org.objectweb.asm.AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
356 AnnotationValue value = new AnnotationValue(desc);
357 mBuilder.addAnnotation(desc, value);
358 return value.getAnnotationVisitor();
359 }
360
361 @Override
362 public void visitMethodInsn(int opcode, String owner, String name, String desc) {
363 identityState = IdentityMethodState.NOT;
364 methodCallCount++;
365 if (isAccessMethod && this.accessOwner == null) {
366 this.accessOwner = owner;
367 this.accessName = name;
368 this.accessDesc = desc;
369 this.accessIsStatic = opcode == Opcodes.INVOKESTATIC;
370 this.accessForField = false;
371 }
372 if (stubState == StubState.LOADED_STUB && opcode == Opcodes.INVOKESPECIAL
373 && owner.equals("java/lang/RuntimeException") && name.equals("<init>"))
374 stubState = StubState.INITIALIZE_RUNTIME;
375 else
376 stubState = StubState.INITIAL;
377 if (owner.startsWith("java/util/concurrent"))
378 mBuilder.setUsesConcurrency();
379 if (opcode == Opcodes.INVOKEINTERFACE)
380 return;
381
382 if (owner.charAt(0) == '[' && owner.charAt(owner.length() - 1) != ';') {
383 // primitive array
384 return;
385 }
386 if (opcode == Opcodes.INVOKESTATIC && owner.equals("java/lang/System") && name.equals("exit")
387 && !sawReturn)
388 sawSystemExit = true;
389 justSawInitializationOfUnsupportedOperationException = opcode == Opcodes.INVOKESPECIAL
390 && owner.equals("java/lang/UnsupportedOperationException") && name.equals("<init>");
391
392 if (isBridge && bridgedMethodSignature == null)
393 switch (opcode) {
394 case Opcodes.INVOKEVIRTUAL:
395 case Opcodes.INVOKESPECIAL:
396 case Opcodes.INVOKESTATIC:
397 case Opcodes.INVOKEINTERFACE:
398 if (desc != null && name.equals(methodName))
399 bridgedMethodSignature = desc;
400 }
401
402 // System.out.println("Call from " +
403 // ClassParserUsingASM.this.slashedClassName +
404 // " to " + owner + " : " + desc);
405 if (desc == null || desc.indexOf('[') == -1 && desc.indexOf('L') == -1)
406 return;
407 if (ClassParserUsingASM.this.slashedClassName.equals(owner))
408 return;
409 ClassDescriptor classDescriptor = DescriptorFactory.instance().getClassDescriptor(owner);
410 calledClassSet.add(classDescriptor);
411
412 }
413
414 private void sawBranchTo(Label label) {
415 sawBranch = true;
416 if (labelsSeen.contains(label))
417 sawBackBranch = true;
418 }
419
420 @Override
421 public void visitJumpInsn(int opcode, Label label) {
422 sawBranchTo(label);
423 identityState = IdentityMethodState.NOT;
424 super.visitJumpInsn(opcode, label);
425 }
426
427 @Override
428 public void visitLookupSwitchInsn(Label dflt, int[] keys, Label[] labels) {
429 sawBranchTo(dflt);
430 for (Label lbl : labels)
431 sawBranchTo(lbl);
432 identityState = IdentityMethodState.NOT;
433 super.visitLookupSwitchInsn(dflt, keys, labels);
434 }
435
436 @Override
437 public void visitTableSwitchInsn(int min, int max, Label dflt, Label[] labels) {
438 sawBranchTo(dflt);
439 for (Label lbl : labels)
440 sawBranchTo(lbl);
441 identityState = IdentityMethodState.NOT;
442 super.visitTableSwitchInsn(min, max, dflt, labels);
443 }
444
445 @Override
446 public void visitLabel(Label label) {
447 labelsSeen.add(label);
448 super.visitLabel(label);
449
450 }
451
452 public void visitEnd() {
453 labelsSeen.clear();
454 if (isAccessMethod && accessOwner != null) {
455 if (!accessForField && methodCallCount == 1) {
456 mBuilder.setAccessMethodForMethod(accessOwner, accessName, accessDesc, accessIsStatic);
457 } else if(accessForField && fieldInstructionCount == 1) {
458 boolean isSetter = methodDesc.endsWith(")V");
459 int numArg = new SignatureParser(methodDesc).getNumParameters();
460 int expected = 0;
461 if (!accessIsStatic) expected++;
462 if (isSetter) expected++;
463 boolean OK;
464 if (isSetter)
465 OK = methodDesc.substring(1).startsWith(ClassName.toSignature(accessOwner) + accessDesc);
466 else
467 OK = methodDesc.substring(1).startsWith(ClassName.toSignature(accessOwner));
468 if (numArg == expected && OK)
469 mBuilder.setAccessMethodForField(accessOwner, accessName, accessDesc, accessIsStatic);
470 }
471 }
472 if (sawBackBranch)
473 mBuilder.setHasBackBranch();
474 boolean sawThrow = sawNormalThrow | sawUnsupportedThrow | sawStubThrow;
475 if (sawThrow && !sawReturn || sawSystemExit && !sawBranch) {
476
477 mBuilder.setIsUnconditionalThrower();
478 if (!sawReturn && !sawNormalThrow) {
479 if (sawUnsupportedThrow)
480 mBuilder.setUnsupported();
481 if (sawStubThrow) {
482 mBuilder.addAccessFlags(Constants.ACC_SYNTHETIC);
483 mBuilder.setIsStub();
484
485 }
486 }
487 // else
488 // System.out.println(slashedClassName+"."+methodName+methodDesc
489 // + " is thrower");
490 }
491 mBuilder.setNumberMethodCalls(methodCallCount);
492 MethodInfo methodInfo = mBuilder.build();
493 Builder classBuilder = (ClassInfo.Builder) cBuilder;
494 if (isBridge && bridgedMethodSignature != null && !bridgedMethodSignature.equals(methodDesc))
495 classBuilder.addBridgeMethodDescriptor(methodInfo, bridgedMethodSignature);
496 else
497 classBuilder.addMethodDescriptor(methodInfo);
498
499 if (methodInfo.usesConcurrency())
500 classBuilder.setUsesConcurrency();
501 if (methodInfo.isStub())
502 classBuilder.setHasStubs();
503 }
504
505 public org.objectweb.asm.AnnotationVisitor visitParameterAnnotation(int parameter, String desc,
506 boolean visible) {
507 AnnotationValue value = new AnnotationValue(desc);
508 mBuilder.addParameterAnnotation(parameter, desc, value);
509 return value.getAnnotationVisitor();
510 }
511 };
615 }
616
617 return new ClassParserMethodVisitor(calledClassSet, mBuilder, methodName, access, methodDesc, cBuilder);
512618
513619 }
514620 return null;
515621 }
516622
623 @Override
517624 public void visitOuterClass(String owner, String name, String desc) {
518625
519626 }
520627
628 @Override
521629 public void visitSource(String arg0, String arg1) {
522630 if (cBuilder instanceof ClassInfo.Builder) {
523631 ((ClassInfo.Builder) cBuilder).setSource(arg0);
544652 case Constants.CONSTANT_Integer:
545653 case Constants.CONSTANT_Float:
546654 case Constants.CONSTANT_NameAndType:
655 case Constants.CONSTANT_InvokeDynamic:
547656 size = 5;
548657 break;
549658 case Constants.CONSTANT_Long:
565674 }
566675 size = 3;
567676 break;
568 // case ClassWriter.CLASS:
569 // case ClassWriter.STR:
570677 case Constants.CONSTANT_String:
678 case Constants.CONSTANT_MethodType:
571679 size = 3;
680 break;
681 case Constants.CONSTANT_MethodHandle:
682 size = 4;
572683 break;
573684 default:
574685 throw new IllegalStateException("Unexpected tag of " + tag + " at offset " + offset + " while parsing "
582693 cBuilder.setReferencedClassDescriptors(referencedClassSet);
583694 }
584695
696 @Override
585697 public void parse(ClassInfo.Builder builder) throws InvalidClassFileFormatException {
586698 parse((ClassNameAndSuperclassInfo.Builder) builder);
587699
6767
6868 /*
6969 * (non-Javadoc)
70 *
70 *
7171 * @see
7272 * edu.umd.cs.findbugs.classfile.engine.ClassParserInterface#parse(edu.umd
7373 * .cs.findbugs.classfile.analysis.ClassNameAndSuperclassInfo.Builder)
7474 */
75 @Override
7576 public void parse(final ClassNameAndSuperclassInfo.Builder builder) throws InvalidClassFileFormatException {
7677
7778 builder.setCodeBaseEntry(codeBaseEntry);
7879 builder.setAccessFlags(javaClass.getAccessFlags());
7980 ClassDescriptor classDescriptor = DescriptorFactory.createClassDescriptorFromDottedClassName(javaClass.getClassName());
80 if (expectedClassDescriptor != null && expectedClassDescriptor.equals(classDescriptor))
81 if (expectedClassDescriptor != null && expectedClassDescriptor.equals(classDescriptor)) {
8182 throw new InvalidClassFileFormatException("Expected " + expectedClassDescriptor, classDescriptor, codeBaseEntry);
83 }
8284 builder.setClassDescriptor(classDescriptor);
8385
8486 builder.setSuperclassDescriptor(DescriptorFactory.createClassDescriptorFromDottedClassName(javaClass.getSuperclassName()));
8587 String[] allInterfaces = javaClass.getInterfaceNames();
8688 ClassDescriptor[] allInterfaceDescriptiors;
87 if (allInterfaces.length == 0)
89 if (allInterfaces.length == 0) {
8890 allInterfaceDescriptiors = ClassDescriptor.EMPTY_ARRAY;
89 else {
91 } else {
9092 allInterfaceDescriptiors = new ClassDescriptor[allInterfaces.length];
91 for (int i = 0; i < allInterfaces.length; i++)
93 for (int i = 0; i < allInterfaces.length; i++) {
9294 allInterfaceDescriptiors[i] = DescriptorFactory.createClassDescriptorFromDottedClassName(allInterfaces[i]);
95 }
9396 }
9497 builder.setInterfaceDescriptorList(allInterfaceDescriptiors);
9598 }
9699
97100 /*
98101 * (non-Javadoc)
99 *
102 *
100103 * @see
101104 * edu.umd.cs.findbugs.classfile.engine.ClassParserInterface#parse(edu.umd
102105 * .cs.findbugs.classfile.analysis.ClassInfo.Builder)
103106 */
107 @Override
104108 public void parse(ClassInfo.Builder builder) throws InvalidClassFileFormatException {
105109 parse((ClassNameAndSuperclassInfo.Builder) builder);
106110
2626
2727 /**
2828 * Register analysis engines with an analysis cache.
29 *
29 *
3030 * @author David Hovemeyer
3131 */
3232 public class EngineRegistrar implements IAnalysisEngineRegistrar {
3333 private static IClassAnalysisEngine<?>[] classAnalysisEngineList = { new ClassDataAnalysisEngine(),
34 new ClassInfoAnalysisEngine(), new ClassNameAndSuperclassInfoAnalysisEngine(), new ClassReaderAnalysisEngine() };
34 new ClassInfoAnalysisEngine(), new ClassNameAndSuperclassInfoAnalysisEngine(), new ClassReaderAnalysisEngine() };
3535
3636 private static IMethodAnalysisEngine<?>[] methodAnalysisEngineList = {};
3737
4343
4444 /*
4545 * (non-Javadoc)
46 *
46 *
4747 * @see
4848 * edu.umd.cs.findbugs.classfile.IAnalysisEngineRegistrar#registerWith(edu
4949 * .umd.cs.findbugs.classfile.IAnalysisCache)
5050 */
51 @Override
5152 public void registerAnalysisEngines(IAnalysisCache analysisCache) {
5253 for (IClassAnalysisEngine<?> engine : classAnalysisEngineList) {
5354 engine.registerWith(analysisCache);
2222 import java.util.Map;
2323
2424 import org.objectweb.asm.ClassReader;
25 import org.objectweb.asm.ClassVisitor;
2526 import org.objectweb.asm.MethodVisitor;
2627 import org.objectweb.asm.Opcodes;
27 import org.objectweb.asm.commons.EmptyVisitor;
2828
2929 import edu.umd.cs.findbugs.asm.FBClassReader;
3030 import edu.umd.cs.findbugs.ba.AnalysisContext;
3131 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
3232 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
3333 import edu.umd.cs.findbugs.classfile.Global;
34 import edu.umd.cs.findbugs.classfile.engine.asm.FindBugsASM;
3435 import edu.umd.cs.findbugs.util.MultiMap;
3536
3637 /**
3940 public class SelfMethodCalls {
4041
4142 static private boolean interestingSignature(String signature) {
42 return !signature.equals("()V");
43 return !"()V".equals(signature);
4344 }
4445
4546 public static <T> MultiMap<T, T> getSelfCalls(final ClassDescriptor classDescriptor, final Map<String, T> methods) {
5253 AnalysisContext.logError("Error finding self method calls for " + classDescriptor, e);
5354 return map;
5455 }
55 reader.accept(new EmptyVisitor() {
56 reader.accept(new ClassVisitor(FindBugsASM.ASM_VERSION) {
5657
5758 @Override
5859 public MethodVisitor visitMethod(final int access, final String name, final String desc, String signature,
5960 String[] exceptions) {
60 return new EmptyVisitor() {
61 return new MethodVisitor(FindBugsASM.ASM_VERSION) {
6162 @Override
62 public void visitMethodInsn(int opcode, String owner, String name2, String desc2) {
63 public void visitMethodInsn(int opcode, String owner, String name2, String desc2, boolean itf) {
6364 if (owner.equals(classDescriptor.getClassName()) && interestingSignature(desc2)) {
6465 T from = methods.get(name + desc + ((access & Opcodes.ACC_STATIC) != 0));
6566 T to = methods.get(name2 + desc2 + (opcode == Opcodes.INVOKESTATIC));
7576 return map;
7677 }
7778
78 private final ClassReader classReader;
79 // private final ClassReader classReader;
7980
8081 public SelfMethodCalls(ClassReader classReader) {
81 this.classReader = classReader;
82 // this.classReader = classReader;
8283 }
8384 }
3030
3131 /**
3232 * Analysis engine to produce the ClassNode (ASM tree format) for a class.
33 *
33 *
3434 * @author David Hovemeyer
3535 */
3636 public class ClassNodeAnalysisEngine extends RecomputableClassAnalysisEngine<ClassNode> {
3737
3838 /*
3939 * (non-Javadoc)
40 *
40 *
4141 * @see
4242 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4343 * .classfile.IAnalysisCache, java.lang.Object)
4444 */
45 @Override
4546 public ClassNode analyze(IAnalysisCache analysisCache, ClassDescriptor descriptor) throws CheckedAnalysisException {
4647 ClassReader classReader = analysisCache.getClassAnalysis(ClassReader.class, descriptor);
4748
6263
6364 /*
6465 * (non-Javadoc)
65 *
66 *
6667 * @see
6768 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#registerWith(edu.umd.cs
6869 * .findbugs.classfile.IAnalysisCache)
6970 */
71 @Override
7072 public void registerWith(IAnalysisCache analysisCache) {
7173 analysisCache.registerClassAnalysisEngine(ClassNode.class, this);
7274 }
2727
2828 /**
2929 * Analysis engine to produce an ASM ClassReader for a class.
30 *
30 *
3131 * @author David Hovemeyer
3232 */
3333 public class ClassReaderAnalysisEngine extends RecomputableClassAnalysisEngine<FBClassReader> {
3434
3535 /*
3636 * (non-Javadoc)
37 *
37 *
3838 * @see
3939 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4040 * .classfile.IAnalysisCache, java.lang.Object)
4141 */
42 @Override
4243 public FBClassReader analyze(IAnalysisCache analysisCache, ClassDescriptor descriptor) throws CheckedAnalysisException {
4344
4445 ClassData classData = analysisCache.getClassAnalysis(ClassData.class, descriptor);
5051
5152 /*
5253 * (non-Javadoc)
53 *
54 *
5455 * @see
5556 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#registerWith(edu.umd.cs
5657 * .findbugs.classfile.IAnalysisCache)
5758 */
59 @Override
5860 public void registerWith(IAnalysisCache analysisCache) {
5961 analysisCache.registerClassAnalysisEngine(FBClassReader.class, this);
6062 }
2525
2626 /**
2727 * Analysis engine registrar for ASM-based analyses.
28 *
28 *
2929 * @author David Hovemeyer
3030 */
3131 public class EngineRegistrar implements IAnalysisEngineRegistrar {
3232 private static final IClassAnalysisEngine<?>[] classAnalysisEngineList = { new ClassNodeAnalysisEngine(),
33 new ClassReaderAnalysisEngine(), };
33 new ClassReaderAnalysisEngine(), };
3434
3535 private static IMethodAnalysisEngine<?>[] methodAnalysisEngineList = {};
3636
4242
4343 /*
4444 * (non-Javadoc)
45 *
45 *
4646 * @see edu.umd.cs.findbugs.classfile.IAnalysisEngineRegistrar#
4747 * registerAnalysisEngines(edu.umd.cs.findbugs.classfile.IAnalysisCache)
4848 */
49 @Override
4950 public void registerAnalysisEngines(IAnalysisCache analysisCache) {
5051 for (IClassAnalysisEngine<?> engine : classAnalysisEngineList) {
5152 engine.registerWith(analysisCache);
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2008 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.classfile.engine.asm;
20
21 import org.objectweb.asm.Opcodes;
22
23 /**
24 * @author pugh
25 */
26 public class FindBugsASM {
27
28 public static final int ASM_VERSION = Opcodes.ASM5;
29
30 }
4242 * Abstract factory class for creating analysis objects.
4343 */
4444 public abstract class AnalysisFactory<Analysis> implements IMethodAnalysisEngine<Analysis> {
45 private String analysisName;
45 private final String analysisName;
4646
47 private Class<Analysis> analysisClass;
47 private final Class<Analysis> analysisClass;
4848
4949 /**
5050 * Constructor.
51 *
51 *
5252 * @param analysisName
5353 * name of the analysis factory: for diagnostics/debugging
5454 */
7070
7171 /*
7272 * (non-Javadoc)
73 *
73 *
7474 * @see
7575 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#registerWith(edu.umd.cs
7676 * .findbugs.classfile.IAnalysisCache)
7777 */
78 @Override
7879 public void registerWith(IAnalysisCache analysisCache) {
7980 analysisCache.registerMethodAnalysisEngine(analysisClass, this);
8081 }
2828
2929 /**
3030 * Class analysis engine for creating AssertionMethods objects.
31 *
31 *
3232 * @author David Hovemeyer
3333 */
3434 public class AssertionMethodsFactory extends RecomputableClassAnalysisEngine<AssertionMethods> {
3535
36 @Override
3637 public AssertionMethods analyze(IAnalysisCache analysisCache, ClassDescriptor descriptor) throws CheckedAnalysisException {
3738 JavaClass jclass = analysisCache.getClassAnalysis(JavaClass.class, descriptor);
3839 return new AssertionMethods(jclass);
4041
4142 /*
4243 * (non-Javadoc)
43 *
44 *
4445 * @see
4546 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#registerWith(edu.umd.cs
4647 * .findbugs.classfile.IAnalysisCache)
4748 */
49 @Override
4850 public void registerWith(IAnalysisCache analysisCache) {
4951 analysisCache.registerClassAnalysisEngine(AssertionMethods.class, this);
5052 }
2626
2727 /**
2828 * Analysis engine for producing an AssignedFieldMap for a class.
29 *
29 *
3030 * @author David Hovemeyer
3131 */
3232 public class AssignedFieldMapFactory extends RecomputableClassAnalysisEngine<AssignedFieldMap> {
3333
3434 /*
3535 * (non-Javadoc)
36 *
36 *
3737 * @see
3838 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
3939 * .classfile.IAnalysisCache, java.lang.Object)
4040 */
41 @Override
4142 public AssignedFieldMap analyze(IAnalysisCache analysisCache, ClassDescriptor descriptor) throws CheckedAnalysisException {
4243 /*
4344 * JavaClass jclass = analysisCache.getClassAnalysis(JavaClass.class,
4849
4950 /*
5051 * (non-Javadoc)
51 *
52 *
5253 * @see
5354 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#registerWith(edu.umd.cs
5455 * .findbugs.classfile.IAnalysisCache)
5556 */
57 @Override
5658 public void registerWith(IAnalysisCache analysisCache) {
5759 /*
5860 * analysisCache.registerClassAnalysisEngine(AssignedFieldMap.class,
2525
2626 /**
2727 * Factory to create BackwardTypeQualifierDataflowFactory objects.
28 *
28 *
2929 * @author David Hovemeyer
3030 */
3131 public class BackwardTypeQualifierDataflowFactoryFactory extends AnalysisFactory<BackwardTypeQualifierDataflowFactory> {
3535
3636 /*
3737 * (non-Javadoc)
38 *
38 *
3939 * @see
4040 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4141 * .classfile.IAnalysisCache, java.lang.Object)
4242 */
43 @Override
4344 public BackwardTypeQualifierDataflowFactory analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor)
4445 throws CheckedAnalysisException {
4546 return new BackwardTypeQualifierDataflowFactory(descriptor);
99
1010 /**
1111 * Analysis engine for producing BlockTypeDataflow for an analyzed method.
12 *
12 *
1313 * @author David Hovemeyer
1414 */
1515 public class BlockTypeAnalysisFactory extends AnalysisFactory<BlockTypeDataflow> {
1919
2020 /*
2121 * (non-Javadoc)
22 *
22 *
2323 * @see
2424 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
2525 * .classfile.IAnalysisCache, java.lang.Object)
2626 */
27 @Override
2728 public BlockTypeDataflow analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
2829 CFG cfg = getCFG(analysisCache, descriptor);
2930 DepthFirstSearch dfs = getDepthFirstSearch(analysisCache, descriptor);
5858 /**
5959 * Analysis engine to produce CFG (control flow graph) objects for an analyzed
6060 * method.
61 *
61 *
6262 * @author David Hovemeyer
6363 */
6464 public class CFGFactory extends AnalysisFactory<CFG> {
7373
7474 /*
7575 * (non-Javadoc)
76 *
76 *
7777 * @see
7878 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
7979 * .classfile.IAnalysisCache, java.lang.Object)
8080 */
81 @Override
8182 public CFG analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
8283 // Construct the CFG in its raw form
8384 MethodGen methodGen = analysisCache.getMethodAnalysis(MethodGen.class, descriptor);
131132 if (prevInstruction instanceof GETSTATIC && lastInstruction instanceof IFNE) {
132133 GETSTATIC getStatic = (GETSTATIC) prevInstruction;
133134
134 if (getStatic.getFieldName(methodGen.getConstantPool()).equals("$assertionsDisabled")
135 && getStatic.getSignature(methodGen.getConstantPool()).equals("Z"))
135 if ("$assertionsDisabled".equals(getStatic.getFieldName(methodGen.getConstantPool()))
136 && "Z".equals(getStatic.getSignature(methodGen.getConstantPool()))) {
136137 edgesToRemove.add(e);
138 }
137139 }
138140 } catch (RuntimeException exception) {
139141 assert true; // ignore it
162164 changed = changed || pruner.wasCFGModified();
163165 } catch (MissingClassException e) {
164166 AnalysisContext.currentAnalysisContext().getLookupFailureCallback()
165 .reportMissingClass(e.getClassNotFoundException());
167 .reportMissingClass(e.getClassNotFoundException());
166168 } catch (DataflowAnalysisException e) {
167169 AnalysisContext.currentAnalysisContext().getLookupFailureCallback()
168 .logError("unable to extract type analysis", e);
170 .logError("unable to extract type analysis", e);
169171 } catch (ClassNotFoundException e) {
170172 AnalysisContext.currentAnalysisContext().getLookupFailureCallback().reportMissingClass(e);
171173 }
208210 dfs.search();
209211 Collection<BasicBlock> unreachable = dfs.unvisitedVertices();
210212 if (!unreachable.isEmpty()) {
211 if (DEBUG_CFG)
213 if (DEBUG_CFG) {
212214 System.out.println("Unreachable blocks");
215 }
213216 for (BasicBlock b : unreachable) {
214 if (DEBUG_CFG)
217 if (DEBUG_CFG) {
215218 System.out.println(" removing " + b);
219 }
216220 cfg.removeVertex(b);
217221 }
218222 }
224228
225229 /*
226230 * (non-Javadoc)
227 *
231 *
228232 * @see
229233 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#registerWith(edu.umd.cs
230234 * .findbugs.classfile.IAnalysisCache)
77
88 /**
99 * Analysis engine to produce CallListDataflow objects for a method.
10 *
10 *
1111 * @author David Hovemeyer
1212 */
1313 public class CallListDataflowFactory extends AnalysisFactory<CallListDataflow> {
1717
1818 /*
1919 * (non-Javadoc)
20 *
20 *
2121 * @see
2222 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
2323 * .classfile.IAnalysisCache, java.lang.Object)
2424 */
25 @Override
2526 public CallListDataflow analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
2627 CallListAnalysis analysis = new CallListAnalysis(getCFG(analysisCache, descriptor), getDepthFirstSearch(analysisCache,
2728 descriptor), getConstantPoolGen(analysisCache, descriptor.getClassDescriptor()));
3030 /**
3131 * Adapter to produce the ClassContext for a given class. This is
3232 * backwards-compatibility for the BCEL-based analysis framework.
33 *
33 *
3434 * @author David Hovemeyer
3535 */
3636 public class ClassContextClassAnalysisEngine extends RecomputableClassAnalysisEngine<ClassContext> {
3737
3838 /*
3939 * (non-Javadoc)
40 *
40 *
4141 * @see
4242 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4343 * .classfile.IAnalysisCache, java.lang.Object)
4444 */
45 @Override
4546 public ClassContext analyze(IAnalysisCache analysisCache, ClassDescriptor descriptor) throws CheckedAnalysisException {
4647
4748 JavaClass javaClass = analysisCache.getClassAnalysis(JavaClass.class, descriptor);
5152
5253 /*
5354 * (non-Javadoc)
54 *
55 *
5556 * @see
5657 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#registerWith(edu.umd.cs
5758 * .findbugs.classfile.IAnalysisCache)
5859 */
60 @Override
5961 public void registerWith(IAnalysisCache analysisCache) {
6062 analysisCache.registerClassAnalysisEngine(ClassContext.class, this);
6163 }
2727
2828 /**
2929 * Analysis engine to produce CompactLocationNumbering objects for methods.
30 *
30 *
3131 * @author David Hovemeyer
3232 */
3333 public class CompactLocationNumberingFactory extends AnalysisFactory<CompactLocationNumbering> {
4040
4141 /*
4242 * (non-Javadoc)
43 *
43 *
4444 * @see
4545 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4646 * .classfile.IAnalysisCache, java.lang.Object)
4747 */
48 @Override
4849 public CompactLocationNumbering analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor)
4950 throws CheckedAnalysisException {
5051 Method method = analysisCache.getMethodAnalysis(Method.class, descriptor);
99
1010 /**
1111 * Analysis engine to produce ConstantDataflow objects for an analyzed method.
12 *
12 *
1313 * @author David Hovemeyer
1414 */
1515 public class ConstantDataflowFactory extends AnalysisFactory<ConstantDataflow> {
1919
2020 /*
2121 * (non-Javadoc)
22 *
22 *
2323 * @see
2424 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
2525 * .classfile.IAnalysisCache, java.lang.Object)
2626 */
27 @Override
2728 public ConstantDataflow analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
2829 MethodGen methodGen = getMethodGen(analysisCache, descriptor);
29 if (methodGen == null)
30 if (methodGen == null) {
3031 return null;
32 }
3133 ConstantAnalysis analysis = new ConstantAnalysis(methodGen, getDepthFirstSearch(analysisCache, descriptor));
3234 ConstantDataflow dataflow = new ConstantDataflow(getCFG(analysisCache, descriptor), analysis);
3335 dataflow.execute();
2929
3030 /**
3131 * Analysis engine to produce ConstantPoolGen objects for an analyzed class.
32 *
32 *
3333 * @author David Hovemeyer
3434 */
3535 public class ConstantPoolGenFactory extends RecomputableClassAnalysisEngine<ConstantPoolGen> {
3636
3737 /*
3838 * (non-Javadoc)
39 *
39 *
4040 * @see
4141 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4242 * .classfile.IAnalysisCache, java.lang.Object)
4343 */
44 @Override
4445 public ConstantPoolGen analyze(IAnalysisCache analysisCache, ClassDescriptor descriptor) throws CheckedAnalysisException {
4546 ClassGen classGen = new ClassGen(analysisCache.getClassAnalysis(JavaClass.class, descriptor));
4647 return classGen.getConstantPool();
4849
4950 /*
5051 * (non-Javadoc)
51 *
52 *
5253 * @see
5354 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#registerWith(edu.umd.cs
5455 * .findbugs.classfile.IAnalysisCache)
5556 */
57 @Override
5658 public void registerWith(IAnalysisCache analysisCache) {
5759 analysisCache.registerClassAnalysisEngine(ConstantPoolGen.class, this);
5860 }
2525
2626 /**
2727 * Analysis engine to produce DepthFirstSearch objects for analyzed methods.
28 *
28 *
2929 * @author David Hovemeyer
3030 */
3131 public class DepthFirstSearchFactory extends AnalysisFactory<DepthFirstSearch> {
3838
3939 /*
4040 * (non-Javadoc)
41 *
41 *
4242 * @see
4343 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4444 * .classfile.IAnalysisCache, java.lang.Object)
4545 */
46 @Override
4647 public DepthFirstSearch analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
4748 CFG cfg = getCFG(analysisCache, descriptor);
4849 DepthFirstSearch dfs = new DepthFirstSearch(cfg);
99
1010 /**
1111 * Analysis engine to produce DominatorsAnalysis objects for analyzed methods.
12 *
12 *
1313 * @author David Hovemeyer
1414 */
1515 public class DominatorsAnalysisFactory extends AnalysisFactory<DominatorsAnalysis> {
2222
2323 /*
2424 * (non-Javadoc)
25 *
25 *
2626 * @see
2727 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
2828 * .classfile.IAnalysisCache, java.lang.Object)
2929 */
30 @Override
3031 public DominatorsAnalysis analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
3132 CFG cfg = getCFG(analysisCache, descriptor);
3233 DepthFirstSearch dfs = getDepthFirstSearch(analysisCache, descriptor);
5353 */
5454 public class EngineRegistrar implements IAnalysisEngineRegistrar {
5555 private static final IClassAnalysisEngine<?>[] classAnalysisEngineList = { new ClassContextClassAnalysisEngine(),
56 new JavaClassAnalysisEngine(), new ConstantPoolGenFactory(),
57 // new AssignedFieldMapFactory(),
58 new AssertionMethodsFactory(), };
56 new JavaClassAnalysisEngine(), new ConstantPoolGenFactory(),
57 // new AssignedFieldMapFactory(),
58 new AssertionMethodsFactory(), };
5959
6060 private static final IMethodAnalysisEngine<?>[] methodAnalysisEngineList = { new MethodFactory(), new MethodGenFactory(),
61 new CFGFactory(), new UsagesRequiringNonNullValuesFactory(), new ValueNumberDataflowFactory(),
62 new IsNullValueDataflowFactory(), new TypeDataflowFactory(), new DepthFirstSearchFactory(),
63 new ReverseDepthFirstSearchFactory(), new UnpackedCodeFactory(), new LockDataflowFactory(), new LockCheckerFactory(),
64 new ReturnPathDataflowFactory(), new DominatorsAnalysisFactory(), new NonExceptionPostdominatorsAnalysisFactory(),
65 new NonImplicitExceptionPostDominatorsAnalysisFactory(), new ExceptionSetFactoryFactory(),
66 new ParameterSignatureListFactory(), new ConstantDataflowFactory(), new LoadDataflowFactory(),
67 new StoreDataflowFactory(), new LoadedFieldSetFactory(), new LiveLocalStoreDataflowFactory(),
68 new BlockTypeAnalysisFactory(), new CallListDataflowFactory(), new UnconditionalValueDerefDataflowFactory(),
69 new CompactLocationNumberingFactory(), new ReturnPathTypeDataflowFactory(),
70 new ForwardTypeQualifierDataflowFactoryFactory(), new BackwardTypeQualifierDataflowFactoryFactory(),
71 new OpcodeStack.JumpInfoFactory(), new StackMapAnalysisFactory(), new ObligationDataflowFactory(), };
61 new CFGFactory(), new UsagesRequiringNonNullValuesFactory(), new ValueNumberDataflowFactory(),
62 new IsNullValueDataflowFactory(), new TypeDataflowFactory(), new DepthFirstSearchFactory(),
63 new ReverseDepthFirstSearchFactory(), new UnpackedCodeFactory(), new LockDataflowFactory(), new LockCheckerFactory(),
64 new ReturnPathDataflowFactory(), new DominatorsAnalysisFactory(), new NonExceptionPostdominatorsAnalysisFactory(),
65 new NonImplicitExceptionPostDominatorsAnalysisFactory(), new ExceptionSetFactoryFactory(),
66 new ParameterSignatureListFactory(), new ConstantDataflowFactory(), new LoadDataflowFactory(),
67 new StoreDataflowFactory(), new LoadedFieldSetFactory(), new LiveLocalStoreDataflowFactory(),
68 new BlockTypeAnalysisFactory(), new CallListDataflowFactory(), new UnconditionalValueDerefDataflowFactory(),
69 new CompactLocationNumberingFactory(), new ReturnPathTypeDataflowFactory(),
70 new ForwardTypeQualifierDataflowFactoryFactory(), new BackwardTypeQualifierDataflowFactoryFactory(),
71 new OpcodeStack.JumpInfoFactory(), new StackMapAnalysisFactory(), new ObligationDataflowFactory(),
72 new ValueRangeAnalysisFactory(), new FinallyDuplicatesInfoFactory()};
7273
7374 private static final IDatabaseFactory<?>[] databaseFactoryList = {
74 // new ReflectionDatabaseFactory<Subtypes>(Subtypes.class),
75 new ReflectionDatabaseFactory<Subtypes2>(Subtypes2.class),
76 new ReflectionDatabaseFactory<InnerClassAccessMap>(InnerClassAccessMap.class),
77 new ReflectionDatabaseFactory<CheckReturnAnnotationDatabase>(CheckReturnAnnotationDatabase.class),
78 new ReflectionDatabaseFactory<AnnotationRetentionDatabase>(AnnotationRetentionDatabase.class),
79 new ReflectionDatabaseFactory<JCIPAnnotationDatabase>(JCIPAnnotationDatabase.class),
80 new ReflectionDatabaseFactory<SourceInfoMap>(SourceInfoMap.class),
81 new ReflectionDatabaseFactory<FieldStoreTypeDatabase>(FieldStoreTypeDatabase.class),
82 new ReflectionDatabaseFactory<ParameterNullnessPropertyDatabase>(ParameterNullnessPropertyDatabase.class),
83 new ReflectionDatabaseFactory<ReturnValueNullnessPropertyDatabase>(ReturnValueNullnessPropertyDatabase.class),
84 new ReflectionDatabaseFactory<DirectlyRelevantTypeQualifiersDatabase>(DirectlyRelevantTypeQualifiersDatabase.class),
85 new ReflectionDatabaseFactory<TypeQualifierDatabase>(TypeQualifierDatabase.class),
86 new ReflectionDatabaseFactory<MethodInfoDatabase>(MethodInfoDatabase.class),
87 };
75 // new ReflectionDatabaseFactory<Subtypes>(Subtypes.class),
76 new ReflectionDatabaseFactory<Subtypes2>(Subtypes2.class),
77 new ReflectionDatabaseFactory<InnerClassAccessMap>(InnerClassAccessMap.class),
78 new ReflectionDatabaseFactory<CheckReturnAnnotationDatabase>(CheckReturnAnnotationDatabase.class),
79 new ReflectionDatabaseFactory<AnnotationRetentionDatabase>(AnnotationRetentionDatabase.class),
80 new ReflectionDatabaseFactory<JCIPAnnotationDatabase>(JCIPAnnotationDatabase.class),
81 new ReflectionDatabaseFactory<SourceInfoMap>(SourceInfoMap.class),
82 new ReflectionDatabaseFactory<FieldStoreTypeDatabase>(FieldStoreTypeDatabase.class),
83 new ReflectionDatabaseFactory<ParameterNullnessPropertyDatabase>(ParameterNullnessPropertyDatabase.class),
84 new ReflectionDatabaseFactory<ReturnValueNullnessPropertyDatabase>(ReturnValueNullnessPropertyDatabase.class),
85 new ReflectionDatabaseFactory<DirectlyRelevantTypeQualifiersDatabase>(DirectlyRelevantTypeQualifiersDatabase.class),
86 new ReflectionDatabaseFactory<TypeQualifierDatabase>(TypeQualifierDatabase.class),
87 new ReflectionDatabaseFactory<MethodInfoDatabase>(MethodInfoDatabase.class),
88 };
8889
8990 /*
9091 * (non-Javadoc)
9293 * @see edu.umd.cs.findbugs.classfile.IAnalysisEngineRegistrar#
9394 * registerAnalysisEngines(edu.umd.cs.findbugs.classfile.IAnalysisCache)
9495 */
96 @Override
9597 public void registerAnalysisEngines(IAnalysisCache analysisCache) {
9698 for (IClassAnalysisEngine<?> engine : classAnalysisEngineList) {
9799 engine.registerWith(analysisCache);
2424
2525 /**
2626 * Analysis engine to produce ExceptionSetFactory objects for analyzed methods.
27 *
27 *
2828 * @author David Hovemeyer
2929 */
3030 public class ExceptionSetFactoryFactory extends AnalysisFactory<ExceptionSetFactory> {
3737
3838 /*
3939 * (non-Javadoc)
40 *
40 *
4141 * @see
4242 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4343 * .classfile.IAnalysisCache, java.lang.Object)
4444 */
45 @Override
4546 public ExceptionSetFactory analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
4647 return new ExceptionSetFactory();
4748 }
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2008 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.classfile.engine.bcel;
20
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.BitSet;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.Iterator;
27 import java.util.LinkedHashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Map.Entry;
31 import java.util.Set;
32 import java.util.SortedMap;
33 import java.util.TreeMap;
34
35 import org.apache.bcel.classfile.CodeException;
36 import org.apache.bcel.classfile.Method;
37 import org.apache.bcel.generic.ALOAD;
38 import org.apache.bcel.generic.ASTORE;
39 import org.apache.bcel.generic.ATHROW;
40 import org.apache.bcel.generic.BranchInstruction;
41 import org.apache.bcel.generic.Instruction;
42 import org.apache.bcel.generic.InstructionHandle;
43 import org.apache.bcel.generic.InstructionList;
44 import org.apache.bcel.generic.JSR;
45 import org.apache.bcel.generic.LocalVariableInstruction;
46 import org.apache.bcel.generic.MethodGen;
47 import org.apache.bcel.generic.StoreInstruction;
48
49 import edu.umd.cs.findbugs.ba.CFG;
50 import edu.umd.cs.findbugs.ba.Edge;
51 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
52 import edu.umd.cs.findbugs.classfile.IAnalysisCache;
53 import edu.umd.cs.findbugs.classfile.IMethodAnalysisEngine;
54 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
55
56 /**
57 * @author Tagir Valeev
58 */
59 public class FinallyDuplicatesInfoFactory implements IMethodAnalysisEngine<FinallyDuplicatesInfoFactory.FinallyDuplicatesInfo> {
60 private static final FinallyDuplicatesInfo NONE_FINALLY_INFO = new FinallyDuplicatesInfo();
61
62 public static class FinallyDuplicatesInfo {
63 private final List<SortedMap<Integer, Integer>> duplicateBlocks;
64 private final int[] positions;
65
66 public FinallyDuplicatesInfo(int[] positions, List<SortedMap<Integer, Integer>> duplicateBlocks) {
67 this.positions = positions;
68 this.duplicateBlocks = duplicateBlocks;
69 }
70
71 public FinallyDuplicatesInfo() {
72 this.duplicateBlocks = null;
73 this.positions = null;
74 }
75
76 public BitSet getDuplicates(int pos) {
77 if(duplicateBlocks == null) {
78 return new BitSet();
79 }
80 BitSet current = new BitSet();
81 current.set(pos);
82 boolean changed;
83 do {
84 changed = false;
85 for(SortedMap<Integer, Integer> duplicates : duplicateBlocks) {
86 for (int i = current.nextSetBit(0); i >= 0; i = current.nextSetBit(i+1)) {
87 int offset = getOffset(duplicates, i);
88 if(offset >= 0) {
89 for(Integer key : duplicates.keySet()) {
90 int dupPosition = positions[getInstructionNumber(positions, key)+offset];
91 if(!current.get(dupPosition)) {
92 changed = true;
93 current.set(dupPosition);
94 }
95 }
96 }
97 }
98 }
99 } while(changed && duplicateBlocks.size() > 1);
100 current.clear(pos);
101 return current;
102 }
103
104 public List<Edge> getDuplicates(CFG cfg, Edge edge) {
105 InstructionHandle ih = edge.getSource().getLastInstruction();
106 if(ih == null) {
107 return Collections.emptyList();
108 }
109 BitSet duplicates = getDuplicates(ih.getPosition());
110 if(duplicates.isEmpty()) {
111 return Collections.emptyList();
112 }
113 List<Edge> result = new ArrayList<>();
114 for(Iterator<Edge> edgeIterator = cfg.edgeIterator(); edgeIterator.hasNext(); ) {
115 Edge next = edgeIterator.next();
116 if(next.getType() != edge.getType()) {
117 continue;
118 }
119 InstructionHandle lastInst = next.getSource().getLastInstruction();
120 if(lastInst != null && lastInst.getPosition() >= 0 && duplicates.get(lastInst.getPosition())) {
121 result.add(next);
122 }
123 }
124 return result;
125 }
126
127 private int getOffset(SortedMap<Integer, Integer> duplicates, int i) {
128 SortedMap<Integer, Integer> headMap = duplicates.headMap(i+1);
129 if(headMap.isEmpty()) {
130 return -1;
131 }
132 int end = headMap.get(headMap.lastKey());
133 if(end <= i) {
134 return -1;
135 }
136 return getInstructionNumber(positions, i)-getInstructionNumber(positions, headMap.lastKey());
137 }
138
139 @Override
140 public String toString() {
141 return String.valueOf(duplicateBlocks);
142 }
143 }
144
145 private static class TryBlock {
146 boolean incorrect = false;
147 final int catchAnyAddress;
148 InstructionHandle firstInstruction;
149 SortedMap<Integer, Integer> normalBlocks = new TreeMap<>();
150 SortedMap<Integer, Integer> duplicates = new TreeMap<>();
151
152 public TryBlock(int catchAnyAddress) {
153 this.catchAnyAddress = catchAnyAddress;
154 }
155
156 public void update(BitSet exceptionTargets, BitSet branchTargets, InstructionList il, Set<Integer> finallyTargets, BitSet usedTargets) {
157 int lastEnd = -1;
158 InstructionHandle ih = il.findHandle(catchAnyAddress);
159 if(ih == null || !(ih.getInstruction() instanceof ASTORE)) {
160 incorrect = true;
161 return;
162 }
163 int varIndex = ((ASTORE)ih.getInstruction()).getIndex();
164 firstInstruction = ih.getNext();
165 if(firstInstruction == null) {
166 incorrect = true;
167 return;
168 }
169 int start = firstInstruction.getPosition();
170 int end = start;
171 while(true) {
172 ih = ih.getNext();
173 if(ih == null) {
174 incorrect = true;
175 return;
176 }
177 end = ih.getPosition();
178 Instruction inst = ih.getInstruction();
179 if((inst instanceof ALOAD) && ((ALOAD)inst).getIndex() == varIndex) {
180 ih = ih.getNext();
181 if(ih == null || !(ih.getInstruction() instanceof ATHROW)) {
182 incorrect = true;
183 return;
184 }
185 break;
186 }
187 if(inst instanceof JSR) {
188 // We are not interested in JSR finally blocks as they are not duplicated
189 incorrect = true;
190 return;
191 }
192 }
193 duplicates.put(start, end);
194 normalBlocks.put(catchAnyAddress, catchAnyAddress);
195 for(Entry<Integer, Integer> entry : normalBlocks.entrySet()) {
196 if(lastEnd > -1) {
197 if(entry.getKey() > lastEnd) {
198 int candidateStart = lastEnd;
199 int block2end = equalBlocks(firstInstruction, il.findHandle(candidateStart), end-start, il.getInstructionPositions());
200 if(block2end > 0 && block2end <= entry.getKey()) {
201 duplicates.put(candidateStart, block2end);
202 while(true) {
203 int newKey = Math.min(exceptionTargets.nextSetBit(block2end+1), branchTargets.nextSetBit(block2end+1));
204 if(newKey < 0 || newKey > entry.getKey()) {
205 break;
206 }
207 InstructionHandle ih2 = il.findHandle(newKey);
208 if(exceptionTargets.get(newKey)) {
209 ih2 = ih2.getNext(); // Skip astore
210 }
211 candidateStart = ih2.getPosition();
212 block2end = equalBlocks(firstInstruction, ih2, end-start, il.getInstructionPositions());
213 if(block2end > 0 && block2end <= entry.getKey()) {
214 duplicates.put(candidateStart, block2end);
215 } else {
216 block2end = newKey;
217 }
218 }
219 }
220 }
221 }
222 lastEnd = entry.getValue();
223 }
224 ih = ih.getNext();
225 int block2end = equalBlocks(firstInstruction, ih, end-start, il.getInstructionPositions());
226 if(block2end > 0) {
227 duplicates.put(ih.getPosition(), block2end);
228 }
229 }
230
231 private int equalBlocks(InstructionHandle ih1, InstructionHandle ih2, int length, int[] positions) {
232 if(length == 0) {
233 return -1;
234 }
235 if(ih1 == null || ih2 == null) {
236 return -1;
237 }
238 int start1 = ih1.getPosition();
239 int start2 = ih2.getPosition();
240 int startNum1 = getInstructionNumber(positions, start1);
241 int startNum2 = getInstructionNumber(positions, start2);
242 Map<Integer, Integer> lvMap = new HashMap<>();
243 while(true) {
244 if(ih1 == null || ih2 == null) {
245 return -1;
246 }
247 Instruction inst1 = ih1.getInstruction();
248 Instruction inst2 = ih2.getInstruction();
249 if(!inst1.equals(inst2)) {
250 if(inst1 instanceof LocalVariableInstruction && inst2 instanceof LocalVariableInstruction) {
251 if(inst1.getClass() != inst2.getClass()) {
252 return -1;
253 }
254 LocalVariableInstruction lvi1 = (LocalVariableInstruction)inst1;
255 LocalVariableInstruction lvi2 = (LocalVariableInstruction)inst2;
256 int lv1 = lvi1.getIndex();
257 int lv2 = lvi2.getIndex();
258 Integer targetLV = lvMap.get(lv1);
259 if(targetLV == null) {
260 if(!(lvi1 instanceof StoreInstruction)) {
261 return -1;
262 }
263 lvMap.put(lv1, lv2);
264 } else if(targetLV != lv2) {
265 return -1;
266 }
267 } else {
268 if(inst1.getOpcode() != inst2.getOpcode()) {
269 return -1;
270 }
271 if(!(inst1 instanceof BranchInstruction)) {
272 return -1;
273 }
274 int target1 = ((BranchInstruction)inst1).getTarget().getPosition();
275 int target2 = ((BranchInstruction)inst2).getTarget().getPosition();
276 if(!(getInstructionNumber(positions, target1)-startNum1 == getInstructionNumber(positions, target2)-startNum2 || (target1 == start1+length))) {
277 return -1;
278 }
279 }
280 }
281 if(ih1.getPosition()-start1+inst1.getLength() >= length) {
282 return ih2.getPosition()+inst2.getLength();
283 }
284 ih1 = ih1.getNext();
285 ih2 = ih2.getNext();
286 }
287 }
288
289 @Override
290 public String toString() {
291 if(incorrect) {
292 return "INCORRECT";
293 }
294 return duplicates.toString();
295 }
296 }
297
298 private static int getInstructionNumber(int[] positions, int position) {
299 return Math.abs(Arrays.binarySearch(positions, position));
300 }
301
302 @Override
303 public FinallyDuplicatesInfo analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
304 Method method = analysisCache.getMethodAnalysis(Method.class, descriptor);
305 if(method == null) {
306 return NONE_FINALLY_INFO;
307 }
308 BitSet exceptionTargets = new BitSet();
309 Map<Integer, TryBlock> finallyTargets = new LinkedHashMap<>();
310 for(CodeException codeException : method.getCode().getExceptionTable()) {
311 if(codeException.getCatchType() == 0) {
312 TryBlock block = finallyTargets.get(codeException.getHandlerPC());
313 if(block == null) {
314 block = new TryBlock(codeException.getHandlerPC());
315 finallyTargets.put(codeException.getHandlerPC(), block);
316 }
317 if(codeException.getStartPC() != codeException.getHandlerPC()) {
318 block.normalBlocks.put(codeException.getStartPC(), codeException.getEndPC());
319 }
320 }
321 exceptionTargets.set(codeException.getHandlerPC());
322 }
323 if(finallyTargets.isEmpty()) {
324 return NONE_FINALLY_INFO;
325 }
326 MethodGen methodGen = analysisCache.getMethodAnalysis(MethodGen.class, descriptor);
327 if(methodGen == null) {
328 return NONE_FINALLY_INFO;
329 }
330 InstructionList il = methodGen.getInstructionList();
331 BitSet branchTargets = new BitSet();
332 for(InstructionHandle ih = il.getStart(); ih != null; ih = ih.getNext()) {
333 Instruction inst = ih.getInstruction();
334 if(inst instanceof BranchInstruction) {
335 branchTargets.set(((BranchInstruction) inst).getTarget().getPosition());
336 }
337 }
338 BitSet usedTargets = new BitSet();
339 List<SortedMap<Integer, Integer>> duplicates = new ArrayList<>();
340 for(TryBlock block : finallyTargets.values()) {
341 if(usedTargets.get(block.catchAnyAddress)) {
342 continue;
343 }
344 block.update(exceptionTargets, branchTargets, il, finallyTargets.keySet(), usedTargets);
345 if(!block.incorrect && block.duplicates.size() > 1) {
346 duplicates.add(block.duplicates);
347 }
348 }
349 if(duplicates.isEmpty()) {
350 return NONE_FINALLY_INFO;
351 }
352 return new FinallyDuplicatesInfo(il.getInstructionPositions(), duplicates);
353 }
354
355 @Override
356 public void registerWith(IAnalysisCache analysisCache) {
357 analysisCache.registerMethodAnalysisEngine(FinallyDuplicatesInfo.class, this);
358 }
359 }
2525
2626 /**
2727 * Factory for ForwardTypeQualifierDataflowFactory objects.
28 *
28 *
2929 * @author David Hovemeyer
3030 */
3131 public class ForwardTypeQualifierDataflowFactoryFactory extends AnalysisFactory<ForwardTypeQualifierDataflowFactory> {
3535
3636 /*
3737 * (non-Javadoc)
38 *
38 *
3939 * @see
4040 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4141 * .classfile.IAnalysisCache, java.lang.Object)
4242 */
43 @Override
4344 public ForwardTypeQualifierDataflowFactory analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor)
4445 throws CheckedAnalysisException {
4546
3737 /**
3838 * Analysis engine to produce IsNullValueDataflow objects for an analyzed
3939 * method.
40 *
40 *
4141 * @author David Hovemeyer
4242 */
4343 public class IsNullValueDataflowFactory extends AnalysisFactory<IsNullValueDataflow> {
5050
5151 /*
5252 * (non-Javadoc)
53 *
53 *
5454 * @see
5555 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
5656 * .classfile.IAnalysisCache, java.lang.Object)
5757 */
58 @Override
5859 public IsNullValueDataflow analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
5960 MethodGen methodGen = getMethodGen(analysisCache, descriptor);
6061 if (methodGen == null) {
5050 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
5151 * .classfile.IAnalysisCache, java.lang.Object)
5252 */
53 @Override
5354 public JavaClass analyze(IAnalysisCache analysisCache, ClassDescriptor descriptor) throws CheckedAnalysisException {
5455 try {
5556 ClassData classData = analysisCache.getClassAnalysis(ClassData.class, descriptor);
7879 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#registerWith(edu.umd.cs
7980 * .findbugs.classfile.IAnalysisCache)
8081 */
82 @Override
8183 public void registerWith(IAnalysisCache analysisCache) {
8284 analysisCache.registerClassAnalysisEngine(JavaClass.class, this);
8385 }
8789 *
8890 * @see edu.umd.cs.findbugs.classfile.IAnalysisEngine#canRecompute()
8991 */
92 @Override
9093 public boolean canRecompute() {
9194 // Currently, JavaClass objects are compared by reference equality in
9295 // some places,
3131 /**
3232 * Analysis engine to produce LiveLocalStoreDataflow objects for analyzed
3333 * methods.
34 *
34 *
3535 * @author David Hovemeyer
3636 */
3737 public class LiveLocalStoreDataflowFactory extends AnalysisFactory<LiveLocalStoreDataflow> {
4444
4545 /*
4646 * (non-Javadoc)
47 *
47 *
4848 * @see
4949 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
5050 * .classfile.IAnalysisCache, java.lang.Object)
5151 */
52 @Override
5253 public LiveLocalStoreDataflow analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor)
5354 throws CheckedAnalysisException {
5455 MethodGen methodGen = getMethodGen(analysisCache, descriptor);
2727
2828 /**
2929 * Analysis engine to produce LoadDataflow objects for analyzed methods.
30 *
30 *
3131 * @author David Hovemeyer
3232 */
3333 public class LoadDataflowFactory extends AnalysisFactory<LoadDataflow> {
4040
4141 /*
4242 * (non-Javadoc)
43 *
43 *
4444 * @see
4545 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4646 * .classfile.IAnalysisCache, java.lang.Object)
4747 */
48 @Override
4849 public LoadDataflow analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
4950 MethodGen methodGen = getMethodGen(analysisCache, descriptor);
50 if (methodGen == null)
51 if (methodGen == null) {
5152 return null;
53 }
5254 LoadAnalysis analysis = new LoadAnalysis(getDepthFirstSearch(analysisCache, descriptor), getConstantPoolGen(
5355 analysisCache, descriptor.getClassDescriptor()));
5456 LoadDataflow dataflow = new LoadDataflow(getCFG(analysisCache, descriptor), analysis);
4444 * (there is no need to remember stores of fields that are never read, or loads
4545 * of fields that are only loaded in one location). However, it might be useful
4646 * for other kinds of analysis.
47 *
47 *
4848 * <p>
4949 * The tricky part is that in addition to fields loaded and stored with
5050 * get/putfield and get/putstatic, we also try to figure out field accessed
6969
7070 /*
7171 * (non-Javadoc)
72 *
72 *
7373 * @see
7474 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
7575 * .classfile.IAnalysisCache, java.lang.Object)
7676 */
77 @Override
7778 public LoadedFieldSet analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
7879 MethodGen methodGen = getMethodGen(analysisCache, descriptor);
79 if (methodGen == null)
80 if (methodGen == null) {
8081 return null;
82 }
8183 InstructionList il = methodGen.getInstructionList();
8284
8385 LoadedFieldSet loadedFieldSet = new LoadedFieldSet(methodGen);
98100 * + " at " + inv); }
99101 */
100102 if (access != null) {
101 if (access.isLoad())
103 if (access.isLoad()) {
102104 loadedFieldSet.addLoad(handle, access.getField());
103 else
105 } else {
104106 loadedFieldSet.addStore(handle, access.getField());
107 }
105108 }
106109 }
107110 } else if (fieldInstructionOpcodeSet.get(opcode)) {
108111 boolean isLoad = (opcode == Constants.GETFIELD || opcode == Constants.GETSTATIC);
109112 XField field = Hierarchy.findXField((FieldInstruction) ins, cpg);
110113 if (field != null) {
111 if (isLoad)
114 if (isLoad) {
112115 loadedFieldSet.addLoad(handle, field);
113 else
116 } else {
114117 loadedFieldSet.addStore(handle, field);
118 }
115119 }
116120 }
117121 } catch (ClassNotFoundException e) {
2424
2525 /**
2626 * Analysis engine to produce LockChecker objects for analyzed methods.
27 *
27 *
2828 * @author David Hovemeyer
2929 */
3030 public class LockCheckerFactory extends AnalysisFactory<LockChecker> {
3737
3838 /*
3939 * (non-Javadoc)
40 *
40 *
4141 * @see
4242 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4343 * .classfile.IAnalysisCache, java.lang.Object)
4444 */
45 @Override
4546 public LockChecker analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
4647 LockChecker lockChecker = new LockChecker(descriptor);
4748
3131
3232 /**
3333 * Analysis engine to produce LockDataflow objects for analyzed methods.
34 *
34 *
3535 * @author David Hovemeyer
3636 */
3737 public class LockDataflowFactory extends AnalysisFactory<LockDataflow> {
4444
4545 /*
4646 * (non-Javadoc)
47 *
47 *
4848 * @see
4949 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
5050 * .classfile.IAnalysisCache, java.lang.Object)
5151 */
52 @Override
5253 public LockDataflow analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
5354 MethodGen methodGen = getMethodGen(analysisCache, descriptor);
5455 if (methodGen == null) {
4242
4343 /*
4444 * (non-Javadoc)
45 *
45 *
4646 * @see
4747 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4848 * .classfile.IAnalysisCache, java.lang.Object)
4949 */
50 @Override
5051 public MethodBytecodeSet analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
5152 Method method = analysisCache.getMethodAnalysis(Method.class, descriptor);
5253 Code code = method.getCode();
7475
7576 /*
7677 * (non-Javadoc)
77 *
78 *
7879 * @see
7980 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#registerWith(edu.umd.cs
8081 * .findbugs.classfile.IAnalysisCache)
2828
2929 /**
3030 * Method analysis engine to produce BCEL Method objects.
31 *
31 *
3232 * @author David Hovemeyer
3333 */
3434 public class MethodFactory extends AnalysisFactory<Method> {
3939
4040 /*
4141 * (non-Javadoc)
42 *
42 *
4343 * @see
4444 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4545 * .classfile.IAnalysisCache, java.lang.Object)
4646 */
47 @Override
4748 public Method analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
4849 JavaClass jclass = analysisCache.getClassAnalysis(JavaClass.class, descriptor.getClassDescriptor());
4950 Method[] methodList = jclass.getMethods();
6869
6970 /*
7071 * (non-Javadoc)
71 *
72 *
7273 * @see
7374 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#registerWith(edu.umd.cs
7475 * .findbugs.classfile.IAnalysisCache)
2525 import edu.umd.cs.findbugs.ba.AnalysisContext;
2626 import edu.umd.cs.findbugs.ba.AnalysisFeatures;
2727 import edu.umd.cs.findbugs.ba.JavaClassAndMethod;
28 import edu.umd.cs.findbugs.ba.XFactory;
29 import edu.umd.cs.findbugs.ba.XMethod;
2830 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
2931 import edu.umd.cs.findbugs.classfile.IAnalysisCache;
3032 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
3133
3234 /**
3335 * Analysis engine to produce MethodGen objects for analyzed methods.
34 *
36 *
3537 * @author David Hovemeyer
3638 * @author Bill Pugh
3739 */
4547
4648 /*
4749 * (non-Javadoc)
48 *
50 *
4951 * @see
5052 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
5153 * .classfile.IAnalysisCache, java.lang.Object)
5254 */
55 @Override
5356 public MethodGen analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
5457 Method method = getMethod(analysisCache, descriptor);
5558
56 if (method.getCode() == null)
59 if (method.getCode() == null) {
5760 return null;
61 }
62 XMethod xmethod = XFactory.createXMethod(descriptor);
63 if (xmethod.usesInvokeDynamic() && false) {
64 AnalysisContext.currentAnalysisContext().analysisSkippedDueToInvokeDynamic(xmethod);
65 return null;
66 }
67
5868 try {
5969 AnalysisContext analysisContext = AnalysisContext.currentAnalysisContext();
6070 JavaClass jclass = getJavaClass(analysisCache, descriptor.getClassDescriptor());
6171 ConstantPoolGen cpg = getConstantPoolGen(analysisCache, descriptor.getClassDescriptor());
6272
6373 String methodName = method.getName();
64 int codeLength = method.getCode().getLength();
74 int codeLength = method.getCode().getCode().length;
6575 String superclassName = jclass.getSuperclassName();
66 if (codeLength > 6000 && methodName.equals("<clinit>") && superclassName.equals("java.lang.Enum")) {
76 if (codeLength > 6000 && "<clinit>".equals(methodName) && "java.lang.Enum".equals(superclassName)) {
6777 analysisContext.getLookupFailureCallback().reportSkippedAnalysis(
6878 new JavaClassAndMethod(jclass, method).toMethodDescriptor());
6979 return null;
7080 }
7181 if (analysisContext.getBoolProperty(AnalysisFeatures.SKIP_HUGE_METHODS)) {
72 if (codeLength > 6000 || (methodName.equals("<clinit>") || methodName.equals("getContents")) && codeLength > 2000) {
82 if (codeLength > 6000 || ("<clinit>".equals(methodName) || "getContents".equals(methodName)) && codeLength > 2000) {
7383 analysisContext.getLookupFailureCallback().reportSkippedAnalysis(
7484 new JavaClassAndMethod(jclass, method).toMethodDescriptor());
7585 return null;
2525
2626 /**
2727 * PostDominatorsAnalysis variant in which all exception edges are ignored.
28 *
28 *
2929 * @author David Hovemeyer
3030 */
3131 public class NonExceptionPostdominatorsAnalysis extends PostDominatorsAnalysis {
3232 /**
3333 * Constructor.
34 *
34 *
3535 * @param cfg
3636 * the CFG to compute dominator relationships for
3737 * @param rdfs
2828 /**
2929 * Analysis engine to produce NonExceptionPostDominatorsAnalysis objects for
3030 * analyzed methods.
31 *
31 *
3232 * @author David Hovemeyer
3333 */
3434 public class NonExceptionPostdominatorsAnalysisFactory extends AnalysisFactory<NonExceptionPostdominatorsAnalysis> {
3838
3939 /*
4040 * (non-Javadoc)
41 *
41 *
4242 * @see
4343 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4444 * .classfile.IAnalysisCache, java.lang.Object)
4545 */
46 @Override
4647 public NonExceptionPostdominatorsAnalysis analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor)
4748 throws CheckedAnalysisException {
4849 CFG cfg = getCFG(analysisCache, descriptor);
3131 * Implicit exception edges correspond to undeclared runtime exceptions; thus,
3232 * this analysis considers only normal control edges and declared exception
3333 * edges.
34 *
34 *
3535 * @author David Hovemeyer
3636 */
3737 public class NonImplicitExceptionPostDominatorsAnalysis extends PostDominatorsAnalysis {
3838 public NonImplicitExceptionPostDominatorsAnalysis(CFG cfg, ReverseDepthFirstSearch rdfs, DepthFirstSearch dfs) {
3939 super(cfg, rdfs, dfs, new EdgeChooser() {
40 @Override
4041 public boolean choose(Edge edge) {
4142 return !edge.isExceptionEdge() || edge.isFlagSet(EdgeTypes.EXPLICIT_EXCEPTIONS_FLAG);
4243 }
2929 /**
3030 * Analysis engine to produce NonImplicitExceptionPostDominatorsAnalysis objects
3131 * for analyzed methods.
32 *
32 *
3333 * @author David Hovemeyer
3434 */
3535 public class NonImplicitExceptionPostDominatorsAnalysisFactory extends
36 AnalysisFactory<NonImplicitExceptionPostDominatorsAnalysis> {
36 AnalysisFactory<NonImplicitExceptionPostDominatorsAnalysis> {
3737 /**
3838 * Constructor.
3939 */
4343
4444 /*
4545 * (non-Javadoc)
46 *
46 *
4747 * @see
4848 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4949 * .classfile.IAnalysisCache, java.lang.Object)
5050 */
51 @Override
5152 public NonImplicitExceptionPostDominatorsAnalysis analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor)
5253 throws CheckedAnalysisException {
5354 CFG cfg = getCFG(analysisCache, descriptor);
3838
3939 /**
4040 * Analysis factory which creates instances of ObligationDataflow.
41 *
41 *
4242 * @author David Hovemeyer
4343 */
4444 public class ObligationDataflowFactory extends AnalysisFactory<ObligationDataflow> {
4949 super("Obligation dataflow", ObligationDataflow.class);
5050 }
5151
52 @Override
5253 public ObligationDataflow analyze(IAnalysisCache analysisCache, MethodDescriptor methodDescriptor)
5354 throws CheckedAnalysisException {
5455 CFG cfg = analysisCache.getMethodAnalysis(CFG.class, methodDescriptor);
2727
2828 /**
2929 * Analysis engine to produce parameter signature lists for analyzed methods.
30 *
30 *
3131 * @author David Hovemeyer
3232 */
3333 public class ParameterSignatureListFactory extends AnalysisFactory<String[]> {
4141
4242 /*
4343 * (non-Javadoc)
44 *
44 *
4545 * @see
4646 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4747 * .classfile.IAnalysisCache, java.lang.Object)
4848 */
49 @Override
4950 public String[] analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
5051 SignatureParser parser = new SignatureParser(descriptor.getSignature());
5152 ArrayList<String> resultList = new ArrayList<String>();
2727
2828 /**
2929 * Analysis engine to produce ReturnPathDataflow objects for analyzed methods.
30 *
30 *
3131 * @author David Hovemeyer
3232 */
3333 public class ReturnPathDataflowFactory extends AnalysisFactory<ReturnPathDataflow> {
4040
4141 /*
4242 * (non-Javadoc)
43 *
43 *
4444 * @see
4545 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4646 * .classfile.IAnalysisCache, java.lang.Object)
4747 */
48 @Override
4849 public ReturnPathDataflow analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
4950 CFG cfg = getCFG(analysisCache, descriptor);
5051 DepthFirstSearch dfs = getDepthFirstSearch(analysisCache, descriptor);
2929 /**
3030 * Analysis engine to produce ReturnPathTypeDataflow objects for analyzed
3131 * methods.
32 *
32 *
3333 * @author David Hovemeyer
3434 */
3535 public class ReturnPathTypeDataflowFactory extends AnalysisFactory<ReturnPathTypeDataflow> {
4242
4343 /*
4444 * (non-Javadoc)
45 *
45 *
4646 * @see
4747 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4848 * .classfile.IAnalysisCache, java.lang.Object)
4949 */
50 @Override
5051 public ReturnPathTypeDataflow analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor)
5152 throws CheckedAnalysisException {
5253 CFG cfg = getCFG(analysisCache, descriptor);
2626 /**
2727 * Analysis engine to produce ReverseDepthFirstSearch objects for analyzed
2828 * methods.
29 *
29 *
3030 * @author David Hovemeyer
3131 */
3232 public class ReverseDepthFirstSearchFactory extends AnalysisFactory<ReverseDepthFirstSearch> {
3939
4040 /*
4141 * (non-Javadoc)
42 *
42 *
4343 * @see
4444 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4545 * .classfile.IAnalysisCache, java.lang.Object)
4646 */
47 @Override
4748 public ReverseDepthFirstSearch analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor)
4849 throws CheckedAnalysisException {
4950 CFG cfg = getCFG(analysisCache, descriptor);
2727
2828 /**
2929 * Analysis engine to produce StoreDataflow objects for analyzed methods.
30 *
30 *
3131 * @author David Hovemeyer
3232 */
3333 public class StoreDataflowFactory extends AnalysisFactory<StoreDataflow> {
4040
4141 /*
4242 * (non-Javadoc)
43 *
43 *
4444 * @see
4545 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4646 * .classfile.IAnalysisCache, java.lang.Object)
4747 */
48 @Override
4849 public StoreDataflow analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
4950 MethodGen methodGen = getMethodGen(analysisCache, descriptor);
50 if (methodGen == null)
51 if (methodGen == null) {
5152 return null;
53 }
5254 StoreAnalysis analysis = new StoreAnalysis(getDepthFirstSearch(analysisCache, descriptor), getConstantPoolGen(
5355 analysisCache, descriptor.getClassDescriptor()));
5456 StoreDataflow dataflow = new StoreDataflow(getCFG(analysisCache, descriptor), analysis);
3636
3737 /**
3838 * Analysis engine to produce TypeDataflow objects for analyzed methods.
39 *
39 *
4040 * @author David Hovemeyer
4141 */
4242 public class TypeDataflowFactory extends AnalysisFactory<TypeDataflow> {
4949
5050 /*
5151 * (non-Javadoc)
52 *
52 *
5353 * @see
5454 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
5555 * .classfile.IAnalysisCache, java.lang.Object)
5656 */
57 @Override
5758 public TypeDataflow analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
5859 MethodGen methodGen = getMethodGen(analysisCache, descriptor);
5960 if (methodGen == null) {
3434 /**
3535 * Analysis engine to produce UnconditionalValueDerefDataflow objects for
3636 * analyzed methods.
37 *
37 *
3838 * @author David Hovemeyer
3939 */
4040 public class UnconditionalValueDerefDataflowFactory extends AnalysisFactory<UnconditionalValueDerefDataflow> {
4747
4848 /*
4949 * (non-Javadoc)
50 *
50 *
5151 * @see
5252 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
5353 * .classfile.IAnalysisCache, java.lang.Object)
5454 */
55 @Override
5556 public UnconditionalValueDerefDataflow analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor)
5657 throws CheckedAnalysisException {
5758 MethodGen methodGen = getMethodGen(analysisCache, descriptor);
2222 import edu.umd.cs.findbugs.ba.MethodBytecodeSet;
2323
2424 public class UnpackedBytecodeCallback implements BytecodeScanner.Callback {
25 private MethodBytecodeSet bytecodeSet;
25 private final MethodBytecodeSet bytecodeSet;
2626
27 private short[] offsetToOpcodeMap;
27 private final short[] offsetToOpcodeMap;
2828
2929 public UnpackedBytecodeCallback(int codeSize) {
3030 this.bytecodeSet = new MethodBytecodeSet();
3333
3434 /*
3535 * (non-Javadoc)
36 *
36 *
3737 * @see
3838 * edu.umd.cs.findbugs.ba.BytecodeScanner.Callback#handleInstruction(int,
3939 * int)
4040 */
41 @Override
4142 public void handleInstruction(int opcode, int index) {
4243 bytecodeSet.set(opcode);
4344 offsetToOpcodeMap[index] = (short) opcode;
66 * well as a map of bytecode offsets to opcodes.
77 */
88 public class UnpackedCode {
9 private MethodBytecodeSet bytecodeSet;
9 private final MethodBytecodeSet bytecodeSet;
1010
11 private short[] offsetToBytecodeMap;
11 private final short[] offsetToBytecodeMap;
1212
1313 public UnpackedCode(MethodBytecodeSet bytecodeSet, short[] offsetToBytecodeMap) {
1414 this.bytecodeSet = bytecodeSet;
2727
2828 /**
2929 * Analysis engine to produce UnpackedCode objects for analyzed methods.
30 *
30 *
3131 * @author David Hovemeyer
3232 */
3333 public class UnpackedCodeFactory extends AnalysisFactory<UnpackedCode> {
4040
4141 /*
4242 * (non-Javadoc)
43 *
43 *
4444 * @see
4545 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4646 * .classfile.IAnalysisCache, java.lang.Object)
4747 */
48 @Override
4849 public UnpackedCode analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
4950 Method method = getMethod(analysisCache, descriptor);
5051 Code code = method.getCode();
51 if (code == null)
52 if (code == null) {
5253 return null;
54 }
5355
5456 byte[] instructionList = code.getCode();
5557
3535
3636 /*
3737 * (non-Javadoc)
38 *
38 *
3939 * @see
4040 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
4141 * .classfile.IAnalysisCache, java.lang.Object)
4242 */
43 @Override
4344 public UsagesRequiringNonNullValues analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor)
4445 throws CheckedAnalysisException {
4546 // ClassContext classContext = getClassContext(jclass);
3939
4040 /**
4141 * Analysis engine to produce ValueNumberDataflow objects for analyzed methods.
42 *
42 *
4343 * @author David Hovemeyer
4444 */
4545 public class ValueNumberDataflowFactory extends AnalysisFactory<ValueNumberDataflow> {
5252
5353 /*
5454 * (non-Javadoc)
55 *
55 *
5656 * @see
5757 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
5858 * .classfile.IAnalysisCache, java.lang.Object)
5959 */
60 @Override
6061 public ValueNumberDataflow analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
6162 MethodGen methodGen = getMethodGen(analysisCache, descriptor);
6263 if (methodGen == null) {
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2008 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.classfile.engine.bcel;
20
21 import static org.apache.bcel.Constants.*;
22
23 import java.util.ArrayList;
24 import java.util.BitSet;
25 import java.util.Collections;
26 import java.util.Comparator;
27 import java.util.HashMap;
28 import java.util.HashSet;
29 import java.util.IdentityHashMap;
30 import java.util.Iterator;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Map.Entry;
34 import java.util.NoSuchElementException;
35 import java.util.Set;
36 import java.util.SortedMap;
37 import java.util.TreeMap;
38
39 import javax.annotation.Nullable;
40
41 import org.apache.bcel.classfile.Constant;
42 import org.apache.bcel.classfile.ConstantCP;
43 import org.apache.bcel.classfile.ConstantNameAndType;
44 import org.apache.bcel.classfile.ConstantObject;
45 import org.apache.bcel.classfile.ConstantPool;
46 import org.apache.bcel.classfile.ConstantUtf8;
47 import org.apache.bcel.classfile.LocalVariable;
48 import org.apache.bcel.classfile.LocalVariableTable;
49 import org.apache.bcel.classfile.Method;
50 import org.apache.bcel.generic.ARRAYLENGTH;
51 import org.apache.bcel.generic.CPInstruction;
52 import org.apache.bcel.generic.ConstantPushInstruction;
53 import org.apache.bcel.generic.GETFIELD;
54 import org.apache.bcel.generic.GETSTATIC;
55 import org.apache.bcel.generic.IFNE;
56 import org.apache.bcel.generic.INVOKEVIRTUAL;
57 import org.apache.bcel.generic.IfInstruction;
58 import org.apache.bcel.generic.Instruction;
59 import org.apache.bcel.generic.InstructionHandle;
60 import org.apache.bcel.generic.LCMP;
61 import org.apache.bcel.generic.LoadInstruction;
62 import org.apache.bcel.generic.MethodGen;
63 import org.apache.bcel.generic.PushInstruction;
64 import org.apache.bcel.generic.Type;
65
66 import edu.umd.cs.findbugs.ba.BasicBlock;
67 import edu.umd.cs.findbugs.ba.CFG;
68 import edu.umd.cs.findbugs.ba.ClassContext;
69 import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
70 import edu.umd.cs.findbugs.ba.Edge;
71 import edu.umd.cs.findbugs.ba.EdgeTypes;
72 import edu.umd.cs.findbugs.ba.Location;
73 import edu.umd.cs.findbugs.ba.MethodUnprofitableException;
74 import edu.umd.cs.findbugs.ba.XFactory;
75 import edu.umd.cs.findbugs.ba.XMethod;
76 import edu.umd.cs.findbugs.ba.vna.ValueNumber;
77 import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow;
78 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
79 import edu.umd.cs.findbugs.classfile.IAnalysisCache;
80 import edu.umd.cs.findbugs.classfile.IMethodAnalysisEngine;
81 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
82 import edu.umd.cs.findbugs.classfile.engine.bcel.FinallyDuplicatesInfoFactory.FinallyDuplicatesInfo;
83
84 /**
85 * @author Tagir Valeev
86 */
87 public class ValueRangeAnalysisFactory implements IMethodAnalysisEngine<ValueRangeAnalysisFactory.ValueRangeAnalysis> {
88 private static class TypeLongRange {
89 long min, max;
90 String signature;
91
92 public TypeLongRange(long min, long max, String signature) {
93 this.min = min;
94 this.max = max;
95 this.signature = signature;
96 }
97
98 public void addBordersTo(Set<Long> borders) {
99 borders.add(min);
100 if(min > Long.MIN_VALUE) {
101 borders.add(min-1);
102 }
103 borders.add(max);
104 if(max < Long.MAX_VALUE) {
105 borders.add(max+1);
106 }
107 }
108 }
109
110 private static final Map<String, TypeLongRange> typeRanges;
111
112 static {
113 typeRanges = new HashMap<>();
114 typeRanges.put("Z", new TypeLongRange(0, 1, "Z"));
115 typeRanges.put("B", new TypeLongRange(Byte.MIN_VALUE, Byte.MAX_VALUE, "B"));
116 typeRanges.put("S", new TypeLongRange(Short.MIN_VALUE, Short.MAX_VALUE, "S"));
117 typeRanges.put("I", new TypeLongRange(Integer.MIN_VALUE, Integer.MAX_VALUE, "I"));
118 typeRanges.put("J", new TypeLongRange(Long.MIN_VALUE, Long.MAX_VALUE, "J"));
119 typeRanges.put("C", new TypeLongRange(Character.MIN_VALUE, Character.MAX_VALUE, "C"));
120 }
121
122 public static class LongRangeSet implements Iterable<LongRangeSet> {
123 private final SortedMap<Long, Long> map = new TreeMap<>();
124 private final TypeLongRange range;
125
126 public LongRangeSet(String type) {
127 TypeLongRange range = typeRanges.get(type);
128 if(range == null) {
129 throw new IllegalArgumentException("Type is not supported: " + type);
130 }
131 map.put(range.min, range.max);
132 this.range = range;
133 }
134
135 private LongRangeSet(TypeLongRange range, long from, long to) {
136 this.range = range;
137 if(from < range.min) {
138 from = range.min;
139 }
140 if(to > range.max) {
141 to = range.max;
142 }
143 if (from <= to) {
144 map.put(from, to);
145 }
146 }
147
148 private LongRangeSet(TypeLongRange range) {
149 this.range = range;
150 }
151
152 public LongRangeSet gt(long value) {
153 splitGreater(value);
154 if(value == Long.MAX_VALUE) {
155 return new LongRangeSet(range);
156 }
157 return new LongRangeSet(range, value + 1, range.max);
158 }
159
160 public LongRangeSet ge(long value) {
161 splitGreater(value-1);
162 return new LongRangeSet(range, value, range.max);
163 }
164
165 public LongRangeSet lt(long value) {
166 splitGreater(value-1);
167 if(value == Long.MIN_VALUE) {
168 return new LongRangeSet(range);
169 }
170 return new LongRangeSet(range, range.min, value - 1);
171 }
172
173 public LongRangeSet le(long value) {
174 splitGreater(value);
175 return new LongRangeSet(range, range.min, value);
176 }
177
178 public LongRangeSet eq(long value) {
179 splitGreater(value);
180 splitGreater(value-1);
181 return new LongRangeSet(range, value, value);
182 }
183
184 public LongRangeSet ne(long value) {
185 splitGreater(value);
186 splitGreater(value-1);
187 LongRangeSet rangeSet = lt(value);
188 if (value < range.max) {
189 rangeSet.map.put(value + 1, range.max);
190 }
191 return rangeSet;
192 }
193
194 public void addBordersTo(Set<Long> borders) {
195 range.addBordersTo(borders);
196 }
197
198 public LongRangeSet empty() {
199 return new LongRangeSet(range);
200 }
201
202 public boolean intersects(LongRangeSet other) {
203 for (Entry<Long, Long> entry : map.entrySet()) {
204 SortedMap<Long, Long> subMap = entry.getValue() == Long.MAX_VALUE ? other.map.tailMap(entry.getKey()) : other.map
205 .subMap(entry.getKey(), entry.getValue() + 1);
206 if (!subMap.isEmpty()) {
207 return true;
208 }
209 SortedMap<Long, Long> headMap = other.map.headMap(entry.getKey());
210 if (!headMap.isEmpty() && headMap.get(headMap.lastKey()) >= entry.getKey()) {
211 return true;
212 }
213 }
214 return false;
215 }
216
217 public void splitGreater(long number) {
218 Long lNumber = number;
219 if(number == Long.MAX_VALUE) {
220 return;
221 }
222 Long nextNumber = number + 1;
223 SortedMap<Long, Long> headMap = map.headMap(nextNumber);
224 if (headMap.isEmpty()) {
225 return;
226 }
227 Long lastKey = headMap.lastKey();
228 Long lastValue = headMap.get(lastKey);
229 if (number >= lastValue) {
230 return;
231 }
232 map.put(lastKey, lNumber);
233 map.put(nextNumber, lastValue);
234 }
235
236 public String getSignature() {
237 return range.signature;
238 }
239
240 public boolean isEmpty() {
241 return map.isEmpty();
242 }
243
244 public boolean isFull() {
245 if(map.size() != 1) {
246 return false;
247 }
248 Long min = map.firstKey();
249 Long max = map.get(min);
250 return min <= range.min && max >= range.max;
251 }
252
253 @Override
254 public String toString() {
255 StringBuilder sb = new StringBuilder();
256 for (Entry<Long, Long> entry : map.entrySet()) {
257 if (sb.length() > 0) {
258 sb.append("+");
259 }
260 if (entry.getKey().equals(entry.getValue())) {
261 sb.append("{").append(entry.getKey()).append("}");
262 } else {
263 sb.append("[").append(entry.getKey()).append(", ").append(entry.getValue()).append("]");
264 }
265 }
266 return sb.toString();
267 }
268
269 @Override
270 public Iterator<LongRangeSet> iterator() {
271 final Iterator<Entry<Long, Long>> iterator = map.entrySet().iterator();
272 return new Iterator<ValueRangeAnalysisFactory.LongRangeSet>() {
273 @Override
274 public boolean hasNext() {
275 return iterator.hasNext();
276 }
277
278 @Override
279 public LongRangeSet next() {
280 Entry<Long, Long> entry = iterator.next();
281 return new LongRangeSet(range, entry.getKey(), entry.getValue());
282 }
283
284 @Override
285 public void remove() {
286 throw new UnsupportedOperationException();
287 }
288 };
289 }
290
291 private void add(Long start, Long end) {
292 SortedMap<Long, Long> headMap;
293 if (end < Long.MAX_VALUE) {
294 headMap = map.headMap(end + 1);
295 Long tailEnd = map.remove(end + 1);
296 if (tailEnd != null) {
297 end = tailEnd;
298 }
299 if (!headMap.isEmpty()) {
300 tailEnd = headMap.get(headMap.lastKey());
301 if (tailEnd > end) {
302 end = tailEnd;
303 }
304 }
305 }
306 headMap = map.headMap(start);
307 if (!headMap.isEmpty()) {
308 Long headStart = headMap.lastKey();
309 Long headEnd = map.get(headStart);
310 if (headEnd >= start - 1) {
311 map.remove(headStart);
312 start = headStart;
313 }
314 }
315 map.subMap(start, end).clear();
316 map.remove(end);
317 map.put(start, end);
318 }
319
320 public LongRangeSet add(LongRangeSet rangeSet) {
321 for(Entry<Long, Long> entry : rangeSet.map.entrySet()) {
322 add(entry.getKey(), entry.getValue());
323 }
324 return this;
325 }
326
327 public boolean same(LongRangeSet rangeSet) {
328 return map.equals(rangeSet.map);
329 }
330 }
331
332 private static class Branch {
333 final LongRangeSet trueSet;
334 final LongRangeSet trueReachedSet, falseReachedSet;
335 final String trueCondition, falseCondition;
336 final Number number;
337 final Set<Long> numbers = new HashSet<>();
338 final String varName;
339
340 public Branch(String varName, String trueCondition, String falseCondition, LongRangeSet trueSet, Number number) {
341 this.trueSet = trueSet;
342 this.trueCondition = fixCondition(trueCondition);
343 this.falseCondition = fixCondition(falseCondition);
344 this.trueReachedSet = trueSet.empty();
345 this.falseReachedSet = trueSet.empty();
346 trueSet.addBordersTo(numbers);
347 this.number = number;
348 this.varName = varName;
349 }
350
351 private String fixCondition(String condition) {
352 if (condition.equals("!= true")) {
353 return "== false";
354 }
355 if (condition.equals("!= false")) {
356 return "== true";
357 }
358 return condition;
359 }
360 }
361
362 private static class Value {
363 final String name;
364 final ValueNumber vn;
365 final String signature;
366
367 public Value(String name, @Nullable ValueNumber vn, String signature) {
368 this.name = name;
369 this.vn = vn;
370 this.signature = signature;
371 }
372 }
373
374 private static class Condition {
375 short opcode;
376 Value value;
377 Number number;
378
379 public Condition(short opcode, Value value, Number number) {
380 super();
381 this.opcode = opcode;
382 this.value = value;
383 this.number = number;
384 }
385 }
386
387 private static class Context {
388 final ConstantPool cp;
389 final LocalVariableTable lvTable;
390 final Map<Integer, Value> types;
391 final ValueNumberDataflow vnaDataflow;
392
393 public Context(ConstantPool cp, LocalVariableTable lvTable, Map<Integer, Value> types, ValueNumberDataflow vnaDataflow) {
394 this.cp = cp;
395 this.lvTable = lvTable;
396 this.types = types;
397 this.vnaDataflow = vnaDataflow;
398 }
399
400 public Condition extractCondition(BackIterator iterator) throws DataflowAnalysisException {
401 Instruction comparisonInstruction = iterator.next().getInstruction();
402 if (!(comparisonInstruction instanceof IfInstruction)) {
403 return null;
404 }
405 short cmpOpcode = comparisonInstruction.getOpcode();
406 int nargs = ((IfInstruction) comparisonInstruction).consumeStack(null);
407 if (nargs == 2) {
408 return extractTwoArgCondition(iterator, cmpOpcode, "I");
409 } else if (nargs == 1) {
410 Object val = extractValue(iterator, "I");
411 if(val instanceof Value) {
412 return new Condition(cmpOpcode, (Value) val, 0);
413 } else if(val instanceof LCMP) {
414 return extractTwoArgCondition(iterator, cmpOpcode, "J");
415 }
416 }
417 return null;
418 }
419
420 private Object extractValue(BackIterator iterator, String defSignature) throws DataflowAnalysisException {
421 if(!iterator.hasNext()) {
422 return null;
423 }
424 BasicBlock block = iterator.block;
425 InstructionHandle ih = iterator.next();
426 Instruction inst = ih.getInstruction();
427 if (inst instanceof ConstantPushInstruction) {
428 return ((ConstantPushInstruction) inst).getValue();
429 }
430 if (inst instanceof CPInstruction && inst instanceof PushInstruction) {
431 Constant constant = cp.getConstant(((CPInstruction) inst).getIndex());
432 if (constant instanceof ConstantObject) {
433 Object value = ((ConstantObject) constant).getConstantValue(cp);
434 if (value instanceof Number) {
435 return value;
436 }
437 }
438 return inst;
439 }
440 if(inst instanceof ARRAYLENGTH) {
441 Object valueObj = extractValue(iterator, defSignature);
442 if(valueObj instanceof Value) {
443 Value value = (Value)valueObj;
444 return new Value(value.name+".length", value.vn, "I");
445 }
446 return null;
447 }
448 if(inst instanceof GETFIELD) {
449 Object valueObj = extractValue(iterator, defSignature);
450 if(valueObj instanceof Value) {
451 Value value = (Value)valueObj;
452 ConstantCP desc = (ConstantCP)cp.getConstant(((GETFIELD)inst).getIndex());
453 ConstantNameAndType nameAndType = (ConstantNameAndType) cp.getConstant(desc.getNameAndTypeIndex());
454 String name = ((ConstantUtf8)cp.getConstant(nameAndType.getNameIndex())).getBytes();
455 String signature = ((ConstantUtf8)cp.getConstant(nameAndType.getSignatureIndex())).getBytes();
456 return new Value(value.name+"."+name, vnaDataflow.getFactAfterLocation(new Location(ih, block)).getStackValue(0), signature);
457 }
458 return null;
459 }
460 if(inst instanceof INVOKEVIRTUAL) {
461 ConstantCP desc = (ConstantCP)cp.getConstant(((INVOKEVIRTUAL)inst).getIndex());
462 ConstantNameAndType nameAndType = (ConstantNameAndType) cp.getConstant(desc.getNameAndTypeIndex());
463 String className = cp.getConstantString(desc.getClassIndex(), CONSTANT_Class);
464 String name = ((ConstantUtf8)cp.getConstant(nameAndType.getNameIndex())).getBytes();
465 String signature = ((ConstantUtf8)cp.getConstant(nameAndType.getSignatureIndex())).getBytes();
466 if(className.equals("java/lang/Integer") && name.equals("intValue") && signature.equals("()I") ||
467 className.equals("java/lang/Long") && name.equals("longValue") && signature.equals("()J") ||
468 className.equals("java/lang/Short") && name.equals("shortValue") && signature.equals("()S") ||
469 className.equals("java/lang/Byte") && name.equals("byteValue") && signature.equals("()B") ||
470 className.equals("java/lang/Boolean") && name.equals("booleanValue") && signature.equals("()Z") ||
471 className.equals("java/lang/Character") && name.equals("charValue") && signature.equals("()C")) {
472 Object valueObj = extractValue(iterator, defSignature);
473 if(valueObj instanceof Value) {
474 Value value = (Value)valueObj;
475 return new Value(value.name, value.vn, String.valueOf(signature.charAt(signature.length()-1)));
476 }
477 }
478 if(className.equals("java/lang/String") && name.equals("length") && signature.equals("()I")) {
479 Object valueObj = extractValue(iterator, defSignature);
480 if(valueObj instanceof Value) {
481 Value value = (Value)valueObj;
482 return new Value(value.name+".length()", value.vn, "I");
483 }
484 }
485 return null;
486 }
487 if(inst instanceof LoadInstruction) {
488 int index = ((LoadInstruction)inst).getIndex();
489 LocalVariable lv = lvTable == null ? null : lvTable.getLocalVariable(index, ih.getPosition());
490 String name, signature;
491 if(lv == null) {
492 name = "local$"+index;
493 if(types.containsKey(index)) {
494 signature = types.get(index).signature;
495 name = types.get(index).name;
496 } else {
497 signature = defSignature;
498 }
499 } else {
500 name = lv.getName();
501 signature = lv.getSignature();
502 }
503 return new Value(name, vnaDataflow.getFactAfterLocation(new Location(ih, block)).getStackValue(0), signature);
504 }
505 return inst;
506 }
507
508 /**
509 * @param opcode
510 * @return opcode which returns the same result when arguments are placed in opposite order
511 */
512 private static short revertOpcode(short opcode) {
513 switch (opcode) {
514 case IF_ICMPGE:
515 return IF_ICMPLE;
516 case IF_ICMPGT:
517 return IF_ICMPLT;
518 case IF_ICMPLE:
519 return IF_ICMPGE;
520 case IF_ICMPLT:
521 return IF_ICMPGT;
522 case IFLE:
523 return IFGE;
524 case IFGE:
525 return IFLE;
526 case IFGT:
527 return IFLT;
528 case IFLT:
529 return IFGT;
530 default:
531 return opcode;
532 }
533 }
534
535 private Condition extractTwoArgCondition(BackIterator iterator, short cmpOpcode, String signature) throws DataflowAnalysisException {
536 Object val2 = extractValue(iterator, signature);
537 if(val2 instanceof Instruction) {
538 return null;
539 }
540 Object val1 = extractValue(iterator, signature);
541 if(val1 instanceof Instruction) {
542 return null;
543 }
544 if (!(val1 instanceof Value) && !(val2 instanceof Value)) {
545 return null;
546 }
547 if (!(val1 instanceof Value)) {
548 Object tmp = val1;
549 val1 = val2;
550 val2 = tmp;
551 cmpOpcode = revertOpcode(cmpOpcode);
552 }
553 if(!(val2 instanceof Number)) {
554 return null;
555 }
556 return new Condition(cmpOpcode, (Value)val1, (Number)val2);
557 }
558 }
559
560 private static class VariableData {
561 final LongRangeSet splitSet;
562 final Map<Edge, Branch> edges = new IdentityHashMap<>();
563 final BitSet reachableBlocks = new BitSet();
564
565 public VariableData(String type) {
566 splitSet = new LongRangeSet(type);
567 }
568
569 public void addBranch(Edge edge, Branch branch) {
570 edges.put(edge, branch);
571 }
572 }
573
574 public static class RedundantCondition {
575 private final Location location;
576 private final String trueCondition;
577 private final String signature;
578 private final boolean byType;
579 private final boolean hasDeadCode;
580 private final boolean border;
581 private final Location deadCodeLocation;
582 private final Location liveCodeLocation;
583 private final Number number;
584
585 public RedundantCondition(Location location, String trueCondition, boolean hasDeadCode, Location deadCodeLocation,
586 Location liveCodeLocation, String signature, boolean byType, Number number, boolean border) {
587 this.location = location;
588 this.trueCondition = trueCondition;
589 this.hasDeadCode = hasDeadCode;
590 this.deadCodeLocation = deadCodeLocation;
591 this.liveCodeLocation = liveCodeLocation;
592 this.signature = signature;
593 this.byType = byType;
594 this.number = number;
595 this.border = border;
596 }
597
598 public boolean isBorder() {
599 return border;
600 }
601
602 public Location getLocation() {
603 return location;
604 }
605
606 public String getTrueCondition() {
607 return trueCondition;
608 }
609
610 public boolean isDeadCodeUnreachable() {
611 return hasDeadCode;
612 }
613
614 public String getSignature() {
615 return signature;
616 }
617
618 public boolean isByType() {
619 return byType;
620 }
621
622 public Location getLiveCodeLocation() {
623 return liveCodeLocation;
624 }
625
626 public Location getDeadCodeLocation() {
627 return deadCodeLocation;
628 }
629
630 public Number getNumber() {
631 return number;
632 }
633 }
634
635 public static class ValueRangeAnalysis {
636 private List<RedundantCondition> redundantConditions = new ArrayList<>();
637
638 public ValueRangeAnalysis(List<RedundantCondition> redundantConditions) {
639 this.redundantConditions = redundantConditions;
640 }
641
642 public RedundantCondition[] getRedundantConditions() {
643 return redundantConditions.toArray(new RedundantCondition[redundantConditions.size()]);
644 }
645 }
646
647 @Override
648 public ValueRangeAnalysis analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor) throws CheckedAnalysisException {
649 XMethod xMethod = XFactory.createXMethod(descriptor);
650 if (xMethod.isNative() || xMethod.isSynthetic() || xMethod.isAbstract()) {
651 return null;
652 }
653 CFG cfg;
654 try {
655 cfg = analysisCache.getMethodAnalysis(CFG.class, descriptor);
656 } catch (MethodUnprofitableException e) {
657 return null;
658 }
659 if (cfg == null) {
660 return null;
661 }
662 ClassContext classContext = analysisCache.getClassAnalysis(ClassContext.class, descriptor.getClassDescriptor());
663 Method method = analysisCache.getMethodAnalysis(Method.class, descriptor);
664 Context context = new Context(cfg.getMethodGen().getConstantPool().getConstantPool(), method.getCode().getLocalVariableTable(),
665 getParameterTypes(descriptor), classContext.getValueNumberDataflow(method));
666 Map<ValueNumber, VariableData> analyzedArguments = new HashMap<>();
667 Map<Edge, Branch> allEdges = new IdentityHashMap<>();
668 for (Iterator<Edge> edgeIterator = cfg.edgeIterator(); edgeIterator.hasNext();) {
669 Edge edge = edgeIterator.next();
670 if (edge.getType() == EdgeTypes.IFCMP_EDGE) {
671 BasicBlock source = edge.getSource();
672 Condition condition = context.extractCondition(new BackIterator(cfg, source));
673 if(condition == null) {
674 continue;
675 }
676 ValueNumber valueNumber = condition.value.vn;
677 String varName = condition.value.name;
678 VariableData data = analyzedArguments.get(valueNumber);
679 if (data == null) {
680 try {
681 data = new VariableData(condition.value.signature);
682 } catch (IllegalArgumentException e) {
683 continue;
684 }
685 analyzedArguments.put(valueNumber, data);
686 }
687 Number number = condition.number;
688 String numberStr = convertNumber(data.splitSet.getSignature(), number);
689 Branch branch = null;
690 switch (condition.opcode) {
691 case IF_ICMPGT:
692 case IFGT:
693 branch = new Branch(varName, "> " + numberStr, "<= " + numberStr, data.splitSet.gt(number.longValue()), number);
694 break;
695 case IF_ICMPLE:
696 case IFLE:
697 branch = new Branch(varName, "<= " + numberStr, "> " + numberStr, data.splitSet.le(number.longValue()), number);
698 break;
699 case IF_ICMPGE:
700 case IFGE:
701 branch = new Branch(varName, ">= " + numberStr, "< " + numberStr, data.splitSet.ge(number.longValue()), number);
702 break;
703 case IF_ICMPLT:
704 case IFLT:
705 branch = new Branch(varName, "< " + numberStr, ">= " + numberStr, data.splitSet.lt(number.longValue()), number);
706 break;
707 case IF_ICMPEQ:
708 case IFEQ:
709 branch = new Branch(varName, "== " + numberStr, "!= " + numberStr, data.splitSet.eq(number.longValue()), number);
710 break;
711 case IF_ICMPNE:
712 case IFNE:
713 branch = new Branch(varName, "!= " + numberStr, "== " + numberStr, data.splitSet.ne(number.longValue()), number);
714 break;
715 default:
716 break;
717 }
718 if(branch != null) {
719 data.addBranch(edge, branch);
720 allEdges.put(edge, branch);
721 }
722 }
723 }
724 FinallyDuplicatesInfo fi = null;
725 List<RedundantCondition> redundantConditions = new ArrayList<>();
726 for (VariableData data : analyzedArguments.values()) {
727 for (LongRangeSet subRange : data.splitSet) {
728 BitSet reachedBlocks = new BitSet();
729 walkCFG(cfg, cfg.getEntry(), subRange, data.edges, reachedBlocks, new HashSet<Long>());
730 data.reachableBlocks.or(reachedBlocks);
731 }
732 }
733 for (VariableData data : analyzedArguments.values()) {
734 for (Entry<Edge, Branch> entry : data.edges.entrySet()) {
735 Branch branch = entry.getValue();
736 Edge edge = entry.getKey();
737 if (branch.trueReachedSet.isEmpty() ^ branch.falseReachedSet.isEmpty()) {
738 if(fi == null) {
739 fi = analysisCache.getMethodAnalysis(FinallyDuplicatesInfo.class, descriptor);
740 }
741 List<Edge> duplicates = fi.getDuplicates(cfg, edge);
742 if(!duplicates.isEmpty()) {
743 boolean trueValue = !branch.trueReachedSet.isEmpty();
744 boolean falseValue = !branch.falseReachedSet.isEmpty();
745 for(Edge dup : duplicates) {
746 Branch dupBranch = allEdges.get(dup);
747 if(dupBranch != null) {
748 trueValue |= !dupBranch.trueReachedSet.isEmpty();
749 falseValue |= !dupBranch.falseReachedSet.isEmpty();
750 }
751 }
752 if(trueValue && falseValue) {
753 continue;
754 }
755 }
756 BasicBlock trueTarget = edge.getTarget();
757 BasicBlock falseTarget = cfg.getSuccessorWithEdgeType(edge.getSource(), EdgeTypes.FALL_THROUGH_EDGE);
758 String condition;
759 BasicBlock deadTarget, aliveTarget;
760 if(branch.trueReachedSet.isEmpty()) {
761 condition = branch.varName + " " + branch.falseCondition;
762 deadTarget = trueTarget;
763 aliveTarget = falseTarget;
764 } else {
765 condition = branch.varName + " " + branch.trueCondition;
766 deadTarget = falseTarget;
767 aliveTarget = trueTarget;
768 }
769 redundantConditions.add(new RedundantCondition(Location.getLastLocation(edge.getSource()), condition,
770 !data.reachableBlocks.get(deadTarget.getLabel()), getLocation(deadTarget), getLocation(aliveTarget),
771 branch.trueSet.getSignature(), branch.trueSet.isEmpty() || branch.trueSet.isFull(),
772 branch.number, branch.numbers.contains(branch.number.longValue())));
773 }
774 }
775 }
776 if (!redundantConditions.isEmpty()) {
777 BitSet assertionBlocks = new BitSet();
778 MethodGen methodGen = cfg.getMethodGen();
779 Iterator<InstructionHandle> iterator = methodGen.getInstructionList().iterator();
780 while(iterator.hasNext()) {
781 InstructionHandle ih = iterator.next();
782 if(ih.getInstruction() instanceof GETSTATIC) {
783 Instruction next = ih.getNext().getInstruction();
784 if(next instanceof IFNE) {
785 GETSTATIC getStatic = (GETSTATIC)ih.getInstruction();
786 if ("$assertionsDisabled".equals(getStatic.getFieldName(methodGen.getConstantPool()))
787 && "Z".equals(getStatic.getSignature(methodGen.getConstantPool()))) {
788 int end = ((IFNE)next).getTarget().getPosition();
789 assertionBlocks.set(ih.getNext().getPosition(), end);
790 }
791 }
792 }
793 }
794 if(!assertionBlocks.isEmpty()) {
795 List<RedundantCondition> filtered = new ArrayList<>();
796 for(RedundantCondition condition : redundantConditions) {
797 if(!(assertionBlocks.get(condition.getLocation().getHandle().getPosition()))) {
798 // TODO: do not filter out failed asserts
799 filtered.add(condition);
800 }
801 }
802 redundantConditions = filtered;
803 }
804 Collections.sort(redundantConditions, new Comparator<RedundantCondition>() {
805 @Override
806 public int compare(RedundantCondition o1, RedundantCondition o2) {
807 return o1.location.compareTo(o2.location);
808 }
809 });
810 return new ValueRangeAnalysis(redundantConditions);
811 }
812 return null;
813 }
814
815 private static Location getLocation(BasicBlock block) {
816 InstructionHandle handle = block.getFirstInstruction();
817 if(handle == null) {
818 handle = block.getExceptionThrower();
819 }
820 return handle == null ? null : new Location(handle, block);
821 }
822
823 private static String convertNumber(String signature, Number number) {
824 long val = number.longValue();
825 switch (signature) {
826 case "Z":
827 if (val == 0) {
828 return "false";
829 }
830 return "true";
831 case "C":
832 if (val == '\n') {
833 return "'\\n'";
834 }
835 if (val == '\r') {
836 return "'\\r'";
837 }
838 if (val == '\b') {
839 return "'\\b'";
840 }
841 if (val == '\t') {
842 return "'\\t'";
843 }
844 if (val == '\'') {
845 return "'\\''";
846 }
847 if (val == '\\') {
848 return "'\\\\'";
849 }
850 if (val >= 32 && val < 128) {
851 return "'" + ((char) val) + "'";
852 }
853 return convertNumber(val);
854 case "I":
855 if(val >= 32 && val < 128) {
856 return val+" ('" + ((char) val) + "')";
857 }
858 return convertNumber(val);
859 default:
860 return convertNumber(val);
861 }
862 }
863
864 private static String convertNumber(long val) {
865 if(val == Long.MIN_VALUE) {
866 return "Long.MIN_VALUE";
867 }
868 if(val == Long.MAX_VALUE) {
869 return "Long.MAX_VALUE";
870 }
871 String suffix = "";
872 if (val > Integer.MAX_VALUE || val < Integer.MIN_VALUE) {
873 suffix = "L";
874 }
875 if (val > 128) {
876 return val + suffix + " (0x" + Long.toHexString(val) + suffix + ")";
877 }
878 return val + suffix;
879 }
880
881 private static Map<Integer, Value> getParameterTypes(MethodDescriptor descriptor) {
882 Type[] argumentTypes = Type.getArgumentTypes(descriptor.getSignature());
883 int j = 0;
884 Map<Integer, Value> result = new HashMap<>();
885 if(!descriptor.isStatic()) {
886 result.put(j++, new Value("this", null, "L"+descriptor.getSlashedClassName()+";"));
887 }
888 for (int i = 0; i < argumentTypes.length; i++) {
889 result.put(j, new Value("arg"+i, null, argumentTypes[i].getSignature()));
890 j += argumentTypes[i].getSize();
891 }
892 return result;
893 }
894
895 private static void walkCFG(CFG cfg, BasicBlock basicBlock, LongRangeSet subRange, Map<Edge, Branch> edges, BitSet reachedBlocks, Set<Long> numbers) {
896 reachedBlocks.set(basicBlock.getLabel());
897 for (Iterator<Edge> iterator = cfg.outgoingEdgeIterator(basicBlock); iterator.hasNext();) {
898 Edge edge = iterator.next();
899 Branch branch = edges.get(edge);
900 if (branch != null) {
901 branch.numbers.addAll(numbers);
902 numbers = new HashSet<>(numbers);
903 numbers.add(branch.number.longValue());
904 if (branch.trueSet.intersects(subRange)) {
905 branch.trueReachedSet.add(subRange);
906 } else {
907 branch.falseReachedSet.add(subRange);
908 continue;
909 }
910 }
911 BasicBlock target = edge.getTarget();
912 if (!reachedBlocks.get(target.getLabel())) {
913 walkCFG(cfg, target, subRange, edges, reachedBlocks, numbers);
914 }
915 if (branch != null) {
916 break;
917 }
918 }
919 }
920
921 private static class BackIterator implements Iterator<InstructionHandle> {
922 private BasicBlock block;
923 private InstructionHandle next;
924 private final CFG cfg;
925
926 public BackIterator(CFG cfg, BasicBlock block) {
927 this.block = block;
928 this.cfg = cfg;
929 this.next = block.getLastInstruction();
930 }
931
932 @Override
933 public boolean hasNext() {
934 return next != null;
935 }
936
937 @Override
938 public InstructionHandle next() {
939 if (!hasNext()) {
940 throw new NoSuchElementException();
941 }
942 InstructionHandle result = next;
943 if(result == block.getFirstInstruction()) {
944 do {
945 Iterator<Edge> edgeIterator = cfg.incomingEdgeIterator(block);
946 if(!edgeIterator.hasNext()) {
947 break;
948 }
949 Edge edge = edgeIterator.next();
950 if(!edgeIterator.hasNext() && edge.getType() == EdgeTypes.FALL_THROUGH_EDGE) {
951 block = edge.getSource();
952 } else {
953 break;
954 }
955 } while(block.isExceptionThrower());
956 }
957 next = (block.isExceptionThrower() || result == block.getFirstInstruction()) ? null : next.getPrev();
958 return result;
959 }
960
961 @Override
962 public void remove() {
963 throw new UnsupportedOperationException();
964 }
965 }
966
967 @Override
968 public void registerWith(IAnalysisCache analysisCache) {
969 analysisCache.registerMethodAnalysisEngine(ValueRangeAnalysis.class, this);
970 }
971 }
3030 * Abstract base class for implementations of IScannableCodeBase. Provides an
3131 * implementation of the getCodeBaseLocator(), containsSourceFiles(),
3232 * setApplicationCodeBase(), and isApplicationCodeBase() methods.
33 *
33 *
3434 * @author David Hovemeyer
3535 */
3636 public abstract class AbstractScannableCodeBase implements IScannableCodeBase {
37 private ICodeBaseLocator codeBaseLocator;
37 private final ICodeBaseLocator codeBaseLocator;
3838
3939 private boolean isAppCodeBase;
4040
4242
4343 private long lastModifiedTime;
4444
45 private Map<String, String> resourceNameTranslationMap;
45 private final Map<String, String> resourceNameTranslationMap;
4646
4747 public AbstractScannableCodeBase(ICodeBaseLocator codeBaseLocator) {
4848 this.codeBaseLocator = codeBaseLocator;
5252
5353 /*
5454 * (non-Javadoc)
55 *
55 *
5656 * @see edu.umd.cs.findbugs.classfile.ICodeBase#getCodeBaseLocator()
5757 */
58 @Override
5859 public ICodeBaseLocator getCodeBaseLocator() {
5960 return codeBaseLocator;
6061 }
6162
6263 /*
6364 * (non-Javadoc)
64 *
65 *
6566 * @see
6667 * edu.umd.cs.findbugs.classfile.IScannableCodeBase#containsSourceFiles()
6768 */
69 @Override
6870 public boolean containsSourceFiles() {
6971 return false;
7072
7274
7375 /*
7476 * (non-Javadoc)
75 *
77 *
7678 * @see
7779 * edu.umd.cs.findbugs.classfile.ICodeBase#setApplicationCodeBase(boolean)
7880 */
81 @Override
7982 public void setApplicationCodeBase(boolean isAppCodeBase) {
8083 this.isAppCodeBase = isAppCodeBase;
8184 }
8285
8386 /*
8487 * (non-Javadoc)
85 *
88 *
8689 * @see edu.umd.cs.findbugs.classfile.ICodeBase#isApplicationCodeBase()
8790 */
91 @Override
8892 public boolean isApplicationCodeBase() {
8993 return isAppCodeBase;
9094 }
9195
9296 /*
9397 * (non-Javadoc)
94 *
98 *
9599 * @see edu.umd.cs.findbugs.classfile.ICodeBase#setHowDiscovered(int)
96100 */
101 @Override
97102 public void setHowDiscovered(ICodeBase.Discovered howDiscovered) {
98103 this.howDiscovered = howDiscovered;
99104 }
100105
101106 /*
102107 * (non-Javadoc)
103 *
108 *
104109 * @see edu.umd.cs.findbugs.classfile.ICodeBase#getHowDiscovered()
105110 */
111 @Override
106112 public ICodeBase.Discovered getHowDiscovered() {
107113 return howDiscovered;
108114 }
109115
110116 /*
111117 * (non-Javadoc)
112 *
118 *
113119 * @see edu.umd.cs.findbugs.classfile.ICodeBase#setLastModifiedTime(long)
114120 */
121 @Override
115122 public void setLastModifiedTime(long lastModifiedTime) {
116123 if (lastModifiedTime > 0 && FindBugs.validTimestamp(lastModifiedTime)) {
117124 this.lastModifiedTime = lastModifiedTime;
125132
126133 /*
127134 * (non-Javadoc)
128 *
135 *
129136 * @see edu.umd.cs.findbugs.classfile.ICodeBase#getLastModifiedTime()
130137 */
138 @Override
131139 public long getLastModifiedTime() {
132140 return lastModifiedTime;
133141 }
134142
135143 public void addResourceNameTranslation(String origResourceName, String newResourceName) {
136 if (!origResourceName.equals(newResourceName))
144 if (!origResourceName.equals(newResourceName)) {
137145 resourceNameTranslationMap.put(origResourceName, newResourceName);
146 }
138147 }
139148
140149 public String translateResourceName(String resourceName) {
2424 * @author David Hovemeyer
2525 */
2626 public abstract class AbstractScannableCodeBaseEntry implements ICodeBaseEntry {
27 @Override
2728 public abstract AbstractScannableCodeBase getCodeBase();
2829
2930 public abstract String getRealResourceName();
3031
3132 /*
3233 * (non-Javadoc)
33 *
34 *
3435 * @see
3536 * edu.umd.cs.findbugs.classfile.ICodeBaseEntry#overrideResourceName(java
3637 * .lang.String)
3738 */
39 @Override
3840 public void overrideResourceName(String resourceName) {
3941 getCodeBase().addResourceNameTranslation(getRealResourceName(), resourceName);
4042 }
4143
4244 /*
4345 * (non-Javadoc)
44 *
46 *
4547 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#getResourceName()
4648 */
49 @Override
4750 public String getResourceName() {
4851 return getCodeBase().translateResourceName(getRealResourceName());
4952 }
1717 */
1818
1919 package edu.umd.cs.findbugs.classfile.impl;
20
21 import static java.util.Objects.requireNonNull;
2022
2123 import java.util.Collection;
2224 import java.util.Collections;
6971 */
7072 private static final int MAX_CLASS_RESULTS_TO_CACHE = 5000;
7173
72 private static final boolean ASSERTIONS_ENABLED = SystemProperties.ASSERTIONS_ENABLED;
74 // private static final boolean ASSERTIONS_ENABLED = SystemProperties.ASSERTIONS_ENABLED;
7375
7476 // Fields
7577 private final IClassPath classPath;
8890
8991 private final Map<?, ?> analysisLocals = Collections.synchronizedMap(new HashMap<Object, Object>());
9092
93 @Override
9194 public final Map<?, ?> getAnalysisLocals() {
9295 return analysisLocals;
9396 }
136139
137140 @SuppressWarnings("unchecked")
138141 static <E> E checkedCast(Class<E> analysisClass, Object o) {
139 if (SystemProperties.ASSERTIONS_ENABLED)
142 if (SystemProperties.ASSERTIONS_ENABLED) {
140143 return analysisClass.cast(o);
144 }
141145 return (E) o;
142146 }
143147
159163 this.databaseMap = new HashMap<Class<?>, Object>();
160164 }
161165
162 /*
163 * (non-Javadoc)
164 *
165 * @see edu.umd.cs.findbugs.classfile.IAnalysisCache#getClassPath()
166 */
166 @Override
167167 public IClassPath getClassPath() {
168168 return classPath;
169169 }
170170
171 @Override
171172 public void purgeAllMethodAnalysis() {
172173 // System.out.println("ZZZ : purging all method analyses");
173174
192193 return (Map<ClassDescriptor, E>) descriptorMap;
193194 }
194195
196 @Override
195197 public void purgeClassAnalysis(Class<?> analysisClass) {
196198 classAnalysisMap.remove(analysisClass);
197199 }
232234 }
233235 }
234236
235 /*
236 * (non-Javadoc)
237 *
238 * @see
239 * edu.umd.cs.findbugs.classfile.IAnalysisCache#getClassAnalysis(java.lang
240 * .Class, edu.umd.cs.findbugs.classfile.ClassDescriptor)
241 */
237 @Override
242238 @SuppressWarnings("unchecked")
243239 public <E> E getClassAnalysis(Class<E> analysisClass, @Nonnull ClassDescriptor classDescriptor) throws CheckedAnalysisException {
244 if (classDescriptor == null) {
245 throw new NullPointerException("classDescriptor is null");
246 }
240 requireNonNull(classDescriptor, "classDescriptor is null");
247241 // Get the descriptor->result map for this analysis class,
248242 // creating if necessary
249243 Map<ClassDescriptor, Object> descriptorMap = findOrCreateDescriptorMap(classAnalysisMap,
303297 return checkedCast(analysisClass, analysisResult);
304298 }
305299
306 /*
307 * (non-Javadoc)
308 *
309 * @see
310 * edu.umd.cs.findbugs.classfile.IAnalysisCache#probeClassAnalysis(java.
311 * lang.Class, edu.umd.cs.findbugs.classfile.ClassDescriptor)
312 */
300 @Override
313301 public <E> E probeClassAnalysis(Class<E> analysisClass, @Nonnull ClassDescriptor classDescriptor) {
314302 Map<ClassDescriptor, Object> descriptorMap = classAnalysisMap.get(analysisClass);
315303 if (descriptorMap == null) {
322310 return Integer.toHexString(System.identityHashCode(o));
323311 }
324312
325 /*
326 * (non-Javadoc)
327 *
328 * @see
329 * edu.umd.cs.findbugs.classfile.IAnalysisCache#getMethodAnalysis(java.lang
330 * .Class, edu.umd.cs.findbugs.classfile.MethodDescriptor)
331 */
313 @Override
332314 public <E> E getMethodAnalysis(Class<E> analysisClass, @Nonnull MethodDescriptor methodDescriptor) throws CheckedAnalysisException {
333 if (methodDescriptor == null) {
334 throw new NullPointerException("methodDescriptor is null");
335 }
315 requireNonNull(methodDescriptor, "methodDescriptor is null");
336316 ClassContext classContext = getClassAnalysis(ClassContext.class, methodDescriptor.getClassDescriptor());
337317 Object object = classContext.getMethodAnalysis(analysisClass, methodDescriptor);
338318
391371 }
392372 }
393373
394 /*
395 * (non-Javadoc)
396 *
397 * @see
398 * edu.umd.cs.findbugs.classfile.IAnalysisCache#eagerlyPutMethodAnalysis
399 * (java.lang.Class, edu.umd.cs.findbugs.classfile.MethodDescriptor,
400 * java.lang.Object)
401 */
374 @Override
402375 public <E> void eagerlyPutMethodAnalysis(Class<E> analysisClass, @Nonnull MethodDescriptor methodDescriptor, E analysisObject) {
403376 try {
404377 ClassContext classContext = getClassAnalysis(ClassContext.class, methodDescriptor.getClassDescriptor());
412385
413386 }
414387
415 /*
416 * (non-Javadoc)
417 *
418 * @see
419 * edu.umd.cs.findbugs.classfile.IAnalysisCache#purgeMethodAnalyses(edu.
420 * umd.cs.findbugs.classfile.MethodDescriptor)
421 */
388 @Override
422389 public void purgeMethodAnalyses(@Nonnull MethodDescriptor methodDescriptor) {
423390 try {
424391
480447 return descriptorMap;
481448 }
482449
483 /*
484 * (non-Javadoc)
485 *
486 * @see
487 * edu.umd.cs.findbugs.classfile.IAnalysisCache#registerClassAnalysisEngine
488 * (java.lang.Class, edu.umd.cs.findbugs.classfile.IClassAnalysisEngine)
489 */
450 @Override
490451 public <E> void registerClassAnalysisEngine(Class<E> analysisResultType, IClassAnalysisEngine<E> classAnalysisEngine) {
491452 classAnalysisEngineMap.put(analysisResultType, classAnalysisEngine);
492453 }
493454
494 /*
495 * (non-Javadoc)
496 *
497 * @see
498 * edu.umd.cs.findbugs.classfile.IAnalysisCache#registerMethodAnalysisEngine
499 * (java.lang.Class, edu.umd.cs.findbugs.classfile.IMethodAnalysisEngine)
500 */
455 @Override
501456 public <E> void registerMethodAnalysisEngine(Class<E> analysisResultType, IMethodAnalysisEngine<E> methodAnalysisEngine) {
502457 methodAnalysisEngineMap.put(analysisResultType, methodAnalysisEngine);
503458 }
504459
505 /*
506 * (non-Javadoc)
507 *
508 * @see
509 * edu.umd.cs.findbugs.classfile.IAnalysisCache#registerDatabaseFactory(
510 * java.lang.Class, edu.umd.cs.findbugs.classfile.IDatabaseFactory)
511 */
460 @Override
512461 public <E> void registerDatabaseFactory(Class<E> databaseClass, IDatabaseFactory<E> databaseFactory) {
513462 databaseFactoryMap.put(databaseClass, databaseFactory);
514463 }
515464
516 /*
517 * (non-Javadoc)
518 *
519 * @see
520 * edu.umd.cs.findbugs.classfile.IAnalysisCache#getDatabase(java.lang.Class)
521 */
465 @Override
522466 public <E> E getDatabase(Class<E> databaseClass) {
523467 return getDatabase(databaseClass, false);
524468 }
469 @Override
525470 public @CheckForNull <E> E getOptionalDatabase(Class<E> databaseClass) {
526471 return getDatabase(databaseClass, true);
527472 }
533478 // Find the database factory
534479 IDatabaseFactory<?> databaseFactory = databaseFactoryMap.get(databaseClass);
535480 if (databaseFactory == null) {
536 if (optional) return null;
481 if (optional) {
482 return null;
483 }
537484 throw new IllegalArgumentException("No database factory registered for " + databaseClass.getName());
538485 }
539486
544491 database = new AbnormalAnalysisResult(e);
545492 }
546493 // FIXME: should catch and re-throw RuntimeExceptions?
547
548494 databaseMap.put(databaseClass, database);
549495 }
550496
552498 throw new UncheckedAnalysisException("Error instantiating " + databaseClass.getName() + " database",
553499 ((AbnormalAnalysisResult) database).checkedAnalysisException);
554500 }
555
556501 return databaseClass.cast(database);
557502 }
558503
559 /*
560 * (non-Javadoc)
561 *
562 * @see
563 * edu.umd.cs.findbugs.classfile.IAnalysisCache#eagerlyPutDatabase(java.
564 * lang.Class, java.lang.Object)
565 */
504 @Override
566505 public <E> void eagerlyPutDatabase(Class<E> databaseClass, E database) {
567506 databaseMap.put(databaseClass, database);
568507 }
569508
570 /*
571 * (non-Javadoc)
572 *
573 * @see edu.umd.cs.findbugs.classfile.IAnalysisCache#getErrorLogger()
574 */
509 @Override
575510 public IErrorLogger getErrorLogger() {
576511 return bugReporter;
577512 }
578513
579 /*
580 * (non-Javadoc)
581 *
582 * @see edu.umd.cs.findbugs.classfile.IAnalysisCache#getProfiler()
583 */
514 @Override
584515 public Profiler getProfiler() {
585516 return bugReporter.getProjectStats().getProfiler();
586517 }
3535
3636 /**
3737 * Factory to create codebase/classpath/classfile objects.
38 *
38 *
3939 * @author David Hovemeyer
4040 */
4141 public class ClassFactory implements IClassFactory {
5050
5151 /*
5252 * (non-Javadoc)
53 *
53 *
5454 * @see edu.umd.cs.findbugs.classfile.impl.IClassFactory#createClassPath()
5555 */
56 @Override
5657 public IClassPath createClassPath() {
5758 return new ClassPathImpl();
5859 }
5960
6061 /*
6162 * (non-Javadoc)
62 *
63 *
6364 * @see
6465 * edu.umd.cs.findbugs.classfile.IClassFactory#createClassPathBuilder(edu
6566 * .umd.cs.findbugs.classfile.IErrorLogger)
6667 */
68 @Override
6769 public IClassPathBuilder createClassPathBuilder(IErrorLogger errorLogger) {
6870 return new ClassPathBuilder(this, errorLogger);
6971 }
7072
7173 /*
7274 * (non-Javadoc)
73 *
75 *
7476 * @see edu.umd.cs.findbugs.classfile.impl.IClassFactory#
7577 * createFilesystemCodeBaseLocator(java.lang.String)
7678 */
79 @Override
7780 public ICodeBaseLocator createFilesystemCodeBaseLocator(String pathName) {
7881 // Attempt to canonicalize the pathname.
7982 // It's not fatal if we can't.
8891
8992 /*
9093 * (non-Javadoc)
91 *
94 *
9295 * @see edu.umd.cs.findbugs.classfile.IClassFactory#
9396 * createNestedArchiveCodeBaseLocator
9497 * (edu.umd.cs.findbugs.classfile.ICodeBase, java.lang.String)
9598 */
99 @Override
96100 public ICodeBaseLocator createNestedArchiveCodeBaseLocator(ICodeBase parentCodeBase, String path) {
97101 return new NestedZipFileCodeBaseLocator(parentCodeBase, path);
98102 }
124128
125129 /*
126130 * (non-Javadoc)
127 *
131 *
128132 * @see
129133 * edu.umd.cs.findbugs.classfile.IClassFactory#createAnalysisCache(edu.umd
130134 * .cs.findbugs.classfile.IClassPath)
131135 */
136 @Override
132137 public IAnalysisCache createAnalysisCache(IClassPath classPath, BugReporter errorLogger) {
133138 IAnalysisCache analysisCache = new AnalysisCache(classPath, errorLogger);
134139 return analysisCache;
140140 return ((IScannableCodeBase) codeBase).iterator();
141141 } else {
142142 return new ICodeBaseIterator() {
143 @Override
143144 public boolean hasNext() throws InterruptedException {
144145 return false;
145146 }
146147
148 @Override
147149 public ICodeBaseEntry next() throws InterruptedException {
148150 throw new UnsupportedOperationException();
149151 }
191193 * edu.umd.cs.findbugs.classfile.IClassPathBuilder#addCodeBase(edu.umd.cs
192194 * .findbugs.classfile.ICodeBaseLocator, boolean)
193195 */
196 @Override
194197 public void addCodeBase(ICodeBaseLocator locator, boolean isApplication) {
195198 addToWorkList(projectWorkList, new WorkListItem(locator, isApplication, ICodeBase.Discovered.SPECIFIED));
196199 }
202205 * edu.umd.cs.findbugs.classfile.IClassPathBuilder#scanNestedArchives(boolean
203206 * )
204207 */
208 @Override
205209 public void scanNestedArchives(boolean scanNestedArchives) {
206210 this.scanNestedArchives = scanNestedArchives;
207211 }
214218 * .classfile.IClassPath,
215219 * edu.umd.cs.findbugs.classfile.IClassPathBuilderProgress)
216220 */
221 @Override
217222 public void build(IClassPath classPath, IClassPathBuilderProgress progress) throws CheckedAnalysisException, IOException,
218 InterruptedException {
223 InterruptedException {
219224 // Discover all directly and indirectly referenced codebases
220225 processWorkList(classPath, projectWorkList, progress);
221226
521526 *
522527 * @see java.io.FileFilter#accept(java.io.File)
523528 */
529 @Override
524530 public boolean accept(File pathname) {
525531 String path = pathname.getPath();
526532 boolean isArchive = Archive.isArchiveFileName(path);
620626 scanJarManifestForClassPathEntries(workList, discoveredCodeBase.getCodeBase());
621627 } catch (IOException e) {
622628 if (item.isAppCodeBase() || item.getHowDiscovered() == ICodeBase.Discovered.SPECIFIED) {
623 if (e instanceof FileNotFoundException)
629 if (e instanceof FileNotFoundException) {
624630 errorLogger.logError("File not found: " + item.getCodeBaseLocator());
625 else errorLogger.logError("Cannot open codebase " + item.getCodeBaseLocator(), e);
631 } else {
632 errorLogger.logError("Cannot open codebase " + item.getCodeBaseLocator(), e);
633 }
626634 }
627635 } catch (ResourceNotFoundException e) {
628636 if (item.getHowDiscovered() == ICodeBase.Discovered.SPECIFIED) {
807815 *
808816 * @see edu.umd.cs.findbugs.classfile.IClassPathBuilder#getAppClassList()
809817 */
818 @Override
810819 public List<ClassDescriptor> getAppClassList() {
811820 return appClassList;
812821 }
3737 * @author David Hovemeyer
3838 */
3939 public class ClassPathImpl implements IClassPath {
40 private List<IScannableCodeBase> appCodeBaseList;
41
42 private List<ICodeBase> auxCodeBaseList;
43
44 private Map<String, ICodeBaseEntry> codeBaseEntryMap;
40 private final List<IScannableCodeBase> appCodeBaseList;
41
42 private final List<ICodeBase> auxCodeBaseList;
43
44 private final Map<String, ICodeBaseEntry> codeBaseEntryMap;
4545
4646 public ClassPathImpl() {
4747 this.appCodeBaseList = new LinkedList<IScannableCodeBase>();
7171 * edu.umd.cs.findbugs.classfile.IClassPath#addCodeBase(edu.umd.cs.findbugs
7272 * .classfile.ICodeBase)
7373 */
74 @Override
7475 public void addCodeBase(ICodeBase codeBase) {
7576 if (codeBase.isApplicationCodeBase()) {
7677 if (!(codeBase instanceof IScannableCodeBase)) {
8788 *
8889 * @see edu.umd.cs.findbugs.classfile.IClassPath#appCodeBaseIterator()
8990 */
91 @Override
9092 public Iterator<? extends ICodeBase> appCodeBaseIterator() {
9193 return appCodeBaseList.iterator();
9294 }
9698 *
9799 * @see edu.umd.cs.findbugs.classfile.IClassPath#auxCodeBaseIterator()
98100 */
101 @Override
99102 public Iterator<? extends ICodeBase> auxCodeBaseIterator() {
100103 return auxCodeBaseList.iterator();
101104 }
105108 *
106109 * @see edu.umd.cs.findbugs.classfile.IClassPath#close()
107110 */
111 @Override
108112 public void close() {
109113 for (ICodeBase codeBase : appCodeBaseList) {
110114 codeBase.close();
117121 codeBaseEntryMap.clear();
118122 }
119123
124 @Override
120125 public Map<String, ICodeBaseEntry> getApplicationCodebaseEntries() {
121126 Map<String, ICodeBaseEntry> appEntries = new HashMap<String, ICodeBaseEntry>();
122127 Iterator<Entry<String, ICodeBaseEntry>> iterator = codeBaseEntryMap.entrySet().iterator();
136141 * @see
137142 * edu.umd.cs.findbugs.classfile.IClassPath#lookupResource(java.lang.String)
138143 */
144 @Override
139145 public ICodeBaseEntry lookupResource(String resourceName) throws ResourceNotFoundException {
140146 // See if we have cached the codebase entry for this resource
141147 ICodeBaseEntry result = codeBaseEntryMap.get(resourceName);
192198 * edu.umd.cs.findbugs.classfile.IClassPath#mapResourceNameToCodeBaseEntry
193199 * (java.lang.String, edu.umd.cs.findbugs.classfile.ICodeBaseEntry)
194200 */
201 @Override
195202 public void mapResourceNameToCodeBaseEntry(String resourceName, ICodeBaseEntry codeBaseEntry) {
196203 codeBaseEntryMap.put(resourceName, codeBaseEntry);
197204 }
3131 * Implementation of ICodeBaseEntry that delegates to another codebase entry.
3232 * This is needed for codebase entries in nested zipfiles, which are implemented
3333 * using a private zipfile codebase.
34 *
34 *
3535 * @author David Hovemeyer
3636 */
3737 public class DelegatingCodeBaseEntry implements ICodeBaseEntry {
38 private ICodeBase frontEndCodeBase;
38 private final ICodeBase frontEndCodeBase;
3939
40 private ICodeBaseEntry delegateCodeBaseEntry;
40 private final ICodeBaseEntry delegateCodeBaseEntry;
4141
4242 public DelegatingCodeBaseEntry(ICodeBase frontEndCodeBase, ICodeBaseEntry delegateCodeBaseEntry) {
4343 this.frontEndCodeBase = frontEndCodeBase;
4646
4747 /*
4848 * (non-Javadoc)
49 *
49 *
5050 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#getNumBytes()
5151 */
52 @Override
5253 public int getNumBytes() {
5354 return delegateCodeBaseEntry.getNumBytes();
5455 }
5556
5657 /*
5758 * (non-Javadoc)
58 *
59 *
5960 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#getResourceName()
6061 */
62 @Override
6163 public String getResourceName() {
6264 return delegateCodeBaseEntry.getResourceName();
6365 }
6466
6567 /*
6668 * (non-Javadoc)
67 *
69 *
6870 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#openResource()
6971 */
72 @Override
7073 public InputStream openResource() throws IOException {
7174 return delegateCodeBaseEntry.openResource();
7275 }
7376
7477 /*
7578 * (non-Javadoc)
76 *
79 *
7780 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#getCodeBase()
7881 */
82 @Override
7983 public ICodeBase getCodeBase() {
8084 return frontEndCodeBase;
8185 }
8286
8387 /*
8488 * (non-Javadoc)
85 *
89 *
8690 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#getClassDescriptor()
8791 */
92 @Override
8893 public ClassDescriptor getClassDescriptor() throws ResourceNotFoundException, InvalidClassFileFormatException {
8994 return delegateCodeBaseEntry.getClassDescriptor();
9095 }
9196
9297 /*
9398 * (non-Javadoc)
94 *
99 *
95100 * @see
96101 * edu.umd.cs.findbugs.classfile.ICodeBaseEntry#overrideResourceName(java
97102 * .lang.String)
98103 */
104 @Override
99105 public void overrideResourceName(String resourceName) {
100106 delegateCodeBaseEntry.overrideResourceName(resourceName);
101107 }
102108
103109 /*
104110 * (non-Javadoc)
105 *
111 *
106112 * @see java.lang.Object#equals(java.lang.Object)
107113 */
108114 @Override
117123
118124 /*
119125 * (non-Javadoc)
120 *
126 *
121127 * @see java.lang.Object#hashCode()
122128 */
123129 @Override
127133
128134 /*
129135 * (non-Javadoc)
130 *
136 *
131137 * @see java.lang.Object#toString()
132138 */
133139 @Override
2727 * An implementation of ICodeBaseIterator that delegates to another codebase. In
2828 * particular, the codebase entries it creates are DelegatingCodeBaseEntry
2929 * objects.
30 *
30 *
3131 * @author David Hovemeyer
3232 */
3333 public class DelegatingCodeBaseIterator implements ICodeBaseIterator {
34 private ICodeBase frontEndCodeBase;
34 private final ICodeBase frontEndCodeBase;
3535
36 private ICodeBaseIterator delegateCodeBaseIterator;
36 private final ICodeBaseIterator delegateCodeBaseIterator;
3737
3838 public DelegatingCodeBaseIterator(ICodeBase frontEndCodeBase, IScannableCodeBase delegateCodeBase)
3939 throws InterruptedException {
4343
4444 /*
4545 * (non-Javadoc)
46 *
46 *
4747 * @see edu.umd.cs.findbugs.classfile.ICodeBaseIterator#hasNext()
4848 */
49 @Override
4950 public boolean hasNext() throws InterruptedException {
5051 return delegateCodeBaseIterator.hasNext();
5152 }
5253
5354 /*
5455 * (non-Javadoc)
55 *
56 *
5657 * @see edu.umd.cs.findbugs.classfile.ICodeBaseIterator#next()
5758 */
59 @Override
5860 public ICodeBaseEntry next() throws InterruptedException {
5961 ICodeBaseEntry delegateCodeBaseEntry = delegateCodeBaseIterator.next();
6062 return new DelegatingCodeBaseEntry(frontEndCodeBase, delegateCodeBaseEntry);
3636 /**
3737 * IScannableCodeBase implementation to read resources from a filesystem
3838 * directory.
39 *
39 *
4040 * @author David Hovemeyer
4141 */
4242 public class DirectoryCodeBase extends AbstractScannableCodeBase {
4646
4747 /*
4848 * (non-Javadoc)
49 *
49 *
5050 * @see edu.umd.cs.findbugs.classfile.ICodeBaseIterator#hasNext()
5151 */
52 @Override
5253 public boolean hasNext() throws InterruptedException {
5354 return fileNameIterator.hasNext();
5455 }
5556
5657 /*
5758 * (non-Javadoc)
58 *
59 *
5960 * @see edu.umd.cs.findbugs.classfile.ICodeBaseIterator#next()
6061 */
62 @Override
6163 public ICodeBaseEntry next() throws InterruptedException {
6264 final String fileName = fileNameIterator.next();
6365
6668
6769 // Update last modified time
6870 File file = new File(fileName);
69 long modTime = file.lastModified();
70 addLastModifiedTime(modTime);
71 long modTime = file.lastModified();
72 addLastModifiedTime(modTime);
7173
7274 return new DirectoryCodeBaseEntry(DirectoryCodeBase.this, resourceName);
7375 }
8183
8284 /**
8385 * Constructor.
84 *
86 *
8587 * @param codeBaseLocator
8688 * the codebase locator for this codebase
8789 * @param directory
9698 this.rfs = new RecursiveFileSearch(directory.getPath(), new FileFilter() {
9799 /*
98100 * (non-Javadoc)
99 *
101 *
100102 * @see java.io.FileFilter#accept(java.io.File)
101103 */
104 @Override
102105 public boolean accept(File pathname) {
103106 return true;
104107 }
108111
109112 /*
110113 * (non-Javadoc)
111 *
114 *
112115 * @see edu.umd.cs.findbugs.classfile.IScannableCodeBase#iterator()
113116 */
117 @Override
114118 public ICodeBaseIterator iterator() throws InterruptedException {
115119 if (!searchPerformed) {
116120 rfs.search();
122126
123127 /*
124128 * (non-Javadoc)
125 *
129 *
126130 * @see edu.umd.cs.findbugs.classfile.ICodeBase#getPathName()
127131 */
132 @Override
128133 public String getPathName() {
129134 return directory.getPath();
130135 }
131136
132137 /*
133138 * (non-Javadoc)
134 *
139 *
135140 * @see edu.umd.cs.findbugs.classfile.ICodeBase#close()
136141 */
142 @Override
137143 public void close() {
138144 // Nothing to do
139145 }
140146
141147 /*
142148 * (non-Javadoc)
143 *
149 *
144150 * @see
145151 * edu.umd.cs.findbugs.classfile.ICodeBase#lookupResource(java.lang.String)
146152 */
153 @Override
147154 public ICodeBaseEntry lookupResource(String resourceName) {
148155 // Translate resource name, in case a resource name
149156 // has been overridden and the resource is being accessed
164171
165172 /**
166173 * Get the full path of given resource.
167 *
168 * @param resourceName
169 * @return
170174 */
171175 File getFullPathOfResource(String resourceName) {
172176 return new File(directory, resourceName);
174178
175179 /**
176180 * Get the resource name given a full filename.
177 *
181 *
178182 * @param fileName
179183 * the full filename (which must be inside the directory)
180184 * @return the resource name (i.e., the filename with the directory stripped
216220
217221 /*
218222 * (non-Javadoc)
219 *
223 *
220224 * @see java.lang.Object#toString()
221225 */
222226 @Override
2828
2929 /**
3030 * Codebase entry class for directory codebases.
31 *
31 *
3232 * @author David Hovemeyer
3333 */
3434 public class DirectoryCodeBaseEntry extends AbstractScannableCodeBaseEntry {
4343
4444 /*
4545 * (non-Javadoc)
46 *
46 *
4747 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#getNumBytes()
4848 */
49 @Override
4950 public int getNumBytes() {
5051 File fullPath = codeBase.getFullPathOfResource(realResourceName);
5152 // this is not needed but causes slowdown on a slow file system IO
5960
6061 /*
6162 * (non-Javadoc)
62 *
63 *
6364 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#openResource()
6465 */
66 @Override
6567 public InputStream openResource() throws IOException {
6668 return codeBase.openFile(realResourceName);
6769 }
6870
6971 /*
7072 * (non-Javadoc)
71 *
73 *
7274 * @see
7375 * edu.umd.cs.findbugs.classfile.impl.AbstractScannableCodeBaseEntry#getCodeBase
7476 * ()
8082
8183 /*
8284 * (non-Javadoc)
83 *
85 *
8486 * @see edu.umd.cs.findbugs.classfile.impl.AbstractScannableCodeBaseEntry#
8587 * getRealResourceName()
8688 */
9193
9294 /*
9395 * (non-Javadoc)
94 *
96 *
9597 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#getClassDescriptor()
9698 */
99 @Override
97100 public ClassDescriptor getClassDescriptor() throws InvalidClassFileFormatException {
98101 return DescriptorFactory.createClassDescriptorFromResourceName(getResourceName());
99102 }
100103
101104 /*
102105 * (non-Javadoc)
103 *
106 *
104107 * @see java.lang.Object#equals(java.lang.Object)
105108 */
106109 @Override
114117
115118 /*
116119 * (non-Javadoc)
117 *
120 *
118121 * @see java.lang.Object#hashCode()
119122 */
120123 @Override
124127
125128 /*
126129 * (non-Javadoc)
127 *
130 *
128131 * @see java.lang.Object#toString()
129132 */
130133 @Override
2626
2727 /**
2828 * Codebase locator for files and directories in the filesystem.
29 *
29 *
3030 * @author David Hovemeyer
3131 */
3232 public class FilesystemCodeBaseLocator implements ICodeBaseLocator {
5151
5252 /*
5353 * (non-Javadoc)
54 *
54 *
5555 * @see
5656 * edu.umd.cs.findbugs.classfile.ICodeBaseLocator#createRelativeCodeBaseLocator
5757 * (java.lang.String)
5858 */
59 @Override
5960 public ICodeBaseLocator createRelativeCodeBaseLocator(String relativePath) {
6061 File path = new File(pathName);
6162 if (!path.isDirectory()) {
6768
6869 /*
6970 * (non-Javadoc)
70 *
71 *
7172 * @see edu.umd.cs.findbugs.classfile.ICodeBaseLocator#openCodeBase()
7273 */
74 @Override
7375 public ICodeBase openCodeBase() throws IOException {
7476 return ClassFactory.createFilesystemCodeBase(this);
7577 }
7678
7779 /*
7880 * (non-Javadoc)
79 *
81 *
8082 * @see java.lang.Object#toString()
8183 */
8284 @Override
8688
8789 /*
8890 * (non-Javadoc)
89 *
91 *
9092 * @see java.lang.Object#equals(java.lang.Object)
9193 */
9294 @Override
100102
101103 /*
102104 * (non-Javadoc)
103 *
105 *
104106 * @see java.lang.Object#hashCode()
105107 */
106108 @Override
3737 * codebase. These are handled by extracting the nested zip/jar file to a
3838 * temporary file, and delegating to an internal ZipFileCodeBase that reads from
3939 * the temporary file.
40 *
40 *
4141 * @author David Hovemeyer
4242 */
4343 public class NestedZipFileCodeBase extends AbstractScannableCodeBase {
44 private ICodeBase parentCodeBase;
44 private final ICodeBase parentCodeBase;
4545
46 private String resourceName;
46 private final String resourceName;
4747
4848 private File tempFile;
4949
5151
5252 /**
5353 * Constructor.
54 *
54 *
5555 * @param codeBaseLocator
5656 * the codebase locator for this codebase
5757 */
6666 // Create a temp file
6767 this.tempFile = File.createTempFile("findbugs", ".zip");
6868 tempFile.deleteOnExit(); // just in case we crash before the
69 // codebase is closed
69 // codebase is closed
7070
7171 // Copy nested zipfile to the temporary file
7272 // FIXME: potentially long blocking operation - should be
9595
9696 /*
9797 * (non-Javadoc)
98 *
98 *
9999 * @see edu.umd.cs.findbugs.classfile.IScannableCodeBase#iterator()
100100 */
101 @Override
101102 public ICodeBaseIterator iterator() throws InterruptedException {
102103 return new DelegatingCodeBaseIterator(this, delegateCodeBase);
103104 }
104105
105106 /*
106107 * (non-Javadoc)
107 *
108 *
108109 * @see
109110 * edu.umd.cs.findbugs.classfile.ICodeBase#lookupResource(java.lang.String)
110111 */
112 @Override
111113 public ICodeBaseEntry lookupResource(String resourceName) {
112114 ICodeBaseEntry delegateCodeBaseEntry = delegateCodeBase.lookupResource(resourceName);
113115 if (delegateCodeBaseEntry == null) {
118120
119121 /*
120122 * (non-Javadoc)
121 *
123 *
122124 * @see edu.umd.cs.findbugs.classfile.ICodeBase#getPathName()
123125 */
126 @Override
124127 public String getPathName() {
125128 return null;
126129 }
127130
128131 /*
129132 * (non-Javadoc)
130 *
133 *
131134 * @see edu.umd.cs.findbugs.classfile.ICodeBase#close()
132135 */
136 @Override
133137 public void close() {
134138 delegateCodeBase.close();
135139 if (!tempFile.delete()) {
2626
2727 /**
2828 * Codebase locator for a zip/jar archive nested inside a parent codebase.
29 *
29 *
3030 * @author David Hovemeyer
3131 */
3232 public class NestedZipFileCodeBaseLocator implements ICodeBaseLocator {
5555
5656 /*
5757 * (non-Javadoc)
58 *
58 *
5959 * @see
6060 * edu.umd.cs.findbugs.classfile.ICodeBaseLocator#createRelativeCodeBaseLocator
6161 * (java.lang.String)
6262 */
63 @Override
6364 public ICodeBaseLocator createRelativeCodeBaseLocator(String relativePath) {
6465 // The relative path indicates another codebase (archive) in the same
6566 // parent codebase
6869
6970 /*
7071 * (non-Javadoc)
71 *
72 *
7273 * @see edu.umd.cs.findbugs.classfile.ICodeBaseLocator#openCodeBase()
7374 */
75 @Override
7476 public ICodeBase openCodeBase() throws ResourceNotFoundException, IOException {
7577 return ClassFactory.createNestedZipFileCodeBase(this);
7678 }
7779
7880 /*
7981 * (non-Javadoc)
80 *
82 *
8183 * @see java.lang.Object#toString()
8284 */
8385 @Override
8789
8890 /*
8991 * (non-Javadoc)
90 *
92 *
9193 * @see java.lang.Object#equals(java.lang.Object)
9294 */
9395 @Override
101103
102104 /*
103105 * (non-Javadoc)
104 *
106 *
105107 * @see java.lang.Object#hashCode()
106108 */
107109 @Override
4242
4343 /**
4444 * Implementation of ICodeBase for a single classfile.
45 *
45 *
4646 * @author David Hovemeyer
4747 */
4848 public class SingleFileCodeBase implements IScannableCodeBase {
4949
50 private ICodeBaseLocator codeBaseLocator;
51
52 private String fileName;
50 private final ICodeBaseLocator codeBaseLocator;
51
52 private final String fileName;
5353
5454 private boolean isAppCodeBase;
5555
7474
7575 /*
7676 * (non-Javadoc)
77 *
77 *
7878 * @see edu.umd.cs.findbugs.classfile.ICodeBase#getCodeBaseLocator()
7979 */
80 @Override
8081 public ICodeBaseLocator getCodeBaseLocator() {
8182 return codeBaseLocator;
8283 }
8384
8485 /*
8586 * (non-Javadoc)
86 *
87 *
8788 * @see
8889 * edu.umd.cs.findbugs.classfile.IScannableCodeBase#containsSourceFiles()
8990 */
91 @Override
9092 public boolean containsSourceFiles() throws InterruptedException {
9193 return false;
9294 }
9395
9496 /*
9597 * (non-Javadoc)
96 *
98 *
9799 * @see edu.umd.cs.findbugs.classfile.IScannableCodeBase#iterator()
98100 */
101 @Override
99102 public ICodeBaseIterator iterator() throws InterruptedException {
100103 return new ICodeBaseIterator() {
101104 boolean done = false;
102105
103106 /*
104107 * (non-Javadoc)
105 *
108 *
106109 * @see edu.umd.cs.findbugs.classfile.ICodeBaseIterator#hasNext()
107110 */
111 @Override
108112 public boolean hasNext() throws InterruptedException {
109113 return !done;
110114 }
111115
112116 /*
113117 * (non-Javadoc)
114 *
118 *
115119 * @see edu.umd.cs.findbugs.classfile.ICodeBaseIterator#next()
116120 */
121 @Override
117122 public ICodeBaseEntry next() throws InterruptedException {
118123 if (done) {
119124 throw new NoSuchElementException();
126131
127132 /*
128133 * (non-Javadoc)
129 *
134 *
130135 * @see
131136 * edu.umd.cs.findbugs.classfile.ICodeBase#lookupResource(java.lang.String)
132137 */
138 @Override
133139 public ICodeBaseEntry lookupResource(String resourceName) {
134140 if (!resourceName.equals(getResourceName())) {
135141 return null;
140146
141147 /*
142148 * (non-Javadoc)
143 *
149 *
144150 * @see
145151 * edu.umd.cs.findbugs.classfile.ICodeBase#setApplicationCodeBase(boolean)
146152 */
153 @Override
147154 public void setApplicationCodeBase(boolean isAppCodeBase) {
148155 this.isAppCodeBase = isAppCodeBase;
149156 }
150157
151158 /*
152159 * (non-Javadoc)
153 *
160 *
154161 * @see edu.umd.cs.findbugs.classfile.ICodeBase#isApplicationCodeBase()
155162 */
163 @Override
156164 public boolean isApplicationCodeBase() {
157165 return isAppCodeBase;
158166 }
159167
160168 /*
161169 * (non-Javadoc)
162 *
170 *
163171 * @see edu.umd.cs.findbugs.classfile.ICodeBase#setHowDiscovered(int)
164172 */
173 @Override
165174 public void setHowDiscovered(ICodeBase.Discovered howDiscovered) {
166175 this.howDiscovered = howDiscovered;
167176 }
168177
169178 /*
170179 * (non-Javadoc)
171 *
180 *
172181 * @see edu.umd.cs.findbugs.classfile.ICodeBase#getHowDiscovered()
173182 */
183 @Override
174184 public ICodeBase.Discovered getHowDiscovered() {
175185 return howDiscovered;
176186 }
177187
178188 /*
179189 * (non-Javadoc)
180 *
190 *
181191 * @see edu.umd.cs.findbugs.classfile.ICodeBase#setLastModifiedTime(long)
182192 */
193 @Override
183194 public void setLastModifiedTime(long lastModifiedTime) {
184195 if (lastModifiedTime > 0 && FindBugs.validTimestamp(lastModifiedTime)) {
185196 this.lastModifiedTime = lastModifiedTime;
188199
189200 /*
190201 * (non-Javadoc)
191 *
202 *
192203 * @see edu.umd.cs.findbugs.classfile.ICodeBase#getLastModifiedTime()
193204 */
205 @Override
194206 public long getLastModifiedTime() {
195207 return lastModifiedTime;
196208 }
197209
198210 /*
199211 * (non-Javadoc)
200 *
212 *
201213 * @see edu.umd.cs.findbugs.classfile.ICodeBase#getPathName()
202214 */
215 @Override
203216 public String getPathName() {
204217 return fileName;
205218 }
210223
211224 /*
212225 * (non-Javadoc)
213 *
226 *
214227 * @see edu.umd.cs.findbugs.classfile.ICodeBase#close()
215228 */
229 @Override
216230 public void close() {
217231 // Nothing to do
218232 }
220234 /**
221235 * Get the resource name of the single file. We have to open the file and
222236 * parse the constant pool in order to find this out.
223 *
237 *
224238 * @return the resource name (e.g., "java/lang/String.class" if the class is
225239 * java.lang.String)
226240 */
267281
268282 /**
269283 * Return the number of bytes in the file.
270 *
284 *
271285 * @return the number of bytes in the file, or -1 if the file's length can't
272286 * be determined
273287 */
1010
1111 /**
1212 * Codebase entry for a single-file codebase.
13 *
13 *
1414 * @author David Hovemeyer
1515 */
1616 public class SingleFileCodeBaseEntry implements ICodeBaseEntry {
2020
2121 /**
2222 * Constructor.
23 *
23 *
2424 * @param codeBase
2525 * parent codebase
2626 */
3030
3131 /*
3232 * (non-Javadoc)
33 *
33 *
3434 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#getNumBytes()
3535 */
36 @Override
3637 public int getNumBytes() {
3738 return codeBase.getNumBytes();
3839 }
3940
4041 /*
4142 * (non-Javadoc)
42 *
43 *
4344 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#getResourceName()
4445 */
46 @Override
4547 public String getResourceName() {
46 if (overriddenResourceName != null)
48 if (overriddenResourceName != null) {
4749 return overriddenResourceName;
50 }
4851 return codeBase.getResourceName();
4952 }
5053
5154 /*
5255 * (non-Javadoc)
53 *
56 *
5457 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#openResource()
5558 */
59 @Override
5660 public InputStream openResource() throws IOException {
5761 return codeBase.openFile();
5862 }
5963
6064 /*
6165 * (non-Javadoc)
62 *
66 *
6367 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#getCodeBase()
6468 */
69 @Override
6570 public ICodeBase getCodeBase() {
6671 return codeBase;
6772 }
6873
6974 /*
7075 * (non-Javadoc)
71 *
76 *
7277 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#getClassDescriptor()
7378 */
79 @Override
7480 public ClassDescriptor getClassDescriptor() throws ResourceNotFoundException, InvalidClassFileFormatException {
7581 return codeBase.getClassDescriptor();
7682 }
7783
7884 /*
7985 * (non-Javadoc)
80 *
86 *
8187 * @see
8288 * edu.umd.cs.findbugs.classfile.ICodeBaseEntry#overrideResourceName(java
8389 * .lang.String)
8490 */
91 @Override
8592 public void overrideResourceName(String resourceName) {
8693 overriddenResourceName = resourceName;
8794 }
8895
8996 /*
9097 * (non-Javadoc)
91 *
98 *
9299 * @see java.lang.Object#equals(java.lang.Object)
93100 */
94101 @Override
102109
103110 /*
104111 * (non-Javadoc)
105 *
112 *
106113 * @see java.lang.Object#hashCode()
107114 */
108115 @Override
112119
113120 /*
114121 * (non-Javadoc)
115 *
122 *
116123 * @see java.lang.Object#toString()
117124 */
118125 @Override
3737 try {
3838 return new ZipFileCodeBase(codeBaseLocator, file);
3939 } catch (ZipException e) {
40 // May be too many zip entries
41 return new ZipInputStreamCodeBase(codeBaseLocator, file);
40 // May be too many zip entries
41 return new ZipInputStreamCodeBase(codeBaseLocator, file);
4242 } finally {
4343 profiler.end(ZipCodeBaseFactory.class);
4444 }
4545 }
4646
4747
48
48
4949
5050 }
5757 } catch (IOException e) {
5858 if (!file.exists()) {
5959 File parent = file.getParentFile();
60 if (!(parent.exists() && parent.isDirectory() && parent.canRead()))
60 if (!(parent.exists() && parent.isDirectory() && parent.canRead())) {
6161 throw new IOException("Can't read directory containing zip file: " + file);
62 }
6263 throw new IOException("Zip file doesn't exist: " + file);
6364 }
64 if (!file.canRead())
65 if (!file.canRead()) {
6566 throw new IOException("Can't read file zip file: " + file);
66 if (!file.isFile())
67 }
68 if (!file.isFile()) {
6769 throw new IOException("Zip file isn't a normal file: " + file);
68 if (file.length() == 0)
70 }
71 if (file.length() == 0) {
6972 throw new IOException("Zip file is empty: " + file);
73 }
7074 if (!(e instanceof ZipException)) {
7175 IOException ioException = new IOException("Error opening zip file " + file + " of " + file.length() + " bytes");
7276 ioException.initCause(e);
7377 throw ioException;
7478 }
75 DataInputStream in = new DataInputStream(new FileInputStream(file));
7679 int magicBytes;
77 try {
80 try (DataInputStream in = new DataInputStream(new FileInputStream(file))){
7881 magicBytes = in.readInt();
79 in.close();
8082 } catch (IOException e3) {
8183 throw new IOException(String.format("Unable read first 4 bytes of zip file %s of %d bytes", file, file.length()));
8284 }
83 if (magicBytes != 0x504b0304)
85 if (magicBytes != 0x504b0304) {
8486 throw new IOException(String.format("Wrong magic bytes of %x for zip file %s of %d bytes", magicBytes, file,
8587 file.length()));
88 }
8689 ZipException e2 = new ZipException("Error opening zip file " + file + " of " + file.length() + " bytes");
8790 e2.initCause(e);
8891 throw e2;
8992 }
9093 }
9194
92 /*
93 * (non-Javadoc)
94 *
95 * @see
96 * edu.umd.cs.findbugs.classfile.ICodeBase#lookupResource(java.lang.String)
97 */
95 @Override
9896 public ICodeBaseEntry lookupResource(String resourceName) {
9997 // Translate resource name, in case a resource name
10098 // has been overridden and the resource is being accessed
114112 }
115113 }
116114
115 @Override
117116 public ICodeBaseIterator iterator() {
118117 final Enumeration<? extends ZipEntry> zipEntryEnumerator = zipFile.entries();
119118
120119 return new ICodeBaseIterator() {
121120 ZipFileCodeBaseEntry nextEntry;
122121
122 @Override
123123 public boolean hasNext() {
124124 scanForNextEntry();
125125 return nextEntry != null;
126126 }
127127
128 /*
129 * (non-Javadoc)
130 *
131 * @see edu.umd.cs.findbugs.classfile.ICodeBaseIterator#next()
132 */
128 @Override
133129 public ICodeBaseEntry next() throws InterruptedException {
134130 scanForNextEntry();
135131 if (nextEntry == null) {
155151 }
156152 }
157153 }
158
159154 };
160155 }
161156
162 /*
163 * (non-Javadoc)
164 *
165 * @see edu.umd.cs.findbugs.classfile.ICodeBase#getPathName()
166 */
157 @Override
167158 public String getPathName() {
168159 return zipFile.getName();
169160 }
170161
171 /*
172 * (non-Javadoc)
173 *
174 * @see edu.umd.cs.findbugs.classfile.ICodeBase#close()
175 */
162 @Override
176163 public void close() {
177164 try {
178165 zipFile.close();
181168 }
182169 }
183170
184 /*
185 * (non-Javadoc)
186 *
187 * @see java.lang.Object#toString()
188 */
189171 @Override
190172 public String toString() {
191173 return zipFile.getName();
2727
2828 /**
2929 * Implementation of ICodeBaseEntry for resources in zipfile codebases.
30 *
30 *
3131 * @author David Hovemeyer
3232 */
3333 public class ZipFileCodeBaseEntry extends AbstractScannableCodeBaseEntry {
4242
4343 /*
4444 * (non-Javadoc)
45 *
45 *
4646 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#getNumBytes()
4747 */
48 @Override
4849 public int getNumBytes() {
4950 return (int) zipEntry.getSize();
5051 }
5152
5253 /*
5354 * (non-Javadoc)
54 *
55 *
5556 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#openResource()
5657 */
58 @Override
5759 public InputStream openResource() throws IOException {
5860 return codeBase.zipFile.getInputStream(zipEntry);
5961 }
6062
6163 /*
6264 * (non-Javadoc)
63 *
65 *
6466 * @see
6567 * edu.umd.cs.findbugs.classfile.impl.AbstractScannableCodeBaseEntry#getCodeBase
6668 * ()
7274
7375 /*
7476 * (non-Javadoc)
75 *
77 *
7678 * @see edu.umd.cs.findbugs.classfile.impl.AbstractScannableCodeBaseEntry#
7779 * getRealResourceName()
7880 */
8385
8486 /*
8587 * (non-Javadoc)
86 *
88 *
8789 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#getClassDescriptor()
8890 */
91 @Override
8992 public ClassDescriptor getClassDescriptor() {
9093 return DescriptorFactory.createClassDescriptorFromResourceName(getResourceName());
9194 }
9295
9396 /*
9497 * (non-Javadoc)
95 *
98 *
9699 * @see java.lang.Object#equals(java.lang.Object)
97100 */
98101 @Override
106109
107110 /*
108111 * (non-Javadoc)
109 *
112 *
110113 * @see java.lang.Object#hashCode()
111114 */
112115 @Override
116119
117120 /*
118121 * (non-Javadoc)
119 *
122 *
120123 * @see java.lang.Object#toString()
121124 */
122125 @Override
3535
3636 /**
3737 * Implementation of ICodeBase to read from a zip file or jar file.
38 *
38 *
3939 * @author David Hovemeyer
4040 */
4141 public class ZipInputStreamCodeBase extends AbstractScannableCodeBase {
4949
5050 /**
5151 * Constructor.
52 *
52 *
5353 * @param codeBaseLocator
5454 * the codebase locator for this codebase
5555 * @param file
7272 while ((ze = zis.getNextEntry()) != null) {
7373 String name = ze.getName();
7474 if (!ze.isDirectory()
75 && (name.equals("META-INF/MANIFEST.MF") || name.endsWith(".class") || Archive.isArchiveFileName(name))) {
75 && ("META-INF/MANIFEST.MF".equals(name) || name.endsWith(".class") || Archive.isArchiveFileName(name))) {
7676 entries.add(name);
77 if (name.equals("META-INF/MANIFEST.MF")) {
77 if ("META-INF/MANIFEST.MF".equals(name)) {
7878 map.put(name, build(zis, ze));
7979 }
8080 }
9292
9393 /*
9494 * (non-Javadoc)
95 *
95 *
9696 * @see
9797 * edu.umd.cs.findbugs.classfile.ICodeBase#lookupResource(java.lang.String)
9898 */
99 @Override
99100 public ICodeBaseEntry lookupResource(String resourceName) {
100101
101102 // Translate resource name, in case a resource name
181182
182183 /*
183184 * (non-Javadoc)
184 *
185 *
185186 * @see edu.umd.cs.findbugs.classfile.ICodeBaseIterator#hasNext()
186187 */
188 @Override
187189 public boolean hasNext() throws InterruptedException {
188190 if (Thread.interrupted()) {
189191 throw new InterruptedException();
194196
195197 /*
196198 * (non-Javadoc)
197 *
199 *
198200 * @see edu.umd.cs.findbugs.classfile.ICodeBaseIterator#next()
199201 */
202 @Override
200203 public ICodeBaseEntry next() throws InterruptedException {
201204 try {
202205 if (Thread.interrupted()) {
213216
214217 }
215218
219 @Override
216220 public ICodeBaseIterator iterator() {
217221 return new MyIterator();
218222 }
219223
220224 /*
221225 * (non-Javadoc)
222 *
226 *
223227 * @see edu.umd.cs.findbugs.classfile.ICodeBase#getPathName()
224228 */
229 @Override
225230 public String getPathName() {
226231 return file.getName();
227232 }
228233
229234 /*
230235 * (non-Javadoc)
231 *
236 *
232237 * @see edu.umd.cs.findbugs.classfile.ICodeBase#close()
233238 */
239 @Override
234240 public void close() {
235241
236242 }
237243
238244 /*
239245 * (non-Javadoc)
240 *
246 *
241247 * @see java.lang.Object#toString()
242248 */
243249 @Override
2828
2929 /**
3030 * Implementation of ICodeBaseEntry for resources in zipfile codebases.
31 *
31 *
3232 * @author David Hovemeyer
3333 */
3434 public class ZipInputStreamCodeBaseEntry extends AbstractScannableCodeBaseEntry {
4646
4747 /*
4848 * (non-Javadoc)
49 *
49 *
5050 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#getNumBytes()
5151 */
52 @Override
5253 public int getNumBytes() {
5354 return (int) zipEntry.getSize();
5455 }
5556
5657 /*
5758 * (non-Javadoc)
58 *
59 *
5960 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#openResource()
6061 */
62 @Override
6163 public InputStream openResource() throws IOException {
6264 return new ByteArrayInputStream(bytes);
6365 }
6870
6971 /*
7072 * (non-Javadoc)
71 *
73 *
7274 * @see
7375 * edu.umd.cs.findbugs.classfile.impl.AbstractScannableCodeBaseEntry#getCodeBase
7476 * ()
8082
8183 /*
8284 * (non-Javadoc)
83 *
85 *
8486 * @see edu.umd.cs.findbugs.classfile.impl.AbstractScannableCodeBaseEntry#
8587 * getRealResourceName()
8688 */
9193
9294 /*
9395 * (non-Javadoc)
94 *
96 *
9597 * @see edu.umd.cs.findbugs.classfile.ICodeBaseEntry#getClassDescriptor()
9698 */
99 @Override
97100 public ClassDescriptor getClassDescriptor() {
98101 return DescriptorFactory.createClassDescriptorFromResourceName(getResourceName());
99102 }
100103
101104 /*
102105 * (non-Javadoc)
103 *
106 *
104107 * @see java.lang.Object#equals(java.lang.Object)
105108 */
106109 @Override
114117
115118 /*
116119 * (non-Javadoc)
117 *
120 *
118121 * @see java.lang.Object#hashCode()
119122 */
120123 @Override
124127
125128 /*
126129 * (non-Javadoc)
127 *
130 *
128131 * @see java.lang.Object#toString()
129132 */
130133 @Override
8282 }
8383 }
8484 LEADERBOARD_BLACKLIST_PATTERN = p;
85
85
8686 }
8787
8888 protected final CloudPlugin plugin;
121121 }
122122
123123 boolean abstractCloudInitialized = false;
124 @Override
124125 public boolean isInitialized() {
125126 return abstractCloudInitialized;
126127 }
128 @Override
127129 @OverridingMethodsMustInvokeSuper
128130 public boolean initialize() throws IOException {
129131 abstractCloudInitialized = true;
151153 this.sourceFileLinkFormatWithLine = sfwl;
152154 } catch (RuntimeException e) {
153155 LOGGER.log(Level.WARNING, "Could not compile pattern " + sp, e);
154 if (THROW_EXCEPTION_IF_CANT_CONNECT)
156 if (THROW_EXCEPTION_IF_CANT_CONNECT) {
155157 throw e;
158 }
156159 }
157160 }
158161 return true;
159162 }
160163
164 @Override
161165 public Mode getMode() {
162166 return mode;
163167 }
164168
169 @Override
165170 public void setMode(Mode mode) {
166171 this.mode = mode;
167172 }
168173
174 @Override
169175 public CloudPlugin getPlugin() {
170176 return plugin;
171177 }
172178
179 @Override
173180 public BugCollection getBugCollection() {
174181 return bugCollection;
175182 }
176183
184 @Override
177185 public boolean supportsBugLinks() {
178186 return false;
179187 }
180188
189 @Override
181190 public void setBugLinkOnCloudAndStoreIssueDetails(BugInstance b, String viewUrl, String linkType)
182191 throws IOException, SignInCancelledException {
183192 throw new UnsupportedOperationException();
184193 }
185194
195 @Override
186196 public void updateBugStatusCache(BugInstance b, String status) {
187197 throw new UnsupportedOperationException();
188198 }
199 @Override
189200 public boolean supportsClaims() {
190201 return false;
191202 }
192203
204 @Override
193205 public boolean supportsCloudReports() {
194206 return true;
195207 }
196208
209 @Override
197210 public String claimedBy(BugInstance b) {
198211 throw new UnsupportedOperationException();
199212 }
200213
214 @Override
201215 public boolean claim(BugInstance b) {
202216 throw new UnsupportedOperationException();
203217 }
204218
219 @Override
205220 public URL getBugLink(BugInstance b) {
206221 throw new UnsupportedOperationException();
207222 }
208223
224 @Override
209225 public String getBugLinkType(BugInstance instance) {
210226 return null;
211227 }
212228
229 @Override
213230 public URL fileBug(BugInstance bug) {
214231 throw new UnsupportedOperationException();
215232 }
216233
234 @Override
217235 public BugFilingStatus getBugLinkStatus(BugInstance b) {
218236 throw new UnsupportedOperationException();
219237 }
231249 }
232250
233251 public boolean hasVoted(BugInstance bug) {
234 for (BugDesignation bd : getLatestDesignationFromEachUser(bug))
235 if (getUser().equals(bd.getUser()))
252 for (BugDesignation bd : getLatestDesignationFromEachUser(bug)) {
253 if (getUser().equals(bd.getUser())) {
236254 return true;
255 }
256 }
237257 return false;
238258 }
239259
240260 public String notInCloudMsg(BugInstance b) {
241
242 if (!!isOnlineCloud())
261
262 if (!!isOnlineCloud()) {
243263 return "off line cloud";
244 if (getSigninState().canDownload())
264 }
265 if (getSigninState().canDownload()) {
245266 return "disconnected from cloud";
246 if (!issueDataDownloaded)
267 }
268 if (!issueDataDownloaded) {
247269 return "Waiting for issue data...";
270 }
248271 return "Issue not recorded in cloud";
249
250 }
272
273 }
274 @Override
251275 public String getCloudReport(BugInstance b) {
252276 return getSelectiveCloudReport(b, Collections.<String>emptySet());
253277 }
254278
279 @Override
255280 public String getCloudReportWithoutMe(BugInstance b) {
256281 String user = getUser();
257282 Set<String> usersToExclude = user == null ? Collections.<String>emptySet() : Collections.singleton(user);
258283 return getSelectiveCloudReport(b, usersToExclude);
259284 }
260285
286 @Override
261287 public void bugsPopulated() {
262288 issueDataDownloaded = false;
263289 }
280306 if (bugLinkStatus != null && bugLinkStatus.bugIsFiled()) {
281307
282308 builder.append("\nBug status is ").append(getBugStatus(b));
283 if (getBugIsUnassigned(b))
309 if (getBugIsUnassigned(b)) {
284310 builder.append("\nBug is unassigned");
311 }
285312
286313 builder.append("\n\n");
287314 }
307334 return false;
308335 }
309336
337 @Override
310338 public String getBugStatus(BugInstance b) {
311339 return null;
312340 }
313341
314342 protected abstract Iterable<BugDesignation> getLatestDesignationFromEachUser(BugInstance bd);
315343
344 @Override
316345 public Date getUserDate(BugInstance b) {
317346 return new Date(getUserTimestamp(b));
318347 }
319348
349 @Override
320350 public void addListener(CloudListener listener) {
321 if (listener == null)
351 if (listener == null) {
322352 throw new NullPointerException();
323 if (!listeners.contains(listener))
353 }
354 if (!listeners.contains(listener)) {
324355 listeners.add(listener);
325 }
326
356 }
357 }
358
359 @Override
327360 public void removeListener(CloudListener listener) {
328361 listeners.remove(listener);
329362 }
330363
364 @Override
331365 public void addStatusListener(CloudStatusListener listener) {
332 if (listener == null)
366 if (listener == null) {
333367 throw new NullPointerException();
334 if (!statusListeners.contains(listener))
368 }
369 if (!statusListeners.contains(listener)) {
335370 statusListeners.add(listener);
336 }
337
371 }
372 }
373
374 @Override
338375 public void removeStatusListener(CloudStatusListener listener) {
339376 statusListeners.remove(listener);
340377 }
341378
379 @Override
342380 public String getStatusMsg() {
343381 return statusMsg;
344382 }
345383
384 @Override
346385 public void shutdown() {
347386
348387 }
349388
389 @Override
350390 public boolean getIWillFix(BugInstance b) {
351391 return getUserDesignation(b) == UserDesignation.I_WILL_FIX;
352392 }
353393
394 @Override
354395 public UserDesignation getConsensusDesignation(BugInstance b) {
355 if (b == null)
396 if (b == null) {
356397 throw new NullPointerException("null bug instance");
398 }
357399 Multiset<UserDesignation> designations = new Multiset<UserDesignation>();
358400 int count = 0;
359401 int totalCount = 0;
362404 int notAProblem = 0;
363405 for (BugDesignation designation : getLatestDesignationFromEachUser(b)) {
364406 UserDesignation d = UserDesignation.valueOf(designation.getDesignationKey());
365 if (d == UserDesignation.I_WILL_FIX)
407 if (d == UserDesignation.I_WILL_FIX) {
366408 d = UserDesignation.MUST_FIX;
367 else if (d == UserDesignation.UNCLASSIFIED)
409 } else if (d == UserDesignation.UNCLASSIFIED) {
368410 continue;
411 }
369412 switch (d) {
370413 case I_WILL_FIX:
371414 case MUST_FIX:
378421 case OBSOLETE_CODE:
379422 notAProblem++;
380423 break;
424 default:
425 break;
381426 }
382427 designations.add(d);
383428 totalCount++;
384 if (d.nonVoting())
429 if (d.nonVoting()) {
385430 continue;
431 }
386432 count++;
387433 total += d.score();
388434 }
389 if (totalCount == 0)
435 if (totalCount == 0) {
390436 return UserDesignation.UNCLASSIFIED;
437 }
391438 UserDesignation mostCommonVotingDesignation = null;
392439 UserDesignation mostCommonDesignation = null;
393440
395442 UserDesignation d = e.getKey();
396443 if (mostCommonVotingDesignation == null && !d.nonVoting()) {
397444 mostCommonVotingDesignation = d;
398 if (e.getValue() > count / 2)
445 if (e.getValue() > count / 2) {
399446 return d;
447 }
400448 }
401449 if (mostCommonDesignation == null && d != UserDesignation.UNCLASSIFIED) {
402450 mostCommonDesignation = d;
403 if (e.getValue() > count / 2)
451 if (e.getValue() > count / 2) {
404452 return d;
453 }
405454 }
406455 }
407456
408457 double score = total / count;
409 if (score >= UserDesignation.SHOULD_FIX.score() || isAProblem > notAProblem)
458 if (score >= UserDesignation.SHOULD_FIX.score() || isAProblem > notAProblem) {
410459 return UserDesignation.SHOULD_FIX;
411 if (score <= UserDesignation.NOT_A_BUG.score())
460 }
461 if (score <= UserDesignation.NOT_A_BUG.score()) {
412462 return UserDesignation.NOT_A_BUG;
413 if (score <= UserDesignation.MOSTLY_HARMLESS.score() || notAProblem > isAProblem)
463 }
464 if (score <= UserDesignation.MOSTLY_HARMLESS.score() || notAProblem > isAProblem) {
414465 return UserDesignation.MOSTLY_HARMLESS;
466 }
415467 return UserDesignation.NEEDS_STUDY;
416468
417469 }
418470
471 @Override
419472 public boolean overallClassificationIsNotAProblem(BugInstance b) {
420473 UserDesignation consensusDesignation = getConsensusDesignation(b);
421474 return consensusDesignation.notAProblem();
422475 }
423476
477 @Override
424478 public double getClassificationScore(BugInstance b) {
425479
426480 int count = 0;
427481 double total = 0.0;
428482 for (BugDesignation designation : getLatestDesignationFromEachUser(b)) {
429483 UserDesignation d = UserDesignation.valueOf(designation.getDesignationKey());
430 if (d.nonVoting())
484 if (d.nonVoting()) {
431485 continue;
486 }
432487 count++;
433488 total += d.score();
434489 }
436491
437492 }
438493
494 @Override
439495 public double getClassificationVariance(BugInstance b) {
440496
441497 int count = 0;
443499 double totalSquares = 0.0;
444500 for (BugDesignation designation : getLatestDesignationFromEachUser(b)) {
445501 UserDesignation d = UserDesignation.valueOf(designation.getDesignationKey());
446 if (d.nonVoting())
502 if (d.nonVoting()) {
447503 continue;
504 }
448505 count++;
449506 total += d.score();
450507 totalSquares += d.score() * d.score();
454511
455512 }
456513
514 @Override
457515 public double getPortionObsoleteClassifications(BugInstance b) {
458516 int count = 0;
459517 double total = 0.0;
460518 for (BugDesignation designation : getLatestDesignationFromEachUser(b)) {
461519 count++;
462520 UserDesignation d = UserDesignation.valueOf(designation.getDesignationKey());
463 if (d == UserDesignation.OBSOLETE_CODE)
521 if (d == UserDesignation.OBSOLETE_CODE) {
464522 total++;
523 }
465524 }
466525 return total / count;
467526 }
468527
528 @Override
469529 public int getNumberReviewers(BugInstance b) {
470530 int count = 0;
471531 Iterable<BugDesignation> designations = getLatestDesignationFromEachUser(b);
476536 return count;
477537 }
478538
539 @Override
479540 @SuppressWarnings("boxing")
480541 public void printCloudSummary(PrintWriter w, Iterable<BugInstance> bugs, String[] packagePrefixes) {
481542
510571 w.println("Code analyzed");
511572 }
512573 w.printf("%,7d packages%n%,7d classes%n", packageCount, classCount);
513 if (ncss > 0)
574 if (ncss > 0) {
514575 w.printf("%,7d thousands of lines of non-commenting source statements%n", (ncss + 999) / 1000);
576 }
515577 w.println();
516578 int count = 0;
517579 for (BugInstance bd : bugs) {
519581 count++;
520582 HashSet<String> reviewers = new HashSet<String>();
521583 String status = supportsBugLinks() && getBugLinkStatus(bd).bugIsFiled() ? getBugStatus(bd) : null;
522 if (status != null)
584 if (status != null) {
523585 bugStatus.add(status);
524
525 for (BugDesignation d : getLatestDesignationFromEachUser(bd))
586 }
587
588 for (BugDesignation d : getLatestDesignationFromEachUser(bd)) {
526589 if (reviewers.add(d.getUser())) {
527590 evaluations.add(d.getUser());
528591 designations.add(i18n.getUserDesignation(d.getDesignationKey()));
529592 }
593 }
530594
531595 int numReviews = Math.min(reviewers.size(), issuesWithThisManyReviews.length - 1);
532596 issuesWithThisManyReviews[numReviews]++;
533597
534598 }
535 if (count == getBugCollection().getCollection().size())
599 if (count == getBugCollection().getCollection().size()) {
536600 w.printf("Summary for %d issues%n%n", count);
537 else
601 } else {
538602 w.printf("Summary for %d issues that are in the current view%n%n", count);
603 }
539604 if (evaluations.numKeys() == 0) {
540605 w.println("No reviews found");
541606 } else {
558623 }
559624 w.println();
560625 w.println("Distribution of number of reviews");
561 for (int i = 0; i < issuesWithThisManyReviews.length; i++)
626 for (int i = 0; i < issuesWithThisManyReviews.length; i++) {
562627 if (issuesWithThisManyReviews[i] > 0) {
563628 w.printf("%4d with %3d review", issuesWithThisManyReviews[i], i);
564 if (i != 1)
629 if (i != 1) {
565630 w.print("s");
631 }
566632 w.println();
567633
568634 }
635 }
569636 }
570637
571638 @SuppressWarnings("boxing")
583650 previousScore = num;
584651 }
585652 String key = e.getKey();
586 if (LEADERBOARD_BLACKLIST_PATTERN != null && LEADERBOARD_BLACKLIST_PATTERN.matcher(key).matches())
653 if (LEADERBOARD_BLACKLIST_PATTERN != null && LEADERBOARD_BLACKLIST_PATTERN.matcher(key).matches()) {
587654 continue;
655 }
588656
589657 boolean shouldAlwaysPrint = key.equals(alwaysPrint);
590 if (row <= maxRows || shouldAlwaysPrint)
658 if (row <= maxRows || shouldAlwaysPrint) {
591659 w.printf(format, position, num, key);
592
593 if (shouldAlwaysPrint)
660 }
661
662 if (shouldAlwaysPrint) {
594663 foundAlwaysPrint = true;
664 }
595665 row++;
596666 if (row >= maxRows) {
597 if (alwaysPrint == null)
667 if (alwaysPrint == null) {
598668 break;
669 }
599670 if (foundAlwaysPrint) {
600671 w.printf("Total of %d %ss%n", evaluations.numKeys(), title);
601672 break;
605676 }
606677 }
607678
679 @Override
608680 public boolean supportsCloudSummaries() {
609681 return true;
610682 }
611683
684 @Override
612685 public boolean canStoreUserAnnotation(BugInstance bugInstance) {
613686 return true;
614687 }
615688
689 @Override
616690 public double getClassificationDisagreement(BugInstance b) {
617691 return 0;
618692 }
619693
694 @Override
620695 public UserDesignation getUserDesignation(BugInstance b) {
621696 BugDesignation bd = getPrimaryDesignation(b);
622 if (bd == null)
697 if (bd == null) {
623698 return UserDesignation.UNCLASSIFIED;
699 }
624700 return UserDesignation.valueOf(bd.getDesignationKey());
625701 }
626702
703 @Override
627704 public String getUserEvaluation(BugInstance b) {
628705 BugDesignation bd = getPrimaryDesignation(b);
629 if (bd == null)
706 if (bd == null) {
630707 return "";
708 }
631709 String result = bd.getAnnotationText();
632 if (result == null)
710 if (result == null) {
633711 return "";
712 }
634713 return result;
635714 }
636715
716 @Override
637717 public long getUserTimestamp(BugInstance b) {
638718 BugDesignation bd = getPrimaryDesignation(b);
639 if (bd == null)
719 if (bd == null) {
640720 return Long.MAX_VALUE;
721 }
641722 return bd.getTimestamp();
642723
643724 }
644725
726 @Override
645727 public long getFirstSeen(BugInstance b) {
646728 return getLocalFirstSeen(b);
647729 }
648730
731 @Override
649732 public void addDateSeen(BugInstance b, long when) {
650733 throw new UnsupportedOperationException();
651734 }
675758
676759 protected void fireIssueDataDownloadedEvent() {
677760 issueDataDownloaded = true;
678 for (CloudStatusListener statusListener : statusListeners)
761 for (CloudStatusListener statusListener : statusListeners) {
679762 statusListener.handleIssueDataDownloadedEvent();
680 }
681
763 }
764 }
765
766 @Override
682767 public SigninState getSigninState() {
683768 return signinState;
684769 }
685770
686 @SuppressWarnings({ "ThrowableInstanceNeverThrown" })
687771 public void setSigninState(SigninState state) {
688772 SigninState oldState = this.signinState;
689 if (oldState == state)
773 if (oldState == state) {
690774 return;
775 }
691776 LOGGER.log(Level.FINER, "State " + oldState + " -> " + state, new Throwable("Change in login state at:"));
692777 this.signinState = state;
693 for (CloudStatusListener statusListener : statusListeners)
778 for (CloudStatusListener statusListener : statusListeners) {
694779 statusListener.handleStateChange(oldState, state);
780 }
695781 }
696782
697783 public BugInstance getBugByHash(String hash) {
723809 listener.taskStarted(task);
724810 }
725811 task.setDefaultListener(new CloudTaskListener() {
812 @Override
726813 public void taskStatusUpdated(String statusLine, double percentCompleted) {
727814 setStatusMsg(name + "... " + statusLine);
728815 }
729816
817 @Override
730818 public void taskFinished() {
731819 setStatusMsg("");
732820 }
733821
822 @Override
734823 public void taskFailed(String message) {
735824 setStatusMsg(name + "... FAILED - " + message);
736825 }
748837
749838 private static void printLeaderBoard(PrintWriter w, Multiset<String> evaluations, int maxRows, String alwaysPrint,
750839 boolean listRank, String title) {
751 if (listRank)
840 if (listRank) {
752841 w.printf("%3s %4s %s%n", "rnk", "num", title);
753 else
842 } else {
754843 w.printf("%4s %s%n", "num", title);
844 }
755845 printLeaderBoard2(w, evaluations, maxRows, alwaysPrint, listRank ? "%3d %4d %s%n" : "%2$4d %3$s%n", title);
756846 }
757847
759849 return properties.getProperty("findbugs.cloud." + propertyName);
760850 }
761851
852 @Override
762853 public boolean supportsSourceLinks() {
763854 return sourceFileLinkPattern != null;
764855 }
765856
857 @Override
766858 @SuppressWarnings("boxing")
767859 public @CheckForNull URL getSourceLink(BugInstance b) {
768 if (sourceFileLinkPattern == null)
860 if (sourceFileLinkPattern == null) {
769861 return null;
862 }
770863
771864 SourceLineAnnotation src = b.getPrimarySourceLineAnnotation();
772865 String fileName = src.getSourcePath();
774867 int endLine = src.getEndLine();
775868 java.util.regex.Matcher m = sourceFileLinkPattern.matcher(fileName);
776869 boolean isMatch = m.matches();
777 if (isMatch)
870 if (isMatch) {
778871 try {
779872 URL link;
780 if (startLine > 0)
873 if (startLine > 0) {
781874 link = new URL(String.format(sourceFileLinkFormatWithLine, m.group(1),
782875 startLine, startLine - 10, endLine));
783 else
876 } else {
784877 link = new URL(String.format(sourceFileLinkFormat, m.group(1)));
878 }
785879 return link;
786880 } catch (Exception e) {
787881 AnalysisContext.logError("Error generating source link for " + src, e);
788882 }
883 }
789884
790885 return null;
791886
792887 }
793888
889 @Override
794890 public String getSourceLinkToolTip(BugInstance b) {
795891 return sourceFileLinkToolTip;
796892 }
802898 * edu.umd.cs.findbugs.cloud.Cloud#getBugIsUnassigned(edu.umd.cs.findbugs
803899 * .BugInstance)
804900 */
901 @Override
805902 public boolean getBugIsUnassigned(BugInstance b) {
806903 return true;
807904 }
813910 * edu.umd.cs.findbugs.cloud.Cloud#getWillNotBeFixed(edu.umd.cs.findbugs
814911 * .BugInstance)
815912 */
913 @Override
816914 public boolean getWillNotBeFixed(BugInstance b) {
817915 return false;
818916 }
819917
918 @Override
820919 public Set<String> getReviewers(BugInstance b) {
821920 HashSet<String> result = new HashSet<String>();
822 for (BugDesignation d : getLatestDesignationFromEachUser(b))
921 for (BugDesignation d : getLatestDesignationFromEachUser(b)) {
823922 result.add(d.getUser());
923 }
824924 return result;
825925 }
826926
927 @Override
827928 public IGuiCallback getGuiCallback() {
828929 return getBugCollection().getProject().getGuiCallback();
829930 }
830931
932 @Override
831933 public String getCloudName() {
832934 return getPlugin().getDescription();
833935 }
834
936
937 @Override
835938 public boolean communicationInitiated() {
836939 return !isOnlineCloud();
837940 }
839942 public long getLocalFirstSeen(BugInstance b) {
840943 long firstVersion = b.getFirstVersion();
841944 AppVersion v = getBugCollection().getAppVersionFromSequenceNumber(firstVersion);
842 if (v == null)
945 if (v == null) {
843946 return getBugCollection().getTimestamp();
947 }
844948 long firstSeen = v.getTimestamp();
845949 if (b.hasXmlProps()) {
846950 XmlProps props = b.getXmlProps();
847951 Date propsFirstSeen = props.getFirstSeen();
848 if (propsFirstSeen != null && firstSeen > propsFirstSeen.getTime())
952 if (propsFirstSeen != null && firstSeen > propsFirstSeen.getTime()) {
849953 firstSeen = propsFirstSeen.getTime();
954 }
850955 }
851956 return firstSeen;
852957 }
5656 }
5757 }
5858
59 @Override
5960 public void waitUntilIssueDataDownloaded() {
6061 }
6162
63 @Override
6264 public void initiateCommunication() {
6365 }
6466
67 @Override
6568 public boolean waitUntilNewIssuesUploaded(long timeout, TimeUnit unit) throws InterruptedException {
6669 return true;
6770 }
6871
72 @Override
6973 public boolean waitUntilIssueDataDownloaded(long timeout, TimeUnit unit) throws InterruptedException {
7074 return true;
7175 }
72
76
7377 @Override
7478 public Mode getMode() {
7579 return Mode.COMMUNAL;
7680 }
7781
82 @Override
7883 public String getUser() {
7984 return null;
8085 }
8388 @Override
8489 public UserDesignation getUserDesignation(BugInstance b) {
8590 BugDesignation bd = b.getUserDesignation();
86 if (bd == null)
91 if (bd == null) {
8792 return UserDesignation.UNCLASSIFIED;
93 }
8894 return UserDesignation.valueOf(bd.getDesignationKey());
8995 }
9096
9298 @Override
9399 public String getUserEvaluation(BugInstance b) {
94100 BugDesignation bd = b.getUserDesignation();
95 if (bd == null)
101 if (bd == null) {
96102 return "";
103 }
97104 return bd.getAnnotationText();
98105 }
99106
101108 @Override
102109 public long getUserTimestamp(BugInstance b) {
103110 BugDesignation bd = b.getUserDesignation();
104 if (bd == null)
111 if (bd == null) {
105112 return Long.MAX_VALUE;
113 }
106114 return bd.getTimestamp();
107115 }
108116
116124
117125 }
118126
127 @Override
119128 public void setSaveSignInInformation(boolean save) {
120129 }
121130
131 @Override
122132 public boolean isSavingSignInInformationEnabled() {
123133 return false;
124134 }
125135
136 @Override
126137 public void signIn() {
127138 }
128139
140 @Override
129141 public void signOut() {
130142 }
131143
144 @Override
132145 public boolean availableForInitialization() {
133146 return true;
134147 }
135148
149 @Override
136150 public void storeUserAnnotation(BugInstance bugInstance) {
137151
138152 }
139153
154 @Override
140155 public void bugFiled(BugInstance b, Object bugLink) {
141156 throw new UnsupportedOperationException();
142157 }
143158
159 @Override
144160 @SuppressWarnings({"deprecation"})
145161 public BugDesignation getPrimaryDesignation(BugInstance b) {
146162 return b.getUserDesignation();
149165 @Override
150166 protected Iterable<BugDesignation> getLatestDesignationFromEachUser(BugInstance bd) {
151167 BugDesignation designation = getPrimaryDesignation(bd);
152 if (designation == null)
168 if (designation == null) {
153169 return Collections.emptySet();
170 }
154171 return Collections.singleton(designation);
155172 }
156173
174 @Override
157175 public Collection<String> getProjects(String className) {
158176 return Collections.emptyList();
159177 }
160178
179 @Override
161180 public boolean isInCloud(BugInstance b) {
162181 return true;
163182 }
164183
184 @Override
165185 public boolean isOnlineCloud() {
166186 return false;
167187 }
168188
189 @Override
169190 public void waitUntilNewIssuesUploaded() {
170191 }
171192
172193 @Override
173194 public void addDateSeen(BugInstance b, long when) {
174 if (when > 0)
175 b.getXmlProps().setFirstSeen(new Date(when));
195 if (when > 0) {
196 b.getXmlProps().setFirstSeen(new Date(when));
197 }
176198 }
177199
178200 }
7676
7777 int firstLine = Integer.MAX_VALUE;
7878 int lastLine = Integer.MIN_VALUE;
79 for (BugAnnotation a : b.getAnnotations())
79 for (BugAnnotation a : b.getAnnotations()) {
8080 if (a instanceof SourceLineAnnotation) {
8181 SourceLineAnnotation s = (SourceLineAnnotation) a;
8282 if (s.getClassName().equals(primaryClass.getClassName()) && s.getStartLine() > 0) {
8686 }
8787
8888 }
89 }
8990
9091 SourceLineAnnotation primarySource = primaryClass.getSourceLines();
9192 if (primarySource.isSourceFileKnown() && firstLine >= 1 && firstLine <= lastLine && lastLine - firstLine < 50) {
99100 List<SourceLine> source = new ArrayList<SourceLine>();
100101 while (lineNumber <= lastLine + 4) {
101102 String txt = in.readLine();
102 if (txt == null)
103 if (txt == null) {
103104 break;
105 }
104106 if (lineNumber >= firstLine - 4) {
105107 String trimmed = txt.trim();
106108 if (trimmed.length() == 0) {
107 if (lineNumber > lastLine)
109 if (lineNumber > lastLine) {
108110 break;
111 }
109112 txt = trimmed;
110113
111114 }
114117 }
115118 lineNumber++;
116119 }
117 if (commonWhiteSpace == null)
120 if (commonWhiteSpace == null) {
118121 commonWhiteSpace = "";
122 }
119123 out.println("\nRelevant source code:");
120124 for (SourceLine s : source) {
121 if (s.text.length() == 0)
125 if (s.text.length() == 0) {
122126 out.printf("%5d: %n", s.line);
123 else
127 } else {
124128 out.printf("%5d: %s%n", s.line, s.text.substring(commonWhiteSpace.length()));
129 }
125130 }
126131
127132 out.println();
146151 ClassAnnotation primaryClass = b.getPrimaryClass();
147152
148153 for (BugAnnotation a : b.getAnnotations()) {
149 if (a == primaryClass)
154 if (a == primaryClass) {
150155 out.println(a);
151 else
156 } else {
152157 out.println(" " + a.toString(primaryClass));
158 }
153159 }
154160 if (cloud.supportsSourceLinks()) {
155161 URL link = cloud.getSourceLink(b);
191197 UserDesignation designation = cloud.getUserDesignation(b);
192198
193199 String result;
194 if (designation != UserDesignation.UNCLASSIFIED)
200 if (designation != UserDesignation.UNCLASSIFIED) {
195201 result = "Classified as: " + designation.toString() + "\n";
196 else
202 } else {
197203 result = "";
204 }
198205 String eval = cloud.getUserEvaluation(b).trim();
199 if (eval.length() > 0)
206 if (eval.length() > 0) {
200207 result = result + eval + "\n";
208 }
201209 return result;
202210 }
203211
209217 // ====================================
210218
211219 private String commonLeadingWhitespace(String soFar, String txt) {
212 if (txt.length() == 0)
220 if (txt.length() == 0) {
213221 return soFar;
214 if (soFar == null)
222 }
223 if (soFar == null) {
215224 return txt;
225 }
216226 soFar = Util.commonPrefix(soFar, txt);
217227 for (int i = 0; i < soFar.length(); i++) {
218 if (!Character.isWhitespace(soFar.charAt(i)))
228 if (!Character.isWhitespace(soFar.charAt(i))) {
219229 return soFar.substring(0, i);
230 }
220231 }
221232 return soFar;
222233 }
9494 * Waits until all new issues have been uploaded
9595 */
9696 public void waitUntilNewIssuesUploaded();
97
97
9898 public boolean waitUntilNewIssuesUploaded(long timeout, TimeUnit unit)
9999 throws InterruptedException;
100100
220220 URL fileBug(BugInstance b);
221221
222222 void setBugLinkOnCloudAndStoreIssueDetails(BugInstance b, String viewUrl, String linkType) throws IOException,
223 SignInCancelledException;
223 SignInCancelledException;
224224
225225 /** Updates the local cache of bug reporting status. Does not modify server code. */
226226 void updateBugStatusCache(BugInstance b, String status);
435435 }
436436 }
437437
438 /**
439 * @return
440 */
441438 public boolean nonVoting() {
442439 return this == UserDesignation.OBSOLETE_CODE || this == UserDesignation.NEEDS_STUDY
443440 || this == UserDesignation.UNCLASSIFIED;
6666 Constructor<? extends Cloud> constructor = cloudClass.getConstructor(CloudPlugin.class, BugCollection.class,
6767 Properties.class);
6868 Cloud cloud = constructor.newInstance(plugin, bc, properties);
69 if (DEBUG)
69 if (DEBUG) {
7070 bc.getProject().getGuiCallback().showMessageDialog("constructed " + cloud.getClass().getName());
71 }
7172 LOGGER.log(Level.FINE, "constructed cloud plugin " + plugin.getId());
72 if (!cloud.availableForInitialization())
73 handleInitializationException(bc, plugin,
73 if (!cloud.availableForInitialization()) {
74 handleInitializationException(bc, plugin,
7475 new IllegalStateException(cloud.getClass().getName() + " cloud " + plugin.getId()+ " doesn't have information needed for initialization"));
76 }
7577 return cloud;
7678 } catch (InvocationTargetException e) {
77 return handleInitializationException(bc, plugin, e.getCause());
79 return handleInitializationException(bc, plugin, e.getCause());
7880 } catch (Exception e) {
7981 return handleInitializationException(bc, plugin, e);
8082 }
8183
8284 }
8385
84 /**
85 * @param bc
86 * @return
87 */
8886 public static CloudPlugin getCloudPlugin(BugCollection bc) {
8987 CloudPlugin plugin = null;
9088 Project project = bc.getProject();
9290 String cloudId = project.getCloudId();
9391 if (cloudId != null) {
9492 plugin = DetectorFactoryCollection.instance().getRegisteredClouds().get(cloudId);
95 if (plugin == null && FAIL_ON_CLOUD_ERROR)
93 if (plugin == null && FAIL_ON_CLOUD_ERROR) {
9694 throw new IllegalArgumentException("Cannot find registered cloud for " + cloudId);
95 }
9796 }
9897 // is the desired plugin disabled for this project (and/or globally)? if so, skip it.
9998 if (plugin != null) {
104103 }
105104 }
106105 if (plugin == null) {
107 if (DEFAULT_CLOUD != null)
106 if (DEFAULT_CLOUD != null) {
108107 LOGGER.log(Level.FINE, "Trying default cloud " + DEFAULT_CLOUD);
108 }
109109 cloudId = DEFAULT_CLOUD;
110110 plugin = DetectorFactoryCollection.instance().getRegisteredClouds().get(cloudId);
111 }
111 }
112112 return plugin;
113113 }
114114
117117 bc.getProject().getGuiCallback().showMessageDialog("failed " + e.getMessage() + e.getClass().getName());
118118 }
119119 LOGGER.log(Level.WARNING, "Could not load cloud plugin " + plugin, e);
120 if (SystemProperties.getBoolean("findbugs.failIfUnableToConnectToCloud"))
120 if (SystemProperties.getBoolean("findbugs.failIfUnableToConnectToCloud")) {
121121 System.exit(1);
122 }
122123 return getPlainCloud(bc);
123124 }
124125
125126 public static void initializeCloud(BugCollection bc, Cloud cloud) throws IOException {
126127 IGuiCallback callback = bc.getProject().getGuiCallback();
127128
128 if (!cloud.availableForInitialization())
129 if (!cloud.availableForInitialization()) {
129130 return;
131 }
130132
131 if (DEBUG)
133 if (DEBUG) {
132134 callback.showMessageDialog("attempting to initialize " + cloud.getClass().getName());
135 }
133136
134 if (!cloud.initialize())
137 if (!cloud.initialize()) {
135138 throw new IOException("Unable to connect to " + cloud.getCloudName());
139 }
136140
137 if (DEBUG)
141 if (DEBUG) {
138142 callback.showMessageDialog("initialized " + cloud.getClass().getName());
143 }
139144 }
140145
141146
142147 public static @Nonnull Cloud getPlainCloud(BugCollection bc) {
143148 DoNothingCloud cloud = new DoNothingCloud(bc);
144 if (cloud.initialize())
149 if (cloud.initialize()) {
145150 return cloud;
151 }
146152 throw new IllegalStateException("Unable to initialize DoNothingCloud");
147153 }
148154
4545 final boolean hidden;
4646
4747 public CloudPlugin(String findbugsPluginId, String cloudid, ClassLoader classLoader, Class<? extends Cloud> cloudClass,
48 Class<? extends NameLookup> usernameClass, boolean hidden, PropertyBundle properties, String description,
49 String details) {
48 Class<? extends NameLookup> usernameClass, boolean hidden, PropertyBundle properties, String description,
49 String details) {
5050 this.findbugsPluginId = findbugsPluginId;
5151 this.cloudid = cloudid;
5252 this.classLoader = classLoader;
9898 public String toString() {
9999 return getDescription();
100100 }
101
101
102102 public boolean isOnline() {
103103 return OnlineCloud.class.isAssignableFrom(cloudClass);
104104 }
0 package edu.umd.cs.findbugs.cloud;
1
2 import java.io.IOException;
3 import java.io.PrintWriter;
4 import java.net.URL;
5 import java.util.Collection;
6 import java.util.Collections;
7 import java.util.Date;
8 import java.util.Properties;
9 import java.util.Set;
10 import java.util.concurrent.TimeUnit;
11
12 import javax.annotation.CheckForNull;
13
14 import edu.umd.cs.findbugs.AppVersion;
15 import edu.umd.cs.findbugs.BugCollection;
16 import edu.umd.cs.findbugs.BugDesignation;
17 import edu.umd.cs.findbugs.BugInstance;
18 import edu.umd.cs.findbugs.IGuiCallback;
19 import edu.umd.cs.findbugs.PropertyBundle;
20 import edu.umd.cs.findbugs.cloud.username.NoNameLookup;
21
22 /**
23 * Doesn't do much. Relies on the {@link edu.umd.cs.findbugs.BugInstance.XmlProps}
24 * read from the analysis XML file, if present.
25 */
26 public class DoNothingCloud implements Cloud {
27 private CloudPlugin plugin;
28 private BugCollection bugCollection;
29
30 private static CloudPlugin getFallbackPlugin() {
31 return new CloudPluginBuilder().setCloudid("edu.umd.cs.findbugs.cloud.doNothingCloud").setDescription("Do Nothing Cloud")
32 .setDetails("No reviews will be stored.")
33 .setClassLoader(BugCollectionStorageCloud.class.getClassLoader())
34 .setCloudClass(BugCollectionStorageCloud.class)
35 .setUsernameClass(NoNameLookup.class)
36 .setProperties(new PropertyBundle())
37 .setOnlineStorage(false)
38 .createCloudPlugin();
39 }
40
41 /** Invoked via reflection */
42 @SuppressWarnings({"UnusedDeclaration"})
43 public DoNothingCloud(CloudPlugin plugin, BugCollection bc, Properties props) {
44 this.plugin = plugin;
45 this.bugCollection = bc;
46 }
47
48 public DoNothingCloud(BugCollection bc) {
49 this(getFallbackPlugin(), bc, new Properties());
50 }
51
52 public CloudPlugin getPlugin() {
53 return plugin;
54 }
55
56 public String getCloudName() {
57 return "(no cloud selected)";
58 }
59
60 public BugCollection getBugCollection() {
61 return bugCollection;
62 }
63
64 public IGuiCallback getGuiCallback() {
65 return null;
66 }
67
68 public String getStatusMsg() {
69 return null;
70 }
71
72 public void printCloudSummary(PrintWriter w, Iterable<BugInstance> bugs, String[] packagePrefixes) {
73 }
74
75 public void addListener(CloudListener listener) {
76 }
77
78 public void removeListener(CloudListener listener) {
79 }
80
81 public void addStatusListener(CloudStatusListener cloudStatusListener) {
82 }
83
84 public void removeStatusListener(CloudStatusListener cloudStatusListener) {
85 }
86
87 public boolean availableForInitialization() {
88 return true;
89 }
90
91 public boolean initialize() {
92 return true;
93 }
94
95 public void waitUntilNewIssuesUploaded() {
96 }
97
98 public void waitUntilIssueDataDownloaded() {
99 }
100
101 public boolean waitUntilNewIssuesUploaded(long timeout, TimeUnit unit) throws InterruptedException {
102 return true;
103 }
104
105 public boolean waitUntilIssueDataDownloaded(long timeout, TimeUnit unit) throws InterruptedException {
106 return true;
107 }
108 public void bugsPopulated() {
109 }
110
111 public void initiateCommunication() {
112 }
113
114 public void shutdown() {
115 }
116
117 public String getUser() {
118 return null;
119 }
120
121 public SigninState getSigninState() {
122 return SigninState.NO_SIGNIN_REQUIRED;
123 }
124
125 public void setSaveSignInInformation(boolean save) {
126 }
127
128 public boolean isSavingSignInInformationEnabled() {
129 return false;
130 }
131
132 public void signIn() throws IOException {
133 }
134
135 public void signOut() {
136 }
137
138 public Mode getMode() {
139 return null;
140 }
141
142 public void setMode(Mode m) {
143 }
144
145 public boolean supportsSourceLinks() {
146 return false;
147 }
148
149 public boolean supportsBugLinks() {
150 return false;
151 }
152
153 public boolean supportsCloudReports() {
154 return false;
155 }
156
157 public boolean supportsClaims() {
158 return false;
159 }
160
161 public boolean supportsCloudSummaries() {
162 return false;
163 }
164
165 public Collection<String> getProjects(String className) {
166 return null;
167 }
168
169 public boolean isInCloud(BugInstance b) {
170 return b.getXmlProps().isInCloud();
171 }
172
173 public boolean isOnlineCloud() {
174 return "true".equals(bugCollection.getXmlCloudDetails().get("online"));
175 }
176
177 public boolean getIWillFix(BugInstance b) {
178 return false;
179 }
180
181 public String getSourceLinkToolTip(@CheckForNull BugInstance b) {
182 return null;
183 }
184
185 public URL getSourceLink(BugInstance b) {
186 return null;
187 }
188
189 public BugFilingStatus getBugLinkStatus(BugInstance b) {
190 return null;
191 }
192
193 public String getBugStatus(BugInstance b) {
194 return null;
195 }
196
197 public boolean getWillNotBeFixed(BugInstance b) {
198 return false;
199 }
200
201 public boolean getBugIsUnassigned(BugInstance b) {
202 return false;
203 }
204
205 public URL getBugLink(BugInstance b) {
206 return null;
207 }
208
209 public String getBugLinkType(BugInstance instance) {
210 return null;
211 }
212
213 public URL fileBug(BugInstance b) {
214 throw new UnsupportedOperationException();
215 }
216
217 public void setBugLinkOnCloudAndStoreIssueDetails(BugInstance b, String viewUrl, String linkType)
218 throws IOException, SignInCancelledException {
219 throw new UnsupportedOperationException();
220 }
221
222 public void updateBugStatusCache(BugInstance b, String status) {
223 }
224
225 public void bugFiled(BugInstance b, @CheckForNull Object bugLink) {
226 throw new UnsupportedOperationException();
227 }
228
229 public String getCloudReport(BugInstance b) {
230 return "";
231 }
232
233 public String getCloudReportWithoutMe(BugInstance b) {
234 return getCloudReport(b);
235 }
236
237 public String claimedBy(BugInstance b) {
238 return null;
239 }
240
241 public boolean claim(BugInstance b) {
242 throw new UnsupportedOperationException();
243 }
244
245 public long getUserTimestamp(BugInstance b) {
246 return 0;
247 }
248
249 public Date getUserDate(BugInstance b) {
250 return null;
251 }
252
253 public BugDesignation getPrimaryDesignation(BugInstance b) {
254 return null;
255 }
256
257 public UserDesignation getUserDesignation(BugInstance b) {
258 return null;
259 }
260
261 public String getUserEvaluation(BugInstance b) {
262 return null;
263 }
264
265 public double getClassificationScore(BugInstance b) {
266 return 0;
267 }
268
269 public double getClassificationVariance(BugInstance b) {
270 return 0;
271 }
272
273 public double getClassificationDisagreement(BugInstance b) {
274 return 0;
275 }
276
277 public double getPortionObsoleteClassifications(BugInstance b) {
278 return 0;
279 }
280
281 public int getNumberReviewers(BugInstance b) {
282 return b.getXmlProps().getReviewCount();
283 }
284
285 public Set<String> getReviewers(BugInstance b) {
286 return Collections.emptySet();
287 }
288
289 public long getFirstSeen(BugInstance b) {
290 long computed = getFirstSeenFromVersion(b);
291 Date fromXml = b.getXmlProps().getFirstSeen();
292 if (fromXml == null)
293 return computed;
294
295 long fromXmlTime = fromXml.getTime();
296 if (computed == 0 && fromXmlTime > 0)
297 return fromXmlTime;
298 else if (fromXmlTime == 0 && computed > 0)
299 return computed;
300
301 return Math.min(fromXmlTime, computed);
302 }
303
304 public void addDateSeen(BugInstance b, long when) {
305 if (when > 0)
306 b.getXmlProps().setFirstSeen(new Date(when));
307 }
308
309 public long getFirstSeenFromVersion(BugInstance b) {
310 long firstVersion = b.getFirstVersion();
311 AppVersion v = getBugCollection().getAppVersionFromSequenceNumber(firstVersion);
312 if (v == null)
313 return getBugCollection().getTimestamp();
314 return v.getTimestamp();
315 }
316
317 public UserDesignation getConsensusDesignation(BugInstance b) {
318 String consensus = b.getXmlProps().getConsensus();
319 if (consensus == null)
320 return UserDesignation.UNCLASSIFIED;
321 try {
322 return UserDesignation.valueOf(consensus);
323 } catch (IllegalArgumentException e) {
324 return UserDesignation.UNCLASSIFIED;
325 }
326 }
327
328 public boolean overallClassificationIsNotAProblem(BugInstance b) {
329 UserDesignation consensusDesignation = getConsensusDesignation(b);
330 return consensusDesignation != UserDesignation.UNCLASSIFIED && consensusDesignation.score() < 0;
331 }
332
333 public boolean canStoreUserAnnotation(BugInstance bugInstance) {
334 return false;
335 }
336
337 public void storeUserAnnotation(BugInstance bugInstance) {
338 throw new UnsupportedOperationException();
339 }
340
341 public boolean communicationInitiated() {
342 return false;
343 }
344
345 public boolean isInitialized() {
346 return true;
347 }
348
349
350 }
0 package edu.umd.cs.findbugs.cloud;
1
2 import java.io.IOException;
3 import java.io.PrintWriter;
4 import java.net.URL;
5 import java.util.Collection;
6 import java.util.Collections;
7 import java.util.Date;
8 import java.util.Properties;
9 import java.util.Set;
10 import java.util.concurrent.TimeUnit;
11
12 import javax.annotation.CheckForNull;
13
14 import edu.umd.cs.findbugs.AppVersion;
15 import edu.umd.cs.findbugs.BugCollection;
16 import edu.umd.cs.findbugs.BugDesignation;
17 import edu.umd.cs.findbugs.BugInstance;
18 import edu.umd.cs.findbugs.IGuiCallback;
19 import edu.umd.cs.findbugs.PropertyBundle;
20 import edu.umd.cs.findbugs.cloud.username.NoNameLookup;
21
22 /**
23 * Doesn't do much. Relies on the {@link edu.umd.cs.findbugs.BugInstance.XmlProps}
24 * read from the analysis XML file, if present.
25 */
26 public class DoNothingCloud implements Cloud {
27 private final CloudPlugin plugin;
28 private final BugCollection bugCollection;
29
30 private static CloudPlugin getFallbackPlugin() {
31 return new CloudPluginBuilder().setCloudid("edu.umd.cs.findbugs.cloud.doNothingCloud").setDescription("Do Nothing Cloud")
32 .setDetails("No reviews will be stored.")
33 .setClassLoader(BugCollectionStorageCloud.class.getClassLoader())
34 .setCloudClass(BugCollectionStorageCloud.class)
35 .setUsernameClass(NoNameLookup.class)
36 .setProperties(new PropertyBundle())
37 .setOnlineStorage(false)
38 .createCloudPlugin();
39 }
40
41 /** Invoked via reflection */
42 @SuppressWarnings({"UnusedDeclaration"})
43 public DoNothingCloud(CloudPlugin plugin, BugCollection bc, Properties props) {
44 this.plugin = plugin;
45 this.bugCollection = bc;
46 }
47
48 public DoNothingCloud(BugCollection bc) {
49 this(getFallbackPlugin(), bc, new Properties());
50 }
51
52 @Override
53 public CloudPlugin getPlugin() {
54 return plugin;
55 }
56
57 @Override
58 public String getCloudName() {
59 return "(no cloud selected)";
60 }
61
62 @Override
63 public BugCollection getBugCollection() {
64 return bugCollection;
65 }
66
67 @Override
68 public IGuiCallback getGuiCallback() {
69 return null;
70 }
71
72 @Override
73 public String getStatusMsg() {
74 return null;
75 }
76
77 @Override
78 public void printCloudSummary(PrintWriter w, Iterable<BugInstance> bugs, String[] packagePrefixes) {
79 }
80
81 @Override
82 public void addListener(CloudListener listener) {
83 }
84
85 @Override
86 public void removeListener(CloudListener listener) {
87 }
88
89 @Override
90 public void addStatusListener(CloudStatusListener cloudStatusListener) {
91 }
92
93 @Override
94 public void removeStatusListener(CloudStatusListener cloudStatusListener) {
95 }
96
97 @Override
98 public boolean availableForInitialization() {
99 return true;
100 }
101
102 @Override
103 public boolean initialize() {
104 return true;
105 }
106
107 @Override
108 public void waitUntilNewIssuesUploaded() {
109 }
110
111 @Override
112 public void waitUntilIssueDataDownloaded() {
113 }
114
115 @Override
116 public boolean waitUntilNewIssuesUploaded(long timeout, TimeUnit unit) throws InterruptedException {
117 return true;
118 }
119
120 @Override
121 public boolean waitUntilIssueDataDownloaded(long timeout, TimeUnit unit) throws InterruptedException {
122 return true;
123 }
124 @Override
125 public void bugsPopulated() {
126 }
127
128 @Override
129 public void initiateCommunication() {
130 }
131
132 @Override
133 public void shutdown() {
134 }
135
136 @Override
137 public String getUser() {
138 return null;
139 }
140
141 @Override
142 public SigninState getSigninState() {
143 return SigninState.NO_SIGNIN_REQUIRED;
144 }
145
146 @Override
147 public void setSaveSignInInformation(boolean save) {
148 }
149
150 @Override
151 public boolean isSavingSignInInformationEnabled() {
152 return false;
153 }
154
155 @Override
156 public void signIn() throws IOException {
157 }
158
159 @Override
160 public void signOut() {
161 }
162
163 @Override
164 public Mode getMode() {
165 return null;
166 }
167
168 @Override
169 public void setMode(Mode m) {
170 }
171
172 @Override
173 public boolean supportsSourceLinks() {
174 return false;
175 }
176
177 @Override
178 public boolean supportsBugLinks() {
179 return false;
180 }
181
182 @Override
183 public boolean supportsCloudReports() {
184 return false;
185 }
186
187 @Override
188 public boolean supportsClaims() {
189 return false;
190 }
191
192 @Override
193 public boolean supportsCloudSummaries() {
194 return false;
195 }
196
197 @Override
198 public Collection<String> getProjects(String className) {
199 return null;
200 }
201
202 @Override
203 public boolean isInCloud(BugInstance b) {
204 return b.getXmlProps().isInCloud();
205 }
206
207 @Override
208 public boolean isOnlineCloud() {
209 return "true".equals(bugCollection.getXmlCloudDetails().get("online"));
210 }
211
212 @Override
213 public boolean getIWillFix(BugInstance b) {
214 return false;
215 }
216
217 @Override
218 public String getSourceLinkToolTip(@CheckForNull BugInstance b) {
219 return null;
220 }
221
222 @Override
223 public URL getSourceLink(BugInstance b) {
224 return null;
225 }
226
227 @Override
228 public BugFilingStatus getBugLinkStatus(BugInstance b) {
229 return null;
230 }
231
232 @Override
233 public String getBugStatus(BugInstance b) {
234 return null;
235 }
236
237 @Override
238 public boolean getWillNotBeFixed(BugInstance b) {
239 return false;
240 }
241
242 @Override
243 public boolean getBugIsUnassigned(BugInstance b) {
244 return false;
245 }
246
247 @Override
248 public URL getBugLink(BugInstance b) {
249 return null;
250 }
251
252 @Override
253 public String getBugLinkType(BugInstance instance) {
254 return null;
255 }
256
257 @Override
258 public URL fileBug(BugInstance b) {
259 throw new UnsupportedOperationException();
260 }
261
262 @Override
263 public void setBugLinkOnCloudAndStoreIssueDetails(BugInstance b, String viewUrl, String linkType)
264 throws IOException, SignInCancelledException {
265 throw new UnsupportedOperationException();
266 }
267
268 @Override
269 public void updateBugStatusCache(BugInstance b, String status) {
270 }
271
272 @Override
273 public void bugFiled(BugInstance b, @CheckForNull Object bugLink) {
274 throw new UnsupportedOperationException();
275 }
276
277 @Override
278 public String getCloudReport(BugInstance b) {
279 return "";
280 }
281
282 @Override
283 public String getCloudReportWithoutMe(BugInstance b) {
284 return getCloudReport(b);
285 }
286
287 @Override
288 public String claimedBy(BugInstance b) {
289 return null;
290 }
291
292 @Override
293 public boolean claim(BugInstance b) {
294 throw new UnsupportedOperationException();
295 }
296
297 @Override
298 public long getUserTimestamp(BugInstance b) {
299 return 0;
300 }
301
302 @Override
303 public Date getUserDate(BugInstance b) {
304 return null;
305 }
306
307 @Override
308 public BugDesignation getPrimaryDesignation(BugInstance b) {
309 return null;
310 }
311
312 @Override
313 public UserDesignation getUserDesignation(BugInstance b) {
314 return null;
315 }
316
317 @Override
318 public String getUserEvaluation(BugInstance b) {
319 return null;
320 }
321
322 @Override
323 public double getClassificationScore(BugInstance b) {
324 return 0;
325 }
326
327 @Override
328 public double getClassificationVariance(BugInstance b) {
329 return 0;
330 }
331
332 @Override
333 public double getClassificationDisagreement(BugInstance b) {
334 return 0;
335 }
336
337 @Override
338 public double getPortionObsoleteClassifications(BugInstance b) {
339 return 0;
340 }
341
342 @Override
343 public int getNumberReviewers(BugInstance b) {
344 return b.getXmlProps().getReviewCount();
345 }
346
347 @Override
348 public Set<String> getReviewers(BugInstance b) {
349 return Collections.emptySet();
350 }
351
352 @Override
353 public long getFirstSeen(BugInstance b) {
354 long computed = getFirstSeenFromVersion(b);
355 Date fromXml = b.getXmlProps().getFirstSeen();
356 if (fromXml == null) {
357 return computed;
358 }
359
360 long fromXmlTime = fromXml.getTime();
361 if (computed == 0 && fromXmlTime > 0) {
362 return fromXmlTime;
363 } else if (fromXmlTime == 0 && computed > 0) {
364 return computed;
365 }
366
367 return Math.min(fromXmlTime, computed);
368 }
369
370 @Override
371 public void addDateSeen(BugInstance b, long when) {
372 if (when > 0) {
373 b.getXmlProps().setFirstSeen(new Date(when));
374 }
375 }
376
377 public long getFirstSeenFromVersion(BugInstance b) {
378 long firstVersion = b.getFirstVersion();
379 AppVersion v = getBugCollection().getAppVersionFromSequenceNumber(firstVersion);
380 if (v == null) {
381 return getBugCollection().getTimestamp();
382 }
383 return v.getTimestamp();
384 }
385
386 @Override
387 public UserDesignation getConsensusDesignation(BugInstance b) {
388 String consensus = b.getXmlProps().getConsensus();
389 if (consensus == null) {
390 return UserDesignation.UNCLASSIFIED;
391 }
392 try {
393 return UserDesignation.valueOf(consensus);
394 } catch (IllegalArgumentException e) {
395 return UserDesignation.UNCLASSIFIED;
396 }
397 }
398
399 @Override
400 public boolean overallClassificationIsNotAProblem(BugInstance b) {
401 UserDesignation consensusDesignation = getConsensusDesignation(b);
402 return consensusDesignation != UserDesignation.UNCLASSIFIED && consensusDesignation.score() < 0;
403 }
404
405 @Override
406 public boolean canStoreUserAnnotation(BugInstance bugInstance) {
407 return false;
408 }
409
410 @Override
411 public void storeUserAnnotation(BugInstance bugInstance) {
412 throw new UnsupportedOperationException();
413 }
414
415 @Override
416 public boolean communicationInitiated() {
417 return false;
418 }
419
420 @Override
421 public boolean isInitialized() {
422 return true;
423 }
424
425
426 }
0 package edu.umd.cs.findbugs.cloud;
1
2 import java.util.ArrayList;
3 import java.util.List;
4 import java.util.concurrent.CopyOnWriteArrayList;
5
6 public class MutableCloudTask implements Cloud.CloudTask {
7 private String name;
8
9 private CopyOnWriteArrayList<Cloud.CloudTaskListener> listeners = new CopyOnWriteArrayList<Cloud.CloudTaskListener>();
10
11 private String substatus = "";
12
13 private double percentDone = 0;
14
15 /** A listener used only if no other listeners are present. */
16 private Cloud.CloudTaskListener defaultListener;
17
18 private boolean finished = false;
19
20 private boolean useDefaultListener = true;
21
22 public MutableCloudTask(String name) {
23 this.name = name;
24 }
25
26 public String getName() {
27 return name;
28 }
29
30 public String getStatusLine() {
31 return substatus;
32 }
33
34 public double getPercentCompleted() {
35 return percentDone;
36 }
37
38 public void addListener(Cloud.CloudTaskListener listener) {
39 listeners.addIfAbsent(listener);
40 }
41
42 public void removeListener(Cloud.CloudTaskListener listener) {
43 listeners.remove(listener);
44 }
45
46 public boolean isFinished() {
47 return finished;
48 }
49
50 public void setUseDefaultListener(boolean enabled) {
51 this.useDefaultListener = enabled;
52 }
53
54 public void update(String substatus, double percentDone) {
55 this.substatus = substatus;
56 this.percentDone = percentDone;
57 for (Cloud.CloudTaskListener listener : getListeners()) {
58 listener.taskStatusUpdated(substatus, percentDone);
59 }
60 }
61
62 public void finished() {
63 finished = true;
64 for (Cloud.CloudTaskListener listener : getListeners()) {
65 listener.taskFinished();
66 }
67 clearListeners();
68 }
69
70 public void failed(String message) {
71 finished = true;
72 for (Cloud.CloudTaskListener listener : getListeners()) {
73 listener.taskFailed(message);
74 }
75 clearListeners();
76 }
77
78 /** A listener used only if no other listeners are present. */
79 public void setDefaultListener(Cloud.CloudTaskListener defaultListener) {
80 this.defaultListener = defaultListener;
81 }
82
83 private List<Cloud.CloudTaskListener> getListeners() {
84 List<Cloud.CloudTaskListener> myListeners = new ArrayList<Cloud.CloudTaskListener>(listeners);
85 if (useDefaultListener && myListeners.isEmpty() && defaultListener != null) {
86 myListeners.add(defaultListener);
87 }
88 return myListeners;
89 }
90
91 /** I think this is a good idea for garbage collection purposes -Keith */
92 private void clearListeners() {
93 listeners.clear();
94 defaultListener = null;
95 }
96
97 public boolean isUsingDefaultListener() {
98 return listeners.isEmpty();
99 }
100 }
0 package edu.umd.cs.findbugs.cloud;
1
2 import java.util.ArrayList;
3 import java.util.List;
4 import java.util.concurrent.CopyOnWriteArrayList;
5
6 public class MutableCloudTask implements Cloud.CloudTask {
7 private final String name;
8
9 private final CopyOnWriteArrayList<Cloud.CloudTaskListener> listeners = new CopyOnWriteArrayList<Cloud.CloudTaskListener>();
10
11 private String substatus = "";
12
13 private double percentDone = 0;
14
15 /** A listener used only if no other listeners are present. */
16 private Cloud.CloudTaskListener defaultListener;
17
18 private boolean finished = false;
19
20 private boolean useDefaultListener = true;
21
22 public MutableCloudTask(String name) {
23 this.name = name;
24 }
25
26 @Override
27 public String getName() {
28 return name;
29 }
30
31 @Override
32 public String getStatusLine() {
33 return substatus;
34 }
35
36 @Override
37 public double getPercentCompleted() {
38 return percentDone;
39 }
40
41 @Override
42 public void addListener(Cloud.CloudTaskListener listener) {
43 listeners.addIfAbsent(listener);
44 }
45
46 @Override
47 public void removeListener(Cloud.CloudTaskListener listener) {
48 listeners.remove(listener);
49 }
50
51 @Override
52 public boolean isFinished() {
53 return finished;
54 }
55
56 @Override
57 public void setUseDefaultListener(boolean enabled) {
58 this.useDefaultListener = enabled;
59 }
60
61 public void update(String substatus, double percentDone) {
62 this.substatus = substatus;
63 this.percentDone = percentDone;
64 for (Cloud.CloudTaskListener listener : getListeners()) {
65 listener.taskStatusUpdated(substatus, percentDone);
66 }
67 }
68
69 public void finished() {
70 finished = true;
71 for (Cloud.CloudTaskListener listener : getListeners()) {
72 listener.taskFinished();
73 }
74 clearListeners();
75 }
76
77 public void failed(String message) {
78 finished = true;
79 for (Cloud.CloudTaskListener listener : getListeners()) {
80 listener.taskFailed(message);
81 }
82 clearListeners();
83 }
84
85 /** A listener used only if no other listeners are present. */
86 public void setDefaultListener(Cloud.CloudTaskListener defaultListener) {
87 this.defaultListener = defaultListener;
88 }
89
90 private List<Cloud.CloudTaskListener> getListeners() {
91 List<Cloud.CloudTaskListener> myListeners = new ArrayList<Cloud.CloudTaskListener>(listeners);
92 if (useDefaultListener && myListeners.isEmpty() && defaultListener != null) {
93 myListeners.add(defaultListener);
94 }
95 return myListeners;
96 }
97
98 /** I think this is a good idea for garbage collection purposes -Keith */
99 private void clearListeners() {
100 listeners.clear();
101 defaultListener = null;
102 }
103
104 public boolean isUsingDefaultListener() {
105 return listeners.isEmpty();
106 }
107 }
00 /*
11 * FindBugs - Find Bugs in Java programs
22 * Copyright (C) 2003-2008 University of Maryland
3 *
3 *
44 * This library is free software; you can redistribute it and/or
55 * modify it under the terms of the GNU Lesser General Public
66 * License as published by the Free Software Foundation; either
77 * version 2.1 of the License, or (at your option) any later version.
8 *
8 *
99 * This library is distributed in the hope that it will be useful,
1010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1111 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1212 * Lesser General Public License for more details.
13 *
13 *
1414 * You should have received a copy of the GNU Lesser General Public
1515 * License along with this library; if not, write to the Free Software
1616 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
00 /*
11 * FindBugs - Find Bugs in Java programs
22 * Copyright (C) 2003-2008 University of Maryland
3 *
3 *
44 * This library is free software; you can redistribute it and/or
55 * modify it under the terms of the GNU Lesser General Public
66 * License as published by the Free Software Foundation; either
77 * version 2.1 of the License, or (at your option) any later version.
8 *
8 *
99 * This library is distributed in the hope that it will be useful,
1010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1111 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1212 * Lesser General Public License for more details.
13 *
13 *
1414 * You should have received a copy of the GNU Lesser General Public
1515 * License along with this library; if not, write to the Free Software
1616 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2525 * @author pugh
2626 */
2727 public class ClearCloudPreferences {
28
28
2929 public static void main(String args[]) throws BackingStoreException {
3030 Preferences prefs = Preferences.userNodeForPackage(WebCloudNameLookup.class);
31
32 System.out.println(prefs.getLong(WebCloudNameLookup.KEY_APPENGINECLOUD_SESSION_ID, 0));
33 WebCloudNameLookup.clearSavedSessionInformation();
34
35 prefs.flush();
36 prefs.clear();
37 prefs.flush();
31
32 System.out.println(prefs.getLong(WebCloudNameLookup.KEY_APPENGINECLOUD_SESSION_ID, 0));
33 WebCloudNameLookup.clearSavedSessionInformation();
34
35 prefs.flush();
36 prefs.clear();
37 prefs.flush();
3838 }
3939
4040 }
2828
2929 String username;
3030
31 @Override
3132 public boolean signIn(CloudPlugin plugin, BugCollection bugCollection) {
3233
3334 try {
3839 }
3940 }
4041
42 @Override
4143 public String getUsername() {
4244 return username;
4345 }
3636 /**
3737 * tries to obtain a user name. May prompt the user and/or perform network
3838 * activity.
39 *
39 *
4040 * @param plugin
4141 * TODO
4242 * @param bugCollection
43 *
43 *
4444 * @return true if successful
4545 */
4646 boolean signIn(CloudPlugin plugin, BugCollection bugCollection) throws IOException;
2626 */
2727 public class NoNameLookup implements NameLookup {
2828
29 @Override
2930 public String getUsername() {
3031 return "no name";
3132 }
3233
34 @Override
3335 public boolean signIn(CloudPlugin plugin, BugCollection bugCollection) {
3436 return true;
3537 }
4646 .showQuestionDialog(
4747 "Name/handle/email for recording your reviews?\n"
4848 + "(sorry, no authentication or confidentiality currently provided)",
49 "Name for recording your reviews", findbugsUser == null ? "" : findbugsUser);
49 "Name for recording your reviews", findbugsUser == null ? "" : findbugsUser);
5050 if (findbugsUser != null) {
5151 prefs.put(USER_NAME, findbugsUser);
5252 username = findbugsUser;
5555 return false;
5656 }
5757
58 @Override
5859 public String getUsername() {
5960 return username;
6061 }
6162
63 @Override
6264 public boolean signIn(CloudPlugin plugin, BugCollection bugCollection) {
6365 this.bugCollection = bugCollection;
6466 return true;
5858 private String username;
5959 private String url;
6060
61 @Override
6162 public boolean signIn(CloudPlugin plugin, BugCollection bugCollection) throws IOException {
6263 loadProperties(plugin);
6364
64 if (softSignin())
65 if (softSignin()) {
6566 return true;
66
67 if (sessionId == null)
67 }
68
69 if (sessionId == null) {
6870 sessionId = loadOrCreateSessionId();
71 }
6972
7073 LOGGER.info("Opening browser for session " + sessionId);
7174 URL u = new URL(url + "/browser-auth/" + sessionId);
8992 public void loadProperties(CloudPlugin plugin) {
9093 PropertyBundle pluginProps = plugin.getProperties();
9194 url = pluginProps.getProperty(APPENGINE_HOST_PROPERTY_NAME);
92 if (url == null)
95 if (url == null) {
9396 throw new IllegalStateException("Host not specified for " + plugin.getId());
97 }
9498 }
9599
96100 /**
100104 * @throws IOException
101105 */
102106 public boolean softSignin() throws IOException {
103 if (url == null)
107 if (url == null) {
104108 throw new IllegalStateException("Null host");
105
109 }
110
106111 checkResolveHost();
107112
108113 if (sessionId != null) {
115120 }
116121 // check the previously used session ID
117122 long id = loadSessionId();
118 if (id == 0)
123 if (id == 0) {
119124 return false;
125 }
120126 boolean authorized = checkAuthorized(getAuthCheckUrl(id));
121127 if (authorized) {
122128 LOGGER.info("Authorized with session ID: " + id);
133139 } catch (MalformedURLException e) {
134140 assert true;
135141 /* this will come out later */
136 }
142 }
137143 }
138144
139145 private URL getAuthCheckUrl(long sessionId) throws MalformedURLException {
167173 return sessionId;
168174 }
169175
176 @Override
170177 public String getUsername() {
171178 return username;
172179 }
178185 // ======================= end of public methods =======================
179186
180187 private static SecureRandom secureRandom = new SecureRandom();
181
188
182189 private long loadOrCreateSessionId() {
183190 long id = loadSessionId();
184191 if (id != 0) {
185192 LOGGER.info("Using saved session ID: " + id);
186193 return id;
187194 }
188 while (id == 0)
195 while (id == 0) {
189196 id = secureRandom.nextLong();
190
191 if (isSavingSessionInfoEnabled())
197 }
198
199 if (isSavingSessionInfoEnabled()) {
192200 saveSessionInformation(id);
201 }
193202
194203 return id;
195204 }
2323
2424 /**
2525 * Configure a specific boolean analysis property.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class AnalysisFeatureSetting {
3030 private @AnalysisFeature
31 final
3132 int property;
3233
33 private boolean enabled;
34 private final boolean enabled;
3435
3536 /**
3637 * Constructor.
37 *
38 *
3839 * @param property
3940 * the analysis property to configure
4041 * @param enabled
4849 /**
4950 * Set the configured value of the analysis property in the given
5051 * AnalysisContext.
51 *
52 *
5253 * @param analysisContext
5354 * the AnalysisContext
5455 */
4545
4646 private static final String SPACES = " ";
4747
48 private List<String> optionList;
49
50 private Set<String> unlistedOptions;
51
52 private Map<Integer, String> optionGroups;
53
54 private Set<String> requiresArgumentSet;
55
56 private Map<String, String> optionDescriptionMap;
57
58 private Map<String, String> optionExtraPartSynopsisMap;
59
60 private Map<String, String> argumentDescriptionMap;
48 private final List<String> optionList;
49
50 private final Set<String> unlistedOptions;
51
52 private final Map<Integer, String> optionGroups;
53
54 private final Set<String> requiresArgumentSet;
55
56 private final Map<String, String> optionDescriptionMap;
57
58 private final Map<String, String> optionExtraPartSynopsisMap;
59
60 private final Map<String, String> argumentDescriptionMap;
6161
6262 int maxWidth;
6363
9595 optionList.add(option);
9696 optionDescriptionMap.put(option, description);
9797
98 if (option.length() > maxWidth)
98 if (option.length() > maxWidth) {
9999 maxWidth = option.length();
100 }
100101 }
101102
102103 /**
117118
118119 // Option will display as -foo[:extraPartSynopsis]
119120 int length = option.length() + optionExtraPartSynopsis.length() + 3;
120 if (length > maxWidth)
121 if (length > maxWidth) {
121122 maxWidth = length;
123 }
122124 }
123125
124126 /**
138140 argumentDescriptionMap.put(option, argumentDesc);
139141
140142 int width = option.length() + 3 + argumentDesc.length();
141 if (width > maxWidth)
143 if (width > maxWidth) {
142144 maxWidth = width;
145 }
143146 }
144147
145148 /**
166169 */
167170
168171 public String[] expandOptionFiles(String[] argv, boolean ignoreComments, boolean ignoreBlankLines) throws IOException,
169 HelpRequestedException {
172 HelpRequestedException {
170173 // Add all expanded options at the end of the options list, before the
171174 // list of
172175 // jar/zip/class files and directories.
222225 while ((line = reader.readLine()) != null) {
223226 line = line.trim();
224227
225 if (ignoreComments && line.startsWith("#"))
228 if (ignoreComments && line.startsWith("#")) {
226229 continue;
227
228 if (ignoreBlankLines && line.equals(""))
230 }
231
232 if (ignoreBlankLines && "".equals(line)) {
229233 continue;
230 if (line.length() >= 2 && line.charAt(0) == '"' && line.charAt(line.length() - 1) == '"')
234 }
235 if (line.length() >= 2 && line.charAt(0) == '"' && line.charAt(line.length() - 1) == '"') {
231236 resultList.add(line.substring(0, line.length() - 1));
232 else
233 for (String segment : line.split(" "))
237 } else {
238 for (String segment : line.split(" ")) {
234239 resultList.add(segment);
240 }
241 }
235242 }
236243 }
237244
304311
305312 while (arg < argv.length) {
306313 String option = argv[arg];
307 if (option.equals("-help") || option.equals("-h"))
314 if ("-help".equals(option) || "-h".equals(option)) {
308315 throw new HelpRequestedException();
309 if (!option.startsWith("-"))
316 }
317 if (!option.startsWith("-")) {
310318 break;
319 }
311320
312321 String optionExtraPart = "";
313322 int colon = option.indexOf(':');
316325 option = option.substring(0, colon);
317326 }
318327
319 if (optionDescriptionMap.get(option) == null)
328 if (optionDescriptionMap.get(option) == null) {
320329 throw new IllegalArgumentException("Unknown option: " + option);
330 }
321331
322332 if (requiresArgumentSet.contains(option)) {
323333 ++arg;
324 if (arg >= argv.length)
334 if (arg >= argv.length) {
325335 throw new IllegalArgumentException("Option " + option + " requires an argument");
336 }
326337 String argument = argv[arg];
327 if (!dryRun)
338 if (!dryRun) {
328339 handleOptionWithArgument(option, argument);
340 }
329341 ++arg;
330342 } else {
331 if (!dryRun)
343 if (!dryRun) {
332344 handleOption(option, optionExtraPart);
345 }
333346 ++arg;
334347 }
335348 }
375388 }
376389 count++;
377390
378 if (unlistedOptions.contains(option))
391 if (unlistedOptions.contains(option)) {
379392 continue;
393 }
380394 out.print(" ");
381395
382396 StringBuilder buf = new StringBuilder();
400414 }
401415
402416 private static void printField(PrintStream out, String s, int width) {
403 if (s.length() > width)
417 if (s.length() > width) {
404418 throw new IllegalArgumentException();
419 }
405420 int nSpaces = width - s.length();
406421 out.print(s);
407422 while (nSpaces > 0) {
7373 * The character used for delimiting whole fields in filter settings encoded
7474 * as strings
7575 */
76 private static String FIELD_DELIMITER = "|";
76 private static final char FIELD_DELIMITER = '|';
7777
7878 /**
7979 * The character used for delimiting list items in filter settings encoded
8080 * as strings
8181 */
82 private static String LISTITEM_DELIMITER = ",";
83
84 private static int DEFAULT_MIN_RANK = 15;
82 private static final String LISTITEM_DELIMITER = ",";
83
84 private static final int DEFAULT_MIN_RANK = 15;
8585
8686 // Fields
8787 private Set<String> activeBugCategorySet; // not used for much:
88 // hiddenBugCategorySet has
89 // priority.
88 // hiddenBugCategorySet has
89 // priority.
9090
9191 private Set<String> hiddenBugCategorySet;
9292
204204 String minRankStr;
205205 if (bar >= 0) {
206206 minRankStr = s.substring(0, bar);
207 s = s.substring(bar + 1);
207 // s = s.substring(bar + 1);
208208 } else {
209209 minRankStr = s;
210 s = "";
210 // s = "";
211211 }
212212 result.setMinRank(Integer.parseInt(minRankStr));
213213 }
214214
215 if (s.length() > 0) {
216 // Can add other fields here...
217 assert true;
218 }
215 // if (s.length() > 0) {
216 // // Can add other fields here...
217 // assert true;
218 // }
219219
220220 return result;
221221
276276 // HACK: it is conceivable that the detector plugin which generated
277277 // this warning is not available any more, in which case we can't
278278 // find out the category. Let the warning be visible in this case.
279 if (bugPattern != null && !containsCategory(bugPattern.getCategory())) {
279 if (!containsCategory(bugPattern.getCategory())) {
280280 return false;
281281 }
282282
560560 }
561561 }
562562
563 // vim:ts=4
1212 public final class SortedProperties extends Properties {
1313 /**
1414 * Overriden to be able to write properties sorted by keys to the disk
15 *
15 *
1616 * @see java.util.Hashtable#keys()
1717 */
1818 @SuppressWarnings("unchecked")
1919 @Override
2020 public synchronized Enumeration<Object> keys() {
2121 // sort elements based on detector (prop key) names
22 Set set = keySet();
23 return (Enumeration<Object>) sortKeys(set);
22 Set<?> set = keySet();
23 return (Enumeration<Object>) sortKeys((Set<String>) set);
2424 }
2525
2626 /**
2828 * before storing them to disk. Otherwise each change may lead to problems
2929 * by diff against previous version - because Property entries are randomly
3030 * distributed (it's a map).
31 *
31 *
3232 * @param keySet
3333 * non null set instance to sort
3434 * @return non null list wich contains all given keys, sorted
6262 */
6363 public class UserPreferences implements Cloneable {
6464
65 // Public constants
66
6765 /**
6866 * Separator string for values composed from a string and boolean
6967 */
70 private static final String BOOL_SEPARATOR = "|";
68 private static final char BOOL_SEPARATOR = '|';
7169
7270 public static final String EFFORT_MIN = "min";
7371
9896
9997 private static final String EFFORT_KEY = "effort";
10098
101 private static final String KEY_INCLUDE_FILTER = "includefilter";
102
103 private static final String KEY_EXCLUDE_FILTER = "excludefilter";
104
105 private static final String KEY_EXCLUDE_BUGS = "excludebugs";
99 /**
100 * Key prefix for custom filters, full key consists of a prefix + filter index starting with 0
101 */
102 public static final String KEY_INCLUDE_FILTER = "includefilter";
103
104 /**
105 * Key prefix for custom filters, full key consists of a prefix + filter index starting with 0
106 */
107 public static final String KEY_EXCLUDE_FILTER = "excludefilter";
108
109 /**
110 * Key prefix for custom filters, full key consists of a prefix + filter index starting with 0
111 */
112 public static final String KEY_EXCLUDE_BUGS = "excludebugs";
106113
107114 // Fields
108115
294301 props.put(DETECTOR_THRESHOLD_KEY, String.valueOf(filterSettings.getMinPriorityAsInt()));
295302 props.put(RUN_AT_FULL_BUILD, String.valueOf(runAtFullBuild));
296303 props.setProperty(EFFORT_KEY, effort);
297 if (cloudId != null)
304 if (cloudId != null) {
298305 props.setProperty(CLOUD_ID_KEY, cloudId);
306 }
299307 writeProperties(props, KEY_INCLUDE_FILTER, includeFilterFiles);
300308 writeProperties(props, KEY_EXCLUDE_FILTER, excludeFilterFiles);
301309 writeProperties(props, KEY_EXCLUDE_BUGS, excludeBugsFiles);
720728 * setting.
721729 */
722730 public AnalysisFeatureSetting[] getAnalysisFeatureSettings() {
723 if (effort.equals(EFFORT_DEFAULT)) {
731 if (EFFORT_DEFAULT.equals(effort)) {
724732 return FindBugs.DEFAULT_EFFORT;
725 } else if (effort.equals(EFFORT_MIN)) {
733 } else if (EFFORT_MIN.equals(effort)) {
726734 return FindBugs.MIN_EFFORT;
727735 }
728736 return FindBugs.MAX_EFFORT;
3636 * class.
3737 */
3838 public class AnyMethodReturnValueStreamFactory implements StreamFactory {
39 private ObjectType baseClassType;
39 private final ObjectType baseClassType;
4040
4141 private String bugType;
4242
5050 return this;
5151 }
5252
53 @Override
5354 public Stream createStream(Location location, ObjectType type, ConstantPoolGen cpg,
5455 RepositoryLookupFailureCallback lookupFailureCallback) {
5556
5758
5859 try {
5960 if (ins instanceof InvokeInstruction) {
60 if (!Hierarchy.isSubtype(type, baseClassType))
61 if (!Hierarchy.isSubtype(type, baseClassType)) {
6162 return null;
63 }
6264
6365 Stream stream = new Stream(location, type.getClassName(), baseClassType.getClassName()).setIsOpenOnCreation(true)
6466 .setIgnoreImplicitExceptions(true);
65 if (bugType != null)
67 if (bugType != null) {
6668 stream.setInteresting(bugType);
69 }
6770
6871 return stream;
6972 }
7578 }
7679 }
7780
78 // vim:ts=4
1818
1919 package edu.umd.cs.findbugs.detect;
2020
21 import java.util.Collections;
22
2123 import org.apache.bcel.classfile.Method;
2224
2325 import edu.umd.cs.findbugs.BugInstance;
2426 import edu.umd.cs.findbugs.BugReporter;
2527 import edu.umd.cs.findbugs.OpcodeStack;
2628 import edu.umd.cs.findbugs.Priorities;
29 import edu.umd.cs.findbugs.ba.ClassContext;
2730 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
2831
2932 public class AppendingToAnObjectOutputStream extends OpcodeStackDetector {
3235
3336 public AppendingToAnObjectOutputStream(BugReporter bugReporter) {
3437 this.bugReporter = bugReporter;
38 }
39
40 @Override
41 public void visitClassContext(ClassContext classContext) {
42 if(hasInterestingClass(classContext.getJavaClass().getConstantPool(), Collections.singleton("java/io/ObjectOutputStream"))) {
43 super.visitClassContext(classContext);
44 }
3545 }
3646
3747 boolean sawOpenInAppendMode;
4353
4454 /*
4555 * (non-Javadoc)
46 *
56 *
4757 * @see edu.umd.cs.findbugs.bcel.OpcodeStackDetector#sawOpcode(int)
4858 */
4959 @Override
5666 String calledMethodName = getNameConstantOperand();
5767 String calledMethodSig = getSigConstantOperand();
5868 if (!sawOpenInAppendMode) {
59 if (calledClassName.equals("java/io/ObjectOutputStream") && calledMethodName.equals("<init>")
60 && calledMethodSig.equals("(Ljava/io/OutputStream;)V")
61 && stack.getStackItem(0).getSpecialKind() == OpcodeStack.Item.FILE_OPENED_IN_APPEND_MODE)
69 if ("java/io/ObjectOutputStream".equals(calledClassName) && "<init>".equals(calledMethodName)
70 && "(Ljava/io/OutputStream;)V".equals(calledMethodSig)
71 && stack.getStackItem(0).getSpecialKind() == OpcodeStack.Item.FILE_OPENED_IN_APPEND_MODE) {
6272 bugReporter.reportBug(new BugInstance(this, "IO_APPENDING_TO_OBJECT_OUTPUT_STREAM", Priorities.HIGH_PRIORITY)
63 .addClassAndMethod(this).addSourceLine(this));
73 .addClassAndMethod(this).addSourceLine(this));
74 }
6475 return;
6576 }
66 if (calledClassName.equals("java/io/FileOutputStream") && calledMethodName.equals("<init>")
67 && (calledMethodSig.equals("(Ljava/io/File;Z)V") || calledMethodSig.equals("(Ljava/lang/String;Z)V"))) {
77 if ("java/io/FileOutputStream".equals(calledClassName) && "<init>".equals(calledMethodName)
78 && ("(Ljava/io/File;Z)V".equals(calledMethodSig) || "(Ljava/lang/String;Z)V".equals(calledMethodSig))) {
6879 OpcodeStack.Item item = stack.getStackItem(0);
6980 Object value = item.getConstant();
7081 sawOpenInAppendMode = value instanceof Integer && ((Integer) value).intValue() == 1;
7182 } else if (!sawOpenInAppendMode) {
7283 return;
73 } else if (calledClassName.equals("java/io/BufferedOutputStream") && calledMethodName.equals("<init>")
74 && calledMethodSig.equals("(Ljava/io/OutputStream;)V")) {
84 } else if ("java/io/BufferedOutputStream".equals(calledClassName) && "<init>".equals(calledMethodName)
85 && "(Ljava/io/OutputStream;)V".equals(calledMethodSig)) {
7586 // do nothing
7687
77 } else if (calledClassName.equals("java/io/ObjectOutputStream") && calledMethodName.equals("<init>")
78 && calledMethodSig.equals("(Ljava/io/OutputStream;)V")) {
88 } else if ("java/io/ObjectOutputStream".equals(calledClassName) && "<init>".equals(calledMethodName)
89 && "(Ljava/io/OutputStream;)V".equals(calledMethodSig)) {
7990 bugReporter.reportBug(new BugInstance(this, "IO_APPENDING_TO_OBJECT_OUTPUT_STREAM", Priorities.HIGH_PRIORITY)
80 .addClassAndMethod(this).addSourceLine(this));
91 .addClassAndMethod(this).addSourceLine(this));
8192 sawOpenInAppendMode = false;
82 } else
93 } else {
8394 sawOpenInAppendMode = false;
95 }
8496
8597 }
8698
1818
1919 package edu.umd.cs.findbugs.detect;
2020
21 import java.util.Collections;
22
2123 import org.apache.bcel.classfile.Code;
2224
2325 import edu.umd.cs.findbugs.BugInstance;
2426 import edu.umd.cs.findbugs.BugReporter;
2527 import edu.umd.cs.findbugs.OpcodeStack;
28 import edu.umd.cs.findbugs.ba.ClassContext;
2629 import edu.umd.cs.findbugs.ba.XClass;
2730 import edu.umd.cs.findbugs.ba.XMethod;
2831 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
3134 * if we get from a ConcurrentHashMap and assign to a variable... and don't do
3235 * anything else and perform a null check on it... and then do a set on it...
3336 * (or anything else inside the if that modifies it?) then we have a bug.
34 *
37 *
3538 * @author Michael Midgley-Biggs
3639 */
3740 public class AtomicityProblem extends OpcodeStackDetector {
4043
4144 int lastQuestionableCheckTarget = -1;
4245
43 private BugReporter bugReporter;
46 private final BugReporter bugReporter;
4447
4548 final static boolean DEBUG = false;
4649
4750 public AtomicityProblem(BugReporter bugReporter) {
4851 this.bugReporter = bugReporter;
52 }
53
54 @Override
55 public void visitClassContext(ClassContext classContext) {
56 if(hasInterestingClass(classContext.getJavaClass().getConstantPool(), Collections.singleton("java/util/concurrent/ConcurrentHashMap"))) {
57 super.visitClassContext(classContext);
58 }
4959 }
5060
5161 @Override
6070 /**
6171 * This is the "dumb" version of the detector. It may generate false
6272 * positives, and/or not detect all instances of the bug.
63 *
73 *
6474 * @see edu.umd.cs.findbugs.visitclass.DismantleBytecode#sawOpcode(int)
6575 */
6676 @Override
7686 System.out.println("Stack top: " + top);
7787 }
7888 XMethod m = top.getReturnValueOf();
79 if (m != null && m.getClassName().equals("java.util.concurrent.ConcurrentHashMap")
80 && m.getName().equals("containsKey")) {
89 if (m != null && "java.util.concurrent.ConcurrentHashMap".equals(m.getClassName())
90 && "containsKey".equals(m.getName())) {
8191 lastQuestionableCheckTarget = getBranchTarget();
8292 if (seen == IFEQ) {
8393 priority = LOW_PRIORITY;
97107 if (DEBUG) {
98108 System.out.println("Found null check");
99109 }
100 if (m != null && m.getClassName().equals("java.util.concurrent.ConcurrentHashMap") && m.getName().equals("get")) {
110 if (m != null && "java.util.concurrent.ConcurrentHashMap".equals(m.getClassName()) && "get".equals(m.getName())) {
101111 lastQuestionableCheckTarget = getBranchTarget();
102112 if (seen == IFNULL) {
103113 priority = LOW_PRIORITY;
109119 }
110120 case INVOKEVIRTUAL:
111121 case INVOKEINTERFACE: {
112 if (getDottedClassConstantOperand().equals("java.util.concurrent.ConcurrentHashMap")) {
122 if ("java.util.concurrent.ConcurrentHashMap".equals(getDottedClassConstantOperand())) {
113123 String methodName = getNameConstantOperand();
114124 XClass xClass = getXClassOperand();
115 if (xClass != null && methodName.equals("put")) {
125 if (xClass != null && "put".equals(methodName)) {
116126 if ((getPC() < lastQuestionableCheckTarget) && (lastQuestionableCheckTarget != -1)) {
117127 bugReporter.reportBug(new BugInstance(this, "AT_OPERATION_SEQUENCE_ON_CONCURRENT_ABSTRACTION", priority)
118 .addClassAndMethod(this).addType(xClass.getClassDescriptor()).addCalledMethod(this)
119 .addSourceLine(this));
128 .addClassAndMethod(this).addType(xClass.getClassDescriptor()).addCalledMethod(this)
129 .addSourceLine(this));
120130 }
121131 }
122132 }
123133 break;
124134 }
135 default:
136 break;
125137 }
126138 }
127139 }
3030 import edu.umd.cs.findbugs.ba.ClassContext;
3131
3232 public class BadAppletConstructor extends BytecodeScanningDetector {
33 private BugReporter bugReporter;
33 private final BugReporter bugReporter;
3434
3535 private final JavaClass appletClass;
3636
4949
5050 @Override
5151 public void visitClassContext(ClassContext classContext) {
52 if (appletClass == null)
52 if (appletClass == null) {
5353 return;
54 }
5455
5556 JavaClass cls = classContext.getJavaClass();
5657 try {
57 if (cls.instanceOf(appletClass))
58 if (cls.instanceOf(appletClass)) {
5859 cls.accept(this);
60 }
5961 } catch (ClassNotFoundException cnfe) {
6062 bugReporter.reportMissingClass(cnfe);
6163 }
6365
6466 @Override
6567 public void visit(Method obj) {
66 inConstructor = obj.getName().equals("<init>");
68 inConstructor = "<init>".equals(obj.getName());
6769 }
6870
6971 @Override
7072 public void visit(Code obj) {
71 if (inConstructor)
73 if (inConstructor) {
7274 super.visit(obj);
75 }
7376 }
7477
7578 @Override
7780 if (seen == INVOKEVIRTUAL) {
7881 String method = getNameConstantOperand();
7982 String signature = getSigConstantOperand();
80 if (((method.equals("getDocumentBase") || method.equals("getCodeBase")) && signature.equals("()Ljava/net/URL;"))
81 || (method.equals("getAppletContext") && signature.equals("()Ljava/applet/AppletContext;"))
82 || (method.equals("getParameter") && signature.equals("(Ljava/lang/String;)Ljava/lang/String;")))
83 if ((("getDocumentBase".equals(method) || "getCodeBase".equals(method)) && "()Ljava/net/URL;".equals(signature))
84 || ("getAppletContext".equals(method) && "()Ljava/applet/AppletContext;".equals(signature))
85 || ("getParameter".equals(method) && "(Ljava/lang/String;)Ljava/lang/String;".equals(signature))) {
8386 bugReporter.reportBug(new BugInstance(this, "BAC_BAD_APPLET_CONSTRUCTOR", NORMAL_PRIORITY)
84 .addClassAndMethod(this).addSourceLine(this));
87 .addClassAndMethod(this).addSourceLine(this));
88 }
8589 }
8690 }
8791 }
8892
89 // vim:ts=4
1919
2020 package edu.umd.cs.findbugs.detect;
2121
22 import java.util.Collections;
2223 import java.util.HashSet;
2324 import java.util.Set;
2425
2526 import edu.umd.cs.findbugs.BugInstance;
2627 import edu.umd.cs.findbugs.BugReporter;
2728 import edu.umd.cs.findbugs.OpcodeStack;
29 import edu.umd.cs.findbugs.ba.ClassContext;
2830 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
2931 import edu.umd.cs.findbugs.internalAnnotations.StaticConstant;
3032 import edu.umd.cs.findbugs.visitclass.PreorderVisitor;
6971 }
7072
7173 @Override
74 public void visitClassContext(ClassContext classContext) {
75 if(hasInterestingClass(classContext.getJavaClass().getConstantPool(), Collections.singleton("java/sql/ResultSet"))) {
76 super.visitClassContext(classContext);
77 }
78 }
79
80 @Override
7281 public void sawOpcode(int seen) {
7382
7483 if (seen == INVOKEINTERFACE) {
7584 String methodName = getNameConstantOperand();
7685 String clsConstant = getClassConstantOperand();
77 if ((clsConstant.equals("java/sql/ResultSet") && ((methodName.startsWith("get") && dbFieldTypesSet
86 if (("java/sql/ResultSet".equals(clsConstant) && ((methodName.startsWith("get") && dbFieldTypesSet
7887 .contains(methodName.substring(3))) || (methodName.startsWith("update") && dbFieldTypesSet
79 .contains(methodName.substring(6)))))
80 || ((clsConstant.equals("java/sql/PreparedStatement") && ((methodName.startsWith("set") && dbFieldTypesSet
81 .contains(methodName.substring(3))))))) {
88 .contains(methodName.substring(6)))))
89 || (("java/sql/PreparedStatement".equals(clsConstant) && ((methodName.startsWith("set") && dbFieldTypesSet
90 .contains(methodName.substring(3))))))) {
8291 String signature = getSigConstantOperand();
8392 int numParms = PreorderVisitor.getNumberArguments(signature);
8493 if (stack.getStackDepth() >= numParms) {
8695
8796 if ("I".equals(item.getSignature()) && item.couldBeZero()) {
8897 bugReporter.reportBug(new BugInstance(this,
89 clsConstant.equals("java/sql/PreparedStatement") ? "SQL_BAD_PREPARED_STATEMENT_ACCESS"
98 "java/sql/PreparedStatement".equals(clsConstant) ? "SQL_BAD_PREPARED_STATEMENT_ACCESS"
9099 : "SQL_BAD_RESULTSET_ACCESS", item.mustBeZero() ? HIGH_PRIORITY : NORMAL_PRIORITY)
91 .addClassAndMethod(this).addSourceLine(this));
100 .addClassAndMethod(this).addSourceLine(this));
92101 }
93102 }
94103 }
97106 }
98107 }
99108
100 // vim:ts=4
3737 }
3838
3939 private void singleDotPatternWouldBeSilly(int stackDepth, boolean ignorePasswordMasking) {
40 if (ignorePasswordMasking && stackDepth != 1)
40 if (ignorePasswordMasking && stackDepth != 1) {
4141 throw new IllegalArgumentException("Password masking requires stack depth 1, but is " + stackDepth);
42 if (stack.getStackDepth() < stackDepth)
42 }
43 if (stack.getStackDepth() < stackDepth) {
4344 return;
45 }
4446 OpcodeStack.Item it = stack.getStackItem(stackDepth);
4547 Object value = it.getConstant();
46 if (value == null || !(value instanceof String))
48 if (value == null || !(value instanceof String)) {
4749 return;
50 }
4851 String regex = (String) value;
49 boolean dotIsUsed = regex.equals(".");
50 if (!dotIsUsed && !regex.equals("|"))
52 boolean dotIsUsed = ".".equals(regex);
53 if (!dotIsUsed && !"|".equals(regex)) {
5154 return;
55 }
5256 int priority = HIGH_PRIORITY;
5357 if (ignorePasswordMasking && dotIsUsed) {
5458 priority = NORMAL_PRIORITY;
5660 Object topValue = top.getConstant();
5761 if (topValue instanceof String) {
5862 String replacementString = (String) topValue;
59 if (replacementString.toLowerCase().equals("x") || replacementString.equals("-") || replacementString.equals("*")
60 || replacementString.equals(" ") || replacementString.equals("\\*"))
63 if ("x".equals(replacementString.toLowerCase()) || "-".equals(replacementString) || "*".equals(replacementString)
64 || " ".equals(replacementString) || "\\*".equals(replacementString)) {
6165 return;
62 if (replacementString.length() == 1 && getMethodName().toLowerCase().indexOf("pass") >= 0)
66 }
67 if (replacementString.length() == 1 && getMethodName().toLowerCase().indexOf("pass") >= 0) {
6368 priority = LOW_PRIORITY;
69 }
6470 }
6571 }
6672
7379 }
7480
7581 private void sawRegExPattern(int stackDepth, int flags) {
76 if (stack.getStackDepth() < stackDepth)
82 if (stack.getStackDepth() < stackDepth) {
7783 return;
84 }
7885 OpcodeStack.Item it = stack.getStackItem(stackDepth);
7986 if (it.getSpecialKind() == OpcodeStack.Item.FILE_SEPARATOR_STRING && (flags & Pattern.LITERAL) == 0) {
8087 bugReporter.reportBug(new BugInstance(this, "RE_CANT_USE_FILE_SEPARATOR_AS_REGULAR_EXPRESSION", HIGH_PRIORITY)
81 .addClassAndMethod(this).addCalledMethod(this).addSourceLine(this));
88 .addClassAndMethod(this).addCalledMethod(this).addSourceLine(this));
8289 return;
8390 }
8491 Object value = it.getConstant();
85 if (value == null || !(value instanceof String))
92 if (value == null || !(value instanceof String)) {
8693 return;
94 }
8795 String regex = (String) value;
8896 try {
8997 Pattern.compile(regex, flags);
9098 } catch (PatternSyntaxException e) {
9199 String message = e.getMessage();
92100 int eol = message.indexOf('\n');
93 if (eol > 0)
101 if (eol > 0) {
94102 message = message.substring(0, eol);
103 }
95104 BugInstance bug = new BugInstance(this, "RE_BAD_SYNTAX_FOR_REGULAR_EXPRESSION", HIGH_PRIORITY)
96 .addClassAndMethod(this).addCalledMethod(this).addString(message).describe(StringAnnotation.ERROR_MSG_ROLE)
97 .addString(regex).describe(StringAnnotation.REGEX_ROLE);
105 .addClassAndMethod(this).addCalledMethod(this).addString(message).describe(StringAnnotation.ERROR_MSG_ROLE)
106 .addString(regex).describe(StringAnnotation.REGEX_ROLE);
98107 String options = getOptions(flags);
99 if (options.length() > 0)
108 if (options.length() > 0) {
100109 bug.addString("Regex flags: " + options).describe(StringAnnotation.STRING_MESSAGE);
110 }
101111 bug.addSourceLine(this);
102112 bugReporter.reportBug(bug);
103113 }
105115
106116 /** return an int on the stack, or 'defaultValue' if can't determine */
107117 private int getIntValue(int stackDepth, int defaultValue) {
108 if (stack.getStackDepth() < stackDepth)
118 if (stack.getStackDepth() < stackDepth) {
109119 return defaultValue;
120 }
110121 OpcodeStack.Item it = stack.getStackItem(stackDepth);
111122 Object value = it.getConstant();
112 if (value == null || !(value instanceof Integer))
123 if (value == null || !(value instanceof Integer)) {
113124 return defaultValue;
125 }
114126 return ((Number) value).intValue();
115127 }
116128
117129 @Override
118130 public void sawOpcode(int seen) {
119 if (seen == INVOKESTATIC && getClassConstantOperand().equals("java/util/regex/Pattern")
120 && getNameConstantOperand().equals("compile") && getSigConstantOperand().startsWith("(Ljava/lang/String;I)"))
131 if (seen == INVOKESTATIC && "java/util/regex/Pattern".equals(getClassConstantOperand())
132 && "compile".equals(getNameConstantOperand()) && getSigConstantOperand().startsWith("(Ljava/lang/String;I)")) {
121133 sawRegExPattern(1, getIntValue(0, 0));
122 else if (seen == INVOKESTATIC && getClassConstantOperand().equals("java/util/regex/Pattern")
123 && getNameConstantOperand().equals("compile") && getSigConstantOperand().startsWith("(Ljava/lang/String;)"))
134 } else if (seen == INVOKESTATIC && "java/util/regex/Pattern".equals(getClassConstantOperand())
135 && "compile".equals(getNameConstantOperand()) && getSigConstantOperand().startsWith("(Ljava/lang/String;)")) {
124136 sawRegExPattern(0);
125 else if (seen == INVOKESTATIC && getClassConstantOperand().equals("java/util/regex/Pattern")
126 && getNameConstantOperand().equals("matches"))
137 } else if (seen == INVOKESTATIC && "java/util/regex/Pattern".equals(getClassConstantOperand())
138 && "matches".equals(getNameConstantOperand())) {
127139 sawRegExPattern(1);
128 else if (seen == INVOKEVIRTUAL && getClassConstantOperand().equals("java/lang/String")
129 && getNameConstantOperand().equals("replaceAll")) {
140 } else if (seen == INVOKEVIRTUAL && "java/lang/String".equals(getClassConstantOperand())
141 && "replaceAll".equals(getNameConstantOperand())) {
130142 sawRegExPattern(1);
131143 singleDotPatternWouldBeSilly(1, true);
132 } else if (seen == INVOKEVIRTUAL && getClassConstantOperand().equals("java/lang/String")
133 && getNameConstantOperand().equals("replaceFirst")) {
144 } else if (seen == INVOKEVIRTUAL && "java/lang/String".equals(getClassConstantOperand())
145 && "replaceFirst".equals(getNameConstantOperand())) {
134146 sawRegExPattern(1);
135147 singleDotPatternWouldBeSilly(1, false);
136 } else if (seen == INVOKEVIRTUAL && getClassConstantOperand().equals("java/lang/String")
137 && getNameConstantOperand().equals("matches")) {
148 } else if (seen == INVOKEVIRTUAL && "java/lang/String".equals(getClassConstantOperand())
149 && "matches".equals(getNameConstantOperand())) {
138150 sawRegExPattern(0);
139151 singleDotPatternWouldBeSilly(0, false);
140 } else if (seen == INVOKEVIRTUAL && getClassConstantOperand().equals("java/lang/String")
141 && getNameConstantOperand().equals("split")) {
152 } else if (seen == INVOKEVIRTUAL && "java/lang/String".equals(getClassConstantOperand())
153 && "split".equals(getNameConstantOperand())) {
142154 sawRegExPattern(0);
143155 singleDotPatternWouldBeSilly(0, false);
144156 }
146158 }
147159
148160 static void appendOption(StringBuilder b, int flags, int mask, String name) {
149 if ((flags & mask) == 0)
161 if ((flags & mask) == 0) {
150162 return;
151 if (b.length() > 0)
163 }
164 if (b.length() > 0) {
152165 b.append(" | ");
166 }
153167 b.append("Pattern." + name);
154168
155169 }
4747
4848 @Override
4949 public void sawOpcode(int seen) {
50 if (seen == INVOKEVIRTUAL && getNameConstantOperand().equals("indexOf")
51 && getClassConstantOperand().equals("java/lang/String")
52 && getSigConstantOperand().equals("(Ljava/lang/String;)I"))
50 if (seen == INVOKEVIRTUAL && "indexOf".equals(getNameConstantOperand())
51 && "java/lang/String".equals(getClassConstantOperand())
52 && "(Ljava/lang/String;)I".equals(getSigConstantOperand())) {
5353 stringIndexOfOnTOS = true;
54 else if (stringIndexOfOnTOS) {
55 if (seen == IFLE || seen == IFGT)
54 } else if (stringIndexOfOnTOS) {
55 if (seen == IFLE || seen == IFGT) {
5656 bugAccumulator.accumulateBug(
5757 new BugInstance(this, "RV_CHECK_FOR_POSITIVE_INDEXOF", LOW_PRIORITY).addClassAndMethod(this), this);
58 }
5859 stringIndexOfOnTOS = false;
5960 }
6061
61 if (seen == INVOKEVIRTUAL && getNameConstantOperand().equals("readLine")
62 && getSigConstantOperand().equals("()Ljava/lang/String;") && getClassConstantOperand().startsWith("java/io")
63 && !getClassConstantOperand().equals("java/io/LineNumberReader"))
62 if (seen == INVOKEVIRTUAL && "readLine".equals(getNameConstantOperand())
63 && "()Ljava/lang/String;".equals(getSigConstantOperand()) && getClassConstantOperand().startsWith("java/io")
64 && !"java/io/LineNumberReader".equals(getClassConstantOperand())) {
6465 readLineOnTOS = true;
65 else if (readLineOnTOS) {
66 if (seen == IFNULL || seen == IFNONNULL)
66 } else if (readLineOnTOS) {
67 if (seen == IFNULL || seen == IFNONNULL) {
6768 bugAccumulator.accumulateBug(
6869 new BugInstance(this, "RV_DONT_JUST_NULL_CHECK_READLINE", NORMAL_PRIORITY).addClassAndMethod(this), this);
70 }
6971
7072 readLineOnTOS = false;
7173 }
3030 import edu.umd.cs.findbugs.BytecodeScanningDetector;
3131
3232 public class BadlyOverriddenAdapter extends BytecodeScanningDetector {
33 private BugReporter bugReporter;
33 private final BugReporter bugReporter;
3434
3535 private boolean isAdapter;
3636
37 private Map<String, String> methodMap;
37 private final Map<String, String> methodMap;
3838
39 private Map<String, BugInstance> badOverrideMap;
39 private final Map<String, BugInstance> badOverrideMap;
4040
4141 public BadlyOverriddenAdapter(BugReporter bugReporter) {
4242 this.bugReporter = bugReporter;
5050 methodMap.clear();
5151 badOverrideMap.clear();
5252 JavaClass superClass = obj.getSuperClass();
53 if (superClass == null)
53 if (superClass == null) {
5454 return;
55 }
5556 String packageName = superClass.getPackageName();
5657 String className = superClass.getClassName();
5758
5859 // A more generic way to add Adapters would be nice here
59 isAdapter = ((className.endsWith("Adapter")) && (packageName.equals("java.awt.event") || packageName
60 .equals("javax.swing.event")))
61 || ((className.equals("DefaultHandler") && (packageName.equals("org.xml.sax.helpers"))));
60 isAdapter = ((className.endsWith("Adapter")) && ("java.awt.event".equals(packageName) || "javax.swing.event".equals(packageName)))
61 || (("DefaultHandler".equals(className) && ("org.xml.sax.helpers".equals(packageName))));
6262 if (isAdapter) {
6363 Method[] methods = superClass.getMethods();
6464 for (Method method1 : methods) {
7373 @Override
7474 public void visitAfter(JavaClass obj) {
7575 for (BugInstance bi : badOverrideMap.values()) {
76 if (bi != null)
76 if (bi != null) {
7777 bugReporter.reportBug(bi);
78 }
7879 }
7980 }
8081
8384 if (isAdapter) {
8485 String methodName = obj.getName();
8586 String signature = methodMap.get(methodName);
86 if (!methodName.equals("<init>") && signature != null) {
87 if (!"<init>".equals(methodName) && signature != null) {
8788 if (!signature.equals(obj.getSignature())) {
8889 if (!badOverrideMap.keySet().contains(methodName)) {
8990 badOverrideMap.put(methodName, new BugInstance(this, "BOA_BADLY_OVERRIDDEN_ADAPTER", NORMAL_PRIORITY)
90 .addClassAndMethod(this).addSourceLine(this));
91 .addClassAndMethod(this).addSourceLine(this));
9192 }
9293 } else {
9394 badOverrideMap.put(methodName, null);
9798 }
9899 }
99100
100 // vim:ts=4
1818
1919 package edu.umd.cs.findbugs.detect;
2020
21 import org.apache.bcel.classfile.Code;
22
23 import edu.umd.cs.findbugs.BugAccumulator;
2421 import edu.umd.cs.findbugs.BugInstance;
2522 import edu.umd.cs.findbugs.BugReporter;
26 import edu.umd.cs.findbugs.ba.AnalysisContext;
27 import edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase;
28 import edu.umd.cs.findbugs.ba.NullnessAnnotation;
29 import edu.umd.cs.findbugs.ba.SignatureParser;
30 import edu.umd.cs.findbugs.ba.XMethod;
31 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
3223
3324 /**
3425 * @author alison
3526 */
36 public class BooleanReturnNull extends OpcodeStackDetector {
37
38 BugAccumulator bugAccumulator;
27 public class BooleanReturnNull extends TypeReturnNull {
3928
4029 public BooleanReturnNull(BugReporter bugReporter) {
41 this.bugAccumulator = new BugAccumulator(bugReporter);
30 super(bugReporter);
4231 }
4332
4433 @Override
45 public void visit(Code code) {
46 String s = getMethodSig();
47 SignatureParser sp = new SignatureParser(s);
48 // Check to see if the method has Boolean return type
49 if (!"Ljava/lang/Boolean;".equals(sp.getReturnTypeSignature()))
50 return;
34 protected boolean matchesReturnSignature(String returnSignature) {
35 return "Ljava/lang/Boolean;".equals(returnSignature);
36 }
5137
52 if (isExplicitlyNullable())
53 return;
54
55 super.visit(code); // make callbacks to sawOpcode for all opcodes
56 bugAccumulator.reportAccumulatedBugs();
57
58 }
59 private boolean isExplicitlyNullable() {
60 AnalysisContext analysisContext = AnalysisContext.currentAnalysisContext();
61 INullnessAnnotationDatabase nullnessAnnotationDatabase = analysisContext.getNullnessAnnotationDatabase();
62 XMethod xMethod = getXMethod();
63 NullnessAnnotation na = nullnessAnnotationDatabase.getResolvedAnnotation(xMethod, true);
64 return na != null && na != NullnessAnnotation.NONNULL;
65 }
66 /*
67 * (non-Javadoc)
68 *
69 * @see edu.umd.cs.findbugs.bcel.OpcodeStackDetector#sawOpcode(int)
70 */
7138 @Override
72 public void sawOpcode(int seen) {
73 if (seen == ARETURN && getPrevOpcode(1) == ACONST_NULL)
74 bugAccumulator.accumulateBug(new BugInstance(this, "NP_BOOLEAN_RETURN_NULL",
75 getMethodName().startsWith("is") ? HIGH_PRIORITY : NORMAL_PRIORITY).addClassAndMethod(this), this);
76
39 protected void accumulateBug() {
40 bugAccumulator.accumulateBug(new BugInstance(this, "NP_BOOLEAN_RETURN_NULL",
41 getMethodName().startsWith("is") ? HIGH_PRIORITY : NORMAL_PRIORITY).addClassAndMethod(this), this);
7742 }
7843
7944 }
5353 defaultKind.put("ForParameters", AnnotationDatabase.Target.PARAMETER);
5454 defaultKind.put("ForMethods", AnnotationDatabase.Target.METHOD);
5555 defaultKind.put("ForFields", AnnotationDatabase.Target.FIELD);
56
5756 }
5857
5958 public BuildCheckReturnAnnotationDatabase() {
6160 }
6261
6362 static String simpleClassName(@DottedClassName String className) {
64 int i = className.lastIndexOf(".");
65 if (i < 0)
63 int i = className.lastIndexOf('.');
64 if (i < 0) {
6665 return className;
66 }
6767 return className.substring(i + 1);
6868 }
6969
7676 annotationClassSimpleName = annotationClassSimpleName.substring(DEFAULT_ANNOTATION_ANNOTATION_CLASS.length());
7777
7878 Target annotationTarget = defaultKind.get(annotationClassSimpleName);
79 if (annotationTarget != Target.METHOD)
79 if (annotationTarget != Target.METHOD) {
8080 return;
81 }
8182
8283 ElementValue v = map.get("value");
8384 if (v instanceof ClassElementValue) {
8485 handleClassElementValue((ClassElementValue) v, map, annotationTarget);
8586 } else if (v instanceof ArrayElementValue) {
8687 for (ElementValue v2 : ((ArrayElementValue) v).getElementValuesArray()) {
87 if (v2 instanceof ClassElementValue)
88 if (v2 instanceof ClassElementValue) {
8889 handleClassElementValue((ClassElementValue) v2, map, annotationTarget);
90 }
8991 }
9092 }
9193
99101 if (v instanceof EnumElementValue) {
100102 EnumElementValue when = (EnumElementValue) v;
101103 String w = simpleClassName(when.getEnumValueString());
102 if (w.equals("NEVER") || w.equals("UNKNOWN"))
104 if ("NEVER".equals(w) || "UNKNOWN".equals(w)) {
103105 n = CheckReturnValueAnnotation.CHECK_RETURN_VALUE_IGNORE;
104 else if (w.equals("MAYBE"))
106 } else if ("MAYBE".equals(w)) {
105107 n = CheckReturnValueAnnotation.CHECK_RETURN_VALUE_MEDIUM_BAD_PRACTICE;
106 else if (w.equals("ALWAYS"))
108 } else if ("ALWAYS".equals(w)) {
107109 n = CheckReturnValueAnnotation.CHECK_RETURN_VALUE_HIGH;
108 else
110 } else {
109111 return;
110 } else
112 }
113 } else {
111114 n = CheckReturnValueAnnotation.CHECK_RETURN_VALUE_MEDIUM;
115 }
112116
113117 } else if (annotationClassName.equals(edu.umd.cs.findbugs.annotations.CheckReturnValue.class.getName())) {
114118 n = CheckReturnValueAnnotation.parse(getAnnotationParameterAsString(map, "priority"));
115 } else if (annotationClassSimpleName.equals("CheckReturnValue")) {
119 } else if ("CheckReturnValue".equals(annotationClassSimpleName)) {
116120 n = CheckReturnValueAnnotation.CHECK_RETURN_VALUE_MEDIUM;
117 } else
121 } else {
118122 return;
119 if (n == null)
123 }
124 if (n == null) {
120125 return;
121 if (visitingMethod())
126 }
127 if (visitingMethod()) {
122128 AnalysisContext.currentAnalysisContext().getCheckReturnAnnotationDatabase()
123 .addDirectAnnotation(XFactory.createXMethod(this), n);
124 else
129 .addDirectAnnotation(XFactory.createXMethod(this), n);
130 } else {
125131 AnalysisContext.currentAnalysisContext().getCheckReturnAnnotationDatabase()
126 .addDefaultAnnotation(Target.METHOD, getDottedClassName(), n);
132 .addDefaultAnnotation(Target.METHOD, getDottedClassName(), n);
133 }
127134
128135 }
129136
130 /**
131 * @param value
132 * @param map
133 * @param annotationTarget
134 */
135137 private void handleClassElementValue(ClassElementValue value, Map<String, ElementValue> map, Target annotationTarget) {
136 if (simpleClassName(value.getClassString()).equals("CheckReturnValue")) {
138 if ("CheckReturnValue".equals(simpleClassName(value.getClassString()))) {
137139 CheckReturnValueAnnotation n = CheckReturnValueAnnotation.parse(getAnnotationParameterAsString(map, "priority"));
138 if (n != null)
140 if (n != null) {
139141 AnalysisContext.currentAnalysisContext().getCheckReturnAnnotationDatabase()
140 .addDefaultAnnotation(annotationTarget, getDottedClassName(), n);
142 .addDefaultAnnotation(annotationTarget, getDottedClassName(), n);
143 }
141144
142145 }
143146 }
3434
3535 /**
3636 * Build the interprocedural call graph.
37 *
37 *
3838 * NOTE: at the present time, this facility is only used to find relevant type
3939 * qualifiers. It could become a more general-purpose facility if there were a
4040 * need.
41 *
41 *
4242 * @author David Hovemeyer
4343 */
4444 public class BuildInterproceduralCallGraph extends BytecodeScanningDetector implements NonReportingDetector {
4949
5050 /**
5151 * Constructor.
52 *
52 *
5353 * @param bugReporter
5454 * the BugReporter to use
5555 */
6060 callGraph = new InterproceduralCallGraph();
6161 }
6262
63 /*
64 * (non-Javadoc)
65 *
66 * @see
67 * edu.umd.cs.findbugs.BytecodeScanningDetector#visitClassContext(edu.umd
68 * .cs.findbugs.ba.ClassContext)
69 */
7063 @Override
7164 public void visitClassContext(ClassContext classContext) {
7265 if (!Analysis.FIND_EFFECTIVE_RELEVANT_QUALIFIERS) {
7568 super.visitClassContext(classContext);
7669 }
7770
78 /*
79 * (non-Javadoc)
80 *
81 * @see
82 * edu.umd.cs.findbugs.visitclass.BetterVisitor#visitMethod(org.apache.bcel
83 * .classfile.Method)
84 */
8571 @Override
8672 public void visitMethod(Method obj) {
8773 currentVertex = findVertex(getXMethod());
8874 super.visitMethod(obj);
8975 }
9076
91 /*
92 * (non-Javadoc)
93 *
94 * @see edu.umd.cs.findbugs.visitclass.DismantleBytecode#sawOpcode(int)
95 */
9677 @Override
9778 public void sawOpcode(int seen) {
9879 switch (seen) {
10485 XMethod calledXMethod = XFactory.createXMethod(called);
10586 InterproceduralCallGraphVertex calledVertex = findVertex(calledXMethod);
10687 callGraph.createEdge(currentVertex, calledVertex);
88 break;
89 default:
90 break;
10791 }
10892 }
10993
11094 /**
11195 * Find the InterproceduralCallGraphVertex for given XMethod.
112 *
96 *
11397 * @param xmethod
11498 * an XMethod
11599 * @return the XMethod's InterproceduralCallGraphVertex
125109 return vertex;
126110 }
127111
128 /*
129 * (non-Javadoc)
130 *
131 * @see edu.umd.cs.findbugs.BytecodeScanningDetector#report()
132 */
133112 @Override
134113 public void report() {
135114 if (!Analysis.FIND_EFFECTIVE_RELEVANT_QUALIFIERS) {
5353 public class BuildNonNullAnnotationDatabase extends AnnotationVisitor {
5454 private static final boolean DEBUG = SystemProperties.getBoolean("fnd.debug.annotation");
5555
56 private static final String DEFAULT_ANNOTATION_ANNOTATION_CLASS = "DefaultAnnotation";
56 // private static final String DEFAULT_ANNOTATION_ANNOTATION_CLASS = "DefaultAnnotation";
5757
5858 @StaticConstant
5959 private static final Map<String, AnnotationDatabase.Target> defaultKind = new HashMap<String, AnnotationDatabase.Target>();
7272 }
7373
7474 static String lastPortion(String className) {
75 int i = className.lastIndexOf(".");
76 if (i < 0)
75 int i = className.lastIndexOf('.');
76 if (i < 0) {
7777 return className;
78 }
7879 return className.substring(i + 1);
7980 }
8081
81 /*
82 * * @param value
83 *
84 * @param map
85 *
86 * @param annotationTarget
87 */
8882 private void handleClassElementValue(ClassElementValue value, Target annotationTarget) {
8983 NullnessAnnotation n = NullnessAnnotation.Parser.parse(value.getClassString());
90 if (n != null)
84 if (n != null) {
9185 database.addDefaultAnnotation(annotationTarget, getDottedClassName(), n);
86 }
9287
9388 }
9489
106101 annotationClass = annotationClass.substring("DefaultAnnotation".length());
107102
108103 Target annotationTarget = defaultKind.get(annotationClass);
109 if (annotationTarget != Target.METHOD)
104 if (annotationTarget != Target.METHOD) {
110105 return;
106 }
111107
112108 ElementValue v = map.get("value");
113109 if (v instanceof ClassElementValue) {
114110 handleClassElementValue((ClassElementValue) v, annotationTarget);
115111 } else if (v instanceof ArrayElementValue) {
116112 for (ElementValue v2 : ((ArrayElementValue) v).getElementValuesArray()) {
117 if (v2 instanceof ClassElementValue)
113 if (v2 instanceof ClassElementValue) {
118114 handleClassElementValue((ClassElementValue) v2, annotationTarget);
115 }
119116 }
120117 }
121118
122119 return;
123120 }
124121
125 } else if (visitingMethod())
122 } else if (visitingMethod()) {
126123 database.addDirectAnnotation(XFactory.createXMethod(this), n);
127 else if (visitingField())
124 } else if (visitingField()) {
128125 database.addDirectAnnotation(XFactory.createXField(this), n);
126 }
129127
130128 }
131129
151149
152150 NullnessAnnotation n = NullnessAnnotation.Parser.parse(annotationClass);
153151 annotationClass = lastPortion(annotationClass);
154 if (n == null)
152 if (n == null) {
155153 return;
154 }
156155
157156 XMethod xmethod = XFactory.createXMethod(this);
158157 if (DEBUG) {
4242
4343 /**
4444 * Build database of methods that return values guaranteed to be nonnull
45 *
45 *
4646 */
4747 public class BuildNonnullReturnDatabase {
4848 public static final boolean VERBOSE_DEBUG = SystemProperties.getBoolean("fnd.debug.nullarg.verbose");
5353 boolean fullAnalysis = AnalysisContext.currentAnalysisContext().getBoolProperty(
5454 FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES);
5555 if (!fullAnalysis && !AnalysisContext.currentAnalysisContext()./*
56 * getSubtypes
57 * ().
58 */isApplicationClass(classContext.getJavaClass()))
56 * getSubtypes
57 * ().
58 */isApplicationClass(classContext.getJavaClass())) {
5959 return;
60 if (VERBOSE_DEBUG)
60 }
61 if (VERBOSE_DEBUG) {
6162 System.out.println("Visiting class " + classContext.getJavaClass().getClassName());
63 }
6264
63 for (Method m : classContext.getMethodsInCallOrder())
65 for (Method m : classContext.getMethodsInCallOrder()) {
6466 considerMethod(classContext, m);
67 }
6568 }
6669
6770 private void considerMethod(ClassContext classContext, Method method) {
6871 if ((method.getReturnType() instanceof ReferenceType) && classContext.getMethodGen(method) != null) {
69 if (VERBOSE_DEBUG)
72 if (VERBOSE_DEBUG) {
7073 System.out.println("Check " + method);
74 }
7175 analyzeMethod(classContext, method);
7276 }
7377 }
8892 InstructionHandle handle = location.getHandle();
8993 Instruction ins = handle.getInstruction();
9094
91 if (!(ins instanceof ARETURN))
95 if (!(ins instanceof ARETURN)) {
9296 continue;
97 }
9398 IsNullValueFrame frame = inv.getFactAtLocation(location);
94 if (!frame.isValid())
99 if (!frame.isValid()) {
95100 continue;
101 }
96102 IsNullValue value = frame.getTopValue();
97103 if (!value.isDefinitelyNotNull()) {
98104 guaranteedNonNull = false;
105111 if (guaranteedNonNull) {
106112 returnsNonNull++;
107113 AnalysisContext.currentAnalysisContext().getReturnValueNullnessPropertyDatabase()
108 .setProperty(xmethod.getMethodDescriptor(), guaranteedNonNull);
109 if (DEBUG)
114 .setProperty(xmethod.getMethodDescriptor(), guaranteedNonNull);
115 if (DEBUG) {
110116 System.out.println("Unconditional deref: " + xmethod + "=" + guaranteedNonNull);
117 }
111118
112119 }
113120
115122 XMethod xmethod = XFactory.createXMethod(classContext.getJavaClass(), method);
116123
117124 AnalysisContext.currentAnalysisContext().getLookupFailureCallback()
118 .logError("Error analyzing " + xmethod + " for unconditional deref training", e);
125 .logError("Error analyzing " + xmethod + " for unconditional deref training", e);
119126 } catch (DataflowAnalysisException e) {
120127 XMethod xmethod = XFactory.createXMethod(classContext.getJavaClass(), method);
121128 AnalysisContext.currentAnalysisContext().getLookupFailureCallback()
122 .logError("Error analyzing " + xmethod + " for unconditional deref training", e);
129 .logError("Error analyzing " + xmethod + " for unconditional deref training", e);
123130 }
124131 }
125132
147147 for (Map.Entry<MethodDescriptor, String> e : db.entrySet()) {
148148 String[] v = e.getValue().split(",");
149149 Obligation obligation = database.getFactory().getObligationByName(v[2]);
150 if (obligation == null)
150 if (obligation == null) {
151151 obligation = database.getFactory().addObligation(v[2]);
152 }
152153 database.addEntry(new MatchMethodEntry(e.getKey(), ObligationPolicyDatabaseActionType.valueOf(v[0]),
153154 ObligationPolicyDatabaseEntryType.valueOf(v[1]), obligation));
154155 }
162163 Global.getAnalysisCache().eagerlyPutDatabase(ObligationPolicyDatabase.class, database);
163164 }
164165
166 @Override
165167 public void visitClass(ClassDescriptor classDescriptor) throws CheckedAnalysisException {
166168
167169 XClass xclass = Global.getAnalysisCache().getClassAnalysis(XClass.class, classDescriptor);
240242 * obligation. If strict checking is performed, // weak
241243 * entries are ignored.
242244 */
243 if (xmethod.getName().equals("<init>") || xmethod.isStatic()
245 if ("<init>".equals(xmethod.getName()) || xmethod.isStatic()
244246 || xmethod.getName().toLowerCase().indexOf("close") >= 0
245 || xmethod.getSignature().toLowerCase().indexOf("Closeable") >= 0)
247 || xmethod.getSignature().toLowerCase().indexOf("Closeable") >= 0) {
246248 addParameterDeletesObligationDatabaseEntry(xmethod, obligationType,
247249 ObligationPolicyDatabaseEntryType.WEAK);
250 }
248251 }
249252 }
250253 }
252255
253256 }
254257
258 @Override
255259 public void finishPass() {
256260 //
257261 // If we saw any obligation-related annotations in the application
270274 }
271275 }
272276
277 @Override
273278 public String getDetectorClassName() {
274279 return this.getClass().getName();
275280 }
382387
383388 // See what type of obligation is being created.
384389 Obligation createdObligation = null;
385 if (xmethod.getName().equals("<init>")) {
390 if ("<init>".equals(xmethod.getName())) {
386391 // Constructor - obligation type is the type of object being created
387392 // (or some supertype)
388393 createdObligation = database.getFactory().getObligationByType(xmethod.getClassDescriptor());
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2008 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.detect;
20
21 import java.util.ArrayDeque;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Queue;
29 import java.util.Set;
30
31 import org.apache.bcel.classfile.Code;
32 import org.apache.bcel.classfile.Method;
33
34 import edu.umd.cs.findbugs.BugReporter;
35 import edu.umd.cs.findbugs.NonReportingDetector;
36 import edu.umd.cs.findbugs.OpcodeStack.Item;
37 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
38 import edu.umd.cs.findbugs.classfile.Global;
39 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
40
41 /**
42 * Builds the database of string parameters passed from method to method unchanged.
43 * @author Tagir Valeev
44 */
45 public class BuildStringPassthruGraph extends OpcodeStackDetector implements NonReportingDetector {
46
47 public static class MethodParameter {
48 final MethodDescriptor md;
49
50 final int parameterNumber;
51
52 public MethodParameter(MethodDescriptor md, int parameterNumber) {
53 super();
54 this.md = md;
55 this.parameterNumber = parameterNumber;
56 }
57
58 public MethodDescriptor getMethodDescriptor() {
59 return md;
60 }
61
62 public int getParameterNumber() {
63 return parameterNumber;
64 }
65
66 @Override
67 public String toString() {
68 return this.md + "[" + this.parameterNumber + "]";
69 }
70
71 @Override
72 public int hashCode() {
73 final int prime = 31;
74 int result = 1;
75 result = prime * result + ((md == null) ? 0 : md.hashCode());
76 result = prime * result + parameterNumber;
77 return result;
78 }
79
80 @Override
81 public boolean equals(Object obj) {
82 if (this == obj) {
83 return true;
84 }
85 if (obj == null) {
86 return false;
87 }
88 if (getClass() != obj.getClass()) {
89 return false;
90 }
91 MethodParameter other = (MethodParameter) obj;
92 if (md == null) {
93 if (other.md != null) {
94 return false;
95 }
96 } else if (!md.equals(other.md)) {
97 return false;
98 }
99 if (parameterNumber != other.parameterNumber) {
100 return false;
101 }
102 return true;
103 }
104 }
105
106 public static class StringPassthruDatabase {
107 private static final List<MethodDescriptor> FILENAME_STRING_METHODS = Arrays.asList(
108 new MethodDescriptor("java/io/File", "<init>", "(Ljava/lang/String;)V"),
109 new MethodDescriptor("java/io/File", "<init>", "(Ljava/lang/String;Ljava/lang/String;)V"),
110 new MethodDescriptor("java/io/RandomAccessFile", "<init>", "(Ljava/lang/String;Ljava/lang/String;)V"),
111 new MethodDescriptor("java/nio/file/Paths", "get", "(Ljava/lang/String;[Ljava/lang/String;)Ljava/nio/file/Path;", true),
112 new MethodDescriptor("java/io/FileReader", "<init>", "(Ljava/lang/String;)V"),
113 new MethodDescriptor("java/io/FileWriter", "<init>", "(Ljava/lang/String;)V"),
114 new MethodDescriptor("java/io/FileWriter", "<init>", "(Ljava/lang/String;Z)V"),
115 new MethodDescriptor("java/io/FileInputStream", "<init>", "(Ljava/lang/String;)V"),
116 new MethodDescriptor("java/io/FileOutputStream", "<init>", "(Ljava/lang/String;)V"),
117 new MethodDescriptor("java/io/FileOutputStream", "<init>", "(Ljava/lang/String;Z)V"),
118 new MethodDescriptor("java/util/Formatter", "<init>", "(Ljava/lang/String;)V"),
119 new MethodDescriptor("java/util/Formatter", "<init>", "(Ljava/lang/String;Ljava/lang/String;)V"),
120 new MethodDescriptor("java/util/Formatter", "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/util/Locale;)V"),
121 new MethodDescriptor("java/util/jar/JarFile", "<init>", "(Ljava/lang/String;)V"),
122 new MethodDescriptor("java/util/jar/JarFile", "<init>", "(Ljava/lang/String;Z)V"),
123 new MethodDescriptor("java/util/zip/ZipFile", "<init>", "(Ljava/lang/String;)V"),
124 new MethodDescriptor("java/util/zip/ZipFile", "<init>", "(Ljava/lang/String;Ljava/nio/charset/Charset;)V"),
125 new MethodDescriptor("java/io/PrintStream", "<init>", "(Ljava/lang/String;)V"),
126 new MethodDescriptor("java/io/PrintStream", "<init>", "(Ljava/lang/String;Ljava/lang/String;)V"),
127 new MethodDescriptor("java/io/PrintWriter", "<init>", "(Ljava/lang/String;)V"),
128 new MethodDescriptor("java/io/PrintWriter", "<init>", "(Ljava/lang/String;Ljava/lang/String;)V")
129 );
130
131 private final Map<MethodParameter, Set<MethodParameter>> graph = new HashMap<>();
132
133 /**
134 * Adds edge to the string passthru graph
135 * @param in callee
136 * @param out caller
137 */
138 void addEdge(MethodParameter in, MethodParameter out) {
139 Set<MethodParameter> outs = graph.get(in);
140 if (outs == null) {
141 outs = new HashSet<>();
142 graph.put(in, outs);
143 }
144 outs.add(out);
145 }
146
147 Set<MethodParameter> findLinked(Set<MethodParameter> inputs) {
148 Set<MethodParameter> result = new HashSet<>(inputs);
149 Queue<MethodParameter> toCheck = new ArrayDeque<>(inputs);
150 while (!toCheck.isEmpty()) {
151 MethodParameter in = toCheck.poll();
152 Set<MethodParameter> outs = graph.get(in);
153 if (outs != null) {
154 for (MethodParameter out : outs) {
155 if (!result.contains(out)) {
156 result.add(out);
157 toCheck.add(out);
158 }
159 }
160 }
161 }
162 return result;
163 }
164
165 /**
166 * Returns methods which call directly or indirectly methods from inputs
167 * passing the parameter unchanged
168 *
169 * @param inputs
170 * input methods with parameter
171 * @return Map where keys are methods and values are parameter indexes which can be passed to requested methods unchanged
172 */
173 public Map<MethodDescriptor, int[]> findLinkedMethods(Set<MethodParameter> inputs) {
174 Map<MethodDescriptor, int[]> result = new HashMap<>();
175 for (MethodParameter found : findLinked(inputs)) {
176 int[] params = result.get(found.getMethodDescriptor());
177 if(params == null) {
178 params = new int[] {found.getParameterNumber()};
179 result.put(found.getMethodDescriptor(), params);
180 } else {
181 int[] newParams = new int[params.length+1];
182 System.arraycopy(params, 0, newParams, 0, params.length);
183 newParams[params.length] = found.getParameterNumber();
184 result.put(found.getMethodDescriptor(), newParams);
185 }
186 }
187 return result;
188 }
189
190 /**
191 * Returns methods which parameter is the file name
192 * @return Map where keys are methods and values are parameter indexes which are used as file names
193 */
194 public Map<MethodDescriptor, int[]> getFileNameStringMethods() {
195 Set<MethodParameter> fileNameStringMethods = new HashSet<>();
196 for(MethodDescriptor md : FILENAME_STRING_METHODS) {
197 fileNameStringMethods.add(new MethodParameter(md, 0));
198 }
199 return findLinkedMethods(fileNameStringMethods);
200 }
201 }
202
203 private final StringPassthruDatabase cache = new StringPassthruDatabase();
204
205 private int nArgs;
206
207 private int shift;
208
209 private boolean[] argEnabled;
210
211 private List<MethodParameter>[] passedParameters;
212
213 public BuildStringPassthruGraph(BugReporter bugReporter) {
214 Global.getAnalysisCache().eagerlyPutDatabase(StringPassthruDatabase.class, cache);
215 }
216
217 @SuppressWarnings("unchecked")
218 @Override
219 public void visitMethod(Method obj) {
220 argEnabled = null;
221 org.apache.bcel.generic.Type[] argumentTypes = obj.getArgumentTypes();
222 if(argumentTypes.length == 0) {
223 return;
224 }
225 nArgs = argumentTypes.length;
226 for(int i=0; i<nArgs; i++) {
227 if(argumentTypes[i].getSignature().equals("Ljava/lang/String;")) {
228 if(argEnabled == null) {
229 argEnabled = new boolean[nArgs];
230 }
231 argEnabled[i] = true;
232 }
233 }
234 if(argEnabled != null) {
235 shift = obj.isStatic() ? 0 : -1;
236 passedParameters = new List[nArgs];
237 }
238 super.visitMethod(obj);
239 }
240
241 @Override
242 public boolean shouldVisitCode(Code obj) {
243 return argEnabled != null;
244 }
245
246 @Override
247 public void visitAfter(Code obj) {
248 super.visitAfter(obj);
249 for (int i = 0; i < nArgs; i++) {
250 List<MethodParameter> list = passedParameters[i];
251 if (list != null) {
252 MethodParameter cur = new MethodParameter(getMethodDescriptor(), i);
253 for (MethodParameter mp : list) {
254 cache.addEdge(mp, cur);
255 }
256 }
257 }
258 }
259
260 @Override
261 public void sawOpcode(int seen) {
262 if (isRegisterStore()) {
263 int param = getRegisterOperand() + shift;
264 if (param >= 0 && param < nArgs) {
265 argEnabled[param] = false;
266 passedParameters[param] = null;
267 }
268 }
269 switch (seen) {
270 case INVOKESPECIAL:
271 case INVOKESTATIC:
272 case INVOKEINTERFACE:
273 case INVOKEVIRTUAL:
274 MethodDescriptor md = getMethodDescriptorOperand();
275 int callArgs = getNumberArguments(md.getSignature());
276 for (int i = 0; i < callArgs; i++) {
277 Item item = getStack().getStackItem(callArgs - 1 - i);
278 int param = item.getRegisterNumber() + shift;
279 if (param >= 0 && param < nArgs && argEnabled[param]) {
280 List<MethodParameter> list = passedParameters[param];
281 if (list == null) {
282 passedParameters[param] = list = new ArrayList<>();
283 }
284 list.add(new MethodParameter(md, i));
285 }
286 }
287 break;
288 default:
289 break;
290 }
291 }
292 }
5656 import edu.umd.cs.findbugs.ba.vna.ValueNumber;
5757 import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow;
5858 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
59 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
60 import edu.umd.cs.findbugs.classfile.DescriptorFactory;
6159
6260 /**
6361 * Build database of unconditionally dereferenced parameters.
6967
7068 private static final boolean DEBUG = SystemProperties.getBoolean("fnd.debug.nullarg") || VERBOSE_DEBUG;
7169
72 public final TypeQualifierValue nonnullTypeQualifierValue;
70 public final TypeQualifierValue<javax.annotation.Nonnull> nonnullTypeQualifierValue;
7371
7472 abstract protected void reportBug(BugInstance bug);
7573
7674 public BuildUnconditionalParamDerefDatabase() {
77 ClassDescriptor nonnullClassDesc = DescriptorFactory.createClassDescriptor(javax.annotation.Nonnull.class);
78 this.nonnullTypeQualifierValue = TypeQualifierValue.getValue(nonnullClassDesc, null);
79 }
80
75 this.nonnullTypeQualifierValue = TypeQualifierValue.getValue(javax.annotation.Nonnull.class, null);
76 }
77
78 @Override
8179 public void visitClassContext(ClassContext classContext) {
8280 boolean fullAnalysis = AnalysisContext.currentAnalysisContext().getBoolProperty(
8381 FindBugsAnalysisFeatures.INTERPROCEDURAL_ANALYSIS_OF_REFERENCED_CLASSES);
84 if (!fullAnalysis && !AnalysisContext.currentAnalysisContext().isApplicationClass(classContext.getJavaClass()))
82 if (!fullAnalysis && !AnalysisContext.currentAnalysisContext().isApplicationClass(classContext.getJavaClass())) {
8583 return;
86 if (VERBOSE_DEBUG)
84 }
85 if (VERBOSE_DEBUG) {
8786 System.out.println("Visiting class " + classContext.getJavaClass().getClassName());
88
89 for (Method m : classContext.getMethodsInCallOrder())
87 }
88
89 for (Method m : classContext.getMethodsInCallOrder()) {
9090 considerMethod(classContext, m);
91 }
9192 }
9293
9394 private void considerMethod(ClassContext classContext, Method method) {
9495 boolean hasReferenceParameters = false;
95 for (Type argument : method.getArgumentTypes())
96 for (Type argument : method.getArgumentTypes()) {
9697 if (argument instanceof ReferenceType) {
9798 hasReferenceParameters = true;
9899 }
100 }
99101
100102 if (hasReferenceParameters && classContext.getMethodGen(method) != null) {
101 if (VERBOSE_DEBUG)
103 if (VERBOSE_DEBUG) {
102104 System.out.println("Check " + method);
105 }
103106 analyzeMethod(classContext, method);
104107 }
105108 }
132135 TypeQualifierAnnotation typeQualifierAnnotation = TypeQualifierApplications
133136 .getEffectiveTypeQualifierAnnotation(xmethod, i, nonnullTypeQualifierValue);
134137 boolean implicitNullCheckForEquals = false;
135 if (directTypeQualifierAnnotation == null && method.getName().equals("equals")
136 && method.getSignature().equals("(Ljava/lang/Object;)Z") && !method.isStatic()) {
138 if (directTypeQualifierAnnotation == null && "equals".equals(method.getName())
139 && "(Ljava/lang/Object;)Z".equals(method.getSignature()) && !method.isStatic()) {
137140 implicitNullCheckForEquals = true;
138141 Code code = method.getCode();
139142 ConstantPool cp = jclass.getConstantPool();
154157 typeQualifierAnnotation = TypeQualifierAnnotation.getValue(nonnullTypeQualifierValue, When.MAYBE);
155158 }
156159
157 if (typeQualifierAnnotation != null && typeQualifierAnnotation.when == When.ALWAYS)
160 if (typeQualifierAnnotation != null && typeQualifierAnnotation.when == When.ALWAYS) {
158161 unconditionalDerefSet.set(i);
159 else if (isCaught(classContext, method, entryFact, paramVN)) {
162 } else if (isCaught(classContext, method, entryFact, paramVN)) {
160163 // ignore
161 } else if (typeQualifierAnnotation == null)
164 } else if (typeQualifierAnnotation == null) {
162165 unconditionalDerefSet.set(i);
163 else {
166 } else {
164167 int paramLocal = xmethod.isStatic() ? i : i + 1;
165168 int priority = Priorities.NORMAL_PRIORITY;
166 if (typeQualifierAnnotation.when != When.UNKNOWN)
169 if (typeQualifierAnnotation.when != When.UNKNOWN) {
167170 priority--;
168 if (xmethod.isStatic() || xmethod.isFinal() || xmethod.isPrivate() || xmethod.getName().equals("<init>")
169 || jclass.isFinal())
171 }
172 if (xmethod.isStatic() || xmethod.isFinal() || xmethod.isPrivate() || "<init>".equals(xmethod.getName())
173 || jclass.isFinal()) {
170174 priority--;
171 if (directTypeQualifierAnnotation == null)
175 }
176 if (directTypeQualifierAnnotation == null) {
172177 priority++;
178 }
173179 String bugPattern = implicitNullCheckForEquals ? "NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT"
174180 : "NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE";
175181 reportBug(new BugInstance(this, bugPattern, priority).addClassAndMethod(jclass, method).add(
177183 }
178184 }
179185 i++;
180 if (paramSig.equals("D") || paramSig.equals("J"))
186 if ("D".equals(paramSig) || "J".equals(paramSig)) {
181187 paramLocalOffset += 2;
182 else
188 } else {
183189 paramLocalOffset += 1;
190 }
184191 }
185192
186193 // No need to add properties if there are no unconditionally
200207 property.setParamsWithProperty(unconditionalDerefSet);
201208
202209 AnalysisContext.currentAnalysisContext().getUnconditionalDerefParamDatabase()
203 .setProperty(xmethod.getMethodDescriptor(), property);
210 .setProperty(xmethod.getMethodDescriptor(), property);
204211 if (DEBUG) {
205212 System.out.println("Unconditional deref: " + xmethod + "=" + property);
206213 }
207214 } catch (CheckedAnalysisException e) {
208215 AnalysisContext.currentAnalysisContext().getLookupFailureCallback()
209 .logError("Error analyzing " + xmethod + " for unconditional deref training", e);
210 }
211 }
212
213 /**
214 * @param classContext
215 * @param method
216 * @param entryFact
217 * @param paramVN
218 * @return
219 */
216 .logError("Error analyzing " + xmethod + " for unconditional deref training", e);
217 }
218 }
219
220220 public boolean isCaught(ClassContext classContext, Method method, UnconditionalValueDerefSet entryFact, ValueNumber paramVN) {
221221 boolean caught = true;
222222
223223 Set<Location> dereferenceSites
224 = entryFact.getDerefLocationSet(paramVN);
224 = entryFact.getDerefLocationSet(paramVN);
225225 if (dereferenceSites != null && !dereferenceSites.isEmpty()) {
226226 ConstantPool cp = classContext.getJavaClass().getConstantPool();
227227
228228 for(Location loc : dereferenceSites) {
229 if (!FindNullDeref.catchesNull(cp, method.getCode(), loc))
229 if (!FindNullDeref.catchesNull(cp, method.getCode(), loc)) {
230230 caught = false;
231 }
231232 }
232233
233234 }
3434 import edu.umd.cs.findbugs.Detector;
3535 import edu.umd.cs.findbugs.MethodAnnotation;
3636 import edu.umd.cs.findbugs.Priorities;
37 import edu.umd.cs.findbugs.SystemProperties;
3738 import edu.umd.cs.findbugs.ba.AnalysisContext;
3839 import edu.umd.cs.findbugs.ba.BasicBlock;
3940 import edu.umd.cs.findbugs.ba.CFG;
5859
5960 AnalysisContext analysisContext;
6061
62 private final boolean testingEnabled;
63
6164 public CallToUnconditionalThrower(BugReporter bugReporter) {
6265 this.bugReporter = bugReporter;
66 testingEnabled = SystemProperties.getBoolean("report_TESTING_pattern_in_standard_detectors");
6367 }
6468
65 /*
66 * (non-Javadoc)
67 *
68 * @see edu.umd.cs.findbugs.Detector#report()
69 */
69 @Override
7070 public void report() {
71 // TODO Auto-generated method stub
72
71 //
7372 }
7473
7574 private void analyzeMethod(ClassContext classContext, Method method) throws CFGBuilderException, DataflowAnalysisException {
76 if (BCELUtil.isSynthetic(method) || (method.getAccessFlags() & Constants.ACC_BRIDGE) == Constants.ACC_BRIDGE)
75 if (BCELUtil.isSynthetic(method) || (method.getAccessFlags() & Constants.ACC_BRIDGE) == Constants.ACC_BRIDGE) {
7776 return;
77 }
7878 CFG cfg = classContext.getCFG(method);
7979
8080 ConstantPoolGen cpg = classContext.getConstantPoolGen();
8484 BasicBlock basicBlock = i.next();
8585
8686 // Check if it's a method invocation.
87 if (!basicBlock.isExceptionThrower())
87 if (!basicBlock.isExceptionThrower()) {
8888 continue;
89 }
8990 InstructionHandle thrower = basicBlock.getExceptionThrower();
9091 Instruction ins = thrower.getInstruction();
91 if (!(ins instanceof InvokeInstruction))
92 if (!(ins instanceof InvokeInstruction)) {
9293 continue;
94 }
9395
9496 InvokeInstruction inv = (InvokeInstruction) ins;
9597 boolean foundThrower = false;
9698 boolean foundNonThrower = false;
9799
98 if (inv instanceof INVOKEINTERFACE)
100 if (inv instanceof INVOKEINTERFACE) {
99101 continue;
102 }
100103
101104 String className = inv.getClassName(cpg);
102105
107110 Set<XMethod> targetSet = null;
108111 try {
109112
110 if (className.startsWith("["))
113 if (className.startsWith("[")) {
111114 continue;
115 }
112116 String methodSig = inv.getSignature(cpg);
113 if (!methodSig.endsWith("V"))
117 if (!methodSig.endsWith("V")) {
114118 continue;
119 }
115120
116121 targetSet = Hierarchy2.resolveMethodCallTargets(inv, typeFrame, cpg);
117122
118123 for (XMethod xMethod : targetSet) {
119 if (DEBUG)
124 if (DEBUG) {
120125 System.out.println("\tFound " + xMethod);
126 }
121127
122128 boolean isUnconditionalThrower = xMethod.isUnconditionalThrower() && !xMethod.isUnsupported()
123129 && !xMethod.isSynthetic();
124130 if (isUnconditionalThrower) {
125131 foundThrower = true;
126 if (DEBUG)
132 if (DEBUG) {
127133 System.out.println("Found thrower");
134 }
128135 } else {
129136 foundNonThrower = true;
130 if (DEBUG)
137 if (DEBUG) {
131138 System.out.println("Found non thrower");
139 }
132140 }
133141
134142 }
136144 analysisContext.getLookupFailureCallback().reportMissingClass(e);
137145 }
138146 boolean newResult = foundThrower && !foundNonThrower;
139 if (newResult)
147 if (newResult) {
140148 bugReporter.reportBug(new BugInstance(this, "TESTING", Priorities.NORMAL_PRIORITY)
141 .addClassAndMethod(classContext.getJavaClass(), method)
142 .addString("Call to method that always throws Exception").addMethod(primaryXMethod)
143 .describe(MethodAnnotation.METHOD_CALLED).addSourceLine(classContext, method, loc));
149 .addClassAndMethod(classContext.getJavaClass(), method)
150 .addString("Call to method that always throws Exception").addMethod(primaryXMethod)
151 .describe(MethodAnnotation.METHOD_CALLED).addSourceLine(classContext, method, loc));
152 }
144153
145154 }
146155
147156 }
148157
158 @Override
149159 public void visitClassContext(ClassContext classContext) {
160 if(!testingEnabled){
161 return;
162 }
150163 analysisContext = AnalysisContext.currentAnalysisContext();
151164 Method[] methodList = classContext.getJavaClass().getMethods();
152165 for (Method method : methodList) {
153 if (method.getCode() == null)
166 if (method.getCode() == null) {
154167 continue;
168 }
155169
156170 try {
157171
5858 this.bugReporter = bugReporter;
5959 }
6060
61 @Override
6162 public void visitClassContext(ClassContext classContext) {
6263 JavaClass javaClass = classContext.getJavaClass();
6364 Method[] methodList = javaClass.getMethods();
6465
6566 for (Method method : methodList) {
66 if (method.getCode() == null)
67 if (method.getCode() == null) {
6768 continue;
69 }
6870
6971 try {
7072 analyzeMethod(classContext, method);
8789 * @param method
8890 */
8991 private void analyzeMethod(ClassContext classContext, Method method) throws MethodUnprofitableException, CFGBuilderException,
90 DataflowAnalysisException {
91 if (BCELUtil.isSynthetic(method)|| (method.getAccessFlags() & Constants.ACC_BRIDGE) == Constants.ACC_BRIDGE)
92 DataflowAnalysisException {
93 if (BCELUtil.isSynthetic(method)|| (method.getAccessFlags() & Constants.ACC_BRIDGE) == Constants.ACC_BRIDGE) {
9294 return;
95 }
9396 CFG cfg = classContext.getCFG(method);
9497 TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
9598 ConstantPoolGen constantPoolGen = classContext.getConstantPoolGen();
100103 Instruction ins = handle.getInstruction();
101104
102105 // Only consider invoke instructions
103 if (!(ins instanceof InvokeInstruction))
106 if (!(ins instanceof InvokeInstruction)) {
104107 continue;
105 if (ins instanceof INVOKEINTERFACE)
108 }
109 if (ins instanceof INVOKEINTERFACE) {
106110 continue;
111 }
107112
108113 InvokeInstruction inv = (InvokeInstruction) ins;
109114 TypeFrame frame = typeDataflow.getFactAtLocation(location);
110115
111116 String methodName = inv.getMethodName(constantPoolGen);
112 if (methodName.toLowerCase().indexOf("unsupported") >= 0)
117 if (methodName.toLowerCase().indexOf("unsupported") >= 0) {
113118 continue;
119 }
114120 String methodSig = inv.getSignature(constantPoolGen);
115 if (methodSig.equals("()Ljava/lang/UnsupportedOperationException;"))
121 if ("()Ljava/lang/UnsupportedOperationException;".equals(methodSig)) {
116122 continue;
123 }
117124
118125 Set<XMethod> targets;
119126 try {
123130 AnalysisContext.reportMissingClass(e);
124131 continue locationLoop;
125132 }
126 if (targets.isEmpty())
133 if (targets.isEmpty()) {
127134 continue locationLoop;
135 }
128136 int priority = targets.size() == 1 ? Priorities.HIGH_PRIORITY : Priorities.NORMAL_PRIORITY;
129137 for (XMethod m : targets) {
130 if (!m.isUnsupported())
138 if (!m.isUnsupported()) {
131139 continue locationLoop;
140 }
132141 XClass xc = AnalysisContext.currentXFactory().getXClass(m.getClassDescriptor());
133 if (!(inv instanceof INVOKESTATIC) && !(m.isFinal() || xc.isFinal()))
142 if (!(inv instanceof INVOKESTATIC) && !(m.isFinal() || xc.isFinal())) {
134143 priority = Priorities.NORMAL_PRIORITY;
144 }
135145 if (xc == null || xc.isAbstract()) {
136146 try {
137 if (!AnalysisContext.currentAnalysisContext().getSubtypes2().hasSubtypes(m.getClassDescriptor()))
147 if (!AnalysisContext.currentAnalysisContext().getSubtypes2().hasSubtypes(m.getClassDescriptor())) {
138148 continue locationLoop;
149 }
139150 } catch (ClassNotFoundException e) {
140151 AnalysisContext.reportMissingClass(e);
141152 continue locationLoop;
143154 }
144155 }
145156 BugInstance bug = new BugInstance(this, "DMI_UNSUPPORTED_METHOD", priority)
146 .addClassAndMethod(classContext.getJavaClass(), method).addCalledMethod(constantPoolGen, inv)
147 .addSourceLine(classContext, method, location);
157 .addClassAndMethod(classContext.getJavaClass(), method).addCalledMethod(constantPoolGen, inv)
158 .addSourceLine(classContext, method, location);
148159 bugReporter.reportBug(bug);
149160
150161 }
153164
154165 /*
155166 * (non-Javadoc)
156 *
167 *
157168 * @see edu.umd.cs.findbugs.Detector#report()
158169 */
170 @Override
159171 public void report() {
160172 // TODO Auto-generated method stub
161173
5151 if ((seen == PUTFIELD || seen == PUTSTATIC)) {
5252 XField f = getXFieldOperand();
5353 if (f != null) {
54 if (f.isFinal() || !f.isProtected() && !f.isPublic())
55 if (emptyArrayOnTOS)
54 if (f.isFinal() || !f.isProtected() && !f.isPublic()) {
55 if (emptyArrayOnTOS) {
5656 emptyArray.add(f);
57 else
57 } else {
5858 nonEmptyArray.add(f);
59 }
60 }
5961 }
6062
6163 }
6466
6567 if (seen == GETSTATIC || seen == GETFIELD) {
6668 XField f = getXFieldOperand();
67 if (emptyArray.contains(f) && !nonEmptyArray.contains(f) && f.isFinal())
69 if (emptyArray.contains(f) && !nonEmptyArray.contains(f) && f.isFinal()) {
6870 emptyArrayOnTOS = true;
71 }
6972 }
7073 switch (seen) {
7174 case INVOKEVIRTUAL:
7477 case INVOKEINTERFACE:
7578 ClassDescriptor c = getClassDescriptorOperand();
7679 Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
77 if (subtypes2.isApplicationClass(c))
80 if (subtypes2.isApplicationClass(c)) {
7881 xFactory.addCalledMethod(getMethodDescriptorOperand());
82 }
7983
8084 break;
8185 default:
8690 @Override
8791 public void report() {
8892 emptyArray.removeAll(nonEmptyArray);
89 for (XField f : emptyArray)
93 for (XField f : emptyArray) {
9094 xFactory.addEmptyArrayField(f);
95 }
9196 emptyArray.clear();
9297 }
9398
9499 }
95100
96 // vim:ts=4
2525 import edu.umd.cs.findbugs.BugInstance;
2626 import edu.umd.cs.findbugs.BugReporter;
2727 import edu.umd.cs.findbugs.Lookup;
28 import edu.umd.cs.findbugs.SystemProperties;
2829 import edu.umd.cs.findbugs.ba.XMethod;
2930 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
3031 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
3738
3839 ClassDescriptor mustOverrideAnnotation = DescriptorFactory.createClassDescriptor(OverridingMethodsMustInvokeSuper.class);
3940
41 private final boolean testingEnabled;
42
4043 public CbeckMustOverrideSuperAnnotation(BugReporter bugReporter) {
4144 this.bugReporter = bugReporter;
45 testingEnabled = SystemProperties.getBoolean("report_TESTING_pattern_in_standard_detectors");
4246 }
4347
4448 private boolean sawCallToSuper;
4549
4650 @Override
4751 public void visit(Code code) {
48 if (getMethod().isStatic() || getMethod().isPrivate())
52 if(!testingEnabled){
4953 return;
54 }
55 if (getMethod().isStatic() || getMethod().isPrivate()) {
56 return;
57 }
5058 XMethod overrides = Lookup.findSuperImplementorAsXMethod(getThisClass(), getMethodName(), getMethodSig(), bugReporter);
5159
52 if (overrides == null)
60 if (overrides == null) {
5361 return;
62 }
5463 AnnotationValue annotation = overrides.getAnnotation(mustOverrideAnnotation);
55 if (annotation == null)
64 if (annotation == null) {
5665 return;
66 }
5767 sawCallToSuper = false;
5868 super.visit(code);
59 if (!sawCallToSuper)
69 if (!sawCallToSuper) {
6070 bugReporter.reportBug(new BugInstance(this, "TESTING", NORMAL_PRIORITY).addClassAndMethod(this).addString(
6171 "Method must invoke override method in superclass"));
72 }
6273 }
6374
6475 /*
6576 * (non-Javadoc)
66 *
77 *
6778 * @see edu.umd.cs.findbugs.bcel.OpcodeStackDetector#sawOpcode(int)
6879 */
6980 @Override
7081 public void sawOpcode(int seen) {
71 if (seen != INVOKESPECIAL)
82 if (seen != INVOKESPECIAL) {
7283 return;
84 }
7385
7486 String calledClassName = getClassConstantOperand();
7587 String calledMethodName = getNameConstantOperand();
2828 import edu.umd.cs.findbugs.OpcodeStack;
2929 import edu.umd.cs.findbugs.OpcodeStack.Item;
3030 import edu.umd.cs.findbugs.OpcodeStack.JumpInfo;
31 import edu.umd.cs.findbugs.SystemProperties;
3132 import edu.umd.cs.findbugs.ba.XClass;
3233 import edu.umd.cs.findbugs.ba.XMethod;
3334 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
4344
4445 final BugAccumulator accumulator;
4546
47 private final boolean testingEnabled;
48
4649 public CheckAnalysisContextContainedAnnotation(BugReporter bugReporter) {
4750 this.bugReporter = bugReporter;
4851 this.accumulator = new BugAccumulator(bugReporter);
52 testingEnabled = SystemProperties.getBoolean("report_TESTING_pattern_in_standard_detectors");
4953 }
5054
5155 final static ClassDescriptor ConstantAnnotation = DescriptorFactory.createClassDescriptor(StaticConstant.class);
5559 private boolean analysisContextContained(XClass xclass) {
5660 AnnotatedObject ao = xclass;
5761 do {
58 if (ao.getAnnotation(AnalysisContextContainedAnnotation) != null)
62 if (ao.getAnnotation(AnalysisContextContainedAnnotation) != null) {
5963 return true;
64 }
6065 ao = ao.getContainingScope();
6166
6267 } while (ao != null);
6570 }
6671 @Override
6772 public void visit(Field field) {
68 if (!field.isStatic())
69 return;
70 String signature = field.getSignature();
71 if (signature.startsWith("Ljava/util/") && !signature.equals("Ljava/util/regex/Pattern;")
72 && !signature.equals("Ljava/util/logging/Logger;") && !signature.equals("Ljava/util/BitSet;")
73 && !signature.equals("Ljava/util/ResourceBundle;")
74 && !signature.equals("Ljava/util/Comparator;")
75 && getXField().getAnnotation(ConstantAnnotation) == null) {
76 boolean flagged = analysisContextContained(getXClass());
73 if (!field.isStatic()) {
74 return;
75 }
76 String signature = field.getSignature();
77 if (signature.startsWith("Ljava/util/") && !"Ljava/util/regex/Pattern;".equals(signature)
78 && !"Ljava/util/logging/Logger;".equals(signature) && !"Ljava/util/BitSet;".equals(signature)
79 && !"Ljava/util/ResourceBundle;".equals(signature)
80 && !"Ljava/util/Comparator;".equals(signature)
81 && getXField().getAnnotation(ConstantAnnotation) == null) {
82 boolean flagged = analysisContextContained(getXClass());
7783
78 bugReporter.reportBug(new BugInstance(this, "TESTING", flagged ? NORMAL_PRIORITY : LOW_PRIORITY).addClass(this).addField(this).addType(signature));
84 bugReporter.reportBug(new BugInstance(this, "TESTING", flagged ? NORMAL_PRIORITY : LOW_PRIORITY).addClass(this).addField(this).addType(signature));
7985
80 }
86 }
8187 }
8288 @Override
8389 public void visit(Code code) {
84 boolean interesting = false;
90 boolean interesting = testingEnabled;
8591 if (interesting) {
8692 // initialize any variables we want to initialize for the method
8793 super.visit(code); // make callbacks to sawOpcode for all opcodes
98104 case Constants.IF_ICMPNE:
99105 OpcodeStack.Item left = stack.getStackItem(1);
100106 OpcodeStack.Item right = stack.getStackItem(0);
101 if (bad(left, right) || bad(right, left))
107 if (bad(left, right) || bad(right, left)) {
102108 accumulator.accumulateBug(new BugInstance(this, "TESTING", NORMAL_PRIORITY).addClassAndMethod(this)
103109 .addValueSource(left, this).addValueSource(right, this)
104110 .addString("Just check the sign of the result of compare or compareTo, not specific values such as 1 or -1"), this);
111 }
105112 break;
106113 }
107114
110117 private boolean bad(Item left, Item right) {
111118 XMethod m = left.getReturnValueOf();
112119
113 if (m == null)
120 if (m == null) {
114121 return false;
122 }
115123 Object value = right.getConstant();
116 if (!(value instanceof Integer) || ((Integer) value).intValue() == 0)
124 if (!(value instanceof Integer) || ((Integer) value).intValue() == 0) {
117125 return false;
118 if (m.isStatic() || !m.isPublic())
126 }
127 if (m.isStatic() || !m.isPublic()) {
119128 return false;
129 }
120130
121 if (m.getName().equals("compareTo") && m.getSignature().equals("(Ljava/lang/Object;)I"))
131 if ("compareTo".equals(m.getName()) && "(Ljava/lang/Object;)I".equals(m.getSignature())) {
122132 return true;
123 if (m.getName().equals("compare") && m.getSignature().equals("(Ljava/lang/Object;Ljava/lang/Object;)I"))
133 }
134 if ("compare".equals(m.getName()) && "(Ljava/lang/Object;Ljava/lang/Object;)I".equals(m.getSignature())) {
124135 return true;
136 }
125137
126138 return false;
127139
4040
4141 /**
4242 * This is just for debugging method call resolution.
43 *
43 *
4444 * @author David Hovemeyer
4545 */
4646 public class CheckCalls implements Detector, NonReportingDetector {
5757
5858 /*
5959 * (non-Javadoc)
60 *
60 *
6161 * @see
6262 * edu.umd.cs.findbugs.Detector#visitClassContext(edu.umd.cs.findbugs.ba
6363 * .ClassContext)
6464 */
65 @Override
6566 public void visitClassContext(ClassContext classContext) {
6667 Method[] methodList = classContext.getJavaClass().getMethods();
6768 for (Method method : methodList) {
68 if (method.getCode() == null)
69 if (method.getCode() == null) {
6970 continue;
71 }
7072
7173 // System.out.println("--> " + method.getName());
72 if (METHOD != null && !method.getName().equals(METHOD))
74 if (METHOD != null && !method.getName().equals(METHOD)) {
7375 continue;
76 }
7477
7578 try {
7679 System.out.println("Analyzing " + SignatureConverter.convertMethodSignature(classContext.getJavaClass(), method));
8689 }
8790
8891 private void analyzeMethod(ClassContext classContext, Method method) throws CFGBuilderException, ClassNotFoundException,
89 DataflowAnalysisException {
92 DataflowAnalysisException {
9093 CFG cfg = classContext.getCFG(method);
9194 for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
9295 Location location = i.next();
9497
9598 if (ins instanceof InvokeInstruction) {
9699 if (TARGET_METHOD != null
97 && !((InvokeInstruction) ins).getMethodName(classContext.getConstantPoolGen()).equals(TARGET_METHOD))
100 && !((InvokeInstruction) ins).getMethodName(classContext.getConstantPoolGen()).equals(TARGET_METHOD)) {
98101 continue;
102 }
99103
100104 System.out.println("\n*******************************************************\n");
101105
120124
121125 /*
122126 * (non-Javadoc)
123 *
127 *
124128 * @see edu.umd.cs.findbugs.Detector#report()
125129 */
130 @Override
126131 public void report() {
127132 }
128133
2525 import java.util.LinkedList;
2626 import java.util.List;
2727 import java.util.Map;
28 import java.util.Set;
2928 import java.util.StringTokenizer;
3029
3130 import javax.annotation.CheckForNull;
6362 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
6463 import edu.umd.cs.findbugs.classfile.analysis.AnnotationValue;
6564 import edu.umd.cs.findbugs.classfile.analysis.EnumValue;
66 import edu.umd.cs.findbugs.plan.AnalysisPass;
67 import edu.umd.cs.findbugs.plan.ExecutionPlan;
6865
6966 /**
7067 * Check uses of the ExpectWarning and NoWarning annotations. This is for
7976
8077 private final BugCollection bugCollection;
8178
82 private Set<String> possibleBugCodes;
83
8479 private boolean initialized = false;
8580 private Map<ClassDescriptor, Collection<BugInstance>> warningsByClass;
8681 private Map<MethodDescriptor, Collection<BugInstance>> warningsByMethod;
107102 }
108103 }
109104
105 @Override
110106 public void visitClass(ClassDescriptor classDescriptor) throws CheckedAnalysisException {
111107 if (reporter == null) {
112108 if (!warned) {
113109 System.err
114 .println("*** NOTE ***: CheckExpectedWarnings disabled because bug reporter doesn't use a BugCollection");
110 .println("*** NOTE ***: CheckExpectedWarnings disabled because bug reporter doesn't use a BugCollection");
115111 warned = true;
116112 }
117113 return;
145141 }
146142 FieldAnnotation field = warning.getPrimaryField();
147143 if (field != null) {
148 if (DEBUG)
144 if (DEBUG) {
149145 System.out.println("primary field of " + field + " for " + warning);
146 }
150147 FieldDescriptor fieldDescriptor = field.toFieldDescriptor();
151148 Collection<BugInstance> warnings = warningsByField.get(fieldDescriptor);
152149
156153 }
157154 warnings.add(warning);
158155 }
159 if(field == null && method == null){
160 ClassAnnotation clazz = warning.getPrimaryClass();
161 if (clazz != null) {
162 ClassDescriptor classDesc = clazz.getClassDescriptor();
163 Collection<BugInstance> warnings = warningsByClass.get(classDesc);
164 if (warnings == null) {
165 warnings = new LinkedList<BugInstance>();
166 warningsByClass.put(classDesc, warnings);
167 }
168 warnings.add(warning);
169 }
170 }
171 }
172
173 //
174 // Based on enabled detectors, figure out which bug codes
175 // could possibly be reported. Don't complain about
176 // expected warnings that would be produced by detectors
177 // that aren't enabled.
178 //
179
180 possibleBugCodes = new HashSet<String>();
181 ExecutionPlan executionPlan = Global.getAnalysisCache().getDatabase(ExecutionPlan.class);
182 Iterator<AnalysisPass> i = executionPlan.passIterator();
183 while (i.hasNext()) {
184 AnalysisPass pass = i.next();
185 Iterator<DetectorFactory> j = pass.iterator();
186 while (j.hasNext()) {
187 DetectorFactory factory = j.next();
188
189 Collection<BugPattern> reportedPatterns = factory.getReportedBugPatterns();
190 for (BugPattern pattern : reportedPatterns) {
191 possibleBugCodes.add(pattern.getType());
192 possibleBugCodes.add(pattern.getAbbrev());
193 }
194 }
195 }
196 if (DEBUG) {
197 System.out.println("CEW: possible warnings are " + possibleBugCodes);
198 }
156
157 ClassAnnotation clazz = warning.getPrimaryClass();
158 if (clazz != null) {
159 ClassDescriptor classDesc = clazz.getClassDescriptor();
160 if(field != null && classDesc.equals(field.getClassDescriptor())) {
161 continue;
162 }
163 if (method != null && classDesc.equals(method.getClassDescriptor())) {
164 continue;
165 }
166 Collection<BugInstance> warnings = warningsByClass.get(classDesc);
167 if (warnings == null) {
168 warnings = new LinkedList<BugInstance>();
169 warningsByClass.put(classDesc, warnings);
170 }
171 warnings.add(warning);
172 }
173
174 }
175
199176 }
200177
201178 XClass xclass = Global.getAnalysisCache().getClassAnalysis(XClass.class, classDescriptor);
203180 if (DEBUG) {
204181 System.out.println("CEW: checking " + xclass.toString());
205182 }
183 if (xclass.isSynthetic()) {
184 if (DEBUG) {
185 System.out.println("Skipping synthetic classxclass " + xclass.toString());
186 }
187 return;
188 }
206189 check(xclass, expectWarning, true, HIGH_PRIORITY);
207190 check(xclass, desireWarning, true, NORMAL_PRIORITY);
208191 check(xclass, noWarning, false, HIGH_PRIORITY);
212195 if (DEBUG) {
213196 System.out.println("CEW: checking " + xmethod.toString());
214197 }
198 if (xmethod.isSynthetic()) {
199 if (DEBUG) {
200 System.out.println("Skipping synthetic method " + xmethod.toString());
201 }
202 continue;
203 }
215204 check(xmethod, expectWarning, true, HIGH_PRIORITY);
216205 check(xmethod, desireWarning, true, NORMAL_PRIORITY);
217206 check(xmethod, noWarning, false, HIGH_PRIORITY);
221210 if (DEBUG) {
222211 System.out.println("CEW: checking " + xfield.toString());
223212 }
213 if (xfield.isSynthetic()) {
214 if (DEBUG) {
215 System.out.println("Skipping synthetic field " + xfield.toString());
216 }
217 continue;
218 }
224219 check(xfield, expectWarning, true, HIGH_PRIORITY);
225220 check(xfield, desireWarning, true, NORMAL_PRIORITY);
226221 check(xfield, noWarning, false, HIGH_PRIORITY);
231226
232227 private void check(XClass xclass, ClassDescriptor annotation, boolean expectWarnings, int priority) {
233228 AnnotationValue expect = xclass.getAnnotation(annotation);
234 if (expect == null)
229 if (expect == null) {
235230 return;
231 }
236232 if (DEBUG) {
237233 System.out.println("*** Found " + annotation + " annotation on " + xclass);
238234 }
243239
244240 private void check(XMethod xmethod, ClassDescriptor annotation, boolean expectWarnings, int priority) {
245241 AnnotationValue expect = xmethod.getAnnotation(annotation);
246 if (expect == null)
242 if (expect == null) {
247243 return;
244 }
248245 if (DEBUG) {
249246 System.out.println("*** Found " + annotation + " annotation on " + xmethod);
250247 }
255252
256253 private void check(XField xfield, ClassDescriptor annotation, boolean expectWarnings, int priority) {
257254 AnnotationValue expect = xfield.getAnnotation(annotation);
258 if (expect == null)
255 if (expect == null) {
259256 return;
257 }
260258
261259 if (DEBUG) {
262260 System.out.println("*** Found " + annotation + " annotation on " + xfield);
275273 EnumValue wantedConfidence = (EnumValue) expect.getValue("confidence");
276274 EnumValue wantedPriority = (EnumValue) expect.getValue("priority");
277275 Integer num = (Integer) expect.getValue("num");
278 if (num == null)
276 if (num == null) {
279277 num = (expectWarnings ? 1 : 0);
278 }
280279 Integer rank = (Integer) expect.getValue("rank");
281 if (rank == null)
280 if (rank == null) {
282281 rank = BugRanker.VISIBLE_RANK_MAX;
282 }
283283
284284 int minPriority = Confidence.LOW.getConfidenceValue();
285 if (wantedConfidence != null)
285 if (wantedConfidence != null) {
286286 minPriority = Confidence.valueOf(wantedConfidence.value).getConfidenceValue();
287 else if (wantedPriority != null)
287 } else if (wantedPriority != null) {
288288 minPriority = Priority.valueOf(wantedPriority.value).getPriorityValue();
289 }
289290
290291 if (DEBUG) {
291 if (warnings == null)
292 if (warnings == null) {
292293 System.out.println("Checking " + expectedBugCodes + " against no bugs");
293 else {
294 } else {
294295 System.out.println("Checking " + expectedBugCodes + " against " + warnings.size() + " bugs");
295296 for (BugInstance b : warnings) {
296297 System.out.println(" " + b.getType());
303304 StringTokenizer tok = new StringTokenizer(expectedBugCodes, ",");
304305 while (tok.hasMoreTokens()) {
305306 String bugCode = tok.nextToken().trim();
306 if (!possibleBugCodes.contains(bugCode))
307 continue;
308307 checkAnnotation(bugCode, warnings, expectWarnings, priority, rank, num, descriptor, minPriority, cd);
309308 }
310309 }
318317 Collection<SourceLineAnnotation> bugs = countWarnings(warnings, bugCode, minPriority,
319318 rank);
320319 if (expectWarnings && bugs.size() < num) {
320 if (DetectorFactoryCollection.instance().isDisabledByDefault(bugCode)) {
321 return;
322 }
321323 BugInstance bug = makeWarning("FB_MISSING_EXPECTED_WARNING", methodDescriptor, priority, cd).addString(bugCodeMessage);
322324 if (!bugs.isEmpty()) {
323325 bug.addString(String.format("Expected %d bugs, saw %d", num, bugs.size()));
324326 }
325327 reporter.reportBug(bug);
326328 } else if (bugs.size() > num) {
329 // More bugs than expected
327330 BugInstance bug = makeWarning("FB_UNEXPECTED_WARNING", methodDescriptor, priority, cd).addString(bugCodeMessage);
328331 if (!expectWarnings) {
332 // Wanted no more than this many warnings
329333 for (SourceLineAnnotation s : bugs) {
330334 reporter.reportBug(bug.add(s));
331335 }
332336 } else if(num > 1){
337 // For example, we told it that we expected 3 warnings, and saw 4 warnings
333338 // num == 1 is default value. So if we set a non default value, and see more warnings
334339 // as expected, it's a problem
335340 bug.addString(String.format("Expected %d bugs, saw %d", num, bugs.size()));
338343 }
339344 }
340345
341 /**
342 * @param bugPattern
343 * @param methodDescriptor
344 * @param priority
345 * @return
346 */
347346 public BugInstance makeWarning(String bugPattern, Object descriptor, int priority, ClassDescriptor cd) {
348347 BugInstance bug = new BugInstance(this, bugPattern, priority).addClass(cd);
349 if (descriptor instanceof FieldDescriptor)
348 if (descriptor instanceof FieldDescriptor) {
350349 bug.addField((FieldDescriptor)descriptor);
351 else if (descriptor instanceof MethodDescriptor)
350 } else if (descriptor instanceof MethodDescriptor) {
352351 bug.addMethod((MethodDescriptor)descriptor);
353 else if (descriptor instanceof ClassDescriptor)
352 } else if (descriptor instanceof ClassDescriptor) {
354353 bug.addClass((ClassDescriptor)descriptor);
355 if (DEBUG)
354 }
355 if (DEBUG) {
356356 System.out.println("Reporting " + bug);
357 }
357358 return bug;
358359
359360 }
361362 private static Collection<SourceLineAnnotation> countWarnings( Collection<BugInstance> warnings,
362363 @CheckForNull String bugCode,
363364 int desiredPriority, int rank) {
365
364366 Collection<SourceLineAnnotation> matching = new HashSet<SourceLineAnnotation>();
365367 DetectorFactoryCollection i18n = DetectorFactoryCollection.instance();
366368 boolean matchPattern = false;
372374
373375 if (warnings != null) {
374376 for (BugInstance warning : warnings) {
375 if (warning.getPriority() > desiredPriority)
377 if (warning.getPriority() > desiredPriority) {
376378 continue;
377 if (warning.getBugRank() > rank)
379 }
380 if (warning.getBugRank() > rank) {
378381 continue;
382 }
379383 if (bugCode == null) {
380384 matching.add(warning.getPrimarySourceLineAnnotation());
385 matching.addAll(warning.getAnotherInstanceSourceLineAnnotations());
381386 continue;
382387 }
383388 BugPattern pattern = warning.getBugPattern();
384389 String match;
385 if (matchPattern)
390 if (matchPattern) {
386391 match = pattern.getType();
387 else
392 } else {
388393 match = pattern.getAbbrev();
394 }
389395 if (match.equals(bugCode)) {
390396 matching.add(warning.getPrimarySourceLineAnnotation());
397 matching.addAll(warning.getAnotherInstanceSourceLineAnnotations());
398
391399 }
392400 }
393401 }
394402 return matching;
395403 }
396404
405 @Override
397406 public void finishPass() {
398407 HashSet<BugPattern> claimedReported = new HashSet<BugPattern>();
399 for (DetectorFactory d : DetectorFactoryCollection.instance().getFactories())
408 for (DetectorFactory d : DetectorFactoryCollection.instance().getFactories()) {
400409 claimedReported.addAll(d.getReportedBugPatterns());
410 }
401411 for (BugPattern b : DetectorFactoryCollection.instance().getBugPatterns()) {
402412 String category = b.getCategory();
403 if (!b.isDeprecated() && !category.equals("EXPERIMENTAL") && !claimedReported.contains(b))
413 if (!b.isDeprecated() && !"EXPERIMENTAL".equals(category) && !claimedReported.contains(b)) {
404414 AnalysisContext.logError("No detector claims " + b.getType());
405 }
406
407 }
408
415 }
416 }
417
418 }
419
420 @Override
409421 public String getDetectorClassName() {
410422 return CheckExpectedWarnings.class.getName();
411423 }
4040 @Override
4141 public void visitJavaClass(JavaClass obj) {
4242 JCIPAnnotationDatabase jcipAnotationDatabase = AnalysisContext.currentAnalysisContext().getJCIPAnnotationDatabase();
43 if (jcipAnotationDatabase.hasClassAnnotation(obj.getClassName().replace('/', '.'), "Immutable"))
43 if (jcipAnotationDatabase.hasClassAnnotation(obj.getClassName().replace('/', '.'), "Immutable")) {
4444 super.visitJavaClass(obj);
45 }
4546 }
4647
4748 @Override
4849 public void visit(Field obj) {
49 if (!obj.isFinal() && !obj.isTransient() && !obj.isVolatile())
50 if (!obj.isFinal() && !obj.isTransient() && !obj.isVolatile()) {
5051 bugReporter.reportBug(new BugInstance(this, "JCIP_FIELD_ISNT_FINAL_IN_IMMUTABLE_CLASS", NORMAL_PRIORITY).addClass(
5152 this).addVisitedField(this));
53 }
5254 }
5355
56 @Override
5457 public void report() {
5558
5659 }
5760
61 @Override
5862 public void visitClassContext(ClassContext classContext) {
5963 classContext.getJavaClass().accept(this);
6064
1818
1919 package edu.umd.cs.findbugs.detect;
2020
21 import static edu.umd.cs.findbugs.ba.NullnessAnnotation.CHECK_FOR_NULL;
22 import static edu.umd.cs.findbugs.ba.NullnessAnnotation.NONNULL;
21 import static edu.umd.cs.findbugs.ba.NullnessAnnotation.*;
2322 import static org.objectweb.asm.Opcodes.ACC_STATIC;
2423
2524 import java.util.Arrays;
5756 import edu.umd.cs.findbugs.classfile.Global;
5857 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
5958 import edu.umd.cs.findbugs.classfile.analysis.AnnotationValue;
59 import edu.umd.cs.findbugs.classfile.engine.asm.FindBugsASM;
6060 import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName;
6161 import edu.umd.cs.findbugs.util.ClassName;
6262
122122
123123 private Map<Integer, NullnessAnnotation> nonNullParameter;
124124
125 private boolean checkForNullReturn;
125 private boolean relaxedNullReturn;
126126
127127 DetectorNode(int access, String name, String desc, String signature, String[] exceptions, XMethod xmethod) {
128 super(access, name, desc, signature, exceptions);
128 super(FindBugsASM.ASM_VERSION, access, name, desc, signature, exceptions);
129129 this.xmethod = xmethod;
130130 }
131131
132 @SuppressWarnings("unchecked")
133132 @Override
134133 public void visitEnd() {
135134 super.visitEnd();
136135 // 1 test if we have suspicious annotations on method or parameters
137 checkForNullReturn = containsCheckForNull(visibleAnnotations);
138 if(!checkForNullReturn){
139 checkForNullReturn = containsCheckForNull(invisibleAnnotations);
140 }
141 boolean needsCheck = checkForNullReturn;
136 relaxedNullReturn = containsRelaxedNonNull(visibleAnnotations);
137 if(!relaxedNullReturn){
138 relaxedNullReturn = containsRelaxedNonNull(invisibleAnnotations);
139 }
140 boolean needsCheck = relaxedNullReturn;
142141 if (invisibleParameterAnnotations != null || visibleParameterAnnotations != null) {
143142 nonNullParameter = getNonnullOrNullableParams(visibleParameterAnnotations);
144143 Map<Integer, NullnessAnnotation> nnp = getNonnullOrNullableParams(invisibleParameterAnnotations);
185184
186185 private final boolean checkMethod(@Nonnull XMethod method) {
187186 boolean foundAny = false;
188 if (checkForNullReturn && containsNullness(method.getAnnotations(), NONNULL)) {
187 if (relaxedNullReturn && containsNullness(method.getAnnotations(), NONNULL)) {
189188 BugInstance bug = new BugInstance(CheckRelaxingNullnessAnnotation.this, "NP_METHOD_RETURN_RELAXING_ANNOTATION",
190189 HIGH_PRIORITY);
191190 bug.addClassAndMethod(xmethod);
198197 if (containsNullness(method.getParameterAnnotations(i), CHECK_FOR_NULL)) {
199198 NullnessAnnotation a = e.getValue();
200199 BugInstance bug = new BugInstance(CheckRelaxingNullnessAnnotation.this,
201 "NP_METHOD_PARAMETER_TIGHTENS_ANNOTATION", a.equals(NONNULL) ? HIGH_PRIORITY : NORMAL_PRIORITY);
202 bug.addClassAndMethod(xmethod);
200 "NP_METHOD_PARAMETER_TIGHTENS_ANNOTATION", a.equals(NONNULL) ? HIGH_PRIORITY : NORMAL_PRIORITY);
201 bug.addClassAndMethod(xmethod);
203202 LocalVariableAnnotation lva = null;
204203 if (localVariables != null) {
205 for(LocalVariableNode lvn : (List<LocalVariableNode>)localVariables) {
204 for(LocalVariableNode lvn : localVariables) {
206205 if (lvn.index == i+1) {
207206 lva = new LocalVariableAnnotation(lvn.name, i+1, 0);
208207 lva.setDescription(LocalVariableAnnotation.PARAMETER_NAMED_ROLE);
261260 }
262261 }
263262
264 static boolean containsCheckForNull(@CheckForNull List<AnnotationNode> methodAnnotations) {
263 static boolean containsRelaxedNonNull(@CheckForNull List<AnnotationNode> methodAnnotations) {
265264 if (methodAnnotations == null) {
266265 return false;
267266 }
268267 for (AnnotationNode annotation : methodAnnotations) {
269268 NullnessAnnotation nullness = getNullness(annotation.desc);
270 if (nullness == CHECK_FOR_NULL) {
269 if (nullness == CHECK_FOR_NULL || nullness == NULLABLE) {
271270 return true;
272271 }
273272 }
123123 int size = allKnownTypeQualifiers.size();
124124 if (size == 1) {
125125 TypeQualifierValue<?> value = Util.first(allKnownTypeQualifiers);
126 if (!value.typeQualifier.getClassName().equals(NONNULL_ANNOTATION))
126 if (!value.typeQualifier.getClassName().equals(NONNULL_ANNOTATION)) {
127127 shouldRunAnalysis = true;
128
129 } else if (size > 1)
128 }
129
130 } else if (size > 1) {
130131 shouldRunAnalysis = true;
131 }
132
133 if (shouldRunAnalysis)
132 }
133 }
134
135 if (shouldRunAnalysis) {
134136 super.visitClass(classDescriptor);
137 }
135138 }
136139
137140 /*
147150 return;
148151 }
149152
150 if (methodDescriptor.getName().startsWith("access$"))
153 if (methodDescriptor.getName().startsWith("access$")) {
151154 return;
155 }
152156
153157 XMethod xMethod = XFactory.createXMethod(methodDescriptor);
154158 if (DEBUG) {
157161
158162 Collection<TypeQualifierValue<?>> relevantQualifiers = Analysis.getRelevantTypeQualifiers(methodDescriptor, cfg);
159163 for(Iterator<TypeQualifierValue<?>> i = relevantQualifiers.iterator(); i.hasNext(); ) {
160 if (i.next().getTypeQualifierClassDescriptor().getClassName().equals(NONNULL_ANNOTATION))
164 if (i.next().getTypeQualifierClassDescriptor().getClassName().equals(NONNULL_ANNOTATION)) {
161165 i.remove();
162 }
163 if (relevantQualifiers.isEmpty())
166 }
167 }
168 if (relevantQualifiers.isEmpty()) {
164169 return;
170 }
165171 if (DEBUG) {
166172 System.out.println(" Relevant type qualifiers are " + relevantQualifiers);
167173 }
172178 BackwardTypeQualifierDataflowFactory.class, methodDescriptor);
173179 ValueNumberDataflow vnaDataflow = analysisCache.getMethodAnalysis(ValueNumberDataflow.class, methodDescriptor);
174180
175 for (TypeQualifierValue typeQualifierValue : relevantQualifiers) {
181 for (TypeQualifierValue<?> typeQualifierValue : relevantQualifiers) {
176182
177183 try {
178184 checkQualifier(xMethod, cfg, typeQualifierValue, forwardDataflowFactory, backwardDataflowFactory,
206212 * @param vnaDataflow
207213 * ValueNumberDataflow for the method
208214 */
209 private void checkQualifier(XMethod xmethod, CFG cfg, TypeQualifierValue typeQualifierValue,
215 private void checkQualifier(XMethod xmethod, CFG cfg, TypeQualifierValue<?> typeQualifierValue,
210216 ForwardTypeQualifierDataflowFactory forwardDataflowFactory,
211217 BackwardTypeQualifierDataflowFactory backwardDataflowFactory, ValueNumberDataflow vnaDataflow)
212 throws CheckedAnalysisException {
218 throws CheckedAnalysisException {
213219
214220 if (DEBUG) {
215221 System.out.println("----------------------------------------------------------------------");
226232 DataflowCFGPrinter<ValueNumberFrame, ValueNumberAnalysis> p = new DataflowCFGPrinter<ValueNumberFrame, ValueNumberAnalysis>(vnaDataflow);
227233 p.print(System.out);
228234 }
229
235
230236 ForwardTypeQualifierDataflow forwardDataflow = forwardDataflowFactory.getDataflow(typeQualifierValue);
231237
232 if (DEBUG_DATAFLOW && (DEBUG_DATAFLOW_MODE.startsWith("forward") || DEBUG_DATAFLOW_MODE.equals("both"))) {
238 if (DEBUG_DATAFLOW && (DEBUG_DATAFLOW_MODE.startsWith("forward") || "both".equals(DEBUG_DATAFLOW_MODE))) {
233239 System.out.println("********* Forwards analysis *********");
234240 DataflowCFGPrinter<TypeQualifierValueSet, ForwardTypeQualifierDataflowAnalysis> p = new DataflowCFGPrinter<TypeQualifierValueSet, ForwardTypeQualifierDataflowAnalysis>(
235241 forwardDataflow);
238244
239245 BackwardTypeQualifierDataflow backwardDataflow = backwardDataflowFactory.getDataflow(typeQualifierValue);
240246
241 if (DEBUG_DATAFLOW && (DEBUG_DATAFLOW_MODE.startsWith("backward") || DEBUG_DATAFLOW_MODE.equals("both"))) {
247 if (DEBUG_DATAFLOW && (DEBUG_DATAFLOW_MODE.startsWith("backward") || "both".equals(DEBUG_DATAFLOW_MODE))) {
242248 System.out.println("********* Backwards analysis *********");
243249 DataflowCFGPrinter<TypeQualifierValueSet, BackwardTypeQualifierDataflowAnalysis> p = new DataflowCFGPrinter<TypeQualifierValueSet, BackwardTypeQualifierDataflowAnalysis>(
244250 backwardDataflow);
249255 checkValueSources(xmethod, cfg, typeQualifierValue, vnaDataflow, forwardDataflow, backwardDataflow);
250256 }
251257
252 private void checkDataflow(XMethod xmethod, CFG cfg, TypeQualifierValue typeQualifierValue,
258 private void checkDataflow(XMethod xmethod, CFG cfg, TypeQualifierValue<?> typeQualifierValue,
253259 ValueNumberDataflow vnaDataflow, ForwardTypeQualifierDataflow forwardDataflow,
254260 BackwardTypeQualifierDataflow backwardDataflow) throws DataflowAnalysisException, CheckedAnalysisException {
255261 for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
310316 }
311317
312318
313 private void checkForEqualityTest(XMethod xmethod, CFG cfg, TypeQualifierValue typeQualifierValue,
319 private void checkForEqualityTest(XMethod xmethod, CFG cfg, TypeQualifierValue<?> typeQualifierValue,
314320 TypeQualifierValueSet forwardsFact, Location loc, ValueNumberFrame factAtLocation) throws DataflowAnalysisException {
315321 InstructionHandle handle = loc.getHandle();
316322 Instruction ins = handle.getInstruction();
321327 isTest = true;
322328 } else if (ins instanceof InvokeInstruction && ins.consumeStack(cpg) == 2) {
323329 InvokeInstruction invoke = (InvokeInstruction) ins;
324 isTest = invoke.getMethodName(cpg).equals("equals") &&invoke.getSignature(cpg).equals("(Ljava/lang/Object;)Z") ;
330 isTest = "equals".equals(invoke.getMethodName(cpg)) &&"(Ljava/lang/Object;)Z".equals(invoke.getSignature(cpg)) ;
325331 }
326332 if (isTest) {
327333 ValueNumber top = factAtLocation.getStackValue(0);
328 if (top.hasFlag(ValueNumber.CONSTANT_VALUE))
334 if (top.hasFlag(ValueNumber.CONSTANT_VALUE)) {
329335 return;
336 }
330337 ValueNumber next = factAtLocation.getStackValue(1);
331 if (next.hasFlag(ValueNumber.CONSTANT_VALUE))
338 if (next.hasFlag(ValueNumber.CONSTANT_VALUE)) {
332339 return;
340 }
333341 FlowValue topTQ = forwardsFact.getValue(top);
334342 FlowValue nextTQ = forwardsFact.getValue(next);
335343 if (DEBUG) {
336344 System.out.println("Comparing values at " + loc.toCompactString());
337345 System.out.println(" Comparing " + topTQ + " and " + nextTQ);
338346 }
339 if (topTQ.equals(nextTQ)) return;
347 if (topTQ.equals(nextTQ)) {
348 return;
349 }
340350 if (FlowValue.valuesConflict(typeQualifierValue.isStrictQualifier() && !xmethod.isIdentity(), topTQ, nextTQ)) {
341351 BugInstance warning = new BugInstance(this,"TQ_COMPARING_VALUES_WITH_INCOMPATIBLE_TYPE_QUALIFIERS", HIGH_PRIORITY).addClassAndMethod(xmethod);
342352 annotateWarningWithTypeQualifier(warning, typeQualifierValue);
343 for(SourceSinkInfo s : forwardsFact.getWhere(top))
344 annotateWarningWithSourceSinkInfo(warning, xmethod, top, s);
345 for(SourceSinkInfo s : forwardsFact.getWhere(next))
353 for(SourceSinkInfo s : forwardsFact.getWhere(top)) {
354 annotateWarningWithSourceSinkInfo(warning, xmethod, top, s);
355 }
356 for(SourceSinkInfo s : forwardsFact.getWhere(next)) {
346357 annotateWarningWithSourceSinkInfo(warning, xmethod, next, s);
358 }
347359 SourceLineAnnotation observedLocation = SourceLineAnnotation.fromVisitedInstruction(xmethod.getMethodDescriptor(),
348360 loc);
349361 warning.add(observedLocation);
357369
358370 }
359371
360 private void checkValueSources(XMethod xMethod, CFG cfg, TypeQualifierValue typeQualifierValue,
372 private void checkValueSources(XMethod xMethod, CFG cfg, TypeQualifierValue<?> typeQualifierValue,
361373 ValueNumberDataflow vnaDataflow, ForwardTypeQualifierDataflow forwardDataflow,
362374 BackwardTypeQualifierDataflow backwardDataflow) throws DataflowAnalysisException, CheckedAnalysisException {
363375
400412 vn, location);
401413 } else if (source.getWhen() == When.UNKNOWN && source.getType() == SourceSinkType.PARAMETER) {
402414
403 int p = source.getParameter();
415 int p = source.getParameter();
404416 TypeQualifierAnnotation directTypeQualifierAnnotation = TypeQualifierApplications
405417 .getDirectTypeQualifierAnnotation(xMethod, p, typeQualifierValue);
406418 if (directTypeQualifierAnnotation != null && directTypeQualifierAnnotation.when == When.UNKNOWN) {
456468 }
457469
458470 private void checkForConflictingValues(XMethod xMethod, CFG cfg,
459 TypeQualifierValue typeQualifierValue, TypeQualifierValueSet forwardsFact, TypeQualifierValueSet backwardsFact,
471 TypeQualifierValue<?> typeQualifierValue, TypeQualifierValueSet forwardsFact, TypeQualifierValueSet backwardsFact,
460472 Location locationToReport, Location locationWhereDoomedValueIsObserved, ValueNumberFrame vnaFrame) throws CheckedAnalysisException {
461473 Set<ValueNumber> valueNumberSet = new HashSet<ValueNumber>();
462474 valueNumberSet.addAll(forwardsFact.getValueNumbers());
465477 for (ValueNumber vn : valueNumberSet) {
466478 FlowValue forward = forwardsFact.getValue(vn);
467479 FlowValue backward = backwardsFact.getValue(vn);
468 if (!FlowValue.valuesConflict(typeQualifierValue.isStrictQualifier() && !xMethod.isIdentity(), forward, backward))
480 if (!FlowValue.valuesConflict(typeQualifierValue.isStrictQualifier() && !xMethod.isIdentity(), forward, backward)) {
469481 continue;
482 }
470483
471484 if (DEBUG) {
472485 System.out.println("Check " + vn + ": forward=" + forward + ", backward=" + backward + " at " + checkLocation);
473 forwardsFact.getValue(vn);
474 backwardsFact.getValue(vn);
475486 }
476487
477488 emitDataflowWarning(xMethod, typeQualifierValue, forwardsFact, backwardsFact, vn, forward, backward,
480491 }
481492 }
482493
483 private void emitDataflowWarning(XMethod xMethod, TypeQualifierValue typeQualifierValue,
494 private void emitDataflowWarning(XMethod xMethod, TypeQualifierValue<?> typeQualifierValue,
484495 TypeQualifierValueSet forwardsFact, TypeQualifierValueSet backwardsFact, ValueNumber vn, FlowValue forward,
485496 FlowValue backward, Location locationToReport, @CheckForNull Location locationWhereDoomedValueIsObserved, ValueNumberFrame vnaFrame)
486 throws CheckedAnalysisException {
497 throws CheckedAnalysisException {
487498 String bugType;
488 if (typeQualifierValue.isStrictQualifier() && forward == FlowValue.UNKNOWN)
499 if (typeQualifierValue.isStrictQualifier() && forward == FlowValue.UNKNOWN) {
489500 bugType = "TQ_UNKNOWN_VALUE_USED_WHERE_ALWAYS_STRICTLY_REQUIRED";
490 else if (backward == FlowValue.NEVER)
501 } else if (backward == FlowValue.NEVER) {
491502 bugType = "TQ_ALWAYS_VALUE_USED_WHERE_NEVER_REQUIRED";
492 else
503 } else {
493504 bugType = "TQ_NEVER_VALUE_USED_WHERE_ALWAYS_REQUIRED";
505 }
494506
495507 // Issue warning
496508 BugInstance warning = new BugInstance(this, bugType, Priorities.NORMAL_PRIORITY).addClassAndMethod(xMethod);
546558 bugReporter.reportBug(warning);
547559 }
548560
549 private void emitSourceWarning(String bugType, XMethod xMethod, TypeQualifierValue typeQualifierValue,
561 private void emitSourceWarning(String bugType, XMethod xMethod, TypeQualifierValue<?> typeQualifierValue,
550562 FlowValue backwardsFlowValue, TypeQualifierValueSet backwardsFact, SourceSinkInfo source, ValueNumber vn,
551563 Location location) {
552564
564576 bugReporter.reportBug(warning);
565577 }
566578
567 private void annotateWarningWithTypeQualifier(BugInstance warning, TypeQualifierValue typeQualifierValue) {
568 if (TypeQualifierValue.hasMultipleVariants(typeQualifierValue)) {
569 StringBuilder buf = new StringBuilder();
570 buf.append("@");
571 buf.append(typeQualifierValue.typeQualifier.getDottedClassName());
579 private void annotateWarningWithTypeQualifier(BugInstance warning, TypeQualifierValue<?> typeQualifierValue) {
580 if (TypeQualifierValue.hasMultipleVariants(typeQualifierValue)) {
581 StringBuilder buf = new StringBuilder();
582 buf.append("@");
583 buf.append(typeQualifierValue.typeQualifier.getDottedClassName());
572584
573585 // When there are multiple variants, qualify the type
574586 // qualifier with the value indicating which variant.
593605 sourceSinkInfo.getLocal());
594606 lva.setDescription(lva.isSignificant() ? LocalVariableAnnotation.PARAMETER_VALUE_SOURCE_NAMED_ROLE
595607 : LocalVariableAnnotation.PARAMETER_VALUE_SOURCE_ROLE);
596
608
597609 warning.add(lva);
598610 } catch (CheckedAnalysisException e) {
599611 warning.addSourceLine(methodDescriptor, sourceSinkInfo.getLocation()).describe("SOURCE_LINE_VALUE_SOURCE");
606618 warning.addString((String) constantValue).describe(StringAnnotation.STRING_CONSTANT_ROLE);
607619 } else if (constantValue instanceof Integer) {
608620 warning.addInt((Integer) constantValue).describe(IntAnnotation.INT_VALUE);
609 } else if (constantValue == null)
621 } else if (constantValue == null) {
610622 warning.addString("null").describe(StringAnnotation.STRING_NONSTRING_CONSTANT_ROLE);
611 else
623 } else {
612624 warning.addString(constantValue.toString()).describe(StringAnnotation.STRING_NONSTRING_CONSTANT_ROLE);
625 }
613626 break;
614627
615628 case RETURN_VALUE_OF_CALLED_METHOD:
646659 Location getSinkLocation(Iterable<? extends SourceSinkInfo> info) {
647660 for (SourceSinkInfo s : info) {
648661 Location l = getSinkLocation(s);
649 if (l != null)
662 if (l != null) {
650663 return l;
664 }
651665
652666 }
653667 return null;
4444
4545 public class CloneIdiom extends DismantleBytecode implements Detector, StatelessDetector {
4646
47 private ClassDescriptor cloneDescriptor = DescriptorFactory.createClassDescriptor(java.lang.Cloneable.class);
47 private final ClassDescriptor cloneDescriptor = DescriptorFactory.createClassDescriptor(java.lang.Cloneable.class);
4848
4949 boolean isCloneable, hasCloneMethod;
5050
6565 // boolean throwsExceptions;
6666 boolean implementsCloneableDirectly;
6767
68 private BugReporter bugReporter;
68 private final BugReporter bugReporter;
6969
7070 public CloneIdiom(BugReporter bugReporter) {
7171 this.bugReporter = bugReporter;
7272 }
7373
74 @Override
7475 public void visitClassContext(ClassContext classContext) {
7576 classContext.getJavaClass().accept(this);
7677 }
7778
7879 @Override
7980 public void visit(Code obj) {
80 if (getMethodName().equals("clone") && getMethodSig().startsWith("()"))
81 if ("clone".equals(getMethodName()) && getMethodSig().startsWith("()")) {
8182 super.visit(obj);
83 }
8284 }
8385
8486 @Override
8587 public void sawOpcode(int seen) {
86 if (seen == INVOKESPECIAL && getNameConstantOperand().equals("clone") && getSigConstantOperand().startsWith("()")) {
88 if (seen == INVOKESPECIAL && "clone".equals(getNameConstantOperand()) && getSigConstantOperand().startsWith("()")) {
8789 /*
8890 * System.out.println("Saw call to " + nameConstant + ":" +
8991 * sigConstant + " in " + betterMethodName);
100102 isCloneable = false;
101103 check = false;
102104 isFinal = obj.isFinal();
103 if (obj.isInterface())
104 return;
105 if (obj.isAbstract())
106 return;
105 if (obj.isInterface()) {
106 return;
107 }
108 if (obj.isAbstract()) {
109 return;
110 }
107111 // Does this class directly implement Cloneable?
108112 String[] interface_names = obj.getInterfaceNames();
109113 for (String interface_name : interface_names) {
110 if (interface_name.equals("java.lang.Cloneable")) {
114 if ("java.lang.Cloneable".equals(interface_name)) {
111115 implementsCloneableDirectly = true;
112116 isCloneable = true;
113117 break;
116120
117121 Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
118122 try {
119 if (subtypes2.isSubtype(getClassDescriptor(), cloneDescriptor))
123 if (subtypes2.isSubtype(getClassDescriptor(), cloneDescriptor)) {
120124 isCloneable = true;
125 }
121126 if (subtypes2.isSubtype(DescriptorFactory.createClassDescriptorFromDottedClassName(obj.getSuperclassName()),
122 cloneDescriptor))
127 cloneDescriptor)) {
123128 implementsCloneableDirectly = false;
129 }
124130
125131 } catch (ClassNotFoundException e) {
126132 bugReporter.reportMissingClass(e);
134140
135141 @Override
136142 public void visitAfter(JavaClass obj) {
137 if (!check)
138 return;
139 if (cloneOnlyThrowsException)
140 return;
143 if (!check) {
144 return;
145 }
146 if (cloneOnlyThrowsException) {
147 return;
148 }
141149 if (implementsCloneableDirectly && !hasCloneMethod) {
142 if (!referencesCloneMethod)
150 if (!referencesCloneMethod) {
143151 bugReporter.reportBug(new BugInstance(this, "CN_IDIOM", NORMAL_PRIORITY).addClass(this));
152 }
144153 }
145154
146155 if (hasCloneMethod && isCloneable && !invokesSuperClone && !isFinal && obj.isPublic()) {
147156 int priority = LOW_PRIORITY;
148 if (obj.isPublic() || obj.isProtected())
157 if (obj.isPublic() || obj.isProtected()) {
149158 priority = NORMAL_PRIORITY;
159 }
150160 try {
151161 Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
152162 Set<ClassDescriptor> directSubtypes = subtypes2.getDirectSubtypes(getClassDescriptor());
153 if (!directSubtypes.isEmpty())
163 if (!directSubtypes.isEmpty()) {
154164 priority--;
165 }
155166 BugInstance bug = new BugInstance(this, "CN_IDIOM_NO_SUPER_CALL", priority).addClass(this).addMethod(
156167 cloneMethodAnnotation);
157 for (ClassDescriptor d : directSubtypes)
168 for (ClassDescriptor d : directSubtypes) {
158169 bug.addClass(d).describe(ClassAnnotation.SUBCLASS_ROLE);
170 }
159171 bugReporter.reportBug(bug);
160172 } catch (ClassNotFoundException e) {
161173 bugReporter.reportMissingClass(e);
163175
164176 } else if (hasCloneMethod && !isCloneable && !cloneOnlyThrowsException && !cloneIsDeprecated && !obj.isAbstract()) {
165177 int priority = Priorities.NORMAL_PRIORITY;
166 if (referencesCloneMethod)
178 if (referencesCloneMethod) {
167179 priority--;
180 }
168181
169182 bugReporter.reportBug(new BugInstance(this, "CN_IMPLEMENTS_CLONE_BUT_NOT_CLONEABLE", priority).addClass(this)
170183 .addMethod(cloneMethodAnnotation));
176189 public void visit(ConstantNameAndType obj) {
177190 String methodName = obj.getName(getConstantPool());
178191 String methodSig = obj.getSignature(getConstantPool());
179 if (!methodName.equals("clone"))
180 return;
181 if (!methodSig.startsWith("()"))
182 return;
192 if (!"clone".equals(methodName)) {
193 return;
194 }
195 if (!methodSig.startsWith("()")) {
196 return;
197 }
183198 referencesCloneMethod = true;
184199 }
185200
186201 @Override
187202 public void visit(Method obj) {
188 if (obj.isAbstract() || BCELUtil.isSynthetic(obj))
189 return;
190 if (!obj.isPublic())
191 return;
192 if (!getMethodName().equals("clone"))
193 return;
194 if (!getMethodSig().startsWith("()"))
195 return;
203 if (obj.isAbstract() || BCELUtil.isSynthetic(obj)) {
204 return;
205 }
206 if (!obj.isPublic()) {
207 return;
208 }
209 if (!"clone".equals(getMethodName())) {
210 return;
211 }
212 if (!getMethodSig().startsWith("()")) {
213 return;
214 }
196215 hasCloneMethod = true;
197216 cloneIsDeprecated = getXMethod().isDeprecated();
198217 cloneMethodAnnotation = MethodAnnotation.fromVisitedMethod(this);
204223
205224 /*
206225 * (non-Javadoc)
207 *
226 *
208227 * @see edu.umd.cs.findbugs.Detector#report()
209228 */
229 @Override
210230 public void report() {
211231 // do nothing
212232
3838 this.bugReporter = bugReporter;
3939 }
4040
41 @Override
4142 public void visitClassContext(ClassContext classContext) {
4243 classContext.getJavaClass().accept(this);
4344 }
4849 if (Subtypes2.instanceOf(obj, "java.util.Comparator") && !ClassName.isAnonymous(getClassName())
4950 && !Subtypes2.instanceOf(obj, "java.io.Serializable")) {
5051 int priority = NORMAL_PRIORITY;
51 if (obj.isInterface() || obj.isAbstract())
52 if (obj.isInterface() || obj.isAbstract()) {
5253 return;
54 }
5355
5456 double easilySerializable = 1.0;
5557 for (Field f : obj.getFields()) {
5658 try {
57 if (f.getName().startsWith("this$"))
59 if (f.getName().startsWith("this$")) {
5860 return;
61 }
5962 String signature = f.getSignature();
6063 char firstChar = signature.charAt(0);
61 if (firstChar == 'L' || firstChar == '[')
64 if (firstChar == 'L' || firstChar == '[') {
6265 easilySerializable *= DeepSubtypeAnalysis.isDeepSerializable(signature);
66 }
6367 } catch (ClassNotFoundException e) {
6468 easilySerializable = 0.0;
6569 break;
6670 }
6771 }
6872
69 if (easilySerializable < 0.9)
73 if (easilySerializable < 0.9) {
7074 priority = LOW_PRIORITY;
75 }
7176
7277 bugReporter.reportBug(new BugInstance(this, "SE_COMPARATOR_SHOULD_BE_SERIALIZABLE", priority).addClass(this));
7378
7580
7681 }
7782
83 @Override
7884 public void report() {
7985
8086 }
3030
3131 public class ConfusedInheritance extends PreorderVisitor implements Detector {
3232
33 private BugReporter bugReporter;
33 private final BugReporter bugReporter;
3434
3535 private JavaClass cls;
3636
3838 this.bugReporter = bugReporter;
3939 }
4040
41 @Override
4142 public void visitClassContext(ClassContext classContext) {
4243 cls = classContext.getJavaClass();
4344 if (cls.isFinal()) {
5354 }
5455 }
5556
57 @Override
5658 public void report() {
5759 }
5860 }
2121 import org.apache.bcel.classfile.Code;
2222 import org.apache.bcel.classfile.Field;
2323 import org.apache.bcel.classfile.JavaClass;
24 import org.apache.bcel.classfile.LocalVariable;
25 import org.apache.bcel.classfile.LocalVariableTable;
2426
2527 import edu.umd.cs.findbugs.BugAccumulator;
2628 import edu.umd.cs.findbugs.BugInstance;
2729 import edu.umd.cs.findbugs.BugReporter;
28 import edu.umd.cs.findbugs.BytecodeScanningDetector;
2930 import edu.umd.cs.findbugs.ba.XFactory;
3031 import edu.umd.cs.findbugs.ba.XMethod;
32 import edu.umd.cs.findbugs.ba.ch.Subtypes2;
3133 import edu.umd.cs.findbugs.bcel.BCELUtil;
34 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
3235
33 public class ConfusionBetweenInheritedAndOuterMethod extends BytecodeScanningDetector {
36 public class ConfusionBetweenInheritedAndOuterMethod extends OpcodeStackDetector {
3437
3538 BugAccumulator bugAccumulator;
39
40 BugInstance iteratorBug;
3641
3742 public ConfusionBetweenInheritedAndOuterMethod(BugReporter bugReporter) {
3843 this.bugAccumulator = new BugAccumulator(bugReporter);
6166 @Override
6267 public void visit(Code obj) {
6368 if (isInnerClass && !BCELUtil.isSynthetic(getMethod())) {
64 // System.out.println(getFullyQualifiedMethodName());
6569 super.visit(obj);
70 iteratorBug = null;
6671 }
6772 }
6873
6974 @Override
7075 public void sawOpcode(int seen) {
71 // System.out.printf("%3d : %s%n", getPC(), OPCODE_NAMES[seen]);
76 if (iteratorBug != null) {
77 if (isRegisterStore()) {
78 LocalVariableTable lvt = getMethod().getLocalVariableTable();
79 if (lvt != null) {
80 LocalVariable localVariable = lvt.getLocalVariable(getRegisterOperand(), getNextPC());
81 if(localVariable == null || localVariable.getName().endsWith("$")) {
82 // iterator() result is stored to the synthetic variable which has no name in LVT or name is suffixed with '$'
83 // Looks like it's for-each cycle like for(Object obj : this)
84 // Do not report such case
85 iteratorBug = null;
86 }
87 }
88 }
89 if(iteratorBug != null) {
90 bugAccumulator.accumulateBug(iteratorBug, this);
91 }
92 iteratorBug = null;
93 }
7294 if (seen != INVOKEVIRTUAL) {
7395 return;
7496 }
79101 getSigConstantOperand(), false);
80102 if (invokedMethod.isResolved() && invokedMethod.getClassName().equals(getDottedClassConstantOperand())
81103 || invokedMethod.isSynthetic()) {
104 return;
105 }
106 if(getStack().getStackItem(getNumberArguments(getSigConstantOperand())).getRegisterNumber() != 0) {
107 // called not for this object
82108 return;
83109 }
84110 // method is inherited
109135 priority++;
110136 }
111137
112 // System.out.println("Found it");
113 bugAccumulator.accumulateBug(
114 new BugInstance(this, "IA_AMBIGUOUS_INVOCATION_OF_INHERITED_OR_OUTER_METHOD", priority)
115 .addClassAndMethod(this).addMethod(invokedMethod).describe("METHOD_INHERITED")
116 .addMethod(alternativeMethod).describe("METHOD_ALTERNATIVE_TARGET"), this);
138 BugInstance bug = new BugInstance(this, "IA_AMBIGUOUS_INVOCATION_OF_INHERITED_OR_OUTER_METHOD", priority)
139 .addClassAndMethod(this).addMethod(invokedMethod).describe("METHOD_INHERITED")
140 .addMethod(alternativeMethod).describe("METHOD_ALTERNATIVE_TARGET");
141 if(invokedMethod.getName().equals("iterator") && invokedMethod.getSignature().equals("()Ljava/util/Iterator;")
142 && Subtypes2.instanceOf(getDottedClassName(), "java.lang.Iterable")) {
143 iteratorBug = bug;
144 } else {
145 bugAccumulator.accumulateBug(bug, this);
146 }
117147 break;
118148 }
119149 }
120
121150 }
122
123151 }
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2008 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.detect;
20
21 import org.apache.bcel.classfile.Code;
22 import org.apache.bcel.classfile.LocalVariable;
23 import org.apache.bcel.classfile.LocalVariableTable;
24
25 import edu.umd.cs.findbugs.BugAccumulator;
26 import edu.umd.cs.findbugs.BugInstance;
27 import edu.umd.cs.findbugs.BugReporter;
28 import edu.umd.cs.findbugs.OpcodeStack.Item;
29 import edu.umd.cs.findbugs.ba.AnalysisContext;
30 import edu.umd.cs.findbugs.ba.SignatureParser;
31 import edu.umd.cs.findbugs.ba.XClass;
32 import edu.umd.cs.findbugs.ba.XField;
33 import edu.umd.cs.findbugs.ba.ch.Subtypes2;
34 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
35 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
36 import edu.umd.cs.findbugs.classfile.DescriptorFactory;
37 import edu.umd.cs.findbugs.classfile.FieldDescriptor;
38 import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName;
39
40 /**
41 * @author Tagir Valeev
42 */
43 public class CovariantArrayAssignment extends OpcodeStackDetector {
44 private final BugAccumulator accumulator;
45
46 public CovariantArrayAssignment(BugReporter bugReporter) {
47 accumulator = new BugAccumulator(bugReporter);
48 }
49
50 @Override
51 public void visit(Code obj) {
52 super.visit(obj);
53 accumulator.reportAccumulatedBugs();
54 }
55
56 /**
57 * @param superClass
58 * @param subClass
59 * @return true if superClass is abstract or interface and all known non-abstract implementations
60 * are derived from given subClass
61 */
62 private static boolean allImplementationsDerivedFromSubclass(@SlashedClassName String superClass, @SlashedClassName String subClass) {
63 ClassDescriptor superDescriptor = DescriptorFactory.createClassDescriptor(superClass);
64 XClass xClass = AnalysisContext.currentXFactory().getXClass(superDescriptor);
65 if(xClass == null || (!xClass.isInterface() && !xClass.isAbstract())) {
66 return false;
67 }
68 try {
69 ClassDescriptor wantedDescriptor = DescriptorFactory.createClassDescriptor(subClass);
70 Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
71 for (ClassDescriptor subDescriptor : subtypes2.getSubtypes(superDescriptor)) {
72 if (subDescriptor.equals(superDescriptor) || subDescriptor.equals(wantedDescriptor)) {
73 continue;
74 }
75 XClass xSubClass = AnalysisContext.currentXFactory().getXClass(subDescriptor);
76 if (xSubClass == null
77 || (!xSubClass.isAbstract() && !xSubClass.isInterface() && !subtypes2.isSubtype(subDescriptor,
78 wantedDescriptor))) {
79 return false;
80 }
81 }
82 return true;
83 } catch (ClassNotFoundException e) {
84 // unresolved class
85 }
86 return false;
87 }
88
89 @Override
90 public void sawOpcode(int seen) {
91 if ((isRegisterStore() && !isRegisterLoad()) || seen == PUTFIELD || seen == PUTSTATIC || seen == ARETURN) {
92 Item valueItem = getStack().getStackItem(0);
93 if(!valueItem.isNull() && valueItem.isNewlyAllocated() && valueItem.getSignature().startsWith("[L")
94 && !((Integer)0).equals(valueItem.getConstant())) {
95 String valueClass = valueItem.getSignature().substring(2, valueItem.getSignature().length()-1);
96 String arraySignature = null;
97 int priority = LOW_PRIORITY;
98 String pattern = null;
99 FieldDescriptor field = null;
100 if(seen == PUTFIELD || seen == PUTSTATIC) {
101 arraySignature = getSigConstantOperand();
102 pattern = "CAA_COVARIANT_ARRAY_FIELD";
103 field = getFieldDescriptorOperand();
104 if(field instanceof XField) {
105 XField xField = (XField)field;
106 if((xField.isPublic() || xField.isProtected())) {
107 XClass xClass = AnalysisContext.currentXFactory().getXClass(xField.getClassDescriptor());
108 if(xClass != null && xClass.isPublic()) {
109 priority = NORMAL_PRIORITY;
110 }
111 }
112 }
113 } else if(seen == ARETURN) {
114 if(getXMethod().bridgeFrom() == null) {
115 pattern = "CAA_COVARIANT_ARRAY_RETURN";
116 arraySignature = new SignatureParser(getMethodSig()).getReturnTypeSignature();
117 if (!arraySignature.equals("[Ljava/lang/Object;")
118 && (getXMethod().isPublic() || getXMethod().isProtected()) && getXClass().isPublic()) {
119 priority = NORMAL_PRIORITY;
120 }
121 }
122 } else {
123 LocalVariableTable lvt = getMethod().getLocalVariableTable();
124 if(lvt != null) {
125 LocalVariable localVariable = lvt.getLocalVariable(getRegisterOperand(), getNextPC());
126 if(localVariable != null) {
127 pattern = "CAA_COVARIANT_ARRAY_LOCAL";
128 arraySignature = localVariable.getSignature();
129 }
130 }
131 }
132 if(arraySignature != null && arraySignature.startsWith("[L")) {
133 String arrayClass = arraySignature.substring(2, arraySignature.length()-1);
134 if(!valueClass.equals(arrayClass)) {
135 if(priority == NORMAL_PRIORITY && allImplementationsDerivedFromSubclass(arrayClass, valueClass)) {
136 priority = LOW_PRIORITY;
137 }
138 BugInstance bug = new BugInstance(this, pattern, priority).addClassAndMethod(this)
139 .addFoundAndExpectedType(valueItem.getSignature(), arraySignature)
140 .addSourceLine(this).addValueSource(valueItem, this);
141 if(field != null) {
142 bug.addField(field);
143 }
144 accumulator.accumulateBug(bug, this);
145 }
146 }
147 }
148 }
149
150 if (seen == AASTORE) {
151 Item valueItem = getStack().getStackItem(0);
152 if(!valueItem.isNull()) {
153 Item arrayItem = getStack().getStackItem(2);
154 String arraySignature = arrayItem.getSignature();
155 String valueSignature = valueItem.getSignature();
156 // if valueSignature is "Ljava/lang/Object;" then OpcodeStack probably could not define actual type at all: skip this case
157 if(arraySignature.startsWith("[L") && valueSignature.startsWith("L") && !valueSignature.equals("Ljava/lang/Object;")) {
158 String arrayClass = arraySignature.substring(2, arraySignature.length()-1);
159 String valueClass = valueSignature.substring(1, valueSignature.length()-1);
160 try {
161 ClassDescriptor valueClassDescriptor = DescriptorFactory.createClassDescriptor(valueClass);
162 ClassDescriptor arrayClassDescriptor = DescriptorFactory.createClassDescriptor(arrayClass);
163 if (!AnalysisContext.currentAnalysisContext().getSubtypes2()
164 .isSubtype(valueClassDescriptor, arrayClassDescriptor)) {
165 int priority = HIGH_PRIORITY; // in this case we may be pretty sure that if this line is executed ArrayStoreException will happen
166 if(AnalysisContext.currentAnalysisContext().getSubtypes2().isSubtype(arrayClassDescriptor, valueClassDescriptor)) {
167 priority = NORMAL_PRIORITY;
168 if(allImplementationsDerivedFromSubclass(valueClass, arrayClass)) {
169 // Every implementation of valueClass also extends arrayClass
170 // In this case ArrayStoreException will never occur in current project
171 // So it's enough that we reported a bug when this array was created
172 priority = IGNORE_PRIORITY;
173 }
174 }
175 BugInstance bug = new BugInstance(this, "CAA_COVARIANT_ARRAY_ELEMENT_STORE", priority).addClassAndMethod(this)
176 .addFoundAndExpectedType(valueSignature, 'L'+arrayClass+';')
177 .addSourceLine(this)
178 .addValueSource(valueItem, this)
179 .addValueSource(arrayItem, this);
180 accumulator.accumulateBug(bug, this);
181 }
182 } catch (ClassNotFoundException e) {
183 // Probably class was not supplied to the analysis: assume that everything is correct
184 }
185 }
186 }
187 }
188 }
189 }
3333 import edu.umd.cs.findbugs.StringAnnotation;
3434 import edu.umd.cs.findbugs.ba.XMethod;
3535 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
36 import edu.umd.cs.findbugs.classfile.Global;
37 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
38 import edu.umd.cs.findbugs.detect.BuildStringPassthruGraph.StringPassthruDatabase;
3639
3740 public class CrossSiteScripting extends OpcodeStackDetector {
3841
3942 final BugReporter bugReporter;
4043
4144 final BugAccumulator accumulator;
45
46 private final Map<MethodDescriptor, int[]> allFileNameStringMethods;
4247
4348 public CrossSiteScripting(BugReporter bugReporter) {
4449 this.bugReporter = bugReporter;
4550 this.accumulator = new BugAccumulator(bugReporter);
51 StringPassthruDatabase database = Global.getAnalysisCache().getDatabase(StringPassthruDatabase.class);
52 allFileNameStringMethods = database.getFileNameStringMethods();
4653 }
4754
4855 Map<String, OpcodeStack.Item> map = new HashMap<String, OpcodeStack.Item>();
6370 assert item.isServletParameterTainted();
6471 String s = item.getHttpParameterName();
6572 int pc = item.getInjectionPC();
66 if (s != null && xmlSafe.matcher(s).matches())
73 if (s != null && xmlSafe.matcher(s).matches()) {
6774 bug.addString(s).describe(StringAnnotation.PARAMETER_NAME_ROLE);
75 }
6876 SourceLineAnnotation thisLine = SourceLineAnnotation.fromVisitedInstruction(this);
6977 if (pc >= 0) {
7078 SourceLineAnnotation source = SourceLineAnnotation.fromVisitedInstruction(this, pc);
71 if (thisLine.getStartLine() != source.getStartLine())
79 if (thisLine.getStartLine() != source.getStartLine()) {
7280 bug.add(source).describe(SourceLineAnnotation.ROLE_GENERATED_AT);
81 }
7382 }
7483
7584 bug.addOptionalLocalVariable(this, item);
8998
9099 OpcodeStack.Item oldTop = top;
91100 top = null;
101 if (seen == INVOKESPECIAL || seen == INVOKESTATIC || seen == INVOKEINTERFACE || seen == INVOKEVIRTUAL) {
102 int[] params = allFileNameStringMethods.get(getMethodDescriptorOperand());
103 if(params != null) {
104 int numArgs = getNumberArguments(getSigConstantOperand());
105 for(int param : params) {
106 OpcodeStack.Item path = stack.getStackItem(numArgs-1-param);
107 if (isTainted(path)) {
108 String bugPattern = taintPriority(path) == Priorities.HIGH_PRIORITY ? "PT_ABSOLUTE_PATH_TRAVERSAL"
109 : "PT_RELATIVE_PATH_TRAVERSAL";
110 annotateAndReport(new BugInstance(this, bugPattern, Priorities.NORMAL_PRIORITY).addClassAndMethod(this)
111 .addCalledMethod(this), path);
112 }
113 }
114 }
115 }
92116 if (seen == INVOKESPECIAL) {
93117 String calledClassName = getClassConstantOperand();
94118 String calledMethodName = getNameConstantOperand();
95119 String calledMethodSig = getSigConstantOperand();
96120
97 if (calledClassName.startsWith("java/io/File") && calledMethodSig.equals("(Ljava/lang/String;)V")) {
98 OpcodeStack.Item path = stack.getStackItem(0);
99 if (isTainted(path)) {
100 String bugPattern = taintPriority(path) == Priorities.HIGH_PRIORITY ? "PT_ABSOLUTE_PATH_TRAVERSAL"
101 : "PT_RELATIVE_PATH_TRAVERSAL";
102 annotateAndReport(new BugInstance(this, bugPattern, Priorities.NORMAL_PRIORITY).addClassAndMethod(this)
103 .addCalledMethod(this), path);
104 }
105
106 }
107
108
109 if (calledClassName.equals("javax/servlet/http/Cookie") && calledMethodName.equals("<init>")
110 && calledMethodSig.equals("(Ljava/lang/String;Ljava/lang/String;)V")) {
121 if ("javax/servlet/http/Cookie".equals(calledClassName) && "<init>".equals(calledMethodName)
122 && "(Ljava/lang/String;Ljava/lang/String;)V".equals(calledMethodSig)) {
111123 OpcodeStack.Item value = stack.getStackItem(0);
112124 OpcodeStack.Item name = stack.getStackItem(1);
113125 if (value.isServletParameterTainted() || name.isServletParameterTainted()) {
122134 String calledClassName = getClassConstantOperand();
123135 String calledMethodName = getNameConstantOperand();
124136 String calledMethodSig = getSigConstantOperand();
125 if (calledClassName.equals("javax/servlet/http/HttpServletResponse") && calledMethodName.equals("setContentType")) {
137 if ("javax/servlet/http/HttpServletResponse".equals(calledClassName) && "setContentType".equals(calledMethodName)) {
126138 OpcodeStack.Item writing = stack.getStackItem(0);
127139 if ("text/plain".equals(writing.getConstant())) {
128140 isPlainText = true;
129141 }
130 } else if (calledClassName.equals("javax/servlet/http/HttpSession") && calledMethodName.equals("setAttribute")) {
142 } else if ("javax/servlet/http/HttpSession".equals(calledClassName) && "setAttribute".equals(calledMethodName)) {
131143
132144 OpcodeStack.Item value = stack.getStackItem(0);
133145 OpcodeStack.Item name = stack.getStackItem(1);
134146 Object nameConstant = name.getConstant();
135 if (nameConstant instanceof String)
147 if (nameConstant instanceof String) {
136148 map.put((String) nameConstant, value);
137 } else if (calledClassName.equals("javax/servlet/http/HttpSession") && calledMethodName.equals("getAttribute")) {
149 }
150 } else if ("javax/servlet/http/HttpSession".equals(calledClassName) && "getAttribute".equals(calledMethodName)) {
138151 OpcodeStack.Item name = stack.getStackItem(0);
139152 Object nameConstant = name.getConstant();
140153 if (nameConstant instanceof String) {
144157 replaceTop = top;
145158 }
146159 }
147 } else if (calledClassName.equals("javax/servlet/http/HttpServletResponse")
160 } else if ("javax/servlet/http/HttpServletResponse".equals(calledClassName)
148161 && (calledMethodName.startsWith("send") || calledMethodName.endsWith("Header"))
149162 && calledMethodSig.endsWith("Ljava/lang/String;)V")) {
150163
151164 OpcodeStack.Item writing = stack.getStackItem(0);
152165 if (isTainted(writing)) {
153 if (calledMethodName.equals("sendError"))
166 if ("sendError".equals(calledMethodName)) {
154167 annotateAndReport(
155168 new BugInstance(this, "XSS_REQUEST_PARAMETER_TO_SEND_ERROR", taintPriority(writing))
156 .addClassAndMethod(this),
169 .addClassAndMethod(this),
157170 writing);
158 else
171 } else {
159172 annotateAndReport(
160173 new BugInstance(this, "HRS_REQUEST_PARAMETER_TO_HTTP_HEADER", taintPriority(writing))
161 .addClassAndMethod(this),
174 .addClassAndMethod(this),
162175 writing);
176 }
163177 }
164178 }
165179
168182 String calledMethodName = getNameConstantOperand();
169183 String calledMethodSig = getSigConstantOperand();
170184
171 if ((calledMethodName.startsWith("print") || calledMethodName.equals("write"))
172 && calledClassName.equals("javax/servlet/jsp/JspWriter")
173 && (calledMethodSig.equals("(Ljava/lang/Object;)V") || calledMethodSig.equals("(Ljava/lang/String;)V"))) {
185 if ((calledMethodName.startsWith("print") || "write".equals(calledMethodName))
186 && "javax/servlet/jsp/JspWriter".equals(calledClassName)
187 && ("(Ljava/lang/Object;)V".equals(calledMethodSig) || "(Ljava/lang/String;)V".equals(calledMethodSig))) {
174188 OpcodeStack.Item writing = stack.getStackItem(0);
175189 // System.out.println(SourceLineAnnotation.fromVisitedInstruction(this)
176190 // + " writing " + writing);
177 if (isTainted(writing))
191 if (isTainted(writing)) {
178192 annotateAndReport(
179193 new BugInstance(this, "XSS_REQUEST_PARAMETER_TO_JSP_WRITER", taintPriority(writing))
180 .addClassAndMethod(this),
194 .addClassAndMethod(this),
181195 writing);
182 else if (isTainted(oldTop))
196 } else if (isTainted(oldTop)) {
183197 annotateAndReport(
184198 new BugInstance(this, "XSS_REQUEST_PARAMETER_TO_JSP_WRITER", Priorities.NORMAL_PRIORITY)
185 .addClassAndMethod(this),
199 .addClassAndMethod(this),
186200 oldTop);
201 }
187202 } else if (calledClassName.startsWith("java/io/") && calledClassName.endsWith("Writer")
188203 && (calledMethodName.startsWith("print") || calledMethodName.startsWith("write"))
189 && (calledMethodSig.equals("(Ljava/lang/Object;)V") || calledMethodSig.equals("(Ljava/lang/String;)V"))) {
204 && ("(Ljava/lang/Object;)V".equals(calledMethodSig) || "(Ljava/lang/String;)V".equals(calledMethodSig))) {
190205 OpcodeStack.Item writing = stack.getStackItem(0);
191206 OpcodeStack.Item writingTo = stack.getStackItem(1);
192 if (isTainted(writing) && writingTo.isServletWriter())
207 if (isTainted(writing) && writingTo.isServletWriter()) {
193208 annotateAndReport(
194209 new BugInstance(this, "XSS_REQUEST_PARAMETER_TO_SERVLET_WRITER", taintPriority(writing))
195 .addClassAndMethod(this),
210 .addClassAndMethod(this),
196211 writing);
197 else if (isTainted(oldTop) && writingTo.isServletWriter())
212 } else if (isTainted(oldTop) && writingTo.isServletWriter()) {
198213 annotateAndReport(
199214 new BugInstance(this, "XSS_REQUEST_PARAMETER_TO_SERVLET_WRITER", Priorities.NORMAL_PRIORITY)
200 .addClassAndMethod(this),
215 .addClassAndMethod(this),
201216 writing);
217 }
202218
203219 }
204220 }
205221 }
206222
207223 private boolean isTainted(OpcodeStack.Item writing) {
208 if (writing == null)
209 return false;
224 if (writing == null) {
225 return false;
226 }
210227 return writing.isServletParameterTainted();
211228 }
212229
230 /*
213231 private boolean isDirectTaint(OpcodeStack.Item writing) {
214232 if (writing == null)
215233 return false;
223241 String clsName = m.getClassName();
224242 return clsName.equals("javax/servlet/http/HttpServletRequest") || clsName.equals("javax/servlet/http/ServletRequest");
225243 }
244 */
245
226246 private int taintPriority(OpcodeStack.Item writing) {
227 if (writing == null)
247 if (writing == null) {
228248 return Priorities.NORMAL_PRIORITY;
249 }
229250 XMethod method = writing.getReturnValueOf();
230 if (method != null && method.getName().equals("getParameter")
231 && method.getClassName().equals("javax.servlet.http.HttpServletRequest"))
251 if (method != null && "getParameter".equals(method.getName())
252 && "javax.servlet.http.HttpServletRequest".equals(method.getClassName())) {
232253 return Priorities.HIGH_PRIORITY;
254 }
233255 return Priorities.NORMAL_PRIORITY;
234
235 }
236
237
256 }
238257
239258 }
44
55 /**
66 * Warning property for FindDeadLocalStores.
7 *
7 *
88 * @author David Hovemeyer
99 */
1010 public class DeadLocalStoreProperty extends AbstractWarningProperty {
7676 protected void addMethodAnnotation(@DottedClassName String cName, String mName, String mSig, boolean isStatic,
7777 DefaultEncodingAnnotation annotation) {
7878 super.addMethodAnnotation(cName, mName, mSig, isStatic, annotation);
79 classes.add(DescriptorFactory.createClassDescriptorFromDottedClassName(cName));
79 classes.add(DescriptorFactory.createClassDescriptorFromDottedClassName(cName));
8080
8181 }
8282
173173 bugAccumulator.accumulateBug(new BugInstance(this, "DM_DEFAULT_ENCODING", HIGH_PRIORITY).addClassAndMethod(this)
174174 .addCalledMethod(this), this);
175175 }
176 break;
177 default:
178 break;
176179 }
177180 }
178181 }
4949
5050 @Override
5151 public void visit(Code obj) {
52 if (isDoPrivileged && getMethodName().equals("run"))
52 if (isDoPrivileged && "run".equals(getMethodName())) {
5353 return;
54 if (getMethod().isPrivate())
54 }
55 if (getMethod().isPrivate()) {
5556 return;
56 if (DumbMethods.isTestMethod(getMethod()))
57 }
58 if (DumbMethods.isTestMethod(getMethod())) {
5759 return;
60 }
5861 super.visit(obj);
5962 bugAccumulator.reportAccumulatedBugs();
6063 }
6164
6265 @Override
6366 public void sawOpcode(int seen) {
64 if (seen == INVOKEVIRTUAL && getNameConstantOperand().equals("setAccessible")) {
67 if (seen == INVOKEVIRTUAL && "setAccessible".equals(getNameConstantOperand())) {
6568 @DottedClassName
6669 String className = getDottedClassConstantOperand();
67 if (className.equals("java.lang.reflect.Field") || className.equals("java.lang.reflect.Method"))
70 if ("java.lang.reflect.Field".equals(className) || "java.lang.reflect.Method".equals(className)) {
6871 bugAccumulator.accumulateBug(
6972 new BugInstance(this, "DP_DO_INSIDE_DO_PRIVILEGED", LOW_PRIORITY).addClassAndMethod(this)
70 .addCalledMethod(this), this);
73 .addCalledMethod(this), this);
74 }
7175
7276 }
7377 if (seen == NEW) {
7478 @DottedClassName
7579 String classOfConstructedClass = getDottedClassConstantOperand();
7680 if (Subtypes2.instanceOf(classOfConstructedClass, "java.lang.ClassLoader")
77 && !(getMethodName().equals("main") && getMethodSig().equals("([Ljava/lang/String;)V") && getMethod()
78 .isStatic()))
81 && !("main".equals(getMethodName()) && "([Ljava/lang/String;)V".equals(getMethodSig()) && getMethod()
82 .isStatic())) {
7983 bugAccumulator.accumulateBug(new BugInstance(this, "DP_CREATE_CLASSLOADER_INSIDE_DO_PRIVILEGED", NORMAL_PRIORITY)
80 .addClassAndMethod(this).addClass(classOfConstructedClass), this);
84 .addClassAndMethod(this).addClass(classOfConstructedClass), this);
85 }
8186 }
8287
8388 }
4343
4444 public DontCatchIllegalMonitorStateException(BugReporter bugReporter) {
4545 this.bugReporter = bugReporter;
46 if (DEBUG)
46 if (DEBUG) {
4747 msgs = new HashSet<String>();
48 }
4849 }
4950
5051 @Override
5152 public void visit(ExceptionTable obj) {
5253 if (DEBUG) {
5354 String names[] = obj.getExceptionNames();
54 for (String name : names)
55 if (name.equals("java.lang.Exception") || name.equals("java.lang.Throwable"))
55 for (String name : names) {
56 if ("java.lang.Exception".equals(name) || "java.lang.Throwable".equals(name)) {
5657 System.out.println(name + " thrown by " + getFullyQualifiedMethodName());
58 }
59 }
5760 }
5861 }
5962
6063 @Override
6164 public void visit(CodeException obj) {
6265 int type = obj.getCatchType();
63 if (type == 0)
66 if (type == 0) {
6467 return;
68 }
6569 String name = getConstantPool().constantToString(getConstantPool().getConstant(type));
6670 if (DEBUG) {
6771 String msg = "Catching " + name + " in " + getFullyQualifiedMethodName();
68 if (msgs.add(msg))
72 if (msgs.add(msg)) {
6973 System.out.println(msg);
74 }
7075 }
71 if (name.equals("java.lang.IllegalMonitorStateException"))
76 if ("java.lang.IllegalMonitorStateException".equals(name)) {
7277 bugReporter.reportBug(new BugInstance(this, "IMSE_DONT_CATCH_IMSE", HIGH_PRIORITY).addClassAndMethod(this)
7378 .addSourceLine(this.classContext, this, obj.getHandlerPC()));
79 }
7480
7581 }
7682
83 @Override
7784 public void visitClassContext(ClassContext classContext) {
7885 this.classContext = classContext;
7986 classContext.getJavaClass().accept(this);
8087 }
8188
89 @Override
8290 public void report() {
8391 }
8492 }
3131 import org.apache.bcel.classfile.Method;
3232 import org.apache.bcel.generic.ArrayType;
3333 import org.apache.bcel.generic.ConstantPoolGen;
34 import org.apache.bcel.generic.IFNONNULL;
35 import org.apache.bcel.generic.IFNULL;
3634 import org.apache.bcel.generic.INVOKESTATIC;
3735 import org.apache.bcel.generic.Instruction;
3836 import org.apache.bcel.generic.InstructionHandle;
4947 import edu.umd.cs.findbugs.Detector;
5048 import edu.umd.cs.findbugs.Priorities;
5149 import edu.umd.cs.findbugs.SourceLineAnnotation;
52 import edu.umd.cs.findbugs.SystemProperties;
5350 import edu.umd.cs.findbugs.TypeAnnotation;
5451 import edu.umd.cs.findbugs.ba.AnalysisContext;
5552 import edu.umd.cs.findbugs.ba.CFG;
6360 import edu.umd.cs.findbugs.ba.XField;
6461 import edu.umd.cs.findbugs.ba.ch.Subtypes2;
6562 import edu.umd.cs.findbugs.ba.type.TypeDataflow;
63 import edu.umd.cs.findbugs.ba.type.TypeFrame;
6664 import edu.umd.cs.findbugs.ba.vna.ValueNumber;
6765 import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow;
6866 import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;
7270 import edu.umd.cs.findbugs.classfile.DescriptorFactory;
7371 import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
7472 import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName;
73 import edu.umd.cs.findbugs.util.ClassName;
7574
7675 public class DontIgnoreResultOfPutIfAbsent implements Detector {
7776
8281 final BugAccumulator accumulator;
8382
8483 final ClassDescriptor concurrentMapDescriptor = DescriptorFactory.createClassDescriptor(ConcurrentMap.class);
84
85 // private final boolean testingEnabled;
8586
8687 public DontIgnoreResultOfPutIfAbsent(BugReporter bugReporter) {
8788 this.bugReporter = bugReporter;
8889 this.accumulator = new BugAccumulator(bugReporter);
89 }
90
91 /*
92 * (non-Javadoc)
93 *
94 * @see edu.umd.cs.findbugs.Detector#report()
95 */
90 // testingEnabled = SystemProperties.getBoolean("report_TESTING_pattern_in_standard_detectors");
91 }
92
93 @Override
9694 public void report() {
97 // TODO Auto-generated method stub
98
99 }
100
95 //
96 }
97
98 @Override
10199 public void visitClassContext(ClassContext classContext) {
102100
103101 JavaClass javaClass = classContext.getJavaClass();
104102 ConstantPool pool = javaClass.getConstantPool();
105103 boolean found = false;
106 for (Constant constantEntry : pool.getConstantPool())
104 for (Constant constantEntry : pool.getConstantPool()) {
107105 if (constantEntry instanceof ConstantNameAndType) {
108106 ConstantNameAndType nt = (ConstantNameAndType) constantEntry;
109 if (nt.getName(pool).equals("putIfAbsent")) {
107 if ("putIfAbsent".equals(nt.getName(pool))) {
110108 found = true;
111109 break;
112110 }
113111 }
114 if (!found)
112 }
113 if (!found) {
115114 return;
115 }
116116
117117 Method[] methodList = javaClass.getMethods();
118118
119119 for (Method method : methodList) {
120120 MethodGen methodGen = classContext.getMethodGen(method);
121 if (methodGen == null)
121 if (methodGen == null) {
122122 continue;
123 }
123124
124125 try {
125126 analyzeMethod(classContext, method);
151152 ClassDescriptor cd = DescriptorFactory.getClassDescriptor((ObjectType) type);
152153 @SlashedClassName
153154 String className = cd.getClassName();
154 if (immutableClassNames.contains(className))
155 if (immutableClassNames.contains(className)) {
155156 return Priorities.LOW_PRIORITY;
157 }
156158
157159 XClass xClass = AnalysisContext.currentXFactory().getXClass(cd);
158 if (xClass == null)
160 if (xClass == null) {
159161 return Priorities.IGNORE_PRIORITY;
162 }
160163 ClassDescriptor superclassDescriptor = xClass.getSuperclassDescriptor();
161164 if (superclassDescriptor != null) {
162165 @SlashedClassName
163166 String superClassName = superclassDescriptor.getClassName();
164 if (superClassName.equals("java/lang/Enum"))
167 if ("java/lang/Enum".equals(superClassName)) {
165168 return Priorities.LOW_PRIORITY;
169 }
166170 }
167171 boolean hasMutableField = false;
168172 boolean hasUpdates = false;
169 for (XField f : xClass.getXFields())
173 for (XField f : xClass.getXFields()) {
170174 if (!f.isStatic()) {
171175 if (!f.isFinal() && !f.isSynthetic()) {
172176 hasMutableField = true;
173 if (unreadFields.isWrittenOutsideOfInitialization(f))
177 if (unreadFields.isWrittenOutsideOfInitialization(f)) {
174178 hasUpdates = true;
179 }
175180 }
176181 String signature = f.getSignature();
177182 if (signature.startsWith("Ljava/util/concurrent") || signature.startsWith("Ljava/lang/StringB")
178183 || signature.charAt(0) == '[' || signature.indexOf("Map") >= 0 || signature.indexOf("List") >= 0
179 || signature.indexOf("Set") >= 0)
184 || signature.indexOf("Set") >= 0) {
180185 hasMutableField = hasUpdates = true;
186 }
181187
182188 }
183
184 if (!hasMutableField && !xClass.isInterface() && !xClass.isAbstract())
189 }
190
191 if (!hasMutableField && !xClass.isInterface() && !xClass.isAbstract()) {
185192 return Priorities.LOW_PRIORITY;
193 }
186194 if (hasUpdates || className.startsWith("java/util") || className.indexOf("Map") >= 0
187 || className.indexOf("List") >= 0)
195 || className.indexOf("List") >= 0) {
188196 return Priorities.HIGH_PRIORITY;
197 }
189198 return Priorities.NORMAL_PRIORITY;
190199
191 } else
200 } else {
192201 return Priorities.IGNORE_PRIORITY;
202 }
193203 }
194204
195205 private void analyzeMethod(ClassContext classContext, Method method) throws DataflowAnalysisException, CFGBuilderException {
196 if (BCELUtil.isSynthetic(method) || (method.getAccessFlags() & Constants.ACC_BRIDGE) == Constants.ACC_BRIDGE)
206 if (BCELUtil.isSynthetic(method) || (method.getAccessFlags() & Constants.ACC_BRIDGE) == Constants.ACC_BRIDGE) {
197207 return;
208 }
198209
199210 if (DEBUG) {
200211 System.out.println(" Analyzing method " + classContext.getJavaClass().getClassName() + "." + method.getName());
219230
220231 if (ins instanceof InvokeInstruction) {
221232 InvokeInstruction invoke = (InvokeInstruction) ins;
222 String className = invoke.getClassName(cpg);
223
224 if (invoke.getMethodName(cpg).equals("putIfAbsent")) {
233 if ("putIfAbsent".equals(invoke.getMethodName(cpg))) {
225234 String signature = invoke.getSignature(cpg);
226 if (signature.equals("(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;")
227 && !(invoke instanceof INVOKESTATIC) && extendsConcurrentMap(className)) {
228 InstructionHandle next = handle.getNext();
229 boolean isIgnored = next != null && next.getInstruction() instanceof POP;
230 boolean isImmediateNullTest = next != null
231 && (next.getInstruction() instanceof IFNULL || next.getInstruction() instanceof IFNONNULL);
232 if (countOtherCalls || isIgnored) {
233 BitSet live = llsaDataflow.getAnalysis().getFactAtLocation(location);
234 ValueNumberFrame vna = vnaDataflow.getAnalysis().getFactAtLocation(location);
235 ValueNumber vn = vna.getTopValue();
236
237 int locals = vna.getNumLocals();
238 boolean isRetained = false;
239 for (int pos = 0; pos < locals; pos++)
240 if (vna.getValue(pos).equals(vn) && live.get(pos)) {
241 BugAnnotation ba = ValueNumberSourceInfo.findAnnotationFromValueNumber(method, location, vn,
242 vnaDataflow.getFactAtLocation(location), "VALUE_OF");
243 if (ba == null)
244 continue;
245 String pattern = "RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORED";
246 if (!isIgnored)
247 pattern = "UNKNOWN";
248 Type type = typeDataflow.getAnalysis().getFactAtLocation(location).getTopValue();
249 int priority = getPriorityForBeingMutable(type);
250 BugInstance bugInstance = new BugInstance(this, pattern, priority)
251 .addClassAndMethod(methodGen, sourceFileName).addCalledMethod(methodGen, invoke)
252 .add(new TypeAnnotation(type)).add(ba);
253 SourceLineAnnotation where = SourceLineAnnotation.fromVisitedInstruction(classContext,
254 method, location);
255 accumulator.accumulateBug(bugInstance, where);
256 isRetained = true;
257 break;
235 if ("(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;".equals(signature)
236 && !(invoke instanceof INVOKESTATIC)) {
237 TypeFrame typeFrame = typeDataflow.getFactAtLocation(location);
238 Type objType = typeFrame.getStackValue(2);
239 if(extendsConcurrentMap(ClassName.toDottedClassName(ClassName.fromFieldSignature(objType.getSignature())))) {
240 InstructionHandle next = handle.getNext();
241 boolean isIgnored = next != null && next.getInstruction() instanceof POP;
242 // boolean isImmediateNullTest = next != null
243 // && (next.getInstruction() instanceof IFNULL || next.getInstruction() instanceof IFNONNULL);
244 if (countOtherCalls || isIgnored) {
245 BitSet live = llsaDataflow.getAnalysis().getFactAtLocation(location);
246 ValueNumberFrame vna = vnaDataflow.getAnalysis().getFactAtLocation(location);
247 ValueNumber vn = vna.getTopValue();
248
249 int locals = vna.getNumLocals();
250 // boolean isRetained = false;
251 for (int pos = 0; pos < locals; pos++) {
252 if (vna.getValue(pos).equals(vn) && live.get(pos)) {
253 BugAnnotation ba = ValueNumberSourceInfo.findAnnotationFromValueNumber(method, location, vn,
254 vnaDataflow.getFactAtLocation(location), "VALUE_OF");
255 if (ba == null) {
256 continue;
257 }
258 String pattern = "RV_RETURN_VALUE_OF_PUTIFABSENT_IGNORED";
259 if (!isIgnored) {
260 pattern = "UNKNOWN";
261 }
262 Type type = typeFrame.getTopValue();
263 int priority = getPriorityForBeingMutable(type);
264 BugInstance bugInstance = new BugInstance(this, pattern, priority)
265 .addClassAndMethod(methodGen, sourceFileName).addCalledMethod(methodGen, invoke)
266 .add(new TypeAnnotation(type)).add(ba);
267 SourceLineAnnotation where = SourceLineAnnotation.fromVisitedInstruction(classContext,
268 method, location);
269 accumulator.accumulateBug(bugInstance, where);
270 // isRetained = true;
271 break;
272 }
258273 }
259 if (countOtherCalls && !isRetained && SystemProperties.getBoolean("report_TESTING_pattern_in_standard_detectors")) {
274 }
275 /*
276 if (testingEnabled && (countOtherCalls && !isRetained)) {
260277 int priority = LOW_PRIORITY;
261278 if (!isImmediateNullTest && !isIgnored) {
262279 TypeDataflow typeAnalysis = classContext.getTypeDataflow(method);
263280 Type type = typeAnalysis.getFactAtLocation(location).getTopValue();
264281 String valueSignature = type.getSignature();
265 if (!valueSignature.startsWith("Ljava/util/concurrent/atomic/Atomic"))
282 if (!valueSignature.startsWith("Ljava/util/concurrent/atomic/Atomic")) {
266283 priority = Priorities.HIGH_PRIORITY;
284 }
267285 }
268286 BugInstance bugInstance = new BugInstance(this, "TESTING", priority)
269 .addClassAndMethod(methodGen, sourceFileName).addString("Counting putIfAbsentCalls")
270 .addCalledMethod(methodGen, invoke);
287 .addClassAndMethod(methodGen, sourceFileName).addString("Counting putIfAbsentCalls")
288 .addCalledMethod(methodGen, invoke);
271289 SourceLineAnnotation where = SourceLineAnnotation.fromVisitedInstruction(classContext, method,
272290 location);
273291 accumulator.accumulateBug(bugInstance, where);
274292 }
275
293 */
276294 }
277 } else if (countOtherCalls) {
295 } /* else if (testingEnabled && countOtherCalls) {
278296 BugInstance bugInstance = new BugInstance(this, "TESTING2", Priorities.NORMAL_PRIORITY)
279 .addClassAndMethod(methodGen, sourceFileName).addCalledMethod(methodGen, invoke);
297 .addClassAndMethod(methodGen, sourceFileName).addCalledMethod(methodGen, invoke);
280298 SourceLineAnnotation where = SourceLineAnnotation.fromVisitedInstruction(classContext, method, location);
281299 accumulator.accumulateBug(bugInstance, where);
282300
283301 }
302 */
284303 }
285304
286305 }
289308 }
290309
291310 private boolean extendsConcurrentMap(@DottedClassName String className) {
292 if (className.equals("java.util.concurrent.ConcurrentHashMap")
293 || className.equals(concurrentMapDescriptor.getDottedClassName()))
311 if ("java.util.concurrent.ConcurrentHashMap".equals(className)
312 || className.equals(concurrentMapDescriptor.getDottedClassName())) {
294313 return true;
314 }
295315 ClassDescriptor c = DescriptorFactory.createClassDescriptorFromDottedClassName(className);
296316 Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
297317
298318 try {
299 if (subtypes2.isSubtype(c, concurrentMapDescriptor))
319 if (subtypes2.isSubtype(c, concurrentMapDescriptor)) {
300320 return true;
321 }
301322 } catch (ClassNotFoundException e) {
302323 AnalysisContext.reportMissingClass(e);
303324 }
5151 }
5252
5353 private boolean isReservedName(String name) {
54 return name.equals("enum") || name.equals("assert");
54 return "enum".equals(name) || "assert".equals(name);
5555 }
5656
5757 @Override
6969 LocalVariableAnnotation var = new LocalVariableAnnotation(obj.getName(), obj.getIndex(), obj.getStartPC());
7070 SourceLineAnnotation source = SourceLineAnnotation.fromVisitedInstruction(getClassContext(), this, obj.getStartPC());
7171 BugInstance bug = new BugInstance(this, "NM_FUTURE_KEYWORD_USED_AS_IDENTIFIER", NORMAL_PRIORITY)
72 .addClassAndMethod(this).add(var).add(source);
72 .addClassAndMethod(this).add(var).add(source);
7373 bugReporter.reportBug(bug);
7474 }
7575 }
2424 /**
2525 * A WarningProperty for warnings that are reported at a "doomed" Location; one
2626 * that cannot return normally.
27 *
27 *
2828 * @author David Hovemeyer
2929 */
3030 public class DoomedCodeWarningProperty extends AbstractWarningProperty {
6666 public DroppedException(BugReporter bugReporter) {
6767 this.bugReporter = bugReporter;
6868 this.bugAccumulator = new BugAccumulator(bugReporter);
69 if (DEBUG)
69 if (DEBUG) {
7070 System.out.println("Dropped Exception debugging turned on");
71 }
72
71 }
72 }
73
74 @Override
7375 public void visitClassContext(ClassContext classContext) {
7476 this.classContext = classContext;
7577 classContext.getJavaClass().accept(this);
7678 bugAccumulator.reportAccumulatedBugs();
77
78 }
79
79
80 }
81
82 @Override
8083 public void report() {
8184 }
8285
8386 boolean isChecked(String c) {
84 if (!causes.add(c))
87 if (!causes.add(c)) {
8588 return checkedCauses.contains(c);
89 }
8690 try {
8791 if (Hierarchy.isSubtype(c, "java.lang.Exception") && !Hierarchy.isSubtype(c, "java.lang.RuntimeException")) {
8892 checkedCauses.add(c);
103107
104108 CodeException[] exp = obj.getExceptionTable();
105109 LineNumberTable lineNumbers = obj.getLineNumberTable();
106 if (exp == null)
110 if (exp == null) {
107111 return;
112 }
108113 byte[] code = obj.getCode();
109114
110115 for (CodeException aExp : exp) {
127132 j += 1 + NO_OF_OPERANDS[opcode];
128133 if (opcode >= IRETURN && opcode <= RETURN || opcode >= IFEQ && opcode <= GOTO && (opcode != GOTO || j < end)) {
129134 exitInTryBlock = true;
130 if (DEBUG)
135 if (DEBUG) {
131136 System.out.println("\texit: " + opcode + " in " + getFullyQualifiedMethodName());
137 }
132138 break;
133139 }
134140
135141 }
136142
137143 if (exitInTryBlock) {
138 if (DEBUG)
144 if (DEBUG) {
139145 System.out.println("Exit in try block");
146 }
140147 continue;
141148 }
142 if (handled < 5)
149 if (handled < 5) {
143150 continue;
151 }
144152 @DottedClassName String causeName;
145 if (cause == 0)
153 if (cause == 0) {
146154 causeName = "java.lang.Throwable";
147 else {
155 } else {
148156 causeName = Utility.compactClassName(getConstantPool().getConstantString(cause, CONSTANT_Class), false);
149 if (!isChecked(causeName))
157 if (!isChecked(causeName)) {
150158 continue;
159 }
151160 }
152161
153162 int jumpAtEnd = 0;
154163 if (end < code.length && asUnsignedByte(code[end]) == GOTO) {
155164 jumpAtEnd = getUnsignedShort(code, end + 1);
156 if (jumpAtEnd < handled)
165 if (jumpAtEnd < handled) {
157166 jumpAtEnd = 0;
167 }
158168 }
159169
160170 int opcode = asUnsignedByte(code[handled]);
161171 int afterHandler = 0;
162 if (DEBUG)
172 if (DEBUG) {
163173 System.out.println("DE:\topcode is " + OPCODE_NAMES[opcode] + ", " + asUnsignedByte(code[handled + 1]));
174 }
164175 boolean drops = false;
165176 boolean startsWithASTORE03 = opcode >= ASTORE_0 && opcode <= ASTORE_3;
166 if (startsWithASTORE03 && asUnsignedByte(code[handled + 1]) == RETURN) {
167 if (DEBUG)
177 if (startsWithASTORE03 && asUnsignedByte(code[handled + 1]) == RETURN) {
178 if (DEBUG) {
168179 System.out.println("Drop 1");
180 }
169181 drops = true;
170182 afterHandler = handled + 1;
171183 }
172184 if (handled + 2 < code.length && opcode == ASTORE && asUnsignedByte(code[handled + 2]) == RETURN) {
173185 drops = true;
174186 afterHandler = handled + 2;
175 if (DEBUG)
187 if (DEBUG) {
176188 System.out.println("Drop 2");
189 }
177190 }
178191 if (handled + 3 < code.length && !exitInTryBlock) {
179 if (DEBUG)
192 if (DEBUG) {
180193 System.out.println("DE: checking for jumps");
194 }
181195 if (startsWithASTORE03 && asUnsignedByte(code[handled - 3]) == GOTO) {
182196 int offsetBefore = getUnsignedShort(code, handled - 2);
183 if (DEBUG)
197 if (DEBUG) {
184198 System.out.println("offset before = " + offsetBefore);
199 }
185200 if (offsetBefore == 4) {
186201 drops = true;
187202 afterHandler = handled + 1;
188 if (DEBUG)
203 if (DEBUG) {
189204 System.out.println("Drop 3");
205 }
190206 }
191207 }
192208 if (opcode == ASTORE && asUnsignedByte(code[handled - 3]) == GOTO) {
194210 if (offsetBefore == 5) {
195211 drops = true;
196212 afterHandler = handled + 2;
197 if (DEBUG)
213 if (DEBUG) {
198214 System.out.println("Drop 4");
215 }
199216 }
200217 }
201218 if (startsWithASTORE03 && asUnsignedByte(code[handled + 1]) == GOTO && asUnsignedByte(code[handled - 3]) == GOTO) {
205222 if (offsetAfter > 0 && offsetAfter + 4 == offsetBefore) {
206223 drops = true;
207224 afterHandler = handled + 4;
208 if (DEBUG)
225 if (DEBUG) {
209226 System.out.println("Drop 5");
227 }
210228 }
211229 }
212230
217235 if (offsetAfter > 0 && offsetAfter + 5 == offsetBefore) {
218236 drops = true;
219237 afterHandler = handled + 5;
220 if (DEBUG)
238 if (DEBUG) {
221239 System.out.println("Drop 6");
240 }
222241 }
223242 }
224243
225244 }
226245
227246 boolean multiLineHandler = false;
228 if (DEBUG)
247 if (DEBUG) {
229248 System.out.println("afterHandler = " + afterHandler + ", handled = " + handled);
249 }
230250 if (afterHandler > handled && lineNumbers != null) {
231251 int startHandlerLinenumber = lineNumbers.getSourceLine(handled);
232252
233253 int endHandlerLinenumber = getNextExecutableLineNumber(lineNumbers, afterHandler) - 1;
234 if (DEBUG)
254 if (DEBUG) {
235255 System.out.println("Handler in lines " + startHandlerLinenumber + "-" + endHandlerLinenumber);
256 }
236257 if (endHandlerLinenumber > startHandlerLinenumber) {
237258 multiLineHandler = true;
238 if (DEBUG)
259 if (DEBUG) {
239260 System.out.println("Multiline handler");
240 }
241 }
242
243 if (end - start >= 4 && drops && !causeName.equals("java.lang.InterruptedException")
244 && !causeName.equals("java.lang.CloneNotSupportedException")) {
261 }
262 }
263 }
264
265 if (end - start >= 4 && drops && !"java.lang.InterruptedException".equals(causeName)
266 && !"java.lang.CloneNotSupportedException".equals(causeName)) {
245267 int priority = NORMAL_PRIORITY;
246 if (exitInTryBlock)
268 if (exitInTryBlock) {
247269 priority++;
248 if (end - start == 4)
270 }
271 if (end - start == 4) {
249272 priority++;
273 }
250274 SourceLineAnnotation srcLine = SourceLineAnnotation.fromVisitedInstruction(this.classContext, this, handled);
251275 if (srcLine != null && LOOK_IN_SOURCE_TO_FIND_COMMENTED_CATCH_BLOCKS) {
252 if (catchBlockHasComment(srcLine))
276 if (catchBlockHasComment(srcLine)) {
253277 return;
254 else
278 } else {
255279 priority++;
280 }
256281 } else {
257282 // can't look at source
258 if (lineNumbers == null || multiLineHandler)
283 if (lineNumbers == null || multiLineHandler) {
259284 priority += 2;
260 }
261 if (causeName.equals("java.lang.Error") || causeName.equals("java.lang.Exception") || causeName.equals("java.lang.Throwable")
262 || causeName.equals("java.lang.RuntimeException")) {
285 }
286 }
287 if ("java.lang.Error".equals(causeName) || "java.lang.Exception".equals(causeName) || "java.lang.Throwable".equals(causeName)
288 || "java.lang.RuntimeException".equals(causeName)) {
263289 priority--;
264 if (end - start > 30)
290 if (end - start > 30) {
265291 priority--;
292 }
266293 }
267294
268295 int register = -1;
277304 if (DEBUG) {
278305 System.out.println("Name: " + name);
279306 }
280 if (name.startsWith("ignore") || name.startsWith("cant"))
307 if (name.startsWith("ignore") || name.startsWith("cant")) {
281308 continue;
309 }
282310 }
283311
284312
285313 if (DEBUG) {
286314 System.out.println("Priority is " + priority);
287315 }
288 if (priority > LOW_PRIORITY)
316 if (priority > LOW_PRIORITY) {
289317 return;
290 if (priority < HIGH_PRIORITY)
318 }
319 if (priority < HIGH_PRIORITY) {
291320 priority = HIGH_PRIORITY;
321 }
292322 if (DEBUG) {
293323 System.out.println("reporting warning");
294324 }
295325
296326 BugInstance bugInstance = new BugInstance(this, exitInTryBlock ? "DE_MIGHT_DROP" : "DE_MIGHT_IGNORE", priority)
297 .addClassAndMethod(this);
327 .addClassAndMethod(this);
298328 bugInstance.addClass(causeName).describe("CLASS_EXCEPTION");
299329 bugInstance.addSourceLine(srcLine);
300330 bugAccumulator.accumulateBug(bugInstance, srcLine);
309339 int i = 0;
310340 for (; i < entries.length && entries[i].getStartPC() < PC; i++) {
311341 int line = entries[i].getLineNumber();
312 if (line > beforePC)
342 if (line > beforePC) {
313343 beforePC = line;
344 }
314345 }
315346
316347 if (i < entries.length) {
317348 int secondChoice = entries[i].getLineNumber();
318349 for (; i < entries.length; i++) {
319350 int line = entries[i].getLineNumber();
320 if (line > beforePC)
351 if (line > beforePC) {
321352 return line;
353 }
322354 }
323355 return secondChoice;
324 } else
356 } else {
325357 return entries[entries.length - 1].getLineNumber();
358 }
326359 }
327360
328361 private static final int START = 0;
351384 /**
352385 * Analyze a class's source code to see if there is a comment (or other
353386 * text) in a catch block we have marked as dropping an exception.
354 *
387 *
355388 * @return true if there is a comment in the catch block, false if not (or
356389 * if we can't tell)
357390 */
358391 private boolean catchBlockHasComment(SourceLineAnnotation srcLine) {
359 if (!LOOK_IN_SOURCE_TO_FIND_COMMENTED_CATCH_BLOCKS)
392 if (!LOOK_IN_SOURCE_TO_FIND_COMMENTED_CATCH_BLOCKS) {
360393 return false;
394 }
361395
362396 SourceFinder sourceFinder = AnalysisContext.currentAnalysisContext().getSourceFinder();
363397 try {
365399 int startLine = srcLine.getStartLine();
366400
367401 int scanStartLine = startLine - NUM_CONTEXT_LINES;
368 if (scanStartLine < 1)
402 if (scanStartLine < 1) {
369403 scanStartLine = 1;
404 }
370405
371406 int offset = sourceFile.getLineOffset(scanStartLine - 1);
372407 if (offset < 0)
408 {
373409 return false; // Source file has changed?
410 }
374411 Tokenizer tokenizer = new Tokenizer(UTF8.reader(sourceFile.getInputStreamFromOffset(offset)));
375412
376413 // Read the tokens into an ArrayList,
381418 for (int line = scanStartLine; line < scanStartLine + MAX_LINES;) {
382419 Token token = tokenizer.next();
383420 int kind = token.getKind();
384 if (kind == Token.EOF)
385 break;
421 if (kind == Token.EOF) {
422 break;
423 }
386424
387425 if (kind == Token.EOL) {
388 if (line == startLine)
426 if (line == startLine) {
389427 eolOfCatchBlockStart = tokenList.size();
428 }
390429 ++line;
391430 }
392431
394433 }
395434
396435 if (eolOfCatchBlockStart < 0)
436 {
397437 return false; // Couldn't scan line reported as start of catch
398 // block
438 // block
439 }
399440
400441 // Starting at the end of the line reported as the start of the
401442 // catch block,
405446
406447 while (iter.hasPrevious()) {
407448 Token token = iter.previous();
408 if (token.getKind() == Token.WORD && token.getLexeme().equals("catch")) {
449 if (token.getKind() == Token.WORD && "catch".equals(token.getLexeme())) {
409450 foundCatch = true;
410451 break;
411452 }
412453 }
413454
414455 if (!foundCatch)
456 {
415457 return false; // Couldn't find "catch" keyword
458 }
416459
417460 // Scan forward from the "catch" keyword to see what text
418461 // is in the handler block. If the block is non-empty,
424467 int state = START;
425468 int level = 0;
426469 do {
427 if (!iter.hasNext())
428 break;
470 if (!iter.hasNext()) {
471 break;
472 }
429473
430474 Token token = iter.next();
431475 int type = token.getKind();
433477
434478 switch (type) {
435479 case Token.EOL:
436 if (DEBUG)
480 if (DEBUG) {
437481 System.out.println("Saw token: [EOL]");
482 }
438483 ++numLines;
439 if (numLines >= MAX_LINES)
484 if (numLines >= MAX_LINES) {
440485 done = true;
486 }
441487 break;
442488 default:
443 if (DEBUG)
489 if (DEBUG) {
444490 System.out.println("Got token: " + value);
491 }
445492 switch (state) {
446493 case START:
447 if (value.equals("catch"))
494 if ("catch".equals(value)) {
448495 state = CATCH;
496 }
449497 break;
450498 case CATCH:
451 if (value.equals("("))
499 if ("(".equals(value)) {
452500 state = OPEN_PAREN;
501 }
453502 break;
454503 case OPEN_PAREN:
455 if (value.equals(")")) {
456 if (level == 0)
504 if (")".equals(value)) {
505 if (level == 0) {
457506 state = CLOSE_PAREN;
458 else
507 } else {
459508 --level;
460 } else if (value.equals("(")) {
509 }
510 } else if ("(".equals(value)) {
461511 ++level;
462512 }
463513 break;
464514 case CLOSE_PAREN:
465 if (value.equals("{"))
515 if ("{".equals(value)) {
466516 state = OPEN_BRACE;
517 }
467518 break;
468519 case OPEN_BRACE:
469 boolean closeBrace = value.equals("}");
470 if (DEBUG && !closeBrace)
520 boolean closeBrace = "}".equals(value);
521 if (DEBUG && !closeBrace) {
471522 System.out.println("Found a comment in catch block: " + value);
523 }
472524 return !closeBrace;
473525 }
474526 break;
482534 }
483535 }
484536
485 // vim:ts=4
00 package edu.umd.cs.findbugs.detect;
11
22 import java.io.File;
3 import java.util.Collections;
34 import java.util.Iterator;
5 import java.util.Map;
46
57 import org.apache.bcel.classfile.Method;
68 import org.apache.bcel.generic.ConstantPoolGen;
2224 import edu.umd.cs.findbugs.ba.constant.Constant;
2325 import edu.umd.cs.findbugs.ba.constant.ConstantDataflow;
2426 import edu.umd.cs.findbugs.ba.constant.ConstantFrame;
27 import edu.umd.cs.findbugs.classfile.Global;
28 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
29 import edu.umd.cs.findbugs.detect.BuildStringPassthruGraph.MethodParameter;
30 import edu.umd.cs.findbugs.detect.BuildStringPassthruGraph.StringPassthruDatabase;
2531
2632 public class DumbMethodInvocations implements Detector {
33 private static final MethodDescriptor STRING_SUBSTRING =
34 new MethodDescriptor("java/lang/String", "substring", "(I)Ljava/lang/String;");
35
2736
2837 private final BugReporter bugReporter;
2938
3039 private final BugAccumulator bugAccumulator;
3140
41 private final Map<MethodDescriptor, int[]> allFileNameStringMethods;
42 private final Map<MethodDescriptor, int[]> allDatabasePasswordMethods;
43
3244 public DumbMethodInvocations(BugReporter bugReporter) {
3345 this.bugReporter = bugReporter;
3446 this.bugAccumulator = new BugAccumulator(bugReporter);
47
48 StringPassthruDatabase database = Global.getAnalysisCache().getDatabase(StringPassthruDatabase.class);
49 allFileNameStringMethods = database.getFileNameStringMethods();
50 allDatabasePasswordMethods = database.findLinkedMethods(Collections.singleton(new MethodParameter(new MethodDescriptor(
51 "java/sql/DriverManager", "getConnection",
52 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/sql/Connection;", true), 2)));
3553 }
3654
55 @Override
3756 public void visitClassContext(ClassContext classContext) {
3857 Method[] methodList = classContext.getJavaClass().getMethods();
3958
4059 for (Method method : methodList) {
41 if (method.getCode() == null)
60 if (method.getCode() == null) {
4261 continue;
62 }
4363
4464 try {
4565 analyzeMethod(classContext, method);
4666 bugAccumulator.reportAccumulatedBugs();
4767 } catch (MethodUnprofitableException mue) {
48 if (SystemProperties.getBoolean("unprofitable.debug")) // otherwise
49 // don't
50 // report
68 if (SystemProperties.getBoolean("unprofitable.debug")) {
69 // don't
70 // report
5171 bugReporter.logError("skipping unprofitable method in " + getClass().getName());
72 }
5273 } catch (CFGBuilderException e) {
5374 bugReporter.logError("Detector " + this.getClass().getName() + " caught exception", e);
5475 } catch (DataflowAnalysisException e) {
6889 Location location = i.next();
6990
7091 Instruction ins = location.getHandle().getInstruction();
71 if (!(ins instanceof InvokeInstruction))
92 if (!(ins instanceof InvokeInstruction)) {
7293 continue;
94 }
7395 InvokeInstruction iins = (InvokeInstruction) ins;
7496
7597 ConstantFrame frame = constantDataflow.getFactAtLocation(location);
78100 continue;
79101 }
80102
81 if (iins.getName(cpg).equals("getConnection")
82 && iins.getSignature(cpg).equals(
83 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/sql/Connection;")
84 && iins.getClassName(cpg).equals("java.sql.DriverManager")) {
85 Constant operandValue = frame.getTopValue();
86 if (operandValue.isConstantString()) {
87 String password = operandValue.getConstantString();
88 if (password.length() == 0)
89 bugAccumulator.accumulateBug(new BugInstance(this, "DMI_EMPTY_DB_PASSWORD", NORMAL_PRIORITY)
90 .addClassAndMethod(methodGen, sourceFile), classContext, methodGen, sourceFile, location);
91 else
92 bugAccumulator.accumulateBug(new BugInstance(this, "DMI_CONSTANT_DB_PASSWORD", NORMAL_PRIORITY)
93 .addClassAndMethod(methodGen, sourceFile), classContext, methodGen, sourceFile, location);
103 MethodDescriptor md = new MethodDescriptor(iins, cpg);
104 if (allDatabasePasswordMethods.containsKey(md)) {
105 for(int paramNumber : allDatabasePasswordMethods.get(md)) {
106 Constant operandValue = frame.getStackValue(iins.getArgumentTypes(cpg).length-1-paramNumber);
107 if (operandValue.isConstantString()) {
108 String password = operandValue.getConstantString();
109 if (password.length() == 0) {
110 bugAccumulator.accumulateBug(new BugInstance(this, "DMI_EMPTY_DB_PASSWORD", NORMAL_PRIORITY)
111 .addClassAndMethod(methodGen, sourceFile), classContext, methodGen, sourceFile, location);
112 } else {
113 bugAccumulator.accumulateBug(new BugInstance(this, "DMI_CONSTANT_DB_PASSWORD", NORMAL_PRIORITY)
114 .addClassAndMethod(methodGen, sourceFile), classContext, methodGen, sourceFile, location);
115 }
94116
117 }
95118 }
96119 }
97120
98 if (iins.getName(cpg).equals("substring") && iins.getSignature(cpg).equals("(I)Ljava/lang/String;")
99 && iins.getClassName(cpg).equals("java.lang.String")) {
121 if (md.equals(STRING_SUBSTRING)) {
100122
101123 Constant operandValue = frame.getTopValue();
102 if (!operandValue.isConstantInteger())
124 if (!operandValue.isConstantInteger()) {
103125 continue;
126 }
104127 int v = operandValue.getConstantInt();
105 if (v == 0)
128 if (v == 0) {
106129 bugAccumulator.accumulateBug(new BugInstance(this, "DMI_USELESS_SUBSTRING", NORMAL_PRIORITY)
107 .addClassAndMethod(methodGen, sourceFile), classContext, methodGen, sourceFile, location);
130 .addClassAndMethod(methodGen, sourceFile), classContext, methodGen, sourceFile, location);
131 }
108132
109 } else if (iins.getName(cpg).equals("<init>") && iins.getSignature(cpg).equals("(Ljava/lang/String;)V")
110 && iins.getClassName(cpg).equals("java.io.File")) {
133 } else if (allFileNameStringMethods.containsKey(md)) {
111134
112 Constant operandValue = frame.getTopValue();
113 if (!operandValue.isConstantString())
114 continue;
115 String v = operandValue.getConstantString();
116 if (isAbsoluteFileName(v) && !v.startsWith("/etc/") && !v.startsWith("/dev/")
117 && !v.startsWith("/proc")) {
118 int priority = NORMAL_PRIORITY;
119 if (v.startsWith("/tmp"))
120 priority = LOW_PRIORITY;
121 else if (v.indexOf("/home") >= 0)
122 priority = HIGH_PRIORITY;
123 bugAccumulator.accumulateBug(new BugInstance(this, "DMI_HARDCODED_ABSOLUTE_FILENAME", priority)
124 .addClassAndMethod(methodGen, sourceFile).addString(v).describe("FILE_NAME"), classContext,
125 methodGen, sourceFile, location);
135 for(int paramNumber : allFileNameStringMethods.get(md)) {
136 Constant operandValue = frame.getStackValue(iins.getArgumentTypes(cpg).length-1-paramNumber);
137 if (!operandValue.isConstantString()) {
138 continue;
139 }
140 String v = operandValue.getConstantString();
141 if (isAbsoluteFileName(v) && !v.startsWith("/etc/") && !v.startsWith("/dev/")
142 && !v.startsWith("/proc")) {
143 int priority = NORMAL_PRIORITY;
144 if (v.startsWith("/tmp")) {
145 priority = LOW_PRIORITY;
146 } else if (v.indexOf("/home") >= 0) {
147 priority = HIGH_PRIORITY;
148 }
149 bugAccumulator.accumulateBug(new BugInstance(this, "DMI_HARDCODED_ABSOLUTE_FILENAME", priority)
150 .addClassAndMethod(methodGen, sourceFile).addString(v).describe("FILE_NAME"), classContext,
151 methodGen, sourceFile, location);
152 }
126153 }
127154
128155 }
131158 }
132159
133160 private boolean isAbsoluteFileName(String v) {
134 if (v.startsWith("/dev/"))
161 if (v.startsWith("/dev/")) {
135162 return false;
136 if (v.startsWith("/"))
163 }
164 if (v.startsWith("/")) {
137165 return true;
138 if (v.startsWith("C:"))
166 }
167 if (v.startsWith("\\\\")) {
168 // UNC pathname like \\Server\share\...
139169 return true;
140 if (v.startsWith("c:"))
141 return true;
170 }
171 if (v.length() >= 2 && v.charAt(1) == ':') {
172 char driveletter = v.charAt(0);
173 if((driveletter >= 'A' && driveletter <= 'Z') || (driveletter >= 'a' && driveletter <= 'z')) {
174 return true;
175 }
176 }
142177 try {
143178 File f = new File(v);
144179 return f.isAbsolute();
147182 }
148183 }
149184
185 @Override
150186 public void report() {
151187 }
152188
4444 import edu.umd.cs.findbugs.BugReporter;
4545 import edu.umd.cs.findbugs.ClassAnnotation;
4646 import edu.umd.cs.findbugs.IntAnnotation;
47 import edu.umd.cs.findbugs.JavaVersion;
4847 import edu.umd.cs.findbugs.LocalVariableAnnotation;
4948 import edu.umd.cs.findbugs.MethodAnnotation;
5049 import edu.umd.cs.findbugs.OpcodeStack;
5251 import edu.umd.cs.findbugs.Priorities;
5352 import edu.umd.cs.findbugs.SourceLineAnnotation;
5453 import edu.umd.cs.findbugs.StringAnnotation;
54 import edu.umd.cs.findbugs.SystemProperties;
5555 import edu.umd.cs.findbugs.ba.AnalysisContext;
5656 import edu.umd.cs.findbugs.ba.CFGBuilderException;
5757 import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
5858 import edu.umd.cs.findbugs.ba.Hierarchy;
5959 import edu.umd.cs.findbugs.ba.ObjectTypeFactory;
6060 import edu.umd.cs.findbugs.ba.SignatureParser;
61 import edu.umd.cs.findbugs.ba.XField;
6162 import edu.umd.cs.findbugs.ba.XMethod;
6263 import edu.umd.cs.findbugs.ba.ch.Subtypes2;
6364 import edu.umd.cs.findbugs.ba.type.TypeDataflow;
7172
7273 public class DumbMethods extends OpcodeStackDetector {
7374
75 private abstract class SubDetector {
76 public void initMethod(Method method) {}
77
78 abstract public void sawOpcode(int seen);
79 }
80
81 private class InvalidMinMaxSubDetector extends SubDetector {
82 Number lowerBound, upperBound;
83
84 @Override
85 public void initMethod(Method method) {
86 lowerBound = upperBound = null;
87 }
88
89 @Override
90 public void sawOpcode(int seen) {
91 if(seen == INVOKESTATIC && getClassConstantOperand().equals("java/lang/Math") && (getMethodDescriptorOperand().getName().equals("max")
92 || getMethodDescriptorOperand().getName().equals("min"))) {
93 Object const1 = stack.getStackItem(0).getConstant();
94 Object const2 = stack.getStackItem(1).getConstant();
95 Number n = null;
96 if(const1 != null ^ const2 != null) {
97 n = (const1 instanceof Number) ? (Number)const1 : (Number)const2;
98 if(getMethodDescriptorOperand().getName().equals("min")) {
99 upperBound = n;
100 } else {
101 lowerBound = n;
102 }
103 } else {
104 upperBound = lowerBound = null;
105 }
106 XMethod rvo1 = stack.getStackItem(0).getReturnValueOf();
107 XMethod rvo2 = stack.getStackItem(1).getReturnValueOf();
108 if(rvo1 != null ^ rvo2 != null) {
109 XMethod rvo = rvo1 == null ? rvo2 : rvo1;
110 if (lowerBound instanceof Comparable && upperBound != null && upperBound.getClass() == lowerBound.getClass()
111 && rvo.getClassDescriptor().getClassName().equals("java/lang/Math")
112 && (rvo.getName().equals("max") || rvo.getName().equals("min"))) {
113 @SuppressWarnings("unchecked")
114 int result = ((Comparable<Number>)lowerBound).compareTo(upperBound);
115 if(result > 0) {
116 accumulator.accumulateBug(
117 new BugInstance("DM_INVALID_MIN_MAX", HIGH_PRIORITY).addClassAndMethod(DumbMethods.this)
118 .addString(String.valueOf(n)),
119 DumbMethods.this);
120 }
121 }
122 }
123 }
124 }
125 }
126
127 private class NullMethodsSubDetector extends SubDetector {
128
129 @Override
130 public void sawOpcode(int seen) {
131 if (seen == INVOKESTATIC && ("com/google/common/base/Preconditions".equals(getClassConstantOperand())
132 && "checkNotNull".equals(getNameConstantOperand())
133 || "com/google/common/base/Strings".equals(getClassConstantOperand())
134 && ("nullToEmpty".equals(getNameConstantOperand()) ||
135 "emptyToNull".equals(getNameConstantOperand()) ||
136 "isNullOrEmpty".equals(getNameConstantOperand())))
137 ) {
138 int args = PreorderVisitor.getNumberArguments(getSigConstantOperand());
139
140 OpcodeStack.Item item = stack.getStackItem(args - 1);
141 Object o = item.getConstant();
142 if (o instanceof String) {
143
144 OpcodeStack.Item secondArgument = null;
145 String bugPattern = "DMI_DOH";
146 if (args > 1) {
147 secondArgument = stack.getStackItem(args - 2);
148 Object secondConstant = secondArgument.getConstant();
149 if (!(secondConstant instanceof String)) {
150 bugPattern = "DMI_ARGUMENTS_WRONG_ORDER";
151 }
152 }
153
154 BugInstance bug = new BugInstance(DumbMethods.this, bugPattern, NORMAL_PRIORITY).addClassAndMethod(DumbMethods.this)
155 .addCalledMethod(DumbMethods.this)
156 .addString("Passing String constant as value that should be null checked").describe(StringAnnotation.STRING_MESSAGE)
157 .addString((String) o).describe(StringAnnotation.STRING_CONSTANT_ROLE);
158 if (secondArgument != null) {
159 bug.addValueSource(secondArgument, DumbMethods.this);
160 }
161
162 accumulator.accumulateBug(bug, DumbMethods.this);
163 }
164 }
165
166 if (seen == INVOKESTATIC && ("junit/framework/Assert".equals(getClassConstantOperand()) || "org/junit/Assert".equals(getClassConstantOperand()))
167 && "assertNotNull".equals(getNameConstantOperand())) {
168 int args = PreorderVisitor.getNumberArguments(getSigConstantOperand());
169
170 OpcodeStack.Item item = stack.getStackItem(0);
171 Object o = item.getConstant();
172 if (o instanceof String) {
173
174 OpcodeStack.Item secondArgument = null;
175 String bugPattern = "DMI_DOH";
176 if (args == 2) {
177 secondArgument = stack.getStackItem(1);
178 Object secondConstant = secondArgument.getConstant();
179 if (!(secondConstant instanceof String)) {
180 bugPattern = "DMI_ARGUMENTS_WRONG_ORDER";
181 }
182 }
183
184 BugInstance bug = new BugInstance(DumbMethods.this, bugPattern, NORMAL_PRIORITY).addClassAndMethod(DumbMethods.this)
185 .addCalledMethod(DumbMethods.this).addString("Passing String constant as value that should be null checked").describe(StringAnnotation.STRING_MESSAGE)
186 .addString((String) o).describe(StringAnnotation.STRING_CONSTANT_ROLE);
187 if (secondArgument != null) {
188 bug.addValueSource(secondArgument, DumbMethods.this);
189 }
190
191 accumulator.accumulateBug(bug, DumbMethods.this);
192 }
193 }
194 }
195 }
196
197 private class FutilePoolSizeSubDetector extends SubDetector {
198 @Override
199 public void sawOpcode(int seen) {
200 if (seen == INVOKEVIRTUAL && "java/util/concurrent/ScheduledThreadPoolExecutor".equals(getClassConstantOperand())
201 && "setMaximumPoolSize".equals(getNameConstantOperand())) {
202 accumulator.accumulateBug(new BugInstance(DumbMethods.this,
203 "DMI_FUTILE_ATTEMPT_TO_CHANGE_MAXPOOL_SIZE_OF_SCHEDULED_THREAD_POOL_EXECUTOR", HIGH_PRIORITY)
204 .addClassAndMethod(DumbMethods.this), DumbMethods.this);
205 }
206 }
207 }
208
209 static int saturatingIncrement(int value) {
210 if (value == Integer.MAX_VALUE) {
211 return Integer.MAX_VALUE;
212 }
213 return value+1;
214 }
215
216 private class RangeCheckSubDetector extends SubDetector {
217
218
219
220 private void checkRange(Item item, Object minValue, Object maxValue, String pattern) {
221 if(!(item.getConstant() instanceof Number)) {
222 return;
223 }
224 int value = ((Number)item.getConstant()).intValue();
225 int intMin = Integer.MIN_VALUE;
226 int intMax = Integer.MAX_VALUE;
227 if(minValue instanceof Number) {
228 intMin = ((Number)minValue).intValue();
229 }
230 if(maxValue instanceof Number) {
231 intMax = ((Number)maxValue).intValue();
232 } else if(maxValue instanceof String) {
233 intMax = ((String)maxValue).length()-1;
234 } else if (maxValue instanceof OpcodeStack.Item){
235 OpcodeStack.Item maxItem = (OpcodeStack.Item ) maxValue;
236 if (maxItem.getSignature().charAt(0) == '[' && maxItem.getConstant() instanceof Integer) {
237 intMax = ((Integer)maxItem.getConstant())-1;
238
239 }
240 }
241
242 if(value < intMin || value > intMax) {
243 BugInstance bug = new BugInstance(pattern, NORMAL_PRIORITY ).addClassAndMethod(DumbMethods.this).addSourceLine(DumbMethods.this)
244 .addInt(value).describe(IntAnnotation.INT_VALUE);
245
246 if (intMin <= intMax) {
247 if (value < intMin) {
248 bug.addInt(intMin).describe(IntAnnotation.INT_MIN_VALUE);
249 }
250 if (value > intMax) {
251 bug.addInt(intMax) .describe(IntAnnotation.INT_MAX_VALUE);
252 }
253 }
254
255
256 if (isMethodCall()) {
257 bug.addCalledMethod(DumbMethods.this);
258 }
259
260
261
262 accumulator.accumulateBug(bug, DumbMethods.this);
263 }
264 }
265
266
267 @Override
268 public void sawOpcode(int seen) {
269 // System.out.printf("%4d %s%n", getPC(), OPCODE_NAMES[seen]);
270 switch(seen) {
271 case IALOAD:
272 case AALOAD:
273 case SALOAD:
274 case CALOAD:
275 case BALOAD:
276 case LALOAD:
277 case DALOAD:
278 case FALOAD: {
279 checkRange(stack.getStackItem(0), 0, stack.getStackItem(1), "RANGE_ARRAY_INDEX");
280 break;
281 }
282 case IASTORE:
283 case AASTORE:
284 case SASTORE:
285 case CASTORE:
286 case BASTORE:
287 case LASTORE:
288 case DASTORE:
289 case FASTORE: {
290
291 checkRange(stack.getStackItem(1), 0, stack.getStackItem(2), "RANGE_ARRAY_INDEX");
292 break;
293 }
294 case INVOKESTATIC: {
295 MethodDescriptor m = getMethodDescriptorOperand();
296 if(m.getSlashedClassName().equals("java/lang/System") && m.getName().equals("arraycopy")) {
297 // void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
298 Item length = stack.getStackItem(0);
299 Object constantLength = length.getConstant();
300 // if (constantLength instanceof Number && constantLength.equals(0)) {
301 // break;
302 // }
303 Item srcPos = stack.getStackItem(3);
304 Item src = stack.getStackItem(4);
305 checkRange(srcPos, 0, src, "RANGE_ARRAY_OFFSET");
306 Item dest = stack.getStackItem(2);
307 Item destPos = stack.getStackItem(1);
308 checkRange(destPos, 0, dest, "RANGE_ARRAY_OFFSET");
309
310 if(constantLength instanceof Number) {
311 int length1 = Integer.MAX_VALUE;
312 if(src.getConstant() instanceof Integer) {
313 length1 = (int) src.getConstant();
314 }
315 if(srcPos.getConstant() instanceof Integer) {
316 length1 -= (int) srcPos.getConstant();
317 }
318 int length2 = Integer.MAX_VALUE;
319 if(dest.getConstant() instanceof Integer) {
320 length2 = (int) stack.getStackItem(2).getConstant();
321 }
322 if(destPos.getConstant() instanceof Integer) {
323 length2 -= (int) stack.getStackItem(1).getConstant();
324 }
325 checkRange(length, 0, Math.min(length1, length2), "RANGE_ARRAY_LENGTH");
326 }
327 }
328 break;
329 }
330 case INVOKEVIRTUAL:
331 case INVOKESPECIAL: {
332 MethodDescriptor m = getMethodDescriptorOperand();
333 if(m.getSlashedClassName().equals("java/lang/String")) {
334 if((m.getName().equals("charAt") || m.getName().equals("codePointAt")) && m.getSignature().startsWith("(I)")) {
335 checkRange(stack.getStackItem(0), 0, stack.getStackItem(1).getConstant(), "RANGE_STRING_INDEX");
336 }
337 if(m.getName().equals("substring") || m.getName().equals("subSequence")) {
338 int nArgs = getNumberArguments(m.getSignature());
339 Item thisArg = stack.getStackItem(nArgs);
340 Item firstArg = stack.getStackItem(nArgs-1);
341 Object thisConstantValue = thisArg.getConstant();
342 int maxLength = thisConstantValue instanceof String ? ((String)thisConstantValue).length() : Integer.MAX_VALUE;
343 checkRange(firstArg, 0,maxLength, "RANGE_STRING_INDEX");
344 if(nArgs == 2) {
345 Item secondArg = stack.getStackItem(0);
346 checkRange(secondArg, firstArg.getConstant() == null ? 0 : firstArg.getConstant(),
347 maxLength,
348 "RANGE_STRING_INDEX");
349 }
350 }
351 }
352 if ((m.getSignature().startsWith("([BII)") || m.getSignature().startsWith("([CII)") || m.getSignature().startsWith("([III)"))
353 && (((m.getName().equals("write") || m.getName().equals("read")) && m.getSlashedClassName().startsWith(
354 "java/io/")) || (m.getName().equals("<init>") && m.getSlashedClassName().equals("java/lang/String")))) {
355 Item arrayArg = stack.getStackItem(2);
356 Item offsetArg = stack.getStackItem(1);
357 Item lengthArg = stack.getStackItem(0);
358 int length = Integer.MAX_VALUE;
359 if(arrayArg.getConstant() instanceof Integer) {
360 length = (int) arrayArg.getConstant();
361 }
362 if(offsetArg.getConstant() instanceof Integer) {
363 checkRange(offsetArg, 0, saturatingIncrement(length), "RANGE_ARRAY_OFFSET");
364 length -= (int) offsetArg.getConstant();
365 }
366 checkRange(lengthArg, 0, saturatingIncrement(length), "RANGE_ARRAY_LENGTH");
367 }
368 break;
369 }
370 default:
371 break;
372 }
373 }
374 }
375
376 private class UrlCollectionSubDetector extends SubDetector {
377 @Override
378 public void sawOpcode(int seen) {
379 if ((seen == INVOKEVIRTUAL && "java/util/HashMap".equals(getClassConstantOperand()) && "get".equals(getNameConstantOperand()))
380 || (seen == INVOKEINTERFACE && "java/util/Map".equals(getClassConstantOperand()) && "get".equals(getNameConstantOperand()))
381 || (seen == INVOKEVIRTUAL && "java/util/HashSet".equals(getClassConstantOperand()) && "contains".equals(getNameConstantOperand()))
382 || (seen == INVOKEINTERFACE && "java/util/Set".equals(getClassConstantOperand()) && "contains".equals(getNameConstantOperand()))) {
383 OpcodeStack.Item top = stack.getStackItem(0);
384 if ("Ljava/net/URL;".equals(top.getSignature())) {
385 accumulator.accumulateBug(new BugInstance(DumbMethods.this, "DMI_COLLECTION_OF_URLS", HIGH_PRIORITY)
386 .addClassAndMethod(DumbMethods.this), DumbMethods.this);
387 }
388 }
389 }
390 }
391
392 private class VacuousComparisonSubDetector extends SubDetector {
393 @Override
394 public void sawOpcode(int seen) {
395 boolean foundVacuousComparison = false;
396 if (seen == IF_ICMPGT || seen == IF_ICMPLE) {
397 OpcodeStack.Item rhs = stack.getStackItem(0);
398 Object rhsConstant = rhs.getConstant();
399 if (rhsConstant instanceof Integer && ((Integer) rhsConstant).intValue() == Integer.MAX_VALUE) {
400 foundVacuousComparison = true;
401 }
402 OpcodeStack.Item lhs = stack.getStackItem(1);
403 Object lhsConstant = lhs.getConstant();
404 if (lhsConstant instanceof Integer && ((Integer) lhsConstant).intValue() == Integer.MIN_VALUE) {
405 foundVacuousComparison = true;
406 }
407
408 }
409 if (seen == IF_ICMPLT || seen == IF_ICMPGE) {
410 OpcodeStack.Item rhs = stack.getStackItem(0);
411 Object rhsConstant = rhs.getConstant();
412 if (rhsConstant instanceof Integer && ((Integer) rhsConstant).intValue() == Integer.MIN_VALUE) {
413 foundVacuousComparison = true;
414 }
415 OpcodeStack.Item lhs = stack.getStackItem(1);
416 Object lhsConstant = lhs.getConstant();
417 if (lhsConstant instanceof Integer && ((Integer) lhsConstant).intValue() == Integer.MAX_VALUE) {
418 foundVacuousComparison = true;
419 }
420
421 }
422 if (foundVacuousComparison) {
423 accumulator.accumulateBug(new BugInstance(DumbMethods.this, "INT_VACUOUS_COMPARISON", getBranchOffset() < 0 ? HIGH_PRIORITY
424 : NORMAL_PRIORITY).addClassAndMethod(DumbMethods.this), DumbMethods.this);
425 }
426 }
427 }
428
429 private class BadCastInEqualsSubDetector extends SubDetector {
430 private boolean isEqualsObject;
431
432 private boolean sawInstanceofCheck;
433
434 private boolean reportedBadCastInEquals;
435
436 @Override
437 public void initMethod(Method method) {
438 isEqualsObject = "equals".equals(getMethodName()) && "(Ljava/lang/Object;)Z".equals(getMethodSig()) && !method.isStatic();
439 sawInstanceofCheck = false;
440 reportedBadCastInEquals = false;
441 }
442
443 @Override
444 public void sawOpcode(int seen) {
445 if (isEqualsObject && !reportedBadCastInEquals) {
446 if (seen == INVOKEVIRTUAL && "isInstance".equals(getNameConstantOperand())
447 && "java/lang/Class".equals(getClassConstantOperand())) {
448 OpcodeStack.Item item = stack.getStackItem(0);
449 if (item.getRegisterNumber() == 1) {
450 sawInstanceofCheck = true;
451 }
452 } else if (seen == INSTANCEOF || seen == INVOKEVIRTUAL && "getClass".equals(getNameConstantOperand())
453 && "()Ljava/lang/Class;".equals(getSigConstantOperand())) {
454 OpcodeStack.Item item = stack.getStackItem(0);
455 if (item.getRegisterNumber() == 1) {
456 sawInstanceofCheck = true;
457 }
458 } else if (seen == INVOKESPECIAL && "equals".equals(getNameConstantOperand())
459 && "(Ljava/lang/Object;)Z".equals(getSigConstantOperand())) {
460 OpcodeStack.Item item0 = stack.getStackItem(0);
461 OpcodeStack.Item item1 = stack.getStackItem(1);
462 if (item1.getRegisterNumber() + item0.getRegisterNumber() == 1) {
463 sawInstanceofCheck = true;
464 }
465 } else if (seen == CHECKCAST && !sawInstanceofCheck) {
466 OpcodeStack.Item item = stack.getStackItem(0);
467 if (item.getRegisterNumber() == 1) {
468 if (getSizeOfSurroundingTryBlock(getPC()) == Integer.MAX_VALUE) {
469 accumulator.accumulateBug(new BugInstance(DumbMethods.this, "BC_EQUALS_METHOD_SHOULD_WORK_FOR_ALL_OBJECTS",
470 NORMAL_PRIORITY).addClassAndMethod(DumbMethods.this), DumbMethods.this);
471 }
472
473 reportedBadCastInEquals = true;
474 }
475 }
476 }
477 }
478 }
479
480 private class RandomOnceSubDetector extends SubDetector {
481 private boolean freshRandomOnTos = false;
482
483 private boolean freshRandomOneBelowTos = false;
484
485 @Override
486 public void initMethod(Method method) {
487 freshRandomOnTos = false;
488 }
489
490 @Override
491 public void sawOpcode(int seen) {
492 if (seen == INVOKEVIRTUAL && "java/util/Random".equals(getClassConstantOperand())
493 && (freshRandomOnTos || freshRandomOneBelowTos)) {
494 accumulator.accumulateBug(new BugInstance(DumbMethods.this, "DMI_RANDOM_USED_ONLY_ONCE", HIGH_PRIORITY)
495 .addClassAndMethod(DumbMethods.this).addCalledMethod(DumbMethods.this), DumbMethods.this);
496
497 }
498 freshRandomOneBelowTos = freshRandomOnTos && isRegisterLoad();
499 freshRandomOnTos = seen == INVOKESPECIAL && "java/util/Random".equals(getClassConstantOperand())
500 && "<init>".equals(getNameConstantOperand());
501 }
502 }
503
504 private final SubDetector[] subDetectors = new SubDetector[] { new VacuousComparisonSubDetector(),
505 new RangeCheckSubDetector(), new BadCastInEqualsSubDetector(), new FutilePoolSizeSubDetector(),
506 new UrlCollectionSubDetector(), new RandomOnceSubDetector(), new NullMethodsSubDetector(),
507 new InvalidMinMaxSubDetector()};
508
74509 private static final ObjectType CONDITION_TYPE = ObjectTypeFactory.getInstance("java.util.concurrent.locks.Condition");
75510
76511 private final BugReporter bugReporter;
96531
97532 private boolean isPublicStaticVoidMain;
98533
99 private boolean isEqualsObject;
100
101 private boolean sawInstanceofCheck;
102
103 private boolean reportedBadCastInEquals;
104
105534 private int sawCheckForNonNegativeSignedByte;
106535
107536 private int sinceBufferedInputStreamReady;
110539
111540 private boolean checkForBitIorofSignedByte;
112541
113 private final boolean jdk15ChecksEnabled;
542 /**
543 * A heuristic - how long a catch block for OutOfMemoryError might be.
544 */
545 private static final int OOM_CATCH_LEN = 20;
546
547 private final boolean testingEnabled;
114548
115549 private final BugAccumulator accumulator;
116550 private final BugAccumulator absoluteValueAccumulator;
117551
118552 private static final int MICROS_PER_DAY_OVERFLOWED_AS_INT
119 = 24 * 60 * 60 * 1000 * 1000;
553 = 24 * 60 * 60 * 1000 * 1000;
120554
121555 public DumbMethods(BugReporter bugReporter) {
122556 this.bugReporter = bugReporter;
123557 accumulator = new BugAccumulator(bugReporter);
124558 absoluteValueAccumulator = new BugAccumulator(bugReporter);
125 jdk15ChecksEnabled = JavaVersion.getRuntimeVersion().isSameOrNewerThan(JavaVersion.JAVA_1_5);
559 testingEnabled = SystemProperties.getBoolean("report_TESTING_pattern_in_standard_detectors");
126560 }
127561
128562 boolean isSynthetic;
130564 @Override
131565 public void visit(JavaClass obj) {
132566 String superclassName = obj.getSuperclassName();
133 isSynthetic = superclassName.equals("java.rmi.server.RemoteStub");
567 isSynthetic = "java.rmi.server.RemoteStub".equals(superclassName);
134568 Attribute[] attributes = obj.getAttributes();
135569 if (attributes != null) {
136570 for (Attribute a : attributes) {
154588 @Override
155589 public void visit(Field field) {
156590 ConstantValue value = field.getConstantValue();
157 if (value == null) return;
591 if (value == null) {
592 return;
593 }
158594 Constant c = getConstantPool().getConstant(value.getConstantValueIndex());
159595
160 if (c instanceof ConstantLong && ((ConstantLong)c).getBytes() == MICROS_PER_DAY_OVERFLOWED_AS_INT) {
596 if (testingEnabled && c instanceof ConstantLong && ((ConstantLong)c).getBytes() == MICROS_PER_DAY_OVERFLOWED_AS_INT) {
161597 bugReporter.reportBug( new BugInstance(this, "TESTING", HIGH_PRIORITY).addClass(this).addField(this)
162 .addString("Did you mean MICROS_PER_DAY")
163 .addInt(MICROS_PER_DAY_OVERFLOWED_AS_INT)
164 .describe(IntAnnotation.INT_VALUE));
598 .addString("Did you mean MICROS_PER_DAY")
599 .addInt(MICROS_PER_DAY_OVERFLOWED_AS_INT)
600 .describe(IntAnnotation.INT_VALUE));
165601
166602 }
167603 }
169605 public void visit(Method method) {
170606 String cName = getDottedClassName();
171607
608 for(SubDetector subDetector : subDetectors) {
609 subDetector.initMethod(method);
610 }
611
172612 // System.out.println(getFullyQualifiedMethodName());
173 isPublicStaticVoidMain = method.isPublic() && method.isStatic() && getMethodName().equals("main")
613 isPublicStaticVoidMain = method.isPublic() && method.isStatic() && "main".equals(getMethodName())
174614 || cName.toLowerCase().indexOf("benchmark") >= 0;
175 prevOpcodeWasReadLine = false;
176 Code code = method.getCode();
177 if (code != null) {
178 this.exceptionTable = code.getExceptionTable();
179 }
180 if (this.exceptionTable == null) {
181 this.exceptionTable = new CodeException[0];
182 }
183 primitiveObjCtorSeen = null;
184 ctorSeen = false;
185 randomNextIntState = 0;
186 checkForBitIorofSignedByte = false;
187 isEqualsObject = getMethodName().equals("equals") && getMethodSig().equals("(Ljava/lang/Object;)Z") && !method.isStatic();
188 sawInstanceofCheck = false;
189 reportedBadCastInEquals = false;
190 freshRandomOnTos = false;
191 sinceBufferedInputStreamReady = 100000;
192 sawCheckForNonNegativeSignedByte = -1000;
193 sawLoadOfMinValue = false;
194 previousMethodCall = null;
615 prevOpcodeWasReadLine = false;
616 Code code = method.getCode();
617 if (code != null) {
618 this.exceptionTable = code.getExceptionTable();
619 }
620 if (this.exceptionTable == null) {
621 this.exceptionTable = new CodeException[0];
622 }
623 primitiveObjCtorSeen = null;
624 ctorSeen = false;
625 randomNextIntState = 0;
626 checkForBitIorofSignedByte = false;
627 sinceBufferedInputStreamReady = 100000;
628 sawCheckForNonNegativeSignedByte = -1000;
629 sawLoadOfMinValue = false;
630 previousMethodCall = null;
195631
196632 }
197633
201637
202638 SourceLineAnnotation pendingAbsoluteValueBugSourceLine;
203639
204 boolean freshRandomOnTos = false;
205
206 boolean freshRandomOneBelowTos = false;
207
208640 boolean sawLoadOfMinValue = false;
209641
210642 MethodDescriptor previousMethodCall = null;
211643
212644 @Override
213645 public void sawOpcode(int seen) {
214
646
215647 if (isMethodCall()) {
216648 MethodDescriptor called = getMethodDescriptorOperand();
217649
218650 if (previousMethodCall != null && !stack.isJumpTarget(getPC())) {
219 if (called.getName().equals("toString")
220 && called.getClassDescriptor().getClassName().equals("java/lang/Integer")
221 && previousMethodCall.getName().equals("valueOf")
222 && previousMethodCall.getSignature().equals("(I)Ljava/lang/Integer;")
651 if ("toString".equals(called.getName())
652 && "java/lang/Integer".equals(called.getClassDescriptor().getClassName())
653 && "valueOf".equals(previousMethodCall.getName())
654 && "(I)Ljava/lang/Integer;".equals(previousMethodCall.getSignature())
223655 ) {
224656 MethodAnnotation preferred = new MethodAnnotation("java.lang.Integer", "toString", "(I)Ljava/lang/String;", true);
225657 BugInstance bug = new BugInstance(this, "DM_BOXED_PRIMITIVE_TOSTRING", HIGH_PRIORITY).addClassAndMethod(this)
226658 .addCalledMethod(this).addMethod(preferred).describe(MethodAnnotation.SHOULD_CALL);
227659 accumulator.accumulateBug(bug, this);
228660
229 } else if (called.getName().equals("intValue")
230 && called.getClassDescriptor().getClassName().equals("java/lang/Integer")
231 && previousMethodCall.getSlashedClassName().equals("java/lang/Integer")
232 && (previousMethodCall.getName().equals("<init>")
233 && previousMethodCall.getSignature().equals("(Ljava/lang/String;)V")
234 || previousMethodCall.getName().equals("valueOf")
235 && previousMethodCall.getSignature().equals("(Ljava/lang/String;)Ljava/lang/Integer;")
661 } else if ("intValue".equals(called.getName())
662 && "java/lang/Integer".equals(called.getClassDescriptor().getClassName())
663 && "java/lang/Integer".equals(previousMethodCall.getSlashedClassName())
664 && ("<init>".equals(previousMethodCall.getName())
665 && "(Ljava/lang/String;)V".equals(previousMethodCall.getSignature())
666 || "valueOf".equals(previousMethodCall.getName())
667 && "(Ljava/lang/String;)Ljava/lang/Integer;".equals(previousMethodCall.getSignature())
236668 )) {
237669
238670 MethodAnnotation preferred = new MethodAnnotation("java.lang.Integer", "parseInt", "(Ljava/lang/String;)I", true);
240672 BugInstance bug = new BugInstance(this, "DM_BOXED_PRIMITIVE_FOR_PARSING", HIGH_PRIORITY).addClassAndMethod(this)
241673 .addCalledMethod(this).addMethod(preferred).describe(MethodAnnotation.SHOULD_CALL);
242674 accumulator.accumulateBug(bug, this);
243 } else if (called.getName().equals("longValue")
244 && called.getClassDescriptor().getClassName().equals("java/lang/Long")
245 && previousMethodCall.getSlashedClassName().equals("java/lang/Long")
246 && ( previousMethodCall.getName().equals("<init>")
247 && previousMethodCall.getSignature().equals("(Ljava/lang/String;)V")
248 || previousMethodCall.getName().equals("valueOf")
249 && previousMethodCall.getSignature().equals("(Ljava/lang/String;)Ljava/lang/Long;"))
675 } else if ("longValue".equals(called.getName())
676 && "java/lang/Long".equals(called.getClassDescriptor().getClassName())
677 && "java/lang/Long".equals(previousMethodCall.getSlashedClassName())
678 && ( "<init>".equals(previousMethodCall.getName())
679 && "(Ljava/lang/String;)V".equals(previousMethodCall.getSignature())
680 || "valueOf".equals(previousMethodCall.getName())
681 && "(Ljava/lang/String;)Ljava/lang/Long;".equals(previousMethodCall.getSignature()))
250682 ) {
251683 MethodAnnotation preferred = new MethodAnnotation("java.lang.Long", "parseLong", "(Ljava/lang/String;)J", true);
252684
253685 BugInstance bug = new BugInstance(this, "DM_BOXED_PRIMITIVE_FOR_PARSING", HIGH_PRIORITY).addClassAndMethod(this)
254686 .addCalledMethod(this).addMethod(preferred).describe(MethodAnnotation.SHOULD_CALL);
255687 accumulator.accumulateBug(bug, this);
688 } else if("compareTo".equals(called.getName())
689 && "valueOf".equals(previousMethodCall.getName())
690 && called.getClassDescriptor().equals(previousMethodCall.getClassDescriptor()) && !previousMethodCall.getSignature().startsWith("(Ljava/lang/String;")
691 ) {
692 String primitiveType = ClassName.getPrimitiveType(called.getClassDescriptor().getClassName());
693 XMethod rvo = stack.getStackItem(1).getReturnValueOf();
694 XField field = stack.getStackItem(1).getXField();
695 String signature;
696 if (rvo != null) {
697 signature = new SignatureParser(rvo.getSignature()).getReturnTypeSignature();
698 } else if (field != null) {
699 signature = field.getSignature();
700 } else {
701 signature = "";
702 }
703 if (primitiveType != null
704 && (previousMethodCall.equals(rvo) || signature.equals(primitiveType))
705 && (getThisClass().getMajor() >= MAJOR_1_7 || getThisClass().getMajor() >= MAJOR_1_4
706 && (primitiveType.equals("D") || primitiveType.equals("F")))) {
707 MethodDescriptor shouldCall = new MethodDescriptor(called.getClassDescriptor().getClassName(), "compare",
708 "(" + primitiveType + primitiveType + ")I", true);
709 BugInstance bug = new BugInstance(this, "DM_BOXED_PRIMITIVE_FOR_COMPARE",
710 primitiveType.equals("Z") ? LOW_PRIORITY : primitiveType.equals("B") ? NORMAL_PRIORITY
711 : HIGH_PRIORITY).addClassAndMethod(this).addCalledMethod(this).addMethod(shouldCall)
712 .describe(MethodAnnotation.SHOULD_CALL);
713 accumulator.accumulateBug(bug, this);
714 }
256715 }
257716 }
258717 previousMethodCall = called;
259 } else
718 } else {
260719 previousMethodCall = null;
261
720 }
721
262722
263723 if (seen == LDC || seen == LDC_W || seen == LDC2_W) {
264724 Constant c = getConstantRefOperand();
265 if ((c instanceof ConstantInteger && ((ConstantInteger) c).getBytes() == MICROS_PER_DAY_OVERFLOWED_AS_INT
725 if (testingEnabled && (c instanceof ConstantInteger && ((ConstantInteger) c).getBytes() == MICROS_PER_DAY_OVERFLOWED_AS_INT
266726 || c instanceof ConstantLong && ((ConstantLong) c).getBytes() == MICROS_PER_DAY_OVERFLOWED_AS_INT)) {
267727 BugInstance bug = new BugInstance(this, "TESTING", HIGH_PRIORITY).addClassAndMethod(this)
268728 .addString("Did you mean MICROS_PER_DAY").addInt(MICROS_PER_DAY_OVERFLOWED_AS_INT)
286746 checkForCompatibleLongComparison(right, left);
287747 }
288748
289 if (stack.getStackDepth() >= 2)
749 if (stack.getStackDepth() >= 2) {
290750 switch (seen) {
291751 case IF_ICMPEQ:
292752 case IF_ICMPNE:
305765 XMethod returnValueOf = item0.getReturnValueOf();
306766 if (constant1 instanceof Integer
307767 && returnValueOf != null
308 && returnValueOf.getName().equals("getYear")
309 && (returnValueOf.getClassName().equals("java.util.Date") || returnValueOf.getClassName().equals(
310 "java.sql.Date"))) {
768 && "getYear".equals(returnValueOf.getName())
769 && ("java.util.Date".equals(returnValueOf.getClassName()) || "java.sql.Date".equals(returnValueOf.getClassName()))) {
311770 int year = (Integer) constant1;
312 if (year > 1900)
771 if (testingEnabled && year > 1900) {
313772 accumulator.accumulateBug(
314773 new BugInstance(this, "TESTING", HIGH_PRIORITY).addClassAndMethod(this)
315 .addString("Comparison of getYear does understand that it returns year-1900")
316 .addMethod(returnValueOf).describe(MethodAnnotation.METHOD_CALLED).addInt(year)
317 .describe(IntAnnotation.INT_VALUE), this);
318 }
319 }
774 .addString("Comparison of getYear does understand that it returns year-1900")
775 .addMethod(returnValueOf).describe(MethodAnnotation.METHOD_CALLED).addInt(year)
776 .describe(IntAnnotation.INT_VALUE), this);
777 }
778 }
779 break;
780 default:
781 break;
782 }
783 }
320784
321785 // System.out.printf("%4d %10s: %s\n", getPC(), OPCODE_NAMES[seen],
322786 // stack);
335799 pendingAbsoluteValueBug.setPriority(Priorities.LOW_PRIORITY);
336800 }
337801 }
802 /*
338803 if (false)
339804 try {
340805 pendingAbsoluteValueBug.addString(OPCODE_NAMES[getPrevOpcode(1)] + ":" + OPCODE_NAMES[seen] + ":"
343808 pendingAbsoluteValueBug.addString(OPCODE_NAMES[getPrevOpcode(1)] + ":" + OPCODE_NAMES[seen]);
344809
345810 }
811 */
346812 absoluteValueAccumulator.accumulateBug(pendingAbsoluteValueBug, pendingAbsoluteValueBugSourceLine);
347813 pendingAbsoluteValueBug = null;
348814 pendingAbsoluteValueBugSourceLine = null;
350816 }
351817
352818 if (seen == INVOKESTATIC
353 && getClassConstantOperand().equals("org/easymock/EasyMock")
354 && (getNameConstantOperand().equals("replay") || getNameConstantOperand().equals("verify") || getNameConstantOperand()
355 .startsWith("reset")) && getSigConstantOperand().equals("([Ljava/lang/Object;)V")
356 && getPrevOpcode(1) == ANEWARRAY && getPrevOpcode(2) == ICONST_0)
819 && "org/easymock/EasyMock".equals(getClassConstantOperand())
820 && ("replay".equals(getNameConstantOperand()) || "verify".equals(getNameConstantOperand()) || getNameConstantOperand()
821 .startsWith("reset")) && "([Ljava/lang/Object;)V".equals(getSigConstantOperand())
822 && getPrevOpcode(1) == ANEWARRAY && getPrevOpcode(2) == ICONST_0) {
357823 accumulator.accumulateBug(new BugInstance(this, "DMI_VACUOUS_CALL_TO_EASYMOCK_METHOD", NORMAL_PRIORITY)
358 .addClassAndMethod(this).addCalledMethod(this), this);
359
360 if (seen == INVOKESTATIC && (getClassConstantOperand().equals("com/google/common/base/Preconditions")
361 && getNameConstantOperand().equals("checkNotNull")
362 || getClassConstantOperand().equals("com/google/common/base/Strings")
363 && (getNameConstantOperand().equals("nullToEmpty") ||
364 getNameConstantOperand().equals("emptyToNull") ||
365 getNameConstantOperand().equals("isNullOrEmpty")))
366 ) {
367 int args = PreorderVisitor.getNumberArguments(getSigConstantOperand());
368
369 OpcodeStack.Item item = stack.getStackItem(args - 1);
370 Object o = item.getConstant();
371 if (o instanceof String) {
372
373 OpcodeStack.Item secondArgument = null;
374 String bugPattern = "DMI_DOH";
375 if (args > 1) {
376 secondArgument = stack.getStackItem(args - 2);
377 Object secondConstant = secondArgument.getConstant();
378 if (!(secondConstant instanceof String)) {
379 bugPattern = "DMI_ARGUMENTS_WRONG_ORDER";
380 }
381 }
382
383 BugInstance bug = new BugInstance(this, bugPattern, NORMAL_PRIORITY).addClassAndMethod(this)
384 .addCalledMethod(this)
385 .addString("Passing String constant as value that should be null checked").describe(StringAnnotation.STRING_MESSAGE)
386 .addString((String) o).describe(StringAnnotation.STRING_CONSTANT_ROLE);
387 if (secondArgument != null)
388 bug.addValueSource(secondArgument, this);
389
390 accumulator.accumulateBug(bug, this);
391 }
392 }
393
394 if (seen == INVOKESTATIC && getClassConstantOperand().equals("junit/framework/Assert")
395 && getNameConstantOperand().equals("assertNotNull")) {
396 int args = PreorderVisitor.getNumberArguments(getSigConstantOperand());
397
398 OpcodeStack.Item item = stack.getStackItem(0);
399 Object o = item.getConstant();
400 if (o instanceof String) {
401
402 OpcodeStack.Item secondArgument = null;
403 String bugPattern = "DMI_DOH";
404 if (args == 2) {
405 secondArgument = stack.getStackItem(1);
406 Object secondConstant = secondArgument.getConstant();
407 if (!(secondConstant instanceof String)) {
408 bugPattern = "DMI_ARGUMENTS_WRONG_ORDER";
409 }
410 }
411
412 BugInstance bug = new BugInstance(this, bugPattern, NORMAL_PRIORITY).addClassAndMethod(this)
413 .addCalledMethod(this).addString("Passing String constant as value that should be null checked").describe(StringAnnotation.STRING_MESSAGE)
414 .addString((String) o).describe(StringAnnotation.STRING_CONSTANT_ROLE);
415 if (secondArgument != null)
416 bug.addValueSource(secondArgument, this);
417
418 accumulator.accumulateBug(bug, this);
419 }
420 }
824 .addClassAndMethod(this).addCalledMethod(this), this);
825 }
826
421827 if ((seen == INVOKESTATIC || seen == INVOKEVIRTUAL || seen == INVOKESPECIAL || seen == INVOKEINTERFACE)
422828 && getSigConstantOperand().indexOf("Ljava/lang/Runnable;") >= 0) {
423829 SignatureParser parser = new SignatureParser(getSigConstantOperand());
424830 int count = 0;
425831 for (Iterator<String> i = parser.parameterSignatureIterator(); i.hasNext(); count++) {
426832 String parameter = i.next();
427 if (parameter.equals("Ljava/lang/Runnable;")) {
833 if ("Ljava/lang/Runnable;".equals(parameter)) {
428834 OpcodeStack.Item item = stack.getStackItem(parser.getNumParameters() - 1 - count);
429835 if ("Ljava/lang/Thread;".equals(item.getSignature())) {
430836 accumulator.accumulateBug(new BugInstance(this, "DMI_THREAD_PASSED_WHERE_RUNNABLE_EXPECTED",
436842
437843 }
438844
439 if (prevOpcode == I2L && seen == INVOKESTATIC && getClassConstantOperand().equals("java/lang/Double")
440 && getNameConstantOperand().equals("longBitsToDouble")) {
845 if (prevOpcode == I2L && seen == INVOKESTATIC && "java/lang/Double".equals(getClassConstantOperand())
846 && "longBitsToDouble".equals(getNameConstantOperand())) {
441847 accumulator.accumulateBug(new BugInstance(this, "DMI_LONG_BITS_TO_DOUBLE_INVOKED_ON_INT", HIGH_PRIORITY)
442 .addClassAndMethod(this).addCalledMethod(this), this);
443 }
444
445 if (seen == INVOKEVIRTUAL && getClassConstantOperand().equals("java/util/Random")
446 && (freshRandomOnTos || freshRandomOneBelowTos)) {
447 accumulator.accumulateBug(new BugInstance(this, "DMI_RANDOM_USED_ONLY_ONCE", HIGH_PRIORITY).addClassAndMethod(this)
448 .addCalledMethod(this), this);
449
450 }
451
452 freshRandomOneBelowTos = freshRandomOnTos && isRegisterLoad();
453 freshRandomOnTos = seen == INVOKESPECIAL && getClassConstantOperand().equals("java/util/Random")
454 && getNameConstantOperand().equals("<init>");
455
456 if ((seen == INVOKEVIRTUAL && getClassConstantOperand().equals("java/util/HashMap") && getNameConstantOperand().equals(
457 "get"))
458 || (seen == INVOKEINTERFACE && getClassConstantOperand().equals("java/util/Map") && getNameConstantOperand()
459 .equals("get"))
460 || (seen == INVOKEVIRTUAL && getClassConstantOperand().equals("java/util/HashSet") && getNameConstantOperand()
461 .equals("contains"))
462 || (seen == INVOKEINTERFACE && getClassConstantOperand().equals("java/util/Set") && getNameConstantOperand()
463 .equals("contains"))) {
464 OpcodeStack.Item top = stack.getStackItem(0);
465 if (top.getSignature().equals("Ljava/net/URL;")) {
466 accumulator.accumulateBug(new BugInstance(this, "DMI_COLLECTION_OF_URLS", HIGH_PRIORITY).addClassAndMethod(this),
467 this);
468 }
469
848 .addClassAndMethod(this).addCalledMethod(this), this);
470849 }
471850
472851 /**
473852 * Since you can change the number of core threads for a scheduled
474853 * thread pool executor, disabling this for now
475 */
854 *
476855 if (false && seen == INVOKESPECIAL
477856 && getClassConstantOperand().equals("java/util/concurrent/ScheduledThreadPoolExecutor")
478857 && getNameConstantOperand().equals("<init>")) {
485864 HIGH_PRIORITY).addClassAndMethod(this), this);
486865
487866 }
488 if (seen == INVOKEVIRTUAL && getClassConstantOperand().equals("java/util/concurrent/ScheduledThreadPoolExecutor")
489 && getNameConstantOperand().equals("setMaximumPoolSize")) {
490 accumulator.accumulateBug(new BugInstance(this,
491 "DMI_FUTILE_ATTEMPT_TO_CHANGE_MAXPOOL_SIZE_OF_SCHEDULED_THREAD_POOL_EXECUTOR", HIGH_PRIORITY)
492 .addClassAndMethod(this), this);
493 }
494 if (isEqualsObject && !reportedBadCastInEquals) {
495 if (seen == INVOKEVIRTUAL && getNameConstantOperand().equals("isInstance")
496 && getClassConstantOperand().equals("java/lang/Class")) {
497 OpcodeStack.Item item = stack.getStackItem(0);
498 if (item.getRegisterNumber() == 1) {
499 sawInstanceofCheck = true;
500 }
501 } else if (seen == INSTANCEOF || seen == INVOKEVIRTUAL && getNameConstantOperand().equals("getClass")
502 && getSigConstantOperand().equals("()Ljava/lang/Class;")) {
503 OpcodeStack.Item item = stack.getStackItem(0);
504 if (item.getRegisterNumber() == 1) {
505 sawInstanceofCheck = true;
506 }
507 } else if (seen == INVOKESPECIAL && getNameConstantOperand().equals("equals")
508 && getSigConstantOperand().equals("(Ljava/lang/Object;)Z")) {
509 OpcodeStack.Item item0 = stack.getStackItem(0);
510 OpcodeStack.Item item1 = stack.getStackItem(1);
511 if (item1.getRegisterNumber() + item0.getRegisterNumber() == 1) {
512 sawInstanceofCheck = true;
513 }
514 } else if (seen == CHECKCAST && !sawInstanceofCheck) {
515 OpcodeStack.Item item = stack.getStackItem(0);
516 if (item.getRegisterNumber() == 1) {
517 if (getSizeOfSurroundingTryBlock(getPC()) == Integer.MAX_VALUE) {
518 accumulator.accumulateBug(new BugInstance(this, "BC_EQUALS_METHOD_SHOULD_WORK_FOR_ALL_OBJECTS",
519 NORMAL_PRIORITY).addClassAndMethod(this), this);
520 }
521
522 reportedBadCastInEquals = true;
523 }
524 }
525 }
526 {
527 boolean foundVacuousComparison = false;
528 if (seen == IF_ICMPGT || seen == IF_ICMPLE) {
529 OpcodeStack.Item rhs = stack.getStackItem(0);
530 Object rhsConstant = rhs.getConstant();
531 if (rhsConstant instanceof Integer && ((Integer) rhsConstant).intValue() == Integer.MAX_VALUE) {
532 foundVacuousComparison = true;
533 }
534 OpcodeStack.Item lhs = stack.getStackItem(1);
535 Object lhsConstant = lhs.getConstant();
536 if (lhsConstant instanceof Integer && ((Integer) lhsConstant).intValue() == Integer.MIN_VALUE) {
537 foundVacuousComparison = true;
538 }
539
540 }
541 if (seen == IF_ICMPLT || seen == IF_ICMPGE) {
542 OpcodeStack.Item rhs = stack.getStackItem(0);
543 Object rhsConstant = rhs.getConstant();
544 if (rhsConstant instanceof Integer && ((Integer) rhsConstant).intValue() == Integer.MIN_VALUE) {
545 foundVacuousComparison = true;
546 }
547 OpcodeStack.Item lhs = stack.getStackItem(1);
548 Object lhsConstant = lhs.getConstant();
549 if (lhsConstant instanceof Integer && ((Integer) lhsConstant).intValue() == Integer.MAX_VALUE) {
550 foundVacuousComparison = true;
551 }
552
553 }
554 if (foundVacuousComparison) {
555 accumulator.accumulateBug(new BugInstance(this, "INT_VACUOUS_COMPARISON", getBranchOffset() < 0 ? HIGH_PRIORITY
556 : NORMAL_PRIORITY).addClassAndMethod(this), this);
557 }
558
867 */
868 for(SubDetector subDetector : subDetectors) {
869 subDetector.sawOpcode(seen);
559870 }
560871
561872 if (!sawLoadOfMinValue && seen == INVOKESTATIC &&
562 ClassName.isMathClass(getClassConstantOperand()) && getNameConstantOperand().equals("abs")
873 ClassName.isMathClass(getClassConstantOperand()) && "abs".equals(getNameConstantOperand())
563874 ) {
564875 OpcodeStack.Item item0 = stack.getStackItem(0);
565876 int special = item0.getSpecialKind();
566877 if (special == OpcodeStack.Item.RANDOM_INT) {
567878 pendingAbsoluteValueBug = new BugInstance(this, "RV_ABSOLUTE_VALUE_OF_RANDOM_INT", HIGH_PRIORITY)
568 .addClassAndMethod(this);
879 .addClassAndMethod(this);
569880 pendingAbsoluteValueBugSourceLine = SourceLineAnnotation.fromVisitedInstruction(this);
570881 opcodesSincePendingAbsoluteValueBug = 0;
571882 }
572883
573884 else if (special == OpcodeStack.Item.HASHCODE_INT) {
574885 pendingAbsoluteValueBug = new BugInstance(this, "RV_ABSOLUTE_VALUE_OF_HASHCODE", HIGH_PRIORITY)
575 .addClassAndMethod(this);
886 .addClassAndMethod(this);
576887 pendingAbsoluteValueBugSourceLine = SourceLineAnnotation.fromVisitedInstruction(this);
577888 opcodesSincePendingAbsoluteValueBug = 0;
578889 }
593904 case OpcodeStack.Item.RANDOM_INT_REMAINDER:
594905 accumulator.accumulateBug(
595906 new BugInstance(this, "RV_REM_OF_RANDOM_INT", HIGH_PRIORITY).addClassAndMethod(this), this);
596
907 break;
908 default:
597909 break;
598910 }
599911
617929 int v = switchLabels[i];
618930 if (v <= -129 || v >= 128) {
619931 accumulator.accumulateBug(new BugInstance(this, "INT_BAD_COMPARISON_WITH_SIGNED_BYTE", HIGH_PRIORITY)
620 .addClassAndMethod(this).addInt(v).describe(IntAnnotation.INT_VALUE),
621 SourceLineAnnotation.fromVisitedInstruction(this, getPC() + switchOffsets[i]));
932 .addClassAndMethod(this).addInt(v).describe(IntAnnotation.INT_VALUE),
933 SourceLineAnnotation.fromVisitedInstruction(this, getPC() + switchOffsets[i]));
622934 }
623935
624936 }
654966 case IF_ICMPLE:
655967 seen2 = IF_ICMPGE;
656968 break;
657
969 default:
970 break;
658971 }
659972 }
660973 Object constant1 = item1.getConstant();
662975 int v1 = ((Number) constant1).intValue();
663976 if (v1 <= -129 || v1 >= 128 || v1 == 127 && !(seen2 == IF_ICMPEQ || seen2 == IF_ICMPNE
664977
665 )) {
978 )) {
666979 int priority = HIGH_PRIORITY;
667980 if (v1 == 127) {
668981 switch (seen2) {
6981011 priority = NORMAL_PRIORITY;
6991012 }
7001013
701 if (getPC() - sawCheckForNonNegativeSignedByte < 10)
1014 if (getPC() - sawCheckForNonNegativeSignedByte < 10) {
7021015 priority++;
1016 }
7031017
7041018 accumulator.accumulateBug(new BugInstance(this, "INT_BAD_COMPARISON_WITH_SIGNED_BYTE", priority)
705 .addClassAndMethod(this).addInt(v1).describe(IntAnnotation.INT_VALUE), this);
1019 .addClassAndMethod(this).addInt(v1).describe(IntAnnotation.INT_VALUE).addValueSource(item0, this), this);
7061020
7071021 }
7081022 } else if (item0.getSpecialKind() == OpcodeStack.Item.NON_NEGATIVE && constant1 instanceof Number) {
7091023 int v1 = ((Number) constant1).intValue();
7101024 if (v1 < 0) {
7111025 accumulator.accumulateBug(new BugInstance(this, "INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE",
712 HIGH_PRIORITY).addClassAndMethod(this).addInt(v1).describe(IntAnnotation.INT_VALUE), this);
1026 HIGH_PRIORITY).addClassAndMethod(this).addInt(v1).describe(IntAnnotation.INT_VALUE).addValueSource(item0, this), this);
7131027 }
7141028
7151029 }
7181032 }
7191033
7201034 switch (seen) {
1035 case IFGE:
1036 case IFLT:
1037 if(stack.getStackDepth() > 0 && stack.getStackItem(0).getSpecialKind() == OpcodeStack.Item.NON_NEGATIVE) {
1038 OpcodeStack.Item top = stack.getStackItem(0);
1039 if (top.getRegisterNumber() != -1 && getMaxPC() > getNextPC() + 6) {
1040 if (false) {
1041 for(int i = -2; i <= 0; i++) {
1042 int o = getPrevOpcode(-i);
1043 System.out.printf("%2d %3d %2x %s%n", i, o, o, OPCODE_NAMES[o]);
1044 }
1045 for(int i = 0; i < 7; i++) {
1046 int o = getNextCodeByte(i);
1047 System.out.printf("%2d %3d %2x %s%n", i, o, o, OPCODE_NAMES[o]);
1048
1049 }
1050 }
1051 int jump1, jump2;
1052 if (seen == IFGE) {
1053 jump1 = IF_ICMPLT;
1054 jump2 = IF_ICMPLE;
1055 } else {
1056 jump1 = IF_ICMPGE;
1057 jump2 = IF_ICMPGT;
1058 }
1059 int nextCodeByte0 = getNextCodeByte(0);
1060 int loadConstant = 1;
1061 if (nextCodeByte0 == ILOAD) {
1062 loadConstant = 2;
1063 }
1064 int nextCodeByte1 = getNextCodeByte(loadConstant);
1065 int nextCodeByte2 = getNextCodeByte(loadConstant+1);
1066 int nextJumpOffset = loadConstant+2;
1067 if (nextCodeByte1 == SIPUSH) {
1068 nextJumpOffset++;
1069 }
1070 int nextCodeByteJump = getNextCodeByte(nextJumpOffset);
1071
1072 if (nextCodeByte0 == getPrevOpcode(1)
1073 && (nextCodeByte1 == BIPUSH || nextCodeByte1 == SIPUSH)
1074 && (IF_ICMPLT <= nextCodeByteJump && nextCodeByteJump <= IF_ICMPLE))
1075 {
1076 break;
1077 }
1078 }
1079 accumulator.accumulateBug(new BugInstance(this, "INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE",
1080 NORMAL_PRIORITY).addClassAndMethod(this).addInt(0).describe(IntAnnotation.INT_VALUE).addValueSource(top, this), this);
1081 }
1082 break;
7211083 case IAND:
7221084 case LAND:
7231085 case IOR:
7311093 int prevPrevOpcode = getPrevOpcode(2);
7321094 if (rhs.hasConstantValue(badValue)
7331095 && (prevOpcode == LDC || prevOpcode == ICONST_0 || prevOpcode == ICONST_M1 || prevOpcode == LCONST_0)
734 && prevPrevOpcode != GOTO)
1096 && prevPrevOpcode != GOTO) {
7351097 reportVacuousBitOperation(seen, lhs);
1098 }
7361099
7371100 }
7381101
7601123 }
7611124
7621125 if (prevOpcodeWasReadLine && sinceBufferedInputStreamReady >= 100 && seen == INVOKEVIRTUAL
763 && getClassConstantOperand().equals("java/lang/String") && getSigConstantOperand().startsWith("()")) {
1126 && "java/lang/String".equals(getClassConstantOperand()) && getSigConstantOperand().startsWith("()")) {
7641127 accumulator.accumulateBug(
7651128 new BugInstance(this, "NP_IMMEDIATE_DEREFERENCE_OF_READLINE", NORMAL_PRIORITY).addClassAndMethod(this),
7661129 this);
7671130 }
7681131
769 if (seen == INVOKEVIRTUAL && getClassConstantOperand().equals("java/io/BufferedReader")
770 && getNameConstantOperand().equals("ready") && getSigConstantOperand().equals("()Z")) {
1132 if (seen == INVOKEVIRTUAL && "java/io/BufferedReader".equals(getClassConstantOperand())
1133 && "ready".equals(getNameConstantOperand()) && "()Z".equals(getSigConstantOperand())) {
7711134 sinceBufferedInputStreamReady = 0;
7721135 } else {
7731136 sinceBufferedInputStreamReady++;
7741137 }
7751138
7761139 prevOpcodeWasReadLine = (seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE)
777 && getNameConstantOperand().equals("readLine") && getSigConstantOperand().equals("()Ljava/lang/String;");
1140 && "readLine".equals(getNameConstantOperand()) && "()Ljava/lang/String;".equals(getSigConstantOperand());
7781141
7791142 // System.out.println(randomNextIntState + " " + OPCODE_NAMES[seen]
7801143 // + " " + getMethodName());
7811144 switch (randomNextIntState) {
7821145 case 0:
783 if (seen == INVOKEVIRTUAL && getClassConstantOperand().equals("java/util/Random")
784 && getNameConstantOperand().equals("nextDouble") || seen == INVOKESTATIC
785 && ClassName.isMathClass(getClassConstantOperand()) && getNameConstantOperand().equals("random")) {
1146 if (seen == INVOKEVIRTUAL && "java/util/Random".equals(getClassConstantOperand())
1147 && "nextDouble".equals(getNameConstantOperand()) || seen == INVOKESTATIC
1148 && ClassName.isMathClass(getClassConstantOperand()) && "random".equals(getNameConstantOperand())) {
7861149 randomNextIntState = 1;
7871150 }
7881151 break;
7901153 if (seen == D2I) {
7911154 accumulator.accumulateBug(new BugInstance(this, "RV_01_TO_INT", HIGH_PRIORITY).addClassAndMethod(this), this);
7921155 randomNextIntState = 0;
793 } else if (seen == DMUL)
1156 } else if (seen == DMUL) {
7941157 randomNextIntState = 4;
795 else if (seen == LDC2_W && getConstantRefOperand() instanceof ConstantDouble
796 && ((ConstantDouble) getConstantRefOperand()).getBytes() == Integer.MIN_VALUE)
1158 } else if (seen == LDC2_W && getConstantRefOperand() instanceof ConstantDouble
1159 && ((ConstantDouble) getConstantRefOperand()).getBytes() == Integer.MIN_VALUE) {
7971160 randomNextIntState = 0;
798 else
1161 } else {
7991162 randomNextIntState = 2;
1163 }
8001164
8011165 break;
8021166 case 2:
8281192 if (isPublicStaticVoidMain
8291193 && seen == INVOKEVIRTUAL
8301194 && getClassConstantOperand().startsWith("javax/swing/")
831 && (getNameConstantOperand().equals("show") && getSigConstantOperand().equals("()V")
832 || getNameConstantOperand().equals("pack") && getSigConstantOperand().equals("()V") || getNameConstantOperand()
833 .equals("setVisible") && getSigConstantOperand().equals("(Z)V"))) {
1195 && ("show".equals(getNameConstantOperand()) && "()V".equals(getSigConstantOperand())
1196 || "pack".equals(getNameConstantOperand()) && "()V".equals(getSigConstantOperand()) || "setVisible".equals(getNameConstantOperand()) && "(Z)V".equals(getSigConstantOperand()))) {
8341197 accumulator.accumulateBug(
8351198 new BugInstance(this, "SW_SWING_METHODS_INVOKED_IN_SWING_THREAD", LOW_PRIORITY).addClassAndMethod(this),
8361199 this);
8531216 // }
8541217 // }
8551218
856 if ((seen == INVOKEVIRTUAL) && getNameConstantOperand().equals("isAnnotationPresent")
857 && getSigConstantOperand().equals("(Ljava/lang/Class;)Z") && stack.getStackDepth() > 0) {
1219 if ((seen == INVOKEVIRTUAL) && "isAnnotationPresent".equals(getNameConstantOperand())
1220 && "(Ljava/lang/Class;)Z".equals(getSigConstantOperand()) && stack.getStackDepth() > 0) {
8581221 OpcodeStack.Item item = stack.getStackItem(0);
8591222 Object value = item.getConstant();
8601223 if (value instanceof String) {
8651228 ClassDescriptor annotationClass = DescriptorFactory.createClassDescriptor(annotationClassName);
8661229 accumulator.accumulateBug(
8671230 new BugInstance(this, "DMI_ANNOTATION_IS_NOT_VISIBLE_TO_REFLECTION", HIGH_PRIORITY)
868 .addClassAndMethod(this).addCalledMethod(this).addClass(annotationClass)
869 .describe(ClassAnnotation.ANNOTATION_ROLE), this);
870 }
871
872 }
873
874 }
875 if ((seen == INVOKEVIRTUAL) && getNameConstantOperand().equals("next")
876 && getSigConstantOperand().equals("()Ljava/lang/Object;") && getMethodName().equals("hasNext")
877 && getMethodSig().equals("()Z") && stack.getStackDepth() > 0) {
1231 .addClassAndMethod(this).addCalledMethod(this).addClass(annotationClass)
1232 .describe(ClassAnnotation.ANNOTATION_ROLE), this);
1233 }
1234
1235 }
1236
1237 }
1238 if ((seen == INVOKEVIRTUAL) && "next".equals(getNameConstantOperand())
1239 && "()Ljava/lang/Object;".equals(getSigConstantOperand()) && "hasNext".equals(getMethodName())
1240 && "()Z".equals(getMethodSig()) && stack.getStackDepth() > 0) {
8781241 OpcodeStack.Item item = stack.getStackItem(0);
8791242
8801243 accumulator.accumulateBug(new BugInstance(this, "DMI_CALLING_NEXT_FROM_HASNEXT", item.isInitialParameter()
8831246
8841247 }
8851248
886 if ((seen == INVOKESPECIAL) && getClassConstantOperand().equals("java/lang/String")
887 && getNameConstantOperand().equals("<init>") && getSigConstantOperand().equals("(Ljava/lang/String;)V")
1249 if ((seen == INVOKESPECIAL) && "java/lang/String".equals(getClassConstantOperand())
1250 && "<init>".equals(getNameConstantOperand()) && "(Ljava/lang/String;)V".equals(getSigConstantOperand())
8881251 && !Subtypes2.isJSP(getThisClass())) {
8891252
8901253 accumulator.accumulateBug(new BugInstance(this, "DM_STRING_CTOR", NORMAL_PRIORITY).addClassAndMethod(this), this);
8911254
8921255 }
8931256
894 if (seen == INVOKESTATIC && getClassConstantOperand().equals("java/lang/System")
895 && getNameConstantOperand().equals("runFinalizersOnExit") || seen == INVOKEVIRTUAL
896 && getClassConstantOperand().equals("java/lang/Runtime")
897 && getNameConstantOperand().equals("runFinalizersOnExit")) {
1257 if (seen == INVOKESTATIC && "java/lang/System".equals(getClassConstantOperand())
1258 && "runFinalizersOnExit".equals(getNameConstantOperand()) || seen == INVOKEVIRTUAL
1259 && "java/lang/Runtime".equals(getClassConstantOperand())
1260 && "runFinalizersOnExit".equals(getNameConstantOperand())) {
8981261 accumulator.accumulateBug(
8991262 new BugInstance(this, "DM_RUN_FINALIZERS_ON_EXIT", HIGH_PRIORITY).addClassAndMethod(this), this);
9001263 }
9011264
902 if ((seen == INVOKESPECIAL) && getClassConstantOperand().equals("java/lang/String")
903 && getNameConstantOperand().equals("<init>") && getSigConstantOperand().equals("()V")) {
1265 if ((seen == INVOKESPECIAL) && "java/lang/String".equals(getClassConstantOperand())
1266 && "<init>".equals(getNameConstantOperand()) && "()V".equals(getSigConstantOperand())) {
9041267
9051268 accumulator.accumulateBug(new BugInstance(this, "DM_STRING_VOID_CTOR", NORMAL_PRIORITY).addClassAndMethod(this),
9061269 this);
9071270
9081271 }
9091272
910 if (!isPublicStaticVoidMain && seen == INVOKESTATIC && getClassConstantOperand().equals("java/lang/System")
911 && getNameConstantOperand().equals("exit") && !getMethodName().equals("processWindowEvent")
1273 if (!isPublicStaticVoidMain && seen == INVOKESTATIC && "java/lang/System".equals(getClassConstantOperand())
1274 && "exit".equals(getNameConstantOperand()) && !"processWindowEvent".equals(getMethodName())
9121275 && !getMethodName().startsWith("windowClos") && getMethodName().indexOf("exit") == -1
9131276 && getMethodName().indexOf("Exit") == -1 && getMethodName().indexOf("crash") == -1
9141277 && getMethodName().indexOf("Crash") == -1 && getMethodName().indexOf("die") == -1
9161279 accumulator.accumulateBug(new BugInstance(this, "DM_EXIT", getMethod().isStatic() ? LOW_PRIORITY
9171280 : NORMAL_PRIORITY).addClassAndMethod(this), SourceLineAnnotation.fromVisitedInstruction(this));
9181281 }
919 if (((seen == INVOKESTATIC && getClassConstantOperand().equals("java/lang/System")) || (seen == INVOKEVIRTUAL && getClassConstantOperand()
920 .equals("java/lang/Runtime")))
921 && getNameConstantOperand().equals("gc")
922 && getSigConstantOperand().equals("()V")
1282 if (((seen == INVOKESTATIC && "java/lang/System".equals(getClassConstantOperand())) || (seen == INVOKEVIRTUAL && "java/lang/Runtime".equals(getClassConstantOperand())))
1283 && "gc".equals(getNameConstantOperand())
1284 && "()V".equals(getSigConstantOperand())
9231285 && !getDottedClassName().startsWith("java.lang")
9241286 && !getMethodName().startsWith("gc") && !getMethodName().endsWith("gc")) {
9251287 if (gcInvocationBugReport == null) {
9401302 // System.out.println("GC invocation at pc " + PC);
9411303 }
9421304 }
943 if (!isSynthetic && (seen == INVOKESPECIAL) && getClassConstantOperand().equals("java/lang/Boolean")
944 && getNameConstantOperand().equals("<init>") && !getClassName().equals("java/lang/Boolean")) {
1305 if (!isSynthetic && (seen == INVOKESPECIAL) && "java/lang/Boolean".equals(getClassConstantOperand())
1306 && "<init>".equals(getNameConstantOperand()) && !"java/lang/Boolean".equals(getClassName())) {
9451307 int majorVersion = getThisClass().getMajor();
9461308 if (majorVersion >= MAJOR_1_4) {
9471309 accumulator.accumulateBug(new BugInstance(this, "DM_BOOLEAN_CTOR", NORMAL_PRIORITY).addClassAndMethod(this),
9491311 }
9501312
9511313 }
952 if ((seen == INVOKESTATIC) && getClassConstantOperand().equals("java/lang/System")
953 && (getNameConstantOperand().equals("currentTimeMillis") || getNameConstantOperand().equals("nanoTime"))) {
1314 if ((seen == INVOKESTATIC) && "java/lang/System".equals(getClassConstantOperand())
1315 && ("currentTimeMillis".equals(getNameConstantOperand()) || "nanoTime".equals(getNameConstantOperand()))) {
9541316 sawCurrentTimeMillis = true;
9551317 }
956 if ((seen == INVOKEVIRTUAL) && getClassConstantOperand().equals("java/lang/String")
957 && getNameConstantOperand().equals("toString") && getSigConstantOperand().equals("()Ljava/lang/String;")) {
1318 if ((seen == INVOKEVIRTUAL) && "java/lang/String".equals(getClassConstantOperand())
1319 && "toString".equals(getNameConstantOperand()) && "()Ljava/lang/String;".equals(getSigConstantOperand())) {
9581320
9591321 accumulator
960 .accumulateBug(new BugInstance(this, "DM_STRING_TOSTRING", LOW_PRIORITY).addClassAndMethod(this), this);
961
962 }
963
964 if ((seen == INVOKEVIRTUAL) && getClassConstantOperand().equals("java/lang/String")
965 && (getNameConstantOperand().equals("toUpperCase") || getNameConstantOperand().equals("toLowerCase"))
966 && getSigConstantOperand().equals("()Ljava/lang/String;")) {
1322 .accumulateBug(new BugInstance(this, "DM_STRING_TOSTRING", LOW_PRIORITY).addClassAndMethod(this), this);
1323
1324 }
1325
1326 if ((seen == INVOKEVIRTUAL) && "java/lang/String".equals(getClassConstantOperand())
1327 && ("toUpperCase".equals(getNameConstantOperand()) || "toLowerCase".equals(getNameConstantOperand()))
1328 && "()Ljava/lang/String;".equals(getSigConstantOperand())) {
9671329
9681330 accumulator.accumulateBug(new BugInstance(this, "DM_CONVERT_CASE", LOW_PRIORITY).addClassAndMethod(this), this);
9691331
9701332 }
9711333
972 if ((seen == INVOKESPECIAL) && getNameConstantOperand().equals("<init>")) {
1334 if ((seen == INVOKESPECIAL) && "<init>".equals(getNameConstantOperand())) {
9731335 String cls = getClassConstantOperand();
9741336 String sig = getSigConstantOperand();
9751337 String primitiveType = ClassName.getPrimitiveType(cls);
9781340 } else {
9791341 primitiveObjCtorSeen = null;
9801342 }
981 } else if ((primitiveObjCtorSeen != null) && (seen == INVOKEVIRTUAL) && getNameConstantOperand().equals("toString")
1343 } else if ((primitiveObjCtorSeen != null) && (seen == INVOKEVIRTUAL) && "toString".equals(getNameConstantOperand())
9821344 && getClassConstantOperand().equals(primitiveObjCtorSeen)
983 && getSigConstantOperand().equals("()Ljava/lang/String;")) {
1345 && "()Ljava/lang/String;".equals(getSigConstantOperand())) {
9841346 BugInstance bug = new BugInstance(this, "DM_BOXED_PRIMITIVE_TOSTRING", NORMAL_PRIORITY).addClassAndMethod(this).addCalledMethod(this);
9851347 MethodAnnotation preferred = new MethodAnnotation(ClassName.toDottedClassName(primitiveObjCtorSeen),
9861348 "toString", "("+ClassName.getPrimitiveType(primitiveObjCtorSeen)+")Ljava/lang/String;", true);
9931355 primitiveObjCtorSeen = null;
9941356 }
9951357
996 if ((seen == INVOKESPECIAL) && getNameConstantOperand().equals("<init>")) {
1358 if ((seen == INVOKESPECIAL) && "<init>".equals(getNameConstantOperand())) {
9971359 ctorSeen = true;
998 } else if (ctorSeen && (seen == INVOKEVIRTUAL) && getClassConstantOperand().equals("java/lang/Object")
999 && getNameConstantOperand().equals("getClass") && getSigConstantOperand().equals("()Ljava/lang/Class;")) {
1360 } else if (ctorSeen && (seen == INVOKEVIRTUAL) && "java/lang/Object".equals(getClassConstantOperand())
1361 && "getClass".equals(getNameConstantOperand()) && "()Ljava/lang/Class;".equals(getSigConstantOperand())) {
10001362 accumulator.accumulateBug(new BugInstance(this, "DM_NEW_FOR_GETCLASS", NORMAL_PRIORITY).addClassAndMethod(this),
10011363 this);
10021364 ctorSeen = false;
10041366 ctorSeen = false;
10051367 }
10061368
1007 if (jdk15ChecksEnabled && (seen == INVOKEVIRTUAL) && isMonitorWait(getNameConstantOperand(), getSigConstantOperand())) {
1369 if ((seen == INVOKEVIRTUAL) && isMonitorWait(getNameConstantOperand(), getSigConstantOperand())) {
10081370 checkMonitorWait();
10091371 }
10101372
1011 if ((seen == INVOKESPECIAL) && getNameConstantOperand().equals("<init>")
1012 && getClassConstantOperand().equals("java/lang/Thread")) {
1373 if ((seen == INVOKESPECIAL) && "<init>".equals(getNameConstantOperand())
1374 && "java/lang/Thread".equals(getClassConstantOperand())) {
10131375 String sig = getSigConstantOperand();
1014 if (sig.equals("()V") || sig.equals("(Ljava/lang/String;)V")
1015 || sig.equals("(Ljava/lang/ThreadGroup;Ljava/lang/String;)V")) {
1376 if ("()V".equals(sig) || "(Ljava/lang/String;)V".equals(sig)
1377 || "(Ljava/lang/ThreadGroup;Ljava/lang/String;)V".equals(sig)) {
10161378 OpcodeStack.Item invokedOn = stack.getItemMethodInvokedOn(this);
1017 if (!getMethodName().equals("<init>") || invokedOn.getRegisterNumber() != 0) {
1379 if (!"<init>".equals(getMethodName()) || invokedOn.getRegisterNumber() != 0) {
10181380 accumulator.accumulateBug(
10191381 new BugInstance(this, "DM_USELESS_THREAD", LOW_PRIORITY).addClassAndMethod(this), this);
10201382
10221384 }
10231385 }
10241386
1025 if (seen == INVOKESPECIAL && getClassConstantOperand().equals("java/math/BigDecimal")
1026 && getNameConstantOperand().equals("<init>") && getSigConstantOperand().equals("(D)V")) {
1387 if (seen == INVOKESPECIAL && "java/math/BigDecimal".equals(getClassConstantOperand())
1388 && "<init>".equals(getNameConstantOperand()) && "(D)V".equals(getSigConstantOperand())) {
10271389 OpcodeStack.Item top = stack.getStackItem(0);
10281390 Object value = top.getConstant();
1029 if (value instanceof Double) {
1391 if (value instanceof Double && !((Double)value).isInfinite() && !((Double)value).isNaN()) {
10301392 double arg = ((Double) value).doubleValue();
10311393 String dblString = Double.toString(arg);
10321394 String bigDecimalString = new BigDecimal(arg).toString();
10341396
10351397 if (!ok) {
10361398 boolean scary = dblString.length() <= 8 && bigDecimalString.length() > 12
1037 && dblString.toUpperCase().indexOf("E") == -1;
1399 && dblString.toUpperCase().indexOf('E') == -1;
10381400 bugReporter.reportBug(new BugInstance(this, "DMI_BIGDECIMAL_CONSTRUCTED_FROM_DOUBLE",
10391401 scary ? NORMAL_PRIORITY : LOW_PRIORITY).addClassAndMethod(this).addCalledMethod(this)
10401402 .addMethod("java.math.BigDecimal", "valueOf", "(D)Ljava/math/BigDecimal;", true)
10551417 long value = ((Number) right.getConstant()).longValue();
10561418 if ( (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE)) {
10571419 int priority = Priorities.HIGH_PRIORITY;
1058 if (value == Integer.MAX_VALUE+1 || value == Integer.MIN_VALUE -1)
1420 if (value == Integer.MAX_VALUE+1L || value == Integer.MIN_VALUE-1L) {
10591421 priority = Priorities.NORMAL_PRIORITY;
1422 }
10601423 String stringValue = IntAnnotation.getShortInteger(value)+"L";
1061 if (value == 0xffffffffL)
1424 if (value == 0xffffffffL) {
10621425 stringValue = "0xffffffffL";
1063 else if (value == 0x80000000L)
1426 } else if (value == 0x80000000L) {
10641427 stringValue = "0x80000000L";
1428 }
10651429 accumulator.accumulateBug(new BugInstance(this, "INT_BAD_COMPARISON_WITH_INT_VALUE", priority ).addClassAndMethod(this)
10661430 .addString(stringValue).describe(StringAnnotation.STRING_NONSTRING_CONSTANT_ROLE)
10671431 .addValueSource(left, this) , this);
10741438 * @param item
10751439 */
10761440 private void reportVacuousBitOperation(int seen, OpcodeStack.Item item) {
1077 if (item.getConstant() == null)
1441 if (item.getConstant() == null) {
10781442 accumulator
1079 .accumulateBug(
1080 new BugInstance(this, "INT_VACUOUS_BIT_OPERATION", NORMAL_PRIORITY)
1081 .addClassAndMethod(this)
1082 .addString(OPCODE_NAMES[seen])
1083 .addOptionalAnnotation(
1084 LocalVariableAnnotation.getLocalVariableAnnotation(getMethod(), item, getPC())), this);
1443 .accumulateBug(
1444 new BugInstance(this, "INT_VACUOUS_BIT_OPERATION", NORMAL_PRIORITY)
1445 .addClassAndMethod(this)
1446 .addString(OPCODE_NAMES[seen])
1447 .addOptionalAnnotation(
1448 LocalVariableAnnotation.getLocalVariableAnnotation(getMethod(), item, getPC())), this);
1449 }
10851450 }
10861451
10871452 /**
10881453 * Return index of stack entry that must be nonnegative.
10891454 *
10901455 * Return -1 if no stack entry is required to be nonnegative.
1091 *
1092 * @param seen
1093 * @return
10941456 */
10951457 private int stackEntryThatMustBeNonnegative(int seen) {
10961458 switch (seen) {
10971459 case INVOKEINTERFACE:
1098 if (getClassConstantOperand().equals("java/util/List")) {
1460 if ("java/util/List".equals(getClassConstantOperand())) {
10991461 return getStackEntryOfListCallThatMustBeNonnegative();
11001462 }
11011463 break;
11021464 case INVOKEVIRTUAL:
1103 if (getClassConstantOperand().equals("java/util/LinkedList")
1104 || getClassConstantOperand().equals("java/util/ArrayList")) {
1465 if ("java/util/LinkedList".equals(getClassConstantOperand())
1466 || "java/util/ArrayList".equals(getClassConstantOperand())) {
11051467 return getStackEntryOfListCallThatMustBeNonnegative();
11061468 }
11071469 break;
11301492
11311493 private int getStackEntryOfListCallThatMustBeNonnegative() {
11321494 String name = getNameConstantOperand();
1133 if ((name.equals("add") || name.equals("set")) && getSigConstantOperand().startsWith("(I")) {
1495 if (("add".equals(name) || "set".equals(name)) && getSigConstantOperand().startsWith("(I")) {
11341496 return 1;
11351497 }
1136 if ((name.equals("get") || name.equals("remove")) && getSigConstantOperand().startsWith("(I)")) {
1498 if (("get".equals(name) || "remove".equals(name)) && getSigConstantOperand().startsWith("(I)")) {
11371499 return 0;
11381500 }
11391501 return -1;
11711533
11721534 private boolean isMonitorWait(String name, String sig) {
11731535 // System.out.println("Check call " + name + "," + sig);
1174 return name.equals("wait") && (sig.equals("()V") || sig.equals("(J)V") || sig.equals("(JI)V"));
1536 return "wait".equals(name) && ("()V".equals(sig) || "(J)V".equals(sig) || "(JI)V".equals(sig));
11751537 }
11761538
11771539 @Override
11801542 super.visit(obj);
11811543 flush();
11821544 }
1183
1184 /**
1185 * A heuristic - how long a catch block for OutOfMemoryError might be.
1186 */
1187 private static final int OOM_CATCH_LEN = 20;
11881545
11891546 /**
11901547 * Flush out cached state at the end of a method.
11911548 */
11921549 private void flush() {
1193
1550
11941551 if (pendingAbsoluteValueBug != null) {
11951552 absoluteValueAccumulator.accumulateBug(pendingAbsoluteValueBug, pendingAbsoluteValueBugSourceLine);
11961553 pendingAbsoluteValueBug = null;
11971554 pendingAbsoluteValueBugSourceLine = null;
11981555 }
11991556 accumulator.reportAccumulatedBugs();
1200 if (sawLoadOfMinValue)
1557 if (sawLoadOfMinValue) {
12011558 absoluteValueAccumulator.clearBugs();
1202 else
1559 } else {
12031560 absoluteValueAccumulator.reportAccumulatedBugs();
1561 }
12041562 if (gcInvocationBugReport != null && !sawCurrentTimeMillis) {
12051563 // Make sure the GC invocation is not in an exception handler
12061564 // for OutOfMemoryError.
12151573 Constant constant = cp.getConstant(catchTypeIndex);
12161574 if (constant instanceof ConstantClass) {
12171575 String exClassName = (String) ((ConstantClass) constant).getConstantValue(cp);
1218 if (exClassName.equals("java/lang/OutOfMemoryError")) {
1576 if ("java/lang/OutOfMemoryError".equals(exClassName)) {
12191577 outOfMemoryHandler = true;
12201578 break;
12211579 }
5555 public class DuplicateBranches extends PreorderVisitor implements Detector {
5656 private ClassContext classContext;
5757
58 private BugReporter bugReporter;
59
60 private Collection<BugInstance> pendingBugs = new LinkedList<BugInstance>();
58 private final BugReporter bugReporter;
59
60 private final Collection<BugInstance> pendingBugs = new LinkedList<BugInstance>();
6161
6262 public DuplicateBranches(BugReporter bugReporter) {
6363 this.bugReporter = bugReporter;
6464 }
6565
66 @Override
6667 public void visitClassContext(ClassContext classContext) {
6768 this.classContext = classContext;
6869 classContext.getJavaClass().accept(this);
7172 @Override
7273 public void visitMethod(Method method) {
7374 try {
74 if (method.getCode() == null)
75 if (method.getCode() == null) {
7576 return;
77 }
7678
7779 CFG cfg = classContext.getCFG(method);
7880
8183 BasicBlock bb = bbi.next();
8284
8385 int numOutgoing = cfg.getNumOutgoingEdges(bb);
84 if (numOutgoing == 2)
86 if (numOutgoing == 2) {
8587 findIfElseDuplicates(cfg, method, bb);
86 else if (numOutgoing > 2)
88 } else if (numOutgoing > 2) {
8789 findSwitchDuplicates(cfg, method, bb);
90 }
8891 }
8992 } catch (MethodUnprofitableException mue) {
90 if (SystemProperties.getBoolean("unprofitable.debug")) // otherwise
91 // don't
92 // report
93 if (SystemProperties.getBoolean("unprofitable.debug")) {
94 // don't
95 // report
9396 bugReporter.logError("skipping unprofitable method in " + getClass().getName());
97 }
9498 } catch (Exception e) {
9599 bugReporter.logError("Failure examining basic blocks in Duplicate Branches detector", e);
96100 }
97 if (pendingBugs.size() <= 2)
98 for (BugInstance b : pendingBugs)
101 if (pendingBugs.size() <= 2) {
102 for (BugInstance b : pendingBugs) {
99103 bugReporter.reportBug(b);
104 }
105 }
100106 pendingBugs.clear();
101107
102108 }
114120 }
115121 }
116122
117 if ((thenBB == null) || (elseBB == null))
118 return;
123 if ((thenBB == null) || (elseBB == null)) {
124 return;
125 }
119126 InstructionHandle thenStartHandle = getDeepFirstInstruction(cfg, thenBB);
120127 InstructionHandle elseStartHandle = getDeepFirstInstruction(cfg, elseBB);
121 if ((thenStartHandle == null) || (elseStartHandle == null))
122 return;
128 if ((thenStartHandle == null) || (elseStartHandle == null)) {
129 return;
130 }
123131
124132 int thenStartPos = thenStartHandle.getPosition();
125133 int elseStartPos = elseStartHandle.getPosition();
127135 InstructionHandle thenFinishIns = findThenFinish(cfg, thenBB, elseStartPos);
128136 int thenFinishPos = thenFinishIns.getPosition();
129137
130 if (!(thenFinishIns.getInstruction() instanceof GotoInstruction))
131 return;
138 if (!(thenFinishIns.getInstruction() instanceof GotoInstruction)) {
139 return;
140 }
132141
133142 InstructionHandle elseFinishHandle = ((GotoInstruction) thenFinishIns.getInstruction()).getTarget();
134143 int elseFinishPos = elseFinishHandle.getPosition();
135144
136 if (thenFinishPos >= elseStartPos)
137 return;
138
139 if ((thenFinishPos - thenStartPos) != (elseFinishPos - elseStartPos))
140 return;
141
142 if (thenFinishPos <= thenStartPos)
143 return;
145 if (thenFinishPos >= elseStartPos) {
146 return;
147 }
148
149 if ((thenFinishPos - thenStartPos) != (elseFinishPos - elseStartPos)) {
150 return;
151 }
152
153 if (thenFinishPos <= thenStartPos) {
154 return;
155 }
144156
145157 byte[] thenBytes = getCodeBytes(method, thenStartPos, thenFinishPos);
146158 byte[] elseBytes = getCodeBytes(method, elseStartPos, elseFinishPos);
147159
148 if (!Arrays.equals(thenBytes, elseBytes))
149 return;
160 if (!Arrays.equals(thenBytes, elseBytes)) {
161 return;
162 }
150163
151164 // adjust elseFinishPos to be inclusive (for source line attribution)
152165 InstructionHandle elseLastIns = elseFinishHandle.getPrev();
153 if (elseLastIns != null)
166 if (elseLastIns != null) {
154167 elseFinishPos = elseLastIns.getPosition();
168 }
155169
156170 pendingBugs.add(new BugInstance(this, "DB_DUPLICATE_BRANCHES", NORMAL_PRIORITY)
157 .addClassAndMethod(classContext.getJavaClass(), method)
158 .addSourceLineRange(classContext, this, thenStartPos, thenFinishPos)
159 .addSourceLineRange(classContext, this, elseStartPos, elseFinishPos));
171 .addClassAndMethod(classContext.getJavaClass(), method)
172 .addSourceLineRange(classContext, this, thenStartPos, thenFinishPos)
173 .addSourceLineRange(classContext, this, elseStartPos, elseFinishPos));
160174 }
161175
162176 /**
165179 */
166180 private static InstructionHandle getDeepFirstInstruction(CFG cfg, BasicBlock bb) {
167181 InstructionHandle ih = bb.getFirstInstruction();
168 if (ih != null)
182 if (ih != null) {
169183 return ih;
184 }
170185 Iterator<Edge> iei = cfg.outgoingEdgeIterator(bb);
171186 while (iei.hasNext()) {
172187 Edge e = iei.next();
173188 String edgeString = e.toString();
174 if (EdgeTypes.FALL_THROUGH_EDGE == e.getType())
189 if (EdgeTypes.FALL_THROUGH_EDGE == e.getType()) {
175190 return getDeepFirstInstruction(cfg, e.getTarget());
191 }
176192 }
177193 return null;
178194 }
192208 BasicBlock target = e.getTarget();
193209 InstructionHandle firstIns = getDeepFirstInstruction(cfg, target);
194210 if (firstIns == null)
211 {
195212 continue; // give up on this edge
213 }
196214 int firstInsPosition = firstIns.getPosition();
197215 switchPos[idx++] = firstInsPosition;
198216 InstructionHandle prevIns = firstIns.getPrev(); // prev in
199 // bytecode, not
200 // flow
201 if (prevIns != null)
217 // bytecode, not
218 // flow
219 if (prevIns != null) {
202220 prevHandle.put(firstInsPosition, prevIns);
221 }
203222 } else {
204223 // hmm, this must not be a switch statement, so give up
205224 return;
206225 }
207226 }
208227
209 if (idx < 2) // need at least two edges to tango
210 return;
228 if (idx < 2) {
229 return;
230 }
211231
212232 Arrays.sort(switchPos, 0, idx); // sort the 'idx' switch positions
213233
218238 HashMap<BigInteger, Collection<Integer>> map = new HashMap<BigInteger, Collection<Integer>>();
219239 for (int i = 0; i < idx; i++) {
220240 if (switchPos[i] + 7 >= switchPos[i + 1])
241 {
221242 continue; // ignore small switch clauses
243 }
222244
223245 int endPos = switchPos[i + 1];
224246 InstructionHandle last = prevHandle.get(switchPos[i + 1]);
233255 // Don't do this since many cases may throw "not implemented".
234256 } else {
235257 if (i + 2 < idx)
258 {
236259 continue; // falls through to next case, so don't store it
237 // at all
260 }
261 // at all
238262 if (i + 1 < idx && switchPos[idx] != switchPos[idx - 1])
263 {
239264 continue; // also falls through unless switch has no default
240 // case
265 // case
266 }
241267 }
242268
243269 BigInteger clauseAsInt = getCodeBytesAsBigInt(method, switchPos, i, endPos);
249275 BugInstance bug = new BugInstance(this, "DB_DUPLICATE_SWITCH_CLAUSES", LOW_PRIORITY).addClassAndMethod(
250276 classContext.getJavaClass(), method);
251277 for (int i : clauses)
278 {
252279 bug.addSourceLineRange(this.classContext, this, switchPos[i], switchPos[i + 1] - 1); // not
253 // endPos,
254 // but
255 // that's
256 // ok
280 }
281 // endPos,
282 // but
283 // that's
284 // ok
257285 pendingBugs.add(bug);
258286 }
259287 }
273301 byte[] clause = getCodeBytes(method, switchPos[i], endPos);
274302
275303 BigInteger clauseAsInt;
276 if (clause.length == 0)
304 if (clause.length == 0) {
277305 clauseAsInt = BigInteger.ZERO;
278 else
306 } else {
279307 clauseAsInt = new BigInteger(clause);
308 }
280309 return clauseAsInt;
281310 }
282311
304333 InstructionHandle targetFirst = getDeepFirstInstruction(cfg, target);
305334 if (targetFirst != null) {
306335 int targetPos = targetFirst.getPosition();
307 if (targetPos > maxGoto)
336 if (targetPos > maxGoto) {
308337 maxGoto = targetPos;
338 }
309339 }
310340 }
311341 }
394424 return lastIns;
395425 }
396426
427 @Override
397428 public void report() {
398429 }
399430 }
2929 /**
3030 * This detector is currently disabled by default.
3131 * It generates false positives when creating directory entries.
32 *
32 *
3333 */
3434 public class EmptyZipFileEntry extends BytecodeScanningDetector implements StatelessDetector {
3535
5555
5656 @Override
5757 public void sawOpcode(int seen) {
58 if (seen == INVOKEVIRTUAL && getNameConstantOperand().equals("putNextEntry")) {
58 if (seen == INVOKEVIRTUAL && "putNextEntry".equals(getNameConstantOperand())) {
5959 streamType = getClassConstantOperand();
60 if (streamType.equals("java/util/zip/ZipOutputStream") || streamType.equals("java/util/jar/JarOutputStream"))
60 if ("java/util/zip/ZipOutputStream".equals(streamType) || "java/util/jar/JarOutputStream".equals(streamType)) {
6161 sawPutEntry = getPC();
62 else
62 } else {
6363 streamType = "";
64 }
6465 } else {
65 if (getPC() - sawPutEntry <= 7 && seen == INVOKEVIRTUAL && getNameConstantOperand().equals("closeEntry")
66 && getClassConstantOperand().equals(streamType))
66 if (getPC() - sawPutEntry <= 7 && seen == INVOKEVIRTUAL && "closeEntry".equals(getNameConstantOperand())
67 && getClassConstantOperand().equals(streamType)) {
6768 bugReporter
68 .reportBug(new BugInstance(this,
69 streamType.equals("java/util/zip/ZipOutputStream") ? "AM_CREATES_EMPTY_ZIP_FILE_ENTRY"
70 : "AM_CREATES_EMPTY_JAR_FILE_ENTRY", NORMAL_PRIORITY).addClassAndMethod(this)
69 .reportBug(new BugInstance(this,
70 "java/util/zip/ZipOutputStream".equals(streamType) ? "AM_CREATES_EMPTY_ZIP_FILE_ENTRY"
71 : "AM_CREATES_EMPTY_JAR_FILE_ENTRY", NORMAL_PRIORITY).addClassAndMethod(this)
7172 .addSourceLine(this));
73 }
7274
7375 }
7476
5353
5454 @Override
5555 public void visit(Code obj) {
56 if (getMethodName().equals("equals") && getMethodSig().equals("(Ljava/lang/Object;)Z")) {
56 if ("equals".equals(getMethodName()) && "(Ljava/lang/Object;)Z".equals(getMethodSig())) {
5757 super.visit(obj);
58 if (AnalysisContext.currentAnalysisContext().isApplicationClass(getThisClass()))
58 if (AnalysisContext.currentAnalysisContext().isApplicationClass(getThisClass())) {
5959 bugAccumulator.reportAccumulatedBugs();
60 }
6061 bugAccumulator.clearBugs();
6162 }
6263
6465
6566 /*
6667 * (non-Javadoc)
67 *
68 *
6869 * @see edu.umd.cs.findbugs.bcel.OpcodeStackDetector#sawOpcode(int)
6970 */
7071 @Override
7172 public void sawOpcode(int seen) {
7273 if (seen == INVOKEVIRTUAL) {
73 if (getNameConstantOperand().equals("equals") && getSigConstantOperand().equals("(Ljava/lang/Object;)Z")) {
74 if ("equals".equals(getNameConstantOperand()) && "(Ljava/lang/Object;)Z".equals(getSigConstantOperand())) {
7475 OpcodeStack.Item item = stack.getStackItem(1);
7576 ClassDescriptor c = DescriptorFactory.createClassDescriptorFromSignature(item.getSignature());
7677 check(c);
7778
78 } else if (getClassConstantOperand().equals("java/lang/Class")
79 && (getNameConstantOperand().equals("isInstance") || getNameConstantOperand().equals("cast"))) {
79 } else if ("java/lang/Class".equals(getClassConstantOperand())
80 && ("isInstance".equals(getNameConstantOperand()) || "cast".equals(getNameConstantOperand()))) {
8081 OpcodeStack.Item item = stack.getStackItem(1);
81 if (item.getSignature().equals("Ljava/lang/Class;")) {
82 if ("Ljava/lang/Class;".equals(item.getSignature())) {
8283 Object value = item.getConstant();
8384 if (value instanceof String) {
8485 ClassDescriptor c = DescriptorFactory.createClassDescriptor((String) value);
101102 OpcodeStack.Item item = stack.getStackItem(0);
102103 if (item.isInitialParameter() && item.getRegisterNumber() == 1) {
103104 ClassDescriptor thisClassDescriptor = getClassDescriptor();
104 if (c.equals(thisClassDescriptor))
105 if (c.equals(thisClassDescriptor)) {
105106 return;
107 }
106108 Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
107109 try {
108 if (!c.isArray() && (subtypes2.isSubtype(c, thisClassDescriptor) || subtypes2.isSubtype(thisClassDescriptor, c)))
110 if (!c.isArray() && (subtypes2.isSubtype(c, thisClassDescriptor) || subtypes2.isSubtype(thisClassDescriptor, c))) {
109111 return;
112 }
110113
111114 Type thisType = Type.getType(thisClassDescriptor.getSignature());
112115 Type cType = Type.getType(c.getSignature());
113116 IncompatibleTypes check = IncompatibleTypes.getPriorityForAssumingCompatible(thisType, cType, false);
114117 int priority = check.getPriority();
115 if ("java/lang/Object".equals(getSuperclassName()) && ClassName.isAnonymous(getClassName()))
118 if ("java/lang/Object".equals(getSuperclassName()) && ClassName.isAnonymous(getClassName())) {
116119 priority++;
120 }
117121 bugAccumulator.accumulateBug(new BugInstance(this, "EQ_CHECK_FOR_OPERAND_NOT_COMPATIBLE_WITH_THIS", priority)
118 .addClassAndMethod(this).addType(c).describe(TypeAnnotation.FOUND_ROLE), this);
122 .addClassAndMethod(this).addType(c).describe(TypeAnnotation.FOUND_ROLE), this);
119123 classSummary.checksForEqualTo(thisClassDescriptor, c);
120124
121125 } catch (ClassNotFoundException e) {
6969 if (seen == INVOKEVIRTUAL && writeObject.equals(getXMethodOperand())) {
7070 OpcodeStack.Item top = stack.getStackItem(0);
7171 String signature = top.getSignature();
72 while (signature.charAt(0) == '[')
72 while (signature.charAt(0) == '[') {
7373 signature = signature.substring(1);
74 }
7475 ClassDescriptor c = DescriptorFactory.createClassDescriptorFromFieldSignature(signature);
75 if (c == null || !Subtypes2.instanceOf(c, Serializable.class))
76 if (c == null || !Subtypes2.instanceOf(c, Serializable.class)) {
7677 return;
78 }
7779
7880 try {
7981 XClass xClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, c);
80 if (xClass.isInterface())
82 if (xClass.isInterface()) {
8183 return;
82 if (xClass.isSynthetic())
84 }
85 if (xClass.isSynthetic()) {
8386 return;
84 if (xClass.isAbstract())
87 }
88 if (xClass.isAbstract()) {
8589 return;
90 }
8691 unreadFields.strongEvidenceForIntendedSerialization(c);
8792 } catch (CheckedAnalysisException e) {
8893 bugReporter.logError("Error looking up xClass of " + c, e);
9398 OpcodeStack.Item top = stack.getStackItem(0);
9499 if (readObject.equals(top.getReturnValueOf())) {
95100 ClassDescriptor c = getClassDescriptorOperand();
96 if (!Subtypes2.instanceOf(c, Serializable.class))
101 if (!Subtypes2.instanceOf(c, Serializable.class)) {
97102 return;
103 }
98104
99105 try {
100106 XClass xClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, c);
101 if (xClass.isInterface())
107 if (xClass.isInterface()) {
102108 return;
103 if (xClass.isSynthetic())
109 }
110 if (xClass.isSynthetic()) {
104111 return;
105 if (xClass.isAbstract())
112 }
113 if (xClass.isAbstract()) {
106114 return;
115 }
107116 unreadFields.strongEvidenceForIntendedSerialization(c);
108117 } catch (CheckedAnalysisException e) {
109118 bugReporter.logError("Error looking up xClass of " + c, e);
5858
5959 @Override
6060 public void sawOpcode(int seen) {
61 if (getMethodName().equals("<init>") && seen == INVOKEVIRTUAL) {
61 if ("<init>".equals(getMethodName()) && seen == INVOKEVIRTUAL) {
6262 XMethod m = getXMethodOperand();
6363 if (m != null && !m.isPrivate() && !m.isFinal()) {
6464 int args = PreorderVisitor.getNumberArguments(m.getSignature());
7070
7171 for (XMethod called : targets) {
7272 if (!called.isAbstract() && !called.equals(m)
73 && subtypes2.isSubtype(called.getClassDescriptor(), getClassDescriptor()))
73 && subtypes2.isSubtype(called.getClassDescriptor(), getClassDescriptor())) {
7474 fieldSummary.setCalledFromSuperConstructor(new ProgramPoint(this), called);
75 }
7576 }
7677 } catch (ClassNotFoundException e) {
7778 AnalysisContext.reportMissingClass(e);
8384
8485 }
8586
86 if (seen == INVOKESPECIAL && getMethodName().equals("<init>") && getNameConstantOperand().equals("<init>")) {
87 if (seen == INVOKESPECIAL && "<init>".equals(getMethodName()) && "<init>".equals(getNameConstantOperand())) {
8788
8889 String classOperand = getClassConstantOperand();
8990 OpcodeStack.Item invokedOn = stack.getItemMethodInvokedOn(this);
9091 if (invokedOn.getRegisterNumber() == 0 && !classOperand.equals(getClassName())) {
9192 sawInitializeSuper = true;
9293 XMethod invoked = getXMethodOperand();
93 if (invoked != null)
94 if (invoked != null) {
9495 fieldSummary.sawSuperCall(getXMethod(), invoked);
96 }
9597 }
9698
9799 }
98100
99101 if (seen == PUTFIELD || seen == PUTSTATIC) {
100102 XField fieldOperand = getXFieldOperand();
101 if (fieldOperand == null)
103 if (fieldOperand == null) {
102104 return;
105 }
103106 touched.add(fieldOperand);
104 if (!fieldOperand.getClassDescriptor().getClassName().equals(getClassName()))
107 if (!fieldOperand.getClassDescriptor().getClassName().equals(getClassName())) {
105108 fieldSummary.addWrittenOutsideOfConstructor(fieldOperand);
106 else if (seen == PUTFIELD) {
109 } else if (seen == PUTFIELD) {
107110 OpcodeStack.Item addr = stack.getStackItem(1);
108111 {
109 if (addr.getRegisterNumber() != 0 || !getMethodName().equals("<init>"))
112 if (addr.getRegisterNumber() != 0 || !"<init>".equals(getMethodName())) {
110113 fieldSummary.addWrittenOutsideOfConstructor(fieldOperand);
114 }
111115 }
112 } else if (seen == PUTSTATIC && !getMethodName().equals("<clinit>"))
116 } else if (seen == PUTSTATIC && !"<clinit>".equals(getMethodName())) {
113117 fieldSummary.addWrittenOutsideOfConstructor(fieldOperand);
118 }
114119 OpcodeStack.Item top = stack.getStackItem(0);
115120 fieldSummary.mergeSummary(fieldOperand, top);
116121 }
122127 sawInitializeSuper = false;
123128 super.visit(obj);
124129 fieldSummary.setFieldsWritten(getXMethod(), touched);
125 if (getMethodName().equals("<init>") && sawInitializeSuper) {
130 if ("<init>".equals(getMethodName()) && sawInitializeSuper) {
126131 XClass thisClass = getXClass();
127 for (XField f : thisClass.getXFields())
132 for (XField f : thisClass.getXFields()) {
128133 if (!f.isStatic() && !f.isFinal() && !touched.contains(f)) {
129134 OpcodeStack.Item item;
130135 char firstChar = f.getSignature().charAt(0);
131 if (firstChar == 'L' || firstChar == '[')
136 if (firstChar == 'L' || firstChar == '[') {
132137 item = OpcodeStack.Item.nullItem(f.getSignature());
133 else if (firstChar == 'I')
138 } else if (firstChar == 'I') {
134139 item = new OpcodeStack.Item("I", (Integer) 0);
135 else if (firstChar == 'J')
140 } else if (firstChar == 'J') {
136141 item = new OpcodeStack.Item("J", 0L);
137 else
142 } else {
138143 item = new OpcodeStack.Item(f.getSignature());
144 }
139145 fieldSummary.mergeSummary(f, item);
140146 }
147 }
141148 }
142149 touched.clear();
143150 }
5151
5252 @Override
5353 public void visit(Method obj) {
54 if (obj.getName().equals("finalize"))
54 if ("finalize".equals(obj.getName())) {
5555 inFinalize = true;
56 else
56 } else {
5757 inFinalize = false;
58 }
5859 }
5960
6061 @Override
7980
8081 @Override
8182 public void sawOpcode(int seen) {
82 if (state == 0 && seen == ALOAD_0)
83 if (state == 0 && seen == ALOAD_0) {
8384 state++;
84 else if (state == 1 && seen == ACONST_NULL)
85 } else if (state == 1 && seen == ACONST_NULL) {
8586 state++;
86 else if (state == 2 && seen == PUTFIELD) {
87 } else if (state == 2 && seen == PUTFIELD) {
8788 bugAccumulator.accumulateBug(
8889 new BugInstance(this, "FI_FINALIZER_NULLS_FIELDS", NORMAL_PRIORITY).addClassAndMethod(this)
89 .addReferencedField(this), this);
90 .addReferencedField(this), this);
9091 sawFieldNulling = true;
9192 state = 0;
9293 } else if (seen == RETURN) {
9595
9696 }
9797
98 @Override
9899 public void visitClassContext(ClassContext classContext) {
99100 JavaClass javaClass = classContext.getJavaClass();
100101 Method[] methodList = javaClass.getMethods();
101102
102103 for (Method method : methodList) {
103 if (method.getCode() == null)
104 continue;
104 if (method.getCode() == null) {
105 continue;
106 }
105107
106108 try {
107109 analyzeMethod(classContext, method);
137139 }
138140
139141 private void analyzeMethod(ClassContext classContext, Method method) throws CFGBuilderException, DataflowAnalysisException {
140 if (BCELUtil.isSynthetic(method) || !prescreen(classContext, method))
142 if (BCELUtil.isSynthetic(method) || !prescreen(classContext, method)) {
141143 return;
144 }
142145 BugAccumulator accumulator = new BugAccumulator(bugReporter);
143146
144147 CFG cfg = classContext.getCFG(method);
150153
151154 ConstantPoolGen cpg = classContext.getConstantPoolGen();
152155 MethodGen methodGen = classContext.getMethodGen(method);
153 if (methodGen == null)
156 if (methodGen == null) {
154157 return;
158 }
155159 String methodName = methodGen.getClassName() + "." + methodGen.getName();
156160 String sourceFile = classContext.getJavaClass().getSourceFileName();
157161 if (DEBUG) {
167171 InstructionHandle handle = location.getHandle();
168172 Instruction ins = handle.getInstruction();
169173
170 if (!(ins instanceof CHECKCAST) && !(ins instanceof INSTANCEOF))
171 continue;
174 if (!(ins instanceof CHECKCAST) && !(ins instanceof INSTANCEOF)) {
175 continue;
176 }
172177
173178 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen,
174179 sourceFile, handle);
175180 if (ins instanceof CHECKCAST) {
176181 if (!haveCast.add(sourceLineAnnotation)) {
177182 haveMultipleCast.add(sourceLineAnnotation);
178 if (DEBUG)
183 if (DEBUG) {
179184 System.out.println("Have multiple casts for " + sourceLineAnnotation);
185 }
180186 }
181187 } else {
182188 if (!haveInstanceOf.add(sourceLineAnnotation)) {
183189 haveMultipleInstanceOf.add(sourceLineAnnotation);
184 if (DEBUG)
190 if (DEBUG) {
185191 System.out.println("Have multiple instanceof for " + sourceLineAnnotation);
192 }
186193 }
187194 }
188195 }
225232 }
226233 }
227234
228 if (!(ins instanceof CHECKCAST) && !(ins instanceof INSTANCEOF))
229 continue;
235 if (!(ins instanceof CHECKCAST) && !(ins instanceof INSTANCEOF)) {
236 continue;
237 }
230238
231239 boolean isCast = ins instanceof CHECKCAST;
232240 int occurrences = cfg.getLocationsContainingInstructionWithOffset(pc).size();
233241 boolean split = occurrences > 1;
234242 if (lineNumberTable != null) {
235243 int line = lineNumberTable.getSourceLine(handle.getPosition());
236 if (line > 0 && linesMentionedMultipleTimes.get(line))
244 if (line > 0 && linesMentionedMultipleTimes.get(line)) {
237245 split = true;
246 }
238247 }
239248
240249 IsNullValueFrame nullFrame = isNullDataflow.getFactAtLocation(location);
241 if (!nullFrame.isValid())
242 continue;
250 if (!nullFrame.isValid()) {
251 continue;
252 }
243253 IsNullValue operandNullness = nullFrame.getTopValue();
244254 if (DEBUG) {
245255 String kind = isCast ? "checkedCast" : "instanceof";
278288 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen,
279289 sourceFile, handle);
280290 assert castSig.length() > 1;
281 if (!isCast)
291 if (!isCast) {
282292 accumulator.accumulateBug(new BugInstance(this, "NP_NULL_INSTANCEOF", split ? LOW_PRIORITY : NORMAL_PRIORITY)
283 .addClassAndMethod(methodGen, sourceFile).addType(castSig), sourceLineAnnotation);
293 .addClassAndMethod(methodGen, sourceFile).addType(castSig), sourceLineAnnotation);
294 }
284295 continue;
285296
286297 }
291302 final ReferenceType refType = (ReferenceType) operandType;
292303 boolean impliesByGenerics = typeDataflow.getAnalysis().isImpliedByGenericTypes(refType);
293304
294 if (impliesByGenerics && !isCast)
295 continue;
305 if (impliesByGenerics && !isCast) {
306 continue;
307 }
296308
297309 final boolean typesAreEqual = refType.equals(castType);
298310 if (isCast && typesAreEqual) {
314326
315327 if (refSig2.charAt(0) != 'L' || castSig2.charAt(0) != 'L') {
316328 if (castSig2.charAt(0) == '['
317 && (refSig2.equals("Ljava/io/Serializable;") || refSig2.equals("Ljava/lang/Object;") || refSig2
318 .equals("Ljava/lang/Cloneable;")))
329 && ("Ljava/io/Serializable;".equals(refSig2) || "Ljava/lang/Object;".equals(refSig2) || "Ljava/lang/Cloneable;".equals(refSig2))) {
319330 continue;
331 }
320332 if (refSig2.charAt(0) == '['
321 && (castSig2.equals("Ljava/io/Serializable;") || castSig2.equals("Ljava/lang/Object;") || castSig2
322 .equals("Ljava/lang/Cloneable;")))
333 && ("Ljava/io/Serializable;".equals(castSig2) || "Ljava/lang/Object;".equals(castSig2) || "Ljava/lang/Cloneable;".equals(castSig2))) {
323334 continue;
335 }
324336 int priority = HIGH_PRIORITY;
325 if (split && (castSig2.endsWith("Error;") || castSig2.endsWith("Exception;")))
337 if (split && (castSig2.endsWith("Error;") || castSig2.endsWith("Exception;"))) {
326338 priority = LOW_PRIORITY;
339 }
327340 // report bug only if types are not equal, see bug 3598482
328341 if(!typesAreEqual){
329342 bugReporter.reportBug(new BugInstance(this, isCast ? "BC_IMPOSSIBLE_CAST" : "BC_IMPOSSIBLE_INSTANCEOF", priority)
330 .addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(refType, castType)
331 .addSourceLine(sourceLineAnnotation));
332 }
333 continue;
334 }
335
336 if (!operandTypeIsExact && refSig2.equals("Ljava/lang/Object;")) {
337 continue;
338 }
343 .addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(refType, castType)
344 .addSourceLine(sourceLineAnnotation));
345 }
346 continue;
347 }
348
349 if (!operandTypeIsExact && "Ljava/lang/Object;".equals(refSig2)) {
350 continue;
351 }
352 /*
339353 if (false && isCast && haveMultipleCast.contains(sourceLineAnnotation) || !isCast
340354 && haveMultipleInstanceOf.contains(sourceLineAnnotation)) {
341355 // skip; might be due to JSR inlining
342356 continue;
343 }
357 }*/
344358 String castName = castSig2.substring(1, castSig2.length() - 1).replace('/', '.');
345359 String refName = refSig2.substring(1, refSig2.length() - 1).replace('/', '.');
346360
347 if (vnaDataflow == null)
361 if (vnaDataflow == null) {
348362 vnaDataflow = classContext.getValueNumberDataflow(method);
363 }
349364 ValueNumberFrame vFrame = vnaDataflow.getFactAtLocation(location);
350 if (paramValueNumberSet == null)
365 if (paramValueNumberSet == null) {
351366 paramValueNumberSet = getParameterValueNumbers(classContext, method, cfg);
367 }
352368 ValueNumber valueNumber = vFrame.getTopValue();
353369 BugAnnotation valueSource = ValueNumberSourceInfo.findAnnotationFromValueNumber(method, location, valueNumber, vFrame,
354370 "VALUE_OF");
371 // XXX call below causes 86% of all OpcodeStackDetector.EarlyExitException (getPC() == targetPC) thrown (13000 on java* JDK7 classes)
355372 BugAnnotation source = BugInstance.getSourceForTopStackValue(classContext, method, location);
356373 boolean isParameter = paramValueNumberSet.contains(valueNumber) && source instanceof LocalVariableAnnotation;
357374
361378
362379 boolean upcast = Repository.instanceOf(refJavaClass, castJavaClass);
363380 if (upcast || typesAreEqual) {
364 if (!isCast)
381 if (!isCast) {
365382 accumulator.accumulateBug(new BugInstance(this, "BC_VACUOUS_INSTANCEOF", NORMAL_PRIORITY)
366 .addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(refType, castType),
367 sourceLineAnnotation);
383 .addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(refType, castType),
384 sourceLineAnnotation);
385 }
368386 } else {
369387 boolean castMayThrow = !Repository.instanceOf(refJavaClass, castJavaClass);
370388 boolean downCast = Repository.instanceOf(castJavaClass, refJavaClass);
371389
372 if (!operandTypeIsExact && refName.equals("java.lang.Object"))
390 if (!operandTypeIsExact && "java.lang.Object".equals(refName)) {
373391 continue;
392 }
374393 double rank = 0.0;
375394 boolean castToConcreteCollection = concreteCollectionClasses.contains(castName)
376395 && abstractCollectionClasses.contains(refName);
384403
385404 if (!operandTypeIsExact) {
386405 rank = Analyze.deepInstanceOf(refJavaClass, castJavaClass);
387 if (castToConcreteCollection && rank > 0.6)
406 if (castToConcreteCollection && rank > 0.6) {
388407 rank = (rank + 0.6) / 2;
389 else if (castToAbstractCollection && rank > 0.3)
408 } else if (castToAbstractCollection && rank > 0.3) {
390409 rank = (rank + 0.3) / 2;
391 }
392
393 if (false)
410 }
411 }
412 /*
413 if (false) {
394414 System.out.println("Rank:\t" + rank + "\t" + refName + "\t" + castName);
415 }
416 */
395417 boolean completeInformation = (!castJavaClass.isInterface() && !refJavaClass.isInterface())
396418 || refJavaClass.isFinal() || castJavaClass.isFinal();
397419 if (DEBUG) {
406428 System.out.println(" isParameter: " + valueNumber);
407429 System.out.println(" score: " + rank);
408430 System.out.println(" source is: " + valueSource);
409 if (catchSize < Integer.MAX_VALUE)
431 if (catchSize < Integer.MAX_VALUE) {
410432 System.out.println(" catch block size is: " + catchSize);
411 if (constantClass != null)
433 }
434 if (constantClass != null) {
412435 System.out.println(" constant class " + constantClass + " at " + pcForConstantClass);
413 if (handle.getPrev() == null)
436 }
437 if (handle.getPrev() == null) {
414438 System.out.println(" prev is null");
415 else
439 } else {
416440 System.out.println(" prev is " + handle.getPrev());
441 }
417442 }
418443 if (!isCast && castMayThrow && valueSource != null) {
419444 String oldCheck = instanceOfChecks.get(valueSource);
420 if (oldCheck == null)
445 if (oldCheck == null) {
421446 instanceOfChecks.put(valueSource, castName);
422 else if (!oldCheck.equals(castName))
447 } else if (!oldCheck.equals(castName)) {
423448 instanceOfChecks.put(valueSource, "");
449 }
424450 }
425451 if (!downCast && completeInformation || operandTypeIsExact) {
426452 String bugPattern;
427453 if (isCast) {
428454 if (downCast && operandTypeIsExact) {
429 if (refSig.equals("[Ljava/lang/Object;") && source instanceof MethodAnnotation
430 && ((MethodAnnotation) source).getMethodName().equals("toArray")
431 && ((MethodAnnotation) source).getMethodSignature().equals("()[Ljava/lang/Object;"))
455 if ("[Ljava/lang/Object;".equals(refSig) && source instanceof MethodAnnotation
456 && "toArray".equals(((MethodAnnotation) source).getMethodName())
457 && "()[Ljava/lang/Object;".equals(((MethodAnnotation) source).getMethodSignature())) {
432458 bugPattern = "BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY";
433 else
459 } else {
434460 bugPattern = "BC_IMPOSSIBLE_DOWNCAST";
435 } else
461 }
462 } else {
436463 bugPattern = "BC_IMPOSSIBLE_CAST";
437 } else
464 }
465 } else {
438466 bugPattern = "BC_IMPOSSIBLE_INSTANCEOF";
467 }
439468
440469 bugReporter.reportBug(new BugInstance(this, bugPattern, isCast ? HIGH_PRIORITY : NORMAL_PRIORITY)
441 .addClassAndMethod(methodGen, sourceFile)
442 .addFoundAndExpectedType(refType, castType).addOptionalUniqueAnnotations(valueSource, source)
443 .addSourceLine(sourceLineAnnotation));
470 .addClassAndMethod(methodGen, sourceFile)
471 .addFoundAndExpectedType(refType, castType).addOptionalUniqueAnnotations(valueSource, source)
472 .addSourceLine(sourceLineAnnotation));
444473 } else if (isCast && rank < 0.9
445474 && !valueNumber.hasFlag(ValueNumber.ARRAY_VALUE)) {
446475
454483 priority += 1;
455484 } else if ("".equals(oldCheck)) {
456485 priority += 1;
457 if (!(source instanceof LocalVariableAnnotation)) continue;
458 }
459
460 if (rank > 0.75)
486 if (!(source instanceof LocalVariableAnnotation)) {
487 continue;
488 }
489 }
490
491 if (rank > 0.75) {
461492 priority += 2;
462 else if (rank > 0.5)
493 } else if (rank > 0.5) {
463494 priority += 1;
464 else if (rank > 0.25)
495 } else if (rank > 0.25) {
465496 priority += 0;
466 else
497 } else {
467498 priority--;
468
469
470
471 if (DEBUG)
499 }
500
501
502
503 if (DEBUG) {
472504 System.out.println(" priority a: " + priority);
473 if (methodGen.getClassName().startsWith(refName) || methodGen.getClassName().startsWith(castName))
505 }
506 if (methodGen.getClassName().startsWith(refName) || methodGen.getClassName().startsWith(castName)) {
474507 priority += 1;
475 if (DEBUG)
508 }
509 if (DEBUG) {
476510 System.out.println(" priority b: " + priority);
477 if (castJavaClass.isInterface() && !castToAbstractCollection)
511 }
512 if (castJavaClass.isInterface() && !castToAbstractCollection) {
478513 priority++;
479 if (DEBUG)
514 }
515 if (DEBUG) {
480516 System.out.println(" priority c: " + priority);
481 if (castToConcreteCollection && veryAbstractCollectionClasses.contains(refName))
517 }
518 if (castToConcreteCollection && veryAbstractCollectionClasses.contains(refName)) {
482519 priority--;
483 if (DEBUG)
520 }
521 if (DEBUG) {
484522 System.out.println(" priority d: " + priority);
523 }
485524 if (priority <= LOW_PRIORITY && !castToAbstractCollection && !castToConcreteCollection
486 && (refJavaClass.isInterface() || refJavaClass.isAbstract()))
525 && (refJavaClass.isInterface() || refJavaClass.isAbstract())) {
487526 priority++;
488 if (DEBUG)
527 }
528 if (DEBUG) {
489529 System.out.println(" priority e: " + priority);
490 if (DEBUG)
530 }
531 if (DEBUG) {
491532 System.out.println(" ref name: " + refName);
492 if (methodGen.getName().equals("compareTo"))
533 }
534 if ("compareTo".equals(methodGen.getName())) {
493535 priority++;
494 else if (methodGen.isPublic() && isParameter && !castName.equals(oldCheck))
536 } else if (methodGen.isPublic() && isParameter && !castName.equals(oldCheck)) {
495537 priority--;
496 if (wasMethodInvocationWasGeneric && valueNumber.hasFlag(ValueNumber.RETURN_VALUE))
538 }
539 if (wasMethodInvocationWasGeneric && valueNumber.hasFlag(ValueNumber.RETURN_VALUE)) {
497540 continue;
541 }
498542 if (constantClass != null && pcForConstantClass +20 >= pc && valueNumber.hasFlag(ValueNumber.RETURN_VALUE)
499 && ClassName.toDottedClassName(constantClass).equals(castName))
543 && ClassName.toDottedClassName(constantClass).equals(castName)) {
500544 priority += 2;
501 if (DEBUG)
545 }
546 if (DEBUG) {
502547 System.out.println(" priority f: " + priority);
503 if (source instanceof MethodAnnotation) {
504 MethodAnnotation m = (MethodAnnotation) source;
505 XMethod xm = m.toXMethod();
506 if (xm != null && (xm.isPrivate() || xm.isStatic()) && priority == Priorities.LOW_PRIORITY)
507 continue;
508 }
509
510 if (valueNumber.hasFlag(ValueNumber.RETURN_VALUE) && priority < Priorities.LOW_PRIORITY)
548 }
549 if (source instanceof MethodAnnotation) {
550 MethodAnnotation m = (MethodAnnotation) source;
551 XMethod xm = m.toXMethod();
552 if (xm != null && (xm.isPrivate() || xm.isStatic()) && priority == Priorities.LOW_PRIORITY) {
553 continue;
554 }
555 }
556
557 if (valueNumber.hasFlag(ValueNumber.RETURN_VALUE) && priority < Priorities.LOW_PRIORITY) {
511558 priority = Priorities.LOW_PRIORITY;
512 if (DEBUG)
559 }
560 if (DEBUG) {
513561 System.out.println(" priority g: " + priority);
514
515 if (DEBUG)
562 }
563
564 if (DEBUG) {
516565 System.out.println(" priority h: " + priority);
517
518 if (catchSize < 15)
566 }
567
568 if (catchSize < 15) {
519569 return;
520 if (catchSize < 25)
570 }
571 if (catchSize < 25) {
521572 priority++;
522 if (DEBUG)
573 }
574 if (DEBUG) {
523575 System.out.println(" priority i: " + priority);
524
525
526 if (priority < HIGH_PRIORITY)
576 }
577
578
579 if (priority < HIGH_PRIORITY) {
527580 priority = HIGH_PRIORITY;
581 }
528582 if (priority <= LOW_PRIORITY) {
529583 String bug = "BC_UNCONFIRMED_CAST";
530 if (valueNumber.hasFlag(ValueNumber.RETURN_VALUE) || valueSource instanceof MethodAnnotation)
584 if (valueNumber.hasFlag(ValueNumber.RETURN_VALUE) || valueSource instanceof MethodAnnotation) {
531585 bug = "BC_UNCONFIRMED_CAST_OF_RETURN_VALUE";
532 else if (castToConcreteCollection)
586 } else if (castToConcreteCollection) {
533587 bug = "BC_BAD_CAST_TO_CONCRETE_COLLECTION";
534 else if (castToAbstractCollection)
588 } else if (castToAbstractCollection) {
535589 bug = "BC_BAD_CAST_TO_ABSTRACT_COLLECTION";
590 }
536591
537592 BugInstance bugInstance = new BugInstance(this, bug, priority)
538 .addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(refType, castType)
539 .addOptionalAnnotation(valueSource);
593 .addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(refType, castType)
594 .addOptionalAnnotation(valueSource);
540595
541596 accumulator.accumulateBug(bugInstance, sourceLineAnnotation);
542597 }
548603 if (DEBUG) {
549604 e.printStackTrace(System.out);
550605 }
551 if (isCast && refSig.equals("[Ljava/lang/Object;") && source instanceof MethodAnnotation
552 && ((MethodAnnotation) source).getMethodName().equals("toArray")
553 && ((MethodAnnotation) source).getMethodSignature().equals("()[Ljava/lang/Object;"))
554 bugReporter.reportBug(new BugInstance(this, "BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY", isCast ? HIGH_PRIORITY : NORMAL_PRIORITY)
606 if (isCast && "[Ljava/lang/Object;".equals(refSig) && source instanceof MethodAnnotation
607 && "toArray".equals(((MethodAnnotation) source).getMethodName())
608 && "()[Ljava/lang/Object;".equals(((MethodAnnotation) source).getMethodSignature())) {
609 bugReporter.reportBug(new BugInstance(this, "BC_IMPOSSIBLE_DOWNCAST_OF_TOARRAY", HIGH_PRIORITY)
555610 .addClassAndMethod(methodGen, sourceFile)
556611 .addFoundAndExpectedType(refType, castType).addOptionalUniqueAnnotations(valueSource, source)
557612 .addSourceLine(sourceLineAnnotation));
613 }
558614
559615
560616 }
562618 accumulator.reportAccumulatedBugs();
563619 }
564620
621 @Override
565622 public void report() {
566623 }
567624
4848
4949 @Override
5050 public void sawOpcode(int seen) {
51 if (seen == ISTORE || seen == ISTORE_0 || seen == ISTORE_1 || seen == ISTORE_2 || seen == ISTORE_3)
51 if (seen == ISTORE || seen == ISTORE_0 || seen == ISTORE_1 || seen == ISTORE_2 || seen == ISTORE_3) {
5252 lastRegStore = getRegisterOperand();
53 }
5354 if (lineNumbers != null
5455 && stack.getStackDepth() >= 2
5556 && (seen == IF_ICMPGE || seen == IF_ICMPGT || seen == IF_ICMPLT || seen == IF_ICMPLE || seen == IF_ICMPNE || seen == IF_ICMPEQ)) {
7677 && beforeIncLineNumber > incLineNumber) {
7778
7879 bugReporter.reportBug(new BugInstance(this, "QF_QUESTIONABLE_FOR_LOOP", NORMAL_PRIORITY)
79 .addClassAndMethod(this).addSourceLine(this));
80 .addClassAndMethod(this).addSourceLine(this));
8081 }
8182 }
8283
3838 import edu.umd.cs.findbugs.visitclass.PreorderVisitor;
3939
4040 public class FindBugsSummaryStats extends PreorderVisitor implements Detector, BugReporterObserver, NonReportingDetector {
41 private ProjectStats stats;
41 private final ProjectStats stats;
4242
4343 BitSet lines = new BitSet(500);
4444
6060
6161 @Override
6262 public void visitJavaClass(JavaClass obj) {
63 if (AnalysisContext.currentAnalysisContext().isApplicationClass(obj))
63 if (AnalysisContext.currentAnalysisContext().isApplicationClass(obj)) {
6464 super.visitJavaClass(obj);
65 }
6566 }
6667
6768 @Override
9293 @Override
9394 public void visitAfter(JavaClass obj) {
9495 int linesNCSS = 1 + methods + fields;
95 if (sawLineNumbers)
96 if (sawLineNumbers) {
9697 linesNCSS += lines.cardinality();
97 else
98 } else {
9899 linesNCSS += classCodeSize / 10;
99 if (stats != null)
100 }
101 if (stats != null) {
100102 stats.addClass(getDottedClassName(), obj.getSourceFileName(), obj.isInterface(), linesNCSS);
103 }
101104 totalCodeSize += classCodeSize;
102105 totalNCSS += linesNCSS;
103106 totalMethods += methods;
121124 this.stats = null;
122125 }
123126
127 @Override
124128 public void visitClassContext(ClassContext classContext) {
125129 classContext.getJavaClass().accept(this);
126130 }
127131
132 @Override
128133 public void report() {
129134 }
130135
136141 out.println("fields\t" + totalFields);
137142 }
138143
144 @Override
139145 public void reportBug(BugInstance bug) {
140146 // already added when bug was added to bug collection
141147 }
3434 public class FindCircularDependencies extends BytecodeScanningDetector {
3535 private HashMap<String, Set<String>> dependencyGraph = null;
3636
37 private BugReporter bugReporter;
37 private final BugReporter bugReporter;
3838
3939 private String clsName;
4040
5353 if ((seen == INVOKESPECIAL) || (seen == INVOKESTATIC) || (seen == INVOKEVIRTUAL)) {
5454 String refClsName = getClassConstantOperand();
5555 refClsName = refClsName.replace('/', '.');
56 if (refClsName.startsWith("java"))
57 return;
58
59 if (clsName.equals(refClsName))
60 return;
61
62 if (clsName.startsWith(refClsName) && (refClsName.indexOf("$") >= 0))
63 return;
64
65 if (refClsName.startsWith(clsName) && (clsName.indexOf("$") >= 0))
66 return;
56 if (refClsName.startsWith("java")) {
57 return;
58 }
59
60 if (clsName.equals(refClsName)) {
61 return;
62 }
63
64 if (clsName.startsWith(refClsName) && (refClsName.indexOf('$') >= 0)) {
65 return;
66 }
67
68 if (refClsName.startsWith(clsName) && (clsName.indexOf('$') >= 0)) {
69 return;
70 }
6771
6872 Set<String> dependencies = dependencyGraph.get(clsName);
6973 if (dependencies == null) {
96100 dependencyGraph.remove(clsName);
97101 pruneLeaves = true;
98102 }
99 if (pruneLeaves)
103 if (pruneLeaves) {
100104 removeDependencyLeaves(dependencyGraph);
105 }
101106 }
102107
103108 dependencyGraph.clear();
131136 private boolean removeLoopLinks(Map<String, Set<String>> dependencyGraph, Set<String> loop) {
132137 Set<String> dependencies = null;
133138 for (String clsName : loop) {
134 if (dependencies != null)
139 if (dependencies != null) {
135140 dependencies.remove(clsName);
141 }
136142 dependencies = dependencyGraph.get(clsName);
137143 }
138 if (dependencies != null)
144 if (dependencies != null) {
139145 dependencies.remove(loop.iterator().next());
146 }
140147
141148 boolean removedClass = false;
142149 Iterator<String> cIt = loop.iterator();
165172 startClass = startCls;
166173 visited = new HashSet<String>();
167174 loop = new LinkedHashSet<String>();
168 if (findLoop(startClass))
175 if (findLoop(startClass)) {
169176 return loop;
177 }
170178 return null;
171179 }
172180
173181 private boolean findLoop(String curClass) {
174182 Set<String> dependencies = dGraph.get(curClass);
175 if (dependencies == null)
183 if (dependencies == null) {
176184 return false;
185 }
177186
178187 visited.add(curClass);
179188 loop.add(curClass);
180189 for (String depClass : dependencies) {
181 if (depClass.equals(startClass))
190 if (depClass.equals(startClass)) {
182191 return true;
183
184 if (visited.contains(depClass))
192 }
193
194 if (visited.contains(depClass)) {
185195 continue;
186
187 if (findLoop(depClass))
196 }
197
198 if (findLoop(depClass)) {
188199 return true;
200 }
189201 }
190202 loop.remove(curClass);
191203 return false;
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2008 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.detect;
20
21 import java.util.ArrayList;
22 import java.util.Iterator;
23 import java.util.List;
24
25 import org.apache.bcel.classfile.Code;
26
27 import edu.umd.cs.findbugs.BugAccumulator;
28 import edu.umd.cs.findbugs.BugInstance;
29 import edu.umd.cs.findbugs.BugReporter;
30 import edu.umd.cs.findbugs.MethodAnnotation;
31 import edu.umd.cs.findbugs.OpcodeStack;
32 import edu.umd.cs.findbugs.OpcodeStack.Item;
33 import edu.umd.cs.findbugs.ba.ClassContext;
34 import edu.umd.cs.findbugs.ba.ch.Subtypes2;
35 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
36 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
37
38 /**
39 * @author Tagir Valeev
40 */
41 public class FindComparatorProblems extends OpcodeStackDetector {
42 private static final MethodDescriptor FLOAT_DESCRIPTOR = new MethodDescriptor("java/lang/Float", "compare", "(FF)I", true);
43 private static final MethodDescriptor DOUBLE_DESCRIPTOR = new MethodDescriptor("java/lang/Double", "compare", "(DD)I", true);
44
45 private boolean isComparator;
46 private int lastEmptyStackPC;
47 private List<int[]> twoDoublesInStack;
48 private final BugAccumulator accumulator;
49
50 public FindComparatorProblems(BugReporter reporter) {
51 this.accumulator = new BugAccumulator(reporter);
52 }
53
54 @Override
55 public void visitClassContext(ClassContext classContext) {
56 boolean comparator = Subtypes2.instanceOf(classContext.getClassDescriptor(), "java.util.Comparator");
57 boolean comparable = Subtypes2.instanceOf(classContext.getClassDescriptor(), "java.lang.Comparable");
58 isComparator = comparator;
59 if (comparator || comparable) {
60 super.visitClassContext(classContext);
61 }
62 }
63
64 @Override
65 public boolean shouldVisitCode(Code obj) {
66 return !getMethodDescriptor().isStatic()
67 && ((isComparator && getMethodName().equals("compare") && getMethodSig().endsWith(")I")) || ((getMethodName()
68 .equals("compareTo") && getMethodSig().equals("(L"+getClassName()+";)I"))));
69 }
70
71 @Override
72 public void visit(Code obj) {
73 this.twoDoublesInStack = new ArrayList<>();
74 this.lastEmptyStackPC = 0;
75 super.visit(obj);
76 this.accumulator.reportAccumulatedBugs();
77 }
78
79 @Override
80 public void sawOpcode(int seen) {
81 if(getStack().getStackDepth() == 0) {
82 this.lastEmptyStackPC = getPC();
83 }
84 if((seen == DCMPG || seen == DCMPL || seen == FCMPL || seen == FCMPG) && getStack().getStackDepth() == 2) {
85 int[] startEnd = new int[] {this.lastEmptyStackPC, getPC()};
86 for(Iterator<int[]> iterator = twoDoublesInStack.iterator(); iterator.hasNext(); ) {
87 int[] oldStartEnd = iterator.next();
88 if(codeEquals(oldStartEnd, startEnd)) {
89 Item item1 = getStack().getStackItem(0);
90 Item item2 = getStack().getStackItem(1);
91 accumulator.accumulateBug(
92 new BugInstance("CO_COMPARETO_INCORRECT_FLOATING", NORMAL_PRIORITY).addClassAndMethod(this)
93 .addType(item1.getSignature())
94 .addMethod(item1.getSignature().equals("D")?DOUBLE_DESCRIPTOR:FLOAT_DESCRIPTOR).describe(MethodAnnotation.SHOULD_CALL)
95 .addValueSource(item1, this)
96 .addValueSource(item2, this), this);
97 iterator.remove();
98 return;
99 }
100 }
101 twoDoublesInStack.add(startEnd);
102 }
103 if (seen == IRETURN) {
104 OpcodeStack.Item top = stack.getStackItem(0);
105 Object o = top.getConstant();
106 if (o instanceof Integer && ((Integer)o).intValue() == Integer.MIN_VALUE) {
107 accumulator.accumulateBug(
108 new BugInstance(this, "CO_COMPARETO_RESULTS_MIN_VALUE", NORMAL_PRIORITY).addClassAndMethod(this), this);
109 }
110 }
111 }
112
113 /**
114 * @param oldStartEnd - int[] {oldStart, oldEnd}
115 * @param startEnd - int[] {start, end}
116 * @return true if code slices are the same
117 */
118 private boolean codeEquals(int[] oldStartEnd, int[] startEnd) {
119 int oldStart = oldStartEnd[0];
120 int oldEnd = oldStartEnd[1];
121 int start = startEnd[0];
122 int end = startEnd[1];
123 if(end-start != oldEnd - oldStart) {
124 return false;
125 }
126 for(int i=start; i<end; i++) {
127 if(getCodeByte(i) != getCodeByte(i-start+oldStart)) {
128 return false;
129 }
130 }
131 return true;
132 }
133 }
2727 import org.apache.bcel.classfile.ConstantClass;
2828 import org.apache.bcel.classfile.Field;
2929 import org.apache.bcel.classfile.JavaClass;
30 import org.apache.bcel.classfile.LocalVariable;
31 import org.apache.bcel.classfile.LocalVariableTable;
3230 import org.apache.bcel.classfile.Method;
3331 import org.apache.bcel.generic.ACONST_NULL;
3432 import org.apache.bcel.generic.ALOAD;
5553 import org.apache.bcel.generic.MULTIANEWARRAY;
5654 import org.apache.bcel.generic.MethodGen;
5755 import org.apache.bcel.generic.NEWARRAY;
56 import org.apache.bcel.generic.ObjectType;
5857 import org.apache.bcel.generic.StoreInstruction;
5958 import org.apache.bcel.generic.Type;
6059
6867 import edu.umd.cs.findbugs.Priorities;
6968 import edu.umd.cs.findbugs.SourceLineAnnotation;
7069 import edu.umd.cs.findbugs.SystemProperties;
70 import edu.umd.cs.findbugs.ba.AnalysisContext;
7171 import edu.umd.cs.findbugs.ba.CFG;
7272 import edu.umd.cs.findbugs.ba.CFGBuilderException;
7373 import edu.umd.cs.findbugs.ba.ClassContext;
105105 @StaticConstant
106106 private static final Set<String> EXCLUDED_LOCALS = new HashSet<String>();
107107
108 private static final boolean DO_EXCLUDE_LOCALS = SystemProperties.getProperty(FINDBUGS_EXCLUDED_LOCALS_PROP_NAME) != null;
108 // private static final boolean DO_EXCLUDE_LOCALS = SystemProperties.getProperty(FINDBUGS_EXCLUDED_LOCALS_PROP_NAME) != null;
109
109110 static {
110111 EXCLUDED_LOCALS.add("gxp_locale");
111112 // Get the value of the property...
115116 if (exclLocalsProperty != null) {
116117 for (String s : exclLocalsProperty.split(",")) {
117118 String s2 = s.trim();
118 if (s2.length() > 0)
119 if (s2.length() > 0) {
119120 EXCLUDED_LOCALS.add(s2);
121 }
120122 }
121123 }
122124 }
162164
163165 public FindDeadLocalStores(BugReporter bugReporter) {
164166 this.bugReporter = bugReporter;
165 if (DEBUG)
167 if (DEBUG) {
166168 System.out.println("Debugging FindDeadLocalStores detector");
169 }
167170 }
168171
169172 private boolean prescreen(ClassContext classContext, Method method) {
170173 return true;
171174 }
172175
176 @Override
173177 public void visitClassContext(ClassContext classContext) {
174178 JavaClass javaClass = classContext.getJavaClass();
175179
177181
178182 for (Method method : methodList) {
179183 MethodGen methodGen = classContext.getMethodGen(method);
180 if (methodGen == null)
184 if (methodGen == null) {
181185 continue;
182
183 if (!prescreen(classContext, method))
186 }
187
188 if (!prescreen(classContext, method)) {
184189 continue;
190 }
185191
186192 try {
187193 analyzeMethod(classContext, method);
194200 }
195201
196202 private void analyzeMethod(ClassContext classContext, Method method) throws DataflowAnalysisException, CFGBuilderException {
197 if (BCELUtil.isSynthetic(method) || (method.getAccessFlags() & Constants.ACC_BRIDGE) == Constants.ACC_BRIDGE)
203 if (BCELUtil.isSynthetic(method) || (method.getAccessFlags() & Constants.ACC_BRIDGE) == Constants.ACC_BRIDGE) {
198204 return;
205 }
199206
200207 if (DEBUG) {
201208 System.out.println(" Analyzing method " + classContext.getJavaClass().getClassName() + "." + method.getName());
212219 int[] localIncrementCount = new int[numLocals];
213220 MethodGen methodGen = classContext.getMethodGen(method);
214221 CFG cfg = classContext.getCFG(method);
215 if (cfg.isFlagSet(CFG.FOUND_INEXACT_UNCONDITIONAL_THROWERS))
222 if (cfg.isFlagSet(CFG.FOUND_INEXACT_UNCONDITIONAL_THROWERS)) {
216223 return;
224 }
217225 BitSet liveStoreSetAtEntry = llsaDataflow.getAnalysis().getResultFact(cfg.getEntry());
218226 BitSet complainedAbout = new BitSet();
219227 TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
220228
221229 // Get number of locals that are parameters.
222230 int localsThatAreParameters = PreorderVisitor.getNumberArguments(method.getSignature());
223 if (!method.isStatic())
231 if (!method.isStatic()) {
224232 localsThatAreParameters++;
233 }
225234
226235 // Scan method to determine number of loads, stores, and increments
227236 // of local variables.
228237 countLocalStoresLoadsAndIncrements(localStoreCount, localLoadCount, localIncrementCount, cfg);
229 for (int i = 0; i < localsThatAreParameters; i++)
238 for (int i = 0; i < localsThatAreParameters; i++) {
230239 localStoreCount[i]++;
240 }
231241
232242 // For each source line, keep track of # times
233243 // the line was a live store. This can eliminate false positives
244254 try {
245255 WarningPropertySet<WarningProperty> propertySet = new WarningPropertySet<WarningProperty>();
246256 // Skip any instruction which is not a store
247 if (!isStore(location))
257 if (!isStore(location)) {
248258 continue;
259 }
249260
250261 // Heuristic: exception handler blocks often contain
251262 // dead stores generated by the compiler.
252 if (location.getBasicBlock().isExceptionHandler())
263 if (location.getBasicBlock().isExceptionHandler()) {
253264 propertySet.addProperty(DeadLocalStoreProperty.EXCEPTION_HANDLER);
265 }
254266 InstructionHandle handle = location.getHandle();
255267 int pc = handle.getPosition();
256268 IndexedInstruction ins = (IndexedInstruction) location.getHandle().getInstruction();
268280 LocalVariableAnnotation lvAnnotation = LocalVariableAnnotation.getLocalVariableAnnotation(method, location, ins);
269281
270282 String sourceFileName = javaClass.getSourceFileName();
271 if (lvAnnotation.getName().equals("?")) {
272 if (sourceFileName.endsWith(".groovy"))
283 if ("?".equals(lvAnnotation.getName())) {
284 if (sourceFileName.endsWith(".groovy")) {
273285 continue;
274 if (method.getCode().getLocalVariableTable() != null)
286 }
287 if (method.getCode().getLocalVariableTable() != null) {
275288 continue;
289 }
276290 }
277291
278292 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen,
290304 }
291305
292306 String lvName = lvAnnotation.getName();
293 if (lvName.charAt(0) == '$' || lvName.charAt(0) == '_')
307 if (lvName.charAt(0) == '$' || lvName.charAt(0) == '_') {
294308 propertySet.addProperty(DeadLocalStoreProperty.SYNTHETIC_NAME);
295 if (EXCLUDED_LOCALS.contains(lvName))
309 }
310 if (EXCLUDED_LOCALS.contains(lvName)) {
296311 continue;
312 }
297313 propertySet.setProperty(DeadLocalStoreProperty.LOCAL_NAME, lvName);
298314
299315 boolean isParameter = local < localsThatAreParameters;
300 if (isParameter)
316 if (isParameter) {
301317 propertySet.addProperty(DeadLocalStoreProperty.IS_PARAMETER);
318 }
302319
303320 Field shadowedField = null;
304321
317334 if (parameterThatIsDeadAtEntry && !complainedAbout.get(local)) {
318335
319336 int priority = storeLive ? LOW_PRIORITY : NORMAL_PRIORITY;
320 if (shadowedField != null)
337 if (shadowedField != null) {
321338 priority--;
339 }
322340 pendingBugReportAboutOverwrittenParameter = new BugInstance(this, "IP_PARAMETER_IS_DEAD_BUT_OVERWRITTEN",
323341 priority).addClassAndMethod(methodGen, sourceFileName).add(lvAnnotation);
324342
325 if (shadowedField != null)
343 if (shadowedField != null) {
326344 pendingBugReportAboutOverwrittenParameter.addField(
327345 FieldAnnotation.fromBCELField(classContext.getJavaClass(), shadowedField)).describe(
328 FieldAnnotation.DID_YOU_MEAN_ROLE);
346 FieldAnnotation.DID_YOU_MEAN_ROLE);
347 }
329348
330349 pendingBugReportAboutOverwrittenParameter.addSourceLine(classContext, methodGen, sourceFileName,
331350 location.getHandle());
332351 complainedAbout.set(local);
333352 }
334353
335 if (storeLive)
354 if (storeLive) {
336355 continue;
356 }
337357
338358 TypeFrame typeFrame = typeDataflow.getAnalysis().getFactAtLocation(location);
339359 Type typeOfValue = null;
347367 Instruction prevIns = prevInsHandle.getInstruction();
348368 boolean foundDeadClassInitialization = false;
349369 String initializationOf = null;
350 if (prevIns instanceof ConstantPushInstruction)
370 if (prevIns instanceof ConstantPushInstruction) {
351371 continue; // not an interesting dead store
352 else if (prevIns instanceof GETSTATIC) {
372 } else if (prevIns instanceof GETSTATIC) {
353373 GETSTATIC getStatic = (GETSTATIC) prevIns;
354374 ConstantPoolGen cpg = methodGen.getConstantPool();
355375 foundDeadClassInitialization = getStatic.getFieldName(cpg).startsWith("class$")
356 && getStatic.getSignature(cpg).equals("Ljava/lang/Class;");
376 && "Ljava/lang/Class;".equals(getStatic.getSignature(cpg));
357377 for (Iterator<Location> j = cfg.locationIterator(); j.hasNext();) {
358378 Location location2 = j.next();
359379 if (location2.getHandle().getPosition() + 15 == location.getHandle().getPosition()) {
362382 Object value = ((LDC) instruction2).getValue(methodGen.getConstantPool());
363383 if (value instanceof String) {
364384 String n = (String) value;
365 if (n.length() > 0)
385 if (n.length() > 0) {
366386 initializationOf = ClassName.toSignature(n);
387 }
367388 }
368389 }
369390 }
372393 } else if (prevIns instanceof LDC) {
373394 LDC ldc = (LDC) prevIns;
374395 Type t = ldc.getType(methodGen.getConstantPool());
375 if (t.getSignature().equals("Ljava/lang/Class;")) {
376 ConstantClass v = (ConstantClass) ldc.getValue(methodGen.getConstantPool());
377 initializationOf = ClassName.toSignature(v.getBytes(javaClass.getConstantPool()));
378 foundDeadClassInitialization = true;
379 } else
396 if ("Ljava/lang/Class;".equals(t.getSignature())) {
397 Object value = ldc.getValue(methodGen.getConstantPool());
398 if (value instanceof ConstantClass) {
399 ConstantClass v = (ConstantClass) value;
400 initializationOf = ClassName.toSignature(v.getBytes(javaClass.getConstantPool()));
401 foundDeadClassInitialization = true;
402 } else if (value instanceof ObjectType) {
403 ObjectType v = (ObjectType) value;
404 initializationOf = ClassName.toSignature(v.getClassName());
405 foundDeadClassInitialization = true;
406 } else {
407 AnalysisContext.logError("LDC loaded " + value + "at " + location.getHandle().getPosition() + " in " + classContext.getFullyQualifiedMethodName(method));
408 }
409
410 }
411 else {
380412 continue; // not an interesting DLS
413 }
381414
382415 } else if (prevIns instanceof DUP2) {
383416 // Check for the case where, due to the bytecode
391424 }
392425 }
393426 if (foundDeadClassInitialization) {
394 if (classContext.getJavaClass().getSuperclassName().equals("org.apache.axis.client.Stub"))
427 if ("org.apache.axis.client.Stub".equals(classContext.getJavaClass().getSuperclassName())) {
395428 continue;
429 }
396430 BugInstance bugInstance = new BugInstance(this, "DLS_DEAD_STORE_OF_CLASS_LITERAL",
397431 Priorities.NORMAL_PRIORITY).addClassAndMethod(methodGen, sourceFileName).add(lvAnnotation)
398432 .addType(initializationOf);
400434 continue;
401435 }
402436
403 if (prevIns instanceof LDC || prevIns instanceof ConstantPushInstruction)
437 if (prevIns instanceof LDC || prevIns instanceof ConstantPushInstruction) {
404438 propertySet.addProperty(DeadLocalStoreProperty.STORE_OF_CONSTANT);
405 else if (prevIns instanceof ACONST_NULL) {
439 } else if (prevIns instanceof ACONST_NULL) {
406440 storeOfNull = true;
407441 propertySet.addProperty(DeadLocalStoreProperty.STORE_OF_NULL);
408442 }
409443 }
410444
411 if (typeOfValue instanceof BasicType || Type.STRING.equals(typeOfValue))
445 if (typeOfValue instanceof BasicType || Type.STRING.equals(typeOfValue)) {
412446 propertySet.addProperty(DeadLocalStoreProperty.BASE_VALUE);
447 }
413448
414449 // Ignore assignments that were killed by a subsequent
415450 // assignment.
416451 boolean killedBySubsequentStore = llsaDataflow.getAnalysis().killedByStore(liveStoreSet, local);
417452 if (killedBySubsequentStore) {
418453 if (propertySet.containsProperty(DeadLocalStoreProperty.STORE_OF_NULL)
419 || propertySet.containsProperty(DeadLocalStoreProperty.STORE_OF_CONSTANT))
454 || propertySet.containsProperty(DeadLocalStoreProperty.STORE_OF_CONSTANT)) {
420455 continue;
456 }
421457 propertySet.addProperty(DeadLocalStoreProperty.KILLED_BY_SUBSEQUENT_STORE);
422458 }
423459
424460 // Ignore dead assignments of null and 0.
425461 // These often indicate defensive programming.
426462 InstructionHandle prev = location.getBasicBlock().getPredecessorOf(location.getHandle());
427 int prevOpCode = -1;
463 // int prevOpCode = -1;
428464
429465 if (prev != null) {
430466 if (defensiveConstantValueOpcodes.get(prev.getInstruction().getOpcode())) {
431467 propertySet.addProperty(DeadLocalStoreProperty.DEFENSIVE_CONSTANT_OPCODE);
432 prevOpCode = prev.getInstruction().getOpcode();
468 // prevOpCode = prev.getInstruction().getOpcode();
433469 }
434470
435471 if (prev.getInstruction() instanceof GETFIELD) {
436472 InstructionHandle prev2 = prev.getPrev();
437473
438 if (prev2 != null && prev2.getInstruction() instanceof ALOAD)
474 if (prev2 != null && prev2.getInstruction() instanceof ALOAD) {
439475 propertySet.addProperty(DeadLocalStoreProperty.CACHING_VALUE);
440 }
441 if (prev.getInstruction() instanceof LoadInstruction)
476 }
477 }
478 if (prev.getInstruction() instanceof LoadInstruction) {
442479 propertySet.addProperty(DeadLocalStoreProperty.COPY_VALUE);
443 if (prev.getInstruction() instanceof InvokeInstruction)
480 }
481 if (prev.getInstruction() instanceof InvokeInstruction) {
444482 propertySet.addProperty(DeadLocalStoreProperty.METHOD_RESULT);
483 }
445484 }
446485 boolean deadObjectStore = false;
447486 if (ins instanceof IINC) {
448487 // special handling of IINC
449488
450 if (method.getName().equals("main") && method.isStatic()
451 && method.getSignature().equals("([Ljava/lang/String;)V"))
489 if ("main".equals(method.getName()) && method.isStatic()
490 && "([Ljava/lang/String;)V".equals(method.getSignature())) {
452491 propertySet.addProperty(DeadLocalStoreProperty.DEAD_INCREMENT_IN_MAIN);
492 }
453493
454494 InstructionHandle next = location.getHandle().getNext();
455 if (next != null && next.getInstruction() instanceof IRETURN)
495 if (next != null && next.getInstruction() instanceof IRETURN) {
456496 propertySet.addProperty(DeadLocalStoreProperty.DEAD_INCREMENT_IN_RETURN);
457 else
497 } else {
458498 propertySet.addProperty(DeadLocalStoreProperty.DEAD_INCREMENT);
499 }
459500 if (localIncrementCount[local] == 1) {
460501 propertySet.addProperty(DeadLocalStoreProperty.SINGLE_DEAD_INCREMENT);
461 } else
502 } else {
462503 propertySet.removeProperty(DeadLocalStoreProperty.IS_PARAMETER);
504 }
463505
464506 } else if (ins instanceof ASTORE && prev != null) {
465507 // Look for objects created but never used
466508
467509 Instruction prevIns = prev.getInstruction();
468 if ((prevIns instanceof INVOKESPECIAL && ((INVOKESPECIAL) prevIns).getMethodName(methodGen.getConstantPool())
469 .equals("<init>"))
510 if ((prevIns instanceof INVOKESPECIAL && "<init>".equals(((INVOKESPECIAL) prevIns).getMethodName(methodGen.getConstantPool())))
470511 || prevIns instanceof ANEWARRAY
471512 || prevIns instanceof NEWARRAY
472513 || prevIns instanceof MULTIANEWARRAY) {
473514 deadObjectStore = true;
474 } else if (prevIns instanceof DUP)
515 } else if (prevIns instanceof DUP) {
475516 propertySet.addProperty(DeadLocalStoreProperty.DUP_THEN_STORE);
476 }
477 if (deadObjectStore)
517 }
518 }
519 if (deadObjectStore) {
478520 propertySet.addProperty(DeadLocalStoreProperty.DEAD_OBJECT_STORE);
479 else if (!killedBySubsequentStore && localStoreCount[local] == 2 && localLoadCount[local] > 0) {
521 } else if (!killedBySubsequentStore && localStoreCount[local] == 2 && localLoadCount[local] > 0) {
480522 // TODO: why is this significant?
481523
482524 propertySet.addProperty(DeadLocalStoreProperty.TWO_STORES_MULTIPLE_LOADS);
495537 && !propertySet.containsProperty(DeadLocalStoreProperty.EXCEPTION_HANDLER)) {
496538 String signatureOfValue = typeOfValue.getSignature();
497539 if ((signatureOfValue.startsWith("Ljava/sql/") || signatureOfValue.startsWith("Ljavax/sql/"))
498 && !signatureOfValue.endsWith("Exception"))
540 && !signatureOfValue.endsWith("Exception")) {
499541 propertySet.addProperty(DeadLocalStoreProperty.STORE_OF_DATABASE_VALUE);
542 }
500543 }
501544
502545 if (parameterThatIsDeadAtEntry) {
503546 propertySet.addProperty(DeadLocalStoreProperty.PARAM_DEAD_ON_ENTRY);
504 if (pendingBugReportAboutOverwrittenParameter != null)
547 if (pendingBugReportAboutOverwrittenParameter != null) {
505548 pendingBugReportAboutOverwrittenParameter.setPriority(Priorities.HIGH_PRIORITY);
506 }
507
508 if (localStoreCount[local] > 3)
549 }
550 }
551
552 if (localStoreCount[local] > 3) {
509553 propertySet.addProperty(DeadLocalStoreProperty.MANY_STORES);
554 }
510555 int occurrences = cfg.getLocationsContainingInstructionWithOffset(pc).size();
511556 if (occurrences > 2 || sourceLineAnnotation.getStartLine() > 0
512 && linesMentionedMultipleTimes.get(sourceLineAnnotation.getStartLine()))
557 && linesMentionedMultipleTimes.get(sourceLineAnnotation.getStartLine())) {
513558 propertySet.addProperty(DeadLocalStoreProperty.CLONED_STORE);
559 }
514560 String sourceFile = javaClass.getSourceFileName();
515 if (Subtypes2.isJSP(javaClass))
561 if (Subtypes2.isJSP(javaClass)) {
516562 propertySet.addProperty(DeadLocalStoreProperty.IN_JSP_PAGE);
517 else if (BCELUtil.isSynthetic(javaClass) || sourceFile != null && !sourceFile.endsWith(".java")) {
518 if (sourceFile != null && sourceFile.endsWith(".gxp") && (lvName.startsWith("gxp$") || lvName.startsWith("gxp_")))
563 } else if (BCELUtil.isSynthetic(javaClass) || sourceFile != null && !sourceFile.endsWith(".java")) {
564 if (sourceFile != null && sourceFile.endsWith(".gxp") && (lvName.startsWith("gxp$") || lvName.startsWith("gxp_"))) {
519565 continue;
566 }
520567 propertySet.addProperty(DeadLocalStoreProperty.NOT_JAVA);
521568 }
522569
523570 // Report the warning
524571 String bugPattern;
525 if (storeOfNull)
572 if (storeOfNull) {
526573 bugPattern = "DLS_DEAD_LOCAL_STORE_OF_NULL";
527 else if (shadowedField != null)
574 } else if (shadowedField != null) {
528575 bugPattern = "DLS_DEAD_LOCAL_STORE_SHADOWS_FIELD";
529 else if (propertySet.containsProperty(DeadLocalStoreProperty.DEAD_INCREMENT_IN_RETURN))
576 } else if (propertySet.containsProperty(DeadLocalStoreProperty.DEAD_INCREMENT_IN_RETURN)) {
530577 bugPattern = "DLS_DEAD_LOCAL_INCREMENT_IN_RETURN";
531 else
578 } else {
532579 bugPattern = "DLS_DEAD_LOCAL_STORE";
580 }
533581 BugInstance bugInstance = new BugInstance(this, bugPattern, NORMAL_PRIORITY).addClassAndMethod(methodGen,
534582 sourceFileName).add(lvAnnotation);
535583
536 if (shadowedField != null)
584 if (shadowedField != null) {
537585 bugInstance.addField(FieldAnnotation.fromBCELField(classContext.getJavaClass(), shadowedField)).describe(
538586 FieldAnnotation.DID_YOU_MEAN_ROLE);
587 }
539588
540589 // If in relaxed reporting mode, encode heuristic
541590 // information.
554603 accumulator.accumulateBug(bugInstance, sourceLineAnnotation);
555604
556605 } finally {
557 if (pendingBugReportAboutOverwrittenParameter != null)
606 if (pendingBugReportAboutOverwrittenParameter != null) {
558607 bugReporter.reportBug(pendingBugReportAboutOverwrittenParameter);
608 }
559609 }
560610 }
561611
611661 for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
612662 Location location = i.next();
613663
614 if (location.getBasicBlock().isExceptionHandler())
664 if (location.getBasicBlock().isExceptionHandler()) {
615665 continue;
666 }
616667
617668 boolean isStore = isStore(location);
618669 boolean isLoad = isLoad(location);
619 if (!isStore && !isLoad)
670 if (!isStore && !isLoad) {
620671 continue;
672 }
621673
622674 IndexedInstruction ins = (IndexedInstruction) location.getHandle().getInstruction();
623675 int local = ins.getIndex();
625677 localStoreCount[local]++;
626678 localLoadCount[local]++;
627679 localIncrementCount[local]++;
628 } else if (isStore)
680 } else if (isStore) {
629681 localStoreCount[local]++;
630 else
682 } else {
631683 localLoadCount[local]++;
684 }
632685 }
633686 }
634687
642695 * index of the local
643696 * @param pc
644697 * program counter value of the instruction
645 */
698 *
646699 private void checkLocalVariableName(LocalVariableTable lvt, int local, int pc,
647700 WarningPropertySet<DeadLocalStoreProperty> propertySet) {
648701 if (lvt != null) {
653706 }
654707 }
655708
656 }
709 }*/
657710
658711 /**
659712 * Is instruction at given location a store?
679732 return (ins instanceof LoadInstruction) || (ins instanceof IINC);
680733 }
681734
735 @Override
682736 public void report() {
683737 }
684738 }
685
686 // vim:ts=4
2121 import java.util.HashSet;
2222 import java.util.Set;
2323
24 import org.apache.bcel.Repository;
25 import org.apache.bcel.classfile.Field;
26 import org.apache.bcel.classfile.JavaClass;
2724 import org.apache.bcel.classfile.Method;
2825
2926 import edu.umd.cs.findbugs.BugInstance;
3027 import edu.umd.cs.findbugs.BugReporter;
31 import edu.umd.cs.findbugs.BytecodeScanningDetector;
3228 import edu.umd.cs.findbugs.FieldAnnotation;
33
34 public class FindDoubleCheck extends BytecodeScanningDetector {
29 import edu.umd.cs.findbugs.OpcodeStack.Item;
30 import edu.umd.cs.findbugs.ba.XField;
31 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
32 import edu.umd.cs.findbugs.classfile.Global;
33 import edu.umd.cs.findbugs.detect.FindNoSideEffectMethods.MethodSideEffectStatus;
34 import edu.umd.cs.findbugs.detect.FindNoSideEffectMethods.NoSideEffectMethodsDatabase;
35
36 public class FindDoubleCheck extends OpcodeStackDetector {
3537 static final boolean DEBUG = false;
3638
3739 int stage = 0;
3840
39 int startPC, endPC;
41 int startPC, endPC, assignPC;
4042
4143 int count;
4244
4850
4951 FieldAnnotation pendingFieldLoad;
5052
53 XField currentDoubleCheckField;
54
5155 int countSinceGetReference;
5256
5357 int countSinceGetBoolean;
5458
55 private BugReporter bugReporter;
59 private final BugReporter bugReporter;
60
61 private final NoSideEffectMethodsDatabase nse;
5662
5763 public FindDoubleCheck(BugReporter bugReporter) {
5864 this.bugReporter = bugReporter;
65 this.nse = Global.getAnalysisCache().getDatabase(NoSideEffectMethodsDatabase.class);
5966 }
6067
6168 @Override
6269 public void visit(Method obj) {
63 if (DEBUG)
70 if (DEBUG) {
6471 System.out.println(getFullyQualifiedMethodName());
72 }
6573 super.visit(obj);
6674 fields.clear();
6775 twice.clear();
7179 countSinceGetBoolean = 1000;
7280 sawMonitorEnter = false;
7381 pendingFieldLoad = null;
82 currentDoubleCheckField = null;
7483 }
7584
7685 @Override
7786 public void sawOpcode(int seen) {
78 if (DEBUG)
87 if (DEBUG) {
7988 System.out.println(getPC() + "\t" + OPCODE_NAMES[seen] + "\t" + stage + "\t" + count + "\t" + countSinceGetReference);
80
81 if (seen == MONITORENTER)
89 }
90
91 if (seen == MONITORENTER) {
8292 sawMonitorEnter = true;
93 }
8394 if (seen == GETFIELD || seen == GETSTATIC) {
8495 pendingFieldLoad = FieldAnnotation.fromReferencedField(this);
85 if (DEBUG)
96 if (DEBUG) {
8697 System.out.println("\t" + pendingFieldLoad);
98 }
8799 String sig = getSigConstantOperand();
88 if (sig.equals("Z")) {
100 if ("Z".equals(sig)) {
89101 countSinceGetBoolean = 0;
90102 countSinceGetReference++;
91103 } else if (sig.startsWith("L") || sig.startsWith("[")) {
126138 }
127139 } else {
128140 count++;
129 if (count > 10)
141 if (count > 10) {
130142 stage = 0;
143 }
131144 }
132145 break;
133146 case 2:
141154 }
142155 }
143156 count++;
144 if (count > 10)
157 if (count > 10) {
145158 stage = 0;
159 }
146160 break;
147161 case 3:
148162 if (seen == PUTFIELD || seen == PUTSTATIC) {
149163 FieldAnnotation f = FieldAnnotation.fromReferencedField(this);
150 if (DEBUG)
164 if (DEBUG) {
151165 System.out.println("\t" + f);
166 }
152167 if (twice.contains(f) && !getNameConstantOperand().startsWith("class$")
153 && !getSigConstantOperand().equals("Ljava/lang/String;")) {
154 Field declaration = findField(getClassConstantOperand(), getNameConstantOperand());
155 /*
156 * System.out.println(f); System.out.println(declaration);
157 * System.out.println(getSigConstantOperand());
158 */
159 if (declaration == null || !declaration.isVolatile())
168 && !"Ljava/lang/String;".equals(getSigConstantOperand())) {
169 XField declaration = getXFieldOperand();
170 if (declaration == null || !declaration.isVolatile()) {
160171 bugReporter.reportBug(new BugInstance(this, "DC_DOUBLECHECK", NORMAL_PRIORITY).addClassAndMethod(this)
161172 .addField(f).describe("FIELD_ON").addSourceLineRange(this, startPC, endPC));
173 } else {
174 if(declaration.isReferenceType()) {
175 currentDoubleCheckField = declaration;
176 assignPC = getPC();
177 }
178 }
162179 stage++;
163180 }
164181 }
165182 break;
183 case 4:
184 if(currentDoubleCheckField != null) {
185 switch(seen) {
186 case MONITOREXIT:
187 stage++;
188 break;
189 case INVOKEINTERFACE:
190 case INVOKESPECIAL:
191 case INVOKEVIRTUAL:
192 if(nse.is(getMethodDescriptorOperand(), MethodSideEffectStatus.OBJ, MethodSideEffectStatus.SE)) {
193 checkStackValue(getNumberArguments(getMethodDescriptorOperand().getSignature()));
194 }
195 break;
196 case PUTFIELD:
197 checkStackValue(1);
198 break;
199 case DASTORE:
200 case FASTORE:
201 case SASTORE:
202 case LASTORE:
203 case BASTORE:
204 case CASTORE:
205 case AASTORE:
206 case IASTORE:
207 checkStackValue(2);
208 break;
209 }
210 }
211 break;
166212 default:
167 }
168 }
169
170 Field findField(String className, String fieldName) {
171 try {
172 // System.out.println("Looking for " + className);
173 JavaClass fieldDefinedIn = getThisClass();
174 if (!className.equals(getClassName())) {
175 // System.out.println("Using repository to look for " +
176 // className);
177
178 fieldDefinedIn = Repository.lookupClass(className);
179 }
180 Field[] f = fieldDefinedIn.getFields();
181 for (Field aF : f)
182 if (aF.getName().equals(fieldName)) {
183 // System.out.println("Found " + f[i]);
184 return aF;
185 }
186 return null;
187 } catch (ClassNotFoundException e) {
188 return null;
189 }
190 }
191
213 break;
214 }
215 }
216
217 private void checkStackValue(int arg) {
218 Item item = getStack().getStackItem(arg);
219 if(item.getXField() == currentDoubleCheckField) {
220 bugReporter.reportBug(new BugInstance(this, "DC_PARTIALLY_CONSTRUCTED", NORMAL_PRIORITY).addClassAndMethod(this)
221 .addField(currentDoubleCheckField).describe("FIELD_ON").addSourceLine(this).addSourceLine(this, assignPC)
222 .describe("SOURCE_LINE_STORED"));
223 stage++;
224 }
225 }
192226 }
3232
3333 @Override
3434 public void sawOpcode(int seen) {
35 if (seen == MONITOREXIT && (getPrevOpcode(2) == MONITORENTER || getPrevOpcode(1) == MONITORENTER))
35 if (seen == MONITOREXIT && (getPrevOpcode(2) == MONITORENTER || getPrevOpcode(1) == MONITORENTER)) {
3636 bugReporter.reportBug(new BugInstance(this, "ESync_EMPTY_SYNC", NORMAL_PRIORITY).addClassAndMethod(this)
3737 .addSourceLine(this));
38 }
3839
3940 }
4041 }
4949 state = 0;
5050 lastMethodCall = -1;
5151
52 if (DEBUG)
52 if (DEBUG) {
5353 System.out.println(getXMethod());
54 }
5455 super.visit(obj);
5556 possibleOverwrite = null;
56 if (DEBUG)
57 if (DEBUG) {
5758 System.out.println();
59 }
5860 initializedFields.clear();
5961 }
6062
6769 @Override
6870 public void sawOpcode(int seen) {
6971
70 if (DEBUG)
72 if (DEBUG) {
7173 System.out.printf("%5d %12s %s%n", getPC(), OPCODE_NAMES[seen],stack);
74 }
7275 if (seen == PUTFIELD) {
7376 OpcodeStack.Item top = stack.getStackItem(0);
7477 OpcodeStack.Item next = stack.getStackItem(1);
7982
8083 }
8184 possibleOverwrite = null;
82
85
8386 if (stack.getStackDepth() >= 4 && getNextOpcode() == PUTFIELD) {
8487 OpcodeStack.Item third = stack.getStackItem(2);
8588 OpcodeStack.Item fourth = stack.getStackItem(3);
8790 int registerNumber2 = fourth.getRegisterNumber();
8891 if (f2 != null && f2.equals(getXFieldOperand()) && registerNumber2 >= 0
8992 && registerNumber2 == third.getFieldLoadedFromRegister()
90 && !third.equals(top) && (third.getPC() == -1 || third.getPC() > lastMethodCall)) {
93 && !third.sameValue(top) && (third.getPC() == -1 || third.getPC() > lastMethodCall)) {
9194 possibleOverwrite = f2;
9295 }
9396 }
94
97
9598 XField f = top.getXField();
9699 int registerNumber = next.getRegisterNumber();
97100 if (f != null && f.equals(getXFieldOperand()) && registerNumber >= 0
100103
101104 LocalVariableAnnotation possibleMatch = LocalVariableAnnotation.findMatchingIgnoredParameter(getClassContext(),
102105 getMethod(), getNameConstantOperand(), getSigConstantOperand());
103 if (possibleMatch != null)
106 if (possibleMatch != null) {
104107 priority--;
105 else
108 } else {
106109 possibleMatch = LocalVariableAnnotation.findUniqueBestMatchingParameter(getClassContext(), getMethod(),
107110 getNameConstantOperand(), getSigConstantOperand());
111 }
108112 if (possibleMatch == null) {
109113 String signature = stack.getLVValue(registerNumber).getSignature();
110 for (int i = 0; i < stack.getNumLocalValues(); i++)
114 for (int i = 0; i < stack.getNumLocalValues(); i++) {
111115 if (i != register) {
112116 Item lvValue = stack.getLVValue(i);
113117 if (lvValue != null && lvValue.getSignature().equals(signature)) {
115119 break;
116120 }
117121 }
122 }
118123 }
119124
120125 bugReporter.reportBug(new BugInstance(this, "SA_FIELD_SELF_ASSIGNMENT", priority).addClassAndMethod(this)
121126 .addReferencedField(this).addOptionalAnnotation(possibleMatch).addSourceLine(this));
122127
123128 }
124 } else
129 } else {
125130 possibleOverwrite = null;
126 if (isMethodCall())
131 }
132 if (isMethodCall()) {
127133 lastMethodCall = getPC();
134 }
128135 switch (state) {
129136 case 0:
130 if (seen == DUP)
137 if (seen == DUP) {
131138 state = 6;
139 }
132140 break;
133141 case 6:
134142 if (isRegisterStore()) {
135143 state = 7;
136144 register = getRegisterOperand();
137 } else
145 } else {
138146 state = 0;
147 }
139148 break;
140149 case 7:
141150 if (isRegisterStore() && register == getRegisterOperand()) {
142151 bugReporter.reportBug(new BugInstance(this, "SA_LOCAL_DOUBLE_ASSIGNMENT", NORMAL_PRIORITY)
143 .addClassAndMethod(this)
144 .add(LocalVariableAnnotation.getLocalVariableAnnotation(getMethod(), register, getPC(), getPC() - 1))
145 .addSourceLine(this));
152 .addClassAndMethod(this)
153 .add(LocalVariableAnnotation.getLocalVariableAnnotation(getMethod(), register, getPC(), getPC() - 1))
154 .addSourceLine(this));
146155 }
147156 state = 0;
157 break;
158 default:
148159 break;
149160 }
150161
3232 public class FindFinalizeInvocations extends BytecodeScanningDetector implements StatelessDetector {
3333 private static final boolean DEBUG = SystemProperties.getBoolean("ffi.debug");
3434
35 private BugReporter bugReporter;
35 private final BugReporter bugReporter;
3636
3737 private final BugAccumulator bugAccumulator;
3838
4545
4646 @Override
4747 public void visit(Method obj) {
48 if (DEBUG)
48 if (DEBUG) {
4949 System.out.println("FFI: visiting " + getFullyQualifiedMethodName());
50 if (getMethodName().equals("finalize") && getMethodSig().equals("()V") && (obj.getAccessFlags() & (ACC_PUBLIC)) != 0)
50 }
51 if ("finalize".equals(getMethodName()) && "()V".equals(getMethodSig()) && (obj.getAccessFlags() & (ACC_PUBLIC)) != 0) {
5152 bugReporter
52 .reportBug(new BugInstance(this, "FI_PUBLIC_SHOULD_BE_PROTECTED", NORMAL_PRIORITY).addClassAndMethod(this));
53 .reportBug(new BugInstance(this, "FI_PUBLIC_SHOULD_BE_PROTECTED", NORMAL_PRIORITY).addClassAndMethod(this));
54 }
5355 }
5456
5557 @Override
5759 sawSuperFinalize = false;
5860 super.visit(obj);
5961 bugAccumulator.reportAccumulatedBugs();
60 if (!getMethodName().equals("finalize") || !getMethodSig().equals("()V"))
62 if (!"finalize".equals(getMethodName()) || !"()V".equals(getMethodSig())) {
6163 return;
64 }
6265 String overridesFinalizeIn = Lookup.findSuperImplementor(getDottedClassName(), "finalize", "()V", bugReporter);
63 boolean superHasNoFinalizer = overridesFinalizeIn.equals("java.lang.Object");
66 boolean superHasNoFinalizer = "java.lang.Object".equals(overridesFinalizeIn);
6467 // System.out.println("superclass: " + superclassName);
6568 if (obj.getCode().length == 1) {
6669 if (superHasNoFinalizer) {
67 if (!getMethod().isFinal())
70 if (!getMethod().isFinal()) {
6871 bugReporter.reportBug(new BugInstance(this, "FI_EMPTY", NORMAL_PRIORITY).addClassAndMethod(this));
69 } else
72 }
73 } else {
7074 bugReporter.reportBug(new BugInstance(this, "FI_NULLIFY_SUPER", NORMAL_PRIORITY).addClassAndMethod(this)
7175 .addClass(overridesFinalizeIn));
72 } else if (obj.getCode().length == 5 && sawSuperFinalize)
76 }
77 } else if (obj.getCode().length == 5 && sawSuperFinalize) {
7378 bugReporter.reportBug(new BugInstance(this, "FI_USELESS", NORMAL_PRIORITY).addClassAndMethod(this));
74 else if (!sawSuperFinalize && !superHasNoFinalizer)
79 } else if (!sawSuperFinalize && !superHasNoFinalizer) {
7580 bugReporter.reportBug(new BugInstance(this, "FI_MISSING_SUPER_CALL", NORMAL_PRIORITY).addClassAndMethod(this)
7681 .addClass(overridesFinalizeIn));
82 }
7783 }
7884
7985 @Override
8086 public void sawOpcode(int seen) {
81 if (seen == INVOKEVIRTUAL && getNameConstantOperand().equals("finalize") && getSigConstantOperand().equals("()V")) {
87 if (seen == INVOKEVIRTUAL && "finalize".equals(getNameConstantOperand()) && "()V".equals(getSigConstantOperand())) {
8288 bugAccumulator.accumulateBug(
83 new BugInstance(this, "FI_EXPLICIT_INVOCATION", getMethodName().equals("finalize")
84 && getMethodSig().equals("()V") ? HIGH_PRIORITY : NORMAL_PRIORITY).addClassAndMethod(this)
89 new BugInstance(this, "FI_EXPLICIT_INVOCATION", "finalize".equals(getMethodName())
90 && "()V".equals(getMethodSig()) ? HIGH_PRIORITY : NORMAL_PRIORITY).addClassAndMethod(this)
8591 .addCalledMethod(this), this);
8692
8793 }
88 if (seen == INVOKESPECIAL && getNameConstantOperand().equals("finalize"))
94 if (seen == INVOKESPECIAL && "finalize".equals(getNameConstantOperand())) {
8995 sawSuperFinalize = true;
96 }
9097 }
9198 }
4040
4141 private int priority;
4242
43 private BugReporter bugReporter;
44
45 private BugAccumulator bugAccumulator;
43 private final BugReporter bugReporter;
44
45 private final BugAccumulator bugAccumulator;
4646
4747 private int state;
4848
6868 boolean first = true;
6969 for (SourceLineAnnotation s : found) {
7070 bug.add(s);
71 if (first)
71 if (first) {
7272 first = false;
73 else
73 } else {
7474 bug.describe(SourceLineAnnotation.ROLE_ANOTHER_INSTANCE);
75 }
7576 }
7677
7778 bugReporter.reportBug(bug);
8182 }
8283
8384 public boolean isZero(Number n) {
84 if (n == null)
85 return false;
85 if (n == null) {
86 return false;
87 }
8688 double v = n.doubleValue();
8789 return v == 0.0;
8890 }
8991
9092 public boolean okValueToCompareAgainst(Number n) {
91 if (n == null)
92 return false;
93 if (n == null) {
94 return false;
95 }
9396 double v = n.doubleValue();
94 if (Double.isInfinite(v) || Double.isNaN(v))
97 if (Double.isInfinite(v) || Double.isNaN(v)) {
9598 return true;
99 }
96100 v = v - Math.floor(v);
97101 return v == 0.0;
98102 }
106110 case DCMPL:
107111 if (stack.getStackDepth() >= 2) {
108112 OpcodeStack.Item first = stack.getStackItem(0);
109 OpcodeStack.Item second = stack.getStackItem(1);
110
111 if (first.getRegisterNumber() == second.getRegisterNumber() && first.getRegisterNumber() != -1)
112 break;
113 if (first.isInitialParameter() && second.isInitialParameter())
114 break;
115 if (sameField(first, second))
116 break;
117
113 OpcodeStack.Item second = stack.getStackItem(1);
114
115 if (first.getRegisterNumber() == second.getRegisterNumber() && first.getRegisterNumber() != -1) {
116 break;
117 }
118 if (first.isInitialParameter() && second.isInitialParameter()) {
119 break;
120 }
121 if (sameField(first, second)) {
122 break;
123 }
124
118125 Number n1 = (Number) first.getConstant();
119126 Number n2 = (Number) second.getConstant();
120127 if (n1 != null && Double.isNaN(n1.doubleValue()) || n2 != null && Double.isNaN(n2.doubleValue())) {
121128 BugInstance bug = new BugInstance(this, "FE_TEST_IF_EQUAL_TO_NOT_A_NUMBER", HIGH_PRIORITY)
122 .addClassAndMethod(this);
129 .addClassAndMethod(this);
123130 bugAccumulator.accumulateBug(bug, this);
124131 state = SAW_NOTHING;
125132 break;
128135 || second.getSpecialKind() == OpcodeStack.Item.NASTY_FLOAT_MATH && !isZero(n1)
129136 || first.getSpecialKind() == OpcodeStack.Item.FLOAT_MATH && !okValueToCompareAgainst(n2)
130137 || second.getSpecialKind() == OpcodeStack.Item.FLOAT_MATH && !okValueToCompareAgainst(n1)) {
131 if (priority != HIGH_PRIORITY)
138 if (priority != HIGH_PRIORITY) {
132139 found.clear();
140 }
133141 priority = HIGH_PRIORITY;
134142 state = SAW_COMP;
135143 break;
136144 }
137 if (priority == HIGH_PRIORITY)
138 break;
145 if (priority == HIGH_PRIORITY) {
146 break;
147 }
139148 // if (first.isInitialParameter() && n2 != null) break;
140149 // if (second.isInitialParameter() && n1 != null) break;
141 if (n1 != null && n2 != null)
142 break;
143
144 if (okValueToCompareAgainst(n1) || okValueToCompareAgainst(n2))
145 break;
150 if (n1 != null && n2 != null) {
151 break;
152 }
153
154 if (okValueToCompareAgainst(n1) || okValueToCompareAgainst(n2)) {
155 break;
156 }
146157 if (n1 != null && !second.isInitialParameter() || n2 != null && !first.isInitialParameter()) {
147 if (priority == LOW_PRIORITY)
158 if (priority == LOW_PRIORITY) {
148159 found.clear();
160 }
149161 priority = NORMAL_PRIORITY;
150162
151 } else if (priority == NORMAL_PRIORITY)
152 break;
163 } else if (priority == NORMAL_PRIORITY) {
164 break;
165 }
153166 state = SAW_COMP;
154167 }
155168 break;
173186 }
174187
175188 static boolean sameField(Item i1, Item i2) {
176 if (i1.getXField() == null)
177 return false;
178 if (!i1.getXField().equals(i2.getXField()))
179 return false;
180 if (i1.getFieldLoadedFromRegister() != i2.getFieldLoadedFromRegister())
181 return false;
189 if (i1.getXField() == null) {
190 return false;
191 }
192 if (!i1.getXField().equals(i2.getXField())) {
193 return false;
194 }
195 if (i1.getFieldLoadedFromRegister() != i2.getFieldLoadedFromRegister()) {
196 return false;
197 }
182198 return true;
183199 }
184200 }
2424 import edu.umd.cs.findbugs.StatelessDetector;
2525
2626 public class FindFloatMath extends BytecodeScanningDetector implements StatelessDetector {
27 private BugReporter bugReporter;
27 private final BugReporter bugReporter;
2828
2929 public FindFloatMath(BugReporter bugReporter) {
3030 this.bugReporter = bugReporter;
3636 case FMUL:
3737 case FDIV:
3838 if (getFullyQualifiedMethodName().indexOf("float") == -1 && getFullyQualifiedMethodName().indexOf("Float") == -1
39 && getFullyQualifiedMethodName().indexOf("FLOAT") == -1)
39 && getFullyQualifiedMethodName().indexOf("FLOAT") == -1) {
4040 bugReporter.reportBug(new BugInstance(this, "FL_MATH_USING_FLOAT_PRECISION", LOW_PRIORITY)
41 .addClassAndMethod(this).addSourceLine(this));
41 .addClassAndMethod(this).addSourceLine(this));
42 }
4243 break;
4344 case FCMPG:
4445 case FCMPL:
4748 case FSUB:
4849 case FREM:
4950 if (getFullyQualifiedMethodName().indexOf("float") == -1 && getFullyQualifiedMethodName().indexOf("Float") == -1
50 && getFullyQualifiedMethodName().indexOf("FLOAT") == -1)
51
51 && getFullyQualifiedMethodName().indexOf("FLOAT") == -1) {
5252 bugReporter.reportBug(new BugInstance(this, "FL_MATH_USING_FLOAT_PRECISION", NORMAL_PRIORITY).addClassAndMethod(
5353 this).addSourceLine(this));
54 }
5455 break;
5556 default:
5657 break;
4040 import edu.umd.cs.findbugs.MethodAnnotation;
4141 import edu.umd.cs.findbugs.OpcodeStack;
4242 import edu.umd.cs.findbugs.Priorities;
43 import edu.umd.cs.findbugs.SourceLineAnnotation;
4344 import edu.umd.cs.findbugs.StatelessDetector;
4445 import edu.umd.cs.findbugs.TypeAnnotation;
4546 import edu.umd.cs.findbugs.ba.AnalysisContext;
5657 import edu.umd.cs.findbugs.visitclass.PreorderVisitor;
5758
5859 public class FindHEmismatch extends OpcodeStackDetector implements StatelessDetector {
59 boolean hasFields = false;
60
61 boolean visibleOutsidePackage = false;
62
63 boolean hasHashCode = false;
64
65 boolean hasEqualsObject = false;
66
67 boolean hashCodeIsAbstract = false;
68
69 boolean equalsObjectIsAbstract = false;
70
71 boolean equalsMethodIsInstanceOfEquals = false;
72
73 boolean equalsReimplementesObjectEquals = false;
74
75 boolean hasCompareToObject = false;
76
77 boolean hasCompareToBridgeMethod = false;
78
79 boolean hasEqualsSelf = false;
80
81 boolean hasEqualsOther = false;
82
83 boolean hasCompareToSelf = false;
84
85 boolean extendsObject = false;
86
87 MethodAnnotation equalsMethod = null;
88
89 MethodAnnotation equalsOtherMethod = null;
90
91 ClassDescriptor equalsOtherClass = null;
92
93 MethodAnnotation compareToMethod = null;
94
95 MethodAnnotation compareToObjectMethod = null;
96
97 MethodAnnotation compareToSelfMethod = null;
98
99 MethodAnnotation hashCodeMethod = null;
100
101 HashSet<String> nonHashableClasses = new HashSet<String>();
60
61 static final Pattern mapPattern = Pattern.compile("[^y]HashMap<L([^;<]*);");
62 static final Pattern hashTablePattern = Pattern.compile("Hashtable<L([^;<]*);");
63 static final Pattern setPattern = Pattern.compile("[^y]HashSet<L([^;<]*);");
64 static final Pattern predicateOverAnInstance = Pattern.compile("\\(L([^;]+);\\)Z");
65
66 boolean isApplicationClass;
67
68 boolean hasFields;
69
70 boolean visibleOutsidePackage;
71
72 boolean hasHashCode;
73
74 boolean hasEqualsObject;
75
76 boolean hashCodeIsAbstract;
77
78 boolean equalsObjectIsAbstract;
79
80 boolean equalsMethodIsInstanceOfEquals;
81
82 boolean hasCompareToObject;
83
84 boolean hasCompareToBridgeMethod;
85
86 boolean hasEqualsSelf;
87
88 boolean hasEqualsOther;
89
90 boolean hasCompareToSelf;
91
92 boolean extendsObject;
93
94 MethodAnnotation equalsMethod;
95
96 MethodAnnotation equalsOtherMethod;
97
98 ClassDescriptor equalsOtherClass;
99
100 MethodAnnotation compareToMethod;
101
102 MethodAnnotation compareToObjectMethod;
103
104 MethodAnnotation compareToSelfMethod;
105
106 MethodAnnotation hashCodeMethod;
107
108 final HashSet<String> nonHashableClasses;
109
110 final Map<String, BugInstance> potentialBugs;
111
112 private final BugReporter bugReporter;
113
114 public FindHEmismatch(BugReporter bugReporter) {
115 this.bugReporter = bugReporter;
116 nonHashableClasses = new HashSet<String>();
117 potentialBugs = new HashMap<String, BugInstance>();
118 }
102119
103120 public boolean isHashableClassName(String dottedClassName) {
104121 return !nonHashableClasses.contains(dottedClassName);
105122 }
106123
107 Map<String, BugInstance> potentialBugs = new HashMap<String, BugInstance>();
108
109 private BugReporter bugReporter;
110
111 public FindHEmismatch(BugReporter bugReporter) {
112 this.bugReporter = bugReporter;
113 }
114
115124 @Override
116125 public void visitAfter(JavaClass obj) {
117 if (!obj.isClass())
118 return;
119 if (getDottedClassName().equals("java.lang.Object"))
120 return;
126 if (!obj.isClass()) {
127 return;
128 }
129 if ("java.lang.Object".equals(getDottedClassName())) {
130 return;
131 }
121132 int accessFlags = obj.getAccessFlags();
122 if ((accessFlags & ACC_INTERFACE) != 0)
123 return;
133 if ((accessFlags & ACC_INTERFACE) != 0) {
134 return;
135 }
124136 visibleOutsidePackage = obj.isPublic() || obj.isProtected();
125137
126138 String whereEqual = getDottedClassName();
143155 }
144156 }
145157 }
146 boolean usesDefaultEquals = whereEqual.equals("java.lang.Object");
158 boolean usesDefaultEquals = "java.lang.Object".equals(whereEqual);
147159 String whereHashCode = getDottedClassName();
148160 if (!hasHashCode) {
149161 XClass wh = Lookup.findSuperImplementor(getXClass(), "hashCode", "()I", false, bugReporter);
152164 } else {
153165 whereHashCode = wh.getClassDescriptor().getDottedClassName();
154166 XMethod m = wh.findMethod("hashCode", "()I", false);
155 if (m != null && m.isFinal())
167 if (m != null && m.isFinal()) {
156168 inheritedHashCodeIsFinal = true;
157 }
158 }
159 boolean usesDefaultHashCode = whereHashCode.equals("java.lang.Object");
169 }
170 }
171 }
172 boolean usesDefaultHashCode = "java.lang.Object".equals(whereHashCode);
173 /*
160174 if (false && (usesDefaultEquals || usesDefaultHashCode)) {
161175 try {
162176 if (Repository.implementationOf(obj, "java/util/Set") || Repository.implementationOf(obj, "java/util/List")
168182 // e.printStackTrace();
169183 }
170184 }
185 */
171186
172187 if (!hasEqualsObject && !hasEqualsSelf && hasEqualsOther) {
173188 BugInstance bug = new BugInstance(this, usesDefaultEquals ? "EQ_OTHER_USE_OBJECT" : "EQ_OTHER_NO_OBJECT",
178193
179194 if (usesDefaultEquals) {
180195 int priority = HIGH_PRIORITY;
181 if (usesDefaultHashCode || obj.isAbstract())
196 if (usesDefaultHashCode || obj.isAbstract()) {
182197 priority++;
183 if (!visibleOutsidePackage)
198 }
199 if (!visibleOutsidePackage) {
184200 priority++;
201 }
185202 String bugPattern = "EQ_SELF_USE_OBJECT";
186203
187204 BugInstance bug = new BugInstance(this, bugPattern, priority).addClass(getDottedClassName());
188 if (equalsMethod != null)
205 if (equalsMethod != null) {
189206 bug.addMethod(equalsMethod);
207 }
190208 bugReporter.reportBug(bug);
191209 } else {
192210 int priority = NORMAL_PRIORITY;
193 if (hasFields)
211 if (hasFields) {
194212 priority--;
195 if (obj.isAbstract())
213 }
214 if (obj.isAbstract()) {
196215 priority++;
216 }
197217 String bugPattern = "EQ_SELF_NO_OBJECT";
198218 String superclassName = obj.getSuperclassName();
199 if (superclassName.equals("java.lang.Enum")) {
219 if ("java.lang.Enum".equals(superclassName)) {
200220 bugPattern = "EQ_DONT_DEFINE_EQUALS_FOR_ENUM";
201221 priority = HIGH_PRIORITY;
202222 }
203223 BugInstance bug = new BugInstance(this, bugPattern, priority).addClass(getDottedClassName());
204 if (equalsMethod != null)
224 if (equalsMethod != null) {
205225 bug.addMethod(equalsMethod);
226 }
206227 bugReporter.reportBug(bug);
207228 }
208229 }
218239 if ((hasCompareToObject || hasCompareToSelf) && usesDefaultEquals) {
219240 BugInstance bug = new BugInstance(this, "EQ_COMPARETO_USE_OBJECT_EQUALS", obj.isAbstract() ? Priorities.LOW_PRIORITY
220241 : Priorities.NORMAL_PRIORITY).addClass(this);
221 if (compareToSelfMethod != null)
242 if (compareToSelfMethod != null) {
222243 bug.addMethod(compareToSelfMethod);
223 else
244 } else {
224245 bug.addMethod(compareToObjectMethod);
246 }
225247 bugReporter.reportBug(bug);
226248 }
227249 if (!hasCompareToObject && !hasCompareToBridgeMethod && hasCompareToSelf) {
228 if (!extendsObject)
250 if (!extendsObject) {
229251 bugReporter.reportBug(new BugInstance(this, "CO_SELF_NO_OBJECT", NORMAL_PRIORITY).addClass(getDottedClassName())
230252 .addMethod(compareToMethod));
253 }
231254 }
232255
233256 // if (!hasFields) return;
234 if (hasHashCode && !hashCodeIsAbstract && !(hasEqualsObject && !equalsReimplementesObjectEquals || hasEqualsSelf)) {
257 if (hasHashCode && !hashCodeIsAbstract && !(hasEqualsObject || hasEqualsSelf)) {
235258 int priority = LOW_PRIORITY;
236 if (usesDefaultEquals)
259 if (usesDefaultEquals) {
237260 bugReporter.reportBug(new BugInstance(this, "HE_HASHCODE_USE_OBJECT_EQUALS", priority).addClass(
238261 getDottedClassName()).addMethod(hashCodeMethod));
239 else if (!inheritedEqualsIsFinal)
262 } else if (!inheritedEqualsIsFinal) {
240263 bugReporter.reportBug(new BugInstance(this, "HE_HASHCODE_NO_EQUALS", priority).addClass(getDottedClassName())
241264 .addMethod(hashCodeMethod));
265 }
242266 }
243267 if (equalsObjectIsAbstract) {
244268 // no errors reported
245269 } else if (!hasHashCode && (hasEqualsObject || hasEqualsSelf)) {
246270 EqualsKindSummary.KindOfEquals equalsKind = AnalysisContext.currentAnalysisContext().getEqualsKindSummary()
247271 .get(new ClassAnnotation(obj.getClassName()));
248 if (equalsKind == EqualsKindSummary.KindOfEquals.ALWAYS_FALSE)
272 if (equalsKind == EqualsKindSummary.KindOfEquals.ALWAYS_FALSE) {
249273 return;
274 }
250275 if (usesDefaultHashCode) {
251276 int priority = HIGH_PRIORITY;
252 if (equalsMethodIsInstanceOfEquals)
277 if (equalsMethodIsInstanceOfEquals) {
253278 priority += 2;
254 else if (obj.isAbstract() || !hasEqualsObject)
279 } else if (obj.isAbstract() || !hasEqualsObject) {
255280 priority++;
256 if (priority == HIGH_PRIORITY)
281 }
282 if (priority == HIGH_PRIORITY) {
257283 nonHashableClasses.add(getDottedClassName());
284 }
258285 if (!visibleOutsidePackage) {
259286 priority++;
260287 }
261288 BugInstance bug = new BugInstance(this, "HE_EQUALS_USE_HASHCODE", priority).addClass(getDottedClassName());
262 if (equalsMethod != null)
289 if (equalsMethod != null) {
263290 bug.addMethod(equalsMethod);
291 }
264292 bugReporter.reportBug(bug);
265293 } else if (!inheritedHashCodeIsFinal && !whereHashCode.startsWith("java.util.Abstract")) {
266294 int priority = LOW_PRIORITY;
267295
268 if (hasEqualsObject && inheritedEqualsIsAbstract)
296 if (hasEqualsObject && inheritedEqualsIsAbstract) {
269297 priority++;
270 if (hasFields)
298 }
299 if (hasFields) {
271300 priority--;
272 if (equalsMethodIsInstanceOfEquals || !hasEqualsObject)
301 }
302 if (equalsMethodIsInstanceOfEquals || !hasEqualsObject) {
273303 priority += 2;
274 else if (obj.isAbstract())
304 } else if (obj.isAbstract()) {
275305 priority++;
306 }
276307 BugInstance bug = new BugInstance(this, "HE_EQUALS_NO_HASHCODE", priority).addClass(getDottedClassName());
277 if (equalsMethod != null)
308 if (equalsMethod != null) {
278309 bug.addMethod(equalsMethod);
310 }
279311 bugReporter.reportBug(bug);
280312 }
281313 }
282314 if (!hasHashCode && !hasEqualsObject && !hasEqualsSelf && !usesDefaultEquals && usesDefaultHashCode && !obj.isAbstract()
283315 && inheritedEqualsFromAbstractClass) {
284316 BugInstance bug = new BugInstance(this, "HE_INHERITS_EQUALS_USE_HASHCODE", NORMAL_PRIORITY)
285 .addClass(getDottedClassName());
286 if (equalsMethod != null)
317 .addClass(getDottedClassName());
318 if (equalsMethod != null) {
287319 bug.addMethod(equalsMethod);
320 }
288321 bugReporter.reportBug(bug);
289322 }
290323 if (!hasEqualsObject && !hasEqualsSelf && !usesDefaultEquals && !obj.isAbstract() && hasFields && inheritedEquals != null
291324 && !inheritedEqualsIsFinal && !inheritedEqualsFromAbstractClass
292325 && !inheritedEquals.getClassDescriptor().getSimpleName().contains("Abstract")
293 && !inheritedEquals.getClassDescriptor().getClassName().equals("java/lang/Enum")) {
294
295 BugInstance bug = new BugInstance(this, "EQ_DOESNT_OVERRIDE_EQUALS", NORMAL_PRIORITY).addClass(this)
296 .addMethod(inheritedEquals).describe(MethodAnnotation.METHOD_DID_YOU_MEAN_TO_OVERRIDE);
326 && !"java/lang/Enum".equals(inheritedEquals.getClassDescriptor().getClassName())) {
327
328 BugInstance bug = new BugInstance(this, "EQ_DOESNT_OVERRIDE_EQUALS", NORMAL_PRIORITY);
329
330 // create annotation pointing to this class source line 1, otherwise the primary annotation shows parent class
331 SourceLineAnnotation sourceLine = new SourceLineAnnotation(getDottedClassName(), obj.getSourceFileName(), 1, 1, 0, 0);
332 bug.addClass(getDottedClassName()).add(sourceLine);
333 bug.addMethod(inheritedEquals).describe(MethodAnnotation.METHOD_DID_YOU_MEAN_TO_OVERRIDE);
297334 bugReporter.reportBug(bug);
298335 }
299336 }
300337
301338 @Override
302339 public void visit(JavaClass obj) {
303 extendsObject = getDottedSuperclassName().equals("java.lang.Object");
340 extendsObject = "java.lang.Object".equals(getDottedSuperclassName());
304341 hasFields = false;
305342 hasHashCode = false;
306343 hasCompareToObject = false;
321358 equalsOtherClass = null;
322359 isApplicationClass = AnalysisContext.currentAnalysisContext().isApplicationClass(obj);
323360 }
324
361
325362 @Override
326363 public boolean shouldVisitCode(Code obj) {
327 if (isApplicationClass)
364 if (isApplicationClass) {
328365 return true;
366 }
329367 String name = getMethod().getName();
330 if (name.equals("hashCode") || name.equals("equals"))
368 if ("hashCode".equals(name) || "equals".equals(name)) {
331369 return true;
370 }
332371 return false;
333
334 }
335 boolean isApplicationClass;
372
373 }
336374
337375 public static int opcode(byte code[], int offset) {
338376 return code[offset] & 0xff;
341379 @Override
342380 public void visit(Field obj) {
343381 int accessFlags = obj.getAccessFlags();
344 if ((accessFlags & ACC_STATIC) != 0)
345 return;
346 if (!obj.getName().startsWith("this$") && !BCELUtil.isSynthetic(obj) && !obj.isTransient())
382 if ((accessFlags & ACC_STATIC) != 0) {
383 return;
384 }
385 if (!obj.getName().startsWith("this$") && !BCELUtil.isSynthetic(obj) && !obj.isTransient()) {
347386 hasFields = true;
348 }
349
350 static final Pattern predicateOverAnInstance = Pattern.compile("\\(L([^;]+);\\)Z");
387 }
388 }
351389
352390 @Override
353391 public void visit(Method obj) {
354392
355393 int accessFlags = obj.getAccessFlags();
356 if ((accessFlags & ACC_STATIC) != 0)
357 return;
394 if ((accessFlags & ACC_STATIC) != 0) {
395 return;
396 }
358397 String name = obj.getName();
359398 String sig = obj.getSignature();
360399 if ((accessFlags & ACC_ABSTRACT) != 0) {
361 if (name.equals("equals") && sig.equals("(L" + getClassName() + ";)Z")) {
400 if ("equals".equals(name) && sig.equals("(L" + getClassName() + ";)Z")) {
362401 bugReporter.reportBug(new BugInstance(this, "EQ_ABSTRACT_SELF", LOW_PRIORITY).addClass(getDottedClassName()));
363402 return;
364 } else if (name.equals("compareTo") && sig.equals("(L" + getClassName() + ";)I")) {
403 } else if ("compareTo".equals(name) && sig.equals("(L" + getClassName() + ";)I")) {
365404 bugReporter.reportBug(new BugInstance(this, "CO_ABSTRACT_SELF", LOW_PRIORITY).addClass(getDottedClassName()));
366405 return;
367406 }
368407 }
369 boolean sigIsObject = sig.equals("(Ljava/lang/Object;)Z");
370 if (name.equals("hashCode") && sig.equals("()I")) {
408 boolean sigIsObject = "(Ljava/lang/Object;)Z".equals(sig);
409 if ("hashCode".equals(name) && "()I".equals(sig)) {
371410 hasHashCode = true;
372 if (obj.isAbstract())
411 if (obj.isAbstract()) {
373412 hashCodeIsAbstract = true;
413 }
374414 hashCodeMethod = MethodAnnotation.fromVisitedMethod(this);
375415 // System.out.println("Found hashCode for " + betterClassName);
376 } else if (obj.isPublic() && name.equals("equals")) {
416 } else if (obj.isPublic() && "equals".equals(name)) {
377417 Matcher m = predicateOverAnInstance.matcher(sig);
378418 if (m.matches()) {
379419 if (sigIsObject) {
380420 equalsMethod = MethodAnnotation.fromVisitedMethod(this);
381421 hasEqualsObject = true;
382 if (obj.isAbstract())
422 if (obj.isAbstract()) {
383423 equalsObjectIsAbstract = true;
384 else if (!obj.isNative()) {
424 } else if (!obj.isNative()) {
385425 Code code = obj.getCode();
386426 byte[] codeBytes = code.getCode();
387427 if (codeBytes.length == 9) {
394434 int op8 = opcode(codeBytes, 8);
395435 if ((op0 == ALOAD_0 && op1 == ALOAD_1 || op0 == ALOAD_1 && op1 == ALOAD_0)
396436 && (op2 == IF_ACMPEQ || op2 == IF_ACMPNE) && (op5 == ICONST_0 || op5 == ICONST_1)
397 && op6 == IRETURN && (op7 == ICONST_0 || op7 == ICONST_1) && op8 == IRETURN)
437 && op6 == IRETURN && (op7 == ICONST_0 || op7 == ICONST_1) && op8 == IRETURN) {
398438 equalsMethodIsInstanceOfEquals = true;
439 }
399440 } else if (codeBytes.length == 11) {
400441 int op0 = opcode(codeBytes, 0);
401442 int op1 = opcode(codeBytes, 1);
406447 int op10 = opcode(codeBytes, 10);
407448 if ((op0 == ALOAD_0 && op1 == ALOAD_1 || op0 == ALOAD_1 && op1 == ALOAD_0)
408449 && (op2 == IF_ACMPEQ || op2 == IF_ACMPNE) && (op5 == ICONST_0 || op5 == ICONST_1)
409 && op6 == GOTO && (op9 == ICONST_0 || op9 == ICONST_1) && op10 == IRETURN)
450 && op6 == GOTO && (op9 == ICONST_0 || op9 == ICONST_1) && op10 == IRETURN) {
410451 equalsMethodIsInstanceOfEquals = true;
452 }
411453
412454 } else if ((codeBytes.length == 5 && (codeBytes[1] & 0xff) == INSTANCEOF)
413455 || (codeBytes.length == 15 && (codeBytes[1] & 0xff) == INSTANCEOF && (codeBytes[11] & 0xff) == INVOKESPECIAL)) {
416458 }
417459 } else if (sig.equals("(L" + getClassName() + ";)Z")) {
418460 hasEqualsSelf = true;
419 if (equalsMethod == null)
461 if (equalsMethod == null) {
420462 equalsMethod = MethodAnnotation.fromVisitedMethod(this);
463 }
421464 } else {
422465 String arg = m.group(1);
423466 if (getSuperclassName().equals(arg)) {
431474
432475 }
433476 }
434 } else if (name.equals("compareTo") && sig.endsWith(")I") && !obj.isStatic()) {
477 } else if ("compareTo".equals(name) && sig.endsWith(")I") && !obj.isStatic()) {
435478 MethodAnnotation tmp = MethodAnnotation.fromVisitedMethod(this);
436 if (BCELUtil.isSynthetic(obj))
479 if (BCELUtil.isSynthetic(obj)) {
437480 hasCompareToBridgeMethod = true;
438 if (sig.equals("(Ljava/lang/Object;)I")) {
481 }
482 if ("(Ljava/lang/Object;)I".equals(sig)) {
439483 hasCompareToObject = true;
440484 compareToObjectMethod = compareToMethod = tmp;
441485 } else if (sig.equals("(L" + getClassName() + ";)I")) {
447491
448492 Method findMethod(JavaClass clazz, String name, String sig) {
449493 Method[] m = clazz.getMethods();
450 for (Method aM : m)
451 if (aM.getName().equals(name) && aM.getSignature().equals(sig))
494 for (Method aM : m) {
495 if (aM.getName().equals(name) && aM.getSignature().equals(sig)) {
452496 return aM;
497 }
498 }
453499 return null;
454500 }
455501
457503 public void sawOpcode(int seen) {
458504 if (seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE) {
459505 String className = getClassConstantOperand();
460 if (className.equals("java/util/Map") || className.equals("java/util/HashMap")
461 || className.equals("java/util/LinkedHashMap") || className.equals("java/util/concurrent/ConcurrentHashMap")
506 if ("java/util/Map".equals(className) || "java/util/HashMap".equals(className)
507 || "java/util/LinkedHashMap".equals(className) || "java/util/concurrent/ConcurrentHashMap".equals(className)
462508 || className.contains("Hash")
463509 && Subtypes2.instanceOf(ClassName.toDottedClassName(className), "java.util.Map")) {
464 if (getNameConstantOperand().equals("put")
465 && getSigConstantOperand().equals("(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;")
466 && stack.getStackDepth() >= 3)
510 if ("put".equals(getNameConstantOperand())
511 && "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;".equals(getSigConstantOperand())
512 && stack.getStackDepth() >= 3) {
467513 check(1);
468 else if ((getNameConstantOperand().equals("get") || getNameConstantOperand().equals("remove"))
469 && getSigConstantOperand().startsWith("(Ljava/lang/Object;)") && stack.getStackDepth() >= 2)
514 } else if (("get".equals(getNameConstantOperand()) || "remove".equals(getNameConstantOperand()))
515 && getSigConstantOperand().startsWith("(Ljava/lang/Object;)") && stack.getStackDepth() >= 2) {
470516 check(0);
471 } else if (className.equals("java/util/Set") || className.equals("java/util/HashSet") || className.contains("Hash")
517 }
518 } else if ("java/util/Set".equals(className) || "java/util/HashSet".equals(className) || className.contains("Hash")
472519 && Subtypes2.instanceOf(ClassName.toDottedClassName(className), "java.util.Set")) {
473 if (getNameConstantOperand().equals("add") || getNameConstantOperand().equals("contains")
474 || getNameConstantOperand().equals("remove") && getSigConstantOperand().equals("(Ljava/lang/Object;)Z")
475 && stack.getStackDepth() >= 2)
520 if ("add".equals(getNameConstantOperand()) || "contains".equals(getNameConstantOperand())
521 || "remove".equals(getNameConstantOperand()) && "(Ljava/lang/Object;)Z".equals(getSigConstantOperand())
522 && stack.getStackDepth() >= 2) {
476523 check(0);
524 }
477525 }
478526 }
479527 }
487535 } catch (ClassNotFoundException e) {
488536 AnalysisContext.reportMissingClass(e);
489537 }
490 if (type == null)
491 return;
538 if (type == null) {
539 return;
540 }
492541 String typeName = type.getClassName();
493 if (typeName.startsWith("java.lang"))
494 return;
542 if (typeName.startsWith("java.lang")) {
543 return;
544 }
495545 int priority = NORMAL_PRIORITY;
496546
497547 OpcodeStack.Item collection = stack.getStackItem(PreorderVisitor.getNumberArguments(getSigConstantOperand()));
498548 String collectionSignature = collection.getSignature();
499549 if (collectionSignature.indexOf("Tree") >= 0
500550 || collectionSignature.indexOf("Sorted") >= 0
501 || collectionSignature.indexOf("SkipList") >= 0 )
502 return;
503
504 if (collectionSignature.indexOf("Hash") >= 0)
551 || collectionSignature.indexOf("SkipList") >= 0 ) {
552 return;
553 }
554
555 if (collectionSignature.indexOf("Hash") >= 0) {
505556 priority--;
506 if (!AnalysisContext.currentAnalysisContext()/* .getSubtypes() */.isApplicationClass(type))
557 }
558 if (!AnalysisContext.currentAnalysisContext()/* .getSubtypes() */.isApplicationClass(type)) {
507559 priority++;
508
509 if (type.isAbstract() || type.isInterface())
560 }
561
562 if (type.isAbstract() || type.isInterface()) {
510563 priority++;
564 }
511565 potentialBugs.put(
512566 type.getClassName(),
513567 new BugInstance(this, "HE_USE_OF_UNHASHABLE_CLASS", priority).addClassAndMethod(this)
514 .addTypeOfNamedClass(type.getClassName()).describe(TypeAnnotation.UNHASHABLE_ROLE).addCalledMethod(this)
515 .addSourceLine(this));
516 }
517
518 static final Pattern mapPattern = Pattern.compile("[^y]HashMap<L([^;<]*);");
519
520 static final Pattern hashTablePattern = Pattern.compile("Hashtable<L([^;<]*);");
521
522 static final Pattern setPattern = Pattern.compile("[^y]HashSet<L([^;<]*);");
568 .addTypeOfNamedClass(type.getClassName()).describe(TypeAnnotation.UNHASHABLE_ROLE).addCalledMethod(this)
569 .addSourceLine(this));
570 }
523571
524572 @CheckForNull
525573 @DottedClassName
526574 String findHashedClassInSignature(String sig) {
527575 Matcher m = mapPattern.matcher(sig);
528 if (m.find())
576 if (m.find()) {
529577 return m.group(1).replace('/', '.');
578 }
530579 m = hashTablePattern.matcher(sig);
531 if (m.find())
580 if (m.find()) {
532581 return m.group(1).replace('/', '.');
582 }
533583
534584 m = setPattern.matcher(sig);
535 if (m.find())
585 if (m.find()) {
536586 return m.group(1).replace('/', '.');
587 }
537588 return null;
538589
539590 }
540591
541592 @Override
542593 public void visit(Signature obj) {
543 if (!isApplicationClass)
544 return;
594 if (!isApplicationClass) {
595 return;
596 }
545597
546598 String sig = obj.getSignature();
547599 String className = findHashedClassInSignature(sig);
548 if (className == null)
549 return;
550 if (className.startsWith("java.lang"))
551 return;
600 if (className == null) {
601 return;
602 }
603 if (className.startsWith("java.lang")) {
604 return;
605 }
552606 JavaClass type = null;
553607
554608 try {
556610 } catch (ClassNotFoundException e) {
557611 AnalysisContext.reportMissingClass(e);
558612 }
559 if (type == null)
560 return;
613 if (type == null) {
614 return;
615 }
561616
562617 int priority = NORMAL_PRIORITY;
563 if (sig.indexOf("Hash") >= 0)
618 if (sig.indexOf("Hash") >= 0) {
564619 priority--;
565 if (type.isAbstract() || type.isInterface())
620 }
621 if (type.isAbstract() || type.isInterface()) {
566622 priority++;
567 if (!AnalysisContext.currentAnalysisContext()/* .getSubtypes() */.isApplicationClass(type))
623 }
624 if (!AnalysisContext.currentAnalysisContext()/* .getSubtypes() */.isApplicationClass(type)) {
568625 priority++;
626 }
569627
570628 BugInstance bug = null;
571629
572 if (visitingField())
630 if (visitingField()) {
573631 bug = new BugInstance(this, "HE_SIGNATURE_DECLARES_HASHING_OF_UNHASHABLE_CLASS", priority).addClass(this)
574632 .addVisitedField(this).addTypeOfNamedClass(className).describe(TypeAnnotation.UNHASHABLE_ROLE);
575 else if (visitingMethod())
633 } else if (visitingMethod()) {
576634 bug = new BugInstance(this, "HE_SIGNATURE_DECLARES_HASHING_OF_UNHASHABLE_CLASS", priority).addClassAndMethod(this)
577635 .addTypeOfNamedClass(className).describe(TypeAnnotation.UNHASHABLE_ROLE);
578 else
636 } else {
579637 bug = new BugInstance(this, "HE_SIGNATURE_DECLARES_HASHING_OF_UNHASHABLE_CLASS", priority).addClass(this)
580638 .addClass(this).addTypeOfNamedClass(className).describe(TypeAnnotation.UNHASHABLE_ROLE);
639 }
581640 potentialBugs.put(className, bug);
582641 }
583642
2727 import java.util.Iterator;
2828 import java.util.List;
2929 import java.util.Map;
30 import java.util.Map.Entry;
3031 import java.util.Set;
3132
3233 import org.apache.bcel.Constants;
189190
190191 public static Collection<SourceLineAnnotation> asSourceLineAnnotation(Collection<FieldAccess> c) {
191192 ArrayList<SourceLineAnnotation> result = new ArrayList<SourceLineAnnotation>(c.size());
192 for (FieldAccess f : c)
193 for (FieldAccess f : c) {
193194 result.add(f.asSourceLineAnnotation());
195 }
194196 return result;
195197 }
196198
206208
207209 try {
208210 Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
209 if (subtypes2.isSubtype(classDescriptor, servlet) && !subtypes2.isSubtype(classDescriptor, singleThreadedServlet))
211 if (subtypes2.isSubtype(classDescriptor, servlet) && !subtypes2.isSubtype(classDescriptor, singleThreadedServlet)) {
210212 return true;
213 }
211214 } catch (ClassNotFoundException e) {
212215 assert true;
213216 }
214 if (classDescriptor.getClassName().endsWith("Servlet"))
217 if (classDescriptor.getClassName().endsWith("Servlet")) {
215218 return true;
219 }
216220 return false;
217221 }
218222
278282 }
279283
280284 public void addAccess(MethodDescriptor method, InstructionHandle handle, boolean isLocked) {
281 if (!interesting)
285 if (!interesting) {
282286 return;
283 if (!SYNC_ACCESS && isLocked)
287 }
288 if (!SYNC_ACCESS && isLocked) {
284289 return;
290 }
285291
286292 if (!servletField && !isLocked && syncAccessList.size() == 0 && unsyncAccessList.size() > 6) {
287293 interesting = false;
290296 return;
291297 }
292298 FieldAccess fa = new FieldAccess(method, handle.getPosition());
293 if (isLocked)
299 if (isLocked) {
294300 syncAccessList = Util.addTo(syncAccessList, fa);
295 else
301 } else {
296302 unsyncAccessList = Util.addTo(unsyncAccessList, fa);
303 }
297304 }
298305
299306 public Iterator<SourceLineAnnotation> unsyncAccessIterator() {
300 if (!interesting)
307 if (!interesting) {
301308 throw new IllegalStateException("Not interesting");
309 }
302310 return FieldAccess.asSourceLineAnnotation(unsyncAccessList).iterator();
303311 }
304312
305313 public Iterator<SourceLineAnnotation> syncAccessIterator() {
306 if (!interesting)
314 if (!interesting) {
307315 throw new IllegalStateException("Not interesting");
316 }
308317 return FieldAccess.asSourceLineAnnotation(syncAccessList).iterator();
309318 }
310319 }
329338 this.bugReporter = bugReporter;
330339 }
331340
341 @Override
332342 public void visitClassContext(ClassContext classContext) {
333343 JavaClass javaClass = classContext.getJavaClass();
334 if (DEBUG)
344 if (DEBUG) {
335345 System.out.println("******** Analyzing class " + javaClass.getClassName());
346 }
336347
337348 // Build self-call graph
338349 SelfCalls selfCalls = new SelfCalls(classContext) {
349360 try {
350361 selfCalls.execute();
351362 CallGraph callGraph = selfCalls.getCallGraph();
352 if (DEBUG)
363 if (DEBUG) {
353364 System.out.println("Call graph (not unlocked methods): " + callGraph.getNumVertices() + " nodes, "
354365 + callGraph.getNumEdges() + " edges");
366 }
355367 // Find call edges that are obviously locked
356368 Set<CallSite> obviouslyLockedSites = findObviouslyLockedCallSites(classContext, selfCalls);
357369 lockedMethodSet = findNotUnlockedMethods(classContext, selfCalls, obviouslyLockedSites);
368380
369381
370382 for (Method method : allMethods) {
371 if (DEBUG)
383 if (DEBUG) {
372384 System.out.println("******** considering method " + method.getName());
373
374 if (classContext.getMethodGen(method) == null)
385 }
386
387 if (classContext.getMethodGen(method) == null) {
375388 continue;
376
377 if (method.getName().startsWith("access$"))
389 }
390
391 if (method.getName().startsWith("access$")) {
378392 // Ignore inner class access methods;
379393 // we will treat calls to them as field accesses
380394 continue;
395 }
381396
382397 String name = method.getName();
383398
384 boolean inConstructor = name.equals("<init>") || name.equals("<clinit>")
385 || name.equals("readObject") || name.equals("clone") || name.equals("close")
386 || name.equals("finalize");
387
388 if (inConstructor)
399 boolean inConstructor = "<init>".equals(name) || "<clinit>".equals(name)
400 || "readObject".equals(name) || "clone".equals(name) || "close".equals(name)
401 || "finalize".equals(name);
402
403 if (inConstructor) {
389404 continue;
390
391 if (DEBUG)
405 }
406
407 if (DEBUG) {
392408 System.out.println("******** Analyzing method " + method.getName());
409 }
393410
394411 try {
395412 analyzeMethod(classContext, method, lockedMethodSet);
399416 bugReporter.logError("Error analyzing method", e);
400417 }
401418 }
402 for (Field f : javaClass.getFields())
419 for (Field f : javaClass.getFields()) {
403420 if (f.isPrivate()) {
404421 XField xf = XFactory.getExactXField(classContext.getClassDescriptor().getClassName(), f);
405422 FieldStats stats = statMap.get(xf);
406 if (stats == null)
423 if (stats == null) {
407424 continue;
408 if (!stats.isServletField() && !stats.hasAnySynchronizedAccesses())
425 }
426 if (!stats.isServletField() && !stats.hasAnySynchronizedAccesses()) {
409427 statMap.remove(xf);
410 }
411 }
412
428 }
429 }
430 }
431 }
432
433 @Override
413434 public void report() {
414435 if(statMap.isEmpty()){
415436 return;
416437 }
417438 JCIPAnnotationDatabase jcipAnotationDatabase = AnalysisContext.currentAnalysisContext().getJCIPAnnotationDatabase();
418 for (XField xfield : statMap.keySet()) {
419 FieldStats stats = statMap.get(xfield);
420 if (!stats.isInteresting())
439 for (Entry<XField, FieldStats> entry : statMap.entrySet()) {
440 XField xfield = entry.getKey();
441 FieldStats stats = entry.getValue();
442 if (!stats.isInteresting()) {
421443 continue;
444 }
422445 boolean notThreadSafe = jcipAnotationDatabase.hasClassAnnotation(xfield.getClassName(), "NotThreadSafe");
423 if (notThreadSafe)
446 if (notThreadSafe) {
424447 continue;
448 }
425449 ElementValue guardedByValue = jcipAnotationDatabase.getFieldAnnotation(xfield, "GuardedBy");
426450 boolean guardedByThis;
427451 if(guardedByValue != null){
428 guardedByThis = guardedByValue.stringifyValue().equals("this");
452 guardedByThis = "this".equals(guardedByValue.stringifyValue());
429453 } else {
430454 guardedByThis = false;
431455 }
442466 int numNullCheckLocked = stats.getNumAccesses(NULLCHECK_LOCKED);
443467
444468 int extra = 0;
445 if (numWriteUnlocked > 0)
469 if (numWriteUnlocked > 0) {
446470 extra = numNullCheckLocked;
471 }
447472 int locked = numReadLocked + numWriteLocked + numNullCheckLocked;
448473 int biasedLocked = numReadLocked + (int) (WRITE_BIAS * (numWriteLocked + numNullCheckLocked + extra));
449474 int unlocked = numReadUnlocked + numWriteUnlocked + numNullCheckUnlocked;
469494 // propertySet.addProperty(InconsistentSyncWarningProperty.NEVER_LOCKED);
470495 }
471496
472 if (stats.isServletField() && numWriteLocked == 0 && numWriteUnlocked == 0)
497 if (stats.isServletField() && numWriteLocked == 0 && numWriteUnlocked == 0) {
473498 continue;
499 }
474500
475501 if (DEBUG) {
476502 System.out.println("IS2: " + xfield);
477 if (guardedByThis)
503 if (guardedByThis) {
478504 System.out.println("Guarded by this");
505 }
479506 System.out.println(" RL: " + numReadLocked);
480507 System.out.println(" WL: " + numWriteLocked);
481508 System.out.println(" NL: " + numNullCheckLocked);
494521
495522 if (numWriteUnlocked + numWriteLocked == 0) {
496523 // No writes outside of constructor
497 if (DEBUG)
524 if (DEBUG) {
498525 System.out.println(" No writes outside of constructor");
526 }
499527 propertySet.addProperty(InconsistentSyncWarningProperty.NEVER_WRITTEN);
500528 // continue;
501529 }
502530
503531 if (numReadUnlocked + numReadLocked == 0) {
504532 // No reads outside of constructor
505 if (DEBUG)
533 if (DEBUG) {
506534 System.out.println(" No reads outside of constructor");
535 }
507536 // continue;
508537 propertySet.addProperty(InconsistentSyncWarningProperty.NEVER_READ);
509538 }
510539
511540 if (stats.getNumLocalLocks() == 0) {
512 if (DEBUG)
541 if (DEBUG) {
513542 System.out.println(" No local locks");
543 }
514544 // continue;
515545 propertySet.addProperty(InconsistentSyncWarningProperty.NO_LOCAL_LOCKS);
516546 }
527557 // continue;
528558 propertySet.addProperty(InconsistentSyncWarningProperty.BELOW_MIN_SYNC_PERCENT);
529559 }
530 if (DEBUG)
560 if (DEBUG) {
531561 System.out.println(" Sync %: " + freq);
562 }
532563
533564 if (stats.getNumGetterMethodAccesses() >= unlocked) {
534565 // Unlocked accesses are only in getter method(s).
537568
538569 // At this point, we report the field as being inconsistently
539570 // synchronized
540 if (stats.isServletField())
571 if (stats.isServletField()) {
541572 propertySet.addProperty(InconsistentSyncWarningProperty.MUTABLE_SERVLET_FIELD);
573 }
542574
543575 BugInstance bugInstance;
544 if (stats.isServletField())
576 if (stats.isServletField()) {
545577 bugInstance = new BugInstance(this, "MSF_MUTABLE_SERVLET_FIELD", Priorities.NORMAL_PRIORITY).addClass(
546578 xfield.getClassName()).addField(xfield);
547 else
579 } else {
548580 bugInstance = new BugInstance(this, guardedByThis ? "IS_FIELD_NOT_GUARDED" : "IS2_INCONSISTENT_SYNC",
549581 Priorities.NORMAL_PRIORITY).addClass(xfield.getClassName()).addField(xfield).addInt(printFreq)
550582 .describe(IntAnnotation.INT_SYNC_PERCENT);
583 }
551584
552585 propertySet.decorateBugInstance(bugInstance);
553586 // Add source lines for unsynchronized accesses
582615 */
583616
584617 private static boolean isConstructor(String methodName) {
585 return methodName.equals("<init>") || methodName.equals("<clinit>") || methodName.equals("readObject")
586 || methodName.equals("clone") || methodName.equals("close") || methodName.equals("writeObject")
587 || methodName.equals("toString") || methodName.equals("init") || methodName.equals("initialize")
588 || methodName.equals("dispose") || methodName.equals("finalize") || methodName.equals("this")
589 || methodName.equals("_jspInit") || methodName.equals("_jspDestroy");
618 return "<init>".equals(methodName) || "<clinit>".equals(methodName) || "readObject".equals(methodName)
619 || "clone".equals(methodName) || "close".equals(methodName) || "writeObject".equals(methodName)
620 || "toString".equals(methodName) || "init".equals(methodName) || "initialize".equals(methodName)
621 || "dispose".equals(methodName) || "finalize".equals(methodName) || "this".equals(methodName)
622 || "_jspInit".equals(methodName) || "_jspDestroy".equals(methodName);
590623
591624 }
592625
593626 private void analyzeMethod(ClassContext classContext, Method method, Set<Method> lockedMethodSet) throws CFGBuilderException,
594 DataflowAnalysisException {
627 DataflowAnalysisException {
595628
596629 InnerClassAccessMap icam = AnalysisContext.currentAnalysisContext().getInnerClassAccessMap();
597630 ConstantPoolGen cpg = classContext.getConstantPoolGen();
598631 MethodGen methodGen = classContext.getMethodGen(method);
599 if (methodGen == null)
632 if (methodGen == null) {
600633 return;
634 }
601635 CFG cfg = classContext.getCFG(method);
602636 LockChecker lockChecker = classContext.getLockChecker(method);
603637 ValueNumberDataflow vnaDataflow = classContext.getValueNumberDataflow(method);
604638 boolean isGetterMethod = isGetterMethod(classContext, method);
605639 MethodDescriptor methodDescriptor = DescriptorFactory.instance().getMethodDescriptor(classContext.getJavaClass(), method);
606 if (DEBUG)
640 if (DEBUG) {
607641 System.out.println("**** Analyzing method " + SignatureConverter.convertMethodSignature(methodGen));
642 }
608643
609644 for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
610645 Location location = i.next();
618653 if (ins instanceof FieldInstruction) {
619654 InstructionHandle n = location.getHandle().getNext();
620655 isNullCheck = n.getInstruction() instanceof IFNONNULL || n.getInstruction() instanceof IFNULL;
621 if (DEBUG && isNullCheck)
656 if (DEBUG && isNullCheck) {
622657 System.out.println("is null check");
658 }
623659 FieldInstruction fins = (FieldInstruction) ins;
624660 xfield = Hierarchy.findXField(fins, cpg);
625 if (xfield == null) continue;
661 if (xfield == null) {
662 continue;
663 }
626664 isWrite = ins.getOpcode() == Constants.PUTFIELD;
627665 isLocal = fins.getClassName(cpg).equals(classContext.getJavaClass().getClassName());
628 if (DEBUG)
666 if (DEBUG) {
629667 System.out.println("Handling field access: " + location.getHandle() + " (frame="
630668 + vnaDataflow.getFactAtLocation(location) + ") :" + n);
669 }
631670 } else if (ins instanceof INVOKESTATIC) {
632671 INVOKESTATIC inv = (INVOKESTATIC) ins;
633672 InnerClassAccess access = icam.getInnerClassAccess(inv, cpg);
635674 xfield = access.getField();
636675 isWrite = !access.isLoad();
637676 isLocal = false;
638 if (DEBUG)
677 if (DEBUG) {
639678 System.out.println("Handling inner class access: " + location.getHandle() + " (frame="
640679 + vnaDataflow.getFactAtLocation(location) + ")");
680 }
641681 }
642682 }
643683
644 if (xfield == null)
684 if (xfield == null) {
645685 continue;
686 }
646687
647688 // We only care about mutable nonvolatile nonpublic instance
648689 // fields.
649 if (xfield.isStatic() || xfield.isPublic() || xfield.isVolatile() || xfield.isFinal())
690 if (xfield.isStatic() || xfield.isPublic() || xfield.isVolatile() || xfield.isFinal()) {
650691 continue;
692 }
651693
652694 // The value number frame could be invalid if the basic
653695 // block became unreachable due to edge pruning (dead code).
654696 ValueNumberFrame frame = vnaDataflow.getFactAtLocation(location);
655 if (!frame.isValid())
697 if (!frame.isValid()) {
656698 continue;
699 }
657700
658701 // Get lock set and instance value
659702 ValueNumber thisValue = !method.isStatic() ? vnaDataflow.getAnalysis().getThisValue() : null;
689732 // Find the type of the object instance
690733 TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
691734 TypeFrame typeFrame = typeDataflow.getFactAtLocation(location);
692 if (!typeFrame.isValid())
735 if (!typeFrame.isValid()) {
693736 continue;
737 }
694738 Type instanceType = typeFrame.getInstance(handle.getInstruction(), cpg);
695739 if (instanceType instanceof TopType) {
696 if (DEBUG)
740 if (DEBUG) {
697741 System.out.println("Freaky: typeFrame is " + typeFrame);
742 }
698743 continue;
699744 }
700745 // Note: instance type can be Null,
722767 kind |= isWrite ? WRITE : isNullCheck ? NULLCHECK : READ;
723768
724769 // if (isLocked || !isConstructor(method.getName())) {
725 if (DEBUG)
770 if (DEBUG) {
726771 System.out.println("IS2:\t" + SignatureConverter.convertMethodSignature(methodGen) + "\t" + xfield + "\t"
727772 + ((isWrite ? "W" : "R") + "/" + (isLocked ? "L" : "U")));
728
729 if (!isLocked && methodDescriptor.getClassDescriptor().isAnonymousClass())
773 }
774
775 if (!isLocked && methodDescriptor.getClassDescriptor().isAnonymousClass()) {
730776 continue;
777 }
731778
732779 FieldStats stats = getStats(xfield);
733780
739786 stats.addAccess(kind);
740787 }
741788
742 if (isExplicitlyLocked && isLocal)
789 if (isExplicitlyLocked && isLocal) {
743790 stats.addLocalLock();
744
745 if (isGetterMethod && !isLocked)
791 }
792
793 if (isGetterMethod && !isLocked) {
746794 stats.addGetterMethodAccess();
795 }
747796
748797 stats.addAccess(methodDescriptor, handle, isLocked);
749798 // }
764813 */
765814 public static boolean isGetterMethod(ClassContext classContext, Method method) {
766815 MethodGen methodGen = classContext.getMethodGen(method);
767 if (methodGen == null)
816 if (methodGen == null) {
768817 return false;
818 }
769819 InstructionList il = methodGen.getInstructionList();
770820 // System.out.println("Checking getter method: " + method.getName());
771 if (il.getLength() > 60)
821 if (il.getLength() > 60) {
772822 return false;
823 }
773824
774825 int count = 0;
775826 Iterator<InstructionHandle> it = il.iterator();
778829 switch (ih.getInstruction().getOpcode()) {
779830 case Constants.GETFIELD:
780831 count++;
781 if (count > 1)
832 if (count > 1) {
782833 return false;
834 }
783835 break;
784836 case Constants.PUTFIELD:
785837 case Constants.BALOAD:
831883 * which is not really a valid assumption.
832884 */
833885 private static Set<Method> findNotUnlockedMethods(ClassContext classContext, SelfCalls selfCalls, Set<CallSite> obviouslyLockedSites)
834 {
886 {
835887
836888 JavaClass javaClass = classContext.getJavaClass();
837889 Method[] methodList = javaClass.getMethods();
862914 CallSite callSite = edge.getCallSite();
863915
864916 // Ignore obviously locked edges
865 if (obviouslyLockedSites.contains(callSite))
917 if (obviouslyLockedSites.contains(callSite)) {
866918 continue;
919 }
867920
868921 // If the calling method is locked, ignore the edge
869 if (lockedMethodSet.contains(callSite.getMethod()))
922 if (lockedMethodSet.contains(callSite.getMethod())) {
870923 continue;
924 }
871925
872926 // Calling method is unlocked, so the called method
873927 // is also unlocked.
874928 CallGraphNode target = edge.getTarget();
875 if (lockedMethodSet.remove(target.getMethod()))
929 if (lockedMethodSet.remove(target.getMethod())) {
876930 change = true;
931 }
877932 }
878933 } while (change);
879934
895950 * which is not really a valid assumption.
896951 */
897952 private static Set<Method> findLockedMethods(ClassContext classContext, SelfCalls selfCalls, Set<CallSite> obviouslyLockedSites)
898 {
953 {
899954
900955 JavaClass javaClass = classContext.getJavaClass();
901956 Method[] methodList = javaClass.getMethods();
926981 // Calling method is locked, so the called method
927982 // is also locked.
928983 CallGraphNode target = edge.getTarget();
929 if (lockedMethodSet.add(target.getMethod()))
984 if (lockedMethodSet.add(target.getMethod())) {
930985 change = true;
986 }
931987 }
932988 }
933989 } while (change);
10051061 // Only instance method calls qualify as candidates for
10061062 // "obviously locked"
10071063 Instruction ins = handle.getInstruction();
1008 if (ins.getOpcode() == Constants.INVOKESTATIC)
1064 if (ins.getOpcode() == Constants.INVOKESTATIC) {
10091065 continue;
1066 }
10101067
10111068 // Get lock set for site
10121069 LockChecker lockChecker = classContext.getLockChecker(method);
10191076 // NOTE: if the CFG on which the value number analysis was performed
10201077 // was pruned, there may be unreachable instructions. Therefore,
10211078 // we can't assume the frame is valid.
1022 if (!frame.isValid())
1079 if (!frame.isValid()) {
10231080 continue;
1081 }
10241082
10251083 // Find the ValueNumber of the receiver object
10261084 int numConsumed = ins.consumeStack(cpg);
10271085 MethodGen methodGen = classContext.getMethodGen(method);
10281086 assert methodGen != null;
1029 if (numConsumed == Constants.UNPREDICTABLE)
1087 if (numConsumed == Constants.UNPREDICTABLE) {
10301088 throw new DataflowAnalysisException("Unpredictable stack consumption", methodGen, handle);
1089 }
10311090 // if (DEBUG) System.out.println("Getting receiver for frame: " +
10321091 // frame);
10331092 ValueNumber instance = frame.getStackValue(numConsumed - 1);
10441103 }
10451104 }
10461105
1047 // vim:ts=3
5757 /**
5858 * Find places where ordinary (balanced) synchronization is performed on JSR166
5959 * Lock objects. Suggested by Doug Lea.
60 *
60 *
6161 * @author David Hovemeyer
6262 */
6363 public final class FindJSR166LockMonitorenter implements Detector, StatelessDetector {
6666 */
6767 private static final String UTIL_CONCURRRENT_SIG_PREFIX = "Ljava/util/concurrent/";
6868
69 private BugReporter bugReporter;
69 private final BugReporter bugReporter;
7070
7171 private static final ObjectType LOCK_TYPE = ObjectTypeFactory.getInstance("java.util.concurrent.locks.Lock");
7272
8383 }
8484 }
8585
86 @Override
8687 public void visitClassContext(ClassContext classContext) {
8788 JavaClass jclass = classContext.getJavaClass();
88 if (jclass.getClassName().startsWith("java.util.concurrent."))
89 if (jclass.getClassName().startsWith("java.util.concurrent.")) {
8990 return;
91 }
9092 Method[] methodList = jclass.getMethods();
9193
9294 for (Method method : methodList) {
93 if (method.getCode() == null)
94 continue;
95 if (method.getCode() == null) {
96 continue;
97 }
9598
9699 // We can ignore methods that don't contain a monitorenter
97100 BitSet bytecodeSet = classContext.getBytecodeSet(method);
98 if (bytecodeSet == null)
99 continue;
100 if (false && !bytecodeSet.get(Constants.MONITORENTER))
101 continue;
101 if (bytecodeSet == null) {
102 continue;
103 }
104 if (false && !bytecodeSet.get(Constants.MONITORENTER)) {
105 continue;
106 }
102107
103108 analyzeMethod(classContext, method);
104109
133138
134139 String methodName = iv.getMethodName(cpg);
135140 String methodSig = iv.getSignature(cpg);
136 if (methodName.equals("wait")
137 && (methodSig.equals("()V") || methodSig.equals("(J)V") || methodSig.equals("(JI)V"))
138 || (methodName.equals("notify") || methodName.equals("notifyAll")) && methodSig.equals("()V")) {
141 if ("wait".equals(methodName)
142 && ("()V".equals(methodSig) || "(J)V".equals(methodSig) || "(JI)V".equals(methodSig))
143 || ("notify".equals(methodName) || "notifyAll".equals(methodName)) && "()V".equals(methodSig)) {
139144 try {
140145 TypeFrame frame = typeDataflow.getFactAtLocation(location);
141 if (!frame.isValid())
142 continue;
146 if (!frame.isValid()) {
147 continue;
148 }
143149 Type type = frame.getInstance(ins, cpg);
144150 if (!(type instanceof ReferenceType)) {
145151 // Something is deeply wrong if a non-reference type
150156 }
151157 ClassDescriptor classDescriptor = DescriptorFactory.createClassDescriptorFromSignature(type
152158 .getSignature());
153 if (classDescriptor.equals(classContext.getClassDescriptor()))
154 continue;
155 if (!classDescriptor.getClassName().startsWith("java/util/concurrent"))
156 continue;
159 if (classDescriptor.equals(classContext.getClassDescriptor())) {
160 continue;
161 }
162 if (!classDescriptor.getClassName().startsWith("java/util/concurrent")) {
163 continue;
164 }
157165 XClass c = Lookup.getXClass(classDescriptor);
158166 XMethod m;
159167 int priority = NORMAL_PRIORITY;
160 if (methodName.equals("wait")) {
168 if ("wait".equals(methodName)) {
161169 m = c.findMethod("await", "()V", false);
162170 priority = HIGH_PRIORITY;
163 } else if (methodName.equals("notify")) {
171 } else if ("notify".equals(methodName)) {
164172 m = c.findMethod("signal", "()V", false);
165 if (m == null)
173 if (m == null) {
166174 m = c.findMethod("countDown", "()V", false);
167 } else if (methodName.equals("notifyAll")) {
175 }
176 } else if ("notifyAll".equals(methodName)) {
168177 m = c.findMethod("signalAll", "()V", false);
169 if (m == null)
178 if (m == null) {
170179 m = c.findMethod("countDown", "()V", false);
171 } else
180 }
181 } else {
172182 throw new IllegalStateException("Unexpected methodName: " + methodName);
173
174 if (m != null && m.isPublic() && c.isPublic())
175
183 }
184
185 if (m != null && m.isPublic() && c.isPublic()) {
176186 bugReporter.reportBug(new BugInstance(this, "JML_JSR166_CALLING_WAIT_RATHER_THAN_AWAIT", priority)
177 .addClassAndMethod(classContext.getJavaClass(), method).addCalledMethod(cpg, iv).addMethod(m)
178 .describe(MethodAnnotation.METHOD_ALTERNATIVE_TARGET).addType(classDescriptor)
179 .describe(TypeAnnotation.FOUND_ROLE).addSourceLine(classContext, method, location));
187 .addClassAndMethod(classContext.getJavaClass(), method).addCalledMethod(cpg, iv).addMethod(m)
188 .describe(MethodAnnotation.METHOD_ALTERNATIVE_TARGET).addType(classDescriptor)
189 .describe(TypeAnnotation.FOUND_ROLE).addSourceLine(classContext, method, location));
190 }
180191
181192 } catch (CheckedAnalysisException e) {
182193 AnalysisContext.logError("Coult not get Type dataflow", e);
187198
188199 }
189200
190 if (ins.getOpcode() != Constants.MONITORENTER)
191 continue;
201 if (ins.getOpcode() != Constants.MONITORENTER) {
202 continue;
203 }
192204 Type type;
193205 try {
194206 TypeFrame frame = typeDataflow.getFactAtLocation(location);
195 if (!frame.isValid())
207 if (!frame.isValid()) {
196208 continue;
209 }
197210 type = frame.getInstance(ins, cpg);
198211 } catch (CheckedAnalysisException e) {
199212 AnalysisContext.logError("Coult not get Type dataflow", e);
224237
225238 int priority = "Ljava/util/concurrent/CopyOnWriteArrayList;".equals(sig) ? HIGH_PRIORITY : NORMAL_PRIORITY;
226239 bugReporter.reportBug(new BugInstance(this, "JLM_JSR166_UTILCONCURRENT_MONITORENTER", priority)
227 .addClassAndMethod(classContext.getJavaClass(), method).addType(sig)
228 .addSourceForTopStackValue(classContext, method, location).addSourceLine(classContext, method, location));
229
230 }
231 }
232 }
233
240 .addClassAndMethod(classContext.getJavaClass(), method).addType(sig)
241 .addSourceForTopStackValue(classContext, method, location).addSourceLine(classContext, method, location));
242
243 }
244 }
245 }
246
247 @Override
234248 public void report() {
235249 }
236250 }
237251
238 // vim:ts=4
3333
3434 public class FindLocalSelfAssignment2 extends BytecodeScanningDetector implements StatelessDetector {
3535
36 private BugReporter bugReporter;
36 private final BugReporter bugReporter;
3737
3838 private int previousLoadOf = -1;
3939
4545 this.bugReporter = bugReporter;
4646 }
4747
48 private BitSet previousStores = new BitSet();
48 private final BitSet previousStores = new BitSet();
4949
5050 @Override
5151 public void visit(Code obj) {
6161 if (seen == GOTO) {
6262 previousGotoTarget = getBranchTarget();
6363 gotoCount++;
64 if (previousGotoTarget < getPC())
64 if (previousGotoTarget < getPC()) {
6565 previousLoadOf = -1;
66 }
6667 } else {
67 if (isRegisterLoad())
68 if (isRegisterLoad()) {
6869 previousLoadOf = getRegisterOperand();
69 else {
70 } else {
7071 if (isRegisterStore()) {
7172 if (previousLoadOf == getRegisterOperand() && gotoCount < 2 && getPC() != previousGotoTarget) {
7273 int priority = NORMAL_PRIORITY;
7374 String methodName = getMethodName();
74 if (methodName.equals("<init>") || methodName.startsWith("set") && getCode().getCode().length <= 5
75 || !previousStores.get(getRegisterOperand()))
75 if ("<init>".equals(methodName) || methodName.startsWith("set") && getCode().getCode().length <= 5
76 || !previousStores.get(getRegisterOperand())) {
7677 priority = HIGH_PRIORITY;
78 }
7779 previousStores.set(getRegisterOperand());
7880 XClass c = getXClass();
7981 LocalVariableAnnotation local = LocalVariableAnnotation.getLocalVariableAnnotation(getMethod(),
8082 getRegisterOperand(), getPC(), getPC());
81 if (local.getName().equals("?")) {
83 if ("?".equals(local.getName())) {
8284 priority++;
83 } else
85 } else {
8486 for (XField f : c.getXFields()) {
8587 if (f.getName().equals(local.getName()) && (f.isStatic() || !getMethod().isStatic())) {
8688 bugReporter.reportBug(new BugInstance(this, "SA_LOCAL_SELF_ASSIGNMENT_INSTEAD_OF_FIELD",
9092
9193 }
9294 }
95 }
9396
9497 bugReporter.reportBug(new BugInstance(this, "SA_LOCAL_SELF_ASSIGNMENT", priority).addClassAndMethod(this)
9598 .add(local).addSourceLine(this));
96 } else
99 } else {
97100 previousStores.set(getRegisterOperand());
101 }
98102 }
99103
100104 previousLoadOf = -1;
4545 import edu.umd.cs.findbugs.classfile.Global;
4646
4747 public class FindMaskedFields extends BytecodeScanningDetector {
48 private BugReporter bugReporter;
48 private final BugReporter bugReporter;
4949
5050 private int numParms;
5151
52 private Map<String, Field> classFields = new HashMap<String, Field>();
52 private final Map<String, Field> classFields = new HashMap<String, Field>();
5353
5454 private boolean staticMethod;
5555
56 private Collection<RememberedBug> rememberedBugs = new LinkedList<RememberedBug>();
56 private final Collection<RememberedBug> rememberedBugs = new LinkedList<RememberedBug>();
5757
5858 static class RememberedBug {
5959 BugInstance bug;
7474 @Override
7575 public void visitClassContext(ClassContext classContext) {
7676 JavaClass obj = classContext.getJavaClass();
77 if (!obj.isInterface())
77 if (!obj.isInterface()) {
7878 classContext.getJavaClass().accept(this);
79 }
7980 }
8081
8182 @Override
8485
8586 Field[] fields = obj.getFields();
8687 String fieldName;
87 for (Field field : fields)
88 for (Field field : fields) {
8889 if (!field.isStatic() && !field.isPrivate()) {
8990 fieldName = field.getName();
9091 classFields.put(fieldName, field);
9192 }
93 }
9294
9395 // Walk up the super class chain, looking for name collisions
9496
9597 XClass c = getXClass();
9698 while (true) {
9799 ClassDescriptor s = c.getSuperclassDescriptor();
98 if (s == null || s.getClassName().equals("java/lang/Object"))
100 if (s == null || "java/lang/Object".equals(s.getClassName())) {
99101 break;
102 }
100103 try {
101104 c = Global.getAnalysisCache().getClassAnalysis(XClass.class, s);
102105 } catch (CheckedAnalysisException e) {
106109 for (XField fld : c.getXFields()) {
107110 if (!fld.isStatic() && (fld.isPublic() || fld.isProtected())) {
108111 fieldName = fld.getName();
109 if (fieldName.length() == 1)
112 if (fieldName.length() == 1) {
110113 continue;
111 if (fieldName.equals("serialVersionUID"))
114 }
115 if ("serialVersionUID".equals(fieldName)) {
112116 continue;
117 }
113118 String superClassName = s.getClassName();
114119 if (superClassName.startsWith("java/io")
115 && (superClassName.endsWith("InputStream") && fieldName.equals("in") || superClassName
116 .endsWith("OutputStream") && fieldName.equals("out")))
120 && (superClassName.endsWith("InputStream") && "in".equals(fieldName) || superClassName
121 .endsWith("OutputStream") && "out".equals(fieldName))) {
117122 continue;
123 }
118124 if (classFields.containsKey(fieldName)) {
119125 Field maskingField = classFields.get(fieldName);
120126 String mClassName = getDottedClassName();
121127 FieldAnnotation fa = new FieldAnnotation(mClassName, maskingField.getName(), maskingField.getSignature(),
122128 maskingField.isStatic());
123129 int priority = NORMAL_PRIORITY;
124 if (maskingField.isStatic() || maskingField.isFinal())
130 if (maskingField.isStatic() || maskingField.isFinal()) {
125131 priority++;
126 else if (fld.getSignature().charAt(0) == 'L' && !fld.getSignature().startsWith("Ljava/lang/")
127 || fld.getSignature().charAt(0) == '[')
132 } else if (fld.getSignature().charAt(0) == 'L' && !fld.getSignature().startsWith("Ljava/lang/")
133 || fld.getSignature().charAt(0) == '[') {
128134 priority--;
129 if (!fld.getSignature().equals(maskingField.getSignature()))
135 }
136 if (!fld.getSignature().equals(maskingField.getSignature())) {
130137 priority += 2;
131 else if (fld.getAccessFlags() != maskingField.getAccessFlags())
138 } else if (fld.getAccessFlags() != maskingField.getAccessFlags()) {
132139 priority++;
133 if (fld.isSynthetic() || fld.getName().indexOf('$') >= 0)
140 }
141 if (fld.isSynthetic() || fld.getName().indexOf('$') >= 0) {
134142 priority++;
143 }
135144
136145 FieldAnnotation maskedFieldAnnotation = FieldAnnotation.fromFieldDescriptor(fld.getFieldDescriptor());
137146 BugInstance bug = new BugInstance(this, "MF_CLASS_MASKS_FIELD", priority).addClass(this).addField(fa)
150159 public void visit(Method obj) {
151160 super.visit(obj);
152161 numParms = getNumberMethodArguments();
153 if (!obj.isStatic())
162 if (!obj.isStatic()) {
154163 numParms++;
164 }
155165 // System.out.println(obj);
156166 // System.out.println(numParms);
157167 staticMethod = obj.isStatic();
166176 @Override
167177 public void visit(LocalVariableTable obj) {
168178 if (ENABLE_LOCALS) {
169 if (staticMethod)
179 if (staticMethod) {
170180 return;
181 }
171182
172183 LocalVariable[] vars = obj.getLocalVariableTable();
173184 // System.out.println("Num params = " + numParms);
174185 for (LocalVariable var : vars) {
175 if (var.getIndex() < numParms)
186 if (var.getIndex() < numParms) {
176187 continue;
188 }
177189 String varName = var.getName();
178 if (varName.equals("serialVersionUID"))
190 if ("serialVersionUID".equals(varName)) {
179191 continue;
192 }
180193 Field f = classFields.get(varName);
181194 // System.out.println("Checking " + varName);
182195 // System.out.println(" got " + f);
186199 // that is.
187200 if (f != null) {
188201 FieldAnnotation fa = FieldAnnotation.fromBCELField(getDottedClassName(), f);
189 if (true || var.getStartPC() > 0)
202 if (true || var.getStartPC() > 0) {
190203 bugReporter.reportBug(new BugInstance(this, "MF_METHOD_MASKS_FIELD", LOW_PRIORITY)
191 .addClassAndMethod(this).addField(fa).addSourceLine(this, var.getStartPC() - 1));
204 .addClassAndMethod(this).addField(fa).addSourceLine(this, var.getStartPC() - 1));
205 }
192206 }
193207 }
194208 }
204218 int score2 = 0;
205219 int priority = bug.getPriority();
206220 if (unreadFields.classesScanned.contains(rb.maskedField.getClassName())) {
207 if (unreadFields.getReadFields().contains(rb.maskedField))
221 if (unreadFields.getReadFields().contains(rb.maskedField)) {
208222 score1++;
209 if (unreadFields.getWrittenFields().contains(rb.maskedField))
223 }
224 if (unreadFields.getWrittenFields().contains(rb.maskedField)) {
210225 score1++;
211 if (unreadFields.isWrittenOutsideOfInitialization(rb.maskedField))
226 }
227 if (unreadFields.isWrittenOutsideOfInitialization(rb.maskedField)) {
212228 score1++;
213 } else
229 }
230 } else {
214231 score1 += 2;
215 if (unreadFields.getReadFields().contains(rb.maskingField))
232 }
233 if (unreadFields.getReadFields().contains(rb.maskingField)) {
216234 score2++;
217 if (unreadFields.getWrittenFields().contains(rb.maskingField))
235 }
236 if (unreadFields.getWrittenFields().contains(rb.maskingField)) {
218237 score2++;
219 if (unreadFields.isWrittenOutsideOfInitialization(rb.maskingField))
238 }
239 if (unreadFields.isWrittenOutsideOfInitialization(rb.maskingField)) {
220240 score2++;
241 }
221242 int score = score1 + score2;
222 if (score1 == 0 || score2 == 0)
243 if (score1 == 0 || score2 == 0) {
223244 bug.setPriority(priority + 1);
224 else if (score >= 5)
245 } else if (score >= 5) {
225246 bug.setPriority(priority - 1);
226 else if (score < 3)
247 } else if (score < 3) {
227248 bug.setPriority(priority + 1);
249 }
228250 bugReporter.reportBug(bug);
229251 }
230252 }
231253 }
232254
233 // vim:ts=4
6868 }
6969 }
7070
71 @Override
7172 public void visitClassContext(ClassContext classContext) {
7273 JavaClass jclass = classContext.getJavaClass();
7374
7475 Method[] methodList = jclass.getMethods();
7576 for (Method method : methodList) {
7677 MethodGen methodGen = classContext.getMethodGen(method);
77 if (methodGen == null)
78 if (methodGen == null) {
7879 continue;
80 }
7981
8082 // Don't bother analyzing the method unless there is both locking
8183 // and a method call.
8284 BitSet bytecodeSet = classContext.getBytecodeSet(method);
83 if (bytecodeSet == null)
85 if (bytecodeSet == null) {
8486 continue;
85 if (!(bytecodeSet.get(Constants.MONITORENTER) && bytecodeSet.get(Constants.INVOKEVIRTUAL)))
87 }
88 if (!(bytecodeSet.get(Constants.MONITORENTER) && bytecodeSet.get(Constants.INVOKEVIRTUAL))) {
8689 continue;
90 }
8791
8892 try {
8993 analyzeMethod(classContext, method);
98102 private void analyzeMethod(ClassContext classContext, Method method) throws CFGBuilderException, DataflowAnalysisException {
99103
100104 MethodGen methodGen = classContext.getMethodGen(method);
101 if (methodGen == null)
105 if (methodGen == null) {
102106 return;
107 }
103108 ConstantPoolGen cpg = methodGen.getConstantPool();
104109 CFG cfg = classContext.getCFG(method);
105110 ValueNumberDataflow vnaDataflow = classContext.getValueNumberDataflow(method);
111116 InstructionHandle handle = location.getHandle();
112117
113118 Instruction ins = handle.getInstruction();
114 if (!(ins instanceof INVOKEVIRTUAL))
119 if (!(ins instanceof INVOKEVIRTUAL)) {
115120 continue;
121 }
116122 INVOKEVIRTUAL inv = (INVOKEVIRTUAL) ins;
117123
118124 String methodName = inv.getName(cpg);
120126
121127 if (Hierarchy.isMonitorWait(methodName, methodSig) || Hierarchy.isMonitorNotify(methodName, methodSig)) {
122128 int numConsumed = inv.consumeStack(cpg);
123 if (numConsumed == Constants.UNPREDICTABLE)
129 if (numConsumed == Constants.UNPREDICTABLE) {
124130 throw new DataflowAnalysisException("Unpredictable stack consumption", methodGen, handle);
131 }
125132
126133 ValueNumberFrame frame = vnaDataflow.getFactAtLocation(location);
127 if (!frame.isValid())
134 if (!frame.isValid()) {
128135 // Probably dead code
129136 continue;
130 if (frame.getStackDepth() - numConsumed < 0)
137 }
138 if (frame.getStackDepth() - numConsumed < 0) {
131139 throw new DataflowAnalysisException("Stack underflow", methodGen, handle);
140 }
132141 ValueNumber ref = frame.getValue(frame.getNumSlots() - numConsumed);
133142 LockSet lockSet = dataflow.getFactAtLocation(location);
134143 int lockCount = lockSet.getLockCount(ref.getNumber());
136145 if (lockCount == 0) {
137146 Collection<ValueNumber> lockedValueNumbers = lockSet.getLockedValueNumbers(frame);
138147 boolean foundMatch = false;
139 for (ValueNumber v : lockedValueNumbers)
148 for (ValueNumber v : lockedValueNumbers) {
140149 if (frame.veryFuzzyMatch(ref, v)) {
141150 foundMatch = true;
142151 break;
143152 }
153 }
144154
145155 if (!foundMatch) {
146156
147 String type = methodName.equals("wait") ? "MWN_MISMATCHED_WAIT" : "MWN_MISMATCHED_NOTIFY";
157 String type = "wait".equals(methodName) ? "MWN_MISMATCHED_WAIT" : "MWN_MISMATCHED_NOTIFY";
148158 String sourceFile = classContext.getJavaClass().getSourceFileName();
149159 // Report as medium priority only if the method is
150160 // public.
162172 bugAccumulator.reportAccumulatedBugs();
163173 }
164174
175 @Override
165176 public void report() {
166177 }
167178 }
168179
169 // vim:ts=3
3636 public class FindNakedNotify extends BytecodeScanningDetector implements StatelessDetector {
3737 int stage = 0;
3838
39 private BugReporter bugReporter;
39 private final BugReporter bugReporter;
4040
4141 boolean synchronizedMethod;
4242
5656 public void visit(Code obj) {
5757 stage = synchronizedMethod ? 1 : 0;
5858 super.visit(obj);
59 if (synchronizedMethod && stage == 4)
59 if (synchronizedMethod && stage == 4) {
6060 bugReporter.reportBug(new BugInstance(this, "NN_NAKED_NOTIFY", NORMAL_PRIORITY).addClassAndMethod(this)
6161 .addSourceLine(this, notifyPC));
62 }
6263 }
6364
6465 @Override
6566 public void sawOpcode(int seen) {
6667 switch (stage) {
6768 case 0:
68 if (seen == MONITORENTER)
69 if (seen == MONITORENTER) {
6970 stage = 1;
71 }
7072 break;
7173 case 1:
7274 stage = 2;
7375 break;
7476 case 2:
7577 if (seen == INVOKEVIRTUAL
76 && (getNameConstantOperand().equals("notify") || getNameConstantOperand().equals("notifyAll"))
77 && getSigConstantOperand().equals("()V")) {
78 && ("notify".equals(getNameConstantOperand()) || "notifyAll".equals(getNameConstantOperand()))
79 && "()V".equals(getSigConstantOperand())) {
7880 stage = 3;
7981 notifyPC = getPC();
80 } else
82 } else {
8183 stage = 0;
84 }
8285 break;
8386 case 3:
8487 stage = 4;
8891 bugReporter.reportBug(new BugInstance(this, "NN_NAKED_NOTIFY", NORMAL_PRIORITY).addClassAndMethod(this)
8992 .addSourceLine(this, notifyPC));
9093 stage = 5;
91 } else
94 } else {
9295 stage = 0;
96 }
9397 break;
9498 case 5:
9599 break;
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2008 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.detect;
20
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.HashMap;
24 import java.util.HashSet;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Map.Entry;
29 import java.util.Set;
30
31 import javax.annotation.Nonnull;
32
33 import org.apache.bcel.classfile.Code;
34 import org.apache.bcel.classfile.CodeException;
35 import org.apache.bcel.classfile.Field;
36 import org.apache.bcel.classfile.JavaClass;
37 import org.apache.bcel.classfile.Method;
38 import org.apache.bcel.generic.Type;
39
40 import edu.umd.cs.findbugs.BugReporter;
41 import edu.umd.cs.findbugs.NonReportingDetector;
42 import edu.umd.cs.findbugs.OpcodeStack;
43 import edu.umd.cs.findbugs.OpcodeStack.Item;
44 import edu.umd.cs.findbugs.ba.AnalysisContext;
45 import edu.umd.cs.findbugs.ba.Hierarchy2;
46 import edu.umd.cs.findbugs.ba.SignatureParser;
47 import edu.umd.cs.findbugs.ba.XClass;
48 import edu.umd.cs.findbugs.ba.XField;
49 import edu.umd.cs.findbugs.ba.XMethod;
50 import edu.umd.cs.findbugs.ba.ch.Subtypes2;
51 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
52 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
53 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
54 import edu.umd.cs.findbugs.classfile.FieldDescriptor;
55 import edu.umd.cs.findbugs.classfile.Global;
56 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
57 import edu.umd.cs.findbugs.util.ClassName;
58
59 /**
60 * @author Tagir Valeev
61 */
62 public class FindNoSideEffectMethods extends OpcodeStackDetector implements NonReportingDetector {
63 private static final MethodDescriptor GET_CLASS = new MethodDescriptor("java/lang/Object", "getClass", "()Ljava/lang/Class;");
64 private static final MethodDescriptor ARRAY_COPY = new MethodDescriptor("java/lang/System", "arraycopy", "(Ljava/lang/Object;ILjava/lang/Object;II)V", true);
65 private static final MethodDescriptor HASH_CODE = new MethodDescriptor("java/lang/Object", "hashCode", "()I");
66 private static final MethodDescriptor CLASS_GET_NAME = new MethodDescriptor("java/lang/Class", "getName", "()Ljava/lang/String;");
67 // Stub method to generalize array store
68 private static final MethodDescriptor ARRAY_STORE_STUB_METHOD = new MethodDescriptor("java/lang/Array", "set", "(ILjava/lang/Object;)V");
69 // Stub method to generalize field store
70 private static final MethodDescriptor FIELD_STORE_STUB_METHOD = new MethodDescriptor("java/lang/Object", "putField", "(Ljava/lang/Object;)V");
71
72 // Fictional method call targets
73 private static final FieldDescriptor TARGET_THIS = new FieldDescriptor("java/lang/Stub", "this", "V", false);
74 private static final FieldDescriptor TARGET_NEW = new FieldDescriptor("java/lang/Stub", "new", "V", false);
75 private static final FieldDescriptor TARGET_OTHER = new FieldDescriptor("java/lang/Stub", "other", "V", false);
76
77 private static final Set<String> NUMBER_CLASSES = new HashSet<>(Arrays.asList("java/lang/Integer", "java/lang/Long",
78 "java/lang/Double", "java/lang/Float", "java/lang/Byte", "java/lang/Short", "java/math/BigInteger",
79 "java/math/BigDecimal"));
80
81 private static final Set<String> ALLOWED_EXCEPTIONS = new HashSet<>(Arrays.asList("java.lang.InternalError",
82 "java.lang.ArrayIndexOutOfBoundsException", "java.lang.StringIndexOutOfBoundsException",
83 "java.lang.IndexOutOfBoundsException"));
84
85 private static final Set<String> NO_SIDE_EFFECT_COLLECTION_METHODS = new HashSet<>(Arrays.asList("contains", "containsKey",
86 "containsValue", "get", "indexOf", "lastIndexOf", "iterator", "listIterator", "isEmpty", "size", "getOrDefault",
87 "subList", "keys", "elements", "keySet", "entrySet", "values", "stream", "firstKey", "lastKey", "headMap", "tailMap",
88 "subMap", "peek", "mappingCount"));
89
90 private static final Set<String> OBJECT_ONLY_CLASSES = new HashSet<>(Arrays.asList("java/lang/StringBuffer",
91 "java/lang/StringBuilder", "java/util/regex/Matcher", "java/io/ByteArrayOutputStream",
92 "java/util/concurrent/atomic/AtomicBoolean", "java/util/concurrent/atomic/AtomicInteger",
93 "java/util/concurrent/atomic/AtomicLong", "java/awt/Point"));
94
95 // Usual implementation of stub methods which are expected to be more complex in derived classes
96 private static final byte[][] STUB_METHODS = new byte[][] {
97 {(byte) RETURN},
98 {ICONST_0, (byte) IRETURN},
99 {ICONST_1, (byte) IRETURN},
100 {ICONST_M1, (byte) IRETURN},
101 {LCONST_0, (byte) LRETURN},
102 {FCONST_0, (byte) FRETURN},
103 {DCONST_0, (byte) DRETURN},
104 {ACONST_NULL, (byte) ARETURN},
105 {ALOAD_0, (byte) ARETURN},
106 {ALOAD_1, (byte) ARETURN},
107 };
108
109 /**
110 * Known methods which change only this object
111 */
112 private static final Set<MethodDescriptor> OBJECT_ONLY_METHODS = new HashSet<>(Arrays.asList(
113 ARRAY_STORE_STUB_METHOD, FIELD_STORE_STUB_METHOD,
114 new MethodDescriptor("java/util/Iterator", "next", "()Ljava/lang/Object;"),
115 new MethodDescriptor("java/util/Enumeration", "nextElement", "()Ljava/lang/Object;"),
116 new MethodDescriptor("java/lang/Throwable", "fillInStackTrace", "()Ljava/lang/Throwable;")
117 ));
118
119 /**
120 * Known methods which have no side-effect
121 */
122 private static final Set<MethodDescriptor> NO_SIDE_EFFECT_METHODS = new HashSet<>(Arrays.asList(
123 GET_CLASS, CLASS_GET_NAME, HASH_CODE,
124 new MethodDescriptor("java/lang/reflect/Array", "newInstance", "(Ljava/lang/Class;I)Ljava/lang/Object;"),
125 new MethodDescriptor("java/lang/Class", "getResource", "(Ljava/lang/String;)Ljava/net/URL;"),
126 new MethodDescriptor("java/lang/Class", "getSimpleName", "()Ljava/lang/String;"),
127 new MethodDescriptor("java/lang/Class", "getMethods", "()[Ljava/lang/reflect/Method;"),
128 new MethodDescriptor("java/lang/Class", "getSuperclass", "()Ljava/lang/Class;"),
129 new MethodDescriptor("java/lang/Runtime", "availableProcessors", "()I"),
130 new MethodDescriptor("java/lang/Runtime", "maxMemory", "()J"),
131 new MethodDescriptor("java/lang/Runtime", "totalMemory", "()J"),
132 new MethodDescriptor("java/lang/Iterable", "iterator", "()Ljava/util/Iterator;"),
133 new MethodDescriptor("java/lang/Comparable", "compareTo", "(Ljava/lang/Object;)I"),
134 new MethodDescriptor("java/util/Arrays", "deepEquals", "([Ljava/lang/Object;[Ljava/lang/Object;)Z", true),
135 new MethodDescriptor("java/util/Enumeration", "hasMoreElements", "()Z"),
136 new MethodDescriptor("java/util/Iterator", "hasNext", "()Z"),
137 new MethodDescriptor("java/util/Comparator", "compare", "(Ljava/lang/Object;Ljava/lang/Object;)I"),
138 new MethodDescriptor("java/util/logging/LogManager", "getLogger", "(Ljava/lang/String;)Ljava/util/logging/Logger;", true),
139 new MethodDescriptor("org/apache/log4j/LogManager", "getLogger", "(Ljava/lang/String;)Lorg/apache/log4j/Logger;", true)
140 ));
141
142 private static final Set<MethodDescriptor> NEW_OBJECT_RETURNING_METHODS = new HashSet<>(Arrays.asList(
143 new MethodDescriptor("java/util/Vector", "elements", "()Ljava/util/Enumeration;"),
144 new MethodDescriptor("java/util/Hashtable", "elements", "()Ljava/util/Enumeration;"),
145 new MethodDescriptor("java/util/Hashtable", "keys", "()Ljava/util/Enumeration;"),
146 new MethodDescriptor("java/lang/reflect/Array", "newInstance", "(Ljava/lang/Class;I)Ljava/lang/Object;")
147 ));
148
149 private static enum SideEffectStatus {
150 SIDE_EFFECT, UNSURE_OBJECT_ONLY, OBJECT_ONLY, UNSURE, NO_SIDE_EFFECT;
151
152 boolean unsure() {
153 return this == UNSURE || this == UNSURE_OBJECT_ONLY;
154 }
155
156 SideEffectStatus toObjectOnly() {
157 switch(this) {
158 case UNSURE:
159 return UNSURE_OBJECT_ONLY;
160 case NO_SIDE_EFFECT:
161 return OBJECT_ONLY;
162 default:
163 return this;
164 }
165 }
166
167 SideEffectStatus toUnsure() {
168 switch(this) {
169 case OBJECT_ONLY:
170 return UNSURE_OBJECT_ONLY;
171 case NO_SIDE_EFFECT:
172 return UNSURE;
173 default:
174 return this;
175 }
176 }
177
178 SideEffectStatus toSure() {
179 switch(this) {
180 case UNSURE_OBJECT_ONLY:
181 return OBJECT_ONLY;
182 case UNSURE:
183 return NO_SIDE_EFFECT;
184 default:
185 return this;
186 }
187 }
188 }
189
190 private static class MethodCall {
191 private final MethodDescriptor method;
192 private final FieldDescriptor target;
193
194 public MethodCall(MethodDescriptor method, FieldDescriptor target) {
195 this.method = method;
196 this.target = target;
197 }
198
199 public MethodDescriptor getMethod() {
200 return method;
201 }
202
203 public FieldDescriptor getTarget() {
204 return target;
205 }
206
207 @Override
208 public int hashCode() {
209 throw new UnsupportedOperationException();
210 }
211
212 @Override
213 public boolean equals(Object obj) {
214 if (this == obj) {
215 return true;
216 }
217 if (obj == null) {
218 return false;
219 }
220 if (getClass() != obj.getClass()) {
221 return false;
222 }
223 MethodCall other = (MethodCall) obj;
224 if (!method.equals(other.method)) {
225 return false;
226 }
227 if (!target.equals(other.target)) {
228 return false;
229 }
230 return true;
231 }
232 }
233
234 /**
235 * Public status of the method in NSE database
236 * TODO: implement CHECK
237 */
238 public static enum MethodSideEffectStatus {
239 NSE, // Non-void method has no side effect
240 NSE_EX, // No side effect method which result value might be ignored for some reason
241 CHECK, // (unimplemented yet) No side effect method which just checks the arguments, throws exceptions and returns one of arguments (or void) like assert or precondition
242 USELESS, // Void method which seems to be useless
243 SE_CLINIT, // Method has no side effect, but it's a constructor or static method of the class having side effect
244 OBJ, // Non-static method which changes only its object
245 SE // Method has side effect or side-effect status for the method is unknown
246 }
247
248 public static class NoSideEffectMethodsDatabase {
249 private final Map<MethodDescriptor, MethodSideEffectStatus> map = new HashMap<>();
250
251 void add(MethodDescriptor m, MethodSideEffectStatus s) {
252 map.put(m, s);
253 }
254
255 public @Nonnull MethodSideEffectStatus status(MethodDescriptor m) {
256 MethodSideEffectStatus s = map.get(m);
257 return s == null ? MethodSideEffectStatus.SE : s;
258 }
259
260 /**
261 * @param m method to check
262 * @param statuses allowed statuses
263 * @return true if method status is one of the statuses
264 */
265 public boolean is(MethodDescriptor m, MethodSideEffectStatus... statuses) {
266 MethodSideEffectStatus s = status(m);
267 for(MethodSideEffectStatus status : statuses) {
268 if(s == status) {
269 return true;
270 }
271 }
272 return false;
273 }
274
275 public boolean hasNoSideEffect(MethodDescriptor m) {
276 return status(m) == MethodSideEffectStatus.NSE;
277 }
278
279 public boolean useless(MethodDescriptor m) {
280 return status(m) == MethodSideEffectStatus.USELESS;
281 }
282
283 public boolean excluded(MethodDescriptor m) {
284 return is(m, MethodSideEffectStatus.NSE_EX, MethodSideEffectStatus.SE_CLINIT);
285 }
286 }
287
288 static class EarlyExitException extends RuntimeException {
289 }
290
291 private final Map<MethodDescriptor, SideEffectStatus> statusMap = new HashMap<>();
292 private final Map<MethodDescriptor, List<MethodCall>> callGraph = new HashMap<>();
293 private final Set<MethodDescriptor> getStaticMethods = new HashSet<>();
294 private final Set<MethodDescriptor> uselessVoidCandidates = new HashSet<>();
295
296 private SideEffectStatus status;
297 private ArrayList<MethodCall> calledMethods;
298 private Set<ClassDescriptor> subtypes;
299 private Set<Integer> finallyTargets;
300 private Set<Integer> finallyExceptionRegisters;
301
302 private boolean constructor;
303 private boolean uselessVoidCandidate;
304 private boolean classInit;
305
306 private Set<FieldDescriptor> allowedFields;
307 private Set<MethodDescriptor> fieldsModifyingMethods;
308
309 private final NoSideEffectMethodsDatabase noSideEffectMethods = new NoSideEffectMethodsDatabase();
310
311 public FindNoSideEffectMethods(BugReporter bugReporter) {
312 Global.getAnalysisCache().eagerlyPutDatabase(NoSideEffectMethodsDatabase.class, noSideEffectMethods);
313 }
314
315 @Override
316 public void visit(JavaClass obj) {
317 super.visit(obj);
318 allowedFields = new HashSet<>();
319 fieldsModifyingMethods = new HashSet<>();
320 subtypes = null;
321 if (!obj.isFinal() && !obj.isEnum()) {
322 try {
323 Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
324 subtypes = new HashSet<>(subtypes2.getSubtypes(getClassDescriptor()));
325 subtypes.remove(getClassDescriptor());
326 } catch (ClassNotFoundException e) {
327 }
328 }
329 }
330
331 @Override
332 public void visit(Method method) {
333 constructor = method.getName().equals("<init>");
334 classInit = method.getName().equals("<clinit>");
335 calledMethods = new ArrayList<>();
336 status = SideEffectStatus.NO_SIDE_EFFECT;
337 if (hasNoSideEffect(getMethodDescriptor())) {
338 handleStatus();
339 return;
340 }
341 if(isObjectOnlyMethod(getMethodDescriptor())) {
342 status = SideEffectStatus.OBJECT_ONLY;
343 }
344 if (method.isNative() || changedArg(getMethodDescriptor()) != -1) {
345 status = SideEffectStatus.SIDE_EFFECT;
346 handleStatus();
347 return;
348 }
349 boolean sawImplementation = false;
350 if (classInit) {
351 superClinitCall();
352 }
353 if (!method.isStatic() && !method.isPrivate() && !method.isFinal() && !constructor && subtypes != null) {
354 for (ClassDescriptor subtype : subtypes) {
355 try {
356 XClass xClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, subtype);
357 XMethod matchingMethod = xClass.findMatchingMethod(getMethodDescriptor());
358 if (matchingMethod != null) {
359 sawImplementation = true;
360 sawCall(new MethodCall(matchingMethod.getMethodDescriptor(), TARGET_THIS), false);
361 }
362 } catch (CheckedAnalysisException e) {
363 }
364 }
365 }
366 if (method.isAbstract() || method.isInterface()) {
367 if (!sawImplementation
368 || getClassName().endsWith("Visitor") || getClassName().endsWith("Listener")
369 || getClassName().startsWith("java/sql/")
370 || (getClassName().equals("java/util/concurrent/Future") && !method.getName().startsWith("is"))
371 || (getClassName().equals("java/lang/Process") && method.getName().equals("exitValue"))) {
372 status = SideEffectStatus.SIDE_EFFECT;
373 } else if(isObjectOnlyMethod(getMethodDescriptor())) {
374 status = SideEffectStatus.OBJECT_ONLY;
375 } else {
376 String[] thrownExceptions = getXMethod().getThrownExceptions();
377 if(thrownExceptions != null && thrownExceptions.length > 0) {
378 status = SideEffectStatus.SIDE_EFFECT;
379 }
380 }
381 }
382 if ((status == SideEffectStatus.SIDE_EFFECT || status == SideEffectStatus.OBJECT_ONLY) || method.isAbstract()
383 || method.isInterface() || method.isNative()) {
384 handleStatus();
385 }
386 }
387
388 @Override
389 public void visit(Field obj) {
390 XField xField = getXField();
391 if(!xField.isStatic() && (xField.isPrivate() || xField.isFinal()) && xField.isReferenceType()) {
392 allowedFields.add(xField.getFieldDescriptor());
393 }
394 }
395
396 @Override
397 public void visitAfter(JavaClass obj) {
398 for(MethodDescriptor method : fieldsModifyingMethods) {
399 List<MethodCall> calls = callGraph.get(method);
400 SideEffectStatus prevStatus = statusMap.get(method);
401 status = prevStatus.toSure();
402 calledMethods = new ArrayList<>();
403 for(MethodCall methodCall : calls) {
404 FieldDescriptor target = methodCall.getTarget();
405 if(target != TARGET_NEW && target != TARGET_OTHER && target != TARGET_THIS) {
406 if(allowedFields.contains(target)) {
407 methodCall = new MethodCall(methodCall.getMethod(), TARGET_THIS);
408 } else {
409 methodCall = new MethodCall(methodCall.getMethod(), TARGET_OTHER);
410 }
411 }
412 sawCall(methodCall, false);
413 if(status == SideEffectStatus.SIDE_EFFECT) {
414 break;
415 }
416 }
417 if (status != prevStatus) {
418 statusMap.put(method, status);
419 }
420 if(status.unsure()) {
421 calledMethods.trimToSize();
422 callGraph.put(method, calledMethods);
423 } else {
424 callGraph.remove(method);
425 }
426 }
427 MethodDescriptor clinit = new MethodDescriptor(getClassName(), "<clinit>", "()V", true);
428 if(!statusMap.containsKey(clinit)) {
429 status = SideEffectStatus.NO_SIDE_EFFECT;
430 calledMethods = new ArrayList<>();
431 superClinitCall();
432 statusMap.put(clinit, status);
433 if(status == SideEffectStatus.UNSURE || status == SideEffectStatus.UNSURE_OBJECT_ONLY) {
434 calledMethods.trimToSize();
435 callGraph.put(clinit, calledMethods);
436 }
437 }
438 }
439
440 private void superClinitCall() {
441 ClassDescriptor superclassDescriptor = getXClass().getSuperclassDescriptor();
442 if(superclassDescriptor != null && !superclassDescriptor.getClassName().equals("java/lang/Object")) {
443 sawCall(new MethodCall(new MethodDescriptor(superclassDescriptor.getClassName(), "<clinit>", "()V", true), TARGET_THIS), false);
444 }
445 }
446
447 private void handleStatus() {
448 statusMap.put(getMethodDescriptor(), status);
449 if(status == SideEffectStatus.UNSURE || status == SideEffectStatus.UNSURE_OBJECT_ONLY) {
450 calledMethods.trimToSize();
451 callGraph.put(getMethodDescriptor(), calledMethods);
452 } else {
453 fieldsModifyingMethods.remove(getMethodDescriptor());
454 }
455 }
456
457 @Override
458 public void visit(Code obj) {
459 uselessVoidCandidate = !classInit && !constructor && !getXMethod().isSynthetic() && Type.getReturnType(getMethodSig()) == Type.VOID;
460 byte[] code = obj.getCode();
461 if(code.length == 4 && (code[0] & 0xFF) == GETSTATIC && (code[3] & 0xFF) == ARETURN) {
462 getStaticMethods.add(getMethodDescriptor());
463 handleStatus();
464 return;
465 }
466
467 if (code.length <= 2 && !getXMethod().isStatic() && (getXMethod().isPublic() || getXMethod().isProtected())
468 && !getXMethod().isFinal() && (getXClass().isPublic() || getXClass().isProtected())) {
469 for(byte[] stubMethod : STUB_METHODS) {
470 if (Arrays.equals(stubMethod, code)
471 && (getClassName().endsWith("Visitor") || getClassName().endsWith("Listener") || !hasOtherImplementations(getXMethod()))) {
472 // stub method which can be extended: assume it can be extended with possible side-effect
473 status = SideEffectStatus.SIDE_EFFECT;
474 handleStatus();
475 return;
476 }
477 }
478 }
479 if (statusMap.containsKey(getMethodDescriptor())) {
480 return;
481 }
482 finallyTargets = new HashSet<>();
483 for(CodeException ex : getCode().getExceptionTable()) {
484 if(ex.getCatchType() == 0) {
485 finallyTargets.add(ex.getHandlerPC());
486 }
487 }
488 finallyExceptionRegisters = new HashSet<>();
489 try {
490 super.visit(obj);
491 } catch (EarlyExitException e) {
492 // Ignore
493 }
494 if (uselessVoidCandidate && code.length > 1
495 && (status == SideEffectStatus.UNSURE || status == SideEffectStatus.NO_SIDE_EFFECT)) {
496 uselessVoidCandidates.add(getMethodDescriptor());
497 }
498 handleStatus();
499 }
500
501 @Override
502 public void sawOpcode(int seen) {
503 if (!allowedFields.isEmpty() && seen == PUTFIELD) {
504 Item objItem = getStack().getStackItem(1);
505 if (objItem.getRegisterNumber() == 0) {
506 if (allowedFields.contains(getFieldDescriptorOperand())) {
507 Item valueItem = getStack().getStackItem(0);
508 if (!isNew(valueItem) && !valueItem.isNull()) {
509 allowedFields.remove(getFieldDescriptorOperand());
510 }
511 }
512 }
513 }
514 if (status == SideEffectStatus.SIDE_EFFECT && allowedFields.isEmpty()) {
515 // Nothing to do: skip the rest of the method
516 throw new EarlyExitException();
517 }
518 if (status == SideEffectStatus.SIDE_EFFECT) {
519 return;
520 }
521 switch (seen) {
522 case ASTORE:
523 case ASTORE_0:
524 case ASTORE_1:
525 case ASTORE_2:
526 case ASTORE_3:
527 if(finallyTargets.contains(getPC())) {
528 finallyExceptionRegisters.add(getRegisterOperand());
529 }
530 break;
531 case ATHROW: {
532 Item exceptionItem = getStack().getStackItem(0);
533 if(!finallyExceptionRegisters.remove(exceptionItem.getRegisterNumber())) {
534 uselessVoidCandidate = false;
535 try {
536 JavaClass javaClass = exceptionItem.getJavaClass();
537 if (javaClass != null && ALLOWED_EXCEPTIONS.contains(javaClass.getClassName())) {
538 break;
539 }
540 } catch (ClassNotFoundException e) {
541 }
542 status = SideEffectStatus.SIDE_EFFECT;
543 }
544 break;
545 }
546 case PUTSTATIC:
547 if(classInit) {
548 if(getClassConstantOperand().equals(getClassName())) {
549 break;
550 }
551 }
552 status = SideEffectStatus.SIDE_EFFECT;
553 break;
554 case INVOKEDYNAMIC:
555 status = SideEffectStatus.SIDE_EFFECT;
556 break;
557 case PUTFIELD:
558 sawCall(getMethodCall(FIELD_STORE_STUB_METHOD), false);
559 break;
560 case AASTORE:
561 case DASTORE:
562 case CASTORE:
563 case BASTORE:
564 case IASTORE:
565 case LASTORE:
566 case FASTORE:
567 case SASTORE:
568 sawCall(getMethodCall(ARRAY_STORE_STUB_METHOD), false);
569 break;
570 case INVOKESTATIC:
571 if (changesOnlyNewObjects(getMethodDescriptorOperand())) {
572 break;
573 }
574 sawCall(new MethodCall(getMethodDescriptorOperand(), TARGET_OTHER), false);
575 break;
576 case INVOKESPECIAL:
577 case INVOKEINTERFACE:
578 case INVOKEVIRTUAL: {
579 XMethod xMethodOperand = getXMethodOperand();
580 MethodDescriptor methodDescriptorOperand = xMethodOperand == null ? getMethodDescriptorOperand() : xMethodOperand
581 .getMethodDescriptor();
582 if (changesOnlyNewObjects(getMethodDescriptorOperand())) {
583 break;
584 }
585 MethodCall methodCall = getMethodCall(methodDescriptorOperand);
586 sawCall(methodCall, false);
587 break;
588 }
589 default:
590 break;
591 }
592 }
593
594 private MethodCall getMethodCall(MethodDescriptor methodDescriptorOperand) {
595 Item objItem = getStack().getStackItem(getNumberArguments(methodDescriptorOperand.getSignature()));
596 if (isNew(objItem)) {
597 return new MethodCall(methodDescriptorOperand, TARGET_NEW);
598 }
599 if (objItem.getRegisterNumber() == 0 && !getMethod().isStatic()) {
600 return new MethodCall(methodDescriptorOperand, constructor ? TARGET_NEW : TARGET_THIS);
601 }
602 XField xField = objItem.getXField();
603 if (xField != null) {
604 if (classInit && xField.isStatic() && xField.getClassDescriptor().getClassName().equals(getClassName())) {
605 return new MethodCall(methodDescriptorOperand, TARGET_NEW);
606 }
607 if (!getMethodDescriptor().isStatic() && objItem.getFieldLoadedFromRegister() == 0
608 && allowedFields.contains(xField.getFieldDescriptor())) {
609 fieldsModifyingMethods.add(getMethodDescriptor());
610 return new MethodCall(methodDescriptorOperand, xField.getFieldDescriptor());
611 }
612 }
613 return new MethodCall(methodDescriptorOperand, TARGET_OTHER);
614 }
615
616 private void sawCall(MethodCall methodCall, boolean finalPass) {
617 if (status == SideEffectStatus.SIDE_EFFECT) {
618 return;
619 }
620 MethodDescriptor methodDescriptor = methodCall.getMethod();
621 if (hasNoSideEffect(methodDescriptor)) {
622 sawNoSideEffectCall(methodDescriptor);
623 return;
624 }
625 FieldDescriptor target = methodCall.getTarget();
626 SideEffectStatus calledStatus = isObjectOnlyMethod(methodDescriptor) ? SideEffectStatus.OBJECT_ONLY : statusMap
627 .get(methodDescriptor);
628 if (calledStatus == null) {
629 calledStatus = finalPass ? hasNoSideEffectUnknown(methodDescriptor) ? SideEffectStatus.NO_SIDE_EFFECT : SideEffectStatus.SIDE_EFFECT
630 : SideEffectStatus.UNSURE;
631 }
632 switch(calledStatus) {
633 case NO_SIDE_EFFECT:
634 sawNoSideEffectCall(methodDescriptor);
635 return;
636 case SIDE_EFFECT:
637 status = SideEffectStatus.SIDE_EFFECT;
638 return;
639 case OBJECT_ONLY:
640 if(target == TARGET_THIS) {
641 status = status.toObjectOnly();
642 } else if(target == TARGET_OTHER) {
643 status = SideEffectStatus.SIDE_EFFECT;
644 } else if(target != TARGET_NEW) {
645 status = status.toObjectOnly();
646 sawUnsureCall(methodCall);
647 }
648 return;
649 case UNSURE_OBJECT_ONLY:
650 if(target == TARGET_NEW) {
651 sawUnsureCall(methodCall);
652 } else if(target == TARGET_OTHER) {
653 status = SideEffectStatus.SIDE_EFFECT;
654 } else {
655 status = status.toObjectOnly();
656 sawUnsureCall(methodCall);
657 }
658 return;
659 case UNSURE:
660 sawUnsureCall(methodCall);
661 return;
662 }
663 }
664
665 /**
666 * @param methodDescriptor
667 */
668 private void sawNoSideEffectCall(MethodDescriptor methodDescriptor) {
669 if(uselessVoidCandidate && Type.getReturnType(methodDescriptor.getSignature()) == Type.VOID
670 && !methodDescriptor.getName().equals("<init>")) {
671 /* To reduce false-positives we do not mark method as useless void if it calls
672 * another useless void method. If that another method also in the scope of our project
673 * then we will report it instead. If there's a cycle of no-side-effect calls, then
674 * it's probably some delegation pattern and methods can be extended in future/derived
675 * projects to do something useful.
676 */
677 uselessVoidCandidate = false;
678 }
679 }
680
681 private void sawUnsureCall(MethodCall methodCall) {
682 calledMethods.add(methodCall);
683 status = status.toUnsure();
684 }
685
686 /**
687 * @param item stack item to check
688 * @return true if this stack item is known to be newly created
689 */
690 private static boolean isNew(OpcodeStack.Item item) {
691 if(item.isNewlyAllocated()) {
692 return true;
693 }
694 XMethod returnValueOf = item.getReturnValueOf();
695 if(returnValueOf == null) {
696 return false;
697 }
698 if("iterator".equals(returnValueOf.getName())
699 && "()Ljava/util/Iterator;".equals(returnValueOf.getSignature())
700 && Subtypes2.instanceOf(returnValueOf.getClassName(), "java.lang.Iterable")) {
701 return true;
702 }
703 if(returnValueOf.getClassName().startsWith("[") && returnValueOf.getName().equals("clone")) {
704 return true;
705 }
706 if(NEW_OBJECT_RETURNING_METHODS.contains(returnValueOf.getMethodDescriptor())) {
707 return true;
708 }
709 return false;
710 }
711
712 private boolean changesOnlyNewObjects(MethodDescriptor methodDescriptor) {
713 int arg = changedArg(methodDescriptor);
714 if(arg == -1) {
715 return false;
716 }
717 int nArgs = getNumberArguments(methodDescriptor.getSignature());
718 if(!isNew(getStack().getStackItem(nArgs-arg-1))) {
719 return false;
720 }
721 return true;
722 }
723
724 /**
725 * @param m method to check
726 * @return array of argument numbers (0-based) which this method writes into or null if we don't know anything about this method
727 */
728 private static int changedArg(MethodDescriptor m) {
729 if(m.equals(ARRAY_COPY)) {
730 return 2;
731 }
732 if(m.getName().equals("toArray") && m.getSignature().equals("([Ljava/lang/Object;)[Ljava/lang/Object;")
733 && Subtypes2.instanceOf(m.getClassDescriptor(), "java.util.Collection")) {
734 return 0;
735 }
736 if ((m.getName().equals("sort") || m.getName().equals("fill") || m.getName().equals("reverse") || m.getName().equals(
737 "shuffle"))
738 && (m.getSlashedClassName().equals("java/util/Arrays") || m.getSlashedClassName().equals("java/util/Collections"))) {
739 return 0;
740 }
741 return -1;
742 }
743
744 /**
745 * @param m method to check
746 * @return true if given method is known to have no side effects
747 */
748 private static boolean hasNoSideEffect(MethodDescriptor m) {
749 String className = m.getSlashedClassName();
750 if("java/lang/String".equals(className)) {
751 return !(m.getName().equals("getChars") || (m.getName().equals("getBytes") && m.getSignature().equals("(II[BI)V")));
752 }
753 if("java/lang/Math".equals(className)) {
754 return !m.getName().equals("random");
755 }
756 if("java/lang/Throwable".equals(className)) {
757 return m.getName().startsWith("get");
758 }
759 if("java/lang/Character".equals(className)) {
760 return !m.getName().equals("toChars");
761 }
762 if("java/lang/Class".equals(className) && m.getName().startsWith("is")) {
763 return true;
764 }
765 if("java/awt/Color".equals(className) && m.getName().equals("<init>")) {
766 return true;
767 }
768 if("java/util/regex/Pattern".contains(className)) {
769 // Pattern.compile is often used to check the PatternSyntaxException, thus we consider it as side-effect method
770 return !m.getName().equals("compile") && !m.getName().equals("<init>");
771 }
772 if(className.startsWith("[") && m.getName().equals("clone")) {
773 return true;
774 }
775 if(className.startsWith("org/w3c/dom/") && (m.getName().startsWith("get") || m.getName().startsWith("has") || m.getName().equals("item"))) {
776 return true;
777 }
778 if(className.startsWith("java/util/") &&
779 (className.endsWith("Set") || className.endsWith("Map") || className.endsWith("Collection")
780 || className.endsWith("List") || className.endsWith("Queue") || className.endsWith("Deque")
781 || className.endsWith("Vector")) || className.endsWith("Hashtable") || className.endsWith("Dictionary")) {
782 // LinkedHashSet in accessOrder mode changes internal state during get/getOrDefault
783 if(className.equals("java/util/LinkedHashMap") && m.getName().startsWith("get")) {
784 return false;
785 }
786 if(NO_SIDE_EFFECT_COLLECTION_METHODS.contains(m.getName()) || (m.getName().equals("toArray") && m.getSignature().equals("()[Ljava/lang/Object;"))) {
787 return true;
788 }
789 }
790 if(m.getName().equals("binarySearch") && (m.getSlashedClassName().equals("java/util/Arrays") || m.getSlashedClassName().equals("java/util/Collections"))) {
791 return true;
792 }
793 if(m.getName().startsWith("$SWITCH_TABLE$")) {
794 return true;
795 }
796 if(m.getName().equals("<init>") && isObjectOnlyClass(className)) {
797 return true;
798 }
799 if(m.getName().equals("toString") && m.getSignature().equals("()Ljava/lang/String;") && m.getSlashedClassName().startsWith("java/")) {
800 return true;
801 }
802 if(NUMBER_CLASSES.contains(className)) {
803 return !m.getSignature().startsWith("(Ljava/lang/String;");
804 }
805 if(!m.isStatic() && m.getName().equals("equals") &&
806 m.getSignature().equals("(Ljava/lang/Object;)Z")) {
807 return true;
808 }
809 if(NO_SIDE_EFFECT_METHODS.contains(m)) {
810 return true;
811 }
812 return false;
813 }
814
815 /**
816 * @param m method to check
817 * @return true if we may assume that given unseen method has no side effect
818 */
819 private static boolean hasNoSideEffectUnknown(MethodDescriptor m) {
820 if(m.isStatic() && m.getName().equals("<clinit>")) {
821 // No side effect for class initializer of unseen class
822 return true;
823 }
824 if(!m.isStatic() && m.getName().equals("toString") && m.getSignature().equals("()Ljava/lang/String;")) {
825 // We assume no side effect for unseen toString methods
826 return true;
827 }
828 if(!m.isStatic() && m.getName().equals("hashCode") && m.getSignature().equals("()I")) {
829 // We assume no side effect for unseen hashCode methods
830 return true;
831 }
832 if(m.isStatic() && m.getName().equals("values") && m.getSignature().startsWith("()")) {
833 // We assume no side effect for unseen enums
834 return Subtypes2.instanceOf(m.getClassDescriptor(), "java.lang.Enum");
835 }
836 return false;
837 }
838
839 /**
840 * @param m method to check
841 * @return true if given method is known to change its object only
842 */
843 private static boolean isObjectOnlyMethod(MethodDescriptor m) {
844 if (m.isStatic() || m.getName().equals("<init>")) {
845 return false;
846 }
847 String className = m.getSlashedClassName();
848 if(isObjectOnlyClass(className)) {
849 return true;
850 }
851 if(className.startsWith("javax/xml/") && m.getName().startsWith("next")) {
852 return true;
853 }
854 if ((className.startsWith("java/net/") || className.startsWith("javax/servlet"))
855 && (m.getName().startsWith("remove") || m.getName().startsWith("add") || m.getName().startsWith("set"))) {
856 return true;
857 }
858 if(OBJECT_ONLY_METHODS.contains(m)) {
859 return true;
860 }
861 return false;
862 }
863
864 /**
865 * @param className class to check
866 * @return true if all methods of this class are known to be object-only or no-side-effect
867 */
868 private static boolean isObjectOnlyClass(String className) {
869 if(OBJECT_ONLY_CLASSES.contains(className)) {
870 return true;
871 }
872 if(className.startsWith("java/lang/") && (className.endsWith("Error") || className.endsWith("Exception"))) {
873 return true;
874 }
875 return className.startsWith("java/util/") &&
876 (className.endsWith("Set") || className.endsWith("Map") || className.endsWith("Collection")
877 || className.endsWith("List") || className.endsWith("Queue") || className.endsWith("Deque")
878 || className.endsWith("Vector"));
879 }
880
881 @Override
882 public void report() {
883 computeFinalStatus();
884 Set<String> sideEffectClinit = new HashSet<>();
885 for(Entry<MethodDescriptor, SideEffectStatus> entry : statusMap.entrySet()) {
886 if (entry.getValue() == SideEffectStatus.SIDE_EFFECT && entry.getKey().isStatic() && entry.getKey().getName().equals("<clinit>")) {
887 sideEffectClinit.add(entry.getKey().getSlashedClassName());
888 }
889 }
890 for(Entry<MethodDescriptor, SideEffectStatus> entry : statusMap.entrySet()) {
891 MethodDescriptor m = entry.getKey();
892 if (entry.getValue() == SideEffectStatus.NO_SIDE_EFFECT) {
893 String returnType = new SignatureParser(m.getSignature()).getReturnTypeSignature();
894 if (!returnType.equals("V") || m.getName().equals("<init>")) {
895 if(m.equals(GET_CLASS)) {
896 /* We do not mark getClass() call as pure, because it can appear in code like this:
897 public class Outer {
898 public class Inner {}
899 public void test(Outer n) { n.new Inner(); }
900 }
901 The test method is compiled into (assumably it's done to generate NPE if n is null)
902 0: new #16 // class a/Outer$Inner
903 3: aload_1
904 4: dup
905 5: invokevirtual #18 // Method java/lang/Object.getClass:()Ljava/lang/Class;
906 8: pop
907 9: invokespecial #22 // Method a/Outer$Inner."<init>":(La/Outer;)V
908 12: return
909 So we would have a false-positive here
910 */
911 continue;
912 }
913 if (m.getName().startsWith("access$") && (!(m instanceof XMethod) || ((XMethod)m).getAccessMethodForMethod() == null)) {
914 /* We skip field access methods, because they can unnecessarily be used for static calls
915 * (probably by older javac)
916 */
917 continue;
918 }
919 if (m.getName().startsWith("jjStopStringLiteral")) {
920 /* Some old JJTree versions may generate redundant calls to this method
921 * Skip it as reports in generated code don't help much
922 */
923 continue;
924 }
925 if (m.isStatic() || m.getName().equals("<init>")) {
926 if(sideEffectClinit.contains(m.getSlashedClassName())) {
927 /* Skip static methods and constructors for classes which have
928 * side-effect class initializer
929 */
930 noSideEffectMethods.add(m, MethodSideEffectStatus.SE_CLINIT);
931 continue;
932 }
933 }
934 if(m.equals(CLASS_GET_NAME) // used sometimes to trigger class loading
935 || m.equals(HASH_CODE) // found intended hashCode call several times in different projects, need further research
936 ) {
937 noSideEffectMethods.add(m, MethodSideEffectStatus.NSE_EX);
938 continue;
939 }
940 if (m.isStatic() && getStaticMethods.contains(m) && !m.getSlashedClassName().startsWith("java/")) {
941 String returnSlashedClassName = ClassName.fromFieldSignature(returnType);
942 if(returnSlashedClassName != null) {
943 String returnClass = ClassName.toDottedClassName(returnSlashedClassName);
944 if(ClassName.extractPackageName(returnClass).equals(m.getClassDescriptor().getPackageName())) {
945 /* Skip methods which only retrieve static field from the same package
946 * As they as often used to trigger class initialization
947 */
948 noSideEffectMethods.add(m, MethodSideEffectStatus.NSE_EX);
949 continue;
950 }
951 }
952 }
953 noSideEffectMethods.add(m, MethodSideEffectStatus.NSE);
954 } else { // void methods
955 if(uselessVoidCandidates.contains(m)) {
956 if(m.getName().equals("maybeForceBuilderInitialization") && m.getSignature().equals("()V")) {
957 // Autogenerated by Google protocol buffer compiler
958 continue;
959 }
960 noSideEffectMethods.add(m, MethodSideEffectStatus.USELESS);
961 }
962 }
963 } else if(entry.getValue() == SideEffectStatus.OBJECT_ONLY) {
964 noSideEffectMethods.add(m, MethodSideEffectStatus.OBJ);
965 }
966 }
967 }
968
969 /**
970 * @param xMethod
971 * @return true if this has other implementations
972 */
973 private static boolean hasOtherImplementations(XMethod xMethod) {
974 Set<XMethod> superMethods = Hierarchy2.findSuperMethods(xMethod);
975 superMethods.add(xMethod);
976 Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
977 Set<ClassDescriptor> subtypes = new HashSet<>();
978 for(XMethod superMethod : superMethods) {
979 try {
980 subtypes.addAll(subtypes2.getSubtypes(superMethod.getClassDescriptor()));
981 } catch (ClassNotFoundException e) {
982 // ignore
983 }
984 }
985 subtypes.remove(xMethod.getClassDescriptor());
986 for (ClassDescriptor subtype : subtypes) {
987 try {
988 XClass xClass = subtype.getXClass();
989 XMethod subMethod = xClass.findMatchingMethod(xMethod.getMethodDescriptor());
990 if (subMethod != null) {
991 if(!subMethod.isAbstract() ) {
992 return true;
993 }
994 }
995 } catch (CheckedAnalysisException e) {
996 // ignore
997 }
998 }
999 return false;
1000 }
1001
1002 private void computeFinalStatus() {
1003 boolean changed = true;
1004 while (changed) {
1005 changed = false;
1006 Iterator<Entry<MethodDescriptor, List<MethodCall>>> iterator = callGraph.entrySet().iterator();
1007 while (iterator.hasNext()) {
1008 Entry<MethodDescriptor, List<MethodCall>> entry = iterator.next();
1009 MethodDescriptor method = entry.getKey();
1010 uselessVoidCandidate = uselessVoidCandidates.contains(method);
1011 SideEffectStatus prevStatus = statusMap.get(method);
1012 status = prevStatus.toSure();
1013 calledMethods = new ArrayList<>();
1014 for(MethodCall methodCall : entry.getValue()) {
1015 sawCall(methodCall, true);
1016 if(status == SideEffectStatus.SIDE_EFFECT) {
1017 break;
1018 }
1019 }
1020 if (!uselessVoidCandidate || (status != SideEffectStatus.UNSURE && status != SideEffectStatus.NO_SIDE_EFFECT)) {
1021 uselessVoidCandidates.remove(method);
1022 }
1023 if (status != prevStatus || !entry.getValue().equals(calledMethods)) {
1024 statusMap.put(method, status);
1025 if (status.unsure()) {
1026 entry.setValue(calledMethods);
1027 } else {
1028 iterator.remove();
1029 }
1030 changed = true;
1031 }
1032 }
1033 }
1034 for(Entry<MethodDescriptor, List<MethodCall>> entry : callGraph.entrySet()) {
1035 MethodDescriptor method = entry.getKey();
1036 status = statusMap.get(method);
1037 if(status == SideEffectStatus.UNSURE) {
1038 boolean safeCycle = true;
1039 for(MethodCall methodCall : entry.getValue()) {
1040 SideEffectStatus calledStatus = statusMap.get(methodCall.getMethod());
1041 if(calledStatus != SideEffectStatus.UNSURE && calledStatus != SideEffectStatus.NO_SIDE_EFFECT) {
1042 safeCycle = false;
1043 break;
1044 }
1045 }
1046 if(safeCycle) {
1047 statusMap.put(method, SideEffectStatus.NO_SIDE_EFFECT);
1048 uselessVoidCandidate = uselessVoidCandidates.contains(method);
1049 if(uselessVoidCandidate) {
1050 for(MethodCall call : entry.getValue()) {
1051 uselessVoidCandidate = false;
1052 if((call.getMethod().equals(method) && call.getTarget() == TARGET_THIS) || method.isStatic()) {
1053 uselessVoidCandidate = true;
1054 } else {
1055 if(call.getMethod() instanceof XMethod) {
1056 XMethod xMethod = (XMethod) call.getMethod();
1057 if(xMethod.isFinal() || (!xMethod.isPublic() && !xMethod.isProtected())) {
1058 uselessVoidCandidate = true;
1059 }
1060 }
1061 }
1062 if(!uselessVoidCandidate) {
1063 break;
1064 }
1065 }
1066 if(!uselessVoidCandidate) {
1067 uselessVoidCandidates.remove(method);
1068 }
1069 }
1070 }
1071 }
1072 }
1073 }
1074 }
4242 this.bugAccumulator = new BugAccumulator(bugReporter);
4343 }
4444
45 @Override
4546 public void visitClassContext(ClassContext classContext) {
4647 Method[] methodList = classContext.getJavaClass().getMethods();
4748
4849 for (Method method : methodList) {
49 if (method.getCode() == null)
50 if (method.getCode() == null) {
5051 continue;
52 }
5153
5254 try {
5355 analyzeMethod(classContext, method);
6365
6466 private void analyzeMethod(ClassContext classContext, Method method) throws CFGBuilderException, DataflowAnalysisException {
6567 MethodGen methodGen = classContext.getMethodGen(method);
66 if (methodGen == null)
68 if (methodGen == null) {
6769 return;
70 }
6871 BitSet bytecodeSet = classContext.getBytecodeSet(method);
69 if (bytecodeSet == null)
72 if (bytecodeSet == null) {
7073 return;
74 }
7175 // We don't adequately model instanceof interfaces yet
72 if (bytecodeSet.get(Constants.INSTANCEOF) || bytecodeSet.get(Constants.CHECKCAST))
76 if (bytecodeSet.get(Constants.INSTANCEOF) || bytecodeSet.get(Constants.CHECKCAST)) {
7377 return;
78 }
7479 CFG cfg = classContext.getCFG(method);
7580 TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
7681 ConstantPoolGen cpg = classContext.getConstantPoolGen();
8691 InstructionHandle handle = location.getHandle();
8792 Instruction ins = handle.getInstruction();
8893
89 if (!(ins instanceof INVOKEINTERFACE))
94 if (!(ins instanceof INVOKEINTERFACE)) {
9095 continue;
96 }
9197
9298 INVOKEINTERFACE invoke = (INVOKEINTERFACE) ins;
9399 String mName = invoke.getMethodName(cpg);
94 if (!mName.equals("setAttribute"))
100 if (!"setAttribute".equals(mName)) {
95101 continue;
102 }
96103 String cName = invoke.getClassName(cpg);
97 if (!cName.equals("javax.servlet.http.HttpSession"))
104 if (!"javax.servlet.http.HttpSession".equals(cName)) {
98105 continue;
106 }
99107
100108 TypeFrame frame = typeDataflow.getFactAtLocation(location);
101109 if (!frame.isValid()) {
129137
130138 bugAccumulator.accumulateBug(new BugInstance(this, "J2EE_STORE_OF_NON_SERIALIZABLE_OBJECT_INTO_SESSION",
131139 isSerializable < 0.15 ? HIGH_PRIORITY : isSerializable > 0.5 ? LOW_PRIORITY : NORMAL_PRIORITY)
132 .addClassAndMethod(methodGen, sourceFile).addType(problem).describe(TypeAnnotation.FOUND_ROLE),
133 sourceLineAnnotation);
140 .addClassAndMethod(methodGen, sourceFile).addType(problem).describe(TypeAnnotation.FOUND_ROLE),
141 sourceLineAnnotation);
134142
135143 }
136144 } catch (ClassNotFoundException e) {
139147 }
140148 }
141149
150 @Override
142151 public void report() {
143152 }
144153
3030
3131 public class FindNonSerializableValuePassedToWriteObject implements Detector {
3232
33 private BugReporter bugReporter;
33 private final BugReporter bugReporter;
3434
3535 private static final boolean DEBUG = false;
3636
3838 this.bugReporter = bugReporter;
3939 }
4040
41 @Override
4142 public void visitClassContext(ClassContext classContext) {
4243 Method[] methodList = classContext.getJavaClass().getMethods();
4344
4445 for (Method method : methodList) {
45 if (method.getCode() == null)
46 if (method.getCode() == null) {
4647 continue;
48 }
4749
4850 try {
4951 analyzeMethod(classContext, method);
5860
5961 private void analyzeMethod(ClassContext classContext, Method method) throws CFGBuilderException, DataflowAnalysisException {
6062 MethodGen methodGen = classContext.getMethodGen(method);
61 if (methodGen == null)
63 if (methodGen == null) {
6264 return;
65 }
6366 BitSet bytecodeSet = classContext.getBytecodeSet(method);
64 if (bytecodeSet == null)
67 if (bytecodeSet == null) {
6568 return;
69 }
6670 // We don't adequately model instanceof interfaces yet
67 if (bytecodeSet.get(Constants.INSTANCEOF) || bytecodeSet.get(Constants.CHECKCAST))
71 if (bytecodeSet.get(Constants.INSTANCEOF) || bytecodeSet.get(Constants.CHECKCAST)) {
6872 return;
73 }
6974 CFG cfg = classContext.getCFG(method);
7075 TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
7176 ConstantPoolGen cpg = classContext.getConstantPoolGen();
8287 int pc = handle.getPosition();
8388 Instruction ins = handle.getInstruction();
8489
85 if (!(ins instanceof InvokeInstruction))
90 if (!(ins instanceof InvokeInstruction)) {
8691 continue;
92 }
8793
8894 InvokeInstruction invoke = (InvokeInstruction) ins;
8995 String mName = invoke.getMethodName(cpg);
90 if (!mName.equals("writeObject"))
96 if (!"writeObject".equals(mName)) {
9197 continue;
98 }
9299 String cName = invoke.getClassName(cpg);
93 if (!cName.equals("java.io.ObjectOutput") && !cName.equals("java.io.ObjectOutputStream"))
100 if (!"java.io.ObjectOutput".equals(cName) && !"java.io.ObjectOutputStream".equals(cName)) {
94101 continue;
102 }
95103
96104 TypeFrame frame = typeDataflow.getFactAtLocation(location);
97105 if (!frame.isValid()) {
118126
119127 double isSerializable = DeepSubtypeAnalysis.isDeepSerializable(refType);
120128
121 if (isSerializable >= 0.9)
129 if (isSerializable >= 0.9) {
122130 continue;
123
131 }
132
124133 ReferenceType problem = DeepSubtypeAnalysis.getLeastSerializableTypeComponent(refType);
125134
126135 double isRemote = DeepSubtypeAnalysis.isDeepRemote(refType);
127 if (isRemote >= 0.9)
136 if (isRemote >= 0.9) {
128137 continue;
129 if (isSerializable < isRemote)
138 }
139 if (isSerializable < isRemote) {
130140 isSerializable = isRemote;
141 }
131142
132143
133144 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(classContext,
144155 }
145156 }
146157
158 @Override
147159 public void report() {
148160 }
149161
5454
5555 boolean sawMethodCall, sawMethodCallOld;
5656
57 private BugAccumulator bugAccumulator;
57 private final BugAccumulator bugAccumulator;
5858
5959 public FindNonShortCircuit(BugReporter bugReporter) {
6060 this.bugAccumulator = new BugAccumulator(bugReporter);
115115 break;
116116
117117 case INVOKEVIRTUAL:
118 if (getNameConstantOperand().equals("length") && getClassConstantOperand().equals("java/lang/String"))
119 break;
118 if ("length".equals(getNameConstantOperand()) && "java/lang/String".equals(getClassConstantOperand())) {
119 break;
120 }
120121 sawDanger = true;
121122 sawMethodCall = true;
122123 break;
152153 OpcodeStack.Item item0 = stack.getStackItem(0);
153154 OpcodeStack.Item item1 = stack.getStackItem(1);
154155 if (item0.getConstant() == null && item1.getConstant() == null && distance < 4) {
155 if (item0.getRegisterNumber() >= 0 && item1.getRegisterNumber() >= 0)
156 if (false)
157 clearAll();
156 // if (item0.getRegisterNumber() >= 0 && item1.getRegisterNumber() >= 0) {
157 // if (false) {
158 // clearAll();
159 // }
160 // }
158161 operator = seen;
159162 stage2 = 1;
160 } else
163 } else {
161164 stage2 = 0;
165 }
162166 break;
163167 case IFEQ:
164168 case IFNE:
187191 String pattern = "NS_NON_SHORT_CIRCUIT";
188192
189193 if (sawDangerOld) {
190 if (sawNullTestVeryOld)
194 if (sawNullTestVeryOld) {
191195 priority = HIGH_PRIORITY;
196 }
192197 if (sawMethodCallOld || sawNumericTestVeryOld && sawArrayDangerOld) {
193198 priority = HIGH_PRIORITY;
194199 pattern = "NS_DANGEROUS_NON_SHORT_CIRCUIT";
195 } else
200 } else {
196201 priority = NORMAL_PRIORITY;
202 }
197203 }
198204
199205 bugAccumulator.accumulateBug(new BugInstance(this, pattern, priority).addClassAndMethod(this), this);
211217 case ILOAD_2:
212218 case ILOAD_3:
213219 clearAll();
220 break;
221 default:
222 break;
214223 }
215224 break;
216225 case ICONST_1:
230239
231240 break;
232241 case GOTO:
233 if (stage1 == 1)
242 if (stage1 == 1) {
234243 stage1 = 2;
235 else {
244 } else {
236245 stage1 = 0;
237246 clearAll();
238247 }
239248 break;
240249 case ICONST_0:
241 if (stage1 == 2)
250 if (stage1 == 2) {
242251 sawBooleanValue();
252 }
243253 stage1 = 0;
244254 break;
245255 case INVOKEINTERFACE:
247257 case INVOKESPECIAL:
248258 case INVOKESTATIC:
249259 String sig = getSigConstantOperand();
250 if (sig.endsWith(")Z"))
260 if (sig.endsWith(")Z")) {
251261 sawBooleanValue();
262 }
252263 stage1 = 0;
253264 break;
254265 default:
8989 import edu.umd.cs.findbugs.ba.XMethod;
9090 import edu.umd.cs.findbugs.ba.XMethodParameter;
9191 import edu.umd.cs.findbugs.ba.interproc.ParameterProperty;
92 import edu.umd.cs.findbugs.ba.interproc.PropertyDatabase;
9392 import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierAnnotation;
9493 import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierApplications;
9594 import edu.umd.cs.findbugs.ba.jsr305.TypeQualifierValue;
145144
146145 private static final boolean MARK_DOOMED = SystemProperties.getBoolean("fnd.markdoomed", true);
147146
148 private static final boolean REPORT_SAFE_METHOD_TARGETS = true;
147 // private static final boolean REPORT_SAFE_METHOD_TARGETS = true;
149148
150149 private static final String METHOD = SystemProperties.getProperty("fnd.method");
151150
179178 this.bugAccumulator = new BugAccumulator(bugReporter);
180179 }
181180
181 @Override
182182 public void visitClassContext(ClassContext classContext) {
183183 this.classContext = classContext;
184184
186186
187187 JavaClass jclass = classContext.getJavaClass();
188188 String className = jclass.getClassName();
189 if (CLASS != null && !className.equals(CLASS))
190 return;
189 if (CLASS != null && !className.equals(CLASS)) {
190 return;
191 }
191192
192193 List<Method> methodsInCallOrder = classContext.getMethodsInCallOrder();
193194 for (Method method : methodsInCallOrder) {
194195 try {
195 if (method.isAbstract() || method.isNative() || method.getCode() == null)
196 if (method.isAbstract() || method.isNative() || method.getCode() == null) {
196197 continue;
198 }
197199
198200 currentMethod = SignatureConverter.convertMethodSignature(jclass, method);
199201
200 if (METHOD != null && !method.getName().equals(METHOD))
202 if (METHOD != null && !method.getName().equals(METHOD)) {
201203 continue;
202 if (DEBUG || DEBUG_NULLARG)
204 }
205 if (DEBUG || DEBUG_NULLARG) {
203206 System.out.println("Checking for NP in " + currentMethod);
207 }
204208 analyzeMethod(classContext, method);
205209 } catch (MissingClassException e) {
206210 bugReporter.reportMissingClass(e.getClassNotFoundException());
213217 }
214218 }
215219
216 private void analyzeMethod(ClassContext classContext, Method method) throws DataflowAnalysisException, CFGBuilderException
217
218 {
219 if (DEBUG || DEBUG_NULLARG)
220 private void analyzeMethod(ClassContext classContext, Method method) throws DataflowAnalysisException, CFGBuilderException {
221 if (DEBUG || DEBUG_NULLARG) {
220222 System.out.println("Pre FND ");
221
222 if ((method.getAccessFlags() & Constants.ACC_VOLATILE) != 0)
223 return;
223 }
224
225 if ((method.getAccessFlags() & Constants.ACC_BRIDGE) != 0) {
226 return;
227 }
224228
225229 MethodGen methodGen = classContext.getMethodGen(method);
226230
227 if (methodGen == null)
228 return;
231 if (methodGen == null) {
232 return;
233 }
229234 if (!checkedDatabases) {
230235 checkDatabases();
231236 checkedDatabases = true;
240245
241246 if (value instanceof Type) {
242247 String className = ((Type) value).getClassName();
243 if (className.equals("java.lang.NullPointerException"))
248 if ("java.lang.NullPointerException".equals(className)) {
244249 return;
250 }
245251 }
246252 }
247253
250256 this.method = method;
251257 this.methodAnnotation = getMethodNullnessAnnotation();
252258
253 if (DEBUG || DEBUG_NULLARG)
259 if (DEBUG || DEBUG_NULLARG) {
254260 System.out.println("FND: " + SignatureConverter.convertMethodSignature(methodGen));
261 }
255262
256263 this.previouslyDeadBlocks = findPreviouslyDeadBlocks();
257264
304311 unconditionalDerefParamDatabase = analysisContext.getUnconditionalDerefParamDatabase();
305312 }
306313
307 private <DatabaseType extends PropertyDatabase<?, ?>> boolean isDatabaseNonEmpty(DatabaseType database) {
308 return database != null && !database.isEmpty();
309 }
314 // private <DatabaseType extends PropertyDatabase<?, ?>> boolean isDatabaseNonEmpty(DatabaseType database) {
315 // return database != null && !database.isEmpty();
316 // }
310317
311318 /**
312319 * See if the currently-visited method declares a
343350 Instruction ins = location.getHandle().getInstruction();
344351 try {
345352 ValueNumberFrame vnaFrame = classContext.getValueNumberDataflow(method).getFactAtLocation(location);
346 if (!vnaFrame.isValid())
353 if (!vnaFrame.isValid()) {
347354 continue;
355 }
348356
349357 if (ins instanceof InvokeInstruction) {
350358 examineCallSite(location, cpg, typeDataflow);
375383 // Don't check equals() calls.
376384 // If an equals() call unconditionally dereferences the parameter,
377385 // it is the fault of the method, not the caller.
378 if (methodName.equals("equals") && signature.equals("(Ljava/lang/Object;)Z"))
379 return;
386 if ("equals".equals(methodName) && "(Ljava/lang/Object;)Z".equals(signature)) {
387 return;
388 }
380389
381390 int returnTypeStart = signature.indexOf(')');
382 if (returnTypeStart < 0)
383 return;
391 if (returnTypeStart < 0) {
392 return;
393 }
384394 String paramList = signature.substring(0, returnTypeStart + 1);
385395
386 if (paramList.equals("()") || (paramList.indexOf("L") < 0 && paramList.indexOf('[') < 0))
396 if ("()".equals(paramList) || (paramList.indexOf('L') < 0 && paramList.indexOf('[') < 0)) {
387397 // Method takes no arguments, or takes no reference arguments
388398 return;
399 }
389400
390401 // See if any null arguments are passed
391402 IsNullValueFrame frame = classContext.getIsNullValueDataflow(method).getFactAtLocation(location);
392 if (!frame.isValid())
393 return;
394
403 if (!frame.isValid()) {
404 return;
405 }
406 /*
395407 if (false && methodName.equals("checkNotNull")
396408 && invokeInstruction.getClassName(cpg).equals("com.google.common.base.Preconditions")) {
397409 SignatureParser sigParser = new SignatureParser(signature);
425437 }
426438
427439 BugInstance warning = new BugInstance(this, pattern, priority)
428 .addClassAndMethod(classContext.getJavaClass(), method).addOptionalAnnotation(annotation)
429 .addCalledMethod(cpg, invokeInstruction).addSourceLine(classContext, method, location);
440 .addClassAndMethod(classContext.getJavaClass(), method).addOptionalAnnotation(annotation)
441 .addCalledMethod(cpg, invokeInstruction).addSourceLine(classContext, method, location);
430442
431443 bugReporter.reportBug(warning);
432444 }
433445 }
434
446 */
435447 BitSet nullArgSet = frame.getArgumentSet(invokeInstruction, cpg, new DataflowValueChooser<IsNullValue>() {
448 @Override
436449 public boolean choose(IsNullValue value) {
437450 // Only choose non-exception values.
438451 // Values null on an exception path might be due to
441454 }
442455 });
443456 BitSet definitelyNullArgSet = frame.getArgumentSet(invokeInstruction, cpg, new DataflowValueChooser<IsNullValue>() {
457 @Override
444458 public boolean choose(IsNullValue value) {
445459 return value.isDefinitelyNull();
446460 }
447461 });
448462 nullArgSet.and(definitelyNullArgSet);
449 if (nullArgSet.isEmpty())
450 return;
463 if (nullArgSet.isEmpty()) {
464 return;
465 }
451466 if (DEBUG_NULLARG) {
452467 System.out.println("Null arguments passed: " + nullArgSet);
453468 System.out.println("Frame is: " + frame);
472487 throws DataflowAnalysisException {
473488
474489 IsNullValueFrame frame = invDataflow.getFactAtLocation(location);
475 if (!frame.isValid())
476 return;
490 if (!frame.isValid()) {
491 return;
492 }
477493 IsNullValue tos = frame.getTopValue();
478494 if (tos.isDefinitelyNull()) {
479495 XField field = XFactory.createXField(ins, cpg);
511527 IsNullValueDataflow invDataflow = classContext.getIsNullValueDataflow(method);
512528 IsNullValueFrame frame = invDataflow.getFactAtLocation(location);
513529 ValueNumberFrame vnaFrame = classContext.getValueNumberDataflow(method).getFactAtLocation(location);
514 if (!vnaFrame.isValid())
515 return;
530 if (!vnaFrame.isValid()) {
531 return;
532 }
516533 ValueNumber valueNumber = vnaFrame.getTopValue();
517 if (!frame.isValid())
518 return;
534 if (!frame.isValid()) {
535 return;
536 }
519537 IsNullValue tos = frame.getTopValue();
520538 if (tos.isDefinitelyNull()) {
521539 BugAnnotation variable = ValueNumberSourceInfo.findAnnotationFromValueNumber(method, location, valueNumber, vnaFrame,
523541
524542 String bugPattern = "NP_NONNULL_RETURN_VIOLATION";
525543 int priority = NORMAL_PRIORITY;
526 if (tos.isDefinitelyNull() && !tos.isException())
544 if (tos.isDefinitelyNull() && !tos.isException()) {
527545 priority = HIGH_PRIORITY;
546 }
528547 String methodName = method.getName();
529 if (methodName.equals("clone")) {
548 if ("clone".equals(methodName)) {
530549 bugPattern = "NP_CLONE_COULD_RETURN_NULL";
531550 priority = NORMAL_PRIORITY;
532 } else if (methodName.equals("toString")) {
551 } else if ("toString".equals(methodName)) {
533552 bugPattern = "NP_TOSTRING_COULD_RETURN_NULL";
534553 priority = NORMAL_PRIORITY;
535554 }
546565 for (Iterator<Location> i = classContext.getCFG(method).locationIterator(); i.hasNext();) {
547566 Location loc = i.next();
548567 int pc2 = loc.getHandle().getPosition();
549 if (pc2 >= pc || pc2 < pc - 30)
568 if (pc2 >= pc || pc2 < pc - 30) {
550569 continue;
570 }
551571 Instruction ins = loc.getHandle().getInstruction();
552572 if ((ins instanceof IFNONNULL || ins instanceof IFNULL || ins instanceof NullnessConversationInstruction)
553573 && !seen.get(pc2)) {
569589 public static final Set<String> catchTypesForNull = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(
570590 "java/lang/NullPointerException", "java/lang/RuntimeException", "java/lang/Exception")));
571591
572 private boolean catchesNull(Location location) {
573
574 ConstantPool constantPool = classContext.getJavaClass().getConstantPool();
575 Code code = method.getCode();
576
577 return catchesNull(constantPool, code, location);
578 }
579
580592 public static boolean catchesNull(ConstantPool constantPool, Code code, Location location) {
581593 int position = location.getHandle().getPosition();
582594
583595 for (String t : catchTypesForNull) {
584596 int catchSize = Util.getSizeOfSurroundingTryBlock(constantPool, code, t, position);
585 if (catchSize < Integer.MAX_VALUE)
597 if (catchSize < Integer.MAX_VALUE) {
586598 return true;
599 }
587600 }
588601
589602 return false;
592605 private boolean safeCallToPrimateParseMethod(XMethod calledMethod, Location location) {
593606 int position = location.getHandle().getPosition();
594607
595 if (calledMethod.getClassName().equals("java.lang.Integer")) {
608 if ("java.lang.Integer".equals(calledMethod.getClassName())) {
596609
597610 ConstantPool constantPool = classContext.getJavaClass().getConstantPool();
598611 Code code = method.getCode();
600613 int catchSize;
601614
602615 catchSize = Util.getSizeOfSurroundingTryBlock(constantPool, code, "java/lang/NumberFormatException", position);
603 if (catchSize < Integer.MAX_VALUE)
616 if (catchSize < Integer.MAX_VALUE) {
604617 return true;
618 }
605619 catchSize = Util.getSizeOfSurroundingTryBlock(constantPool, code, "java/lang/IllegalArgumentException", position);
606 if (catchSize < Integer.MAX_VALUE)
620 if (catchSize < Integer.MAX_VALUE) {
607621 return true;
622 }
608623
609624 catchSize = Util.getSizeOfSurroundingTryBlock(constantPool, code, "java/lang/RuntimeException", position);
610 if (catchSize < Integer.MAX_VALUE)
625 if (catchSize < Integer.MAX_VALUE) {
611626 return true;
627 }
612628 catchSize = Util.getSizeOfSurroundingTryBlock(constantPool, code, "java/lang/Exception", position);
613 if (catchSize < Integer.MAX_VALUE)
629 if (catchSize < Integer.MAX_VALUE) {
614630 return true;
631 }
615632 }
616633 return false;
617634 }
618635
619636 private void checkUnconditionallyDereferencedParam(Location location, ConstantPoolGen cpg, TypeDataflow typeDataflow,
620637 InvokeInstruction invokeInstruction, BitSet nullArgSet, BitSet definitelyNullArgSet)
621 throws DataflowAnalysisException, ClassNotFoundException {
622
623 if (inExplictCatchNullBlock(location))
624 return;
638 throws DataflowAnalysisException, ClassNotFoundException {
639
640 if (inExplictCatchNullBlock(location)) {
641 return;
642 }
625643 boolean caught = inIndirectCatchNullBlock(location);
626 if (caught && skipIfInsideCatchNull())
627 return;
644 if (caught && skipIfInsideCatchNull()) {
645 return;
646 }
628647
629648 // See what methods might be called here
630649 XMethod calledMethod = XFactory.createXMethod(invokeInstruction, cpg);
634653 nullArgSet = (BitSet) nullArgSet.clone();
635654 definitelyNullArgSet = (BitSet) definitelyNullArgSet.clone();
636655 ClassDescriptor nonnullClassDesc = DescriptorFactory.createClassDescriptor(javax.annotation.Nonnull.class);
637 TypeQualifierValue nonnullTypeQualifierValue = TypeQualifierValue.getValue(nonnullClassDesc, null);
656 TypeQualifierValue<?> nonnullTypeQualifierValue = TypeQualifierValue.getValue(nonnullClassDesc, null);
638657 for (int i = nullArgSet.nextSetBit(0); i >= 0; i = nullArgSet.nextSetBit(i + 1)) {
639658 TypeQualifierAnnotation tqa = TypeQualifierApplications.getEffectiveTypeQualifierAnnotation(calledMethod, i,
640659 nonnullTypeQualifierValue);
662681 }
663682
664683 ParameterProperty property = unconditionalDerefParamDatabase.getProperty(targetMethod.toMethodDescriptor());
665 if (property == null)
684 if (property == null) {
666685 continue;
686 }
667687 if (DEBUG_NULLARG) {
668688 System.out.println("\tUnconditionally dereferenced params: " + property);
669689 }
670690
671691 BitSet targetUnconditionallyDereferencedNullArgSet = property.getMatchingParameters(nullArgSet);
672692
673 if (targetUnconditionallyDereferencedNullArgSet.isEmpty())
693 if (targetUnconditionallyDereferencedNullArgSet.isEmpty()) {
674694 continue;
695 }
675696
676697 dangerousCallTargetList.add(targetMethod);
677698
678699 unconditionallyDereferencedNullArgSet.or(targetUnconditionallyDereferencedNullArgSet);
679700
680 if (!property.getMatchingParameters(definitelyNullArgSet).isEmpty())
701 if (!property.getMatchingParameters(definitelyNullArgSet).isEmpty()) {
681702 veryDangerousCallTargetList.add(targetMethod);
682 }
683
684 if (dangerousCallTargetList.isEmpty())
685 return;
703 }
704 }
705
706 if (dangerousCallTargetList.isEmpty()) {
707 return;
708 }
686709
687710 WarningPropertySet<WarningProperty> propertySet = new WarningPropertySet<WarningProperty>();
688711
715738 return;
716739 }
717740
718 if (caught)
741 if (caught) {
719742 priority++;
720 if (dangerousCallTargetList.size() > veryDangerousCallTargetList.size())
743 }
744 if (dangerousCallTargetList.size() > veryDangerousCallTargetList.size()) {
721745 priority++;
722 else
746 } else {
723747 propertySet.addProperty(NullArgumentWarningProperty.ACTUAL_PARAMETER_GUARANTEED_NULL);
748 }
724749 XMethod calledFrom = XFactory.createXMethod(classContext.getJavaClass(), method);
725750
726 if (safeCallToPrimateParseMethod(calledMethod, location))
727 return;
751 if (safeCallToPrimateParseMethod(calledMethod, location)) {
752 return;
753 }
728754 BugInstance warning = new BugInstance(this, bugType, priority).addClassAndMethod(classContext.getJavaClass(), method)
729755 .addMethod(calledMethod).describe(MethodAnnotation.METHOD_CALLED).addSourceLine(classContext, method, location);
730756
731 boolean uncallable = false;
757 // boolean uncallable = false;
732758 if (!AnalysisContext.currentXFactory().isCalledDirectlyOrIndirectly(calledFrom) && calledFrom.isPrivate()) {
733759
734760 propertySet.addProperty(GeneralWarningProperty.IN_UNCALLABLE_METHOD);
735 uncallable = true;
761 // uncallable = true;
736762 }
737763 // Check which params might be null
738764 addParamAnnotations(location, definitelyNullArgSet, unconditionallyDereferencedNullArgSet, propertySet, warning);
739765
740 if (bugType.equals("NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS")) {
766 if ("NP_NULL_PARAM_DEREF_ALL_TARGETS_DANGEROUS".equals(bugType)) {
741767 // Add annotations for dangerous method call targets
742768 for (JavaClassAndMethod dangerousCallTarget : veryDangerousCallTargetList) {
743769 warning.addMethod(dangerousCallTarget).describe(MethodAnnotation.METHOD_DANGEROUS_TARGET_ACTUAL_GUARANTEED_NULL);
786812 for (int i = violatedParamSet.nextSetBit(0); i >= 0; i = violatedParamSet.nextSetBit(i + 1)) {
787813 boolean definitelyNull = definitelyNullArgSet.get(i);
788814
789 if (definitelyNull)
815 if (definitelyNull) {
790816 propertySet.addProperty(NullArgumentWarningProperty.ARG_DEFINITELY_NULL);
817 }
791818 ValueNumber valueNumber = null;
792 if (vnaFrame != null)
819 if (vnaFrame != null) {
793820 try {
794821 valueNumber = vnaFrame.getArgument(instruction, classContext.getConstantPoolGen(), i, sigParser);
795822 BugAnnotation variableAnnotation = ValueNumberSourceInfo.findAnnotationFromValueNumber(method, location,
798825 } catch (DataflowAnalysisException e) {
799826 AnalysisContext.logError("error", e);
800827 }
828 }
801829
802830 // Note: we report params as being indexed starting from 1, not
803831 // 0
820848 private void checkNonNullParam(Location location, ConstantPoolGen cpg, TypeDataflow typeDataflow,
821849 InvokeInstruction invokeInstruction, BitSet nullArgSet, BitSet definitelyNullArgSet) {
822850
823 if (inExplictCatchNullBlock(location))
824 return;
851 if (inExplictCatchNullBlock(location)) {
852 return;
853 }
825854 boolean caught = inIndirectCatchNullBlock(location);
826 if (caught && skipIfInsideCatchNull())
827 return;
855 if (caught && skipIfInsideCatchNull()) {
856 return;
857 }
828858
829859 XMethod m = XFactory.createXMethod(invokeInstruction, cpg);
830860
854884 }
855885
856886 int priority = definitelyNull ? HIGH_PRIORITY : NORMAL_PRIORITY;
857 if (caught)
887 if (caught) {
858888 priority++;
859 if (m.isPrivate() && priority == HIGH_PRIORITY)
889 }
890 if (m.isPrivate() && priority == HIGH_PRIORITY) {
860891 priority = NORMAL_PRIORITY;
892 }
861893 String description = definitelyNull ? "INT_NULL_ARG" : "INT_MAYBE_NULL_ARG";
862894 WarningPropertySet<WarningProperty> propertySet = new WarningPropertySet<WarningProperty>();
863895 Set<Location> derefLocationSet = Collections.singleton(location);
866898
867899 boolean duplicated = isDuplicated(propertySet, location.getHandle().getPosition(), false);
868900
869 if (duplicated)
901 if (duplicated) {
870902 return;
903 }
871904 BugInstance warning = new BugInstance(this, "NP_NONNULL_PARAM_VIOLATION", priority)
872 .addClassAndMethod(classContext.getJavaClass(), method).addMethod(m)
873 .describe(MethodAnnotation.METHOD_CALLED).addParameterAnnotation(i, description)
874 .addOptionalAnnotation(variableAnnotation).addSourceLine(classContext, method, location);
905 .addClassAndMethod(classContext.getJavaClass(), method).addMethod(m)
906 .describe(MethodAnnotation.METHOD_CALLED).addParameterAnnotation(i, description)
907 .addOptionalAnnotation(variableAnnotation).addSourceLine(classContext, method, location);
875908
876909 propertySet.decorateBugInstance(warning);
877910 bugReporter.reportBug(warning);
880913
881914 }
882915
916 @Override
883917 public void report() {
884918 }
885919
893927 * {@link #foundNullDeref(Location,ValueNumber,IsNullValue,ValueNumberFrame,boolean)}
894928 * instead
895929 */
930 @Override
896931 @Deprecated
897932 public void foundNullDeref(Location location, ValueNumber valueNumber, IsNullValue refValue, ValueNumberFrame vnaFrame) {
898933 foundNullDeref(location, valueNumber, refValue, vnaFrame, true);
899934 }
900935
936 @Override
901937 public void foundNullDeref(Location location, ValueNumber valueNumber, IsNullValue refValue, ValueNumberFrame vnaFrame,
902938 boolean isConsistent) {
903939 WarningPropertySet<WarningProperty> propertySet = new WarningPropertySet<WarningProperty>();
904 if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT))
905 return;
940 if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) {
941 return;
942 }
906943
907944 boolean onExceptionPath = refValue.isException();
908945 if (onExceptionPath) {
915952 Instruction ins = location.getHandle().getInstruction();
916953 if (ins instanceof InvokeInstruction && refValue.isDefinitelyNull()) {
917954 InvokeInstruction iins = (InvokeInstruction) ins;
918 if (iins.getMethodName(classContext.getConstantPoolGen()).equals("close")
919 && iins.getSignature(classContext.getConstantPoolGen()).equals("()V"))
955 if ("close".equals(iins.getMethodName(classContext.getConstantPoolGen()))
956 && "()V".equals(iins.getSignature(classContext.getConstantPoolGen()))) {
920957 propertySet.addProperty(NullDerefProperty.CLOSING_NULL);
958 }
921959 }
922960 boolean duplicated = isDuplicated(propertySet, pc, isConsistent);
923961
924 if (inExplictCatchNullBlock(location))
925 return;
962 if (inExplictCatchNullBlock(location)) {
963 return;
964 }
926965 boolean caught = inIndirectCatchNullBlock(location);
927 if (caught && skipIfInsideCatchNull())
928 return;
966 if (caught && skipIfInsideCatchNull()) {
967 return;
968 }
929969
930970 if (refValue.isDefinitelyNull()) {
931971 String type = "NP_ALWAYS_NULL";
932972 if (propertySet.containsProperty(NullDerefProperty.CLOSING_NULL)
933 && !propertySet.containsProperty(NullDerefProperty.DEREFS_ARE_CLONED))
973 && !propertySet.containsProperty(NullDerefProperty.DEREFS_ARE_CLONED)) {
934974 type = "NP_CLOSING_NULL";
935 else if (onExceptionPath)
975 } else if (onExceptionPath) {
936976 type = "NP_ALWAYS_NULL_EXCEPTION";
937 else if (duplicated)
977 } else if (duplicated) {
938978 type = "NP_NULL_ON_SOME_PATH";
979 }
939980 int priority = onExceptionPath ? NORMAL_PRIORITY : HIGH_PRIORITY;
940 if (caught)
981 if (caught) {
941982 priority++;
983 }
942984 reportNullDeref(propertySet, location, type, priority, variable);
943985 } else if (refValue.mightBeNull() && refValue.isParamValue()) {
944986
945987 String type;
946988 int priority = NORMAL_PRIORITY;
947 if (caught)
989 if (caught) {
948990 priority++;
949
950 if (method.getName().equals("equals") && method.getSignature().equals("(Ljava/lang/Object;)Z")) {
951 if (caught)
991 }
992
993 if ("equals".equals(method.getName()) && "(Ljava/lang/Object;)Z".equals(method.getSignature())) {
994 if (caught) {
952995 return;
996 }
953997 type = "NP_EQUALS_SHOULD_HANDLE_NULL_ARGUMENT";
954998
955 } else
999 } else {
9561000 type = "NP_ARGUMENT_MIGHT_BE_NULL";
957
958 if (DEBUG)
1001 }
1002
1003 if (DEBUG) {
9591004 System.out.println("Reporting null on some path: value=" + refValue);
1005 }
9601006
9611007 reportNullDeref(propertySet, location, type, priority, variable);
9621008 }
9631009 }
9641010
965 /**
966 * @param propertySet
967 * @param pc
968 * @param isConsistent
969 * @return
970 */
9711011 public boolean isDuplicated(WarningPropertySet<WarningProperty> propertySet, int pc, boolean isConsistent) {
9721012 boolean duplicated = false;
9731013 if (!isConsistent) {
974 if (propertySet.containsProperty(NullDerefProperty.DEREFS_ARE_CLONED))
1014 if (propertySet.containsProperty(NullDerefProperty.DEREFS_ARE_CLONED)) {
9751015 duplicated = true;
976
977 else
1016 } else {
9781017 try {
9791018 CFG cfg = classContext.getCFG(method);
9801019 if (cfg.getLocationsContainingInstructionWithOffset(pc).size() > 1) {
9841023 } catch (CFGBuilderException e) {
9851024 AnalysisContext.logError("Error while analyzing " + classContext.getFullyQualifiedMethodName(method), e);
9861025 }
1026 }
9871027 }
9881028 return duplicated;
9891029 }
9931033 BugAnnotation variable) {
9941034
9951035 BugInstance bugInstance = new BugInstance(this, type, priority).addClassAndMethod(classContext.getJavaClass(), method);
996 if (variable != null)
1036 if (variable != null) {
9971037 bugInstance.add(variable);
998 else
1038 } else {
9991039 bugInstance.add(new LocalVariableAnnotation("?", -1, -1));
1040 }
10001041 bugInstance.addSourceLine(classContext, method, location).describe("SOURCE_LINE_DEREF");
10011042
10021043 if (FindBugsAnalysisFeatures.isRelaxedMode()) {
10131054 InstructionHandle ins = target.getFirstInstruction();
10141055 int maxCount = 7;
10151056 while (ins != null) {
1016 if (maxCount-- <= 0)
1057 if (maxCount-- <= 0) {
10171058 break;
1059 }
10181060 Instruction i = ins.getInstruction();
10191061 if (i instanceof ATHROW) {
10201062 return true;
10211063 }
1022 if (i instanceof InstructionTargeter || i instanceof ReturnInstruction)
1064 if (i instanceof InstructionTargeter || i instanceof ReturnInstruction) {
10231065 return false;
1066 }
10241067 ins = ins.getNext();
10251068 }
10261069 return false;
10271070 }
10281071
1072 @Override
10291073 public void foundRedundantNullCheck(Location location, RedundantBranch redundantBranch) {
10301074
10311075 boolean isChecked = redundantBranch.firstValue.isChecked();
10331077 boolean isParameter = redundantBranch.firstValue.isParamValue();
10341078
10351079 Location locationOfKaBoom = redundantBranch.firstValue.getLocationOfKaBoom();
1036 if (isParameter && !wouldHaveBeenAKaboom)
1037 return;
1080 if (isParameter && !wouldHaveBeenAKaboom) {
1081 return;
1082 }
10381083 boolean createdDeadCode = false;
10391084 boolean infeasibleEdgeSimplyThrowsException = false;
10401085 Edge infeasibleEdge = redundantBranch.infeasibleEdge;
10411086 if (infeasibleEdge != null) {
1042 if (DEBUG)
1087 if (DEBUG) {
10431088 System.out.println("Check if " + redundantBranch + " creates dead code");
1089 }
10441090 BasicBlock target = infeasibleEdge.getTarget();
10451091
1046 if (DEBUG)
1092 if (DEBUG) {
10471093 System.out.println("Target block is "
10481094 + (target.isExceptionThrower() ? " exception thrower" : " not exception thrower"));
1095 }
10491096 // If the block is empty, it probably doesn't matter that it was
10501097 // killed.
10511098 // FIXME: really, we should crawl the immediately reachable blocks
10561103 if (!empty) {
10571104 try {
10581105 if (classContext.getCFG(method).getNumIncomingEdges(target) > 1) {
1059 if (DEBUG)
1106 if (DEBUG) {
10601107 System.out.println("Target of infeasible edge has multiple incoming edges");
1108 }
10611109 empty = true;
10621110 }
10631111 } catch (CFGBuilderException e) {
10641112 assert true; // ignore it
10651113 }
10661114 }
1067 if (DEBUG)
1115 if (DEBUG) {
10681116 System.out.println("Target block is " + (empty ? "empty" : "not empty"));
1117 }
10691118
10701119 if (!empty) {
1071 if (isThrower(target))
1120 if (isThrower(target)) {
10721121 infeasibleEdgeSimplyThrowsException = true;
1122 }
10731123
10741124 }
10751125 if (!empty && !previouslyDeadBlocks.get(target.getLabel())) {
1076 if (DEBUG)
1126 if (DEBUG) {
10771127 System.out.println("target was alive previously");
1128 }
10781129 // Block was not dead before the null pointer analysis.
10791130 // See if it is dead now by inspecting the null value frame.
10801131 // If it's TOP, then the block became dead.
10811132 IsNullValueFrame invFrame = invDataflow.getStartFact(target);
10821133 createdDeadCode = invFrame.isTop();
1083 if (DEBUG)
1134 if (DEBUG) {
10841135 System.out.println("target is now " + (createdDeadCode ? "dead" : "alive"));
1136 }
10851137
10861138 }
10871139 }
11131165 valueIsNull = false;
11141166 priority = isChecked ? HIGH_PRIORITY : NORMAL_PRIORITY;
11151167 }
1116 if (infeasibleEdgeSimplyThrowsException)
1168 if (infeasibleEdgeSimplyThrowsException) {
11171169 priority++;
1170 }
11181171
11191172 } else {
1120 if (stack != null)
1173 if (stack != null) {
11211174 item2 = stack.getStackItem(1);
1175 }
11221176 boolean bothNull = redundantBranch.firstValue.isDefinitelyNull() && redundantBranch.secondValue.isDefinitelyNull();
1123 if (redundantBranch.secondValue.isChecked())
1177 if (redundantBranch.secondValue.isChecked()) {
11241178 isChecked = true;
1179 }
11251180 if (redundantBranch.secondValue.wouldHaveBeenAKaboom()) {
11261181 wouldHaveBeenAKaboom = true;
11271182 locationOfKaBoom = redundantBranch.secondValue.getLocationOfKaBoom();
11391194 if (wouldHaveBeenAKaboom) {
11401195 priority = HIGH_PRIORITY;
11411196 warning = "RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE";
1142 if (locationOfKaBoom == null)
1197 if (locationOfKaBoom == null) {
11431198 throw new NullPointerException("location of KaBoom is null");
1144 }
1145
1146 if (DEBUG)
1199 }
1200 }
1201
1202 if (DEBUG) {
11471203 System.out.println(createdDeadCode + " " + infeasibleEdgeSimplyThrowsException + " " + valueIsNull + " " + priority);
1204 }
11481205 if (createdDeadCode && !infeasibleEdgeSimplyThrowsException) {
11491206 priority += 0;
11501207 } else if (createdDeadCode && infeasibleEdgeSimplyThrowsException) {
11511208 // throw clause
1152 if (valueIsNull)
1209 if (valueIsNull) {
11531210 priority += 0;
1154 else
1211 } else {
11551212 priority += 1;
1213 }
11561214 } else {
11571215 // didn't create any dead code
11581216 priority += 1;
11591217 }
11601218
1161
1219
11621220 if (DEBUG) {
1163 System.out.println("RCN" + priority + " " + redundantBranch.firstValue + " =? " + redundantBranch.secondValue + " : "
1221 System.out.println("RCN " + priority + " " + redundantBranch.firstValue + " =? " + redundantBranch.secondValue + " : "
11641222 + warning);
11651223
1166 if (isChecked)
1224 if (isChecked) {
11671225 System.out.println("isChecked");
1168 if (wouldHaveBeenAKaboom)
1226 }
1227 if (wouldHaveBeenAKaboom) {
11691228 System.out.println("wouldHaveBeenAKaboom");
1170 if (createdDeadCode)
1229 }
1230 if (createdDeadCode) {
11711231 System.out.println("createdDeadCode");
1172 }
1173 if (priority > LOW_PRIORITY)
1174 return;
1232 }
1233 }
1234 if (priority > LOW_PRIORITY) {
1235 return;
1236 }
11751237 BugAnnotation variableAnnotation = null;
11761238 try {
11771239 // Get the value number
11801242 Instruction ins = location.getHandle().getInstruction();
11811243
11821244 ValueNumber valueNumber = vnaFrame.getInstance(ins, classContext.getConstantPoolGen());
1183 if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT))
1245 if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) {
11841246 return;
1247 }
11851248 variableAnnotation = ValueNumberSourceInfo.findAnnotationFromValueNumber(method, location, valueNumber, vnaFrame,
11861249 "VALUE_OF");
11871250 if (variableAnnotation instanceof LocalVariableAnnotation) {
11881251 LocalVariableAnnotation local = (LocalVariableAnnotation) variableAnnotation;
11891252 if (!local.isNamed()) {
1190 if (warning.equals("RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"))
1253 if ("RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE".equals(warning)) {
11911254 return;
1255 }
11921256 priority++;
11931257 }
11941258 }
12061270 BugInstance.getFieldOrMethodValueSource(item1), BugInstance.getFieldOrMethodValueSource(item2));
12071271
12081272 if (!foundSource) {
1209 if (warning.equals("RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE"))
1273 if ("RCN_REDUNDANT_NULLCHECK_OF_NULL_VALUE".equals(warning)) {
12101274 return;
1275 }
12111276 bugInstance.setPriority(priority+1);
12121277 bugInstance.add(fallback);
12131278 }
1214 if (wouldHaveBeenAKaboom)
1279 if (wouldHaveBeenAKaboom) {
12151280 bugInstance.addSourceLine(classContext, method, locationOfKaBoom);
1281 }
12161282
12171283 if (FindBugsAnalysisFeatures.isRelaxedMode()) {
12181284 WarningPropertySet<WarningProperty> propertySet = new WarningPropertySet<WarningProperty>();
12191285 WarningPropertyUtil.addPropertiesForDataMining(propertySet, classContext, method, location);
1220 if (isChecked)
1286 if (isChecked) {
12211287 propertySet.addProperty(NullDerefProperty.CHECKED_VALUE);
1222 if (wouldHaveBeenAKaboom)
1288 }
1289 if (wouldHaveBeenAKaboom) {
12231290 propertySet.addProperty(NullDerefProperty.WOULD_HAVE_BEEN_A_KABOOM);
1224 if (createdDeadCode)
1291 }
1292 if (createdDeadCode) {
12251293 propertySet.addProperty(NullDerefProperty.CREATED_DEAD_CODE);
1294 }
12261295
12271296 propertySet.decorateBugInstance(bugInstance);
12281297 }
12411310 Instruction ins = location.getHandle().getInstruction();
12421311
12431312 ValueNumber valueNumber = vnaFrame.getInstance(ins, classContext.getConstantPoolGen());
1244 if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT))
1313 if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) {
12451314 return null;
1315 }
12461316 variableAnnotation = ValueNumberSourceInfo.findAnnotationFromValueNumber(method, location, valueNumber, vnaFrame,
12471317 "VALUE_OF");
12481318
12691339
12701340 int minPC(Collection<Location> locs) {
12711341 int result = 1000000;
1272 for (Location l : locs)
1273 if (result > l.getHandle().getPosition())
1342 for (Location l : locs) {
1343 if (result > l.getHandle().getPosition()) {
12741344 result = l.getHandle().getPosition();
1345 }
1346 }
12751347 return result;
12761348 }
12771349
12781350 int maxPC(Collection<Location> locs) {
12791351 int result = -1000000;
1280 for (Location l : locs)
1281 if (result < l.getHandle().getPosition())
1352 for (Location l : locs) {
1353 if (result < l.getHandle().getPosition()) {
12821354 result = l.getHandle().getPosition();
1355 }
1356 }
12831357 return result;
12841358 }
12851359
12951369 int pos = h.getPosition();
12961370
12971371 if (ln == null) {
1298 if (pos > firstPos + 15)
1372 if (pos > firstPos + 15) {
12991373 break;
1374 }
13001375 } else {
13011376 int line = ln.getSourceLine(pos);
1302 if (line != firstLine)
1377 if (line != firstLine) {
13031378 break;
1379 }
13041380 }
13051381 Instruction i = h.getInstruction();
13061382 if (i instanceof InvokeInstruction) {
13071383 InvokeInstruction ii = (InvokeInstruction) i;
13081384 String name = ii.getMethodName(classContext.getConstantPoolGen());
1309 if (name.startsWith("check") || name.startsWith("assert"))
1385 if (name.startsWith("check") || name.startsWith("assert")) {
13101386 return true;
1387 }
13111388 }
13121389 h = h.getNext();
13131390 }
13221399 * foundGuaranteedNullDeref(java.util.Set, java.util.Set,
13231400 * edu.umd.cs.findbugs.ba.vna.ValueNumber, boolean)
13241401 */
1402 @Override
13251403 public void foundGuaranteedNullDeref(@Nonnull
1326 Set<Location> assignedNullLocationSet, @Nonnull
1327 Set<Location> derefLocationSet, SortedSet<Location> doomedLocations, ValueNumberDataflow vna, ValueNumber refValue,
1404 Set<Location> assignedNullLocationSet, @Nonnull
1405 Set<Location> derefLocationSet, SortedSet<Location> doomedLocations, ValueNumberDataflow vna, ValueNumber refValue,
13281406 @CheckForNull
13291407 BugAnnotation variableAnnotation, NullValueUnconditionalDeref deref, boolean npeIfStatementCovered) {
1330 if (refValue.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT))
1331 return;
1408 if (refValue.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) {
1409 return;
1410 }
13321411
13331412 if (DEBUG) {
13341413 System.out.println("Found guaranteed null deref in " + method.getName());
1335 for (Location loc : doomedLocations)
1414 for (Location loc : doomedLocations) {
13361415 System.out.println("Doomed at " + loc);
1416 }
13371417 }
13381418
13391419 String bugType;
13411421 int priority = npeIfStatementCovered ? HIGH_PRIORITY : NORMAL_PRIORITY;
13421422
13431423 if (deref.isMethodReturnValue()) {
1344 if (deref.isReadlineValue())
1424 if (deref.isReadlineValue()) {
13451425 bugType = "NP_DEREFERENCE_OF_READLINE_VALUE";
1346 else
1426 } else {
13471427 bugType = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE";
1348 } else if (derefLocationSet.size() > 1)
1349 if (!deref.isAlwaysOnExceptionPath())
1350 bugType = "NP_GUARANTEED_DEREF";
1351 else
1352 bugType = "NP_GUARANTEED_DEREF_ON_EXCEPTION_PATH";
1353 else if (!deref.isAlwaysOnExceptionPath())
1354 bugType = "NP_NULL_ON_SOME_PATH";
1355 else
1356 bugType = "NP_NULL_ON_SOME_PATH_EXCEPTION";
1428 }
1429 } else if (derefLocationSet.size() > 1) {
1430 if (!deref.isAlwaysOnExceptionPath()) {
1431 bugType = "NP_GUARANTEED_DEREF";
1432 } else {
1433 bugType = "NP_GUARANTEED_DEREF_ON_EXCEPTION_PATH";
1434 }
1435 } else if (!deref.isAlwaysOnExceptionPath()) {
1436 bugType = "NP_NULL_ON_SOME_PATH";
1437 } else {
1438 bugType = "NP_NULL_ON_SOME_PATH_EXCEPTION";
1439 }
13571440
13581441 boolean allCallToAssertionMethod = !doomedLocations.isEmpty();
13591442 for (Location loc : doomedLocations) {
1360 if (!callToAssertionMethod(loc))
1443 if (!callToAssertionMethod(loc)) {
13611444 allCallToAssertionMethod = false;
1362 }
1363 if (allCallToAssertionMethod)
1364 return;
1445 }
1446 }
1447 if (allCallToAssertionMethod) {
1448 return;
1449 }
13651450
13661451 // Add Locations in the set of locations at least one of which
13671452 // is guaranteed to be dereferenced
13681453
13691454 SortedSet<Location> sourceLocations;
1370 if (doomedLocations.isEmpty() || doomedLocations.size() > 3 && doomedLocations.size() > assignedNullLocationSet.size())
1455 if (doomedLocations.isEmpty() || doomedLocations.size() > 3 && doomedLocations.size() > assignedNullLocationSet.size()) {
13711456 sourceLocations = new TreeSet<Location>(assignedNullLocationSet);
1372 else
1457 } else {
13731458 sourceLocations = doomedLocations;
1374
1375 if (doomedLocations.isEmpty() || derefLocationSet.isEmpty())
1376 return;
1459 }
1460
1461 if (doomedLocations.isEmpty() || derefLocationSet.isEmpty()) {
1462 return;
1463 }
13771464
13781465 WarningPropertySet<WarningProperty> propertySet = new WarningPropertySet<WarningProperty>();
13791466
13831470 int distance1 = minDereferencePC - maxPC(assignedNullLocationSet);
13841471 int distance2 = minDereferencePC - maxPC(doomedLocations);
13851472 int distance = Math.max(distance1, distance2);
1386 if (false)
1473 /*
1474 if (false) {
13871475 System.out.printf("%9d %9d %9d RANGE %s.%s%s\n", distance, distance1, distance2, classContext.getClassDescriptor()
13881476 .toDottedClassName(), method.getName(), method.getSignature());
1477 }
1478 */
13891479
13901480 // Create BugInstance
13911481
13941484 SortedSet<SourceLineAnnotation> knownNullLocations = new TreeSet<SourceLineAnnotation>();
13951485 for (Location loc : sourceLocations) {
13961486 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(classContext, method, loc);
1397 if (sourceLineAnnotation == null)
1487 if (sourceLineAnnotation == null) {
13981488 continue;
1489 }
13991490 int startLine = sourceLineAnnotation.getStartLine();
1400 if (startLine == -1)
1491 if (startLine == -1) {
14011492 knownNullLocations.add(sourceLineAnnotation);
1402 else if (!knownNull.get(startLine)) {
1493 } else if (!knownNull.get(startLine)) {
14031494 knownNull.set(startLine);
14041495 knownNullLocations.add(sourceLineAnnotation);
14051496 }
14231514 AnalysisContext.logError("Error getting UsagesRequiringNonNullValues for " + method, e);
14241515 }
14251516
1426 if (pu == null)
1517 if (pu == null) {
14271518 assert true; // nothing to do
1428 else if (deref.isReadlineValue()) {
1519 } else if (deref.isReadlineValue()) {
14291520 bugType = "NP_DEREFERENCE_OF_READLINE_VALUE";
14301521 priority = NORMAL_PRIORITY;
14311522 } else if (deref.isMethodReturnValue() && !deref.isReadlineValue()) {
14351526 bugType = "NP_NONNULL_RETURN_VIOLATION";
14361527 String methodName = method.getName();
14371528 String methodSig = method.getSignature();
1438 if (methodName.equals("clone") && methodSig.equals("()Ljava/lang/Object;")) {
1529 if ("clone".equals(methodName) && "()Ljava/lang/Object;".equals(methodSig)) {
14391530 bugType = "NP_CLONE_COULD_RETURN_NULL";
14401531 priority = NORMAL_PRIORITY;
1441 } else if (methodName.equals("toString") && methodSig.equals("()Ljava/lang/String;")) {
1532 } else if ("toString".equals(methodName) && "()Ljava/lang/String;".equals(methodSig)) {
14421533 bugType = "NP_TOSTRING_COULD_RETURN_NULL";
14431534 priority = NORMAL_PRIORITY;
14441535 }
14521543 XMethodParameter mp = pu.getNonNullParameter();
14531544 if (mp != null) {
14541545 invokedXMethod = mp.getMethod();
1455 for (Location derefLoc : derefLocationSet)
1456 if (safeCallToPrimateParseMethod(invokedXMethod, derefLoc))
1546 for (Location derefLoc : derefLocationSet) {
1547 if (safeCallToPrimateParseMethod(invokedXMethod, derefLoc)) {
14571548 return;
1549 }
1550 }
14581551 invokedMethod = MethodAnnotation.fromXMethod(mp.getMethod());
14591552 if (mp.getParameterNumber() == 0
1460 && TypeQualifierNullnessAnnotationDatabase.assertsFirstParameterIsNonnull(invokedXMethod))
1553 && TypeQualifierNullnessAnnotationDatabase.assertsFirstParameterIsNonnull(invokedXMethod)) {
14611554 return;
1555 }
14621556 parameterNumber = mp.getParameterNumber();
14631557 bugType = "NP_NULL_PARAM_DEREF";
14641558 }
14681562
14691563 boolean hasManyNullTests = true;
14701564 for (SourceLineAnnotation sourceLineAnnotation : knownNullLocations) {
1471 if (!hasManyPreceedingNullTests(sourceLineAnnotation.getStartBytecode()))
1565 if (!hasManyPreceedingNullTests(sourceLineAnnotation.getStartBytecode())) {
14721566 hasManyNullTests = false;
1567 }
14731568 }
14741569 if (hasManyNullTests) {
1475 if (bugType.equals("NP_NULL_ON_SOME_PATH") || bugType.equals("NP_GUARANTEED_DEREF"))
1570 if ("NP_NULL_ON_SOME_PATH".equals(bugType) || "NP_GUARANTEED_DEREF".equals(bugType)) {
14761571 bugType = "NP_NULL_ON_SOME_PATH_MIGHT_BE_INFEASIBLE";
1477 else
1572 } else {
14781573 priority++;
1574 }
14791575 }
14801576
14811577 BugInstance bugInstance = new BugInstance(this, bugType, priority).addClassAndMethod(classContext.getJavaClass(), method);
14821578 if (invokedMethod != null) {
14831579 assert invokedXMethod != null;
14841580 XMethod i = invokedXMethod.resolveAccessMethodForMethod();
1485 if (i != invokedXMethod)
1581 if (i != invokedXMethod) {
14861582 bugInstance.addMethod(i).describe(MethodAnnotation.METHOD_CALLED);
1487 else
1583 } else {
14881584 bugInstance.addMethod(invokedMethod).describe(MethodAnnotation.METHOD_CALLED)
1489 .addParameterAnnotation(parameterNumber, "INT_MAYBE_NULL_ARG");
1490 }
1491 if (storedField != null)
1585 .addParameterAnnotation(parameterNumber, "INT_MAYBE_NULL_ARG");
1586 }
1587 }
1588 if (storedField != null) {
14921589 bugInstance.addField(storedField).describe("FIELD_STORED");
1590 }
14931591 bugInstance.addOptionalAnnotation(variableAnnotation);
1494 if (variableAnnotation instanceof FieldAnnotation)
1592 if (variableAnnotation instanceof FieldAnnotation) {
14951593 bugInstance.describe("FIELD_CONTAINS_VALUE");
1594 }
14961595
14971596 addPropertiesForDereferenceLocations(propertySet, derefLocationSet, false);
14981597
1499 if (deref.isAlwaysOnExceptionPath())
1598 if (deref.isAlwaysOnExceptionPath()) {
15001599 propertySet.addProperty(NullDerefProperty.ALWAYS_ON_EXCEPTION_PATH);
1501
1502 if (!assignedNullLocationSet.isEmpty() && distance > 100)
1600 }
1601
1602 if (!assignedNullLocationSet.isEmpty() && distance > 100) {
15031603 propertySet.addProperty(NullDerefProperty.LONG_RANGE_NULL_SOURCE);
1604 }
15041605
15051606 propertySet.decorateBugInstance(bugInstance);
15061607
1507 if (bugType.equals("NP_DEREFERENCE_OF_READLINE_VALUE")) {
1608 if ("NP_DEREFERENCE_OF_READLINE_VALUE".equals(bugType)) {
15081609
15091610 int source = -9999;
1510 if (knownNullLocations.size() == 1)
1611 if (knownNullLocations.size() == 1) {
15111612 source = knownNullLocations.iterator().next().getEndBytecode();
1613 }
15121614 for (Location loc : derefLocationSet) {
15131615 int pos = loc.getHandle().getPosition();
1514 if (pos != source + 3) // immediate dereferences are handled by
1616 if (pos != source + 3) {
15151617 // another detector
15161618 bugAccumulator.accumulateBug(bugInstance,
15171619 SourceLineAnnotation.fromVisitedInstruction(classContext, method, loc));
1620 }
15181621 }
15191622
15201623 } else {
1521 for (Location loc : derefLocationSet)
1624 for (Location loc : derefLocationSet) {
15221625 bugInstance.addSourceLine(classContext, method, loc).describe(getDescription(loc, refValue));
1626 }
15231627
15241628 if (sourceLocations == doomedLocations && assignedNullLocationSet.size() == 1) {
15251629 Location assignedNull = assignedNullLocationSet.iterator().next();
15271631 assignedNull);
15281632 if (sourceLineAnnotation != null) {
15291633 int startLine = sourceLineAnnotation.getStartLine();
1530 if (startLine > 0 && !knownNull.get(startLine))
1634 if (startLine > 0 && !knownNull.get(startLine)) {
15311635 bugInstance.add(sourceLineAnnotation).describe("SOURCE_LINE_NULL_VALUE");
1636 }
15321637 }
15331638
15341639 }
15411646 }
15421647 }
15431648
1544 /**
1545 * @param propertySet
1546 * @param derefLocationSet
1547 * @param isConsistent
1548 * TODO
1549 */
15501649 private void addPropertiesForDereferenceLocations(WarningPropertySet<WarningProperty> propertySet,
15511650 Collection<Location> derefLocationSet, boolean isConsistent) {
15521651 boolean derefOutsideCatchBlock = false;
15571656 for (Location loc : derefLocationSet) {
15581657 if (!inExplictCatchNullBlock(loc)) {
15591658 derefOutsideCatchNullBlock = true;
1560 if (!inIndirectCatchNullBlock(loc))
1659 if (!inIndirectCatchNullBlock(loc)) {
15611660 derefOutsideCatchBlock = true;
1562 }
1563
1564 if (!isDoomed(loc))
1661 }
1662 }
1663
1664 if (!isDoomed(loc)) {
15651665 allDerefsAtDoomedLocations = false;
1666 }
15661667 }
15671668
15681669 if (!derefOutsideCatchNullBlock) {
15771678 boolean uniqueDereferenceLocations = uniqueLocations(derefLocationSet);
15781679
15791680 if (!derefOutsideCatchBlock) {
1580 if (!uniqueDereferenceLocations || skipIfInsideCatchNull())
1681 if (!uniqueDereferenceLocations || skipIfInsideCatchNull()) {
15811682 propertySet.addProperty(GeneralWarningProperty.FALSE_POSITIVE);
1582 else
1683 } else {
15831684 propertySet.addProperty(NullDerefProperty.DEREFS_IN_CATCH_BLOCKS);
1584 }
1585 if (!isConsistent && !uniqueDereferenceLocations)
1685 }
1686 }
1687 if (!isConsistent && !uniqueDereferenceLocations) {
15861688 // Add a WarningProperty
15871689 propertySet.addProperty(NullDerefProperty.DEREFS_ARE_CLONED);
1690 }
15881691
15891692 addPropertiesForMethodContainingWarning(propertySet);
15901693 }
15911694
1592 /**
1593 * @param derefLocationSet
1594 * @return
1595 */
15961695 private boolean uniqueLocations(Collection<Location> derefLocationSet) {
15971696 boolean uniqueDereferenceLocations = false;
15981697 CodeException[] exceptionTable = method.getCode().getExceptionTable();
1599 if (exceptionTable == null)
1698 if (exceptionTable == null) {
16001699 return true;
1700 }
16011701 checkForCatchAll: {
1602 for (CodeException e : exceptionTable)
1603 if (e.getCatchType() == 0)
1702 for (CodeException e : exceptionTable) {
1703 if (e.getCatchType() == 0) {
16041704 break checkForCatchAll;
1705 }
1706 }
16051707 return true;
16061708 }
16071709
16081710 LineNumberTable table = method.getLineNumberTable();
1609 if (table == null)
1711 if (table == null) {
16101712 uniqueDereferenceLocations = true;
1611 else {
1713 } else {
16121714 BitSet linesMentionedMultipleTimes = classContext.linesMentionedMultipleTimes(method);
16131715 for (Location loc : derefLocationSet) {
16141716 int lineNumber = table.getSourceLine(loc.getHandle().getPosition());
1615 if (lineNumber > 0 && !linesMentionedMultipleTimes.get(lineNumber))
1717 if (lineNumber > 0 && !linesMentionedMultipleTimes.get(lineNumber)) {
16161718 uniqueDereferenceLocations = true;
1719 }
16171720 }
16181721 }
16191722 return uniqueDereferenceLocations;
16201723 }
16211724
1622 /**
1623 * @param propertySet
1624 * @param xMethod
1625 */
16261725 private void addPropertiesForMethodContainingWarning(WarningPropertySet<WarningProperty> propertySet) {
16271726 XMethod xMethod = XFactory.createXMethod(classContext.getJavaClass(), method);
16281727
16291728 boolean uncallable = !AnalysisContext.currentXFactory().isCalledDirectlyOrIndirectly(xMethod) && xMethod.isPrivate();
16301729
1631 if (uncallable)
1730 if (uncallable) {
16321731 propertySet.addProperty(GeneralWarningProperty.IN_UNCALLABLE_METHOD);
1732 }
16331733 }
16341734
16351735 private boolean isDoomed(Location loc) {
16551755 try {
16561756 UsagesRequiringNonNullValues usages = classContext.getUsagesRequiringNonNullValues(method);
16571757 pu = usages.get(loc, refValue, vnaDataflow);
1658 if (pu == null)
1758 if (pu == null) {
16591759 return "SOURCE_LINE_DEREF";
1760 }
16601761 return pu.getDescription();
16611762 } catch (DataflowAnalysisException e) {
16621763 AnalysisContext.logError("Error getting UsagesRequiringNonNullValues for " + method, e);
16721773 int pc = loc.getHandle().getPosition();
16731774 int catchSize = Util.getSizeOfSurroundingTryBlock(classContext.getJavaClass().getConstantPool(), method.getCode(),
16741775 "java/lang/NullPointerException", pc);
1675 if (catchSize < Integer.MAX_VALUE)
1776 if (catchSize < Integer.MAX_VALUE) {
16761777 return true;
1778 }
16771779 return false;
16781780 }
16791781
16811783 int pc = loc.getHandle().getPosition();
16821784 int catchSize = Util.getSizeOfSurroundingTryBlock(classContext.getJavaClass().getConstantPool(), method.getCode(),
16831785 "java/lang/Exception", pc);
1684 if (catchSize < 5)
1786 if (catchSize < 5) {
16851787 return true;
1788 }
16861789 catchSize = Util.getSizeOfSurroundingTryBlock(classContext.getJavaClass().getConstantPool(), method.getCode(),
16871790 "java/lang/RuntimeException", pc);
1688 if (catchSize < 5)
1791 if (catchSize < 5) {
16891792 return true;
1793 }
16901794 catchSize = Util.getSizeOfSurroundingTryBlock(classContext.getJavaClass().getConstantPool(), method.getCode(),
16911795 "java/lang/Throwable", pc);
1692 if (catchSize < 5)
1796 if (catchSize < 5) {
16931797 return true;
1798 }
16941799 return false;
16951800 }
16961801 }
1697
1698 // vim:ts=4
6565 }
6666 }
6767
68 /*
69 * (non-Javadoc)
70 *
71 * @see edu.umd.cs.findbugs.bcel.OpcodeStackDetector#sawOpcode(int)
72 */
7368 @Override
7469 public void sawOpcode(int seen) {
7570 if (seen == IAND || seen == IOR) {
9085 // null guarantees a branch
9186 boolean nullGuaranteesZero = seen == IAND;
9287 boolean nullGuaranteesBranch = nullGuaranteesZero ^ (nextOpcode == IFNE);
93 if (DEBUG)
88 if (DEBUG) {
9489 System.out.println(item.getPC() + " null guarantees " + nullGuaranteesBranch + " branch");
90 }
9591 try {
9692 CFG cfg = getClassContext().getCFG(getMethod());
9793 Location produced = findLocation(cfg, item.getPC());
9894 Location branch = findLocation(cfg, getPC() + 1);
99 if (produced == null || branch == null)
95 if (produced == null || branch == null) {
10096 return;
97 }
10198
10299 IfInstruction branchInstruction = (IfInstruction) branch.getHandle().getInstruction();
103100
108105 ValueNumberFrame valueNumberFact = valueNumberDataflow.getFactAtLocation(produced);
109106 IsNullValueFrame isNullFact = isNullValueDataflow.getFactAtLocation(produced);
110107 ValueNumber value = valueNumberFact.getTopValue();
111 if (isNullFact.getTopValue().isDefinitelyNotNull())
108 if (isNullFact.getTopValue().isDefinitelyNotNull()) {
112109 return;
110 }
113111 if (DEBUG) {
114112 System.out.println("Produced: " + produced);
115113 System.out.println(valueNumberFact);
122120 }
123121 Location guaranteed = findLocation(cfg, nullGuaranteesBranch ? branchInstruction.getTarget() : branch.getHandle()
124122 .getNext());
125 if (guaranteed == null)
123 if (guaranteed == null) {
126124 return;
125 }
127126
128127 UnconditionalValueDerefSet unconditionalDeref = unconditionalValueDerefDataflow.getFactAtLocation(guaranteed);
129128 if (DEBUG) {
143142 bug = new BugInstance(this, "NP_GUARANTEED_DEREF", NORMAL_PRIORITY).addClassAndMethod(this);
144143 bug.addOptionalAnnotation(variableAnnotation);
145144 bug.addSourceLine(tested).describe("SOURCE_LINE_KNOWN_NULL");
146 for (Location dereferenced : unconditionalDerefLocationSet)
145 for (Location dereferenced : unconditionalDerefLocationSet) {
147146 bug.addSourceLine(getClassContext(), getMethod(), dereferenced).describe("SOURCE_LINE_DEREF");
147 }
148148
149149 } else {
150150 bug = new BugInstance(this, "NP_NULL_ON_SOME_PATH", NORMAL_PRIORITY).addClassAndMethod(this);
151151 bug.addOptionalAnnotation(variableAnnotation);
152 for (Location dereferenced : unconditionalDerefLocationSet)
152 for (Location dereferenced : unconditionalDerefLocationSet) {
153153 bug.addSourceLine(getClassContext(), getMethod(), dereferenced).describe("SOURCE_LINE_DEREF");
154 }
154155
155156 bug.addSourceLine(tested).describe("SOURCE_LINE_KNOWN_NULL");
156157
172173 Location findLocation(CFG cfg, int pc) {
173174 for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
174175 Location loc = i.next();
175 if (loc.getHandle().getPosition() == pc)
176 if (loc.getHandle().getPosition() == pc) {
176177 return loc;
178 }
177179 }
178180 return null;
179181 }
182184 Location findLocation(CFG cfg, InstructionHandle handle) {
183185 for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
184186 Location loc = i.next();
185 if (loc.getHandle() == handle)
187 if (loc.getHandle() == handle) {
186188 return loc;
189 }
187190 }
188191 return null;
189192 }
193196 || item.getSpecialKind() == OpcodeStack.Item.NONZERO_MEANS_NULL && seen == IOR;
194197 }
195198
199 /*
196200 private void emitWarning() {
197 System.out.println("Warn about " + getMethodName()); // TODO
198 }
201 System.out.println("Warn about " + getMethodName());
202 }
203 */
199204
200205 }
5858 * method, and are not closed on all paths out of the method. Note that "stream"
5959 * is a bit misleading, since we also use the detector to look for database
6060 * resources that aren't closed.
61 *
61 *
6262 * @author David Hovemeyer
6363 */
6464 public final class FindOpenStream extends ResourceTrackingDetector<Stream, StreamResourceTracker> implements StatelessDetector {
7676 * List of base classes of tracked resources.
7777 */
7878 static final ObjectType[] streamBaseList = { ObjectTypeFactory.getInstance("java.io.InputStream"),
79 ObjectTypeFactory.getInstance("java.io.OutputStream"), ObjectTypeFactory.getInstance("java.util.zip.ZipFile"),
80 ObjectTypeFactory.getInstance("java.io.Reader"), ObjectTypeFactory.getInstance("java.io.Writer"),
81 ObjectTypeFactory.getInstance("java.sql.Connection"),
82 ObjectTypeFactory.getInstance("java.sql.Statement"), ObjectTypeFactory.getInstance("java.sql.ResultSet") };
79 ObjectTypeFactory.getInstance("java.io.OutputStream"), ObjectTypeFactory.getInstance("java.util.zip.ZipFile"),
80 ObjectTypeFactory.getInstance("java.io.Reader"), ObjectTypeFactory.getInstance("java.io.Writer"),
81 ObjectTypeFactory.getInstance("java.sql.Connection"),
82 ObjectTypeFactory.getInstance("java.sql.Statement"), ObjectTypeFactory.getInstance("java.sql.ResultSet") };
8383
8484 /**
8585 * StreamFactory objects used to detect resources created within analyzed
9595 streamFactoryCollection.add(new IOStreamFactory("java.io.InputStream", new String[] { "java.io.ByteArrayInputStream",
9696 "java.io.StringBufferInputStream", "java.io.PipedInputStream" }, "OS_OPEN_STREAM"));
9797 streamFactoryCollection.add(new IOStreamFactory("java.io.OutputStream", new String[] { "java.io.ByteArrayOutputStream",
98 "java.io.PipedOutputStream" }, "OS_OPEN_STREAM"));
98 "java.io.PipedOutputStream" }, "OS_OPEN_STREAM"));
9999 streamFactoryCollection.add(new IOStreamFactory("java.io.Reader", new String[] { "java.io.StringReader",
100100 "java.io.CharArrayReader", "java.io.PipedReader" }, "OS_OPEN_STREAM"));
101101 streamFactoryCollection.add(new IOStreamFactory("java.io.Writer", new String[] { "java.io.StringWriter",
119119 "()Ljavax/servlet/ServletOutputStream;"));
120120 streamFactoryCollection.add(new MethodReturnValueStreamFactory("javax.servlet.ServletResponse", "getWriter",
121121 "()Ljava/io/PrintWriter;"));
122
122
123123 // Ignore System.{in,out,err}
124124 streamFactoryCollection.add(new StaticFieldLoadStreamFactory("java.io.InputStream", "java.lang.System", "in",
125125 "Ljava/io/InputStream;"));
207207 public final int priority;
208208
209209 public final Stream stream;
210
210
211211 @Override
212212 public String toString() {
213213 return stream.toString();
226226 * ----------------------------------------------------------------------
227227 */
228228
229 private List<PotentialOpenStream> potentialOpenStreamList;
229 private final List<PotentialOpenStream> potentialOpenStreamList;
230230
231231 /*
232232 * ----------------------------------------------------------------------
253253 // class containing one of these words, then we don't run the
254254 // detector on the class.
255255 private static final String[] PRESCREEN_CLASS_LIST = { "Stream", "Reader", "Writer", "ZipFile", "JarFile", "DriverManager",
256 "Connection", "Statement" };
256 "Connection", "Statement" };
257257
258258 /*
259259 * (non-Javadoc)
260 *
260 *
261261 * @see
262262 * edu.umd.cs.findbugs.Detector#visitClassContext(edu.umd.cs.findbugs.ba
263263 * .ClassContext)
287287 }
288288
289289 if (className != null) {
290 if (DEBUG)
290 if (DEBUG) {
291291 System.out.println("FindOpenStream: saw class " + className);
292 }
292293
293294 for (String aPRESCREEN_CLASS_LIST : PRESCREEN_CLASS_LIST) {
294295 if (className.indexOf(aPRESCREEN_CLASS_LIST) >= 0) {
308309 @Override
309310 public boolean prescreen(ClassContext classContext, Method method, boolean mightClose) {
310311 BitSet bytecodeSet = classContext.getBytecodeSet(method);
311 if (bytecodeSet == null)
312 if (bytecodeSet == null) {
312313 return false;
314 }
313315 return bytecodeSet.get(Constants.NEW) || bytecodeSet.get(Constants.INVOKEINTERFACE)
314316 || bytecodeSet.get(Constants.INVOKESPECIAL) || bytecodeSet.get(Constants.INVOKESTATIC)
315317 || bytecodeSet.get(Constants.INVOKEVIRTUAL);
321323 }
322324
323325 public static boolean isMainMethod(Method method) {
324 return method.isStatic() && method.getName().equals("main") && method.getSignature().equals("([Ljava/lang/String;)V");
326 return method.isStatic() && "main".equals(method.getName()) && "([Ljava/lang/String;)V".equals(method.getSignature());
325327 }
326328
327329 @Override
332334
333335 JavaClass javaClass = classContext.getJavaClass();
334336 MethodGen methodGen = classContext.getMethodGen(method);
335 if (methodGen == null)
337 if (methodGen == null) {
336338 return;
339 }
337340 CFG cfg = classContext.getCFG(method);
338341
339342 // Add Streams passed into the method as parameters.
348351 for (Type type : parameterTypeList) {
349352 if (type instanceof ObjectType) {
350353 ObjectType objectType = (ObjectType) type;
351
354
352355 for (ObjectType streamBase : streamBaseList) {
353356 if (Hierarchy.isSubtype(objectType, streamBase)) {
354357 // OK, found a parameter that is a resource.
402405 for (Iterator<Stream> i = resourceCollection.resourceIterator(); i.hasNext();) {
403406 Stream stream = i.next();
404407 StreamEquivalenceClass equivalenceClass = resourceTracker.getStreamEquivalenceClass(stream);
405 if (stream.isClosed())
408 if (stream.isClosed()) {
406409 equivalenceClass.setClosed();
410 }
407411 }
408412
409413 // Iterate through potential open streams, reporting warnings
412416 // that was closed).
413417 for (PotentialOpenStream pos : potentialOpenStreamList) {
414418 Stream stream = pos.stream;
415 if (stream.isClosed())
419 if (stream.isClosed()) {
416420 // Stream was in an equivalence class with another
417421 // stream that was properly closed.
418422 continue;
419
420 if (stream.isUninteresting())
423 }
424
425 if (stream.isUninteresting()) {
421426 continue;
427 }
422428
423429 Location openLocation = stream.getOpenLocation();
424 if (openLocation == null)
430 if (openLocation == null) {
425431 continue;
426
427 if (IGNORE_WRAPPED_UNINTERESTING_STREAMS && resourceTracker.isUninterestingStreamEscape(stream))
432 }
433
434 if (IGNORE_WRAPPED_UNINTERESTING_STREAMS && resourceTracker.isUninterestingStreamEscape(stream)) {
428435 continue;
436 }
429437
430438 String sourceFile = javaClass.getSourceFileName();
431439 String leakClass = stream.getStreamBase();
432 if (isMainMethod(method) && (leakClass.contains("InputStream") || leakClass.contains("Reader")))
440 if (isMainMethod(method) && (leakClass.contains("InputStream") || leakClass.contains("Reader"))) {
433441 return;
442 }
434443
435444 bugAccumulator.accumulateBug(new BugInstance(this, pos.bugType, pos.priority)
436 .addClassAndMethod(methodGen, sourceFile).addTypeOfNamedClass(leakClass)
437 .describe(TypeAnnotation.CLOSEIT_ROLE), SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen,
445 .addClassAndMethod(methodGen, sourceFile).addTypeOfNamedClass(leakClass)
446 .describe(TypeAnnotation.CLOSEIT_ROLE), SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen,
438447 sourceFile, stream.getLocation().getHandle()));
439448 }
440449 }
446455 if (DEBUG) {
447456 System.out.printf("Result for %s in %s%n", stream, methodGen);
448457 dataflow.dumpDataflow(dataflow.getAnalysis());
449
458
450459 }
451460 ResourceValueFrame exitFrame = dataflow.getResultFact(cfg.getExit());
452461
498507
499508 }
500509
501 // vim:ts=3
4242 import edu.umd.cs.findbugs.OpcodeStack.Item;
4343 import edu.umd.cs.findbugs.Priorities;
4444 import edu.umd.cs.findbugs.SourceLineAnnotation;
45 import edu.umd.cs.findbugs.SystemProperties;
4546 import edu.umd.cs.findbugs.ba.AnalysisContext;
4647 import edu.umd.cs.findbugs.ba.XFactory;
4748 import edu.umd.cs.findbugs.ba.XField;
6364
6465 final BugAccumulator bugAccumulator;
6566
67 private final boolean testingEnabled;
68
6669 public FindPuzzlers(BugReporter bugReporter) {
6770 this.bugReporter = bugReporter;
6871 this.bugAccumulator = new BugAccumulator(bugReporter);
72 testingEnabled = SystemProperties.getBoolean("report_TESTING_pattern_in_standard_detectors");
6973 }
7074
7175 @Override
110114 XMethod previousMethodInvocation;
111115
112116 boolean isTigerOrHigher;
113
117
114118 static ClassDescriptor ITERATOR = DescriptorFactory.createClassDescriptor(Iterator.class);
115119 static ClassDescriptor MAP_ENTRY = DescriptorFactory.createClassDescriptor(Map.Entry.class);
116
120
117121
118122 @Override
119123 public void visit(JavaClass obj) {
136140 }
137141
138142 private int adjustPriority(int factor, int priority) {
139 if (factor <= 4)
143 if (factor <= 4) {
140144 return LOW_PRIORITY + 2;
141 if (factor <= 10000)
145 }
146 if (factor <= 10000) {
142147 return priority + 1;
143 if (factor <= 60 * 60 * 1000)
148 }
149 if (factor <= 60 * 60 * 1000) {
144150 return priority;
151 }
145152 return priority - 1;
146153 }
147154
148155 private int adjustMultiplier(Object constant, int mul) {
149 if (!(constant instanceof Integer))
156 if (!(constant instanceof Integer)) {
150157 return mul;
158 }
151159 return Math.abs(((Integer) constant).intValue()) * mul;
152160
153161 }
154
162
155163 @Override
156164 public boolean beforeOpcode(int seen) {
157165 super.beforeOpcode(seen);
162170
163171 @Override
164172 public void sawOpcode(int seen) {
165
173
166174 if (stack.isTop()) {
167175 pendingUnreachableBranch = null;
168 if (becameTop == -1)
176 if (becameTop == -1) {
169177 becameTop = getPC();
170 if (seen == GOTO && getBranchTarget() < becameTop) {
178 }
179 if (testingEnabled && seen == GOTO && getBranchTarget() < becameTop) {
171180 pendingUnreachableBranch = new BugInstance(this, "TESTING", NORMAL_PRIORITY)
172181 .addClassAndMethod(this).addString("Unreachable loop body").addSourceLineRange(this, becameTop, getPC());
173182 }
174183 return;
175
184
176185 }
177186 if (pendingUnreachableBranch != null) {
178 bugReporter.reportBug(pendingUnreachableBranch);
179 pendingUnreachableBranch = null;
187 bugReporter.reportBug(pendingUnreachableBranch);
188 pendingUnreachableBranch = null;
180189 }
181190 becameTop = -1;
182191
183 if (seen == INVOKESPECIAL && getNameConstantOperand().equals("<init>") && getSigConstantOperand().equals("(Ljava/util/Collection;)V")
184 && getClassConstantOperand().contains("Set")
185 || (seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE) && getNameConstantOperand().equals("addAll") && getSigConstantOperand().equals("(Ljava/util/Collection;)Z")) {
192 if (seen == INVOKESPECIAL && "<init>".equals(getNameConstantOperand()) && "(Ljava/util/Collection;)V".equals(getSigConstantOperand())
193 && getClassConstantOperand().contains("Set")
194 || (seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE) && "addAll".equals(getNameConstantOperand()) && "(Ljava/util/Collection;)Z".equals(getSigConstantOperand())) {
186195 OpcodeStack.Item top = stack.getStackItem(0);
187196 XMethod returnValueOf = top.getReturnValueOf();
188 if (returnValueOf != null && returnValueOf.getName().equals("entrySet")) {
197 if (returnValueOf != null && "entrySet".equals(returnValueOf.getName())) {
189198 String name = returnValueOf.getClassName();
190199 int priority = Priorities.LOW_PRIORITY;
191 if (name.equals("java.util.Map"))
200 if ("java.util.Map".equals(name)) {
192201 priority = Priorities.NORMAL_PRIORITY;
193 else if (name.equals(EnumMap.class.getName())
194 || name.equals(IdentityHashMap.class.getName()))
202 } else if (name.equals(EnumMap.class.getName())
203 || name.equals(IdentityHashMap.class.getName())) {
195204 priority = Priorities.HIGH_PRIORITY;
205 }
196206 bugReporter.reportBug(new BugInstance(this, "DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS", priority)
197207 .addClassAndMethod(this).addCalledMethod(returnValueOf).addCalledMethod(this).addValueSource(top, this).addSourceLine(this));
198208 }
200210
201211 }
202212
203 if (seen == INVOKEVIRTUAL && getNameConstantOperand().equals("hashCode") && getSigConstantOperand().equals("()I")
213 if (seen == INVOKEVIRTUAL && "hashCode".equals(getNameConstantOperand()) && "()I".equals(getSigConstantOperand())
204214 && stack.getStackDepth() > 0) {
205215 OpcodeStack.Item item0 = stack.getStackItem(0);
206 if (item0.getSignature().charAt(0) == '[')
216 if (item0.getSignature().charAt(0) == '[') {
207217 bugReporter.reportBug(new BugInstance(this, "DMI_INVOKING_HASHCODE_ON_ARRAY", NORMAL_PRIORITY)
208 .addClassAndMethod(this).addValueSource(item0, this).addSourceLine(this));
218 .addClassAndMethod(this).addValueSource(item0, this).addSourceLine(this));
219 }
209220 }
210221 if (seen != RETURN && isReturn(seen) && isRegisterStore(getPrevOpcode(1))) {
211222
212223 int priority = Priorities.NORMAL_PRIORITY;
213 if (getMethodSig().endsWith(")Z"))
224 if (getMethodSig().endsWith(")Z")) {
214225 priority = Priorities.HIGH_PRIORITY;
215 else {
216 if (getMethodSig().endsWith(")Ljava/lang/String;"))
226 } else {
227 if (getMethodSig().endsWith(")Ljava/lang/String;")) {
217228 priority = Priorities.LOW_PRIORITY;
218 if (getPC() == getCode().getCode().length - 1)
229 }
230 if (getPC() == getCode().getCode().length - 1) {
219231 priority++;
232 }
220233 }
221234 bugReporter.reportBug(new BugInstance(this, "DLS_DEAD_LOCAL_STORE_IN_RETURN", priority).addClassAndMethod(this)
222235 .addSourceLine(this));
224237 // System.out.println(getPC() + " " + OPCODE_NAMES[seen] + " " +
225238 // ternaryConversionState);
226239 if (seen == IMUL) {
227 if (imul_distance != 1)
240 if (imul_distance != 1) {
228241 resetIMulCastLong();
242 }
229243 imul_distance = 0;
230244 if (stack.getStackDepth() > 1) {
231245 OpcodeStack.Item item0 = stack.getStackItem(0);
233247 imul_constant = adjustMultiplier(item0.getConstant(), imul_constant);
234248 imul_constant = adjustMultiplier(item1.getConstant(), imul_constant);
235249
236 if (item0.isInitialParameter() || item1.isInitialParameter())
250 if (item0.isInitialParameter() || item1.isInitialParameter()) {
237251 imul_operand_is_parameter = true;
252 }
238253 }
239254 } else {
240255 imul_distance++;
242257
243258 if (prevOpCode == IMUL && seen == I2L) {
244259 int priority = adjustPriority(imul_constant, NORMAL_PRIORITY);
245 if (priority >= LOW_PRIORITY && imul_constant != 1000 && imul_constant != 60 && imul_operand_is_parameter)
260 if (priority >= LOW_PRIORITY && imul_constant != 1000 && imul_constant != 60 && imul_operand_is_parameter) {
246261 priority = NORMAL_PRIORITY;
262 }
247263 if (priority <= best_priority_for_ICAST_INTEGER_MULTIPLY_CAST_TO_LONG) {
248264 best_priority_for_ICAST_INTEGER_MULTIPLY_CAST_TO_LONG = priority;
249265 bugAccumulator.accumulateBug(
251267 }
252268 }
253269
254 if (getMethodName().equals("<clinit>") && (seen == PUTSTATIC || seen == GETSTATIC || seen == INVOKESTATIC)) {
270 if ("<clinit>".equals(getMethodName()) && (seen == PUTSTATIC || seen == GETSTATIC || seen == INVOKESTATIC)) {
255271 String clazz = getClassConstantOperand();
256272 if (!clazz.equals(getClassName())) {
257273 try {
258274 JavaClass targetClass = Repository.lookupClass(clazz);
259275 if (Repository.instanceOf(targetClass, getThisClass())) {
260276 int priority = NORMAL_PRIORITY;
261 if (seen == GETSTATIC)
277 if (seen == GETSTATIC) {
262278 priority--;
263 if (!targetClass.isPublic())
279 }
280 if (!targetClass.isPublic()) {
264281 priority++;
282 }
265283 bugAccumulator.accumulateBug(new BugInstance(this, "IC_SUPERCLASS_USES_SUBCLASS_DURING_INITIALIZATION",
266284 priority).addClassAndMethod(this).addClass(getClassConstantOperand()), this);
267285
272290
273291 }
274292 }
293 /*
275294 if (false && (seen == INVOKEVIRTUAL) && getNameConstantOperand().equals("equals")
276295 && getSigConstantOperand().equals("(Ljava/lang/Object;)Z") && stack.getStackDepth() > 1) {
277296 OpcodeStack.Item item0 = stack.getStackItem(0);
282301 new BugInstance(this, "EC_BAD_ARRAY_COMPARE", NORMAL_PRIORITY).addClassAndMethod(this), this);
283302 }
284303 }
304 */
285305
286306 if (seen >= IALOAD && seen <= SALOAD || seen >= IASTORE && seen <= SASTORE) {
287307 Item index = stack.getStackItem(0);
288308 if (index.getSpecialKind() == Item.AVERAGE_COMPUTED_USING_DIVISION) {
289309 SourceLineAnnotation where;
290 if (index.getPC() >= 0)
310 if (index.getPC() >= 0) {
291311 where = SourceLineAnnotation.fromVisitedInstruction(this, index.getPC());
292 else
312 } else {
293313 where = SourceLineAnnotation.fromVisitedInstruction(this);
314 }
294315 bugAccumulator.accumulateBug(
295316 new BugInstance(this, "IM_AVERAGE_COMPUTATION_COULD_OVERFLOW", NORMAL_PRIORITY).addClassAndMethod(this),
296317 where);
299320 }
300321
301322 if ((seen == IFEQ || seen == IFNE) && getPrevOpcode(1) == IMUL
302 && (getPrevOpcode(2) == SIPUSH || getPrevOpcode(2) == BIPUSH) && getPrevOpcode(3) == IREM)
323 && (getPrevOpcode(2) == SIPUSH || getPrevOpcode(2) == BIPUSH) && getPrevOpcode(3) == IREM) {
303324 bugAccumulator.accumulateBug(
304325 new BugInstance(this, "IM_MULTIPLYING_RESULT_OF_IREM", LOW_PRIORITY).addClassAndMethod(this), this);
326 }
305327
306328 if (seen == I2S && getPrevOpcode(1) == IUSHR && !shiftOfNonnegativeValue
307329 && (!constantArgumentToShift || valueOfConstantArgumentToShift % 16 != 0) || seen == I2B
308330 && getPrevOpcode(1) == IUSHR && !shiftOfNonnegativeValue
309 && (!constantArgumentToShift || valueOfConstantArgumentToShift % 8 != 0))
310
331 && (!constantArgumentToShift || valueOfConstantArgumentToShift % 8 != 0)) {
311332 bugAccumulator.accumulateBug(
312333 new BugInstance(this, "ICAST_QUESTIONABLE_UNSIGNED_RIGHT_SHIFT", NORMAL_PRIORITY).addClassAndMethod(this),
313334 this);
314
335 }
336
337
338 if (seen == IADD && (getNextOpcode() == ISHL || getNextOpcode() == LSHL) && stack.getStackDepth() >=3) {
339 OpcodeStack.Item l = stack.getStackItem(2);
340 OpcodeStack.Item v = stack.getStackItem(1);
341 Object constantValue = v.getConstant();
342 // Ignore 1 << (const + var) as it's usually intended
343 if (constantValue instanceof Integer && !Integer.valueOf(1).equals(l.getConstant())) {
344 int c = ((Integer) constantValue).intValue();
345 int priority = LOW_PRIORITY;
346 // If (foo << 32 + var) encountered, then ((foo << 32) + var) is absolutely meaningless,
347 // but (foo << (32 + var)) can be meaningful for negative var values
348 if (c < 32 || (c < 64 && getNextOpcode() == LSHL)) {
349 if (c == 8) {
350 priority--;
351 }
352 if (getPrevOpcode(1) == IAND) {
353 priority--;
354 }
355 if (getMethodName().equals("hashCode") && getMethodSig().equals("()I")
356 && (getCode().getCode()[getNextPC() + 1] & 0xFF) == IRETURN) {
357 // commonly observed error is hashCode body like "return foo << 16 + bar;"
358 priority = HIGH_PRIORITY;
359 }
360 bugAccumulator.accumulateBug(new BugInstance(this, "BSHIFT_WRONG_ADD_PRIORITY", priority)
361 .addClassAndMethod(this)
362 .addInt(c).describe(IntAnnotation.INT_SHIFT)
363 .addValueSource(stack.getStackItem(2), this)
364 .addValueSource(stack.getStackItem(0), this)
365 , this);
366 }
367 }
368
369 }
315370 constantArgumentToShift = false;
316371 shiftOfNonnegativeValue = false;
317372 if ((seen == IUSHR || seen == ISHR || seen == ISHL)) {
327382 if (rightHandSide instanceof Integer) {
328383 constantArgumentToShift = true;
329384 valueOfConstantArgumentToShift = ((Integer) rightHandSide);
330 if (valueOfConstantArgumentToShift < 0 || valueOfConstantArgumentToShift >= 32)
385 if (valueOfConstantArgumentToShift < 0 || valueOfConstantArgumentToShift >= 32) {
331386 bugAccumulator.accumulateBug(new BugInstance(this, "ICAST_BAD_SHIFT_AMOUNT",
332387 valueOfConstantArgumentToShift < 0 ? LOW_PRIORITY : (valueOfConstantArgumentToShift == 32
333 && getMethodName().equals("hashCode") ? NORMAL_PRIORITY : HIGH_PRIORITY))
334 .addClassAndMethod(this).addInt(valueOfConstantArgumentToShift).describe(IntAnnotation.INT_SHIFT)
335 .addValueSource(stack.getStackItem(1), this), this);
388 && "hashCode".equals(getMethodName()) ? NORMAL_PRIORITY : HIGH_PRIORITY))
389 .addClassAndMethod(this).addInt(valueOfConstantArgumentToShift).describe(IntAnnotation.INT_SHIFT)
390 .addValueSource(stack.getStackItem(1), this), this);
391 }
336392 }
337393 if (leftHandSide != null && leftHandSide instanceof Integer && ((Integer) leftHandSide) > 0) {
338394 // boring; lie so other detectors won't get concerned
344400 }
345401
346402 if (seen == INVOKEVIRTUAL && stack.getStackDepth() > 0
347 && (getClassConstantOperand().equals("java/util/Date") || getClassConstantOperand().equals("java/sql/Date"))
348 && getNameConstantOperand().equals("setMonth") && getSigConstantOperand().equals("(I)V")) {
403 && ("java/util/Date".equals(getClassConstantOperand()) || "java/sql/Date".equals(getClassConstantOperand()))
404 && "setMonth".equals(getNameConstantOperand()) && "(I)V".equals(getSigConstantOperand())) {
349405 OpcodeStack.Item item = stack.getStackItem(0);
350406 Object o = item.getConstant();
351407 if (o != null && o instanceof Integer) {
352408 int v = (Integer) o;
353 if (v < 0 || v > 11)
409 if (v < 0 || v > 11) {
354410 bugReporter.reportBug(new BugInstance(this, "DMI_BAD_MONTH", HIGH_PRIORITY).addClassAndMethod(this).addInt(v)
355411 .describe(IntAnnotation.INT_VALUE).addCalledMethod(this).addSourceLine(this));
356 }
357 }
358
359 if (seen == INVOKEVIRTUAL && stack.getStackDepth() > 1 && getClassConstantOperand().equals("java/util/Calendar")
360 && getNameConstantOperand().equals("set")
412 }
413 }
414 }
415
416 if (seen == INVOKEVIRTUAL && stack.getStackDepth() > 1 && "java/util/Calendar".equals(getClassConstantOperand())
417 && "set".equals(getNameConstantOperand())
361418
362419 || seen == INVOKESPECIAL && stack.getStackDepth() > 1
363 && getClassConstantOperand().equals("java/util/GregorianCalendar") && getNameConstantOperand().equals("<init>")
364
365 ) {
420 && "java/util/GregorianCalendar".equals(getClassConstantOperand()) && "<init>".equals(getNameConstantOperand())
421
422 ) {
366423 String sig = getSigConstantOperand();
367424 if (sig.startsWith("(III")) {
368425 int pos = sig.length() - 5;
370427 Object o = item.getConstant();
371428 if (o != null && o instanceof Integer) {
372429 int v = (Integer) o;
373 if (v < 0 || v > 11)
430 if (v < 0 || v > 11) {
374431 bugReporter.reportBug(new BugInstance(this, "DMI_BAD_MONTH", NORMAL_PRIORITY).addClassAndMethod(this)
375432 .addInt(v).describe(IntAnnotation.INT_VALUE).addCalledMethod(this).addSourceLine(this));
433 }
376434 }
377435 }
378436 }
385443 }
386444 if (seen == IINC) {
387445 prevOpcodeIncrementedRegister = getRegisterOperand();
388 } else
446 } else {
389447 prevOpcodeIncrementedRegister = -1;
448 }
390449
391450 // Java Puzzlers, Chapter 2, puzzle 1
392451 // Look for ICONST_2 IREM ICONST_1 IF_ICMPNE L1
393452
394453 switch (badlyComputingOddState) {
395454 case 0:
396 if (seen == ICONST_2)
455 if (seen == ICONST_2) {
397456 badlyComputingOddState++;
457 }
398458 break;
399459 case 1:
400460 if (seen == IREM) {
401461 OpcodeStack.Item item = stack.getStackItem(1);
402 if (!item.isNonNegative() && item.getSpecialKind() != OpcodeStack.Item.MATH_ABS)
462 if (!item.isNonNegative() && item.getSpecialKind() != OpcodeStack.Item.MATH_ABS) {
403463 badlyComputingOddState++;
404 else
464 } else {
405465 badlyComputingOddState = 0;
406 } else
466 }
467 } else {
407468 badlyComputingOddState = 0;
469 }
408470 break;
409471 case 2:
410 if (seen == ICONST_1)
472 if (seen == ICONST_1) {
411473 badlyComputingOddState++;
412 else
474 } else {
413475 badlyComputingOddState = 0;
476 }
414477 break;
415478 case 3:
416479 if (seen == IF_ICMPEQ || seen == IF_ICMPNE) {
419482 }
420483 badlyComputingOddState = 0;
421484 break;
485 default:
486 break;
422487 }
423488
424489 // Java Puzzlers, chapter 3, puzzle 12
425490 if (seen == INVOKEVIRTUAL
426491 && stack.getStackDepth() > 0
427 && (getNameConstantOperand().equals("toString") && getSigConstantOperand().equals("()Ljava/lang/String;")
428 || getNameConstantOperand().equals("append")
429 && getSigConstantOperand().equals("(Ljava/lang/Object;)Ljava/lang/StringBuilder;")
430 && getClassConstantOperand().equals("java/lang/StringBuilder")
431 || getNameConstantOperand().equals("append")
432 && getSigConstantOperand().equals("(Ljava/lang/Object;)Ljava/lang/StringBuffer;")
433 && getClassConstantOperand().equals("java/lang/StringBuffer") || (getNameConstantOperand()
434 .equals("print") || getNameConstantOperand().equals("println"))
435 && getSigConstantOperand().equals("(Ljava/lang/Object;)V"))) {
492 && ("toString".equals(getNameConstantOperand()) && "()Ljava/lang/String;".equals(getSigConstantOperand())
493 || "append".equals(getNameConstantOperand())
494 && "(Ljava/lang/Object;)Ljava/lang/StringBuilder;".equals(getSigConstantOperand())
495 && "java/lang/StringBuilder".equals(getClassConstantOperand())
496 || "append".equals(getNameConstantOperand())
497 && "(Ljava/lang/Object;)Ljava/lang/StringBuffer;".equals(getSigConstantOperand())
498 && "java/lang/StringBuffer".equals(getClassConstantOperand()) || ("print".equals(getNameConstantOperand()) || "println".equals(getNameConstantOperand()))
499 && "(Ljava/lang/Object;)V".equals(getSigConstantOperand()))) {
436500 OpcodeStack.Item item = stack.getStackItem(0);
437501 String signature = item.getSignature();
438502 if (signature != null && signature.startsWith("[")) {
439 boolean debuggingContext = signature.equals("[Ljava/lang/StackTraceElement;");
503 boolean debuggingContext = "[Ljava/lang/StackTraceElement;".equals(signature);
440504
441505 if (!debuggingContext) {
442506 for (CodeException e : getCode().getExceptionTable()) {
443 if (e.getHandlerPC() <= getPC() && e.getHandlerPC() + 30 >= getPC())
507 if (e.getHandlerPC() <= getPC() && e.getHandlerPC() + 30 >= getPC()) {
444508 debuggingContext = true;
509 }
445510 }
446511
447512 for (int i = 1; !debuggingContext && i < stack.getStackDepth(); i++) {
448513 OpcodeStack.Item e = stack.getStackItem(i);
449514
450 if (e.getSignature().indexOf("Logger") >= 0 || e.getSignature().indexOf("Exception") >= 0)
515 if (e.getSignature().indexOf("Logger") >= 0 || e.getSignature().indexOf("Exception") >= 0) {
451516 debuggingContext = true;
517 }
452518
453519 XField f = e.getXField();
454 if (f != null && (SYSTEM_ERR.equals(f.getFieldDescriptor()) || SYSTEM_OUT.equals(f.getFieldDescriptor())))
520 if (f != null && (SYSTEM_ERR.equals(f.getFieldDescriptor()) || SYSTEM_OUT.equals(f.getFieldDescriptor()))) {
455521 debuggingContext = true;
456 }
457 }
458 String name = null;
522 }
523 }
524 }
525 // String name = null;
459526 int reg = item.getRegisterNumber();
460527 Collection<BugAnnotation> as = new ArrayList<BugAnnotation>();
461528 XField field = item.getXField();
470537 getPC() - 1);
471538 if (lva.isNamed()) {
472539 as.add(lva);
473 if (fieldAnnotation != null)
540 if (fieldAnnotation != null) {
474541 as.add(fieldAnnotation);
542 }
475543 } else {
476 if (fieldAnnotation != null)
544 if (fieldAnnotation != null) {
477545 as.add(fieldAnnotation);
546 }
478547 as.add(lva);
479548 }
480 } else if (fieldAnnotation != null)
549 } else if (fieldAnnotation != null) {
481550 as.add(fieldAnnotation);
482 else {
551 } else {
483552 XMethod m = item.getReturnValueOf();
484553 if (m != null) {
485554 MethodAnnotation methodAnnotation = MethodAnnotation.fromXMethod(m);
490559 int priority = debuggingContext ? NORMAL_PRIORITY : HIGH_PRIORITY;
491560 if (!as.isEmpty()) {
492561 bugAccumulator.accumulateBug(new BugInstance(this, "DMI_INVOKING_TOSTRING_ON_ARRAY", priority)
493 .addClassAndMethod(this).addAnnotations(as), this);
562 .addClassAndMethod(this).addAnnotations(as), this);
494563 } else {
495564 bugAccumulator.accumulateBug(
496565 new BugInstance(this, "DMI_INVOKING_TOSTRING_ON_ANONYMOUS_ARRAY", priority).addClassAndMethod(this),
507576 && classNameForPreviousMethod.equals(classNameForThisMethod.replace('/', '.'))
508577 && previousMethodInvocation.getName().endsWith("Value")
509578 && previousMethodInvocation.getSignature().length() == 3
510 && getNameConstantOperand().equals("valueOf")
511 && getSigConstantOperand().charAt(1) == previousMethodInvocation.getSignature().charAt(2))
512 bugAccumulator.accumulateBug(
513 new BugInstance(this, "BX_UNBOXING_IMMEDIATELY_REBOXED", NORMAL_PRIORITY).addClassAndMethod(this)
514 .addCalledMethod(this),
515 this);
579 && "valueOf".equals(getNameConstantOperand())
580 && getSigConstantOperand().charAt(1) == previousMethodInvocation.getSignature().charAt(2)) {
581 bugAccumulator.accumulateBug(
582 new BugInstance(this, "BX_UNBOXING_IMMEDIATELY_REBOXED", NORMAL_PRIORITY).addClassAndMethod(this)
583 .addCalledMethod(this),
584 this);
585 }
516586
517587 }
518588
522592 if (classNameForPreviousMethod.startsWith("java.lang.")
523593 && classNameForPreviousMethod.equals(classNameForThisMethod.replace('/', '.'))
524594 && getNameConstantOperand().endsWith("Value") && getSigConstantOperand().length() == 3) {
525 if (getSigConstantOperand().charAt(2) == previousMethodInvocation.getSignature().charAt(1))
595 if (getSigConstantOperand().charAt(2) == previousMethodInvocation.getSignature().charAt(1)) {
526596 bugAccumulator.accumulateBug(
527597 new BugInstance(this, "BX_BOXING_IMMEDIATELY_UNBOXED", NORMAL_PRIORITY).addClassAndMethod(this),
528598 this);
529
530 else
599 } else {
531600 bugAccumulator.accumulateBug(new BugInstance(this, "BX_BOXING_IMMEDIATELY_UNBOXED_TO_PERFORM_COERCION",
532601 NORMAL_PRIORITY).addClassAndMethod(this), this);
602 }
533603
534604 ternaryConversionState = 1;
535 } else
605 } else {
536606 ternaryConversionState = 0;
607 }
537608
538609 } else if (seen == INVOKEVIRTUAL) {
539610 if (getClassConstantOperand().startsWith("java/lang") && getNameConstantOperand().endsWith("Value")
540 && getSigConstantOperand().length() == 3)
611 && getSigConstantOperand().length() == 3) {
541612 ternaryConversionState = 1;
542 else
613 } else {
543614 ternaryConversionState = 0;
615 }
544616 } else if (ternaryConversionState == 1) {
545 if (I2L < seen && seen <= I2S)
617 if (I2L < seen && seen <= I2S) {
546618 ternaryConversionState = 2;
547 else
619 } else {
548620 ternaryConversionState = 0;
621 }
549622 } else if (ternaryConversionState == 2) {
550623 ternaryConversionState = 0;
551 if (seen == GOTO)
624 if (seen == GOTO) {
552625 bugReporter.reportBug(new BugInstance(this, "BX_UNBOXED_AND_COERCED_FOR_TERNARY_OPERATOR", NORMAL_PRIORITY)
553 .addClassAndMethod(this).addSourceLine(this));
554 } else
626 .addClassAndMethod(this).addSourceLine(this));
627 }
628 } else {
555629 ternaryConversionState = 0;
556 }
557
558 AssertInvokedFromRun: if (seen == INVOKESTATIC)
630 }
631 }
632
633 AssertInvokedFromRun: if (seen == INVOKESTATIC) {
559634 if ((getNameConstantOperand().startsWith("assert") || getNameConstantOperand().startsWith("fail"))
560 && getMethodName().equals("run") && implementsRunnable(getThisClass())) {
635 && "run".equals(getMethodName()) && implementsRunnable(getThisClass())) {
561636 int size1 = Util.getSizeOfSurroundingTryBlock(getConstantPool(), getMethod().getCode(), "java/lang/Throwable",
562637 getPC());
563638 int size2 = Util.getSizeOfSurroundingTryBlock(getConstantPool(), getMethod().getCode(), "java/lang/Error",
570645 if (!dottedClassName.startsWith("junit")) {
571646 try {
572647 JavaClass targetClass = AnalysisContext.currentAnalysisContext().lookupClass(dottedClassName);
573 if (!targetClass.getSuperclassName().startsWith("junit"))
648 if (!targetClass.getSuperclassName().startsWith("junit")) {
574649 break AssertInvokedFromRun;
650 }
575651 } catch (ClassNotFoundException e) {
576652 AnalysisContext.reportMissingClass(e);
577653 break AssertInvokedFromRun;
584660 }
585661
586662 }
663 }
587664 if (seen == INVOKESPECIAL && getClassConstantOperand().startsWith("java/lang/")
588 && getNameConstantOperand().equals("<init>") && getSigConstantOperand().length() == 4)
589
665 && "<init>".equals(getNameConstantOperand()) && getSigConstantOperand().length() == 4) {
590666 previousMethodInvocation = XFactory.createReferencedXMethod(this);
591 else if (seen == INVOKESTATIC && getClassConstantOperand().startsWith("java/lang/")
592 && getNameConstantOperand().equals("valueOf") && getSigConstantOperand().length() == 4)
667 } else if (seen == INVOKESTATIC && getClassConstantOperand().startsWith("java/lang/")
668 && "valueOf".equals(getNameConstantOperand()) && getSigConstantOperand().length() == 4) {
593669 previousMethodInvocation = XFactory.createReferencedXMethod(this);
594 else if (seen == INVOKEVIRTUAL && getClassConstantOperand().startsWith("java/lang/")
670 } else if (seen == INVOKEVIRTUAL && getClassConstantOperand().startsWith("java/lang/")
595671 && getNameConstantOperand().endsWith("Value")
596 && getSigConstantOperand().length() == 3)
672 && getSigConstantOperand().length() == 3) {
597673 previousMethodInvocation = XFactory.createReferencedXMethod(this);
598 else
674 } else {
599675 previousMethodInvocation = null;
600
601 if (seen == IAND || seen == LAND) {
676 }
677
678 if (testingEnabled && seen == IAND || seen == LAND) {
602679 OpcodeStack.Item rhs = stack.getStackItem(0);
603680 OpcodeStack.Item lhs = stack.getStackItem(1);
604681 Object constant = rhs.getConstant();
610687 if (constant instanceof Number && (seen == LAND || value.getSpecialKind() == OpcodeStack.Item.RESULT_OF_L2I)) {
611688 long constantValue = ((Number) constant).longValue();
612689 if ((constantValue == 0xEFFFFFFFL || constantValue == 0xEFFFFFFFFFFFFFFFL || seen == IAND
613 && constantValue == 0xEFFFFFFF))
690 && constantValue == 0xEFFFFFFF)) {
614691 bugAccumulator.accumulateBug(new BugInstance(this, "TESTING", seen == LAND ? HIGH_PRIORITY : NORMAL_PRIORITY)
615 .addClassAndMethod(this).addString("Possible failed attempt to mask lower 31 bits of an int")
616 .addValueSource(value, this), this);
692 .addClassAndMethod(this).addString("Possible failed attempt to mask lower 31 bits of an int")
693 .addValueSource(value, this), this);
694 }
617695
618696 }
619697 }
622700 OpcodeStack.Item top = stack.getStackItem(0);
623701 XMethod m = top.getReturnValueOf();
624702 if (m != null) {
625 if (m.getName().equals("compareTo") || m.getName().equals("compare"))
703 if ("compareTo".equals(m.getName()) || "compare".equals(m.getName())) {
626704 bugAccumulator.accumulateBug(new BugInstance(this, "RV_NEGATING_RESULT_OF_COMPARETO", NORMAL_PRIORITY)
627705 .addClassAndMethod(this)
628706 .addCalledMethod(m).addValueSource(top, this), this);
629 }
630
631 }
632 if (seen == IRETURN && (getMethod().getName().equals("compareTo") || getMethod().getName().equals("compare"))) {
633 OpcodeStack.Item top = stack.getStackItem(0);
634 Object o = top.getConstant();
635 if (o instanceof Integer && ((Integer)o).intValue() == Integer.MIN_VALUE)
636 bugAccumulator.accumulateBug(new BugInstance(this, "CO_COMPARETO_RESULTS_MIN_VALUE", NORMAL_PRIORITY)
637 .addClassAndMethod(this), this);
707 }
708 }
638709
639710 }
640711 prevOpCode = seen;
122122 public class FindRefComparison implements Detector, ExtendedTypes {
123123 private static final boolean DEBUG = SystemProperties.getBoolean("frc.debug");
124124
125 private static final boolean REPORT_ALL_REF_COMPARISONS = true || SystemProperties.getBoolean("findbugs.refcomp.reportAll");
125 private static final boolean REPORT_ALL_REF_COMPARISONS = true /*|| SystemProperties.getBoolean("findbugs.refcomp.reportAll")*/;
126126
127127 private static final int BASE_ES_PRIORITY = SystemProperties.getInt("es.basePriority", NORMAL_PRIORITY);
128128
178178
179179 private static final byte T_PARAMETER_STRING = T_AVAIL_TYPE + 2;
180180
181 private static final byte T_STATIC_FINAL_PUBLIC_CONSTANT = T_AVAIL_TYPE + 3;
181 // private static final byte T_STATIC_FINAL_PUBLIC_CONSTANT = T_AVAIL_TYPE + 3;
182182
183183 private static final String STRING_SIGNATURE = "Ljava/lang/String;";
184184
186186 * @author pugh
187187 */
188188 private final static class SpecialTypeAnalysis extends TypeAnalysis {
189 /**
190 * @param method
191 * @param methodGen
192 * @param cfg
193 * @param dfs
194 * @param typeMerger
195 * @param visitor
196 * @param lookupFailureCallback
197 * @param exceptionSetFactory
198 */
189
199190 private SpecialTypeAnalysis(Method method, MethodGen methodGen, CFG cfg, DepthFirstSearch dfs, TypeMerger typeMerger,
200191 TypeFrameModelingVisitor visitor, RepositoryLookupFailureCallback lookupFailureCallback,
201192 ExceptionSetFactory exceptionSetFactory) {
265256
266257 @Override
267258 public boolean equals(Object obj) {
268 if (this == obj)
259 if (this == obj) {
269260 return true;
270 if (!(obj instanceof FinalConstant))
261 }
262 if (!(obj instanceof FinalConstant)) {
271263 return false;
264 }
272265 FinalConstant other = (FinalConstant) obj;
273266
274267 return super.equals(other) && this.field.equals(other.field);
410403 consumeStack(obj);
411404
412405 String className = obj.getClassName(getCPG());
413 if (className.equals("java.lang.String")) {
406 if ("java.lang.String".equals(className)) {
414407 pushValue(dynamicStringTypeInstance);
415408 } else {
416409 pushReturnType(obj);
422415
423416 @Override
424417 public void visitINVOKESPECIAL(INVOKESPECIAL obj) {
425 if (returnsString(obj))
418 if (returnsString(obj)) {
426419 handleInstanceMethod(obj);
427 else
420 } else {
428421 super.visitINVOKESPECIAL(obj);
422 }
429423 }
430424
431425 @Override
432426 public void visitINVOKEINTERFACE(INVOKEINTERFACE obj) {
433 if (returnsString(obj))
427 if (returnsString(obj)) {
434428 handleInstanceMethod(obj);
435 else
429 } else {
436430 super.visitINVOKEINTERFACE(obj);
431 }
437432
438433 }
439434
440435 @Override
441436 public void visitINVOKEVIRTUAL(INVOKEVIRTUAL obj) {
442 if (returnsString(obj))
437 if (returnsString(obj)) {
443438 handleInstanceMethod(obj);
444 else
439 } else {
445440 super.visitINVOKEVIRTUAL(obj);
441 }
446442 }
447443
448444 private boolean returnsString(InvokeInstruction inv) {
458454 String methodName = obj.getName(getCPG());
459455 // System.out.println(className + "." + methodName);
460456
461 if (methodName.equals("intern") && className.equals("java.lang.String")) {
457 if ("intern".equals(methodName) && "java.lang.String".equals(className)) {
462458 sawStringIntern = true;
463459 pushValue(staticStringTypeInstance);
464 } else if (methodName.equals("toString") || className.equals("java.lang.String")) {
460 } else if ("toString".equals(methodName) || "java.lang.String".equals(className)) {
465461 pushValue(dynamicStringTypeInstance);
466462 // System.out.println(" dynamic");
467463 } else {
475471 Type type = obj.getType(getCPG());
476472 if (isString(type)) {
477473 Object value = obj.getValue(getCPG());
478 if (value instanceof String && ((String)value).length() == 0)
474 if (value instanceof String && ((String)value).length() == 0) {
479475 pushValue( emptyStringTypeInstance);
480 else pushValue( staticStringTypeInstance);
481 }
482 else pushValue(type);
476 } else {
477 pushValue( staticStringTypeInstance);
478 }
479 } else {
480 pushValue(type);
481 }
483482 }
484483
485484 @Override
489488 }
490489
491490 private boolean isString(Type type) {
492 return type.getSignature().equals(STRING_SIGNATURE);
491 return STRING_SIGNATURE.equals(type.getSignature());
493492 }
494493
495494 @Override
516515 }
517516
518517 }
519 if (type.getSignature().equals(STRING_SIGNATURE)) {
518 if (STRING_SIGNATURE.equals(type.getSignature())) {
520519 handleLoad(obj);
521 } else
520 } else {
522521 super.visitGETSTATIC(obj);
522 }
523523 }
524524
525525 @Override
526526 public void visitGETFIELD(GETFIELD obj) {
527527 Type type = obj.getType(getCPG());
528 if (type.getSignature().equals(STRING_SIGNATURE)) {
528 if (STRING_SIGNATURE.equals(type.getSignature())) {
529529 handleLoad(obj);
530530 } else {
531531 XField xf = XFactory.createXField(obj, cpg);
557557 consumeStack(obj);
558558
559559 Type type = obj.getType(getCPG());
560 if (!type.getSignature().equals(STRING_SIGNATURE))
560 if (!STRING_SIGNATURE.equals(type.getSignature())) {
561561 throw new IllegalArgumentException("type is not String: " + type);
562 }
562563 try {
563564 String className = obj.getClassName(getCPG());
564565 String fieldName = obj.getName(getCPG());
641642
642643 private final Set<String> suspiciousSet;
643644
645 private final boolean testingEnabled;
646
644647 /*
645648 * ----------------------------------------------------------------------
646649 * Implementation
661664 suspiciousSet.add(tok.nextToken());
662665 }
663666 }
664 }
665
667 testingEnabled = SystemProperties.getBoolean("report_TESTING_pattern_in_standard_detectors");
668 }
669
670 @Override
666671 public void visitClassContext(ClassContext classContext) {
667672 this.classContext = classContext;
668673
725730 }
726731
727732 private void analyzeMethod(ClassContext classContext, final Method method) throws CFGBuilderException,
728 DataflowAnalysisException {
733 DataflowAnalysisException {
729734
730735 MethodGen methodGen = classContext.getMethodGen(method);
731736 if (methodGen == null) {
781786 final boolean likelyTestcase = TestCaseDetector.likelyTestCase(XFactory.createXMethod(jclass, method));
782787
783788 decorateWarnings(stringComparisonList, new WarningDecorator() {
789 @Override
784790 public void decorate(WarningWithProperties warn) {
785791 if (mightBeLaterCheckedUsingEquals(warn)) {
786792 warn.propertySet.addProperty(RefComparisonWarningProperty.SAW_CALL_TO_EQUALS);
789795 if (likelyTestcase) {
790796 warn.propertySet.addProperty(RefComparisonWarningProperty.COMPARE_IN_TEST_CASE);
791797 }
792
798 /*
793799 if (false && !(method.isPublic() || method.isProtected())) {
794800 warn.propertySet.addProperty(RefComparisonWarningProperty.PRIVATE_METHOD);
795801 }
802 */
796803 }
797804 });
798805 decorateWarnings(refComparisonList, new WarningDecorator() {
806 @Override
799807 public void decorate(WarningWithProperties warn) {
800808 if (likelyTestcase) {
801809 warn.propertySet.addProperty(RefComparisonWarningProperty.COMPARE_IN_TEST_CASE);
814822 }
815823
816824 boolean mightBeLaterCheckedUsingEquals(WarningWithProperties warning) {
817 for (BugAnnotation a : warning.instance.getAnnotations())
825 for (BugAnnotation a : warning.instance.getAnnotations()) {
818826 if (a instanceof TypeAnnotation) {
819827 String signature = ((TypeAnnotation) a).getTypeDescriptor();
820828 Integer pc = comparedForEqualityInThisMethod.get(signature);
821 if (pc != null && pc > warning.location.getHandle().getPosition())
829 if (pc != null && pc > warning.location.getHandle().getPosition()) {
822830 return true;
823 }
831 }
832 }
833 }
824834 return false;
825835 }
826836
827837 private void inspectLocation(JavaClass jclass, ConstantPoolGen cpg, Method method, MethodGen methodGen,
828838 LinkedList<WarningWithProperties> refComparisonList, LinkedList<WarningWithProperties> stringComparisonList,
829839 RefComparisonTypeFrameModelingVisitor visitor, TypeDataflow typeDataflow, Location location)
830 throws DataflowAnalysisException {
840 throws DataflowAnalysisException {
831841 Instruction ins = location.getHandle().getInstruction();
832842 short opcode = ins.getOpcode();
833843 if (opcode == Constants.IF_ACMPEQ || opcode == Constants.IF_ACMPNE) {
839849 @DottedClassName String className = inv.getClassName(cpg);
840850 String methodName = inv.getMethodName(cpg);
841851 String methodSig = inv.getSignature(cpg);
842 if ( methodName.equals("assertSame") && methodSig.equals("(Ljava/lang/Object;Ljava/lang/Object;)V")) {
852 if ( "assertSame".equals(methodName) && "(Ljava/lang/Object;Ljava/lang/Object;)V".equals(methodSig)) {
843853 checkRefComparison(location, jclass, method, methodGen, visitor, typeDataflow, stringComparisonList,
844854 refComparisonList);
845 } else if ( methodName.equals("assertFalse") && methodSig.equals("(Z)V")) {
855 } else if ( "assertFalse".equals(methodName) && "(Z)V".equals(methodSig)) {
846856 SourceLineAnnotation lastLocation = bugAccumulator.getLastBugLocation();
847857 InstructionHandle prevHandle = location.getHandle().getPrev();
848858 if (lastLocation != null && prevHandle != null && lastLocation.getEndBytecode() == prevHandle.getPosition()){
849 bugAccumulator.forgetLastBug();
850 if (DEBUG)
851 System.out.println("Forgetting last bug due to call to " + className +"." + methodName);
859 bugAccumulator.forgetLastBug();
860 if (DEBUG) {
861 System.out.println("Forgetting last bug due to call to " + className +"." + methodName);
862 }
852863 }
853864
854865 } else {
855 boolean equalsMethod = !isStatic && methodName.equals("equals") && methodSig.equals("(Ljava/lang/Object;)Z")
856 || isStatic && methodName.equals("assertEquals")
857 && methodSig.equals("(Ljava/lang/Object;Ljava/lang/Object;)V")
858 || isStatic && methodName.equals("equal") && methodSig.equals("(Ljava/lang/Object;Ljava/lang/Object;)Z")
859 && className.equals("com.google.common.base.Objects")
860 || isStatic && methodName.equals("equals") && methodSig.equals("(Ljava/lang/Object;Ljava/lang/Object;)Z")
861 && className.equals("java.util.Objects");
862
863 if (equalsMethod) {
864 checkEqualsComparison(location, jclass, method, methodGen, cpg, typeDataflow);
865 }
866 boolean equalsMethod = !isStatic && "equals".equals(methodName) && "(Ljava/lang/Object;)Z".equals(methodSig)
867 || isStatic && "assertEquals".equals(methodName)
868 && "(Ljava/lang/Object;Ljava/lang/Object;)V".equals(methodSig)
869 || isStatic && "equal".equals(methodName) && "(Ljava/lang/Object;Ljava/lang/Object;)Z".equals(methodSig)
870 && "com.google.common.base.Objects".equals(className)
871 || isStatic && "equals".equals(methodName) && "(Ljava/lang/Object;Ljava/lang/Object;)Z".equals(methodSig)
872 && "java.util.Objects".equals(className);
873
874 if (equalsMethod) {
875 checkEqualsComparison(location, jclass, method, methodGen, cpg, typeDataflow);
876 }
866877 }
867878 }
868879
882893 int bestPriority = Integer.MAX_VALUE;
883894 for (WarningWithProperties warn : warningList) {
884895 int priority = warn.instance.getPriority();
885 if (bestPriority > priority)
896 if (bestPriority > priority) {
886897 bestPriority = priority;
898 }
887899
888900 if (reportAll) {
889901 if (relaxed) {
897909 }
898910
899911 }
900 if (!reportAll)
912 if (!reportAll) {
901913 for (WarningWithProperties warn : warningList) {
902 BugInstance bug = warn.instance;
903914 int priority = warn.instance.getPriority();
904 if (priority <= bestPriority)
915 if (priority <= bestPriority) {
905916 bugAccumulator.accumulateBug(warn.instance, warn.sourceLine);
906 }
917 }
918 }
919 }
907920 }
908921
909922 private void checkRefComparison(Location location, JavaClass jclass, Method method, MethodGen methodGen,
910923 RefComparisonTypeFrameModelingVisitor visitor, TypeDataflow typeDataflow,
911924 List<WarningWithProperties> stringComparisonList, List<WarningWithProperties> refComparisonList)
912 throws DataflowAnalysisException {
925 throws DataflowAnalysisException {
913926
914927 InstructionHandle handle = location.getHandle();
915928
931944 String sourceFile = jclass.getSourceFileName();
932945
933946 boolean isAssertSame = handle.getInstruction() instanceof INVOKESTATIC;
934 if (isAssertSame)
947 if (isAssertSame) {
948 if(testingEnabled) {
949 bugAccumulator.accumulateBug(
950 new BugInstance(this, "TESTING", result.getPriority())
951 .addClassAndMethod(methodGen, sourceFile)
952 .addString("Calling assertSame with two distinct objects")
953 .addFoundAndExpectedType(rhsType, lhsType)
954 .addSomeSourceForTopTwoStackValues(classContext, method, location),
955 SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen, sourceFile, handle));
956 }
957 } else {
935958 bugAccumulator.accumulateBug(
936 new BugInstance(this, "TESTING", result.getPriority())
937 .addClassAndMethod(methodGen, sourceFile)
938 .addString("Calling assertSame with two distinct objects")
939 .addFoundAndExpectedType(rhsType, lhsType)
940 .addSomeSourceForTopTwoStackValues(classContext, method, location),
959 new BugInstance(this, "EC_UNRELATED_TYPES_USING_POINTER_EQUALITY", result.getPriority())
960 .addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(rhsType, lhsType)
961 .addSomeSourceForTopTwoStackValues(classContext, method, location),
941962 SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen, sourceFile, handle));
942 else
943 bugAccumulator.accumulateBug(
944 new BugInstance(this, "EC_UNRELATED_TYPES_USING_POINTER_EQUALITY", result.getPriority())
945 .addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(rhsType, lhsType)
946 .addSomeSourceForTopTwoStackValues(classContext, method, location),
947 SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen, sourceFile, handle));
963 }
948964 return;
949965 }
950 if (lhsType.equals(Type.OBJECT) && rhsType.equals(Type.OBJECT))
966 if (lhsType.equals(Type.OBJECT) && rhsType.equals(Type.OBJECT)) {
951967 return;
968 }
952969 String lhs = SignatureConverter.convert(lhsType.getSignature());
953970 String rhs = SignatureConverter.convert(rhsType.getSignature());
954971
955 if (lhs.equals("java.lang.String") || rhs.equals("java.lang.String")) {
972 if ("java.lang.String".equals(lhs) || "java.lang.String".equals(rhs)) {
956973 handleStringComparison(jclass, method, methodGen, visitor, stringComparisonList, location, lhsType, rhsType);
957974 } else if (suspiciousSet.contains(lhs)) {
958975 handleSuspiciousRefComparison(jclass, method, methodGen, refComparisonList, location, lhs,
10001017 propertySet.addProperty(RefComparisonWarningProperty.STRING_PARAMETER);
10011018 }
10021019 } else if (type1 == T_STATIC_STRING || type2 == T_STATIC_STRING) {
1003 if (lhsType instanceof EmptyStringType || rhsType instanceof EmptyStringType)
1020 if (lhsType instanceof EmptyStringType || rhsType instanceof EmptyStringType) {
10041021 propertySet.addProperty(RefComparisonWarningProperty.EMPTY_AND_UNKNOWN);
1005 else
1022 } else {
10061023 propertySet.addProperty(RefComparisonWarningProperty.STATIC_AND_UNKNOWN);
1024 }
10071025 } else if (visitor.sawStringIntern()) {
10081026 propertySet.addProperty(RefComparisonWarningProperty.SAW_INTERN);
10091027 }
10131031 .addType("Ljava/lang/String;").describe(TypeAnnotation.FOUND_ROLE).addSomeSourceForTopTwoStackValues(classContext, method, location);
10141032 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen,
10151033 sourceFile, location.getHandle());
1016 if (sourceLineAnnotation != null) {
1017 WarningWithProperties warn = new WarningWithProperties(instance, propertySet, sourceLineAnnotation, location);
1018 stringComparisonList.add(warn);
1019 }
1020
1034
1035 WarningWithProperties warn = new WarningWithProperties(instance, propertySet, sourceLineAnnotation, location);
1036 stringComparisonList.add(warn);
10211037 }
10221038
10231039 private void handleSuspiciousRefComparison(JavaClass jclass, Method method, MethodGen methodGen,
10241040 List<WarningWithProperties> refComparisonList, Location location, String lhs, ReferenceType lhsType,
10251041 ReferenceType rhsType) {
10261042 XField xf = null;
1027 if (lhsType instanceof FinalConstant)
1043 if (lhsType instanceof FinalConstant) {
10281044 xf = ((FinalConstant) lhsType).getXField();
1029 else if (rhsType instanceof FinalConstant)
1045 } else if (rhsType instanceof FinalConstant) {
10301046 xf = ((FinalConstant) rhsType).getXField();
1047 }
10311048 String sourceFile = jclass.getSourceFileName();
10321049 String bugPattern = "RC_REF_COMPARISON";
10331050 int priority = Priorities.HIGH_PRIORITY;
1034 if (lhs.equals("java.lang.Boolean")) {
1051 if ("java.lang.Boolean".equals(lhs)) {
10351052 bugPattern = "RC_REF_COMPARISON_BAD_PRACTICE_BOOLEAN";
10361053 priority = Priorities.NORMAL_PRIORITY;
10371054 } else if (xf != null && xf.isStatic() && xf.isFinal()) {
10381055 bugPattern = "RC_REF_COMPARISON_BAD_PRACTICE";
1039 if (xf.isPublic() || !methodGen.isPublic())
1056 if (xf.isPublic() || !methodGen.isPublic()) {
10401057 priority = Priorities.NORMAL_PRIORITY;
1058 }
10411059 }
10421060 BugInstance instance = new BugInstance(this, bugPattern, priority).addClassAndMethod(methodGen, sourceFile)
10431061 .addType("L" + lhs.replace('.', '/') + ";").describe(TypeAnnotation.FOUND_ROLE);
1044 if (xf != null)
1062 if (xf != null) {
10451063 instance.addField(xf).describe(FieldAnnotation.LOADED_FROM_ROLE);
1046 else
1064 } else {
10471065 instance.addSomeSourceForTopTwoStackValues(classContext, method, location);
1066 }
10481067 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen,
10491068 sourceFile, location.getHandle());
1050 if (sourceLineAnnotation != null)
1051 refComparisonList.add(new WarningWithProperties(instance, new WarningPropertySet<WarningProperty>(),
1052 sourceLineAnnotation, location));
1069
1070 refComparisonList.add(new WarningWithProperties(instance, new WarningPropertySet<WarningProperty>(),
1071 sourceLineAnnotation, location));
10531072 }
10541073
10551074 private Map<String, Integer> comparedForEqualityInThisMethod;
10561075
10571076 void addEqualsCheck(String type, int pc) {
10581077 Integer oldPC = comparedForEqualityInThisMethod.get(type);
1059 if (oldPC == null || pc < oldPC)
1078 if (oldPC == null || pc < oldPC) {
10601079 comparedForEqualityInThisMethod.put(type, pc);
1080 }
10611081 }
10621082
10631083 private void checkEqualsComparison(Location location, JavaClass jclass, Method method, MethodGen methodGen,
10671087 InstructionHandle next = handle.getNext();
10681088 if (next != null && next.getInstruction() instanceof INVOKESTATIC) {
10691089 INVOKESTATIC is = (INVOKESTATIC) next.getInstruction();
1070 if (is.getMethodName(cpg).equals("assertFalse")) {
1090 if ("assertFalse".equals(is.getMethodName(cpg))) {
10711091 return;
10721092 }
10731093 }
11041124 IsNullValueFrame isNullFrame = isNullDataflow.getFactAtLocation(location);
11051125 BugAnnotation a = BugInstance.getSourceForTopStackValue(classContext, method, location);
11061126 int priority = NORMAL_PRIORITY;
1107 if (a instanceof FieldAnnotation && ((FieldAnnotation) a).isStatic())
1127 if (a instanceof FieldAnnotation && ((FieldAnnotation) a).isStatic()) {
11081128 priority = LOW_PRIORITY;
1129 }
11091130 if (isNullFrame.isValid() && isNullFrame.getTopValue().isDefinitelyNull()) {
11101131 String type = "EC_NULL_ARG";
11111132 if (calledMethodAnnotation != null && calledMethodAnnotation.isStatic()){
11131134 priority = LOW_PRIORITY;
11141135 }
11151136 BugInstance bug = new BugInstance(this, type, priority + priorityModifier).addClassAndMethod(methodGen, sourceFile)
1116 .addOptionalAnnotation(calledMethodAnnotation);
1117 if (type.equals("DMI_DOH"))
1137 .addOptionalAnnotation(calledMethodAnnotation);
1138 if ("DMI_DOH".equals(type)) {
11181139 bug.addString("Use \"== null\" to check for a value being null");
1140 }
11191141 bugAccumulator.accumulateBug(
11201142 bug,
11211143 SourceLineAnnotation.fromVisitedInstruction(this.classContext, methodGen, sourceFile,
11421164 if (lhsType_ instanceof ArrayType && rhsType_ instanceof ArrayType) {
11431165 String pattern = "EC_BAD_ARRAY_COMPARE";
11441166 IncompatibleTypes result2 = IncompatibleTypes.getPriorityForAssumingCompatible(lhsType_, rhsType_, true);
1145 if (result2.getPriority() <= Priorities.NORMAL_PRIORITY)
1167 if (result2.getPriority() <= Priorities.NORMAL_PRIORITY) {
11461168 pattern = "EC_INCOMPATIBLE_ARRAY_COMPARE";
1147 else if (calledMethodAnnotation != null && calledMethodAnnotation.getClassName().equals("org.testng.Assert"))
1169 } else if (calledMethodAnnotation != null && "org.testng.Assert".equals(calledMethodAnnotation.getClassName())) {
11481170 return;
1171 }
11491172 bugAccumulator.accumulateBug(new BugInstance(this, pattern, NORMAL_PRIORITY).addClassAndMethod(methodGen, sourceFile)
11501173 .addFoundAndExpectedType(rhsType_, lhsType_)
11511174 .addSomeSourceForTopTwoStackValues(classContext, method, location)
11591182 addEqualsCheck(rhsType_.getSignature(), handle.getPosition());
11601183 }
11611184
1162 if (result == IncompatibleTypes.SEEMS_OK) return;
1163
1164
1165 if (result.getPriority() > Priorities.LOW_PRIORITY)
1185 if (result == IncompatibleTypes.SEEMS_OK) {
11661186 return;
1167
1168 if (result == IncompatibleTypes.ARRAY_AND_NON_ARRAY || result == IncompatibleTypes.ARRAY_AND_OBJECT) {
1187 }
1188
1189
1190 if (result.getPriority() > Priorities.LOW_PRIORITY) {
1191 return;
1192 }
1193
1194 if (result == IncompatibleTypes.ARRAY_AND_NON_ARRAY || result == IncompatibleTypes.ARRAY_AND_OBJECT) {
11691195 String lhsSig = lhsType_.getSignature();
11701196 String rhsSig = rhsType_.getSignature();
11711197 boolean allOk = checkForWeirdEquals(lhsSig, rhsSig, new HashSet<XMethod>());
1172 if (allOk)
1198 if (allOk) {
11731199 priorityModifier += 2;
1200 }
11741201 bugAccumulator.accumulateBug(new BugInstance(this, "EC_ARRAY_AND_NONARRAY", result.getPriority() + priorityModifier)
1175 .addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(rhsType_, lhsType_)
1176 .addSomeSourceForTopTwoStackValues(classContext, method, location)
1177 .addOptionalAnnotation(calledMethodAnnotation, MethodAnnotation.METHOD_CALLED),
1178 SourceLineAnnotation.fromVisitedInstruction(this.classContext, methodGen, sourceFile, location.getHandle()));
1202 .addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(rhsType_, lhsType_)
1203 .addSomeSourceForTopTwoStackValues(classContext, method, location)
1204 .addOptionalAnnotation(calledMethodAnnotation, MethodAnnotation.METHOD_CALLED),
1205 SourceLineAnnotation.fromVisitedInstruction(this.classContext, methodGen, sourceFile, location.getHandle()));
11791206 } else if (result == IncompatibleTypes.INCOMPATIBLE_CLASSES) {
11801207 String lhsSig = lhsType_.getSignature();
11811208 String rhsSig = rhsType_.getSignature();
11871214 if (true) {
11881215 Set<XMethod> targets = new HashSet<XMethod>();
11891216 boolean allOk = checkForWeirdEquals(lhsSig, rhsSig, targets);
1190 if (allOk)
1217 if (allOk) {
11911218 priorityModifier += 2;
1219 }
11921220
11931221 int priority = result.getPriority() + priorityModifier;
11941222 bugAccumulator.accumulateBug(
11951223 new BugInstance(this, "EC_UNRELATED_TYPES", priority)
1196 .addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(rhsType_, lhsType_)
1197 .addSomeSourceForTopTwoStackValues(classContext, method, location).addEqualsMethodUsed(targets)
1198 .addOptionalAnnotation(calledMethodAnnotation, MethodAnnotation.METHOD_CALLED),
1224 .addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(rhsType_, lhsType_)
1225 .addSomeSourceForTopTwoStackValues(classContext, method, location).addEqualsMethodUsed(targets)
1226 .addOptionalAnnotation(calledMethodAnnotation, MethodAnnotation.METHOD_CALLED),
11991227 SourceLineAnnotation.fromVisitedInstruction(this.classContext, methodGen, sourceFile,
12001228 location.getHandle()));
12011229 }
12031231 || result == IncompatibleTypes.UNRELATED_FINAL_CLASS_AND_INTERFACE) {
12041232 bugAccumulator.accumulateBug(
12051233 new BugInstance(this, "EC_UNRELATED_CLASS_AND_INTERFACE", result.getPriority() + priorityModifier)
1206 .addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(rhsType_, lhsType_)
1207 .addSomeSourceForTopTwoStackValues(classContext, method, location)
1208 .addEqualsMethodUsed(DescriptorFactory.createClassDescriptorFromSignature(lhsType_.getSignature()))
1209 .addOptionalAnnotation(calledMethodAnnotation, MethodAnnotation.METHOD_CALLED),
1234 .addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(rhsType_, lhsType_)
1235 .addSomeSourceForTopTwoStackValues(classContext, method, location)
1236 .addEqualsMethodUsed(DescriptorFactory.createClassDescriptorFromSignature(lhsType_.getSignature()))
1237 .addOptionalAnnotation(calledMethodAnnotation, MethodAnnotation.METHOD_CALLED),
12101238 SourceLineAnnotation.fromVisitedInstruction(this.classContext, methodGen, sourceFile, location.getHandle()));
12111239 } else if (result == IncompatibleTypes.UNRELATED_INTERFACES) {
12121240 bugAccumulator.accumulateBug(
12131241 new BugInstance(this, "EC_UNRELATED_INTERFACES", result.getPriority() + priorityModifier)
1214 .addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(rhsType_, lhsType_)
1215 .addSomeSourceForTopTwoStackValues(classContext, method, location)
1216 .addEqualsMethodUsed(DescriptorFactory.createClassDescriptorFromSignature(lhsType_.getSignature()))
1217 .addOptionalAnnotation(calledMethodAnnotation, MethodAnnotation.METHOD_CALLED),
1242 .addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(rhsType_, lhsType_)
1243 .addSomeSourceForTopTwoStackValues(classContext, method, location)
1244 .addEqualsMethodUsed(DescriptorFactory.createClassDescriptorFromSignature(lhsType_.getSignature()))
1245 .addOptionalAnnotation(calledMethodAnnotation, MethodAnnotation.METHOD_CALLED),
12181246 SourceLineAnnotation.fromVisitedInstruction(this.classContext, methodGen, sourceFile, location.getHandle()));
12191247 } else if (result != IncompatibleTypes.UNCHECKED && result.getPriority() <= Priorities.LOW_PRIORITY) {
12201248 bugAccumulator.accumulateBug(new BugInstance(this, "EC_UNRELATED_TYPES", result.getPriority() + priorityModifier)
1221 .addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(rhsType_, lhsType_)
1222 .addSomeSourceForTopTwoStackValues(classContext, method, location)
1223 .addOptionalAnnotation(calledMethodAnnotation, MethodAnnotation.METHOD_CALLED),
1224 SourceLineAnnotation.fromVisitedInstruction(this.classContext, methodGen, sourceFile, location.getHandle()));
1225 }
1226
1227 }
1228
1229 /**
1230 * @param cpg
1231 * @param inv
1232 */
1249 .addClassAndMethod(methodGen, sourceFile).addFoundAndExpectedType(rhsType_, lhsType_)
1250 .addSomeSourceForTopTwoStackValues(classContext, method, location)
1251 .addOptionalAnnotation(calledMethodAnnotation, MethodAnnotation.METHOD_CALLED),
1252 SourceLineAnnotation.fromVisitedInstruction(this.classContext, methodGen, sourceFile, location.getHandle()));
1253 }
1254
1255 }
1256
12331257 public @CheckForNull MethodAnnotation getMethodCalledAnnotation(ConstantPoolGen cpg, InvokeInstruction inv) {
12341258 MethodDescriptor invokedMethod = getInvokedMethod(cpg, inv);
1235 boolean standardEquals = invokedMethod.getName().equals("equals")
1236 && invokedMethod.getSignature().equals("(Ljava/lang/Object;)Z") && !invokedMethod.isStatic();
1259 boolean standardEquals = "equals".equals(invokedMethod.getName())
1260 && "(Ljava/lang/Object;)Z".equals(invokedMethod.getSignature()) && !invokedMethod.isStatic();
12371261 return standardEquals ? null : MethodAnnotation.fromMethodDescriptor(invokedMethod);
12381262 }
12391263
1240 /**
1241 * @param cpg
1242 * @param inv
1243 * @return
1244 */
12451264 public MethodDescriptor getInvokedMethod(ConstantPoolGen cpg, InvokeInstruction inv) {
12461265 String invoked = inv.getClassName(cpg);
12471266 String methodName = inv.getMethodName(cpg);
12481267 String methodSig = inv.getSignature(cpg);
12491268 MethodDescriptor invokedMethod =
1250 DescriptorFactory.instance().getMethodDescriptor(ClassName.toSlashedClassName(invoked), methodName, methodSig, inv instanceof INVOKESTATIC);
1269 DescriptorFactory.instance().getMethodDescriptor(ClassName.toSlashedClassName(invoked), methodName, methodSig, inv instanceof INVOKESTATIC);
12511270 return invokedMethod;
12521271 }
12531272
1254 /**
1255 * @param lhsSig
1256 * @param rhsSig
1257 * @param targets
1258 * @return
1259 */
12601273 private boolean checkForWeirdEquals(String lhsSig, String rhsSig, Set<XMethod> targets) {
12611274 boolean allOk = false;
12621275 try {
12681281 targets.addAll(Hierarchy2.resolveVirtualMethodCallTargets(expectedClassDescriptor, "equals", "(Ljava/lang/Object;)Z",
12691282 false, false));
12701283 allOk = targets.size() > 0;
1271 for (XMethod m2 : targets)
1272 if (!classSummary.mightBeEqualTo(m2.getClassDescriptor(), actualClassDescriptor))
1284 for (XMethod m2 : targets) {
1285 if (!classSummary.mightBeEqualTo(m2.getClassDescriptor(), actualClassDescriptor)) {
12731286 allOk = false;
1287 }
1288 }
12741289
12751290 } catch (ClassNotFoundException e) {
12761291 AnalysisContext.reportMissingClass(e);
12781293 return allOk;
12791294 }
12801295
1296 @Override
12811297 public void report() {
12821298 // do nothing
12831299 }
12841300 }
12851301
1286 // vim:ts=3
5151
5252 boolean fieldIsStatic;
5353
54 private BugAccumulator bugAccumulator;
54 private final BugAccumulator bugAccumulator;
5555
5656 // private LocalVariableTable variableNames;
5757
7373 @Override
7474 public void visit(Method obj) {
7575 check = publicClass && (obj.getAccessFlags() & (ACC_PUBLIC)) != 0;
76 if (!check)
76 if (!check) {
7777 return;
78 }
7879 staticMethod = (obj.getAccessFlags() & (ACC_STATIC)) != 0;
7980 // variableNames = obj.getLocalVariableTable();
8081 parameterCount = getNumberMethodArguments();
8182
82 if (!staticMethod)
83 if (!staticMethod) {
8384 parameterCount++;
85 }
8486
8587 thisOnTOS = false;
8688 fieldOnTOS = false;
9193
9294 @Override
9395 public void visit(Code obj) {
94 if (check)
96 if (check) {
9597 super.visit(obj);
98 }
9699 }
97100
98101 @Override
99102 public void sawOpcode(int seen) {
100103
101 if (!check)
104 if (!check) {
102105 return;
106 }
103107
104108 if (staticMethod && seen == PUTSTATIC && MutableStaticFields.mutableSignature(getSigConstantOperand())) {
105109 OpcodeStack.Item top = stack.getStackItem(0);
106 if (isPotentialCapture(top))
110 if (isPotentialCapture(top)) {
107111 bugAccumulator.accumulateBug(
108112 new BugInstance(this, "EI_EXPOSE_STATIC_REP2", NORMAL_PRIORITY)
109 .addClassAndMethod(this)
110 .addReferencedField(this)
111 .add(LocalVariableAnnotation.getLocalVariableAnnotation(getMethod(), top.getRegisterNumber(),
112 getPC(), getPC() - 1)), this);
113 .addClassAndMethod(this)
114 .addReferencedField(this)
115 .add(LocalVariableAnnotation.getLocalVariableAnnotation(getMethod(), top.getRegisterNumber(),
116 getPC(), getPC() - 1)), this);
117 }
113118 }
114119 if (!staticMethod && seen == PUTFIELD && MutableStaticFields.mutableSignature(getSigConstantOperand())) {
115120 OpcodeStack.Item top = stack.getStackItem(0);
116121 OpcodeStack.Item target = stack.getStackItem(1);
117 if (isPotentialCapture(top) && target.getRegisterNumber() == 0)
122 if (isPotentialCapture(top) && target.getRegisterNumber() == 0) {
118123 bugAccumulator.accumulateBug(
119124 new BugInstance(this, "EI_EXPOSE_REP2", NORMAL_PRIORITY)
120 .addClassAndMethod(this)
121 .addReferencedField(this)
122 .add(LocalVariableAnnotation.getLocalVariableAnnotation(getMethod(), top.getRegisterNumber(),
123 getPC(), getPC() - 1)), this);
125 .addClassAndMethod(this)
126 .addReferencedField(this)
127 .add(LocalVariableAnnotation.getLocalVariableAnnotation(getMethod(), top.getRegisterNumber(),
128 getPC(), getPC() - 1)), this);
129 }
124130 }
125131
126132 if (seen == ALOAD_0 && !staticMethod) {
152158 }
153159 thisOnTOS = false;
154160 if (check && fieldOnTOS && seen == ARETURN
155 /*
156 * && !sigOnStack.equals("Ljava/lang/String;") &&
157 * sigOnStack.indexOf("Exception") == -1 && sigOnStack.indexOf("[") >= 0
158 */
159 && nameOnStack.indexOf("EMPTY") == -1 && MutableStaticFields.mutableSignature(sigOnStack)) {
161 /*
162 * && !sigOnStack.equals("Ljava/lang/String;") &&
163 * sigOnStack.indexOf("Exception") == -1 && sigOnStack.indexOf("[") >= 0
164 */
165 && nameOnStack.indexOf("EMPTY") == -1 && MutableStaticFields.mutableSignature(sigOnStack)) {
160166 bugAccumulator.accumulateBug(new BugInstance(this, staticMethod ? "MS_EXPOSE_REP" : "EI_EXPOSE_REP", NORMAL_PRIORITY)
161 .addClassAndMethod(this).addField(classNameOnStack, nameOnStack, sigOnStack, fieldIsStatic), this);
167 .addClassAndMethod(this).addField(classNameOnStack, nameOnStack, sigOnStack, fieldIsStatic), this);
162168 }
163169
164170 fieldOnTOS = false;
166172 }
167173
168174 private boolean isPotentialCapture(OpcodeStack.Item top) {
169 if (!top.isInitialParameter())
175 if (!top.isInitialParameter()) {
170176 return false;
171 if ((getMethod().getAccessFlags() & ACC_VARARGS) == 0)
177 }
178 if ((getMethod().getAccessFlags() & ACC_VARARGS) == 0) {
172179 return true;
180 }
173181 if (top.getRegisterNumber() == parameterCount - 1)
182 {
174183 return false; // var-arg parameter
184 }
175185 return true;
176186
177187 }
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2007 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 package edu.umd.cs.findbugs.detect;
19
20 import java.math.BigDecimal;
21 import java.math.MathContext;
22 import java.math.RoundingMode;
23 import java.util.HashSet;
24 import java.util.Set;
25
26 import org.apache.bcel.classfile.Constant;
27 import org.apache.bcel.classfile.ConstantDouble;
28 import org.apache.bcel.classfile.ConstantFloat;
29 import org.apache.bcel.classfile.ConstantPool;
30 import org.apache.bcel.classfile.JavaClass;
31
32 import edu.umd.cs.findbugs.BugAccumulator;
33 import edu.umd.cs.findbugs.BugInstance;
34 import edu.umd.cs.findbugs.BugReporter;
35 import edu.umd.cs.findbugs.BytecodeScanningDetector;
36 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
37 import edu.umd.cs.findbugs.ba.ClassContext;
38
39 public class FindRoughConstants extends BytecodeScanningDetector {
40
41 static class BadConstant {
42 double base;
43 double factor;
44 String replacement;
45 double value;
46 int basePriority;
47
48 Set<Number> approxSet = new HashSet<Number>();
49
50 BadConstant(double base, double factor, String replacement, int basePriority) {
51 this.base = base;
52 this.factor = factor;
53 this.value = this.base * this.factor;
54 this.replacement = replacement;
55 this.basePriority = basePriority;
56 BigDecimal valueBig = BigDecimal.valueOf(value);
57 BigDecimal baseBig = BigDecimal.valueOf(base);
58 BigDecimal factorBig = BigDecimal.valueOf(factor);
59 for (int prec = 0; prec < 14; prec++) {
60 addApprox(baseBig.round(new MathContext(prec, RoundingMode.FLOOR)).multiply(factorBig));
61 addApprox(baseBig.round(new MathContext(prec, RoundingMode.CEILING)).multiply(factorBig));
62 addApprox(valueBig.round(new MathContext(prec, RoundingMode.FLOOR)));
63 addApprox(valueBig.round(new MathContext(prec, RoundingMode.CEILING)));
64 }
65 }
66
67 @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY")
68 public boolean exact(Number candidate) {
69 if (candidate instanceof Double) {
70 return candidate.doubleValue() == value;
71 }
72 return candidate.floatValue() == (float) value;
73 }
74
75 public double diff(double candidate) {
76 return Math.abs(value - candidate) / value;
77 }
78
79 public boolean equalPrefix(Number candidate) {
80 return approxSet.contains(candidate);
81 }
82
83 @SuppressFBWarnings("FE_FLOATING_POINT_EQUALITY")
84 private void addApprox(BigDecimal roundFloor) {
85 double approxDouble = roundFloor.doubleValue();
86 if (approxDouble != value && Math.abs(approxDouble - value) / value < 0.001) {
87 approxSet.add(approxDouble);
88 }
89 float approxFloat = roundFloor.floatValue();
90 if (Math.abs(approxFloat - value) / value < 0.001) {
91 approxSet.add(approxFloat);
92 approxSet.add((double) approxFloat);
93 }
94 }
95 }
96
97 private static final BadConstant[] badConstants = new BadConstant[] {
98 new BadConstant(Math.PI, 1, "Math.PI", HIGH_PRIORITY),
99 new BadConstant(Math.PI, 1/2.0, "Math.PI/2", NORMAL_PRIORITY),
100 new BadConstant(Math.PI, 1/3.0, "Math.PI/3", LOW_PRIORITY),
101 new BadConstant(Math.PI, 1/4.0, "Math.PI/4", LOW_PRIORITY),
102 new BadConstant(Math.PI, 2, "2*Math.PI", NORMAL_PRIORITY),
103 new BadConstant(Math.E, 1, "Math.E", LOW_PRIORITY)
104 };
105
106 private final BugAccumulator bugAccumulator;
107
108 private BugInstance lastBug;
109 private int lastPriority;
110
111 public FindRoughConstants(BugReporter bugReporter) {
112 this.bugAccumulator = new BugAccumulator(bugReporter);
113 }
114
115 @Override
116 public void visitClassContext(ClassContext classContext) {
117 if(hasInterestingConstant(classContext.getJavaClass().getConstantPool())) {
118 super.visitClassContext(classContext);
119 }
120 }
121
122 @Override
123 public void visitAfter(JavaClass obj) {
124 bugAccumulator.reportAccumulatedBugs();
125 }
126
127 @Override
128 public void sawOpcode(int seen) {
129 if (seen == LDC || seen == LDC_W || seen == LDC2_W) {
130 Constant c = getConstantRefOperand();
131 if (c instanceof ConstantFloat) {
132 checkConst(((ConstantFloat) c).getBytes());
133 } else if (c instanceof ConstantDouble) {
134 checkConst(((ConstantDouble) c).getBytes());
135 }
136 return;
137 }
138 // Lower priority if the constant is put into array immediately or after the boxing:
139 // this is likely to be just similar number in some predefined dataset (like lookup table)
140 if(seen == INVOKESTATIC && lastBug != null) {
141 if (getNextOpcode() == AASTORE
142 && getNameConstantOperand().equals("valueOf")
143 && (getClassConstantOperand().equals("java/lang/Double") || getClassConstantOperand().equals(
144 "java/lang/Float"))) {
145 lastBug = ((BugInstance)lastBug.clone());
146 lastBug.setPriority(lastPriority+1);
147 bugAccumulator.forgetLastBug();
148 bugAccumulator.accumulateBug(lastBug, this);
149 }
150 }
151 lastBug = null;
152 }
153
154 private boolean hasInterestingConstant(ConstantPool cp) {
155 for(Constant constant : cp.getConstantPool()) {
156 if(constant instanceof ConstantFloat) {
157 float val = ((ConstantFloat)constant).getBytes();
158 if(isInteresting(val, val)) {
159 return true;
160 }
161 }
162 if(constant instanceof ConstantDouble) {
163 double val = ((ConstantDouble)constant).getBytes();
164 if(isInteresting(val, val)) {
165 return true;
166 }
167 }
168 }
169 return false;
170 }
171
172 private boolean isInteresting(Number constValue, double candidate) {
173 for (BadConstant badConstant : badConstants) {
174 if(getPriority(badConstant, constValue, candidate) < IGNORE_PRIORITY) {
175 return true;
176 }
177 }
178 return false;
179 }
180
181 private int getPriority(BadConstant badConstant, Number constValue, double candidate) {
182 if (badConstant.exact(constValue)) {
183 return IGNORE_PRIORITY;
184 }
185 double diff = badConstant.diff(candidate);
186 if (diff > 1e-3) {
187 return IGNORE_PRIORITY;
188 }
189 if (badConstant.equalPrefix(constValue)) {
190 return diff > 1e-4 ? badConstant.basePriority+1 :
191 diff < 1e-6 ? badConstant.basePriority-1 : badConstant.basePriority;
192 }
193 if (diff > 1e-7) {
194 return IGNORE_PRIORITY;
195 }
196 return badConstant.basePriority+1;
197 }
198
199 private void checkConst(Number constValue) {
200 double candidate = constValue.doubleValue();
201 if (Double.isNaN(candidate) || Double.isInfinite(candidate)) {
202 return;
203 }
204 for (BadConstant badConstant : badConstants) {
205 int priority = getPriority(badConstant, constValue, candidate);
206 if(getNextOpcode() == FASTORE || getNextOpcode() == DASTORE) {
207 priority++;
208 }
209 if(priority < IGNORE_PRIORITY) {
210 lastPriority = priority;
211 lastBug = new BugInstance(this, "CNT_ROUGH_CONSTANT_VALUE", priority).addClassAndMethod(this)
212 .addString(constValue.toString()).addString(badConstant.replacement);
213 bugAccumulator.accumulateBug(lastBug, this);
214 return;
215 }
216 }
217 }
218 }
5858
5959 @Override
6060 public void sawOpcode(int seen) {
61 if (alreadySawStart)
61 if (alreadySawStart) {
6262 return;
63 if ((seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE) && getSigConstantOperand().equals("()V")
63 }
64 if ((seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE) && "()V".equals(getSigConstantOperand())
6465 && isThread(getDottedClassConstantOperand())) {
65 if (getNameConstantOperand().equals("start"))
66 if ("start".equals(getNameConstantOperand())) {
6667 alreadySawStart = true;
67 else {
68 boolean isJustThread = !getDottedClassConstantOperand().equals("java.lang.Thread");
69 if (amVisitingMainMethod() && getPC() == getCode().getLength() - 4 && isJustThread)
68 } else {
69 boolean isJustThread = !"java.lang.Thread".equals(getDottedClassConstantOperand());
70 if (amVisitingMainMethod() && getPC() == getCode().getLength() - 4 && isJustThread) {
7071 return;
71 else if (getNameConstantOperand().equals("run"))
72 } else if ("run".equals(getNameConstantOperand())) {
7273 bugAccumulator.accumulateBug(new BugInstance(this, "RU_INVOKE_RUN", isJustThread ? HIGH_PRIORITY
7374 : NORMAL_PRIORITY).addClassAndMethod(this), this);
75 }
7476 }
7577 }
7678 }
5555 XField putFieldXField;
5656
5757 int lastMethodCall;
58
58
5959 static final boolean DEBUG = SystemProperties.getBoolean("fsc.debug");
6060 @Override
6161 public void visit(Code obj) {
62 if (DEBUG)
62 if (DEBUG) {
6363 System.out.println(getFullyQualifiedMethodName());
64 }
6465 whichRegister = -1;
6566 registerLoadCount = 0;
6667 lastMethodCall = -1;
6869 super.visit(obj);
6970 resetDoubleAssignmentState();
7071 bugAccumulator.reportAccumulatedBugs();
71 if (DEBUG)
72 if (DEBUG) {
7273 System.out.println();
74 }
7375 }
7476
7577 private void resetDoubleAssignmentState() {
8688
8789 @Override
8890 public void sawOpcode(int seen) {
89 if (DEBUG)
91 if (DEBUG) {
9092 System.out.printf("%3d %-15s %s%n", getPC(), OPCODE_NAMES[seen], stack);
91
92
93 if (stack.hasIncomingBranches(getPC()))
93 }
94
95
96 if (stack.hasIncomingBranches(getPC())) {
9497 resetDoubleAssignmentState();
98 }
9599
96100 if (seen == PUTFIELD) {
97101 OpcodeStack.Item obj = stack.getStackItem(1);
100104 XClass x = getXClassOperand();
101105
102106 checkPUTFIELD: if (putFieldPC + 10 > getPC() && f != null && obj != null && f.equals(putFieldXField)
103 && !f.isSynthetic() && obj.equals(putFieldObj) && x != null) {
107 && !f.isSynthetic() && obj.sameValue(putFieldObj) && x != null) {
108
104109
105110 LineNumberTable table = getCode().getLineNumberTable();
106111 if (table != null) {
107112 int first = table.getSourceLine(putFieldPC);
108113 int second = table.getSourceLine(getPC());
109 if (first + 1 != second && first != second)
114 if (first + 1 != second && first != second) {
110115 break checkPUTFIELD;
111 } else if (putFieldPC + 3 != getPC())
116 }
117 } else if (putFieldPC + 3 != getPC()) {
112118 break checkPUTFIELD;
119 }
113120
114121 int priority = NORMAL_PRIORITY;
115 if (value.equals(putFieldValue) && putFieldPC + 3 != getPC())
116 priority++;
122 if (value.equals(putFieldValue) && putFieldPC + 3 != getPC()) {
123 priority++;
124 }
117125 boolean storeOfDefaultValue = putFieldValue.isNull() || putFieldValue.hasConstantValue(0);
118 if (storeOfDefaultValue)
119 priority++;
120 if (f.isVolatile())
121 priority++;
126 if (storeOfDefaultValue) {
127 priority++;
128 }
129 if (f.isVolatile()) {
130 priority++;
131 }
122132 XField intendedTarget = null;
123133
124134 double minimumDistance = 2;
125135 int matches = 0;
126 for (XField f2 : x.getXFields())
136 for (XField f2 : x.getXFields()) {
127137 if (!f.equals(f2) && !f2.isStatic() && !f2.isFinal() && !f2.isSynthetic()
128138 && f2.getSignature().equals(f.getSignature())) {
129139
135145 }
136146
137147 }
138 if (minimumDistance > 0.6 && (matches > 1 || storeOfDefaultValue))
148 }
149 if (minimumDistance > 0.6 && (matches > 1 || storeOfDefaultValue)) {
139150 intendedTarget = null;
140 else if (intendedTarget != null)
151 } else if (intendedTarget != null) {
141152 priority--;
153 }
142154 BugInstance bug = new BugInstance(this, "SA_FIELD_DOUBLE_ASSIGNMENT", priority).addClassAndMethod(this)
143155 .addReferencedField(this);
144 if (intendedTarget != null)
156 if (intendedTarget != null) {
145157 bug.addField(intendedTarget).describe(FieldAnnotation.DID_YOU_MEAN_ROLE);
158 }
146159
147160 bugAccumulator.accumulateBug(bug, this);
148161 }
151164 putFieldObj = obj;
152165 putFieldValue = value;
153166
154 } else if (isReturn(seen))
167 } else if (isReturn(seen)) {
155168 resetDoubleAssignmentState();
156 else if (seen == GETFIELD && Util.nullSafeEquals(getXFieldOperand(), putFieldXField)) {
169 } else if (seen == GETFIELD && Util.nullSafeEquals(getXFieldOperand(), putFieldXField)) {
157170 OpcodeStack.Item obj = stack.getStackItem(0);
158 if (obj.equals(putFieldObj))
171 if (obj.sameValue(putFieldObj)) {
159172 resetDoubleAssignmentState();
173 }
160174 }
161175
162176 switch (seen) {
163177 case INVOKEVIRTUAL:
164178 case INVOKEINTERFACE:
165 // case INVOKESTATIC:
166 if (getClassName().toLowerCase().indexOf("test") >= 0)
179 // case INVOKESTATIC:
180 if (getClassName().toLowerCase().indexOf("test") >= 0) {
167181 break;
168 if (getMethodName().toLowerCase().indexOf("test") >= 0)
182 }
183 if (getMethodName().toLowerCase().indexOf("test") >= 0) {
169184 break;
170 if (getSuperclassName().toLowerCase().indexOf("test") >= 0)
185 }
186 if (getSuperclassName().toLowerCase().indexOf("test") >= 0) {
171187 break;
172 if (getNextOpcode() == POP)
188 }
189 if (getNextOpcode() == POP) {
173190 break;
191 }
174192 String name = getNameConstantOperand();
175193
176194 boolean booleanComparisonMethod = FindSelfComparison2.booleanComparisonMethod(name);
180198 int numParameters = parser.getNumParameters();
181199 if ((numParameters == 1 || seen == INVOKESTATIC && numParameters == 2)
182200 && (booleanComparisonMethod && sig.endsWith(";)Z")
183 || FindSelfComparison2.comparatorMethod(name) && sig.endsWith(";)I")))
201 || FindSelfComparison2.comparatorMethod(name) && sig.endsWith(";)I"))) {
184202 checkForSelfOperation(seen, "COMPARISON");
203 }
185204 }
186205 break;
187206
210229 case IF_ICMPLT:
211230 case IF_ICMPGE:
212231 checkForSelfOperation(seen, "COMPARISON");
232 break;
233 default:
234 break;
213235 }
214236 if (isRegisterLoad() && seen != IINC) {
215 if (getRegisterOperand() == whichRegister)
237 if (getRegisterOperand() == whichRegister) {
216238 registerLoadCount++;
217 else {
239 } else {
218240 whichRegister = getRegisterOperand();
219241 registerLoadCount = 1;
220242 }
222244 whichRegister = -1;
223245 registerLoadCount = 0;
224246 }
225
226 if (isMethodCall())
247
248 if (isMethodCall()) {
227249 lastMethodCall = getPC();
250 }
228251
229252 }
230253
235258 private void checkForSelfOperation(int opCode, String op) {
236259 {
237260
238
261
239262 OpcodeStack.Item item0 = stack.getStackItem(0);
240263 OpcodeStack.Item item1 = stack.getStackItem(1);
241264
242 if (item0.getSignature().equals("D") || item0.getSignature().equals("F"))
265 if ("D".equals(item0.getSignature()) || "F".equals(item0.getSignature())) {
243266 return;
244 if (item1.getSignature().equals("D") || item1.getSignature().equals("F"))
267 }
268 if ("D".equals(item1.getSignature()) || "F".equals(item1.getSignature())) {
245269 return;
270 }
246271
247272 BitSet linesMentionedMultipleTimes = getClassContext().linesMentionedMultipleTimes(getMethod());
248273 SourceLineAnnotation source = SourceLineAnnotation.fromVisitedInstruction(this);
254279 int line0 = lineNumberTable.getSourceLine(item0.getPC());
255280 int line1 = lineNumberTable.getSourceLine(item1.getPC());
256281 int firstPos = Math.min(item0.getPC(), item1.getPC());
257 if (firstPos < lastMethodCall && line0 != line1)
282 if (firstPos < lastMethodCall && line0 != line1) {
258283 return;
284 }
259285
260286 linesDifference = Math.abs(line0 - line1);
261287 } else {
262288 int firstPos = Math.min(item0.getPC(), item1.getPC());
263289 int lastPos = Math.max(item0.getPC(), item1.getPC());
264290
265 if (firstPos < lastMethodCall && lastPos - firstPos > 4)
291 if (firstPos < lastMethodCall && lastPos - firstPos > 4) {
266292 return;
293 }
267294 linesDifference = (lastPos - firstPos)/10;
268295 }
269296 }
273300 int fr1 = item1.getFieldLoadedFromRegister();
274301 if (field0 != null && field0.equals(field1) && (field0.isStatic() || fr0 != -1 && fr0 == fr1)) {
275302 int priority = NORMAL_PRIORITY;
276 if (field0.isVolatile())
277 priority++;
303 if (field0.isVolatile()) {
304 priority++;
305 }
278306 if (linesDifference > 1) {
279 if (possibleClone)
307 if (possibleClone) {
280308 return;
281 priority++;
282 }
283
309 }
310 priority++;
311 }
312
284313 BugInstance bug = new BugInstance(this, "SA_FIELD_SELF_" + op, priority)
285 .addClassAndMethod(this).addField(field0);
286
287 if (this.isMethodCall())
314 .addClassAndMethod(this).addField(field0);
315
316 if (this.isMethodCall()) {
288317 bug.addCalledMethod(this);
318 }
289319 bugAccumulator.accumulateBug(bug, this);
290320 }
291321
292 else if (opCode == IXOR && item0.equals(item1)) {
322 else if (opCode == IXOR && item0.sameValue(item1)) {
293323 LocalVariableAnnotation localVariableAnnotation = LocalVariableAnnotation.getLocalVariableAnnotation(this, item0);
294 if (localVariableAnnotation != null)
324 if (localVariableAnnotation != null) {
295325 bugAccumulator.accumulateBug(
296326 new BugInstance(this, "SA_LOCAL_SELF_" + op, linesDifference > 1 ? NORMAL_PRIORITY : HIGH_PRIORITY).addClassAndMethod(this).add(
297327 localVariableAnnotation), this);
328 }
298329 }
299330 }
300331 }
00 package edu.umd.cs.findbugs.detect;
11
2 import static org.apache.bcel.Constants.DCMPG;
3 import static org.apache.bcel.Constants.DCMPL;
4 import static org.apache.bcel.Constants.FCMPG;
5 import static org.apache.bcel.Constants.FCMPL;
6 import static org.apache.bcel.Constants.IAND;
7 import static org.apache.bcel.Constants.IF_ACMPEQ;
8 import static org.apache.bcel.Constants.IF_ACMPNE;
9 import static org.apache.bcel.Constants.IF_ICMPEQ;
10 import static org.apache.bcel.Constants.IF_ICMPGE;
11 import static org.apache.bcel.Constants.IF_ICMPGT;
12 import static org.apache.bcel.Constants.IF_ICMPLE;
13 import static org.apache.bcel.Constants.IF_ICMPLT;
14 import static org.apache.bcel.Constants.IF_ICMPNE;
15 import static org.apache.bcel.Constants.INVOKEINTERFACE;
16 import static org.apache.bcel.Constants.INVOKEVIRTUAL;
17 import static org.apache.bcel.Constants.IOR;
18 import static org.apache.bcel.Constants.ISUB;
19 import static org.apache.bcel.Constants.IXOR;
20 import static org.apache.bcel.Constants.LAND;
21 import static org.apache.bcel.Constants.LCMP;
22 import static org.apache.bcel.Constants.LOR;
23 import static org.apache.bcel.Constants.LSUB;
24 import static org.apache.bcel.Constants.LXOR;
25 import static org.apache.bcel.Constants.POP;
2 import static org.apache.bcel.Constants.*;
263
274 import java.util.BitSet;
285 import java.util.Iterator;
3714 import edu.umd.cs.findbugs.BugInstance;
3815 import edu.umd.cs.findbugs.BugReporter;
3916 import edu.umd.cs.findbugs.Detector;
40 import edu.umd.cs.findbugs.FieldAnnotation;
4117 import edu.umd.cs.findbugs.SourceLineAnnotation;
4218 import edu.umd.cs.findbugs.SystemProperties;
4319 import edu.umd.cs.findbugs.ba.CFG;
6137 this.bugReporter = bugReporter;
6238 }
6339
40 @Override
6441 public void visitClassContext(ClassContext classContext) {
6542 Method[] methodList = classContext.getJavaClass().getMethods();
6643
6744 for (Method method : methodList) {
68 if (method.getCode() == null)
45 if (method.getCode() == null) {
6946 continue;
47 }
7048
7149 try {
7250 analyzeMethod(classContext, method);
7351 } catch (MethodUnprofitableException mue) {
74 if (SystemProperties.getBoolean("unprofitable.debug")) // otherwise
75 // don't
76 // report
52 if (SystemProperties.getBoolean("unprofitable.debug")) {
53 // don't
54 // report
7755 bugReporter.logError("skipping unprofitable method in " + getClass().getName());
56 }
7857 } catch (CFGBuilderException e) {
7958 bugReporter.logError("Detector " + this.getClass().getName() + " caught exception", e);
8059 } catch (DataflowAnalysisException e) {
8362 }
8463 }
8564
86 static boolean booleanComparisonMethod(String methodName) {
87 return methodName.equals("equals") ||methodName.equals("endsWith") || methodName.equals("startsWith")
88 || methodName.equals("contains") || methodName.equals("equalsIgnoreCase");
89 }
90
91 static boolean comparatorMethod(String methodName) {
92 return methodName.equals("compareTo") || methodName.equals("compareToIgnoreCase");
65 static boolean booleanComparisonMethod(String methodName) {
66 return "equals".equals(methodName) ||"endsWith".equals(methodName) || "startsWith".equals(methodName)
67 || "contains".equals(methodName) || "equalsIgnoreCase".equals(methodName);
68 }
69
70 static boolean comparatorMethod(String methodName) {
71 return "compareTo".equals(methodName) || "compareToIgnoreCase".equals(methodName);
9372 }
9473 private void analyzeMethod(ClassContext classContext, Method method) throws CFGBuilderException, DataflowAnalysisException {
9574 CFG cfg = classContext.getCFG(method);
10887 InvokeInstruction iins = (InvokeInstruction) ins;
10988 String invoking = iins.getName(cpg);
11089 if ( comparatorMethod(invoking) || booleanComparisonMethod(invoking) ) {
111 if (methodGen.getName().toLowerCase().indexOf("test") >= 0)
112 break;
113 if (methodGen.getClassName().toLowerCase().indexOf("test") >= 0)
114 break;
115 if (classContext.getJavaClass().getSuperclassName().toLowerCase().indexOf("test") >= 0)
116 break;
117 if (location.getHandle().getNext().getInstruction().getOpcode() == POP)
118 break;
90 if (methodGen.getName().toLowerCase().indexOf("test") >= 0) {
91 break;
92 }
93 if (methodGen.getClassName().toLowerCase().indexOf("test") >= 0) {
94 break;
95 }
96 if (classContext.getJavaClass().getSuperclassName().toLowerCase().indexOf("test") >= 0) {
97 break;
98 }
99 if (location.getHandle().getNext().getInstruction().getOpcode() == POP) {
100 break;
101 }
119102 String sig = iins.getSignature(cpg);
120103
121104 SignatureParser parser = new SignatureParser(sig);
122105 if (parser.getNumParameters() == 1
123 && ( booleanComparisonMethod(invoking) && sig.endsWith(";)Z") || comparatorMethod(invoking) && sig.endsWith(";)I")))
106 && ( booleanComparisonMethod(invoking) && sig.endsWith(";)Z") || comparatorMethod(invoking) && sig.endsWith(";)I"))) {
124107 checkForSelfOperation(classContext, location, valueNumberDataflow, "COMPARISON", method, methodGen,
125108 sourceFile);
109 }
126110
127111 }
128112 break;
152136 case IF_ICMPLT:
153137 case IF_ICMPGE:
154138 checkForSelfOperation(classContext, location, valueNumberDataflow, "COMPARISON", method, methodGen, sourceFile);
155
139 break;
140 default:
141 break;
156142 }
157143
158144 }
160146
161147 /**
162148 * @param classContext
163 * TODO
164149 * @param location
165150 * @param method
166 * TODO
167151 * @param methodGen
168 * TODO
169152 * @param sourceFile
170 * TODO
171 * @param string
172153 * @throws DataflowAnalysisException
173154 */
174155 private void checkForSelfOperation(ClassContext classContext, Location location, ValueNumberDataflow valueNumberDataflow,
175156 String op, Method method, MethodGen methodGen, String sourceFile) throws DataflowAnalysisException {
176157 ValueNumberFrame frame = valueNumberDataflow.getFactAtLocation(location);
177 if (!frame.isValid())
178 return;
158 if (!frame.isValid()) {
159 return;
160 }
179161 Instruction ins = location.getHandle().getInstruction();
180162 int opcode = ins.getOpcode();
181163 int offset = 1;
182 if (opcode == LCMP || opcode == LXOR || opcode == LAND || opcode == LOR || opcode == LSUB)
164 if (opcode == LCMP || opcode == LXOR || opcode == LAND || opcode == LOR || opcode == LSUB) {
183165 offset = 2;
166 }
184167 ValueNumber v0 = frame.getStackValue(0);
185168 ValueNumber v1 = frame.getStackValue(offset);
186 if (!v1.equals(v0))
187 return;
188 if (v0.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT) || v0.hasFlag(ValueNumber.CONSTANT_VALUE))
189 return;
169 if (!v1.equals(v0)) {
170 return;
171 }
172 if (v0.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT) || v0.hasFlag(ValueNumber.CONSTANT_VALUE)) {
173 return;
174 }
190175
191176 int priority = HIGH_PRIORITY;
192 if (opcode == ISUB || opcode == LSUB || opcode == INVOKEINTERFACE || opcode == INVOKEVIRTUAL)
177 if (opcode == ISUB || opcode == LSUB || opcode == INVOKEINTERFACE || opcode == INVOKEVIRTUAL) {
193178 priority = NORMAL_PRIORITY;
179 }
194180 XField field = ValueNumberSourceInfo.findXFieldFromValueNumber(method, location, v0, frame);
195181 BugAnnotation annotation;
196182 String prefix;
197183 if (field != null) {
198 if (field.isVolatile())
184 if (field.isVolatile()) {
199185 return;
200 if (true)
186 }
187 if (true) {
201188 return; // don't report these; too many false positives
202 annotation = FieldAnnotation.fromXField(field);
203 prefix = "SA_FIELD_SELF_";
189 }
190 // annotation = FieldAnnotation.fromXField(field);
191 // prefix = "SA_FIELD_SELF_";
204192 } else {
205193 annotation = ValueNumberSourceInfo.findLocalAnnotationFromValueNumber(method, location, v0, frame);
206194 prefix = "SA_LOCAL_SELF_";
207 if (opcode == ISUB)
195 if (opcode == ISUB) {
208196 return; // only report this if simple detector reports it
209 }
210 if (annotation == null)
211 return;
197 }
198 }
199 if (annotation == null) {
200 return;
201 }
212202 SourceLineAnnotation sourceLine = SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen, sourceFile,
213203 location.getHandle());
214204 int line = sourceLine.getStartLine();
215205 BitSet occursMultipleTimes = classContext.linesMentionedMultipleTimes(method);
216 if (line > 0 && occursMultipleTimes.get(line))
217 return;
206 if (line > 0 && occursMultipleTimes.get(line)) {
207 return;
208 }
218209 BugInstance bug = new BugInstance(this, prefix + op, priority).addClassAndMethod(methodGen, sourceFile);
219 if (ins instanceof InvokeInstruction)
210 if (ins instanceof InvokeInstruction) {
220211 bug.addCalledMethod(classContext.getConstantPoolGen(), (InvokeInstruction) ins);
212 }
221213
222214 bug.add(annotation)
223 .addSourceLine(classContext, methodGen, sourceFile, location.getHandle());
215 .addSourceLine(classContext, methodGen, sourceFile, location.getHandle());
224216 bugReporter.reportBug(bug);
225217 }
226218
219 @Override
227220 public void report() {
228221 }
229222
4141
4242 /**
4343 * Find calls to Thread.sleep() made with a lock held.
44 *
44 *
4545 * @author David Hovemeyer
4646 */
4747 public class FindSleepWithLockHeld implements Detector {
5555 this.bugAccumulator = new BugAccumulator(bugReporter);
5656 }
5757
58 @Override
5859 public void visitClassContext(ClassContext classContext) {
5960 JavaClass javaClass = classContext.getJavaClass();
6061
6162 Method[] methodList = javaClass.getMethods();
6263 for (Method method : methodList) {
63 if (method.getCode() == null)
64 if (method.getCode() == null) {
6465 continue;
66 }
6567
66 if (!prescreen(classContext, method))
68 if (!prescreen(classContext, method)) {
6769 continue;
70 }
6871
6972 try {
7073 analyzeMethod(classContext, method);
7881
7982 private boolean prescreen(ClassContext classContext, Method method) {
8083 BitSet bytecodeSet = classContext.getBytecodeSet(method);
81 if (bytecodeSet == null)
84 if (bytecodeSet == null) {
8285 return false;
86 }
8387 // method must acquire a lock
84 if (!bytecodeSet.get(Constants.MONITORENTER) && !method.isSynchronized())
88 if (!bytecodeSet.get(Constants.MONITORENTER) && !method.isSynchronized()) {
8589 return false;
90 }
8691
8792 // and contain a static method invocation
88 if (!bytecodeSet.get(Constants.INVOKESTATIC))
93 if (!bytecodeSet.get(Constants.INVOKESTATIC)) {
8994 return false;
95 }
9096
9197 return true;
9298 }
101107 Location location = i.next();
102108 Instruction ins = location.getHandle().getInstruction();
103109
104 if (!(ins instanceof INVOKESTATIC))
110 if (!(ins instanceof INVOKESTATIC)) {
105111 continue;
112 }
106113
107 if (!isSleep((INVOKESTATIC) ins, classContext.getConstantPoolGen()))
114 if (!isSleep((INVOKESTATIC) ins, classContext.getConstantPoolGen())) {
108115 continue;
116 }
109117
110118 // System.out.println("Found sleep at " + location.getHandle());
111119
121129
122130 private boolean isSleep(INVOKESTATIC ins, ConstantPoolGen cpg) {
123131 String className = ins.getClassName(cpg);
124 if (!className.equals("java.lang.Thread"))
132 if (!"java.lang.Thread".equals(className)) {
125133 return false;
134 }
126135 String methodName = ins.getMethodName(cpg);
127136 String signature = ins.getSignature(cpg);
128137
129 return methodName.equals("sleep") && (signature.equals("(J)V") || signature.equals("(JI)V"));
138 return "sleep".equals(methodName) && ("(J)V".equals(signature) || "(JI)V".equals(signature));
130139 }
131140
141 @Override
132142 public void report() {
133143 }
134144
3434
3535 int start;
3636
37 private BugReporter bugReporter;
37 private final BugReporter bugReporter;
3838
3939 private FieldAnnotation lastFieldSeen;
4040
4444
4545 @Override
4646 public void visit(Method obj) {
47 if (DEBUG)
47 if (DEBUG) {
4848 System.out.println("Saw " + getFullyQualifiedMethodName());
49 }
4950 stage = 0;
5051 }
5152
5960 case ALOAD_2:
6061 case ALOAD_3:
6162 case ALOAD:
62 if (DEBUG)
63 if (DEBUG) {
6364 System.out.println(" ALOAD at PC " + getPC());
65 }
6466 start = getPC();
6567 stage = 1;
6668 break;
6769 case GETSTATIC:
68 if (DEBUG)
70 if (DEBUG) {
6971 System.out.println(" getfield in stage " + stage);
72 }
7073 lastFieldSeen = FieldAnnotation.fromReferencedField(this);
7174 start = getPC();
7275 stage = 2;
7376 break;
7477 case GETFIELD:
75 if (DEBUG)
78 if (DEBUG) {
7679 System.out.println(" getfield in stage " + stage);
80 }
7781 lastFieldSeen = FieldAnnotation.fromReferencedField(this);
7882 if (stage == 1 || stage == 2) {
7983 stage = 2;
80 } else
84 } else {
8185 stage = 0;
86 }
8287 break;
8388 case GOTO:
8489 case IFNE:
8590 case IFEQ:
8691 case IFNULL:
8792 case IFNONNULL:
88 if (DEBUG)
93 if (DEBUG) {
8994 System.out.println(" conditional branch in stage " + stage + " to " + getBranchTarget());
95 }
9096 if (stage == 2 && getBranchTarget() == start) {
9197 bugReporter.reportBug(new BugInstance(this, "SP_SPIN_ON_FIELD", NORMAL_PRIORITY).addClassAndMethod(this)
9298 .addReferencedField(lastFieldSeen).addSourceLine(this, start));
9399 stage = 0;
94 } else if (getBranchTarget() < getPC())
100 } else if (getBranchTarget() < getPC()) {
95101 stage = 0;
102 }
96103 break;
97104 default:
98105 stage = 0;
1818
1919 package edu.umd.cs.findbugs.detect;
2020
21 import java.util.HashSet;
2122 import java.util.Iterator;
23 import java.util.Map;
24 import java.util.Set;
2225 import java.util.regex.Pattern;
2326
2427 import javax.annotation.CheckForNull;
2932 import org.apache.bcel.generic.ConstantPoolGen;
3033 import org.apache.bcel.generic.GETFIELD;
3134 import org.apache.bcel.generic.GETSTATIC;
32 import org.apache.bcel.generic.INVOKEINTERFACE;
3335 import org.apache.bcel.generic.INVOKEVIRTUAL;
3436 import org.apache.bcel.generic.Instruction;
3537 import org.apache.bcel.generic.InstructionHandle;
4446 import edu.umd.cs.findbugs.BugReporter;
4547 import edu.umd.cs.findbugs.Detector;
4648 import edu.umd.cs.findbugs.SourceLineAnnotation;
49 import edu.umd.cs.findbugs.SystemProperties;
4750 import edu.umd.cs.findbugs.ba.BasicBlock;
4851 import edu.umd.cs.findbugs.ba.CFG;
4952 import edu.umd.cs.findbugs.ba.CFGBuilderException;
5861 import edu.umd.cs.findbugs.ba.type.TypeDataflow;
5962 import edu.umd.cs.findbugs.ba.type.TypeFrame;
6063 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
64 import edu.umd.cs.findbugs.classfile.Global;
65 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
66 import edu.umd.cs.findbugs.detect.BuildStringPassthruGraph.MethodParameter;
67 import edu.umd.cs.findbugs.detect.BuildStringPassthruGraph.StringPassthruDatabase;
68 import edu.umd.cs.findbugs.visitclass.PreorderVisitor;
6169
6270 /**
6371 * Find potential SQL injection vulnerabilities.
6775 * @author Matt Hargett
6876 */
6977 public class FindSqlInjection implements Detector {
78 private static final String[] PREPARE_STATEMENT_SIGNATURES = new String[] {
79 "(Ljava/lang/String;)Ljava/sql/PreparedStatement;",
80 "(Ljava/lang/String;I)Ljava/sql/PreparedStatement;",
81 "(Ljava/lang/String;II)Ljava/sql/PreparedStatement;",
82 "(Ljava/lang/String;III)Ljava/sql/PreparedStatement;",
83 "(Ljava/lang/String;[I)Ljava/sql/PreparedStatement;",
84 "(Ljava/lang/String;[Ljava/lang/String;)Ljava/sql/PreparedStatement;",
85 };
86
87 private static final MethodDescriptor[] EXECUTE_METHODS = new MethodDescriptor[] {
88 new MethodDescriptor("java/sql/Statement", "executeQuery", "(Ljava/lang/String;)Ljava/sql/ResultSet;"),
89 new MethodDescriptor("java/sql/Statement", "executeUpdate", "(Ljava/lang/String;)I"),
90 new MethodDescriptor("java/sql/Statement", "executeUpdate", "(Ljava/lang/String;I)I"),
91 new MethodDescriptor("java/sql/Statement", "executeUpdate", "(Ljava/lang/String;[I)I"),
92 new MethodDescriptor("java/sql/Statement", "executeUpdate", "(Ljava/lang/String;[Ljava/lang/String;)I"),
93 new MethodDescriptor("java/sql/Statement", "executeLargeUpdate", "(Ljava/lang/String;)J"),
94 new MethodDescriptor("java/sql/Statement", "executeLargeUpdate", "(Ljava/lang/String;I)J"),
95 new MethodDescriptor("java/sql/Statement", "executeLargeUpdate", "(Ljava/lang/String;[I)J"),
96 new MethodDescriptor("java/sql/Statement", "executeLargeUpdate", "(Ljava/lang/String;[Ljava/lang/String;)J"),
97 new MethodDescriptor("java/sql/Statement", "execute", "(Ljava/lang/String;)Z"),
98 new MethodDescriptor("java/sql/Statement", "execute", "(Ljava/lang/String;I)Z"),
99 new MethodDescriptor("java/sql/Statement", "execute", "(Ljava/lang/String;[I)Z"),
100 new MethodDescriptor("java/sql/Statement", "execute", "(Ljava/lang/String;[Ljava/lang/String;)Z"),
101 new MethodDescriptor("java/sql/Statement", "addBatch", "(Ljava/lang/String;)V"),
102 };
103
70104 private static class StringAppendState {
71105 // remember the smallest position at which we saw something that
72106 // concerns us
150184
151185 BugAccumulator bugAccumulator;
152186
187 final Map<MethodDescriptor, int[]> preparedStatementMethods;
188 final Map<MethodDescriptor, int[]> executeMethods;
189 final Set<MethodDescriptor> allMethods = new HashSet<>();
190
191 private final boolean testingEnabled;
192
153193 public FindSqlInjection(BugReporter bugReporter) {
154194 this.bugReporter = bugReporter;
155195 this.bugAccumulator = new BugAccumulator(bugReporter);
156 }
157
196 testingEnabled = SystemProperties.getBoolean("report_TESTING_pattern_in_standard_detectors");
197 Set<MethodParameter> baseExecuteMethods = new HashSet<>();
198 for(MethodDescriptor executeMethod : EXECUTE_METHODS) {
199 baseExecuteMethods.add(new MethodParameter(executeMethod, 0));
200 }
201 executeMethods = Global.getAnalysisCache().getDatabase(StringPassthruDatabase.class).findLinkedMethods(baseExecuteMethods);
202 Set<MethodParameter> basePrepareMethods = new HashSet<>();
203 for(String signature : PREPARE_STATEMENT_SIGNATURES) {
204 basePrepareMethods.add(new MethodParameter(new MethodDescriptor("java/sql/Connection", "prepareStatement", signature), 0));
205 }
206 preparedStatementMethods = Global.getAnalysisCache().getDatabase(StringPassthruDatabase.class).findLinkedMethods(basePrepareMethods);
207 allMethods.addAll(executeMethods.keySet());
208 allMethods.addAll(preparedStatementMethods.keySet());
209 }
210
211 @Override
158212 public void visitClassContext(ClassContext classContext) {
159213 JavaClass javaClass = classContext.getJavaClass();
214 if(!PreorderVisitor.hasInterestingMethod(javaClass.getConstantPool(), allMethods)) {
215 return;
216 }
160217 Method[] methodList = javaClass.getMethods();
161218
162219 for (Method method : methodList) {
163220 MethodGen methodGen = classContext.getMethodGen(method);
164 if (methodGen == null)
221 if (methodGen == null) {
165222 continue;
223 }
166224
167225 try {
168226 analyzeMethod(classContext, method);
186244 if (ins instanceof INVOKEVIRTUAL) {
187245 INVOKEVIRTUAL invoke = (INVOKEVIRTUAL) ins;
188246
189 if (invoke.getMethodName(cpg).equals("append") && invoke.getClassName(cpg).startsWith("java.lang.StringB")) {
247 if ("append".equals(invoke.getMethodName(cpg)) && invoke.getClassName(cpg).startsWith("java.lang.StringB")) {
190248 String sig = invoke.getSignature(cpg);
191249 char firstChar = sig.charAt(1);
192250 return firstChar == '[' || firstChar == 'L';
222280 }
223281
224282 private StringAppendState updateStringAppendState(Location location, ConstantPoolGen cpg, StringAppendState stringAppendState)
225 {
283 {
226284 InstructionHandle handle = location.getHandle();
227285 Instruction ins = handle.getInstruction();
228286 if (!isConstantStringLoad(location, cpg)) {
232290 LDC load = (LDC) ins;
233291 Object value = load.getValue(cpg);
234292 String stringValue = ((String) value).trim();
235 if (stringValue.startsWith(",") || stringValue.endsWith(","))
293 if (stringValue.startsWith(",") || stringValue.endsWith(",")) {
236294 stringAppendState.setSawComma(handle);
237 if (isCloseQuote(stringValue) && stringAppendState.getSawOpenQuote(handle))
295 }
296 if (isCloseQuote(stringValue) && stringAppendState.getSawOpenQuote(handle)) {
238297 stringAppendState.setSawCloseQuote(handle);
239 if (isOpenQuote(stringValue))
298 }
299 if (isOpenQuote(stringValue)) {
240300 stringAppendState.setSawOpenQuote(handle);
301 }
241302
242303 return stringAppendState;
243 }
244
245 private boolean isPreparedStatementDatabaseSink(Instruction ins, ConstantPoolGen cpg) {
246 if (!(ins instanceof INVOKEINTERFACE)) {
247 return false;
248 }
249
250 INVOKEINTERFACE invoke = (INVOKEINTERFACE) ins;
251
252 String methodName = invoke.getMethodName(cpg);
253 String methodSignature = invoke.getSignature(cpg);
254 String interfaceName = invoke.getClassName(cpg);
255 if (methodName.equals("prepareStatement") && interfaceName.equals("java.sql.Connection")
256 && methodSignature.startsWith("(Ljava/lang/String;")) {
257 return true;
258 }
259
260 return false;
261 }
262
263 private boolean isExecuteDatabaseSink(InvokeInstruction ins, ConstantPoolGen cpg) {
264 if (!(ins instanceof INVOKEINTERFACE)) {
265 return false;
266 }
267
268 INVOKEINTERFACE invoke = (INVOKEINTERFACE) ins;
269
270 String methodName = invoke.getMethodName(cpg);
271 String methodSignature = invoke.getSignature(cpg);
272 String interfaceName = invoke.getClassName(cpg);
273 if (methodName.startsWith("execute") && interfaceName.equals("java.sql.Statement")
274 && methodSignature.startsWith("(Ljava/lang/String;")) {
275 return true;
276 }
277
278 return false;
279 }
280
281 private boolean isDatabaseSink(InvokeInstruction ins, ConstantPoolGen cpg) {
282 return isPreparedStatementDatabaseSink(ins, cpg) || isExecuteDatabaseSink(ins, cpg);
283304 }
284305
285306 private StringAppendState getStringAppendState(CFG cfg, ConstantPoolGen cpg) throws CFGBuilderException {
287308 String sig = method.getSignature();
288309 sig = sig.substring(0, sig.indexOf(')'));
289310
290 if (sig.indexOf("java/lang/String") >= 0)
311 if (sig.indexOf("java/lang/String") >= 0) {
291312 stringAppendState.setSawInitialTaint();
313 }
292314 for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
293315 Location location = i.next();
294316 InstructionHandle handle = location.getHandle();
299321 stringAppendState.setSawAppend(handle);
300322
301323 Location prevLocation = getPreviousLocation(cfg, location, true);
302 if (prevLocation != null && !isSafeValue(prevLocation, cpg))
324 if (prevLocation != null && !isSafeValue(prevLocation, cpg)) {
303325 stringAppendState.setSawUnsafeAppend(handle);
326 }
304327
305328 } else if (ins instanceof InvokeInstruction) {
306329 InvokeInstruction inv = (InvokeInstruction) ins;
310333 if (sig2.indexOf("java/lang/String") >= 0) {
311334 String methodName = inv.getMethodName(cpg);
312335 String className = inv.getClassName(cpg);
313 if (methodName.equals("valueOf") && className.equals("java.lang.String")
314 && sig1.equals("(Ljava/lang/Object;)Ljava/lang/String;")) {
336 if ("valueOf".equals(methodName) && "java.lang.String".equals(className)
337 && "(Ljava/lang/Object;)Ljava/lang/String;".equals(sig1)) {
315338 try {
316339 TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
317340 TypeFrame frame = typeDataflow.getFactAtLocation(location);
325348 continue;
326349 }
327350 String sig3 = operandType.getSignature();
328 if (!sig3.equals("Ljava/lang/String;"))
351 if (!"Ljava/lang/String;".equals(sig3)) {
329352 stringAppendState.setSawTaint(handle);
353 }
330354 } catch (CheckedAnalysisException e) {
331355 stringAppendState.setSawTaint(handle);
332356 }
333 } else if (className.startsWith("java.lang.String") || className.equals("java.lang.Long")
334 || className.equals("java.lang.Integer") || className.equals("java.lang.Float")
335 || className.equals("java.lang.Double") || className.equals("java.lang.Short")
336 || className.equals("java.lang.Byte") || className.equals("java.lang.Character")) {
357 } else if (className.startsWith("java.lang.String") || "java.lang.Long".equals(className)
358 || "java.lang.Integer".equals(className) || "java.lang.Float".equals(className)
359 || "java.lang.Double".equals(className) || "java.lang.Short".equals(className)
360 || "java.lang.Byte".equals(className) || "java.lang.Character".equals(className)) {
337361 // ignore it
338362 assert true;
339363 } else if (methodName.startsWith("to") && methodName.endsWith("String") && methodName.length() > 8) {
342366 } else if (className.startsWith("javax.servlet") && methodName.startsWith("get")) {
343367 stringAppendState.setSawTaint(handle);
344368 stringAppendState.setSawSeriousTaint(handle);
345 } else
369 } else {
346370 stringAppendState.setSawTaint(handle);
371 }
347372
348373 }
349374 } else if (ins instanceof GETFIELD) {
350375 GETFIELD getfield = (GETFIELD) ins;
351376 String sig2 = getfield.getSignature(cpg);
352 if (sig2.indexOf("java/lang/String") >= 0)
377 if (sig2.indexOf("java/lang/String") >= 0) {
353378 stringAppendState.setSawTaint(handle);
379 }
354380 }
355381 }
356382
359385
360386 private boolean isSafeValue(Location location, ConstantPoolGen cpg) throws CFGBuilderException {
361387 Instruction prevIns = location.getHandle().getInstruction();
362 if (prevIns instanceof LDC || prevIns instanceof GETSTATIC)
388 if (prevIns instanceof LDC || prevIns instanceof GETSTATIC) {
363389 return true;
390 }
364391 if (prevIns instanceof InvokeInstruction) {
365392 String methodName = ((InvokeInstruction) prevIns).getMethodName(cpg);
366 if (methodName.startsWith("to") && methodName.endsWith("String") && methodName.length() > 8)
393 if (methodName.startsWith("to") && methodName.endsWith("String") && methodName.length() > 8) {
367394 return true;
395 }
368396 }
369397 if (prevIns instanceof AALOAD) {
370398 CFG cfg = classContext.getCFG(method);
374402 Location prev2 = getPreviousLocation(cfg, prev, true);
375403 if (prev2 != null && prev2.getHandle().getInstruction() instanceof GETSTATIC) {
376404 GETSTATIC getStatic = (GETSTATIC) prev2.getHandle().getInstruction();
377 if (getStatic.getSignature(cpg).equals("[Ljava/lang/String;"))
405 if ("[Ljava/lang/String;".equals(getStatic.getSignature(cpg))) {
378406 return true;
407 }
379408 }
380409 }
381410 }
398427 Location getPreviousLocation(CFG cfg, Location startLocation, boolean skipNops) {
399428 Location loc = startLocation;
400429 InstructionHandle prev = getPreviousInstruction(loc.getHandle(), skipNops);
401 if (prev != null)
430 if (prev != null) {
402431 return new Location(prev, loc.getBasicBlock());
432 }
403433 BasicBlock block = loc.getBasicBlock();
404434 while (true) {
405435 block = cfg.getPredecessorWithEdgeType(block, EdgeTypes.FALL_THROUGH_EDGE);
406 if (block == null)
436 if (block == null) {
407437 return null;
438 }
408439 InstructionHandle lastInstruction = block.getLastInstruction();
409 if (lastInstruction != null)
440 if (lastInstruction != null) {
410441 return new Location(lastInstruction, block);
442 }
411443 }
412444 }
413445
414446 private BugInstance generateBugInstance(JavaClass javaClass, MethodGen methodGen, InstructionHandle handle,
415 StringAppendState stringAppendState) {
416 Instruction instruction = handle.getInstruction();
417 ConstantPoolGen cpg = methodGen.getConstantPool();
447 StringAppendState stringAppendState, boolean isExecute) {
418448 int priority = LOW_PRIORITY;
419449 boolean sawSeriousTaint = false;
420450 if (stringAppendState.getSawAppend(handle)) {
435465 }
436466
437467 String description = "TESTING";
438 if (instruction instanceof InvokeInstruction && isExecuteDatabaseSink((InvokeInstruction) instruction, cpg)) {
468 if (isExecute) {
439469 description = "SQL_NONCONSTANT_STRING_PASSED_TO_EXECUTE";
440 } else if (isPreparedStatementDatabaseSink(instruction, cpg)) {
470 } else {
441471 description = "SQL_PREPARED_STATEMENT_GENERATED_FROM_NONCONSTANT_STRING";
442472 }
443473
444474 BugInstance bug = new BugInstance(this, description, priority);
445475 bug.addClassAndMethod(methodGen, javaClass.getSourceFileName());
446 if (description.equals("TESTING"))
476 if ("TESTING".equals(description)) {
447477 bug.addString("Incomplete report invoking non-constant SQL string");
448 if (sawSeriousTaint)
478 }
479 if (sawSeriousTaint) {
449480 bug.addString("non-constant SQL string involving HTTP taint");
481 }
450482
451483 return bug;
452484 }
460492 this.method = method;
461493 this.classContext = classContext;
462494 MethodGen methodGen = classContext.getMethodGen(method);
463 if (methodGen == null)
495 if (methodGen == null) {
464496 return;
497 }
465498
466499 ConstantPoolGen cpg = methodGen.getConstantPool();
467500 CFG cfg = classContext.getCFG(method);
472505 for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
473506 Location location = i.next();
474507 Instruction ins = location.getHandle().getInstruction();
475 if (!(ins instanceof InvokeInstruction))
508 if (!(ins instanceof InvokeInstruction)) {
476509 continue;
510 }
477511 InvokeInstruction invoke = (InvokeInstruction) ins;
478 if (isDatabaseSink(invoke, cpg)) {
479 ConstantFrame frame = dataflow.getFactAtLocation(location);
480 int numArguments = frame.getNumArguments(invoke, cpg);
481 Constant value = frame.getStackValue(numArguments - 1);
482
483 if (!value.isConstantString()) {
484 // TODO: verify it's the same string represented by
485 // stringAppendState
486 // FIXME: will false positive on const/static strings
487 // returns by methods
488 Location prev = getPreviousLocation(cfg, location, true);
489 if (prev == null || !isSafeValue(prev, cpg)) {
490 BugInstance bug = generateBugInstance(javaClass, methodGen, location.getHandle(), stringAppendState);
491 bugAccumulator.accumulateBug(
492 bug,
493 SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen,
494 javaClass.getSourceFileName(), location.getHandle()));
512 MethodDescriptor md = new MethodDescriptor(invoke, cpg);
513 boolean executeMethod;
514 int[] params = preparedStatementMethods.get(md);
515 int paramNumber;
516 // Currently only one method parameter is checked, though it's the most common case
517 // TODO: support methods which take several SQL statements
518 if(params != null) {
519 executeMethod = false;
520 paramNumber = params[0];
521 } else {
522 params = executeMethods.get(md);
523 if(params != null) {
524 executeMethod = true;
525 paramNumber = params[0];
526 } else {
527 continue;
528 }
529 }
530 ConstantFrame frame = dataflow.getFactAtLocation(location);
531 int numArguments = frame.getNumArguments(invoke, cpg);
532 Constant value = frame.getStackValue(numArguments - 1 - paramNumber);
533
534 if (!value.isConstantString()) {
535 // TODO: verify it's the same string represented by
536 // stringAppendState
537 // FIXME: will false positive on const/static strings
538 // returns by methods
539 Location prev = getPreviousLocation(cfg, location, true);
540 if (prev == null || !isSafeValue(prev, cpg)) {
541 BugInstance bug = generateBugInstance(javaClass, methodGen, location.getHandle(), stringAppendState, executeMethod);
542 if(!testingEnabled && "TESTING".equals(bug.getType())){
543 continue;
495544 }
545 bugAccumulator.accumulateBug(
546 bug,
547 SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen,
548 javaClass.getSourceFileName(), location.getHandle()));
496549 }
497550 }
498551 }
499552 bugAccumulator.reportAccumulatedBugs();
500553 }
501554
555 @Override
502556 public void report() {
503557 }
504558 }
505559
506 // vim:ts=4
4646
4747 public final class FindTwoLockWait implements Detector, StatelessDetector {
4848
49 private BugReporter bugReporter;
49 private final BugReporter bugReporter;
5050
5151 private JavaClass javaClass;
5252
53 private Collection<BugInstance> possibleWaitBugs = new LinkedList<BugInstance>();
53 private final Collection<BugInstance> possibleWaitBugs = new LinkedList<BugInstance>();
5454
55 private Collection<SourceLineAnnotation> possibleNotifyLocations = new LinkedList<SourceLineAnnotation>();
55 private final Collection<SourceLineAnnotation> possibleNotifyLocations = new LinkedList<SourceLineAnnotation>();
5656
5757 public FindTwoLockWait(BugReporter bugReporter) {
5858 this.bugReporter = bugReporter;
6767 }
6868 }
6969
70 @Override
7071 public void visitClassContext(ClassContext classContext) {
7172 javaClass = classContext.getJavaClass();
7273 possibleWaitBugs.clear();
7475 Method[] methodList = javaClass.getMethods();
7576 for (Method method : methodList) {
7677 MethodGen methodGen = classContext.getMethodGen(method);
77 if (methodGen == null)
78 if (methodGen == null) {
7879 continue;
80 }
7981
80 if (!preScreen(methodGen))
82 if (!preScreen(methodGen)) {
8183 continue;
84 }
8285
8386 try {
8487 analyzeMethod(classContext, method);
8992 bugReporter.logError("Error analyzing " + method.toString(), e);
9093 }
9194 }
92 if (!possibleNotifyLocations.isEmpty())
95 if (!possibleNotifyLocations.isEmpty()) {
9396 for (BugInstance bug : possibleWaitBugs) {
94 for (SourceLineAnnotation notifyLine : possibleNotifyLocations)
97 for (SourceLineAnnotation notifyLine : possibleNotifyLocations) {
9598 bug.addSourceLine(notifyLine).describe("SOURCE_NOTIFICATION_DEADLOCK");
99 }
96100 bugReporter.reportBug(bug);
97101 }
102 }
98103 }
99104
100105 private void analyzeMethod(ClassContext classContext, Method method) throws CFGBuilderException, DataflowAnalysisException {
118123 InstructionHandle handle = mg.getInstructionList().getStart();
119124 while (handle != null && !(lockCount >= 2 && sawWaitOrNotify)) {
120125 Instruction ins = handle.getInstruction();
121 if (ins instanceof MONITORENTER)
126 if (ins instanceof MONITORENTER) {
122127 ++lockCount;
123 else if (ins instanceof INVOKEVIRTUAL) {
128 } else if (ins instanceof INVOKEVIRTUAL) {
124129 INVOKEVIRTUAL inv = (INVOKEVIRTUAL) ins;
125130 String methodName = inv.getMethodName(cpg);
126 if (methodName.equals("wait") || methodName.startsWith("notify"))
131 if ("wait".equals(methodName) || methodName.startsWith("notify")) {
127132 sawWaitOrNotify = true;
133 }
128134 }
129135
130136 handle = handle.getNext();
157163 }
158164 }
159165
166 @Override
160167 public void report() {
161168 }
162169 }
163170
164 // vim:ts=3
2121 import java.util.HashSet;
2222
2323 import org.apache.bcel.classfile.AnnotationEntry;
24 import org.apache.bcel.classfile.Constant;
25 import org.apache.bcel.classfile.ConstantCP;
26 import org.apache.bcel.classfile.ConstantMethodHandle;
27 import org.apache.bcel.classfile.ConstantNameAndType;
28 import org.apache.bcel.classfile.ConstantPool;
29 import org.apache.bcel.classfile.ConstantUtf8;
2430 import org.apache.bcel.classfile.Method;
2531
2632 import edu.umd.cs.findbugs.BugInstance;
2935 import edu.umd.cs.findbugs.MethodAnnotation;
3036 import edu.umd.cs.findbugs.StatelessDetector;
3137 import edu.umd.cs.findbugs.ba.ClassContext;
38 import edu.umd.cs.findbugs.util.ClassName;
3239
3340 /**
3441 * Detector to find private methods that are never called.
3542 */
3643 public class FindUncalledPrivateMethods extends BytecodeScanningDetector implements StatelessDetector {
37 private BugReporter bugReporter;
44 private final BugReporter bugReporter;
3845
3946 private String className;
4047
4855
4956 @Override
5057 public void visitMethod(Method obj) {
58 if (!obj.isPrivate() || obj.isSynthetic()) {
59 return;
60 }
5161 super.visitMethod(obj);
52 if (obj.isPrivate() && !getMethodName().equals("writeReplace") && !getMethodName().equals("readResolve")
53 && !getMethodName().equals("readObject") && !getMethodName().equals("readObjectNoData")
54 && !getMethodName().equals("writeObject") && getMethodName().indexOf("debug") == -1
55 && getMethodName().indexOf("Debug") == -1 && getMethodName().indexOf("trace") == -1
56 && getMethodName().indexOf("Trace") == -1 && !getMethodName().equals("<init>")
57 && !getMethodName().equals("<clinit>")) {
62 String methodName = getMethodName();
63 if (!"writeReplace".equals(methodName) && !"readResolve".equals(methodName)
64 && !"readObject".equals(methodName) && !"readObjectNoData".equals(methodName)
65 && !"writeObject".equals(methodName)
66 && methodName.indexOf("debug") == -1 && methodName.indexOf("Debug") == -1
67 && methodName.indexOf("trace") == -1 && methodName.indexOf("Trace") == -1
68 && !"<init>".equals(methodName) && !"<clinit>".equals(methodName)) {
5869 for(AnnotationEntry a : obj.getAnnotationEntries()) {
5970 String typeName = a.getAnnotationType();
60 if (typeName.equals("Ljavax/annotation/PostConstruct;")
61 || typeName.equals("Ljavax/annotation/PreDestroy;"))
71 if ("Ljavax/annotation/PostConstruct;".equals(typeName)
72 || "Ljavax/annotation/PreDestroy;".equals(typeName)) {
6273 return;
74 }
6375 }
6476 definedPrivateMethods.add(MethodAnnotation.fromVisitedMethod(this));
6577 }
7789 seen == INVOKESTATIC);
7890 calledMethods.add(called);
7991 calledMethodNames.add(getNameConstantOperand().toLowerCase());
80 // System.out.println("Saw call to " + called);
81
8292 }
8393 break;
8494 default:
94104 className = classContext.getJavaClass().getClassName();
95105 String[] parts = className.split("[$+.]");
96106 String simpleClassName = parts[parts.length - 1];
107
108 ConstantPool cp = classContext.getJavaClass().getConstantPool();
109 for(Constant constant : cp.getConstantPool()) {
110 if(constant instanceof ConstantMethodHandle) {
111 int kind = ((ConstantMethodHandle) constant).getReferenceKind();
112 if(kind >= 5 && kind <= 9) {
113 Constant ref = cp.getConstant(((ConstantMethodHandle)constant).getReferenceIndex());
114 if(ref instanceof ConstantCP) {
115 String className = cp.getConstantString(((ConstantCP) ref).getClassIndex(), CONSTANT_Class);
116 ConstantNameAndType nameAndType = (ConstantNameAndType) cp.getConstant(((ConstantCP) ref).getNameAndTypeIndex());
117 String name = ((ConstantUtf8)cp.getConstant(nameAndType.getNameIndex())).getBytes();
118 String signature = ((ConstantUtf8)cp.getConstant(nameAndType.getSignatureIndex())).getBytes();
119 MethodAnnotation called = new MethodAnnotation(ClassName.toDottedClassName(className), name, signature, kind==6 /* invokestatic */);
120 calledMethods.add(called);
121 calledMethodNames.add(name.toLowerCase());
122 }
123 }
124 }
125 }
126
97127 super.visitClassContext(classContext);
98128
99129 definedPrivateMethods.removeAll(calledMethods);
102132 // System.out.println("Checking " + m);
103133 int priority = LOW_PRIORITY;
104134 String methodName = m.getMethodName();
105 if (methodName.equals(simpleClassName) && m.getMethodSignature().equals("()V"))
135 if (methodName.equals(simpleClassName) && "()V".equals(m.getMethodSignature())) {
106136 continue;
107 if (methodName.length() > 1 && calledMethodNames.contains(methodName.toLowerCase()))
137 }
138 if (methodName.length() > 1 && calledMethodNames.contains(methodName.toLowerCase())) {
108139 priority = NORMAL_PRIORITY;
140 }
109141 BugInstance bugInstance = new BugInstance(this, "UPM_UNCALLED_PRIVATE_METHOD", priority).addClass(this).addMethod(m);
110142 bugReporter.reportBug(bugInstance);
111143 }
115147 }
116148 }
117149
118 // vim:ts=4
3535 public class FindUnconditionalWait extends BytecodeScanningDetector implements StatelessDetector {
3636 int stage = 0;
3737
38 private BugReporter bugReporter;
38 private final BugReporter bugReporter;
3939
4040 public FindUnconditionalWait(BugReporter bugReporter) {
4141 this.bugReporter = bugReporter;
4848
4949 @Override
5050 public void sawBranchTo(int target) {
51 if (stage == 1)
51 if (stage == 1) {
5252 stage = 0;
53 }
5354 }
5455
5556 @Override
5657 public void sawOpcode(int seen) {
5758 switch (stage) {
5859 case 0:
59 if (seen == MONITORENTER)
60 if (seen == MONITORENTER) {
6061 stage = 1;
62 }
6163 break;
6264 case 1:
63 if (seen == INVOKEVIRTUAL && getNameConstantOperand().equals("wait")) {
65 if (seen == INVOKEVIRTUAL && "wait".equals(getNameConstantOperand())) {
6466 bugReporter.reportBug(new BugInstance(this, "UW_UNCOND_WAIT",
65 getSigConstantOperand().equals("()V") ? NORMAL_PRIORITY : LOW_PRIORITY).addClassAndMethod(this)
67 "()V".equals(getSigConstantOperand()) ? NORMAL_PRIORITY : LOW_PRIORITY).addClassAndMethod(this)
6668 .addSourceLine(this));
6769 stage = 2;
6870 }
6971 break;
72 default:
73 break;
7074 }
7175 }
7276 }
5757
5858 boolean thisOnTOS = false;
5959
60 private BugReporter bugReporter;
60 private final BugReporter bugReporter;
6161
6262 public FindUninitializedGet(BugReporter bugReporter) {
6363 this.bugReporter = bugReporter;
8181
8282 @Override
8383 public void visitAnnotation(String annotationClass, Map<String, ElementValue> map, boolean runtimeVisible) {
84 if (!visitingField())
84 if (!visitingField()) {
8585 return;
86 }
8687 if (UnreadFields.isInjectionAttribute(annotationClass)) {
8788 containerFields.add(FieldAnnotation.fromVisitedField(this));
8889 }
9596 initializedFields.clear();
9697
9798 thisOnTOS = false;
98 inConstructor = getMethodName().equals("<init>") && getMethodSig().indexOf(getClassName()) == -1;
99 inConstructor = "<init>".equals(getMethodName()) && getMethodSig().indexOf(getClassName()) == -1;
99100
100101 }
101102
102103 @Override
103104 public void visit(Code obj) {
104 if (!inConstructor)
105 if (!inConstructor) {
105106 return;
107 }
106108 uninitializedFieldReadAndCheckedForNonnull = null;
107109 super.visit(obj);
108110 for (BugInstance bug : pendingBugs) {
116118 Iterator<BugInstance> i = pendingBugs.iterator();
117119 while (i.hasNext()) {
118120 BugInstance bug = i.next();
119 if (bug.getPrimarySourceLineAnnotation().getStartBytecode() >= target)
121 if (bug.getPrimarySourceLineAnnotation().getStartBytecode() >= target) {
120122 i.remove();
123 }
121124 }
122125 }
123126
124127 @Override
125128 public void sawOpcode(int seen) {
126 if (!inConstructor)
129 if (!inConstructor) {
127130 return;
131 }
128132 if (uninitializedFieldReadAndCheckedForNonnull != null) {
129 if (seen == NEW && getClassConstantOperand().endsWith("Exception"))
133 if (seen == NEW && getClassConstantOperand().endsWith("Exception")) {
130134 uninitializedFieldReadAndCheckedForNonnull.raisePriority();
135 }
131136 uninitializedFieldReadAndCheckedForNonnull = null;
132137 }
133138
136141 return;
137142 }
138143
139 if (seen == PUTFIELD && getClassConstantOperand().equals(getClassName()))
144 if (seen == PUTFIELD && getClassConstantOperand().equals(getClassName())) {
140145 initializedFields.add(FieldAnnotation.fromReferencedField(this));
141
142 else if (thisOnTOS && seen == GETFIELD && getClassConstantOperand().equals(getClassName())) {
146 } else if (thisOnTOS && seen == GETFIELD && getClassConstantOperand().equals(getClassName())) {
143147 UnreadFieldsData unreadFields = AnalysisContext.currentAnalysisContext().getUnreadFieldsData();
144148 XField xField = XFactory.createReferencedXField(this);
145149 FieldAnnotation f = FieldAnnotation.fromReferencedField(this);
150154 // OPCODE_NAMES[nextOpcode]);
151155 LocalVariableAnnotation possibleTarget = LocalVariableAnnotation.findMatchingIgnoredParameter(getClassContext(),
152156 getMethod(), getNameConstantOperand(), xField.getSignature());
153 if (possibleTarget == null)
157 if (possibleTarget == null) {
154158 possibleTarget = LocalVariableAnnotation.findUniqueBestMatchingParameter(getClassContext(), getMethod(),
155159 getNameConstantOperand(), getSigConstantOperand());
160 }
156161 int priority = unreadFields.getReadFields().contains(xField) ? NORMAL_PRIORITY : LOW_PRIORITY;
157162 boolean priorityLoweredBecauseOfIfNonnullTest = false;
158 if (possibleTarget != null)
163 if (possibleTarget != null) {
159164 priority--;
160 else {
165 } else {
161166 FieldSummary fieldSummary = AnalysisContext.currentAnalysisContext().getFieldSummary();
162 if (fieldSummary.callsOverriddenMethodsFromSuperConstructor(getClassDescriptor()))
167 if (fieldSummary.callsOverriddenMethodsFromSuperConstructor(getClassDescriptor())) {
163168 priority++;
164 else if (nextOpcode == IFNONNULL) {
169 } else if (nextOpcode == IFNONNULL) {
165170 priority++;
166171 priorityLoweredBecauseOfIfNonnullTest = true;
167172 }
175180 }
176181 initializedFields.add(f);
177182 }
178 } else if ((seen == INVOKESPECIAL && !(getNameConstantOperand().equals("<init>") && !getClassConstantOperand().equals(
183 } else if ((seen == INVOKESPECIAL && !("<init>".equals(getNameConstantOperand()) && !getClassConstantOperand().equals(
179184 getClassName())))
180 || (seen == INVOKESTATIC && getNameConstantOperand().equals("doPrivileged") && getClassConstantOperand().equals(
181 "java/security/AccessController"))
182 || (seen == INVOKEVIRTUAL && getClassConstantOperand().equals(getClassName()))
183 || (seen == INVOKEVIRTUAL && getNameConstantOperand().equals("start"))) {
185 || (seen == INVOKESTATIC && "doPrivileged".equals(getNameConstantOperand()) && "java/security/AccessController".equals(getClassConstantOperand()))
186 || (seen == INVOKEVIRTUAL && getClassConstantOperand().equals(getClassName()))
187 || (seen == INVOKEVIRTUAL && "start".equals(getNameConstantOperand()))) {
184188
185189 inConstructor = false;
186190 }
244244 *
245245 * @see edu.umd.cs.findbugs.Detector#visitClassContext(edu.umd.cs.findbugs.ba.ClassContext)
246246 */
247 @Override
247248 public void visitClassContext(ClassContext classContext) {
248249 JavaClass javaClass = classContext.getJavaClass();
249250 Method[] methodList = javaClass.getMethods();
250251
251252 for (Method method : methodList) {
252 if (method.getCode() == null)
253 if (method.getCode() == null) {
253254 continue;
255 }
254256
255257 try {
256258 analyzeMethod(classContext, method);
258260 assert true; // move along; nothing to see
259261 } catch (CFGBuilderException e) {
260262 String msg = "Detector " + this.getClass().getName() + " caught exception while analyzing "
261 + javaClass.getClassName() + "." + method.getName() + " : " + method.getSignature();
263 + javaClass.getClassName() + "." + method.getName() + " : " + method.getSignature();
262264 bugReporter.logError(msg, e);
263265 } catch (DataflowAnalysisException e) {
264266 String msg = "Detector " + this.getClass().getName() + " caught exception while analyzing "
265 + javaClass.getClassName() + "." + method.getName() + " : " + method.getSignature();
267 + javaClass.getClassName() + "." + method.getName() + " : " + method.getSignature();
266268 bugReporter.logError(msg, e);
267269 }
268270 }
274276 public boolean prescreen(ClassContext classContext, Method method) {
275277 BitSet bytecodeSet = classContext.getBytecodeSet(method);
276278 return bytecodeSet != null
277 && (bytecodeSet.get(Constants.INVOKEINTERFACE) || bytecodeSet.get(Constants.INVOKEVIRTUAL)
278 || bytecodeSet.get(Constants.INVOKESPECIAL) || bytecodeSet.get(Constants.INVOKESTATIC) || bytecodeSet
279 .get(Constants.INVOKENONVIRTUAL));
279 && (bytecodeSet.get(Constants.INVOKEINTERFACE) || bytecodeSet.get(Constants.INVOKEVIRTUAL)
280 || bytecodeSet.get(Constants.INVOKESPECIAL) || bytecodeSet.get(Constants.INVOKESTATIC) || bytecodeSet
281 .get(Constants.INVOKENONVIRTUAL));
280282 }
281283
282284 /**
284286 * code
285287 */
286288 private boolean isSynthetic(Method m) {
287 if ((m.getAccessFlags() & Constants.ACC_SYNTHETIC) != 0)
289 if ((m.getAccessFlags() & Constants.ACC_SYNTHETIC) != 0) {
288290 return true;
291 }
289292 Attribute[] attrs = m.getAttributes();
290293 for (Attribute attr : attrs) {
291 if (attr instanceof Synthetic)
294 if (attr instanceof Synthetic) {
292295 return true;
296 }
293297 }
294298 return false;
295299 }
299303 static {
300304 baseGenericTypes.addAll(Arrays.asList(new String[] { "java.util.Map", "java.util.Collection", "java.lang.Iterable",
301305 "java.util.Iterator", "com.google.common.collect.Multimap", "com.google.common.collect.Multiset",
302 "com.google.common.collect.Table" }));
306 "com.google.common.collect.Table" }));
303307 }
304308
305309 private boolean isGenericCollection(ClassDescriptor operandClass) {
306310 String dottedClassName = operandClass.getDottedClassName();
307311
308 if (baseGenericTypes.contains(dottedClassName))
312 if (baseGenericTypes.contains(dottedClassName)) {
309313 return true;
314 }
310315
311316 String found = null;
312317 for(String c : baseGenericTypes) {
315320 break;
316321 }
317322 }
318 if (found == null)
323 if (found == null) {
319324 return false;
320 if (dottedClassName.startsWith("java.util.") || dottedClassName.startsWith("com.google.common.collect.") )
325 }
326 if (dottedClassName.startsWith("java.util.") || dottedClassName.startsWith("com.google.common.collect.") ) {
321327 return true;
328 }
322329 try {
323330 XClass xclass = Global.getAnalysisCache().getClassAnalysis(XClass.class, operandClass);
324331
325332 String sig = xclass.getSourceSignature();
326 if (sig == null)
333 if (sig == null) {
327334 return false;
335 }
328336
329337 String typeParameter = null;
330338 List<String> split = GenericUtilities.split(sig, true);
331339 if (sig.charAt(0) == '<') {
332340 int end = sig.indexOf(':');
333 if (end > 0)
341 if (end > 0) {
334342 typeParameter = sig.substring(1, end);
335
336 }
337 if (DEBUG) System.out.println(dottedClassName + " " + typeParameter + " " + split);
343 }
344
345 }
346 if (DEBUG) {
347 System.out.println(dottedClassName + " " + typeParameter + " " + split);
348 }
338349 for (String s : split) {
339350 int i = s.indexOf('<');
340 if (i < 0)
341 continue;
342 if (s.charAt(0) != 'L')
351 if (i < 0) {
352 continue;
353 }
354 if (s.charAt(0) != 'L') {
343355 throw new IllegalStateException("unexpected non signature: " + s);
356 }
344357 ClassDescriptor c = DescriptorFactory.createClassDescriptor(s.substring(1, i));
345358 String superTypeParameter = s.substring(i+1);
346359 if (isGenericCollection(c) && (typeParameter == null || superTypeParameter.startsWith("T" + typeParameter))) {
347 if (DEBUG)
360 if (DEBUG) {
348361 System.out.println(operandClass + " is a subtype of " + s);
362 }
349363 return true;
350364 }
351365 }
352 if (DEBUG)
366 if (DEBUG) {
353367 System.out.println("Not a subtype");
368 }
354369
355370
356371 } catch (CheckedAnalysisException e1) {
360375 }
361376
362377 private void analyzeMethod(ClassContext classContext, Method method) throws CFGBuilderException, DataflowAnalysisException {
363 if (isSynthetic(method) || !prescreen(classContext, method))
378 if (isSynthetic(method) || !prescreen(classContext, method)) {
364379 return;
380 }
365381 XMethod xmethod = XFactory.createXMethod(classContext.getJavaClass(), method);
366 if (xmethod.isSynthetic())
382 if (xmethod.isSynthetic()) {
367383 return;
384 }
368385
369386 BugAccumulator accumulator = new BugAccumulator(bugReporter);
370387
374391
375392 ConstantPoolGen cpg = classContext.getConstantPoolGen();
376393 MethodGen methodGen = classContext.getMethodGen(method);
377 if (methodGen == null)
394 if (methodGen == null) {
378395 return;
396 }
379397 String fullMethodName = methodGen.getClassName() + "." + methodGen.getName();
380398
381399 String sourceFile = classContext.getJavaClass().getSourceFileName();
390408 Instruction ins = handle.getInstruction();
391409
392410 // Only consider invoke instructions
393 if (!(ins instanceof InvokeInstruction))
411 if (!(ins instanceof InvokeInstruction)) {
394412 continue;
413 }
395414
396415 InvokeInstruction inv = (InvokeInstruction) ins;
397416
404423 SignatureParser sigParser = new SignatureParser(inv.getSignature(cpg));
405424
406425 Collection<Info> collection = callMap.get(call);
407 if (!callMap.containsKey(call))
426 if (!callMap.containsKey(call)) {
408427 continue;
428 }
409429 for(Info info : collection) {
410430 Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
411 if (DEBUG)
431 if (DEBUG) {
412432 System.out.println("at " + handle.getPosition() + " Checking call to " + info.interfaceForCall + " : " + invokedMethod);
433 }
413434 try {
414 if (!subtypes2.isSubtype(invokedMethod.getClassDescriptor(), info.interfaceForCall))
435 if (!subtypes2.isSubtype(invokedMethod.getClassDescriptor(), info.interfaceForCall)) {
415436 continue;
437 }
416438 } catch (ClassNotFoundException e) {
417 if (info.interfaceForCall.getClassName().equals("java/util/Collection")
418 && invokedMethod.getClassName().equals("com.google.common.collect.Multiset")) {
439 if ("java/util/Collection".equals(info.interfaceForCall.getClassName())
440 && "com.google.common.collect.Multiset".equals(invokedMethod.getClassName())) {
419441 assert true;
420442 // we know this is OK without needing to find definition of Multiset
421443 } else {
438460
439461
440462 int lhsPos;
441 if (inv instanceof INVOKESTATIC)
463 if (inv instanceof INVOKESTATIC) {
442464 lhsPos = sigParser.getSlotsFromTopOfStackForParameter(0);
443 else
465 } else {
444466 lhsPos = sigParser.getTotalArgumentSize();
467 }
445468
446469 int stackPos = sigParser.getSlotsFromTopOfStackForParameter(pos);
447470
476499 if (objectVN.equals(argVN)) {
477500 String bugPattern = "DMI_COLLECTIONS_SHOULD_NOT_CONTAIN_THEMSELVES";
478501 int priority = HIGH_PRIORITY;
479 if (invokedMethodName.equals("removeAll")) {
502 if ("removeAll".equals(invokedMethodName)) {
480503 bugPattern = "DMI_USING_REMOVEALL_TO_CLEAR_COLLECTION";
481504 priority = NORMAL_PRIORITY;
482505 } else if (invokedMethodName.endsWith("All")) {
490513
491514 if (nextIns instanceof InvokeInstruction) {
492515 XMethod nextMethod = XFactory.createXMethod((InvokeInstruction) nextIns, cpg);
493 if (nextMethod.getName().equals("assertFalse"))
516 if ("assertFalse".equals(nextMethod.getName())) {
494517 continue;
518 }
495519 }
496520 }
497521 }
507531
508532 // Only consider generic...
509533 Type objectType = frame.getStackValue(lhsPos);
510 if (!(objectType instanceof GenericObjectType))
511 continue;
534 if (!(objectType instanceof GenericObjectType)) {
535 continue;
536 }
512537
513538 GenericObjectType operand = (GenericObjectType) objectType;
514539
515540 int expectedTypeParameters = 1;
516541 String simpleName = info.interfaceForCall.getSimpleName();
517 if ( simpleName.toLowerCase().endsWith("map") || simpleName.equals("Hashtable"))
542 if ( simpleName.toLowerCase().endsWith("map") || "Hashtable".equals(simpleName)) {
518543 expectedTypeParameters = 2;
519 else if (simpleName.equals("Table"))
544 } else if ("Table".equals(simpleName)) {
520545 expectedTypeParameters = 3;
546 }
521547
522548 // ... containers
523 if (!operand.hasParameters())
524 continue;
525 if (operand.getNumParameters() != expectedTypeParameters)
526 continue;
549 if (!operand.hasParameters()) {
550 continue;
551 }
552 if (operand.getNumParameters() != expectedTypeParameters) {
553 continue;
554 }
527555 ClassDescriptor operandClass = DescriptorFactory.getClassDescriptor(operand);
528 if (!isGenericCollection(operandClass))
529 continue;
556 if (!isGenericCollection(operandClass)) {
557 continue;
558 }
530559
531560 if (expectedTypeParameters == 2 &&
532561 Subtypes2.instanceOf(operandClass, Map.class)
533 && !TypeFrameModelingVisitor.isStraightGenericMap(operandClass))
534 continue;
562 && !TypeFrameModelingVisitor.isStraightGenericMap(operandClass)) {
563 continue;
564 }
535565 Type expectedType;
536 if (allMethod)
566 if (allMethod) {
537567 expectedType = operand;
538 else
568 } else {
539569 expectedType = operand.getParameterAt(typeArgument);
570 }
540571 Type actualType = frame.getStackValue(stackPos);
541572 Type equalsType = actualType;
542573 if (allMethod) {
543574 if (!(actualType instanceof GenericObjectType)) {
544 continue;
575 continue;
545576 }
546577 equalsType = ((GenericObjectType)actualType).getParameterAt(typeArgument);
547578 }
549580
550581 IncompatibleTypes matchResult = compareTypes(expectedType, actualType, allMethod);
551582
552 boolean parmIsObject = expectedType.getSignature().equals("Ljava/lang/Object;");
583 boolean parmIsObject = "Ljava/lang/Object;".equals(expectedType.getSignature());
553584 boolean selfOperation = !allMethod && operand.equals(actualType) && !parmIsObject;
554585 if (!allMethod && !parmIsObject && actualType instanceof GenericObjectType) {
555586
556587 GenericObjectType p2 = (GenericObjectType) actualType;
557588 List<? extends ReferenceType> parameters = p2.getParameters();
558 if (parameters != null && parameters.equals(operand.getParameters()))
589 if (parameters != null && parameters.equals(operand.getParameters())) {
559590 selfOperation = true;
560 }
561
562 if (!selfOperation && ( matchResult == IncompatibleTypes.SEEMS_OK || matchResult.getPriority() == Priorities.IGNORE_PRIORITY))
563 continue;
564
565 if (invokedMethodName.startsWith("contains") || invokedMethodName.equals("remove")) {
591 }
592 }
593
594 if (!selfOperation && ( matchResult == IncompatibleTypes.SEEMS_OK || matchResult.getPriority() == Priorities.IGNORE_PRIORITY)) {
595 continue;
596 }
597
598 if (invokedMethodName.startsWith("contains") || "remove".equals(invokedMethodName)) {
566599 InstructionHandle next = handle.getNext();
567600 if (next != null) {
568601 Instruction nextIns = next.getInstruction();
569602
570603 if (nextIns instanceof InvokeInstruction) {
571604 XMethod nextMethod = XFactory.createXMethod((InvokeInstruction) nextIns, cpg);
572 if (nextMethod.getName().equals("assertFalse"))
605 if ("assertFalse".equals(nextMethod.getName())) {
573606 continue;
607 }
574608 }
575609 }
576 } else if (invokedMethodName.equals("get") || invokedMethodName.equals("remove")) {
610 } else if ("get".equals(invokedMethodName) || "remove".equals(invokedMethodName)) {
577611 InstructionHandle next = handle.getNext();
578612 if (next != null) {
579613 Instruction nextIns = next.getInstruction();
580614
581615 if (nextIns instanceof InvokeInstruction) {
582616 XMethod nextMethod = XFactory.createXMethod((InvokeInstruction) nextIns, cpg);
583 if (nextMethod.getName().equals("assertNull"))
617 if ("assertNull".equals(nextMethod.getName())) {
584618 continue;
619 }
585620 }
586621 }
587622 }
588623 boolean noisy = false;
589 if (invokedMethodName.equals("get")) {
624 if ("get".equals(invokedMethodName)) {
590625 UnconditionalValueDerefDataflow unconditionalValueDerefDataflow = classContext
591 .getUnconditionalValueDerefDataflow(method);
626 .getUnconditionalValueDerefDataflow(method);
592627
593628 UnconditionalValueDerefSet unconditionalDeref = unconditionalValueDerefDataflow.getFactAtLocation(location);
594629 ValueNumberFrame vnAfter = vnDataflow.getFactAfterLocation(location);
602637 // Report a bug that mentions each of the failed arguments in
603638 // matches
604639
605 if (expectedType instanceof GenericObjectType)
640 if (expectedType instanceof GenericObjectType) {
606641 expectedType = ((GenericObjectType) expectedType).getUpperBound();
642 }
607643
608644 int priority = matchResult.getPriority();
609 if (!operandClass.getClassName().startsWith("java/util") && priority == Priorities.HIGH_PRIORITY)
645 if (!operandClass.getClassName().startsWith("java/util") && priority == Priorities.HIGH_PRIORITY) {
610646 priority = Math.max(priority, Priorities.NORMAL_PRIORITY);
611 if (TestCaseDetector.likelyTestCase(xmethod))
647 }
648 if (TestCaseDetector.likelyTestCase(xmethod)) {
612649 priority = Math.max(priority, Priorities.NORMAL_PRIORITY);
613 else if (selfOperation)
650 } else if (selfOperation) {
614651 priority = Priorities.HIGH_PRIORITY;
652 }
615653 ClassDescriptor expectedClassDescriptor = DescriptorFactory
616 .createClassOrObjectDescriptorFromSignature(expectedType.getSignature());
654 .createClassOrObjectDescriptorFromSignature(expectedType.getSignature());
617655 ClassDescriptor actualClassDescriptor = DescriptorFactory.createClassOrObjectDescriptorFromSignature(equalsType
618656 .getSignature());
619657 ClassSummary classSummary = AnalysisContext.currentAnalysisContext().getClassSummary();
622660 targets = Hierarchy2.resolveVirtualMethodCallTargets(actualClassDescriptor, "equals",
623661 "(Ljava/lang/Object;)Z", false, false);
624662 boolean allOk = targets.size() > 0;
625 for (XMethod m2 : targets)
626 if (!classSummary.mightBeEqualTo(m2.getClassDescriptor(), expectedClassDescriptor))
663 for (XMethod m2 : targets) {
664 if (!classSummary.mightBeEqualTo(m2.getClassDescriptor(), expectedClassDescriptor)) {
627665 allOk = false;
628 if (allOk)
666 }
667 }
668 if (allOk) {
629669 priority += 2;
670 }
630671 } catch (ClassNotFoundException e) {
631672 AnalysisContext.reportMissingClass(e);
632673 }
669710 private IncompatibleTypes compareTypes(Type expectedType, Type actualType, boolean ignoreBaseType) {
670711 // XXX equality not implemented for GenericObjectType
671712 // if (parmType.equals(argType)) return true;
672 if (expectedType == actualType)
713 if (expectedType == actualType) {
673714 return IncompatibleTypes.SEEMS_OK;
715 }
674716 // Compare type signatures instead
675717 String expectedString = GenericUtilities.getString(expectedType);
676718 String actualString = GenericUtilities.getString(actualType);
677 if (expectedString.equals(actualString))
719 if (expectedString.equals(actualString)) {
678720 return IncompatibleTypes.SEEMS_OK;
721 }
679722
680723 if (expectedType.equals(Type.OBJECT))
724 {
681725 return IncompatibleTypes.SEEMS_OK;
682 // if either type is java.lang.Object, then automatically true!
683 // again compare strings...
726 // if either type is java.lang.Object, then automatically true!
727 // again compare strings...
728 }
684729
685730 String objString = GenericUtilities.getString(Type.OBJECT);
686731
694739 if (actualString.equals(objString) && expectedCat == TypeCategory.TYPE_VARIABLE) {
695740 return IncompatibleTypes.SEEMS_OK;
696741 }
742 if (expectedCat == TypeCategory.WILDCARD) {
743 return IncompatibleTypes.SEEMS_OK;
744 }
697745 if (ignoreBaseType) {
698746 if (expectedCat == TypeCategory.PARAMETERIZED && argCat == TypeCategory.PARAMETERIZED) {
699747 GenericObjectType parmGeneric = (GenericObjectType) expectedType;
703751 return IncompatibleTypes.SEEMS_OK;
704752 }
705753
706 if (actualType.equals(Type.OBJECT) && expectedCat == TypeCategory.ARRAY_TYPE)
754 if (actualType.equals(Type.OBJECT) && expectedCat == TypeCategory.ARRAY_TYPE) {
707755 return IncompatibleTypes.ARRAY_AND_OBJECT;
756 }
708757
709758 // -~- plain objects are easy
710 if (expectedCat == TypeCategory.PLAIN_OBJECT_TYPE && argCat == TypeCategory.PLAIN_OBJECT_TYPE)
759 if (expectedCat == TypeCategory.PLAIN_OBJECT_TYPE && argCat == TypeCategory.PLAIN_OBJECT_TYPE) {
711760 return IncompatibleTypes.getPriorityForAssumingCompatible(expectedType, actualType, false);
712
713 if (expectedCat == TypeCategory.PARAMETERIZED && argCat == TypeCategory.PLAIN_OBJECT_TYPE)
761 }
762
763 if (expectedCat == TypeCategory.PARAMETERIZED && argCat == TypeCategory.PLAIN_OBJECT_TYPE) {
714764 return IncompatibleTypes.getPriorityForAssumingCompatible((GenericObjectType) expectedType, actualType);
715 if (expectedCat == TypeCategory.PLAIN_OBJECT_TYPE && argCat == TypeCategory.PARAMETERIZED)
765 }
766 if (expectedCat == TypeCategory.PLAIN_OBJECT_TYPE && argCat == TypeCategory.PARAMETERIZED) {
716767 return IncompatibleTypes.getPriorityForAssumingCompatible((GenericObjectType) actualType, expectedType);
768 }
717769
718770 // -~- parmType is: "? extends Another Type" OR "? super Another Type"
719 if (expectedCat == TypeCategory.WILDCARD_EXTENDS || expectedCat == TypeCategory.WILDCARD_SUPER)
771 if (expectedCat == TypeCategory.WILDCARD_EXTENDS || expectedCat == TypeCategory.WILDCARD_SUPER) {
720772 return compareTypes(((GenericObjectType) expectedType).getExtension(), actualType, ignoreBaseType);
773 }
721774
722775 // -~- Not handling type variables
723 if (expectedCat == TypeCategory.TYPE_VARIABLE || argCat == TypeCategory.TYPE_VARIABLE)
776 if (expectedCat == TypeCategory.TYPE_VARIABLE || argCat == TypeCategory.TYPE_VARIABLE) {
724777 return IncompatibleTypes.SEEMS_OK;
778 }
725779
726780 // -~- Array Types: compare dimensions, then base type
727781 if (expectedCat == TypeCategory.ARRAY_TYPE && argCat == TypeCategory.ARRAY_TYPE) {
728782 ArrayType parmArray = (ArrayType) expectedType;
729783 ArrayType argArray = (ArrayType) actualType;
730784
731 if (parmArray.getDimensions() != argArray.getDimensions())
785 if (parmArray.getDimensions() != argArray.getDimensions()) {
732786 return IncompatibleTypes.ARRAY_AND_NON_ARRAY;
787 }
733788
734789 return compareTypes(parmArray.getBasicType(), argArray.getBasicType(), ignoreBaseType);
735790 }
747802 // base types should be related
748803 {
749804 IncompatibleTypes result = compareTypes(parmGeneric.getObjectType(), argGeneric.getObjectType(), ignoreBaseType);
750 if (!result.equals(IncompatibleTypes.SEEMS_OK))
805 if (!result.equals(IncompatibleTypes.SEEMS_OK)) {
751806 return result;
807 }
752808 }
753809 return compareTypeParameters(parmGeneric, argGeneric);
754810
765821 }
766822
767823 // -~- Wildcard e.g. List<*>.contains(...)
768 if (expectedCat == TypeCategory.WILDCARD) // No Way to know
824 if (expectedCat == TypeCategory.WILDCARD) {
769825 return IncompatibleTypes.SEEMS_OK;
826 }
770827
771828 // -~- Non Reference types
772829 // if ( parmCat == TypeCategory.NON_REFERENCE_TYPE ||
788845 }
789846 for (int x = 0; x < p; x++) {
790847 IncompatibleTypes result = compareTypes(parmGeneric.getParameterAt(x), argGeneric.getParameterAt(x), false);
791 if (result != IncompatibleTypes.SEEMS_OK)
848 if (result != IncompatibleTypes.SEEMS_OK) {
792849 return result;
850 }
793851 }
794852 return IncompatibleTypes.SEEMS_OK;
795853 }
799857 // XXX equality not implemented for GenericObjectType
800858 // if (parmType.equals(argType)) return true;
801859 // Compare type signatures instead
802 if (GenericUtilities.getString(parmType).equals(GenericUtilities.getString(argType)))
860 if (GenericUtilities.getString(parmType).equals(GenericUtilities.getString(argType))) {
803861 return true;
862 }
804863
805864 if (parmType instanceof GenericObjectType) {
806865 GenericObjectType o = (GenericObjectType) parmType;
809868 }
810869 }
811870 // ignore type variables for now
812 if (parmType instanceof GenericObjectType && !((GenericObjectType) parmType).hasParameters())
871 if (parmType instanceof GenericObjectType && !((GenericObjectType) parmType).hasParameters()) {
813872 return true;
814 if (argType instanceof GenericObjectType && !((GenericObjectType) argType).hasParameters())
873 }
874 if (argType instanceof GenericObjectType && !((GenericObjectType) argType).hasParameters()) {
815875 return true;
876 }
816877
817878 // Case: Both are generic containers
818879 if (parmType instanceof GenericObjectType && argType instanceof GenericObjectType) {
819880 return true;
820881 } else {
821882 // Don't consider non reference types (should not be possible)
822 if (!(parmType instanceof ReferenceType && argType instanceof ReferenceType))
883 if (!(parmType instanceof ReferenceType && argType instanceof ReferenceType)) {
823884 return true;
885 }
824886
825887 // Don't consider non object types (for now)
826 if (!(parmType instanceof ObjectType && argType instanceof ObjectType))
888 if (!(parmType instanceof ObjectType && argType instanceof ObjectType)) {
827889 return true;
890 }
828891
829892 // Otherwise, compare base types ignoring generic information
830893 try {
841904 *
842905 * @see edu.umd.cs.findbugs.Detector#report()
843906 */
907 @Override
844908 public void report() {
845909 }
846910
6666 import edu.umd.cs.findbugs.bcel.BCELUtil;
6767
6868 class Lock extends ResourceCreationPoint {
69 private ValueNumber lockValue;
69 private final ValueNumber lockValue;
7070
7171 public Lock(Location location, String lockClass, ValueNumber lockValue) {
7272 super(location, lockClass);
8484 private int numAcquires = 0;
8585
8686 private static class LockFrameModelingVisitor extends ResourceValueFrameModelingVisitor {
87 private LockResourceTracker resourceTracker;
88
89 private Lock lock;
90
91 private ValueNumberDataflow vnaDataflow;
87 private final LockResourceTracker resourceTracker;
88
89 private final Lock lock;
90
91 private final ValueNumberDataflow vnaDataflow;
9292
9393 // private IsNullValueDataflow isNullDataflow;
9494
109109
110110 int status = -1;
111111
112 if (DEBUG)
112 if (DEBUG) {
113113 System.out.println("PC : " + handle.getPosition() + " " + ins);
114 }
114115 if (DEBUG && ins instanceof InvokeInstruction) {
115116 InvokeInstruction iins = (InvokeInstruction) ins;
116117 System.out.println(" " + ins.toString(cpg.getConstantPool()));
117118 }
118 if (DEBUG)
119 if (DEBUG) {
119120 System.out.println("resource frame before instruction: " + frame.toString());
121 }
120122
121123 // Is a lock acquired or released by this instruction?
122124 Location creationPoint = lock.getLocation();
123125 if (handle == creationPoint.getHandle() && basicBlock == creationPoint.getBasicBlock()) {
124126 status = ResourceValueFrame.OPEN;
125 if (DEBUG)
127 if (DEBUG) {
126128 System.out.println("OPEN");
129 }
127130 } else if (resourceTracker.isResourceClose(basicBlock, handle, cpg, lock, frame)) {
128131 status = ResourceValueFrame.CLOSED;
129 if (DEBUG)
132 if (DEBUG) {
130133 System.out.println("CLOSE");
134 }
131135 }
132136
133137 // Model use of instance values in frame slots
140144 if (DEBUG) {
141145 System.out.println("vna frame after instruction: " + vnaFrame.toString());
142146 System.out.println("Lock value number: " + lock.getLockValue());
143 if (lock.getLockValue().hasFlag(ValueNumber.RETURN_VALUE))
147 if (lock.getLockValue().hasFlag(ValueNumber.RETURN_VALUE)) {
144148 System.out.println("is return value");
149 }
145150 }
146151
147152 for (int i = 0; i < updatedNumSlots; ++i) {
148153 if (DEBUG) {
149154 System.out.println("Slot " + i);
150155 System.out.println(" Lock value number: " + vnaFrame.getValue(i));
151 if (vnaFrame.getValue(i).hasFlag(ValueNumber.RETURN_VALUE))
156 if (vnaFrame.getValue(i).hasFlag(ValueNumber.RETURN_VALUE)) {
152157 System.out.println(" is return value");
158 }
153159 }
154160 if (vnaFrame.fuzzyMatch(lock.getLockValue(), vnaFrame.getValue(i))) {
155 if (DEBUG)
161 if (DEBUG) {
156162 System.out.println("Saw lock value!");
163 }
157164 frame.setValue(i, ResourceValue.instance());
158165 }
159166 }
162169 if (status != -1) {
163170 frame.setStatus(status);
164171 }
165 if (DEBUG)
172 if (DEBUG) {
166173 System.out.println("resource frame after instruction: " + frame.toString());
174 }
167175
168176 }
169177
174182 }
175183
176184 class LockResourceTracker implements ResourceTracker<Lock> {
177 private RepositoryLookupFailureCallback lookupFailureCallback;
178
179 private CFG cfg;
180
181 private ValueNumberDataflow vnaDataflow;
182
183 private IsNullValueDataflow isNullDataflow;
185 private final RepositoryLookupFailureCallback lookupFailureCallback;
186
187 private final CFG cfg;
188
189 private final ValueNumberDataflow vnaDataflow;
190
191 private final IsNullValueDataflow isNullDataflow;
184192
185193 public LockResourceTracker(RepositoryLookupFailureCallback lookupFailureCallback, CFG cfg,
186194 ValueNumberDataflow vnaDataflow, IsNullValueDataflow isNullDataflow) {
190198 this.isNullDataflow = isNullDataflow;
191199 }
192200
201 @Override
193202 public Lock isResourceCreation(BasicBlock basicBlock, InstructionHandle handle, ConstantPoolGen cpg)
194203 throws DataflowAnalysisException {
195204
196205 InvokeInstruction inv = toInvokeInstruction(handle.getInstruction());
197 if (inv == null)
206 if (inv == null) {
198207 return null;
208 }
199209
200210 String className = inv.getClassName(cpg);
201211 String methodName = inv.getName(cpg);
202212 String methodSig = inv.getSignature(cpg);
203213
204214 try {
205 if (methodName.equals("lock") && methodSig.equals("()V")
215 if ("lock".equals(methodName) && "()V".equals(methodSig)
206216 && Hierarchy.isSubtype(className, "java.util.concurrent.locks.Lock")) {
207217
208218 Location location = new Location(handle, basicBlock);
209219 ValueNumberFrame frame = vnaDataflow.getFactAtLocation(location);
210220 ValueNumber lockValue = frame.getTopValue();
211 if (DEBUG)
221 if (DEBUG) {
212222 System.out.println("Lock value is " + lockValue.getNumber() + ", frame=" + frame.toString());
213 if (DEBUG)
223 }
224 if (DEBUG) {
214225 ++numAcquires;
226 }
215227 return new Lock(location, className, lockValue);
216228 }
217229 } catch (ClassNotFoundException e) {
220232 return null;
221233 }
222234
235 @Override
223236 public boolean mightCloseResource(BasicBlock basicBlock, InstructionHandle handle, ConstantPoolGen cpg)
224237 throws DataflowAnalysisException {
225238 InvokeInstruction inv = toInvokeInstruction(handle.getInstruction());
226 if (inv == null)
239 if (inv == null) {
227240 return false;
241 }
228242
229243 String className = inv.getClassName(cpg);
230244 String methodName = inv.getName(cpg);
231245 String methodSig = inv.getSignature(cpg);
232246
233247 try {
234 if (methodName.equals("unlock") && methodSig.equals("()V")
248 if ("unlock".equals(methodName) && "()V".equals(methodSig)
235249 && Hierarchy.isSubtype(className, "java.util.concurrent.locks.Lock")) {
236250
237251 return true;
243257 return false;
244258 }
245259
260 @Override
246261 public boolean isResourceClose(BasicBlock basicBlock, InstructionHandle handle, ConstantPoolGen cpg, Lock resource,
247262 ResourceValueFrame frame) throws DataflowAnalysisException {
248263
249 if (!mightCloseResource(basicBlock, handle, cpg))
264 if (!mightCloseResource(basicBlock, handle, cpg)) {
250265 return false;
266 }
251267 ResourceValue topValue = frame.getTopValue();
252268 return topValue.isInstance();
253269
254270 }
255271
272 @Override
256273 public ResourceValueFrameModelingVisitor createVisitor(Lock resource, ConstantPoolGen cpg) {
257274 return new LockFrameModelingVisitor(cpg, this, resource, vnaDataflow, isNullDataflow);
258275 }
259276
277 @Override
260278 public boolean ignoreImplicitExceptions(Lock resource) {
261279 // JSR166 locks should be ALWAYS be released,
262280 // including when implicit runtime exceptions are thrown
263281 return false;
264282 }
265283
284 @Override
266285 public boolean ignoreExceptionEdge(Edge edge, Lock resource, ConstantPoolGen cpg) {
267286
268287 try {
280299 }
281300 // Ignore exceptions from getfield instructions where the
282301 // object reference is known not to be null
283 if (fieldName.equals("lock"))
302 if ("lock".equals(fieldName)) {
284303 return true;
304 }
285305 IsNullValueFrame frame = isNullDataflow.getFactAtLocation(location);
286 if (!frame.isValid())
306 if (!frame.isValid()) {
287307 return false;
308 }
288309 IsNullValue receiver = frame.getInstance(ins, cpg);
289310 boolean notNull = receiver.isDefinitelyNotNull();
290311 if (DEBUG && notNull) {
295316 InvokeInstruction iins = (InvokeInstruction) ins;
296317 String methodName = iins.getMethodName(cpg);
297318 // System.out.println("Method " + methodName);
298 if (methodName.startsWith("access$"))
319 if (methodName.startsWith("access$")) {
299320 return true;
300 if (methodName.equals("readLock") || methodName.equals("writeLock"))
321 }
322 if ("readLock".equals(methodName) || "writeLock".equals(methodName)) {
301323 return true;
302 if (methodName.equals("lock") || methodName.equals("unlock"))
324 }
325 if ("lock".equals(methodName) || "unlock".equals(methodName)) {
303326 return true;
327 }
304328 }
305329 if (DEBUG) {
306330 System.out.println("FOUND Exception thrower at: " + location);
312336 return false;
313337 }
314338
339 @Override
315340 public boolean isParamInstance(Lock resource, int slot) {
316341 // There is nothing special about Lock objects passed
317342 // into the method as parameters.
320345
321346 private InvokeInstruction toInvokeInstruction(Instruction ins) {
322347 short opcode = ins.getOpcode();
323 if (opcode != Constants.INVOKEVIRTUAL && opcode != Constants.INVOKEINTERFACE)
348 if (opcode != Constants.INVOKEVIRTUAL && opcode != Constants.INVOKEINTERFACE) {
324349 return null;
350 }
325351 return (InvokeInstruction) ins;
326352 }
327353 }
338364
339365 /*
340366 * (non-Javadoc)
341 *
367 *
342368 * @see
343369 * edu.umd.cs.findbugs.Detector#visitClassContext(edu.umd.cs.findbugs.ba
344370 * .ClassContext)
350376 // We can ignore classes that were compiled for anything
351377 // less than JDK 1.5. This should avoid lots of unnecessary work
352378 // when analyzing code for older VM targets.
353 if (BCELUtil.preTiger(jclass))
379 if (BCELUtil.preTiger(jclass)) {
354380 return;
381 }
355382
356383 boolean sawUtilConcurrentLocks = false;
357 for (Constant c : jclass.getConstantPool().getConstantPool())
384 for (Constant c : jclass.getConstantPool().getConstantPool()) {
358385 if (c instanceof ConstantMethodref) {
359386 ConstantMethodref m = (ConstantMethodref) c;
360387 ConstantClass cl = (ConstantClass) jclass.getConstantPool().getConstant(m.getClassIndex());
361388 ConstantUtf8 name = (ConstantUtf8) jclass.getConstantPool().getConstant(cl.getNameIndex());
362389 String nameAsString = name.getBytes();
363 if (nameAsString.startsWith("java/util/concurrent/locks"))
390 if (nameAsString.startsWith("java/util/concurrent/locks")) {
364391 sawUtilConcurrentLocks = true;
365
366 }
367 if (sawUtilConcurrentLocks)
392 }
393
394 }
395 }
396 if (sawUtilConcurrentLocks) {
368397 super.visitClassContext(classContext);
398 }
369399 }
370400
371401 @Override
372402 public boolean prescreen(ClassContext classContext, Method method, boolean mightClose) {
373 if (!mightClose)
374 return false;
403 if (!mightClose) {
404 return false;
405 }
375406 BitSet bytecodeSet = classContext.getBytecodeSet(method);
376 if (bytecodeSet == null)
377 return false;
407 if (bytecodeSet == null) {
408 return false;
409 }
378410
379411 MethodGen methodGen = classContext.getMethodGen(method);
380412
384416
385417 @Override
386418 public LockResourceTracker getResourceTracker(ClassContext classContext, Method method) throws CFGBuilderException,
387 DataflowAnalysisException {
419 DataflowAnalysisException {
388420 return new LockResourceTracker(bugReporter, classContext.getCFG(method), classContext.getValueNumberDataflow(method),
389421 classContext.getIsNullValueDataflow(method));
390422 }
417449 InstructionHandle handle = location.getHandle();
418450 InstructionHandle nextInstruction = handle.getNext();
419451 if (nextInstruction.getInstruction() instanceof RETURN)
452 {
420453 return; // don't report as error; intentional
454 }
421455 bugAccumulator.accumulateBug(new BugInstance(this, bugType, priority).addClassAndMethod(methodGen, sourceFile),
422456 SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen, sourceFile, handle));
423457 }
425459
426460 @Override
427461 public void report() {
428 if (DEBUG)
462 if (DEBUG) {
429463 System.out.println("numAcquires=" + numAcquires);
464 }
430465 }
431466
432467 // /* ----------------------------------------------------------------------
468503 // }
469504 }
470505
471 // vim:ts=4
4444 import edu.umd.cs.findbugs.SourceLineAnnotation;
4545 import edu.umd.cs.findbugs.StringAnnotation;
4646 import edu.umd.cs.findbugs.SystemProperties;
47 import edu.umd.cs.findbugs.ba.AnalysisContext;
4748 import edu.umd.cs.findbugs.ba.BasicBlock;
4849 import edu.umd.cs.findbugs.ba.CFG;
4950 import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
6667 import edu.umd.cs.findbugs.bcel.CFGDetector;
6768 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
6869 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
70 import edu.umd.cs.findbugs.classfile.DescriptorFactory;
6971 import edu.umd.cs.findbugs.classfile.Global;
7072 import edu.umd.cs.findbugs.classfile.IAnalysisCache;
7173 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
7375 /**
7476 * Find unsatisfied obligations in Java methods. Examples: open streams, open
7577 * database connections, etc.
76 *
78 *
7779 * <p>
7880 * See Weimer and Necula, <a href="http://doi.acm.org/10.1145/1028976.1029011"
7981 * >Finding and preventing run-time error handling mistakes</a>, OOPSLA 2004.
8082 * </p>
81 *
83 *
8284 * @author David Hovemeyer
8385 */
8486 public class FindUnsatisfiedObligation extends CFGDetector {
110112
111113 private final BugReporter bugReporter;
112114
113 private ObligationPolicyDatabase database;
115 private final ObligationPolicyDatabase database;
114116
115117 public FindUnsatisfiedObligation(BugReporter bugReporter) {
116118 this.bugReporter = bugReporter;
143145 }
144146 }
145147 }
146 if (DEBUG)
148 if (DEBUG) {
147149 System.out.println(classDescriptor + " isn't interesting for obligation analysis");
150 }
148151 }
149152
150153 @Override
169172 /**
170173 * Determine whether the state has "balanced" obligation counts for the
171174 * consumed and produced Obligation types.
172 *
175 *
173176 * @param state
174177 * a State
175178 * @return true if the obligation counts are balanced, false otherwise
296299
297300 // Apply the false-positive suppression heuristics
298301 int leakCount = getAdjustedLeakCount(state, id);
299
302
300303
301304 if (leakCount > 0) {
302305 leakedObligationMap.put(obligation, state);
309312 private void reportWarning(Obligation obligation, State state, StateSet factAtExit) {
310313 String className = obligation.getClassName();
311314
312 if (methodDescriptor.isStatic() && methodDescriptor.getName().equals("main")
313 && methodDescriptor.getSignature().equals("([Ljava/lang/String;)V")
315 if (methodDescriptor.isStatic() && "main".equals(methodDescriptor.getName())
316 && "([Ljava/lang/String;)V".equals(methodDescriptor.getSignature())
314317 && (className.contains("InputStream") || className.contains("Reader") || factAtExit.isOnExceptionPath())) {
315318 // Don't report unclosed input streams and readers in main()
316319 // methods
317320 return;
321 }
322
323 if (methodDescriptor.getName().equals("<init>")) {
324 try {
325
326 if (subtypes2.isSubtype(methodDescriptor.getClassDescriptor(), DescriptorFactory.createClassDescriptorFromDottedClassName(obligation.getClassName()))) {
327 return;
328 }
329
330 } catch (Exception e) {
331 AnalysisContext.logError("huh", e);
332 }
318333 }
319334 String bugPattern = factAtExit.isOnExceptionPath() ? "OBL_UNSATISFIED_OBLIGATION_EXCEPTION_EDGE" : "OBL_UNSATISFIED_OBLIGATION";
320335 BugInstance bugInstance = new BugInstance(FindUnsatisfiedObligation.this, bugPattern,
360375 List<PossibleObligationTransfer> transferList;
361376
362377 public PostProcessingPathVisitor(Obligation possiblyLeakedObligation/*
363 * ,
364 * int
365 * initialLeakCount
366 */, State state) {
378 * ,
379 * int
380 * initialLeakCount
381 */, State state) {
367382 this.possiblyLeakedObligation = possiblyLeakedObligation;
368383 this.state = state;
369384 this.adjustedLeakCount = state.getObligationSet().getCount(possiblyLeakedObligation.getId());
380395 return couldNotAnalyze;
381396 }
382397
398 @Override
383399 public void visitBasicBlock(BasicBlock basicBlock) {
384400 curBlock = basicBlock;
385401
392408 }
393409 }
394410
411 @Override
395412 public void visitInstructionHandle(InstructionHandle handle) {
396413 try {
397414 Instruction ins = handle.getInstruction();
398415 short opcode = ins.getOpcode();
399 if (DEBUG) System.out.printf("%3d %s%n", handle.getPosition(),Constants.OPCODE_NAMES[opcode]);
416 if (DEBUG) {
417 System.out.printf("%3d %s%n", handle.getPosition(),Constants.OPCODE_NAMES[opcode]);
418 }
400419
401420 if (opcode == Constants.PUTFIELD || opcode == Constants.PUTSTATIC || opcode == Constants.ARETURN) {
402421 //
415434 possiblyLeakedObligation.getType())) {
416435 // Remove one obligation of this type
417436 adjustedLeakCount--;
418 if (DEBUG)
437 if (DEBUG) {
419438 System.out.println("removing obligation to close " + tosType + " at " + handle.getPosition());
439 }
420440 }
421441 }
422442
507527 }
508528
509529 String methodName = inv.getMethodName(cpg);
510 Type producedType = methodName.equals("<init>") ? inv.getReferenceType(cpg) : inv.getReturnType(cpg);
530 Type producedType = "<init>".equals(methodName) ? inv.getReferenceType(cpg) : inv.getReturnType(cpg);
511531
512532 if (DEBUG_FP && !(producedType instanceof ObjectType)) {
513533 System.out.println("Produced type " + producedType + " not an ObjectType");
556576 }
557577 }
558578
579 @Override
559580 public void visitEdge(Edge edge) {
560581 if (DEBUG_FP) {
561582 System.out.println("visit edge " + edge);
597618 * <li>return statements (if an instance of the obligation type is
598619 * returned from the method, subtract one from leak count)</li>
599620 * </ul>
600 *
621 *
601622 * @return the adjusted leak count (positive if leaked obligation,
602623 * negative if attempt to release an un-acquired obligation)
603624 */
640661
641662 BasicBlock curBlock;
642663
664 @Override
643665 public void visitBasicBlock(BasicBlock basicBlock) {
644666 curBlock = basicBlock;
645667
655677 if (entryState.getObligationSet().getCount(obligation.getId()) > 0) {
656678 lastSourceLine = SourceLineAnnotation.forFirstLineOfMethod(methodDescriptor);
657679 lastSourceLine
658 .setDescription(SourceLineAnnotation.ROLE_OBLIGATION_CREATED_BY_WILLCLOSE_PARAMETER);
680 .setDescription(SourceLineAnnotation.ROLE_OBLIGATION_CREATED_BY_WILLCLOSE_PARAMETER);
659681 bugInstance.add(lastSourceLine);
660682 sawFirstCreation = true;
661683
668690 }
669691 }
670692
693 @Override
671694 public void visitInstructionHandle(InstructionHandle handle) {
672695 boolean isCreation = (dataflow.getAnalysis().getActionCache().addsObligation(curBlock, handle, obligation));
673696
695718 }
696719 }
697720
721 @Override
698722 public void visitEdge(Edge edge) {
699723 if (REPORT_PATH_DEBUG) {
700724 System.out.println("Edge of type " + Edge.edgeTypeToString(edge.getType()) + " to "
719743
720744 /*
721745 * (non-Javadoc)
722 *
746 *
723747 * @see edu.umd.cs.findbugs.Detector#report()
724748 */
725749 public void report() {
3333 public class FindUnsyncGet extends BytecodeScanningDetector {
3434 String prevClassName = " none ";
3535
36 private BugReporter bugReporter;
36 private final BugReporter bugReporter;
3737
3838 static final int doNotConsider = ACC_PRIVATE | ACC_STATIC | ACC_NATIVE;
3939
4040 // Maps of property names to get and set methods
41 private HashMap<String, MethodAnnotation> getMethods = new HashMap<String, MethodAnnotation>();
41 private final HashMap<String, MethodAnnotation> getMethods = new HashMap<String, MethodAnnotation>();
4242
43 private HashMap<String, MethodAnnotation> setMethods = new HashMap<String, MethodAnnotation>();
43 private final HashMap<String, MethodAnnotation> setMethods = new HashMap<String, MethodAnnotation>();
4444
4545 public FindUnsyncGet(BugReporter bugReporter) {
4646 this.bugReporter = bugReporter;
7474 @Override
7575 public void visit(Method obj) {
7676 int flags = obj.getAccessFlags();
77 if ((flags & doNotConsider) != 0)
77 if ((flags & doNotConsider) != 0) {
7878 return;
79 }
7980 String name = obj.getName();
8081 boolean isSynchronized = (flags & ACC_SYNCHRONIZED) != 0;
8182 /*
8384 * returnValue = sig.charAt(1 + sig.indexOf(')')); boolean firstArgIsRef
8485 * = (firstArg == 'L') || (firstArg == '['); boolean returnValueIsRef =
8586 * (returnValue == 'L') || (returnValue == '[');
86 *
87 *
8788 * System.out.println(className + "." + name + " " + firstArgIsRef + " "
8889 * + returnValueIsRef + " " + isSynchronized + " " + isNative );
8990 */
9091 if (name.startsWith("get") && !isSynchronized
91 // && returnValueIsRef
92 ) {
92 // && returnValueIsRef
93 ) {
9394 getMethods.put(name.substring(3), MethodAnnotation.fromVisitedMethod(this));
9495 } else if (name.startsWith("set") && isSynchronized
95 // && firstArgIsRef
96 ) {
96 // && firstArgIsRef
97 ) {
9798 setMethods.put(name.substring(3), MethodAnnotation.fromVisitedMethod(this));
9899 }
99100 }
5252 this.bugAccumulator = new BugAccumulator(bugReporter);
5353 }
5454
55 @Override
5556 public void visitClassContext(ClassContext classContext) {
5657 JavaClass javaClass = classContext.getJavaClass();
5758 boolean skip = false;
6263 @DottedClassName String clazz = m.getClass(constantPool);
6364 ConstantNameAndType nt = (ConstantNameAndType) constantPool.getConstant(m.getNameAndTypeIndex(), Constants.CONSTANT_NameAndType);
6465 String name = nt.getName(constantPool);
65 if (name.equals("setAttribute") && clazz.equals("javax.servlet.http.HttpSession") || (name.equals("writeObject")
66 && (clazz.equals("java.io.ObjectOutput")
67 || clazz.equals("java.io.ObjectOutputStream")))) {
68 if (DEBUG)
66 if ("setAttribute".equals(name) && "javax.servlet.http.HttpSession".equals(clazz) || ("writeObject".equals(name)
67 && ("java.io.ObjectOutput".equals(clazz)
68 || "java.io.ObjectOutputStream".equals(clazz)))) {
69 if (DEBUG) {
6970 System.out.println("Found call to " + clazz + "." + name);
71 }
7072
7173 skip = false;
7274 break;
7476
7577 }
7678 }
77 if (skip)
78 return;
79 if (DEBUG)
79 if (skip) {
80 return;
81 }
82 if (DEBUG) {
8083 System.out.println(this.getClass().getSimpleName() + " Checking " + javaClass.getClassName());
84 }
8185 Method[] methodList = javaClass.getMethods();
8286
8387 for (Method method : methodList) {
84 if (method.getCode() == null)
85 continue;
88 if (method.getCode() == null) {
89 continue;
90 }
8691
8792 try {
8893 analyzeMethod(classContext, method);
102107 if (ins instanceof InvokeInstruction) {
103108 InvokeInstruction invoke = (InvokeInstruction) ins;
104109
105 String mName = invoke.getMethodName(cpg);
110 String mName = invoke.getMethodName(cpg);
106111 String cName = invoke.getClassName(cpg);
107112
108 if (mName.equals("setAttribute") && cName.equals("javax.servlet.http.HttpSession"))
113 if ("setAttribute".equals(mName) && "javax.servlet.http.HttpSession".equals(cName)) {
109114 return Use.STORE_INTO_HTTP_SESSION;
110 if (mName.equals("writeObject")
111 && (cName.equals("java.io.ObjectOutput")
112 || cName.equals("java.io.ObjectOutputStream")))
115 }
116 if ("writeObject".equals(mName)
117 && ("java.io.ObjectOutput".equals(cName)
118 || "java.io.ObjectOutputStream".equals(cName))) {
113119 return Use.PASSED_TO_WRITE_OBJECT;
114 }
115 return null;
120 }
121 }
122 return null;
116123 }
117124 private void analyzeMethod(ClassContext classContext, Method method) throws CFGBuilderException, DataflowAnalysisException {
118125 MethodGen methodGen = classContext.getMethodGen(method);
119 if (methodGen == null)
120 return;
126 if (methodGen == null) {
127 return;
128 }
121129 BitSet bytecodeSet = classContext.getBytecodeSet(method);
122 if (bytecodeSet == null)
123 return;
130 if (bytecodeSet == null) {
131 return;
132 }
124133 // We don't adequately model instanceof interfaces yet
125 if (bytecodeSet.get(Constants.INSTANCEOF) || bytecodeSet.get(Constants.CHECKCAST))
126 return;
134 if (bytecodeSet.get(Constants.INSTANCEOF) || bytecodeSet.get(Constants.CHECKCAST)) {
135 return;
136 }
127137 CFG cfg = classContext.getCFG(method);
128138 TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
129139 ConstantPoolGen cpg = classContext.getConstantPoolGen();
140150 Instruction ins = handle.getInstruction();
141151
142152 Use use = getUse(cpg, ins);
143 if (use == null)
144 continue;
153 if (use == null) {
154 continue;
155 }
145156
146157 TypeFrame frame = typeDataflow.getFactAtLocation(location);
147158 if (!frame.isValid()) {
178189 case PASSED_TO_WRITE_OBJECT:
179190 pattern = "DMI_NONSERIALIZABLE_OBJECT_WRITTEN";
180191 double isRemote = DeepSubtypeAnalysis.isDeepRemote(refType);
181 if (isRemote >= 0.9)
192 if (isRemote >= 0.9) {
182193 continue;
183 if (isSerializable < isRemote)
194 }
195 if (isSerializable < isRemote) {
184196 isSerializable = isRemote;
197 }
185198 break;
186199 case STORE_INTO_HTTP_SESSION:
187200 pattern = "J2EE_STORE_OF_NON_SERIALIZABLE_OBJECT_INTO_SESSION";
188201 break;
189 default:
190 throw new IllegalStateException();
202 default:
203 throw new IllegalStateException();
191204 }
192205
193206 bugAccumulator.accumulateBug(new BugInstance(this, pattern,
194207 isSerializable < 0.15 ? HIGH_PRIORITY : isSerializable > 0.5 ? LOW_PRIORITY : NORMAL_PRIORITY)
195 .addClassAndMethod(methodGen, sourceFile).addType(problem).describe(TypeAnnotation.FOUND_ROLE),
196 sourceLineAnnotation);
208 .addClassAndMethod(methodGen, sourceFile).addType(problem).describe(TypeAnnotation.FOUND_ROLE),
209 sourceLineAnnotation);
197210
198211 }
199212 } catch (ClassNotFoundException e) {
202215 }
203216 }
204217
218 @Override
205219 public void report() {
206220 }
207221
3333
3434 /**
3535 * A Detector to look for useless control flow. For example,
36 *
36 *
3737 * <pre>
3838 * if (argv.length == 1)
3939 * ;
4040 * System.out.println(&quot;Hello, &quot; + argv[0]);
4141 * </pre>
42 *
42 *
4343 * In this kind of bug, we'll see an ifcmp instruction where the IF target is
4444 * the same as the fall-through target.
4545 * <p/>
4747 * The idea for this detector came from Richard P. King, and the idea of looking
4848 * for if instructions with identical branch and fall-through targets is from
4949 * Mike Fagan.
50 *
50 *
5151 * @author David Hovemeyer
5252 */
5353 public class FindUselessControlFlow extends BytecodeScanningDetector implements StatelessDetector {
7272 ifInstructionSet.set(Constants.IFNONNULL);
7373 }
7474
75 private BugAccumulator bugAccumulator;
75 private final BugAccumulator bugAccumulator;
7676
7777 public FindUselessControlFlow(BugReporter bugReporter) {
7878 this.bugAccumulator = new BugAccumulator(bugReporter);
9797 int nextLine = getNextSourceLine(lineNumbers, branchLineNumber);
9898
9999 if (branchLineNumber + 1 == targetLineNumber || branchLineNumber == targetLineNumber
100 && nextLine == branchLineNumber + 1)
100 && nextLine == branchLineNumber + 1) {
101101 priority = HIGH_PRIORITY;
102 else if (branchLineNumber + 2 < Math.max(targetLineNumber, nextLine))
102 } else if (branchLineNumber + 2 < Math.max(targetLineNumber, nextLine)) {
103103 priority = LOW_PRIORITY;
104 } else
104 }
105 } else {
105106 priority = LOW_PRIORITY;
107 }
106108 bugAccumulator.accumulateBug(new BugInstance(this,
107109 priority == HIGH_PRIORITY ? "UCF_USELESS_CONTROL_FLOW_NEXT_LINE" : "UCF_USELESS_CONTROL_FLOW", priority)
108 .addClassAndMethod(this), this);
110 .addClassAndMethod(this), this);
109111 }
110112 }
111113 }
115117 for (LineNumber ln : lineNumbers.getLineNumberTable()) {
116118
117119 int thisLine = ln.getLineNumber();
118 if (sourceLine < thisLine && thisLine < result)
120 if (sourceLine < thisLine && thisLine < result) {
119121 result = thisLine;
122 }
120123 }
121124 return result;
122125
123126 }
124127 }
125128
126 // vim:ts=4
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2008 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.detect;
20
21 import static org.apache.bcel.Constants.*;
22
23 import java.util.BitSet;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.Iterator;
27 import java.util.Map;
28 import java.util.Map.Entry;
29 import java.util.NoSuchElementException;
30 import java.util.Set;
31
32 import org.apache.bcel.classfile.LocalVariable;
33 import org.apache.bcel.classfile.LocalVariableTable;
34 import org.apache.bcel.classfile.Method;
35 import org.apache.bcel.generic.ANEWARRAY;
36 import org.apache.bcel.generic.ConstantPoolGen;
37 import org.apache.bcel.generic.IINC;
38 import org.apache.bcel.generic.INVOKESPECIAL;
39 import org.apache.bcel.generic.Instruction;
40 import org.apache.bcel.generic.InstructionHandle;
41 import org.apache.bcel.generic.InvokeInstruction;
42 import org.apache.bcel.generic.MULTIANEWARRAY;
43 import org.apache.bcel.generic.NEWARRAY;
44 import org.apache.bcel.generic.POP;
45 import org.apache.bcel.generic.POP2;
46 import org.apache.bcel.generic.StoreInstruction;
47 import org.apache.bcel.generic.Type;
48
49 import edu.umd.cs.findbugs.BugInstance;
50 import edu.umd.cs.findbugs.BugReporter;
51 import edu.umd.cs.findbugs.Detector;
52 import edu.umd.cs.findbugs.StringAnnotation;
53 import edu.umd.cs.findbugs.ba.BasicBlock;
54 import edu.umd.cs.findbugs.ba.CFG;
55 import edu.umd.cs.findbugs.ba.ClassContext;
56 import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
57 import edu.umd.cs.findbugs.ba.EdgeTypes;
58 import edu.umd.cs.findbugs.ba.Location;
59 import edu.umd.cs.findbugs.ba.XClass;
60 import edu.umd.cs.findbugs.ba.XMethod;
61 import edu.umd.cs.findbugs.ba.type.TypeAnalysis;
62 import edu.umd.cs.findbugs.ba.type.TypeFrame;
63 import edu.umd.cs.findbugs.ba.vna.ValueNumber;
64 import edu.umd.cs.findbugs.ba.vna.ValueNumberAnalysis;
65 import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;
66 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
67 import edu.umd.cs.findbugs.classfile.DescriptorFactory;
68 import edu.umd.cs.findbugs.classfile.Global;
69 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
70 import edu.umd.cs.findbugs.detect.FindNoSideEffectMethods.MethodSideEffectStatus;
71 import edu.umd.cs.findbugs.detect.FindNoSideEffectMethods.NoSideEffectMethodsDatabase;
72
73 /**
74 * @author Tagir Valeev
75 */
76 public class FindUselessObjects implements Detector {
77 private final BugReporter reporter;
78 private final NoSideEffectMethodsDatabase noSideEffectMethods;
79
80 private static class ValueInfo {
81 Location created;
82 String var;
83 int origValue;
84 boolean hasObjectOnlyCall;
85 boolean escaped;
86 boolean used;
87 boolean derivedEscaped;
88 public BitSet origValues;
89 public BitSet derivedValues = new BitSet();
90 Type type;
91
92 public ValueInfo(int origValue, Location location, Type type) {
93 this.created = location;
94 this.origValue = origValue;
95 this.type = type;
96 }
97
98 @Override
99 public String toString() {
100 return "[" + (escaped ? "E" : "-") + (hasObjectOnlyCall ? "O" : "-") + (used ? "U" : "-")
101 + (derivedEscaped ? "D" : "-") + "] " + (var == null ? "" : var + " ") + type + " " + created;
102 }
103 }
104
105 private class UselessValuesContext {
106 ValueNumberAnalysis vna;
107 TypeAnalysis ta;
108 CFG cfg;
109 int count;
110 Map<Integer, ValueInfo> observedValues = new HashMap<>();
111 ConstantPoolGen cpg;
112 Map<Integer, Set<ValueInfo>> values;
113 ValueNumber thisValue;
114 ClassContext classContext;
115 Method method;
116
117 UselessValuesContext(ClassContext classContext, Method method) throws CheckedAnalysisException {
118 this.classContext = classContext;
119 this.method = method;
120 cfg = classContext.getCFG(method);
121 cpg = cfg.getMethodGen().getConstantPool();
122 ta = classContext.getTypeDataflow(method).getAnalysis();
123 vna = classContext.getValueNumberDataflow(method).getAnalysis();
124 }
125
126 void initObservedValues() throws DataflowAnalysisException {
127 for(Iterator<Location> iterator = cfg.locationIterator(); iterator.hasNext(); ) {
128 Location location = iterator.next();
129 Instruction instruction = location.getHandle().getInstruction();
130 if(instruction instanceof ANEWARRAY || instruction instanceof NEWARRAY || instruction instanceof MULTIANEWARRAY) {
131 int number = vna.getFactAfterLocation(location).getTopValue().getNumber();
132 TypeFrame typeFrame = ta.getFactAfterLocation(location);
133 if(typeFrame.isValid()) {
134 Type type = typeFrame.getTopValue();
135 observedValues.put(number, new ValueInfo(number, location, type));
136 }
137 } else if(instruction instanceof INVOKESPECIAL) {
138 InvokeInstruction inv = (InvokeInstruction) instruction;
139 if (inv.getMethodName(cpg).equals("<init>")
140 && noSideEffectMethods.hasNoSideEffect(new MethodDescriptor(inv, cpg))) {
141 int number = vna.getFactAtLocation(location).getStackValue(inv.consumeStack(cpg)-1).getNumber();
142 TypeFrame typeFrame = ta.getFactAtLocation(location);
143 if(typeFrame.isValid()) {
144 Type type = typeFrame.getStackValue(inv.consumeStack(cpg)-1);
145 observedValues.put(number, new ValueInfo(number, location, type));
146 }
147 }
148 }
149 }
150 thisValue = vna.getThisValue();
151 if(thisValue != null) {
152 observedValues.remove(thisValue.getNumber());
153 }
154 count = observedValues.size();
155 }
156
157 void enhanceViaMergeTree() {
158 values = new HashMap<>();
159 for (Entry<Integer, ValueInfo> entry : observedValues.entrySet()) {
160 BitSet outputSet = vna.getMergeTree().getTransitiveOutputSet(entry.getKey());
161 outputSet.set(entry.getKey());
162 entry.getValue().origValues = outputSet;
163 for (int i = outputSet.nextSetBit(0); i >= 0; i = outputSet.nextSetBit(i+1)) {
164 Set<ValueInfo> list = values.get(i);
165 if(list == null) {
166 list = new HashSet<>();
167 values.put(i, list);
168 }
169 list.add(entry.getValue());
170 }
171 }
172 }
173
174 boolean setEscape(Set<ValueInfo> vals) {
175 boolean result = false;
176 for(ValueInfo vi : vals) {
177 result |= !vi.escaped;
178 vi.escaped = true;
179 count--;
180 }
181 return result;
182 }
183
184 boolean setDerivedEscape(Set<ValueInfo> vals, ValueNumber vn) {
185 boolean result = false;
186 for(ValueInfo vi : vals) {
187 if(vi.origValues.get(vn.getNumber())) {
188 result |= !vi.derivedEscaped;
189 vi.derivedEscaped = true;
190 }
191 }
192 return result;
193 }
194
195 boolean setUsed(Set<ValueInfo> vals) {
196 boolean result = false;
197 for(ValueInfo vi : vals) {
198 result |= !vi.used;
199 vi.used = true;
200 }
201 return result;
202 }
203
204 boolean setObjectOnly(Set<ValueInfo> vals, ValueNumber vn) {
205 boolean result = false;
206 for(ValueInfo vi : vals) {
207 if(vi.origValues.get(vn.getNumber()) || (!vi.derivedEscaped && vi.derivedValues.get(vn.getNumber()))) {
208 result |= !vi.hasObjectOnlyCall;
209 vi.hasObjectOnlyCall = true;
210 } else {
211 result |= !vi.escaped;
212 vi.escaped = true;
213 count--;
214 }
215 }
216 return result;
217 }
218
219 boolean propagateValues(Set<ValueInfo> vals, ValueNumber origNumber, ValueNumber vn) {
220 int number = vn.getNumber();
221 if(vals.size() == 1 && vals.iterator().next().origValue == number) {
222 return false;
223 }
224 boolean result = setUsed(vals);
225 if(origNumber != null) {
226 for(ValueInfo vi : vals) {
227 if(vi.origValues.get(origNumber.getNumber()) && !vi.derivedValues.get(number)) {
228 vi.derivedValues.set(number);
229 result = true;
230 }
231 }
232 }
233 Set<ValueInfo> list = values.get(number);
234 if(list == null) {
235 list = new HashSet<>();
236 values.put(number, list);
237 }
238 result |= list.addAll(vals);
239 BitSet outputSet = vna.getMergeTree().getTransitiveOutputSet(number);
240 for (int i = outputSet.nextSetBit(0); i >= 0; i = outputSet.nextSetBit(i+1)) {
241 list = values.get(i);
242 if(list == null) {
243 list = new HashSet<>();
244 values.put(i, list);
245 }
246 result |= list.addAll(vals);
247 }
248 return result;
249 }
250
251 boolean propagateToReturnValue(Set<ValueInfo> vals, ValueNumber vn, GenLocation location, MethodDescriptor m)
252 throws DataflowAnalysisException {
253 for(ValueInfo vi : vals) {
254 if(vi.type.getSignature().startsWith("[") && vi.hasObjectOnlyCall && vi.var == null && vn.getNumber() == vi.origValue) {
255 // Ignore initialized arrays passed to methods
256 vi.escaped = true;
257 count--;
258 }
259 }
260 if (Type.getReturnType(m.getSignature()) == Type.VOID || location instanceof ExceptionLocation) {
261 return false;
262 }
263 InstructionHandle nextHandle = location.getHandle().getNext();
264 if (nextHandle == null || (nextHandle.getInstruction() instanceof POP || nextHandle.getInstruction() instanceof POP2)) {
265 return false;
266 }
267 return propagateValues(vals, null, location.frameAfter().getTopValue());
268 }
269
270 boolean isEmpty() {
271 return count == 0;
272 }
273
274 Iterator<GenLocation> genIterator() {
275 return new Iterator<FindUselessObjects.GenLocation>() {
276 Iterator<Location> locIterator = cfg.locationIterator();
277 Iterator<BasicBlock> blockIterator = cfg.blockIterator();
278 GenLocation next = advance();
279
280 private GenLocation advance() {
281 if(locIterator.hasNext()) {
282 return new RegularLocation(ta, vna, locIterator.next());
283 }
284 while(blockIterator.hasNext()) {
285 BasicBlock block = blockIterator.next();
286 if(block.isExceptionThrower() && cfg.getOutgoingEdgeWithType(block, EdgeTypes.FALL_THROUGH_EDGE) == null) {
287 return new ExceptionLocation(ta, vna, block);
288 }
289 }
290 return null;
291 }
292
293 @Override
294 public boolean hasNext() {
295 return next != null;
296 }
297
298 @Override
299 public GenLocation next() {
300 if (!hasNext()) {
301 throw new NoSuchElementException();
302 }
303 GenLocation cur = next;
304 next = advance();
305 return cur;
306 }
307
308 @Override
309 public void remove() {
310 throw new UnsupportedOperationException();
311 }
312 };
313 }
314
315 boolean escaped(ValueNumber vn) {
316 Set<ValueInfo> vals = values.get(vn.getNumber());
317 if(vals == null) {
318 return true;
319 }
320 for(ValueInfo vi : vals) {
321 if(vi.escaped) {
322 return true;
323 }
324 }
325 return false;
326 }
327
328 Set<ValueInfo> getLiveVals(ValueNumber vn) {
329 Set<ValueInfo> vals = this.values.get(vn.getNumber());
330 if(vals == null) {
331 return null;
332 }
333 if(vals.size() == 1) {
334 return vals.iterator().next().escaped ? null : vals;
335 }
336 Set<ValueInfo> result = new HashSet<>();
337 for(ValueInfo vi : vals) {
338 if(!vi.escaped) {
339 result.add(vi);
340 }
341 }
342 return result.isEmpty() ? null : result;
343 }
344
345 void report() {
346 for(ValueInfo vi : observedValues.values()) {
347 if(!vi.escaped) {
348 if(vi.hasObjectOnlyCall && vi.used && vi.var == null) {
349 continue;
350 }
351 if(vi.hasObjectOnlyCall || (vi.used && vi.var != null)) {
352 BugInstance bug = new BugInstance(vi.var == null ? "UC_USELESS_OBJECT_STACK" : "UC_USELESS_OBJECT",
353 NORMAL_PRIORITY).addClassAndMethod(classContext.getJavaClass(), method);
354 if(vi.var != null) {
355 bug.add(new StringAnnotation(vi.var));
356 }
357 reporter.reportBug(bug.addType(vi.type).addSourceLine(classContext, method, vi.created));
358 }
359 }
360 }
361 }
362 }
363
364 private static interface GenLocation {
365 InstructionHandle getHandle();
366 TypeFrame typeFrameBefore() throws DataflowAnalysisException;
367 ValueNumberFrame frameBefore();
368 ValueNumberFrame frameAfter();
369 }
370
371 private static class RegularLocation implements GenLocation {
372 Location loc;
373 ValueNumberAnalysis vna;
374 TypeAnalysis ta;
375
376 public RegularLocation(TypeAnalysis ta, ValueNumberAnalysis vna, Location loc) {
377 this.ta = ta;
378 this.vna = vna;
379 this.loc = loc;
380 }
381
382 @Override
383 public InstructionHandle getHandle() {
384 return loc.getHandle();
385 }
386
387 @Override
388 public ValueNumberFrame frameBefore() {
389 return vna.getFactAtLocation(loc);
390 }
391
392 @Override
393 public ValueNumberFrame frameAfter() {
394 return vna.getFactAfterLocation(loc);
395 }
396
397 @Override
398 public TypeFrame typeFrameBefore() throws DataflowAnalysisException {
399 return ta.getFactAtLocation(loc);
400 }
401
402 @Override
403 public String toString() {
404 return loc.toString();
405 }
406 }
407
408 private static class ExceptionLocation implements GenLocation {
409 BasicBlock b;
410 ValueNumberAnalysis vna;
411 TypeAnalysis ta;
412
413 public ExceptionLocation(TypeAnalysis ta, ValueNumberAnalysis vna, BasicBlock block) {
414 this.vna = vna;
415 this.ta = ta;
416 this.b = block;
417 }
418
419 @Override
420 public InstructionHandle getHandle() {
421 return b.getExceptionThrower();
422 }
423
424 @Override
425 public ValueNumberFrame frameBefore() {
426 return vna.getStartFact(b);
427 }
428
429 @Override
430 public ValueNumberFrame frameAfter() {
431 return vna.getResultFact(b);
432 }
433
434 @Override
435 public TypeFrame typeFrameBefore() {
436 return ta.getStartFact(b);
437 }
438
439 @Override
440 public String toString() {
441 return "ex: "+b.getExceptionThrower()+" at "+b;
442 }
443 }
444
445 public FindUselessObjects(BugReporter reporter) {
446 this.reporter = reporter;
447 this.noSideEffectMethods = Global.getAnalysisCache().getDatabase(NoSideEffectMethodsDatabase.class);
448 }
449
450 @Override
451 public void visitClassContext(ClassContext classContext) {
452 for(Method method : classContext.getMethodsInCallOrder()) {
453 if(method.isAbstract() || method.isNative()) {
454 continue;
455 }
456 try {
457 analyzeMethod(classContext, method);
458 } catch (CheckedAnalysisException e) {
459 reporter.logError("Error analyzing "+method+" (class: "+classContext.getJavaClass().getClassName()+")", e);
460 }
461 }
462 }
463
464 private void analyzeMethod(ClassContext classContext, Method method) throws CheckedAnalysisException {
465 LocalVariableTable lvt = method.getLocalVariableTable();
466 UselessValuesContext context = new UselessValuesContext(classContext, method);
467 context.initObservedValues();
468 if(context.isEmpty()) {
469 return;
470 }
471 context.enhanceViaMergeTree();
472 boolean changed;
473 do {
474 changed = false;
475 for(Iterator<GenLocation> iterator = context.genIterator(); iterator.hasNext() && !context.isEmpty(); ) {
476 GenLocation location = iterator.next();
477 Instruction inst = location.getHandle().getInstruction();
478 ValueNumberFrame before = location.frameBefore();
479 if (!before.isValid()) {
480 continue;
481 }
482 if(inst instanceof IINC) {
483 int index = ((IINC)inst).getIndex();
484 Set<ValueInfo> vals = context.getLiveVals(before.getValue(index));
485 if(vals != null) {
486 changed |= context.propagateValues(vals, null, location.frameAfter().getValue(index));
487 }
488 continue;
489 }
490 int nconsumed = inst.consumeStack(context.cpg);
491 if(nconsumed > 0) {
492 ValueNumber[] vns = new ValueNumber[nconsumed];
493 before.getTopStackWords(vns);
494 for(int i=0; i<nconsumed; i++) {
495 ValueNumber vn = vns[i];
496 Set<ValueInfo> vals = context.getLiveVals(vn);
497 if(vals != null) {
498 switch(inst.getOpcode()) {
499 case ASTORE:
500 case ASTORE_0:
501 case ASTORE_1:
502 case ASTORE_2:
503 case ASTORE_3:
504 for(ValueInfo vi : vals) {
505 if(vi.var == null && vi.origValue == vn.getNumber()) {
506 int index = ((StoreInstruction)inst).getIndex();
507 LocalVariable lv = lvt == null ? null : lvt.getLocalVariable(index, location.getHandle().getNext().getPosition());
508 vi.var = lv == null ? "var$"+index : lv.getName();
509 vi.hasObjectOnlyCall = false;
510 changed = true;
511 }
512 }
513 break;
514 case POP:
515 case POP2:
516 case DUP:
517 case DUP2:
518 case DUP_X1:
519 case DUP2_X1:
520 case ISTORE:
521 case ISTORE_0:
522 case ISTORE_1:
523 case ISTORE_2:
524 case ISTORE_3:
525 case LSTORE:
526 case LSTORE_0:
527 case LSTORE_1:
528 case LSTORE_2:
529 case LSTORE_3:
530 case FSTORE:
531 case FSTORE_0:
532 case FSTORE_1:
533 case FSTORE_2:
534 case FSTORE_3:
535 case DSTORE:
536 case DSTORE_0:
537 case DSTORE_1:
538 case DSTORE_2:
539 case DSTORE_3:
540 case SWAP:
541 case IMPDEP1:
542 case IMPDEP2:
543 case CHECKCAST:
544 case MONITORENTER:
545 break;
546 case IADD:
547 case LADD:
548 case FADD:
549 case DADD:
550 case ISUB:
551 case LSUB:
552 case FSUB:
553 case DSUB:
554 case IMUL:
555 case DMUL:
556 case LMUL:
557 case FMUL:
558 case IDIV:
559 case DDIV:
560 case LDIV:
561 case FDIV:
562 case INEG:
563 case LNEG:
564 case FNEG:
565 case DNEG:
566 case IREM:
567 case LREM:
568 case FREM:
569 case DREM:
570 case ISHL:
571 case LSHL:
572 case ISHR:
573 case LSHR:
574 case IUSHR:
575 case LUSHR:
576 case IAND:
577 case LAND:
578 case IOR:
579 case LOR:
580 case IXOR:
581 case LXOR:
582 case I2L:
583 case I2F:
584 case I2D:
585 case L2I:
586 case L2F:
587 case L2D:
588 case F2I:
589 case F2L:
590 case F2D:
591 case D2I:
592 case D2L:
593 case D2F:
594 case I2B:
595 case I2C:
596 case I2S:
597 case LCMP:
598 case FCMPL:
599 case FCMPG:
600 case DCMPL:
601 case DCMPG:
602 case ARRAYLENGTH:
603 changed |= context.propagateValues(vals, null, location.frameAfter().getTopValue());
604 break;
605 case GETFIELD:
606 case AALOAD:
607 case DALOAD:
608 case BALOAD:
609 case CALOAD:
610 case LALOAD:
611 case SALOAD:
612 case IALOAD:
613 changed |= context.propagateValues(vals, vn, location.frameAfter().getTopValue());
614 break;
615 case AASTORE:
616 case DASTORE:
617 case BASTORE:
618 case CASTORE:
619 case LASTORE:
620 case SASTORE:
621 case IASTORE:
622 case PUTFIELD:
623 if(i == 0) {
624 ValueNumber value = vns[vns.length-1];
625 if(!value.hasFlag(ValueNumber.CONSTANT_VALUE) && !value.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT) &&
626 !context.observedValues.containsKey(value.getNumber())) {
627 changed |= context.setDerivedEscape(vals, vn);
628 }
629 changed |= context.setObjectOnly(vals, vn);
630 } else {
631 if(context.escaped(vns[0])) {
632 changed |= context.setEscape(vals);
633 } else {
634 changed |= context.propagateValues(vals, null, vns[0]);
635 }
636 }
637 break;
638 case INVOKESTATIC:
639 case INVOKESPECIAL:
640 case INVOKEINTERFACE:
641 case INVOKEVIRTUAL:
642 MethodDescriptor m = new MethodDescriptor((InvokeInstruction) inst, context.cpg);
643 XMethod xMethod = null;
644 try {
645 Type type = location.typeFrameBefore().getStackValue(nconsumed-1);
646 xMethod = Global
647 .getAnalysisCache()
648 .getClassAnalysis(XClass.class,
649 DescriptorFactory.createClassDescriptorFromSignature(type.getSignature()))
650 .findMatchingMethod(m);
651 } catch (CheckedAnalysisException e) {
652 // ignore
653 }
654 if(xMethod != null) {
655 m = xMethod.getMethodDescriptor();
656 }
657 MethodSideEffectStatus status = noSideEffectMethods.status(m);
658 if(status == MethodSideEffectStatus.NSE || status == MethodSideEffectStatus.SE_CLINIT) {
659 if(m.getName().equals("<init>")) {
660 if(vns[0].equals(context.thisValue)) {
661 changed |= context.setEscape(vals);
662 } else {
663 changed |= context.propagateValues(vals, null, vns[0]);
664 }
665 } else {
666 changed |= context.propagateToReturnValue(vals, vn, location, m);
667 }
668 break;
669 }
670 if(status == MethodSideEffectStatus.OBJ) {
671 if(i == 0) {
672 changed |= context.setDerivedEscape(vals, vn);
673 changed |= context.propagateToReturnValue(vals, vn, location, m);
674 changed |= context.setObjectOnly(vals, vn);
675 break;
676 } else {
677 if(!context.escaped(vns[0])) {
678 changed |= context.propagateValues(vals, null, vns[0]);
679 changed |= context.propagateToReturnValue(vals, vn, location, m);
680 break;
681 }
682 }
683 }
684 changed |= context.setEscape(vals);
685 break;
686 default:
687 changed |= context.setEscape(vals);
688 break;
689 }
690 }
691 }
692 }
693 }
694 } while(changed);
695 context.report();
696 }
697
698 @Override
699 public void report() {
700 }
701
702 }
119119 && "format".equals(nm) || "java/io/PrintStream".equals(cl) && "format".equals(nm)
120120 || "java/io/PrintStream".equals(cl) && "printf".equals(nm) || cl.endsWith("Writer")
121121 && "format".equals(nm) || cl.endsWith("Writer") && "printf".equals(nm)) || cl.endsWith("Logger")
122 && nm.endsWith("fmt")) {
123
124 if (formatString.indexOf('\n') >= 0)
122 && nm.endsWith("fmt")) {
123
124 if (formatString.indexOf('\n') >= 0) {
125125 bugReporter.reportBug(new BugInstance(this, "VA_FORMAT_STRING_USES_NEWLINE", NORMAL_PRIORITY)
126 .addClassAndMethod(this).addCalledMethod(this).addString(formatString)
127 .describe(StringAnnotation.FORMAT_STRING_ROLE).addSourceLine(this));
126 .addClassAndMethod(this).addCalledMethod(this).addString(formatString)
127 .describe(StringAnnotation.FORMAT_STRING_ROLE).addSourceLine(this));
128 }
128129 try {
129130 String[] signatures = new String[arguments.length];
130 for (int i = 0; i < signatures.length; i++)
131 for (int i = 0; i < signatures.length; i++) {
131132 signatures[i] = arguments[i].getSignature();
133 }
132134 Formatter.check(formatString, signatures);
133135
134136 } catch (IllegalFormatConversionException e) {
135137
136 if (e.getConversion() == 'b')
138 if (e.getConversion() == 'b') {
137139 bugReporter.reportBug(new BugInstance(this, "VA_FORMAT_STRING_BAD_CONVERSION_TO_BOOLEAN", HIGH_PRIORITY)
138 .addClassAndMethod(this).addCalledMethod(this).addType(e.getArgumentSignature())
139 .describe(TypeAnnotation.FOUND_ROLE).addString(e.getFormatSpecifier())
140 .describe(StringAnnotation.FORMAT_SPECIFIER_ROLE).addString(formatString)
141 .describe(StringAnnotation.FORMAT_STRING_ROLE)
142 .addValueSource(arguments[e.getArgIndex()], getMethod(), getPC()).addSourceLine(this));
143 else if (e.getArgumentSignature().charAt(0) == '[' && e.getConversion() == 's')
140 .addClassAndMethod(this).addCalledMethod(this).addType(e.getArgumentSignature())
141 .describe(TypeAnnotation.FOUND_ROLE).addString(e.getFormatSpecifier())
142 .describe(StringAnnotation.FORMAT_SPECIFIER_ROLE).addString(formatString)
143 .describe(StringAnnotation.FORMAT_STRING_ROLE)
144 .addValueSource(arguments[e.getArgIndex()], getMethod(), getPC()).addSourceLine(this));
145 } else if (e.getArgumentSignature().charAt(0) == '[' && e.getConversion() == 's') {
144146 bugReporter.reportBug(new BugInstance(this, "VA_FORMAT_STRING_BAD_CONVERSION_FROM_ARRAY", HIGH_PRIORITY)
145 .addClassAndMethod(this).addCalledMethod(this).addType(e.getArgumentSignature())
146 .describe(TypeAnnotation.FOUND_ROLE).addString(e.getFormatSpecifier())
147 .describe(StringAnnotation.FORMAT_SPECIFIER_ROLE).addString(formatString)
148 .describe(StringAnnotation.FORMAT_STRING_ROLE)
149 .addValueSource(arguments[e.getArgIndex()], getMethod(), getPC()).addSourceLine(this));
150 else {
147 .addClassAndMethod(this).addCalledMethod(this).addType(e.getArgumentSignature())
148 .describe(TypeAnnotation.FOUND_ROLE).addString(e.getFormatSpecifier())
149 .describe(StringAnnotation.FORMAT_SPECIFIER_ROLE).addString(formatString)
150 .describe(StringAnnotation.FORMAT_STRING_ROLE)
151 .addValueSource(arguments[e.getArgIndex()], getMethod(), getPC()).addSourceLine(this));
152 } else {
151153 String aSig = e.getArgumentSignature();
152154 char conversion = e.getConversion();
153155 if ((conversion == 't' || conversion == 'T') && aSig.charAt(0) == 'L') {
155157 assert argDescriptor != null : "sig started with L, should get descriptor";
156158 String arg = argDescriptor.toDottedClassName();
157159 try {
158 if (Hierarchy.isSubtype(arg, java.util.Date.class.getName())
159 || Hierarchy.isSubtype(arg, java.util.Calendar.class.getName())) {
160 if (arg.equals("java.time.LocalDate")
161 || Hierarchy.isSubtype(arg, java.util.Date.class.getName())
162 || Hierarchy.isSubtype(arg, java.util.Calendar.class.getName())
163 || Hierarchy.isSubtype(arg, "java.time.temporal.TemporalAccessor")) {
160164 return;
161165 }
162166 } catch (ClassNotFoundException e1) {
163 AnalysisContext.reportMissingClass(e1);
167 AnalysisContext.reportMissingClass(e1);
164168 }
165169
166170 }
167171 bugReporter.reportBug(new BugInstance(this, "VA_FORMAT_STRING_BAD_CONVERSION", HIGH_PRIORITY)
168 .addClassAndMethod(this).addCalledMethod(this).addType(aSig)
169 .describe(TypeAnnotation.FOUND_ROLE).addString(e.getFormatSpecifier())
170 .describe(StringAnnotation.FORMAT_SPECIFIER_ROLE).addString(formatString)
171 .describe(StringAnnotation.FORMAT_STRING_ROLE)
172 .addValueSource(arguments[e.getArgIndex()], getMethod(), getPC()).addSourceLine(this));
172 .addClassAndMethod(this).addCalledMethod(this).addType(aSig)
173 .describe(TypeAnnotation.FOUND_ROLE).addString(e.getFormatSpecifier())
174 .describe(StringAnnotation.FORMAT_SPECIFIER_ROLE).addString(formatString)
175 .describe(StringAnnotation.FORMAT_STRING_ROLE)
176 .addValueSource(arguments[e.getArgIndex()], getMethod(), getPC()).addSourceLine(this));
173177 }
174178 } catch (IllegalArgumentException e) {
175179 bugReporter.reportBug(new BugInstance(this, "VA_FORMAT_STRING_ILLEGAL", HIGH_PRIORITY)
176 .addClassAndMethod(this).addCalledMethod(this).addString(formatString)
177 .describe(StringAnnotation.FORMAT_STRING_ROLE).addSourceLine(this));
180 .addClassAndMethod(this).addCalledMethod(this).addString(formatString)
181 .describe(StringAnnotation.FORMAT_STRING_ROLE).addSourceLine(this));
178182 } catch (MissingFormatArgumentException e) {
179183
180184 if (e.pos < 0) {
181185 bugReporter.reportBug(new BugInstance(this, "VA_FORMAT_STRING_NO_PREVIOUS_ARGUMENT", HIGH_PRIORITY)
182 .addClassAndMethod(this).addCalledMethod(this).addString(e.formatSpecifier)
183 .describe(StringAnnotation.FORMAT_SPECIFIER_ROLE).addString(formatString)
184 .describe(StringAnnotation.FORMAT_STRING_ROLE).addSourceLine(this));
186 .addClassAndMethod(this).addCalledMethod(this).addString(e.formatSpecifier)
187 .describe(StringAnnotation.FORMAT_SPECIFIER_ROLE).addString(formatString)
188 .describe(StringAnnotation.FORMAT_STRING_ROLE).addSourceLine(this));
185189 } else {
186190 bugReporter.reportBug(new BugInstance(this, "VA_FORMAT_STRING_MISSING_ARGUMENT", HIGH_PRIORITY)
187 .addClassAndMethod(this).addCalledMethod(this).addString(e.formatSpecifier)
188 .describe(StringAnnotation.FORMAT_SPECIFIER_ROLE).addString(formatString)
189 .describe(StringAnnotation.FORMAT_STRING_ROLE).addInt(e.pos + 1)
190 .describe(IntAnnotation.INT_EXPECTED_ARGUMENTS).addInt(arguments.length)
191 .describe(IntAnnotation.INT_ACTUAL_ARGUMENTS).addSourceLine(this));
191 .addClassAndMethod(this).addCalledMethod(this).addString(e.formatSpecifier)
192 .describe(StringAnnotation.FORMAT_SPECIFIER_ROLE).addString(formatString)
193 .describe(StringAnnotation.FORMAT_STRING_ROLE).addInt(e.pos + 1)
194 .describe(IntAnnotation.INT_EXPECTED_ARGUMENTS).addInt(arguments.length)
195 .describe(IntAnnotation.INT_ACTUAL_ARGUMENTS).addSourceLine(this));
192196 }
193197
194198 } catch (ExtraFormatArgumentsException e) {
196200 String pattern = "VA_FORMAT_STRING_EXTRA_ARGUMENTS_PASSED";
197201 if (e.used == 0) {
198202 priority = HIGH_PRIORITY;
199 if (formatString.indexOf("{0") >= 0 || formatString.indexOf("{1") >= 0)
203 if (formatString.indexOf("{0") >= 0 || formatString.indexOf("{1") >= 0) {
200204 pattern = "VA_FORMAT_STRING_EXPECTED_MESSAGE_FORMAT_SUPPLIED";
205 }
201206 }
202207
203208 bugReporter.reportBug(new BugInstance(this, pattern, priority).addClassAndMethod(this).addCalledMethod(this)
4949
5050 final BugReporter bugReporter;
5151
52 private final boolean testingEnabled;
53
5254 final static boolean REPORT_INFERRED_METHODS = SystemProperties.getBoolean("mrc.inferred.report");
5355
5456 public FunctionsThatMightBeMistakenForProcedures(BugReporter bugReporter) {
5557 this.bugReporter = bugReporter;
5658 setVisitMethodsInCallOrder(true);
59 testingEnabled = SystemProperties.getBoolean("report_TESTING_pattern_in_standard_detectors");
5760 }
5861
5962 boolean isInnerClass, hasNonFinalFields;
6770
6871 @Override
6972 public void visit(Field obj) {
70 if (obj.getName().equals("this$0"))
73 if ("this$0".equals(obj.getName())) {
7174 isInnerClass = true;
72 if (!obj.isFinal() && !obj.isStatic() && !BCELUtil.isSynthetic(obj))
75 }
76 if (!obj.isFinal() && !obj.isStatic() && !BCELUtil.isSynthetic(obj)) {
7377 hasNonFinalFields = true;
78 }
7479 }
7580
7681 @Override
106111 String returnType = parser.getReturnTypeSignature();
107112 @SlashedClassName
108113 String r = ClassName.fromFieldSignature(returnType);
109 if (r == null || !r.equals(getClassName()))
114 if (r == null || !r.equals(getClassName())) {
110115 return;
116 }
111117 // System.out.println("Checking " + getFullyQualifiedMethodName());
112118 boolean funky = false;
113119 for (int i = 0; i < parser.getNumParameters(); i++) {
128134 funky = true;
129135 }
130136
131 // if (false) {
132 // XClass c = getXClass();
133 // String classSourceSig = c.getSourceSignature();
134 // if (!genericReturnValue.equals(classSourceSig))
135 // return;
136 // }
137 }
138
139 // System.out.println("Investigating " + getFullyQualifiedMethodName());
137 // if (false) {
138 // XClass c = getXClass();
139 // String classSourceSig = c.getSourceSignature();
140 // if (!genericReturnValue.equals(classSourceSig))
141 // return;
142 // }
143 }
144
145 // System.out.println("Investigating " + getFullyQualifiedMethodName());
140146 returnSelf = returnOther = updates = returnNew = returnUnknown = 0;
141147
142 if (REPORT_INFERRED_METHODS
143 && AnalysisContext.currentAnalysisContext().isApplicationClass(getThisClass()))
148 if (testingEnabled && REPORT_INFERRED_METHODS
149 && AnalysisContext.currentAnalysisContext().isApplicationClass(getThisClass())) {
144150 inferredMethod = new BugInstance("TESTING", NORMAL_PRIORITY).addClassAndMethod(this);
145 else
151 } else {
146152 inferredMethod = null;
153 }
147154 super.visit(code); // make callbacks to sawOpcode for all opcodes
148 // System.out.printf(" %3d %3d %3d %3d%n", returnSelf, updates, returnOther, returnNew);
155 // System.out.printf(" %3d %3d %3d %3d%n", returnSelf, updates, returnOther, returnNew);
149156
150157 if (returnSelf > 0 && returnOther == 0) {
151158 okToIgnore.add(m);
154161 } else if (returnOther > 0 && returnOther >= returnSelf && returnNew > 0 && returnNew >= returnOther - 1) {
155162
156163 int priority = HIGH_PRIORITY;
157 if (returnSelf > 0 || updates > 0)
164 if (returnSelf > 0 || updates > 0) {
158165 priority++;
159 if (returnUnknown > 0)
166 }
167 if (returnUnknown > 0) {
160168 priority++;
161 if (returnNew > 0 && priority > NORMAL_PRIORITY)
169 }
170 if (returnNew > 0 && priority > NORMAL_PRIORITY) {
162171 priority = NORMAL_PRIORITY;
163 if (updates > 0)
172 }
173 if (updates > 0) {
164174 priority = LOW_PRIORITY;
165 if (priority <= HIGH_PRIORITY)
175 }
176 if (priority <= HIGH_PRIORITY) {
166177 doNotIgnoreHigh.add(m);
178 }
167179 if (priority <= NORMAL_PRIORITY) {
168180 // System.out.printf(" adding %d %s%n", priority,
169181 // MethodAnnotation.fromVisitedMethod(this).getSourceLines());
191203 switch (seen) {
192204 case INVOKEVIRTUAL:
193205 case INVOKESPECIAL: {
194 if (getMethod().isStatic() || !hasNonFinalFields)
195 break;
206 if (getMethod().isStatic() || !hasNonFinalFields) {
207 break;
208 }
196209
197210 String name = getNameConstantOperand();
198211 String sig = getSigConstantOperand();
199212 if ((name.startsWith("set") || name.startsWith("update")) || sig.endsWith(")V")) {
200213 Item invokedOn = stack.getItemMethodInvokedOn(this);
201 if (invokedOn.isInitialParameter() && invokedOn.getRegisterNumber() == 0)
214 if (invokedOn.isInitialParameter() && invokedOn.getRegisterNumber() == 0) {
202215 updates++;
203 if (inferredMethod != null)
216 }
217 if (inferredMethod != null) {
204218 inferredMethod.addCalledMethod(this);
219 }
205220 }
206221 break;
207222 }
208223
209224 case ARETURN: {
210225 OpcodeStack.Item rv = stack.getStackItem(0);
211 if (rv.isNull())
212 break;
226 if (rv.isNull()) {
227 break;
228 }
213229 if (rv.isInitialParameter()) {
214230 returnSelf++;
215231 break;
220236 returnSelf++;
221237 break;
222238 }
223 if (inferredMethod != null)
239 if (inferredMethod != null) {
224240 inferredMethod.addCalledMethod(xMethod);
241 }
225242 if (okToIgnore.contains(xMethod) ) {
226243 returnSelf++;
227244 break;
228245 }
229 if (xMethod.getName().equals("<init>")) {
246 if ("<init>".equals(xMethod.getName())) {
230247 String sig = xMethod.getSignature();
231248 // returning a newly constructed value
232249 boolean voidConstructor;
233250 if (!isInnerClass) {
234 voidConstructor = sig.equals("()V");
251 voidConstructor = "()V".equals(sig);
235252 } else {
236253 SignatureParser parser = new SignatureParser(sig);
237254 voidConstructor = parser.getNumParameters() <= 1;
251268 returnUnknown++;
252269 break;
253270 }
254 if (xMethod.getName().equals("<init>") || doNotIgnoreHigh.contains(xMethod)) {
271 if ("<init>".equals(xMethod.getName()) || doNotIgnoreHigh.contains(xMethod)) {
255272 returnOther++;
256273 // System.out.println(" calls " + xMethod);
257274 // System.out.println(" at " +
258275 // MethodAnnotation.fromXMethod(xMethod).getSourceLines());
259 if (xMethod.getName().equals("<init>") || doNotIgnore.contains(xMethod))
276 if ("<init>".equals(xMethod.getName()) || doNotIgnore.contains(xMethod)) {
260277 returnNew++;
278 }
261279 } else if (doNotIgnore.contains(xMethod)) {
262280 returnOther++;
263281 // System.out.println(" calls " + xMethod);
264282 // System.out.println(" at " +
265283 // MethodAnnotation.fromXMethod(xMethod).getSourceLines());
266284
267 } else
285 } else {
268286 returnUnknown++;
287 }
269288
270289 }
271290 break;
273292
274293 OpcodeStack.Item rv = stack.getStackItem(1);
275294 if (rv.getRegisterNumber() == 0 && rv.isInitialParameter()) {
276 if (inferredMethod != null)
295 if (inferredMethod != null) {
277296 inferredMethod.addReferencedField(this);
297 }
278298 updates++;
279299
280300 }
281301 }
302 break;
303 default:
304 break;
282305 }
283306 }
284307 }
6161 @Override
6262 public void visit(ConstantString s) {
6363 String value = s.getBytes(getConstantPool());
64 if (value.length() < SIZE_OF_HUGE_CONSTANT)
64 if (value.length() < SIZE_OF_HUGE_CONSTANT) {
6565 return;
66 }
6667 String key = getStringKey(value);
6768 SortedSet<String> set = map.get(key);
6869 if (set == null) {
7475
7576 @Override
7677 public void visit(ConstantValue s) {
77 if (!visitingField())
78 if (!visitingField()) {
7879 return;
80 }
7981 int i = s.getConstantValueIndex();
8082 Constant c = getConstantPool().getConstant(i);
8183 if (c instanceof ConstantString) {
8284 String value = ((ConstantString) c).getBytes(getConstantPool());
83 if (value.length() < SIZE_OF_HUGE_CONSTANT)
85 if (value.length() < SIZE_OF_HUGE_CONSTANT) {
8486 return;
87 }
8588 String key = getStringKey(value);
8689 definition.put(key, XFactory.createXField(this));
8790 stringSize.put(key, value.length());
9396 public void report() {
9497 for (Map.Entry<String, SortedSet<String>> e : map.entrySet()) {
9598 Set<String> occursIn = e.getValue();
96 if (occursIn.size() == 1)
99 if (occursIn.size() == 1) {
97100 continue;
101 }
98102 XField field = definition.get(e.getKey());
99 if (field == null)
103 if (field == null) {
100104 continue;
105 }
101106 Integer length = stringSize.get(e.getKey());
102107 int overhead = length * (occursIn.size() - 1);
103 if (overhead < 3 * SIZE_OF_HUGE_CONSTANT)
108 if (overhead < 3 * SIZE_OF_HUGE_CONSTANT) {
104109 continue;
110 }
105111 String className = field.getClassName();
106112
107113 BugInstance bug = new BugInstance(this, "HSC_HUGE_SHARED_STRING_CONSTANT",
108114 overhead > 20 * SIZE_OF_HUGE_CONSTANT ? HIGH_PRIORITY
109115 : (overhead > 8 * SIZE_OF_HUGE_CONSTANT ? NORMAL_PRIORITY : LOW_PRIORITY)).addClass(className)
110 .addField(field).addInt(length).addInt(occursIn.size()).describe(IntAnnotation.INT_OCCURRENCES);
111 for (String c : occursIn)
112 if (!c.equals(className))
116 .addField(field).addInt(length).addInt(occursIn.size()).describe(IntAnnotation.INT_OCCURRENCES);
117 for (String c : occursIn) {
118 if (!c.equals(className)) {
113119 bug.addClass(c);
120 }
121 }
114122
115123 bugReporter.reportBug(bug);
116124
1313 public class IDivResultCastToDouble extends BytecodeScanningDetector {
1414 private static final boolean DEBUG = SystemProperties.getBoolean("idcd.debug");
1515
16 private final BugReporter bugReporter;
16 // private final BugReporter bugReporter;
1717
1818 private final BugAccumulator bugAccumulator;
1919
2020 private int prevOpCode;
2121
2222 public IDivResultCastToDouble(BugReporter bugReporter) {
23 this.bugReporter = bugReporter;
23 // this.bugReporter = bugReporter;
2424 this.bugAccumulator = new BugAccumulator(bugReporter);
2525 }
2626
2727 @Override
2828 public void visit(Method obj) {
29 if (DEBUG)
29 if (DEBUG) {
3030 System.out.println("Visiting " + obj);
31 }
3132 }
3233
3334 @Override
4142 @Override
4243 public void sawOpcode(int seen) {
4344
44 if (DEBUG)
45 if (DEBUG) {
4546 System.out.println("Saw opcode " + OPCODE_NAMES[seen] + " " + pendingIdivCastToDivBugLocation);
47 }
4648
4749 if ((prevOpCode == I2D || prevOpCode == L2D) && seen == INVOKESTATIC && ClassName.isMathClass(getClassConstantOperand())
48 && getNameConstantOperand().equals("ceil")) {
50 && "ceil".equals(getNameConstantOperand())) {
4951 bugAccumulator
50 .accumulateBug(new BugInstance(this, "ICAST_INT_CAST_TO_DOUBLE_PASSED_TO_CEIL", HIGH_PRIORITY)
51 .addClassAndMethod(this), this);
52 .accumulateBug(new BugInstance(this, "ICAST_INT_CAST_TO_DOUBLE_PASSED_TO_CEIL", HIGH_PRIORITY)
53 .addClassAndMethod(this), this);
5254 pendingIdivCastToDivBugLocation = null;
5355 } else if ((prevOpCode == I2F || prevOpCode == L2F) && seen == INVOKESTATIC
54 && ClassName.isMathClass(getClassConstantOperand()) && getNameConstantOperand().equals("round")) {
56 && ClassName.isMathClass(getClassConstantOperand()) && "round".equals(getNameConstantOperand())) {
5557 bugAccumulator.accumulateBug(
5658 new BugInstance(this, "ICAST_INT_CAST_TO_FLOAT_PASSED_TO_ROUND", NORMAL_PRIORITY).addClassAndMethod(this),
5759 this);
6365 pendingIdivCastToDivBugLocation = null;
6466 }
6567
66 if (prevOpCode == IDIV && (seen == I2D || seen == I2F) || prevOpCode == LDIV && (seen == L2D || seen == L2F))
68 if (prevOpCode == IDIV && (seen == I2D || seen == I2F) || prevOpCode == LDIV && (seen == L2D || seen == L2F)) {
6769 pendingIdivCastToDivBugLocation = SourceLineAnnotation.fromVisitedInstruction(this);
70 }
6871 prevOpCode = seen;
6972 }
7073 }
3333 * instructions.
3434 */
3535 public class IOStreamFactory implements StreamFactory {
36 private ObjectType baseClassType;
36 private final ObjectType baseClassType;
3737
38 private ObjectType[] uninterestingSubclassTypeList;
38 private final ObjectType[] uninterestingSubclassTypeList;
3939
40 private String bugType;
40 private final String bugType;
4141
42 @Override
43 public String toString() {
44 return "IOStreamFactory("+baseClassType+")";
45 }
4246 public IOStreamFactory(String baseClass, String[] uninterestingSubclassList, String bugType) {
4347 this.baseClassType = ObjectTypeFactory.getInstance(baseClass);
4448 this.uninterestingSubclassTypeList = new ObjectType[uninterestingSubclassList.length];
4852 this.bugType = bugType;
4953 }
5054
55 @Override
5156 public Stream createStream(Location location, ObjectType type, ConstantPoolGen cpg,
5257 RepositoryLookupFailureCallback lookupFailureCallback) {
5358
5459 try {
5560 Instruction ins = location.getHandle().getInstruction();
5661
57 if (ins.getOpcode() != Constants.NEW)
62 if (ins.getOpcode() != Constants.NEW) {
5863 return null;
64 }
5965
6066 if (Hierarchy.isSubtype(type, baseClassType)) {
6167 boolean isUninteresting = false;
6672 }
6773 }
6874 Stream result = new Stream(location, type.getClassName(), baseClassType.getClassName())
69 .setIgnoreImplicitExceptions(true);
70 if (!isUninteresting)
75 .setIgnoreImplicitExceptions(true);
76 if (!isUninteresting) {
7177 result.setInteresting(bugType);
78 }
7279 return result;
7380 }
7481 } catch (ClassNotFoundException e) {
7986 }
8087 }
8188
82 // vim:ts=3
2828 /**
2929 * Find comparisons involving values computed with bitwise operations whose
3030 * outcomes are fixed at compile time.
31 *
31 *
3232 * @author Tom Truscott <trt@unx.sas.com>
3333 */
3434 public class IncompatMask extends BytecodeScanningDetector implements StatelessDetector {
4040
4141 boolean isLong;
4242
43 private BugReporter bugReporter;
43 private final BugReporter bugReporter;
4444
4545 public IncompatMask(BugReporter bugReporter) {
4646 this.state = 0;
5454 }
5555
5656 private void checkState(int expectedState) {
57 if (state == expectedState)
57 if (state == expectedState) {
5858 state++;
59 else
60 state = 0;
59 } else {
60 state = 0;
61 }
6162 }
6263
6364 private void noteVal(long val) {
64 if (state == 0)
65 if (state == 0) {
6566 arg0 = val;
66 else if (state == 2)
67 } else if (state == 2) {
6768 arg1 = val;
68 else
69 } else {
6970 state = -1;
71 }
7072 state++;
7173 }
7274
8385 static int populationCount(long i) {
8486 int result = 0;
8587 while (i != 0) {
86 if ((i & 1) == 1)
88 if ((i & 1) == 1) {
8789 result++;
90 }
8891 i >>>= 1;
8992 }
9093 return result;
159162 case IFLT:
160163 case IFGT:
161164 case IFGE:
162 if (state == 3 && isLong || state == 2 & !isLong) {
165 if (state == 3 && isLong || state == 2 && !isLong) {
163166 long bits = getFlagBits(isLong, arg0);
164167 boolean highbit = !isLong && (bits & 0x80000000) != 0 || isLong && bits < 0 && bits << 1 == 0;
165168 boolean onlyLowBits = bits >>> 12 == 0;
166169 BugInstance bug;
167 if (highbit)
170 if (highbit) {
168171 bug = new BugInstance(this, "BIT_SIGNED_CHECK_HIGH_BIT", (seen == IFLE || seen == IFGT) ? HIGH_PRIORITY
169172 : NORMAL_PRIORITY);
170 else
173 } else {
171174 bug = new BugInstance(this, "BIT_SIGNED_CHECK", onlyLowBits ? LOW_PRIORITY : NORMAL_PRIORITY);
175 }
172176 bugReporter.reportBug(bug.addClassAndMethod(this).addSourceLine(this));
173177 }
174178 state = 0;
182186 state = 3;
183187 }
184188
185 /* fallthrough */
186
189 //$FALL-THROUGH$
187190 case IF_ICMPEQ:
188191 case IF_ICMPNE:
189192 checkState(3);
190 if (state != 4)
193 if (state != 4) {
191194 return;
195 }
192196 break; /* the only break in this switch! gross */
193197
194198 case GOTO:
218222 if (dif != 0) {
219223 // System.out.println("Match at offset " + getPC());
220224 BugInstance bug = new BugInstance(this, t, HIGH_PRIORITY).addClassAndMethod(this);
221 if (!t.equals("BIT_AND_ZZ"))
225 if (!"BIT_AND_ZZ".equals(t)) {
222226 bug.addString("0x" + Long.toHexString(arg0)).addString("0x" + Long.toHexString(arg1));
227 }
223228
224229 bug.addSourceLine(this);
225230 bugReporter.reportBug(bug);
227232 state = 0;
228233 }
229234
230 /**
231 * @return
232 */
233235 static long getFlagBits(boolean isLong, long arg0) {
234236 long bits = arg0;
235237 if (isLong) {
236 if (populationCount(bits) > populationCount(~bits))
238 if (populationCount(bits) > populationCount(~bits)) {
237239 bits = ~bits;
238 } else if (populationCount(0xffffffffL & bits) > populationCount(0xffffffffL & ~bits))
240 }
241 } else if (populationCount(0xffffffffL & bits) > populationCount(0xffffffffL & ~bits)) {
239242 bits = 0xffffffffL & ~bits;
243 }
240244 return bits;
241245 }
242246 }
243247
244 // vim:ts=4
4040
4141 public class InconsistentAnnotations implements Detector, UseAnnotationDatabase {
4242
43 public final TypeQualifierValue nonnullTypeQualifierValue;
43 public final TypeQualifierValue<?> nonnullTypeQualifierValue;
4444
4545 final BugReporter reporter;
4646
5050 this.reporter = reporter;
5151 }
5252
53 @Override
5354 public void visitClassContext(ClassContext classContext) {
5455
5556 JavaClass jclass = classContext.getJavaClass();
7273
7374 reporter.reportBug(new BugInstance(this, "NP_PARAMETER_MUST_BE_NONNULL_BUT_MARKED_AS_NULLABLE",
7475 NORMAL_PRIORITY).addClassAndMethod(jclass, method).add(
75 LocalVariableAnnotation.getParameterLocalVariableAnnotation(method, paramLocal)));
76 LocalVariableAnnotation.getParameterLocalVariableAnnotation(method, paramLocal)));
7677
7778 }
7879
8283
8384 }
8485
86 @Override
8587 public void report() {
8688 }
8789 }
2222
2323 /**
2424 * Warning properties for inconsistent synchronization detector.
25 *
25 *
2626 * @author David Hovemeyer
2727 */
2828 public class InconsistentSyncWarningProperty extends AbstractWarningProperty {
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2006, University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.detect;
20
21 import java.util.Arrays;
22 import java.util.List;
23
24 import edu.umd.cs.findbugs.BugInstance;
25 import edu.umd.cs.findbugs.BugReporter;
26 import edu.umd.cs.findbugs.OpcodeStack;
27 import edu.umd.cs.findbugs.StringAnnotation;
28 import edu.umd.cs.findbugs.ba.ClassContext;
29 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
30 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
31
32 /**
33 * Use whenever possible String.indexOf(int) instead of String.indexOf(String),
34 * or String.lastIndexOf(int) instead of String.lastIndexOf(String).
35 *
36 * @author Reto Merz
37 */
38 public class InefficientIndexOf extends OpcodeStackDetector {
39 private final BugReporter bugReporter;
40
41 private static final List<MethodDescriptor> methods = Arrays.asList(
42 new MethodDescriptor("java/lang/String", "indexOf", "(Ljava/lang/String;)I"),
43 new MethodDescriptor("java/lang/String", "lastIndexOf", "(Ljava/lang/String;)I"),
44 new MethodDescriptor("java/lang/String", "indexOf", "(Ljava/lang/String;I)I"),
45 new MethodDescriptor("java/lang/String", "lastIndexOf", "(Ljava/lang/String;I)I")
46 );
47
48 public InefficientIndexOf(BugReporter bugReporter) {
49 this.bugReporter = bugReporter;
50 }
51
52 @Override
53 public void visitClassContext(ClassContext classContext) {
54 if(hasInterestingMethod(classContext.getJavaClass().getConstantPool(), methods)) {
55 super.visitClassContext(classContext);
56 }
57 }
58
59 @Override
60 public void sawOpcode(int seen) {
61 if (seen == INVOKEVIRTUAL && stack.getStackDepth() > 0 && "java/lang/String".equals(getClassConstantOperand())) {
62
63 boolean lastIndexOf = "lastIndexOf".equals(getNameConstantOperand());
64 if (lastIndexOf || "indexOf".equals(getNameConstantOperand())) {
65
66 int stackOff = -1;
67 if ("(Ljava/lang/String;)I".equals(getSigConstantOperand())) { // sig: String
68 stackOff = 0;
69 } else if ("(Ljava/lang/String;I)I".equals(getSigConstantOperand())) { // sig: String, int
70 stackOff = 1;
71 }
72 if (stackOff > -1) {
73 OpcodeStack.Item item = stack.getStackItem(stackOff);
74 Object o = item.getConstant();
75 if (o != null && ((String) o).length() == 1) {
76 bugReporter.reportBug(new BugInstance(this, lastIndexOf ? "IIO_INEFFICIENT_LAST_INDEX_OF" : "IIO_INEFFICIENT_INDEX_OF", LOW_PRIORITY).addClassAndMethod(this)
77 .describe(StringAnnotation.STRING_MESSAGE).addCalledMethod(this).addSourceLine(this));
78 }
79 }
80 }
81 }
82 }
83
84 }
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2008 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.detect;
20
21 import java.util.ArrayList;
22 import java.util.Arrays;
23 import java.util.Collections;
24 import java.util.HashSet;
25 import java.util.List;
26 import java.util.Map.Entry;
27 import java.util.Set;
28 import java.util.SortedMap;
29 import java.util.TreeMap;
30
31 import org.apache.bcel.classfile.Method;
32
33 import edu.umd.cs.findbugs.BugInstance;
34 import edu.umd.cs.findbugs.BugReporter;
35 import edu.umd.cs.findbugs.OpcodeStack.Item;
36 import edu.umd.cs.findbugs.StringAnnotation;
37 import edu.umd.cs.findbugs.ba.ClassContext;
38 import edu.umd.cs.findbugs.ba.XMethod;
39 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
40 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
41
42 /**
43 * @author Tagir Valeev
44 */
45 public class InefficientInitializationInsideLoop extends OpcodeStackDetector {
46 private static final MethodDescriptor NODELIST_GET_LENGTH = new MethodDescriptor("org/w3c/dom/NodeList", "getLength", "()I");
47 private static final MethodDescriptor PATTERN_COMPILE = new MethodDescriptor("java/util/regex/Pattern", "compile", "(Ljava/lang/String;)Ljava/util/regex/Pattern;", true);
48 private static final MethodDescriptor PATTERN_COMPILE_2 = new MethodDescriptor("java/util/regex/Pattern", "compile", "(Ljava/lang/String;I)Ljava/util/regex/Pattern;", true);
49 private static final MethodDescriptor PATTERN_MATCHES = new MethodDescriptor("java/util/regex/Pattern", "matches", "(Ljava/lang/String;Ljava/lang/CharSequence;)Z", true);
50 private static final MethodDescriptor STRING_REPLACEALL = new MethodDescriptor("java/lang/String", "replaceAll", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
51 private static final MethodDescriptor STRING_REPLACEFIRST = new MethodDescriptor("java/lang/String", "replaceFirst", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
52 private static final MethodDescriptor STRING_MATCHES = new MethodDescriptor("java/lang/String", "matches", "(Ljava/lang/String;)Z");
53 private static final MethodDescriptor STRING_SPLIT = new MethodDescriptor("java/lang/String", "split", "(Ljava/lang/String;)[Ljava/lang/String;");
54 private static final MethodDescriptor STRING_SPLIT_2 = new MethodDescriptor("java/lang/String", "split", "(Ljava/lang/String;I)[Ljava/lang/String;");
55
56 private static final Set<MethodDescriptor> implicitPatternMethods = new HashSet<>(Arrays.asList(PATTERN_MATCHES,
57 STRING_MATCHES, STRING_REPLACEALL, STRING_REPLACEFIRST, STRING_SPLIT, STRING_SPLIT_2));
58
59 private static final List<MethodDescriptor> methods = new ArrayList<>();
60
61 static {
62 methods.add(NODELIST_GET_LENGTH);
63 methods.add(PATTERN_COMPILE);
64 methods.add(PATTERN_COMPILE_2);
65 methods.addAll(implicitPatternMethods);
66 }
67
68 private SortedMap<Integer, BugInstance> matched;
69
70 private SortedMap<Integer, Integer> conditions;
71
72 private SortedMap<Integer, Integer> sources;
73
74 private final BugReporter bugReporter;
75
76 public InefficientInitializationInsideLoop(BugReporter bugReporter) {
77 this.bugReporter = bugReporter;
78 }
79
80 @Override
81 public void visitClassContext(ClassContext classContext) {
82 if (hasInterestingMethod(classContext.getJavaClass().getConstantPool(), methods)
83 || hasInterestingClass(classContext.getJavaClass().getConstantPool(),
84 Collections.singleton("java/sql/Connection"))) {
85 super.visitClassContext(classContext);
86 }
87 }
88
89 @Override
90 public void visitMethod(Method obj) {
91 matched = new TreeMap<>();
92 conditions = new TreeMap<>();
93 sources = new TreeMap<>();
94 super.visitMethod(obj);
95 }
96
97 /**
98 * Since JDK 1.7 there's a special branch in String.split which works very fast for one-character pattern
99 * We do not report a bug if this case takes place
100 * (in fact precompilation will make split much slower since this fast path doesn't use regexp engine at all)
101 * @param regex regex to test whether it's suitable for the fast path
102 * @return true if fast path is possible
103 */
104 private boolean isFastPath(String regex) {
105 char ch;
106 return (((regex.length() == 1 && ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) || (regex.length() == 2
107 && regex.charAt(0) == '\\' && (((ch = regex.charAt(1)) - '0') | ('9' - ch)) < 0 && ((ch - 'a') | ('z' - ch)) < 0 && ((ch - 'A') | ('Z' - ch)) < 0)) && (ch < Character.MIN_HIGH_SURROGATE || ch > Character.MAX_LOW_SURROGATE));
108 }
109
110 @Override
111 public void sawOpcode(int seen) {
112 if (seen == INVOKEINTERFACE && getClassConstantOperand().equals("java/sql/Connection")
113 && getMethodDescriptorOperand().getName().equals("prepareStatement") && hasConstantArguments()) {
114 matched.put(getPC(), new BugInstance(this, "IIL_PREPARE_STATEMENT_IN_LOOP", NORMAL_PRIORITY).addClassAndMethod(this)
115 .addSourceLine(this, getPC()).addCalledMethod(this));
116 } else if (seen == INVOKEINTERFACE && getMethodDescriptorOperand().equals(NODELIST_GET_LENGTH)) {
117 Item item = getStack().getStackItem(0);
118 XMethod returnValueOf = item.getReturnValueOf();
119 if(returnValueOf != null && returnValueOf.getClassName().startsWith("org.w3c.dom.") && returnValueOf.getName().startsWith("getElementsByTagName")) {
120 matched.put(getPC(),
121 new BugInstance(this, "IIL_ELEMENTS_GET_LENGTH_IN_LOOP", NORMAL_PRIORITY).addClassAndMethod(this)
122 .addSourceLine(this, getPC()).addCalledMethod(this));
123 sources.put(getPC(), item.getPC());
124 }
125 } else if (seen == INVOKESTATIC
126 && (getMethodDescriptorOperand().equals(PATTERN_COMPILE) || getMethodDescriptorOperand()
127 .equals(PATTERN_COMPILE_2)) && hasConstantArguments()) {
128 String regex = getFirstArgument();
129 matched.put(getPC(), new BugInstance(this, "IIL_PATTERN_COMPILE_IN_LOOP", NORMAL_PRIORITY).addClassAndMethod(this)
130 .addSourceLine(this, getPC()).addCalledMethod(this).addString(regex).describe(StringAnnotation.REGEX_ROLE));
131 } else if ((seen == INVOKESTATIC || seen == INVOKEVIRTUAL) && implicitPatternMethods.contains(getMethodDescriptorOperand())) {
132 String regex = getFirstArgument();
133 if (regex != null && !(getNameConstantOperand().equals("split") && isFastPath(regex))) {
134 BugInstance bug = new BugInstance(this, "IIL_PATTERN_COMPILE_IN_LOOP_INDIRECT", LOW_PRIORITY)
135 .addClassAndMethod(this).addSourceLine(this, getPC()).addCalledMethod(this).addString(regex)
136 .describe(StringAnnotation.REGEX_ROLE);
137 matched.put(getPC(), bug);
138 }
139 } else if (isBranch(seen) && getBranchOffset() > 0) {
140 conditions.put(getPC(), getBranchTarget());
141 } else if (!matched.isEmpty() && isBranch(seen) && getBranchOffset() < 0) {
142 for (Entry<Integer, BugInstance> entry : matched.tailMap(getBranchTarget()).entrySet()) {
143 Integer source = sources.get(entry.getKey());
144 if(source != null && (source > getBranchTarget() && source < getPC())) {
145 // Object was created in the same loop: ignore
146 return;
147 }
148 for (int target : conditions.subMap(getBranchTarget(), entry.getKey()).values()) {
149 if (target > entry.getKey() && target < getPC()) {
150 return;
151 }
152 }
153 bugReporter.reportBug(entry.getValue());
154 }
155 }
156 }
157
158 /**
159 * @return first argument of the called method if it's a constant
160 */
161 private String getFirstArgument() {
162 Object value = getStack().getStackItem(getNumberArguments(getMethodDescriptorOperand().getSignature()) - 1)
163 .getConstant();
164 return value == null ? null : value.toString();
165 }
166
167 /**
168 * @return true if only constants are passed to the called method
169 */
170 private boolean hasConstantArguments() {
171 int nArgs = getNumberArguments(getMethodDescriptorOperand().getSignature());
172 for (int i = 0; i < nArgs; i++) {
173 if (getStack().getStackItem(i).getConstant() == null) {
174 return false;
175 }
176 }
177 return true;
178 }
179
180 }
2525 import edu.umd.cs.findbugs.BugReporter;
2626 import edu.umd.cs.findbugs.BytecodeScanningDetector;
2727 import edu.umd.cs.findbugs.StatelessDetector;
28 import edu.umd.cs.findbugs.ba.AnalysisContext;
2829 import edu.umd.cs.findbugs.ba.ClassContext;
30 import edu.umd.cs.findbugs.ba.InnerClassAccess;
31 import edu.umd.cs.findbugs.ba.SignatureParser;
2932
3033 public class InefficientMemberAccess extends BytecodeScanningDetector implements StatelessDetector {
3134
3235 public static final String ACCESS_PREFIX = "access$";
3336
34 private BugReporter bugReporter;
37 private final BugReporter bugReporter;
3538
3639 private String clsName;
3740
4346 public void visitClassContext(ClassContext classContext) {
4447 JavaClass cls = classContext.getJavaClass();
4548 clsName = cls.getClassName();
46 if (clsName.indexOf("$") >= 0)
49 if (clsName.indexOf('$') >= 0) {
4750 super.visitClassContext(classContext);
51 }
4852 }
4953
5054 @Override
5256
5357 if (seen == INVOKESTATIC) {
5458 String methodName = getNameConstantOperand();
55 if (!methodName.startsWith(ACCESS_PREFIX))
59 if (!methodName.startsWith(ACCESS_PREFIX)) {
5660 return;
61 }
5762
5863 String methodSig = getSigConstantOperand();
5964 Type[] argTypes = Type.getArgumentTypes(methodSig);
60 if ((argTypes.length < 1) || (argTypes.length > 2))
65 if ((argTypes.length < 1) || (argTypes.length > 2)) {
6166 return;
67 }
6268 String parCls = argTypes[0].getSignature();
63 if (parCls.length() < 3)
69 if (parCls.length() < 3) {
6470 return;
71 }
6572 parCls = parCls.substring(1, parCls.length() - 1);
66 if (!parCls.equals(getClassConstantOperand()))
73 if (!parCls.equals(getClassConstantOperand())) {
6774 return;
68 if ((argTypes.length == 2) && !argTypes[1].getSignature().equals(Type.getReturnType(methodSig).getSignature()))
75 }
76 if ((argTypes.length == 2) && !argTypes[1].getSignature().equals(new SignatureParser(methodSig).getReturnTypeSignature())) {
6977 return;
78 }
7079
71 bugReporter.reportBug(new BugInstance(this, "IMA_INEFFICIENT_MEMBER_ACCESS", LOW_PRIORITY).addClassAndMethod(this)
72 .addSourceLine(this));
80 InnerClassAccess access = null;
81 try {
82 String dottedClassConstantOperand = getDottedClassConstantOperand();
83 access = AnalysisContext.currentAnalysisContext().getInnerClassAccessMap().getInnerClassAccess(dottedClassConstantOperand, methodName);
84 if(access != null) {
85 // if the enclosing class of the field differs from the enclosing class of the method, we shouln't report
86 // because there is nothing wrong: see bug 1226
87 if (!access.getField().getClassName().equals(dottedClassConstantOperand)) {
88 return;
89 }
90 // the access method is created to access the synthetic reference to the enclosing class, we shouln't report
91 // user can't do anything here, see bug 1191
92 if(access.getField().isSynthetic()){
93 return;
94 }
95 }
96 } catch (ClassNotFoundException e) {
97 }
98
99 BugInstance bug = new BugInstance(this, "IMA_INEFFICIENT_MEMBER_ACCESS", LOW_PRIORITY).addClassAndMethod(this)
100 .addSourceLine(this);
101 if(access != null) {
102 bug.addField(access.getField());
103 }
104 bugReporter.reportBug(bug);
73105 }
74106 }
75107
1919
2020 package edu.umd.cs.findbugs.detect;
2121
22 import java.util.Collections;
23 import java.util.List;
24
2225 import org.apache.bcel.Repository;
2326 import org.apache.bcel.classfile.Code;
2427 import org.apache.bcel.classfile.JavaClass;
3235 import edu.umd.cs.findbugs.SystemProperties;
3336 import edu.umd.cs.findbugs.ba.AnalysisContext;
3437 import edu.umd.cs.findbugs.ba.ClassContext;
38 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
3539
3640 /**
3741 * Find occurrences of collection.toArray( new Foo[0] ); This causes another
3842 * memory allocation through reflection Much better to do collection.toArray(
3943 * new Foo[collection.size()] );
40 *
44 *
4145 * @author Dave Brosius
4246 */
4347 public class InefficientToArray extends BytecodeScanningDetector implements StatelessDetector {
4448 private static final boolean DEBUG = SystemProperties.getBoolean("ita.debug");
49
50 private static final List<MethodDescriptor> methods = Collections.singletonList(new MethodDescriptor("", "toArray",
51 "([Ljava/lang/Object;)[Ljava/lang/Object;"));
4552
4653 static final int SEEN_NOTHING = 0;
4754
5158
5259 private final static JavaClass collectionClass;
5360
54 private BugReporter bugReporter;
61 private final BugReporter bugReporter;
5562
56 private BugAccumulator bugAccumulator;
63 private final BugAccumulator bugAccumulator;
5764
5865 private int state = SEEN_NOTHING;
5966
7481
7582 @Override
7683 public void visitClassContext(ClassContext classContext) {
77 if (collectionClass != null)
84 if (collectionClass != null && hasInterestingMethod(classContext.getJavaClass().getConstantPool(), methods)) {
7885 classContext.getJavaClass().accept(this);
86 }
7987 }
8088
8189 @Override
8290 public void visit(Method obj) {
83 if (DEBUG)
91 if (DEBUG) {
8492 System.out.println("------------------- Analyzing " + obj.getName() + " ----------------");
93 }
8594 state = SEEN_NOTHING;
8695 super.visit(obj);
8796 }
95104
96105 @Override
97106 public void sawOpcode(int seen) {
98 if (DEBUG)
107 if (DEBUG) {
99108 System.out.println("State: " + state + " Opcode: " + OPCODE_NAMES[seen]);
109 }
100110
101111 switch (state) {
102112 case SEEN_NOTHING:
103 if (seen == ICONST_0)
113 if (seen == ICONST_0) {
104114 state = SEEN_ICONST_0;
115 }
105116 break;
106117
107118 case SEEN_ICONST_0:
108119 if (seen == ANEWARRAY) {
109120 state = SEEN_ANEWARRAY;
110 } else
121 } else {
111122 state = SEEN_NOTHING;
123 }
112124 break;
113125
114126 case SEEN_ANEWARRAY:
115 if (((seen == INVOKEVIRTUAL) || (seen == INVOKEINTERFACE)) && (getNameConstantOperand().equals("toArray"))
116 && (getSigConstantOperand().equals("([Ljava/lang/Object;)[Ljava/lang/Object;"))) {
127 if (((seen == INVOKEVIRTUAL) || (seen == INVOKEINTERFACE)) && ("toArray".equals(getNameConstantOperand()))
128 && ("([Ljava/lang/Object;)[Ljava/lang/Object;".equals(getSigConstantOperand()))) {
117129 try {
118130 String clsName = getDottedClassConstantOperand();
119131 JavaClass cls = Repository.lookupClass(clsName);
120 if (cls.implementationOf(collectionClass))
132 if (cls.implementationOf(collectionClass)) {
121133 bugAccumulator.accumulateBug(
122134 new BugInstance(this, "ITA_INEFFICIENT_TO_ARRAY", LOW_PRIORITY).addClassAndMethod(this), this);
135 }
123136
124137 } catch (ClassNotFoundException cnfe) {
125138 bugReporter.reportMissingClass(cnfe);
135148 }
136149 }
137150
138 // vim:ts=4
4040
4141 public class InfiniteLoop extends OpcodeStackDetector {
4242
43 private static final boolean active = true;
43 // private static final boolean active = true;
4444
4545 ArrayList<BitSet> regModifiedAt = new ArrayList<BitSet>();
4646
4747 @Nonnull
4848 BitSet getModifiedBitSet(int reg) {
49 while (regModifiedAt.size() <= reg)
49 while (regModifiedAt.size() <= reg) {
5050 regModifiedAt.add(new BitSet());
51 }
5152 return regModifiedAt.get(reg);
5253 }
5354
5758 }
5859
5960 private void clearRegModified() {
60 for (BitSet b : regModifiedAt)
61 for (BitSet b : regModifiedAt) {
6162 b.clear();
63 }
6264 }
6365
6466 private boolean isRegModified(int reg, int firstPC, int lastPC) {
65 if (reg < 0)
67 if (reg < 0) {
6668 return false;
69 }
6770 BitSet b = getModifiedBitSet(reg);
6871 int modified = b.nextSetBit(firstPC);
6972 return (modified >= firstPC && modified <= lastPC);
8992
9093 @Override
9194 public boolean equals(Object o) {
92 if (o == null)
95 if (o == null) {
9396 return false;
94 if (this.getClass() != o.getClass())
97 }
98 if (this.getClass() != o.getClass()) {
9599 return false;
100 }
96101 Jump that = (Jump) o;
97102 return this.from == that.from && this.to == that.to;
98103 }
106111 BackwardsBranch(OpcodeStack stack, int from, int to) {
107112 super(from, to);
108113 numLastUpdates = stack.getNumLastUpdates();
109 for (int i = 0; i < numLastUpdates; i++)
110 if (stack.getLastUpdate(i) < to)
114 for (int i = 0; i < numLastUpdates; i++) {
115 if (stack.getLastUpdate(i) < to) {
111116 invariantRegisters.add(i);
117 }
118 }
112119 }
113120
114121 @Override
118125
119126 @Override
120127 public boolean equals(Object o) {
121 if (!super.equals(o))
128 if (!super.equals(o)) {
122129 return false;
130 }
123131 BackwardsBranch that = (BackwardsBranch) o;
124132 return this.invariantRegisters.equals(that.invariantRegisters) && this.numLastUpdates == that.numLastUpdates;
125133 }
141149
142150 @Override
143151 public boolean equals(Object o) {
144 if (!super.equals(o))
152 if (!super.equals(o)) {
145153 return false;
154 }
146155 ForwardConditionalBranch that = (ForwardConditionalBranch) o;
147 return this.item0.equals(that.item0) && this.item1.equals(that.item1);
156 return this.item0.sameValue(that.item0) && this.item1.sameValue(that.item1);
148157 }
149158
150159 }
160169 LinkedList<Jump> forwardJumps = new LinkedList<Jump>();
161170
162171 void purgeForwardJumps(int before) {
163 if (true)
172 if (true) {
164173 return;
174 /*
165175 for (Iterator<Jump> i = forwardJumps.iterator(); i.hasNext();) {
166176 Jump j = i.next();
167177 if (j.to < before)
168178 i.remove();
169179 }
180 */
181 }
170182 }
171183
172184 void addForwardJump(int from, int to) {
173 if (from >= to)
185 if (from >= to) {
174186 return;
187 }
175188 purgeForwardJumps(from);
176189 forwardJumps.add(new Jump(from, to));
177190 }
181194 int from2 = getBackwardsReach(from);
182195 assert from2 <= from;
183196 from = from2;
184 for (Jump f : forwardJumps)
185 if (f.from >= from && f.to > result)
197 for (Jump f : forwardJumps) {
198 if (f.from >= from && f.to > result) {
186199 result = f.to;
200 }
201 }
187202 return result;
188203 }
189204
193208
194209 @Override
195210 public void visit(Code obj) {
196 if (DEBUG)
211 if (DEBUG) {
197212 System.out.println(getFullyQualifiedMethodName());
213 }
198214 clearRegModified();
199215 backwardBranches.clear();
200216 forwardConditionalBranches.clear();
205221 LinkedList<ForwardConditionalBranch> myForwardBranches = new LinkedList<ForwardConditionalBranch>();
206222 int myBackwardsReach = getBackwardsReach(bb.to);
207223
208 for (ForwardConditionalBranch fcb : forwardConditionalBranches)
209 if (myBackwardsReach < fcb.from && fcb.from < bb.from && bb.from < fcb.to)
224 for (ForwardConditionalBranch fcb : forwardConditionalBranches) {
225 if (myBackwardsReach < fcb.from && fcb.from < bb.from && bb.from < fcb.to) {
210226 myForwardBranches.add(fcb);
211
212 if (myForwardBranches.size() != 1)
227 }
228 }
229
230 if (myForwardBranches.size() != 1) {
213231 continue;
232 }
214233 ForwardConditionalBranch fcb = myForwardBranches.get(0);
215 for (Jump fj : forwardJumps)
216 if (fcb.from != fj.from && myBackwardsReach < fj.from && fj.from < bb.from && bb.from < fj.to)
234 for (Jump fj : forwardJumps) {
235 if (fcb.from != fj.from && myBackwardsReach < fj.from && fj.from < bb.from && bb.from < fj.to) {
217236 continue backwardBranchLoop;
237 }
238 }
218239
219240 if (isConstant(fcb.item0, bb) && isConstant(fcb.item1, bb)) {
220241 SourceLineAnnotation loopBottom = SourceLineAnnotation.fromVisitedInstruction(getClassContext(), this, bb.from);
232253 constantSince(fcb.item0));
233254 int lastChangeLine = lastChange.getEndLine();
234255 if (loopBottomLine != -1 && lastChangeLine != -1 && loopTopLine != -1 && loopTopLine <= lastChangeLine
235 && lastChangeLine < loopBottomLine)
256 && lastChangeLine < loopBottomLine) {
236257 continue backwardBranchLoop;
258 }
237259 bug.add(LocalVariableAnnotation.getLocalVariableAnnotation(getMethod(), reg0, fcb.from, bb.from))
238 .addSourceLine(lastChange).describe(SourceLineAnnotation.DESCRIPTION_LAST_CHANGE);
260 .addSourceLine(lastChange).describe(SourceLineAnnotation.DESCRIPTION_LAST_CHANGE);
239261 }
240262 int reg1 = fcb.item1.getRegisterNumber();
241263 if (reg1 >= 0 && reg1 != reg0 && fcb.item1.getConstant() == null) {
243265 constantSince(fcb.item1));
244266 int lastChangeLine = lastChange.getEndLine();
245267 if (loopBottomLine != -1 && lastChangeLine != -1 && loopTopLine != -1 && loopTopLine <= lastChangeLine
246 && lastChangeLine < loopBottomLine)
268 && lastChangeLine < loopBottomLine) {
247269 continue backwardBranchLoop;
270 }
248271 bug.add(LocalVariableAnnotation.getLocalVariableAnnotation(getMethod(), reg1, fcb.from, bb.from))
249 .addSourceLine(lastChange).describe(SourceLineAnnotation.DESCRIPTION_LAST_CHANGE);
272 .addSourceLine(lastChange).describe(SourceLineAnnotation.DESCRIPTION_LAST_CHANGE);
250273 }
251274 boolean reg1Invariant = true;
252 if (reg1 >= 0)
275 if (reg1 >= 0) {
253276 reg1Invariant = !isRegModified(reg1, myBackwardsReach, bb.from);
254 if (reg0Invariant && reg1Invariant)
277 }
278 if (reg0Invariant && reg1Invariant) {
255279 bugReporter.reportBug(bug);
256 }
257
258 }
259 if (DEBUG)
280 }
281 }
282
283 }
284 if (DEBUG) {
260285 System.out.println();
261
262 }
263
264 /**
265 * @param item0
266 * @param invariantRegisters
267 * @return
268 */
286 }
287
288 }
289
269290 private boolean isConstant(Item item0, BackwardsBranch bb) {
270291
271292 int reg = item0.getRegisterNumber();
272 if (reg >= 0)
293 if (reg >= 0) {
273294 return bb.invariantRegisters.contains(reg) || reg >= bb.numLastUpdates;
274 if (item0.getConstant() != null)
295 }
296 if (item0.getConstant() != null) {
275297 return true;
298 }
276299 return false;
277300 }
278301
284307 static final boolean DEBUG = false;
285308 @Override
286309 public void sawOpcode(int seen) {
287 if (DEBUG)
310 if (DEBUG) {
288311 System.out.printf("%3d %-15s %s%n", getPC(), OPCODE_NAMES[seen], stack);
289 if (isRegisterStore())
312 }
313 if (isRegisterStore()) {
290314 regModifiedAt(getRegisterOperand(), getPC());
315 }
291316 switch (seen) {
292317 case GOTO:
293318 if (getBranchOffset() < 0) {
294319 BackwardsBranch bb = new BackwardsBranch(stack, getPC(), getBranchTarget());
295 if (bb.invariantRegisters.size() > 0)
320 if (bb.invariantRegisters.size() > 0) {
296321 backwardBranches.add(bb);
322 }
297323 addBackwardsReach();
324 /*
298325 if (false) {
299326 int target = getBranchTarget();
300327 if (getFurthestJump(target) > getPC())
305332 .addSourceLine(this, getPC());
306333 reportPossibleBug(bug);
307334 }
335 */
308336 }
309337
310338 break;
321349 case LOOKUPSWITCH:
322350 case TABLESWITCH: {
323351 OpcodeStack.Item item0 = stack.getStackItem(0);
324 if (getDefaultSwitchOffset() > 0)
352 if (getDefaultSwitchOffset() > 0) {
325353 forwardConditionalBranches.add(new ForwardConditionalBranch(item0, item0, getPC(), getPC()
326354 + getDefaultSwitchOffset()));
327 for (int offset : getSwitchOffsets())
328 if (offset > 0)
355 }
356 for (int offset : getSwitchOffsets()) {
357 if (offset > 0) {
329358 forwardConditionalBranches.add(new ForwardConditionalBranch(item0, item0, getPC(), getPC() + offset));
359 }
360 }
330361 break;
331362 }
332363 case IFNE:
344375 forwardConditionalBranches.add(new ForwardConditionalBranch(item0, item0, getPC(), target));
345376 break;
346377 }
347 if (getFurthestJump(target) > getPC())
378 if (getFurthestJump(target) > getPC()) {
348379 break;
380 }
349381
350382 if (constantSince(item0, target)) {
351383 int since0 = constantSince(item0);
352384 BugInstance bug = new BugInstance(this, "IL_INFINITE_LOOP", HIGH_PRIORITY).addClassAndMethod(this).addSourceLine(
353385 this, getPC());
354386 int reg0 = item0.getRegisterNumber();
355 if (reg0 >= 0)
387 if (reg0 >= 0) {
356388 bug.add(LocalVariableAnnotation.getLocalVariableAnnotation(getMethod(), reg0, getPC(), target))
357 .addSourceLine(this, since0);
358 if (reg0 < 0 || !isRegModified(reg0, target, getPC()))
389 .addSourceLine(this, since0);
390 }
391 if (reg0 < 0 || !isRegModified(reg0, target, getPC())) {
359392 reportPossibleBug(bug);
360
361 }
362 }
363 break;
393 }
394
395 }
396 }
397 break;
364398 case IF_ACMPEQ:
365399 case IF_ACMPNE:
366400 case IF_ICMPNE:
377411 forwardConditionalBranches.add(new ForwardConditionalBranch(item0, item1, getPC(), target));
378412 break;
379413 }
380 if (getFurthestJump(target) > getPC())
414 if (getFurthestJump(target) > getPC()) {
381415 break;
416 }
382417
383418 if (constantSince(item0, target) && constantSince(item1, target)) {
384419 // int since0 = constantSince(item0);
386421 BugInstance bug = new BugInstance(this, "IL_INFINITE_LOOP", HIGH_PRIORITY).addClassAndMethod(this).addSourceLine(
387422 this, getPC());
388423 int reg0 = item0.getRegisterNumber();
389 if (reg0 >= 0)
424 if (reg0 >= 0) {
390425 bug.add(LocalVariableAnnotation.getLocalVariableAnnotation(getMethod(), reg0, getPC(), target));
426 }
391427 int reg1 = item1.getRegisterNumber();
392 if (reg1 >= 0)
428 if (reg1 >= 0) {
393429 bug.add(LocalVariableAnnotation.getLocalVariableAnnotation(getMethod(), reg1, getPC(), target));
430 }
394431
395432 reportPossibleBug(bug);
396433 }
397434
398435 }
436 break;
437 default:
399438 break;
400439 }
401440
405444 *
406445 */
407446 private void addBackwardsReach() {
408 if (getBranchOffset() >= 0)
447 if (getBranchOffset() >= 0) {
409448 return;
449 }
410450 int target = getBranchTarget();
411 for (Jump j : backwardReach)
412 if (j.to < target && target <= j.from)
451 for (Jump j : backwardReach) {
452 if (j.to < target && target <= j.from) {
413453 target = j.to;
454 }
455 }
414456 assert target <= getBranchTarget();
415457 assert target < getPC();
416458 for (Iterator<Jump> i = backwardReach.iterator(); i.hasNext();) {
417459 Jump j = i.next();
418 if (target <= j.to && getPC() >= j.from)
460 if (target <= j.to && getPC() >= j.from) {
419461 i.remove();
462 }
420463 }
421464 backwardReach.add(new Jump(getPC(), target));
422465 }
423466
424467 private int getBackwardsReach(int target) {
425468 int originalTarget = target;
426 for (Jump j : backwardReach)
427 if (j.to < target && target <= j.from)
469 for (Jump j : backwardReach) {
470 if (j.to < target && target <= j.from) {
428471 target = j.to;
472 }
473 }
429474 assert target <= originalTarget;
430475 return target;
431476 }
432477
433 /**
434 * @param item1
435 * @param branchTarget
436 * @return
437 */
438478 private boolean constantSince(Item item1, int branchTarget) {
439479 int reg = item1.getRegisterNumber();
440 if (reg >= 0)
480 if (reg >= 0) {
441481 return stack.getLastUpdate(reg) < getBackwardsReach(branchTarget);
442 if (item1.getConstant() != null)
482 }
483 if (item1.getConstant() != null) {
443484 return true;
485 }
444486 return false;
445487 }
446488
447489 private int constantSince(Item item1) {
448490 int reg = item1.getRegisterNumber();
449 if (reg >= 0)
491 if (reg >= 0) {
450492 return stack.getLastUpdate(reg);
493 }
451494 return Integer.MAX_VALUE;
452495
453496 }
454497
455498 void reportPossibleBug(BugInstance bug) {
456499 int catchSize = Util.getSizeOfSurroundingTryBlock(getConstantPool(), getCode(), "java/io/EOFException", getPC());
457 if (catchSize < Integer.MAX_VALUE)
500 if (catchSize < Integer.MAX_VALUE) {
458501 bug.lowerPriorityALot();
459 else {
502 } else {
460503 catchSize = Util.getSizeOfSurroundingTryBlock(getConstantPool(), getCode(), "java/lang/NoSuchElementException",
461504 getPC());
462 if (catchSize < Integer.MAX_VALUE)
505 if (catchSize < Integer.MAX_VALUE) {
463506 bug.lowerPriorityALot();
464 else {
507 } else {
465508 LocalVariableAnnotation lv = bug.getPrimaryLocalVariableAnnotation();
466 if (lv == null && getMethodName().equals("run"))
509 if (lv == null && "run".equals(getMethodName())) {
467510 bug.lowerPriority();
511 }
468512 }
469513 }
470514 bugReporter.reportBug(bug);
3232
3333 public class InfiniteRecursiveLoop extends OpcodeStackDetector implements StatelessDetector {
3434
35 private BugReporter bugReporter;
35 private final BugReporter bugReporter;
3636
3737 private boolean seenTransferOfControl;
3838
3939 private boolean seenReturn;
4040
41 private boolean seenThrow;
41 // private boolean seenThrow;
4242
4343 private boolean seenStateChange;
4444
5555 seenTransferOfControl = false;
5656 seenStateChange = false;
5757 seenReturn = false;
58 seenThrow = false;
58 // seenThrow = false;
5959 largestBranchTarget = -1;
6060
6161 if (DEBUG) {
6767
6868 @Override
6969 public void sawBranchTo(int target) {
70 if (target == getNextPC())
70 if (target == getNextPC()) {
7171 return;
72 if (largestBranchTarget < target)
72 }
73 if (largestBranchTarget < target) {
7374 largestBranchTarget = target;
75 }
7476 seenTransferOfControl = true;
7577 }
7678
8183 */
8284 @Override
8385 public void sawOpcode(int seen) {
84 if (seenReturn && seenTransferOfControl && seenStateChange)
86 if (seenReturn && seenTransferOfControl && seenStateChange) {
8587 return;
88 }
8689
8790 if (DEBUG) {
8891 System.out.println(stack);
8992 System.out.println(getPC() + " : " + OPCODE_NAMES[seen]);
9093 }
9194
92 if ((seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE) && getNameConstantOperand().equals("add")
93 && getSigConstantOperand().equals("(Ljava/lang/Object;)Z") && stack.getStackDepth() >= 2) {
95 if ((seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE) && "add".equals(getNameConstantOperand())
96 && "(Ljava/lang/Object;)Z".equals(getSigConstantOperand()) && stack.getStackDepth() >= 2) {
9497 OpcodeStack.Item it0 = stack.getStackItem(0);
9598 int r0 = it0.getRegisterNumber();
9699 OpcodeStack.Item it1 = stack.getStackItem(1);
97100 int r1 = it1.getRegisterNumber();
98 if (r0 == r1 && r0 > 0)
101 if (r0 == r1 && r0 > 0) {
99102 bugReporter.reportBug(new BugInstance(this, "IL_CONTAINER_ADDED_TO_ITSELF", NORMAL_PRIORITY).addClassAndMethod(
100103 this).addSourceLine(this));
104 }
101105 }
102106
103107 if ((seen == INVOKEVIRTUAL || seen == INVOKESPECIAL || seen == INVOKEINTERFACE || seen == INVOKESTATIC)
104108 && getNameConstantOperand().equals(getMethodName())
105109 && getSigConstantOperand().equals(getMethodSig())
106110 && (seen == INVOKESTATIC) == getMethod().isStatic()
107 && (seen == INVOKESPECIAL) == (getMethod().isPrivate() && !getMethod().isStatic() || getMethodName().equals(
108 "<init>"))) {
111 && (seen == INVOKESPECIAL) == (getMethod().isPrivate() && !getMethod().isStatic() || "<init>".equals(getMethodName()))) {
109112 Type arguments[] = getMethod().getArgumentTypes();
110113 // stack.getStackDepth() >= parameters
111114 int parameters = arguments.length;
112 if (!getMethod().isStatic())
115 if (!getMethod().isStatic()) {
113116 parameters++;
117 }
114118 XMethod xMethod = XFactory.createReferencedXMethod(this);
115119 if (DEBUG) {
116120 System.out.println("IL: Checking...");
122126 // Invocation of same method
123127 // Now need to see if parameters are the same
124128 int firstParameter = 0;
125 if (getMethodName().equals("<init>"))
129 if ("<init>".equals(getMethodName())) {
126130 firstParameter = 1;
131 }
127132
128133 // match1 should be true if it is any call to the exact same
129134 // method
136141 boolean match1 = !seenStateChange;
137142 for (int i = firstParameter; match1 && i < parameters; i++) {
138143 OpcodeStack.Item it = stack.getStackItem(parameters - 1 - i);
139 if (!it.isInitialParameter() || it.getRegisterNumber() != i)
144 if (!it.isInitialParameter() || it.getRegisterNumber() != i) {
140145 match1 = false;
141 }
142
143 boolean sameMethod = seen == INVOKESTATIC || getNameConstantOperand().equals("<init>");
146 }
147 }
148
149 boolean sameMethod = seen == INVOKESTATIC || "<init>".equals(getNameConstantOperand());
144150 if (!sameMethod) {
145151 // Have to check if first parmeter is the same
146152 // know there must be a this argument
147 if (DEBUG)
153 if (DEBUG) {
148154 System.out.println("Stack is " + stack);
155 }
149156 OpcodeStack.Item p = stack.getStackItem(parameters - 1);
150 if (DEBUG)
157 if (DEBUG) {
151158 System.out.println("parameters = " + parameters + ", Item is " + p);
159 }
152160 String sig = p.getSignature();
153161 sameMethod = p.isInitialParameter() && p.getRegisterNumber() == 0 && sig.equals("L" + getClassName() + ";");
154162
167175 boolean match2 = sameMethod && !seenTransferOfControl;
168176 boolean match3 = sameMethod && !seenReturn && largestBranchTarget < getPC();
169177 if (match1 || match2 || match3) {
170 if (DEBUG)
178 if (DEBUG) {
171179 System.out.println("IL: " + sameMethod + " " + match1 + " " + match2 + " " + match3);
172 int priority = HIGH_PRIORITY;
173 if (!match1 && !match2 && seenThrow)
174 priority = NORMAL_PRIORITY;
175 if (seen == INVOKEINTERFACE)
176 priority = NORMAL_PRIORITY;
180 }
181 // int priority = HIGH_PRIORITY;
182 // if (!match1 && !match2 && seenThrow)
183 // priority = NORMAL_PRIORITY;
184 // if (seen == INVOKEINTERFACE)
185 // priority = NORMAL_PRIORITY;
177186 bugReporter.reportBug(new BugInstance(this, "IL_INFINITE_RECURSIVE_LOOP", HIGH_PRIORITY).addClassAndMethod(
178187 this).addSourceLine(this));
179188 }
191200 seenTransferOfControl = true;
192201 break;
193202 case ATHROW:
194 seenThrow = true;
203 // seenThrow = true;
195204 seenTransferOfControl = true;
196205 break;
197206 case PUTSTATIC:
210219 case INVOKESPECIAL:
211220 case INVOKEINTERFACE:
212221 case INVOKESTATIC:
213 if (getNameConstantOperand().equals("print") || getNameConstantOperand().equals("println")
214 || getNameConstantOperand().equals("log") || getNameConstantOperand().equals("toString"))
222 if ("print".equals(getNameConstantOperand()) || "println".equals(getNameConstantOperand())
223 || "log".equals(getNameConstantOperand()) || "toString".equals(getNameConstantOperand())) {
215224 break;
225 }
216226 seenStateChange = true;
217227 break;
228 default:
229 break;
218230 }
219231 }
220232
3636
3737 public class InheritanceUnsafeGetResource extends BytecodeScanningDetector implements StatelessDetector {
3838
39 private BugReporter bugReporter;
39 private final BugReporter bugReporter;
4040
4141 private boolean classIsFinal;
4242
7676
7777 @Override
7878 public void sawOpcode(int seen) {
79 if (reportedForThisClass)
79 if (reportedForThisClass) {
8080 return;
81 }
8182
8283 switch (seen) {
8384 case LDC:
8485 Constant constantValue = getConstantRefOperand();
85 if (constantValue instanceof ConstantClass)
86 if (constantValue instanceof ConstantClass) {
8687 sawGetClass = -100;
87 else if (constantValue instanceof ConstantString) {
88 } else if (constantValue instanceof ConstantString) {
8889 stringConstant = ((ConstantString) constantValue).getBytes(getConstantPool());
8990 }
9091 break;
9394 state = 1;
9495 break;
9596 case INVOKEVIRTUAL:
96 if (getClassConstantOperand().equals("java/lang/Class")
97 && (getNameConstantOperand().equals("getResource") || getNameConstantOperand().equals("getResourceAsStream"))
97 if ("java/lang/Class".equals(getClassConstantOperand())
98 && ("getResource".equals(getNameConstantOperand()) || "getResourceAsStream".equals(getNameConstantOperand()))
9899 && sawGetClass + 10 >= getPC()) {
99100 int priority = NORMAL_PRIORITY;
100 if (prevOpcode == LDC && stringConstant != null && stringConstant.length() > 0 && stringConstant.charAt(0) == '/')
101 if (prevOpcode == LDC && stringConstant != null && stringConstant.length() > 0 && stringConstant.charAt(0) == '/') {
101102 priority = LOW_PRIORITY;
102 else {
103 } else {
103104 priority = adjustPriority(priority);
104105 }
105106 bugReporter.reportBug(new BugInstance(this, "UI_INHERITANCE_UNSAFE_GETRESOURCE", priority)
106 .addClassAndMethod(this).addSourceLine(this));
107 .addClassAndMethod(this).addSourceLine(this));
107108 reportedForThisClass = true;
108109
109110 } else if (state == 1 && !methodIsStatic && !classIsFinal && classIsVisibleToOtherPackages
110 && getNameConstantOperand().equals("getClass") && getSigConstantOperand().equals("()Ljava/lang/Class;")) {
111 && "getClass".equals(getNameConstantOperand()) && "()Ljava/lang/Class;".equals(getSigConstantOperand())) {
111112 sawGetClass = getPC();
112113 }
113114 state = 0;
116117 state = 0;
117118 break;
118119 }
119 if (seen != LDC)
120 if (seen != LDC) {
120121 stringConstant = null;
122 }
121123 prevOpcode = seen;
122124
123125 }
124126
125127 /**
126128 * Adjust the priority of a warning about to be reported.
127 *
129 *
128130 * @param priority
129131 * initial priority
130132 * @return adjusted priority
2323 import java.util.HashSet;
2424 import java.util.List;
2525 import java.util.Map;
26 import java.util.Map.Entry;
2627 import java.util.Set;
2728 import java.util.TreeMap;
2829 import java.util.TreeSet;
4546
4647 Map<String, Set<String>> classRequires = new TreeMap<String, Set<String>>();
4748
48
49
50 private BugReporter bugReporter;
51
52 private Map<XMethod, Set<XField>> staticFieldsRead = new HashMap<XMethod, Set<XField>>();
53 private Set<XField> staticFieldsReadInAnyConstructor = new HashSet<XField>();
49
50
51 private final BugReporter bugReporter;
52
53 private final Map<XMethod, Set<XField>> staticFieldsRead = new HashMap<XMethod, Set<XField>>();
54 private final Set<XField> staticFieldsReadInAnyConstructor = new HashSet<XField>();
5455 private Set<XField> fieldsReadInThisConstructor = new HashSet<XField>();
5556
56 private Set<XMethod> constructorsInvokedInStaticInitializer = new HashSet<XMethod>();
57 private List<InvocationInfo> invocationInfo = new ArrayList<InvocationInfo>();
58 private Set<XField> warningGiven = new HashSet<XField>();
57 private final Set<XMethod> constructorsInvokedInStaticInitializer = new HashSet<XMethod>();
58 private final List<InvocationInfo> invocationInfo = new ArrayList<InvocationInfo>();
59 private final Set<XField> warningGiven = new HashSet<XField>();
5960
6061 private InvocationInfo lastInvocation;
61
62
6263 static class InvocationInfo {
6364 public InvocationInfo(XMethod constructor, int pc) {
6465 this.constructor = constructor;
8182 Method staticInitializer = null;
8283 for(Method m : obj.getMethods()) {
8384 String name = m.getName();
84 if (name.equals("<clinit>"))
85 if ("<clinit>".equals(name)) {
8586 staticInitializer = m;
86 else if (name.equals("<init>"))
87 } else if ("<init>".equals(name)) {
8788 visitOrder.add(m);
88
89 }
90 if (staticInitializer != null)
89 }
90
91 }
92 if (staticInitializer != null) {
9193 visitOrder.add(staticInitializer);
94 }
9295 return visitOrder;
9396 }
94
95
97
98
9699 @Override
97100 public void visit(Code obj) {
98101 fieldsReadInThisConstructor = new HashSet<XField>();
99102 super.visit(obj);
100103 staticFieldsRead.put(getXMethod(), fieldsReadInThisConstructor);
101104 requires.remove(getDottedClassName());
102 if (getDottedClassName().equals("java.lang.System")) {
105 if ("java.lang.System".equals(getDottedClassName())) {
103106 requires.add("java.io.FileInputStream");
104107 requires.add("java.io.FileOutputStream");
105108 requires.add("java.io.BufferedInputStream");
114117
115118 @Override
116119 public void visitAfter(JavaClass obj) {
117
120
118121 staticFieldsRead.clear();
119
122
120123 staticFieldsReadInAnyConstructor.clear();
121124 fieldsReadInThisConstructor.clear();
122125
130133 public void sawOpcode(int seen) {
131134 InvocationInfo prev = lastInvocation;
132135 lastInvocation = null;
133 if (getMethodName().equals("<init>")) {
136 if ("<init>".equals(getMethodName())) {
134137 if (seen == GETSTATIC && getClassConstantOperand().equals(getClassName())) {
135138 staticFieldsReadInAnyConstructor.add(getXFieldOperand());
136139 fieldsReadInThisConstructor.add(getXFieldOperand());
138141 return;
139142 }
140143
141 if (seen == INVOKESPECIAL && getNameConstantOperand().equals("<init>") && getClassConstantOperand().equals(getClassName())) {
142
144 if (seen == INVOKESPECIAL && "<init>".equals(getNameConstantOperand()) && getClassConstantOperand().equals(getClassName())) {
145
143146 XMethod m = getXMethodOperand();
144147 Set<XField> read = staticFieldsRead.get(m);
145148 if (constructorsInvokedInStaticInitializer.add(m) && read != null && !read.isEmpty()) {
146149 lastInvocation = new InvocationInfo(m, getPC());
147150 invocationInfo.add(lastInvocation);
148
151
152 }
153
154 }
155 if (seen == PUTSTATIC && getClassConstantOperand().equals(getClassName())) {
156 XField f = getXFieldOperand();
157 if (prev != null) {
158 prev.field = f;
159 }
160 if (staticFieldsReadInAnyConstructor.contains(f) && !warningGiven.contains(f)) {
161 for(InvocationInfo i : invocationInfo) {
162 Set<XField> fields = staticFieldsRead.get(i.constructor);
163 if (fields != null && fields.contains(f)) {
164 warningGiven.add(f);
165 BugInstance bug = new BugInstance(this, "SI_INSTANCE_BEFORE_FINALS_ASSIGNED", NORMAL_PRIORITY).addClassAndMethod(this);
166 if (i.field != null) {
167 bug.addField(i.field).describe(FieldAnnotation.STORED_ROLE);
168 }
169 bug.addMethod(i.constructor).describe(MethodAnnotation.METHOD_CONSTRUCTOR);
170 bug.addReferencedField(this).describe(FieldAnnotation.VALUE_OF_ROLE).addSourceLine(this, i.pc);
171 bugReporter.reportBug(bug);
172 break;
173
174 }
149175 }
150
151 }
152 if (seen == PUTSTATIC && getClassConstantOperand().equals(getClassName())) {
153 XField f = getXFieldOperand();
154 if (prev != null)
155 prev.field = f;
156 if (staticFieldsReadInAnyConstructor.contains(f) && !warningGiven.contains(f)) {
157 for(InvocationInfo i : invocationInfo) {
158 Set<XField> fields = staticFieldsRead.get(i.constructor);
159 if (fields != null && fields.contains(f)) {
160 warningGiven.add(f);
161 BugInstance bug = new BugInstance(this, "SI_INSTANCE_BEFORE_FINALS_ASSIGNED", NORMAL_PRIORITY).addClassAndMethod(this);
162 if (i.field != null) {
163 bug.addField(i.field).describe(FieldAnnotation.STORED_ROLE);
164 }
165 bug.addMethod(i.constructor).describe(MethodAnnotation.METHOD_CONSTRUCTOR);
166 bug.addReferencedField(this).describe(FieldAnnotation.VALUE_OF_ROLE).addSourceLine(this, i.pc);
167 bugReporter.reportBug(bug);
168 break;
169
170 }
171 }
172 }
173
174 } else if (seen == PUTSTATIC || seen == GETSTATIC || seen == INVOKESTATIC || seen == NEW)
175 if (getPC() + 6 < codeBytes.length)
176 }
177
178 } else if (seen == PUTSTATIC || seen == GETSTATIC || seen == INVOKESTATIC || seen == NEW) {
179 if (getPC() + 6 < codeBytes.length) {
176180 requires.add(getDottedClassConstantOperand());
181 }
182 }
177183 }
178184
179185 public void compute() {
183189 Set<String> needs = classRequires.get(c);
184190 needs.retainAll(allClasses);
185191 Set<String> extra = new TreeSet<String>();
186 for (String need : needs)
192 for (String need : needs) {
187193 extra.addAll(classRequires.get(need));
194 }
188195 needs.addAll(extra);
189196 needs.retainAll(allClasses);
190197 classRequires.put(c, needs);
191 if (needs.isEmpty())
198 if (needs.isEmpty()) {
192199 emptyClasses.add(c);
200 }
193201 }
194202 for (String c : emptyClasses) {
195203 classRequires.remove(c);
199207 @Override
200208 public void report() {
201209
202 if (DEBUG)
210 if (DEBUG) {
203211 System.out.println("Finishing computation");
204 compute();
205 compute();
206 compute();
207 compute();
208 compute();
209 compute();
210 compute();
211 compute();
212 Set<String> allClasses = classRequires.keySet();
213
214 for (String c : allClasses) {
215 if (DEBUG)
212 }
213 compute();
214 compute();
215 compute();
216 compute();
217 compute();
218 compute();
219 compute();
220 compute();
221
222 for (Entry<String, Set<String>> entry : classRequires.entrySet()) {
223 String c = entry.getKey();
224 if (DEBUG) {
216225 System.out.println("Class " + c + " requires:");
217 for (String needs : (classRequires.get(c))) {
218 if (DEBUG)
226 }
227 for (String needs : entry.getValue()) {
228 if (DEBUG) {
219229 System.out.println(" " + needs);
230 }
220231 Set<String> s = classRequires.get(needs);
221 if (s != null && s.contains(c) && c.compareTo(needs) < 0)
232 if (s != null && s.contains(c) && c.compareTo(needs) < 0) {
222233 bugReporter.reportBug(new BugInstance(this, "IC_INIT_CIRCULARITY", NORMAL_PRIORITY).addClass(c).addClass(
223234 needs));
235 }
224236 }
225237 }
226238 }
6868 super.visit(obj);
6969 XField f = XFactory.createXField(this);
7070 if (checkForInitialization(f) && !f.isSynthetic()) {
71 if (f.isStatic())
71 if (f.isStatic()) {
7272 nonnullStaticFields.add(f);
73 else
73 } else {
7474 nonnullFields.add(f);
75 }
7576 }
7677 }
7778
78 /**
79 * @param f
80 * @return
81 */
8279 public boolean checkForInitialization(XField f) {
83 if (!f.isReferenceType() || f.isFinal())
80 if (!f.isReferenceType() || f.isFinal()) {
8481 return false;
82 }
8583 NullnessAnnotation annotation = AnalysisContext.currentAnalysisContext().getNullnessAnnotationDatabase()
8684 .getResolvedAnnotation(f, false);
8785 boolean isNonnull = annotation == NullnessAnnotation.NONNULL;
9088
9189 @Override
9290 public void visit(Code code) {
93 boolean interesting = getMethodName().equals("<init>") || getMethodName().equals("<clinit>");
94 if (!interesting)
91 boolean interesting = "<init>".equals(getMethodName()) || "<clinit>".equals(getMethodName());
92 if (!interesting) {
9593 return;
94 }
9695
9796 secondaryConstructor = false;
9897 HashSet<XField> needToInitialize = getMethod().isStatic() ? nonnullStaticFields : nonnullFields;
99 if (needToInitialize.isEmpty())
98 if (needToInitialize.isEmpty()) {
10099 return;
100 }
101101 // initialize any variables we want to initialize for the method
102102 super.visit(code); // make callbacks to sawOpcode for all opcodes
103103 if (!secondaryConstructor && !initializedFields.containsAll(needToInitialize)) {
104104 int priority = Priorities.NORMAL_PRIORITY;
105 if (needToInitialize.size() - initializedFields.size() == 1 && needToInitialize.size() > 1)
105 if (needToInitialize.size() - initializedFields.size() == 1 && needToInitialize.size() > 1) {
106106 priority = Priorities.HIGH_PRIORITY;
107 }
107108
108109 for (XField f : needToInitialize) {
109 if (initializedFields.contains(f))
110 if (initializedFields.contains(f)) {
110111 continue;
112 }
111113
112114 BugInstance b = new BugInstance(this, "NP_NONNULL_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", priority)
113 .addClassAndMethod(this).addField(f);
115 .addClassAndMethod(this).addField(f);
114116 bugReporter.reportBug(b);
115117 }
116118
124126 @Override
125127 public void sawOpcode(int seen) {
126128
127 if (secondaryConstructor)
129 if (secondaryConstructor) {
128130 return;
131 }
129132
130133 switch (seen) {
131134 case Constants.INVOKESPECIAL:
132 if (!getMethod().isStatic() && getNameConstantOperand().equals("<init>") && isSelfOperation()) {
135 if (!getMethod().isStatic() && "<init>".equals(getNameConstantOperand()) && isSelfOperation()) {
133136 OpcodeStack.Item invokedOn = stack.getItemMethodInvokedOn(this);
134137 if (invokedOn.isInitialParameter() && invokedOn.getRegisterNumber() == 0) {
135138 secondaryConstructor = true;
138141 }
139142 break;
140143 case Constants.PUTFIELD:
141 if (getMethod().isStatic())
144 if (getMethod().isStatic()) {
142145 return;
146 }
143147 OpcodeStack.Item left = stack.getStackItem(1);
144148 if (left.isInitialParameter() && left.getRegisterNumber() == 0 && isSelfOperation()) {
145149 XField f = getXFieldOperand();
146 if (f == null)
150 if (f == null) {
147151 break;
148 if (checkForInitialization(f))
152 }
153 if (checkForInitialization(f)) {
149154 initializedFields.add(f);
155 }
150156 }
151157 break;
152158 case Constants.PUTSTATIC:
153 if (!getMethod().isStatic())
159 if (!getMethod().isStatic()) {
154160 break;
161 }
155162
156163 if (isSelfOperation()) {
157164 XField f = getXFieldOperand();
158 if (f == null)
165 if (f == null) {
159166 break;
167 }
160168
161 if (checkForInitialization(f))
169 if (checkForInitialization(f)) {
162170 initializedFields.add(f);
171 }
163172 }
164173 break;
165174 default:
2929
3030 /**
3131 * StreamFactory for stream objects loaded from instance fields.
32 *
32 *
3333 * @author David Hovemeyer
3434 */
3535 public class InstanceFieldLoadStreamFactory implements StreamFactory {
36 private String streamBaseClass;
36 private final String streamBaseClass;
3737
3838 private String bugPatternType;
3939
4141 * Constructor. By default, Streams created by this factory will not be
4242 * marked as interesting. The setBugPatternType() method should be called to
4343 * make the factory produce interesting streams.
44 *
44 *
4545 * @param streamBaseClass
4646 * the base class of the streams produced by the factory
4747 */
5252 /**
5353 * Set the bug pattern type reported for unclosed streams loaded from this
5454 * field. This makes the created streams "interesting".
55 *
55 *
5656 * @param bugPatternType
5757 * the bug pattern type
5858 */
6161 return this;
6262 }
6363
64 @Override
6465 public Stream createStream(Location location, ObjectType type, ConstantPoolGen cpg,
6566 RepositoryLookupFailureCallback lookupFailureCallback) {
6667
6768 Instruction ins = location.getHandle().getInstruction();
68 if (ins.getOpcode() != Constants.GETFIELD)
69 if (ins.getOpcode() != Constants.GETFIELD) {
6970 return null;
71 }
7072
7173 String fieldClass = type.getClassName();
7274 try {
73 if (fieldClass.startsWith("["))
75 if (fieldClass.startsWith("[")) {
7476 return null;
75 if (!Hierarchy.isSubtype(fieldClass, streamBaseClass))
77 }
78 if (!Hierarchy.isSubtype(fieldClass, streamBaseClass)) {
7679 return null;
80 }
7781
7882 Stream stream = new Stream(location, fieldClass, streamBaseClass);
7983 stream.setIsOpenOnCreation(true);
8084 stream.setOpenLocation(location);
81 if (bugPatternType != null)
85 if (bugPatternType != null) {
8286 stream.setInteresting(bugPatternType);
87 }
8388
8489 // System.out.println("Instance field stream at " + location);
8590 return stream;
9095 }
9196 }
9297
93 // vim:ts=4
3030 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
3131
3232 public class InstantiateStaticClass extends BytecodeScanningDetector {
33 private BugReporter bugReporter;
33 private final BugReporter bugReporter;
3434
3535 public InstantiateStaticClass(BugReporter bugReporter) {
3636 this.bugReporter = bugReporter;
3939 @Override
4040 public void sawOpcode(int seen) {
4141
42 if ((seen == INVOKESPECIAL) && getNameConstantOperand().equals("<init>") && getSigConstantOperand().equals("()V")) {
43 XClass xClass = getXClassOperand();
44 if (xClass == null)
45 return;
46 String clsName = getClassConstantOperand();
47 if (clsName.equals("java/lang/Object"))
48 return;
42 if ((seen == INVOKESPECIAL) && "<init>".equals(getNameConstantOperand()) && "()V".equals(getSigConstantOperand())) {
43 XClass xClass = getXClassOperand();
44 if (xClass == null) {
45 return;
46 }
47 String clsName = getClassConstantOperand();
48 if ("java/lang/Object".equals(clsName)) {
49 return;
50 }
4951
50 // ignore superclass synthesized ctor calls
51 if (getMethodName().equals("<init>") && (getPC() == 1))
52 return;
52 // ignore superclass synthesized ctor calls
53 if ("<init>".equals(getMethodName()) && (getPC() == 1)) {
54 return;
55 }
5356
54 // ignore the typesafe enumerated constant pattern
55 if (getMethodName().equals("<clinit>") && (getClassName().equals(clsName)))
56 return;
57 // ignore the typesafe enumerated constant pattern
58 if ("<clinit>".equals(getMethodName()) && (getClassName().equals(clsName))) {
59 return;
60 }
5761
58 if (isStaticOnlyClass(xClass))
59 bugReporter.reportBug(new BugInstance(this, "ISC_INSTANTIATE_STATIC_CLASS", LOW_PRIORITY).addClassAndMethod(
60 this).addSourceLine(this));
62 if (isStaticOnlyClass(xClass)) {
63 bugReporter.reportBug(new BugInstance(this, "ISC_INSTANTIATE_STATIC_CLASS", LOW_PRIORITY).addClassAndMethod(
64 this).addSourceLine(this));
6165 }
62
66 }
67
6368 }
6469
6570 private boolean isStaticOnlyClass(XClass xClass) {
6671
67 if (xClass.getInterfaceDescriptorList().length > 0)
72 if (xClass.getInterfaceDescriptorList().length > 0) {
6873 return false;
74 }
6975 ClassDescriptor superclassDescriptor = xClass.getSuperclassDescriptor();
70 if (superclassDescriptor == null)
76 if (superclassDescriptor == null) {
7177 return false;
78 }
7279 String superClassName = superclassDescriptor.getClassName();
73 if (!superClassName.equals("java/lang/Object"))
80 if (!"java/lang/Object".equals(superClassName)) {
7481 return false;
82 }
7583 int staticCount = 0;
7684
7785 List<? extends XMethod> methods = xClass.getXMethods();
7886 for (XMethod m : methods) {
79 if (m.isStatic()) {
87 // !m.isSynthetic(): bug #1282: No warning should be generated if only static methods are synthetic
88 if (m.isStatic() && !m.isSynthetic()) {
8089 staticCount++;
81 } else if (!m.getName().equals("<init>") || !m.getSignature().equals("()V"))
90 } else if (!"<init>".equals(m.getName()) || !"()V".equals(m.getSignature())) {
8291 return false;
92 }
8393 }
8494
8595 List<? extends XField> fields = xClass.getXFields();
8696 for (XField f : fields) {
8797 if (f.isStatic()) {
8898 staticCount++;
89 } else if (!f.isPrivate())
99 } else if (!f.isPrivate()) {
90100 return false;
101 }
91102 }
92103
93 if (staticCount == 0)
104 if (staticCount == 0) {
94105 return false;
106 }
95107 return true;
96108
97109 }
8080
8181 /*
8282 * (non-Javadoc)
83 *
83 *
8484 * @see edu.umd.cs.findbugs.Detector#report()
8585 */
8686 @Override
4242
4343 private static final int SEEN_ALOAD_0 = 1;
4444
45 private BugReporter bugReporter;
45 private final BugReporter bugReporter;
4646
4747 private int state;
4848
5454
5555 @Override
5656 public void visitClassContext(ClassContext classContext) {
57 if (!enabled())
57 if (!enabled()) {
5858 return;
59 }
5960
6061 JavaClass jClass = classContext.getJavaClass();
6162 XClass xClass = classContext.getXClass();
6263
6364 try {
6465
65 if (!isJunit3TestCase(xClass))
66 if (!isJunit3TestCase(xClass)) {
6667 return;
68 }
6769 if ((jClass.getAccessFlags() & ACC_ABSTRACT) == 0) {
6870 if (!hasTestMethods(jClass)) {
6971 bugReporter.reportBug(new BugInstance(this, "IJU_NO_TESTS", LOW_PRIORITY).addClass(jClass));
7981
8082 private boolean isJunit3TestCase(XClass jClass) throws ClassNotFoundException {
8183 ClassDescriptor sDesc = jClass.getSuperclassDescriptor();
82 if (sDesc == null)
84 if (sDesc == null) {
8385 return false;
86 }
8487 String sName = sDesc.getClassName();
85 if (sName.equals("junit/framework/TestCase"))
88 if (sName.equals("junit/framework/TestCase")) {
8689 return true;
87 if (sName.equals("java/lang/Object"))
90 }
91 if (sName.equals("java/lang/Object")) {
8892 return false;
93 }
8994
9095 try {
9196 XClass sClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, sDesc);
92 if (sClass == null)
97 if (sClass == null) {
9398 return false;
99 }
94100 return isJunit3TestCase(sClass);
95101 } catch (CheckedAnalysisException e) {
96102 return false;
102108 boolean foundTest = false;
103109 Method[] methods = jClass.getMethods();
104110 for (Method m : methods) {
105 if (m.isPublic() && m.getName().startsWith("test") && m.getSignature().equals("()V"))
111 if (m.isPublic() && m.getName().startsWith("test") && m.getSignature().equals("()V")) {
106112 return true;
107 if (m.getName().startsWith("runTest") && m.getSignature().endsWith("()V"))
113 }
114 if (m.getName().startsWith("runTest") && m.getSignature().endsWith("()V")) {
108115 return true;
109 }
110 if (hasSuite(methods))
116 }
117 }
118 if (hasSuite(methods)) {
111119 return true;
120 }
112121
113122 try {
114123 JavaClass sClass = jClass.getSuperClass();
115 if (sClass != null)
124 if (sClass != null) {
116125 return hasTestMethods(sClass);
126 }
117127 } catch (ClassNotFoundException e) {
118128 AnalysisContext.reportMissingClass(e);
119129 }
125135 private boolean hasSuite(Method[] methods) {
126136 for (Method m : methods) {
127137 if (m.getName().equals("suite") && m.isPublic() && m.isStatic()
128 // && m.getReturnType().equals(junit.framework.Test.class)
129 // && m.getArgumentTypes().length == 0
130 && m.getSignature().equals("()Ljunit/framework/Test;"))
138 // && m.getReturnType().equals(junit.framework.Test.class)
139 // && m.getArgumentTypes().length == 0
140 && m.getSignature().equals("()Ljunit/framework/Test;")) {
131141 return true;
142 }
132143 }
133144 return false;
134145 }
137148 * Check whether or not this detector should be enabled. The detector is
138149 * disabled if the TestCase class cannot be found (meaning we don't have
139150 * junit.jar on the aux classpath).
140 *
151 *
141152 * @return true if it should be enabled, false if not
142153 */
143154 private boolean enabled() {
146157
147158 @Override
148159 public void visit(Method obj) {
149 if (getMethodName().equals("suite") && !obj.isStatic())
160 if (getMethodName().equals("suite") && !obj.isStatic()) {
150161 bugReporter.reportBug(new BugInstance(this, "IJU_SUITE_NOT_STATIC", NORMAL_PRIORITY).addClassAndMethod(this));
162 }
151163
152164 if (getMethodName().equals("suite") && obj.getSignature().startsWith("()") && obj.isStatic()) {
153165 if ((!obj.getSignature().equals("()Ljunit/framework/Test;") && !obj.getSignature().equals(
154166 "()Ljunit/framework/TestSuite;"))
155 || !obj.isPublic())
167 || !obj.isPublic()) {
156168 bugReporter.reportBug(new BugInstance(this, "IJU_BAD_SUITE_METHOD", NORMAL_PRIORITY).addClassAndMethod(this));
169 }
157170
158171 }
159172 }
166179 && !getMethod().isPrivate() && getMethodSig().equals("()V")) {
167180 sawSuperCall = false;
168181 super.visit(obj);
169 if (sawSuperCall)
182 if (sawSuperCall) {
170183 return;
184 }
171185 JavaClass we = Lookup.findSuperImplementor(getThisClass(), getMethodName(), "()V", bugReporter);
172186 if (we != null && !we.getClassName().equals("junit.framework.TestCase")) {
173187 // OK, got a bug
174188 int offset = 0;
175 if (getMethodName().equals("tearDown"))
189 if (getMethodName().equals("tearDown")) {
176190 offset = obj.getCode().length - 1;
191 }
177192 Method superMethod = Lookup.findImplementation(we, getMethodName(), "()V");
178193 Code superCode = superMethod.getCode();
179 if (superCode != null && superCode.getCode().length > 3)
194 if (superCode != null && superCode.getCode().length > 3) {
180195 bugReporter.reportBug(new BugInstance(this, getMethodName().equals("setUp") ? "IJU_SETUP_NO_SUPER"
181196 : "IJU_TEARDOWN_NO_SUPER", NORMAL_PRIORITY).addClassAndMethod(this).addMethod(we, superMethod)
182197 .describe(MethodAnnotation.METHOD_OVERRIDDEN).addSourceLine(this, offset));
198 }
183199 }
184200 }
185201 }
188204 public void sawOpcode(int seen) {
189205 switch (state) {
190206 case SEEN_NOTHING:
191 if (seen == ALOAD_0)
207 if (seen == ALOAD_0) {
192208 state = SEEN_ALOAD_0;
209 }
193210 break;
194211
195212 case SEEN_ALOAD_0:
196213 if ((seen == INVOKESPECIAL) && (getNameConstantOperand().equals(getMethodName()))
197 && (getSigConstantOperand().equals("()V")))
214 && (getSigConstantOperand().equals("()V"))) {
198215 sawSuperCall = true;
216 }
199217 state = SEEN_NOTHING;
200218 break;
201219 default:
204222 }
205223 }
206224
207 // vim:ts=4
6262
6363 @Override
6464 public void visit(Method method) {
65 if(method.isPublic() && method.getName().equals("next") && method.getArgumentTypes().length == 0){
65 if(method.isPublic() && "next".equals(method.getName()) && method.getArgumentTypes().length == 0){
6666 shouldVisitCode = true;
6767 super.visit(method);
6868 } else {
8787
8888 @Override
8989 public void sawOpcode(int seen) {
90 if (seen == NEW && getClassConstantOperand().equals("java/util/NoSuchElementException")) {
90 if (seen == NEW && "java/util/NoSuchElementException".equals(getClassConstantOperand())) {
9191 sawNoSuchElement = true;
9292 } else if (seen == INVOKESPECIAL || seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE) {
9393 sawCall = true;
7979 */
8080
8181 public final class LazyInit extends ByteCodePatternDetector implements StatelessDetector {
82 private BugReporter bugReporter;
82 private final BugReporter bugReporter;
8383
8484 private static final boolean DEBUG = SystemProperties.getBoolean("lazyinit.debug");
8585
9090
9191 static {
9292 pattern.add(new Load("f", "val").label("start")).add(new IfNull("val").label("test"))
93 .add(new Wild(1, 1).label("createObject").dominatedBy("test"))
94 .add(new Store("f", pattern.dummyVariable()).label("end").dominatedBy("createObject"));
93 .add(new Wild(1, 1).label("createObject").dominatedBy("test"))
94 .add(new Store("f", pattern.dummyVariable()).label("end").dominatedBy("createObject"));
9595 }
9696
9797 public LazyInit(BugReporter bugReporter) {
121121
122122 @Override
123123 public boolean prescreen(Method method, ClassContext classContext) {
124 if (method.getName().equals("<clinit>"))
125 return false;
126
124 if ("<clinit>".equals(method.getName())) {
125 return false;
126 }
127
127128 Code code = method.getCode();
128 if (code.getCode().length > 5000)
129 return false;
130
129 if (code.getCode().length > 5000) {
130 return false;
131 }
132
131133 BitSet bytecodeSet = classContext.getBytecodeSet(method);
132 if (bytecodeSet == null)
133 return false;
134 if (bytecodeSet == null) {
135 return false;
136 }
134137
135138 // The pattern requires a get/put pair accessing the same field.
136139 boolean hasGetStatic = bytecodeSet.get(Constants.GETSTATIC);
137140 boolean hasPutStatic = bytecodeSet.get(Constants.PUTSTATIC);
138 if (!hasGetStatic || !hasPutStatic)
139 return false;
141 if (!hasGetStatic || !hasPutStatic) {
142 return false;
143 }
140144
141145 // If the method is synchronized, then we'll assume that
142146 // things are properly synchronized
143 if (method.isSynchronized())
144 return false;
147 if (method.isSynchronized()) {
148 return false;
149 }
145150
146151 reported.clear();
147152 return true;
149154
150155 @Override
151156 public void reportMatch(ClassContext classContext, Method method, ByteCodePatternMatch match) throws CFGBuilderException,
152 DataflowAnalysisException {
157 DataflowAnalysisException {
153158 JavaClass javaClass = classContext.getJavaClass();
154159 MethodGen methodGen = classContext.getMethodGen(method);
155160 CFG cfg = classContext.getCFG(method);
156161
157162
158 // Get the variable referenced in the pattern instance.
159 BindingSet bindingSet = match.getBindingSet();
160 Binding binding = bindingSet.lookup("f");
161
162 // Look up the field as an XField.
163 // If it is volatile, then the instance is not a bug.
164 FieldVariable field = (FieldVariable) binding.getVariable();
165 XField xfield = Hierarchy.findXField(field.getClassName(), field.getFieldName(), field.getFieldSig(),
166 field.isStatic());
167 if (!xfield.isResolved())
168 return;
169
170 // XXX: for now, ignore lazy initialization of instance fields
171 if (!xfield.isStatic())
172 return;
173
174 // Definitely ignore synthetic class$ fields
175 if (xfield.getName().startsWith("class$") || xfield.getName().startsWith("array$")) {
176 if (DEBUG)
177 System.out.println("Ignoring field " + xfield.getName());
178 return;
179 }
180
181 // Ignore non-reference fields
182 String signature = xfield.getSignature();
183 if (!signature.startsWith("[") && !signature.startsWith("L")) {
184 if (DEBUG)
185 System.out.println("Ignoring non-reference field " + xfield.getName());
186 return;
187 }
188
189 // Strings are (mostly) safe to pass by data race in 1.5
190 if (signature.equals("Ljava/lang/String;"))
191 return;
192
193 // GUI types should not be accessed from multiple threads
194
195 if (signature.charAt(0) == 'L') {
196 ClassDescriptor fieldType = DescriptorFactory.createClassDescriptorFromFieldSignature(signature);
197
198 while (fieldType != null) {
199 XClass fieldClass;
200 try {
201 fieldClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, fieldType);
202 } catch (CheckedAnalysisException e) {
203 break;
163 // Get the variable referenced in the pattern instance.
164 BindingSet bindingSet = match.getBindingSet();
165 Binding binding = bindingSet.lookup("f");
166
167 // Look up the field as an XField.
168 // If it is volatile, then the instance is not a bug.
169 FieldVariable field = (FieldVariable) binding.getVariable();
170 XField xfield = Hierarchy.findXField(field.getClassName(), field.getFieldName(), field.getFieldSig(),
171 field.isStatic());
172 if (!xfield.isResolved()) {
173 return;
174 }
175
176 // XXX: for now, ignore lazy initialization of instance fields
177 if (!xfield.isStatic()) {
178 return;
179 }
180
181 // Definitely ignore synthetic class$ fields
182 if (xfield.getName().startsWith("class$") || xfield.getName().startsWith("array$")) {
183 if (DEBUG) {
184 System.out.println("Ignoring field " + xfield.getName());
185 }
186 return;
187 }
188
189 // Ignore non-reference fields
190 String signature = xfield.getSignature();
191 if (!signature.startsWith("[") && !signature.startsWith("L")) {
192 if (DEBUG) {
193 System.out.println("Ignoring non-reference field " + xfield.getName());
194 }
195 return;
196 }
197
198 // Strings are (mostly) safe to pass by data race in 1.5
199 if ("Ljava/lang/String;".equals(signature)) {
200 return;
201 }
202
203 // GUI types should not be accessed from multiple threads
204
205 if (signature.charAt(0) == 'L') {
206 ClassDescriptor fieldType = DescriptorFactory.createClassDescriptorFromFieldSignature(signature);
207
208 while (fieldType != null) {
209 XClass fieldClass;
210 try {
211 fieldClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, fieldType);
212 } catch (CheckedAnalysisException e) {
213 break;
214 }
215
216 String name = fieldClass.getClassDescriptor().getClassName();
217 if (name.startsWith("java/awt") || name.startsWith("javax/swing")) {
218 return;
219 }
220 if ("java/lang/Object".equals(name)) {
221 break;
222 }
223 fieldType = fieldClass.getSuperclassDescriptor();
224 }
225 }
226
227 // Get locations matching the beginning of the object creation,
228 // and the final field store.
229 PatternElementMatch createBegin = match.getFirstLabeledMatch("createObject");
230 PatternElementMatch store = match.getFirstLabeledMatch("end");
231 PatternElementMatch test = match.getFirstLabeledMatch("test");
232 InstructionHandle testInstructionHandle = test.getMatchedInstructionInstructionHandle();
233 if (reported.get(testInstructionHandle.getPosition())) {
234 return;
235 }
236
237 // Get all blocks
238 //
239 // (1) dominated by the wildcard instruction matching
240 // the beginning of the instructions creating the object, and
241 // (2) postdominated by the field store
242 //
243 // Exception edges are not considered in computing
244 // dominators/postdominators.
245 // We will consider this to be all of the code that creates
246 // the object.
247 DominatorsAnalysis domAnalysis = classContext.getNonExceptionDominatorsAnalysis(method);
248 PostDominatorsAnalysis postDomAnalysis = classContext.getNonExceptionPostDominatorsAnalysis(method);
249 BitSet extent = domAnalysis.getAllDominatedBy(createBegin.getBasicBlock());
250 BitSet postDom = postDomAnalysis.getAllDominatedBy(store.getBasicBlock());
251 // System.out.println("Extent: " + extent);
252 if (DEBUG) {
253 System.out.println("test dominates: " + extent);
254 System.out.println("Field store postdominates " + postDom);
255 }
256 extent.and(postDom);
257 if (DEBUG) {
258 System.out.println("extent: " + extent);
259 }
260 // Check all instructions in the object creation extent
261 //
262 // (1) to determine the common lock set, and
263 // (2) to check for NEW and Invoke instructions that might create an
264 // object
265 //
266 // We ignore matches where a lock is held consistently,
267 // or if the extent does not appear to create a new object.
268 LockDataflow lockDataflow = classContext.getLockDataflow(method);
269 LockSet lockSet = null;
270 boolean sawNEW = false, sawINVOKE = false;
271 for (BasicBlock block : cfg.getBlocks(extent)) {
272 for (Iterator<InstructionHandle> j = block.instructionIterator(); j.hasNext();) {
273 InstructionHandle handle = j.next();
274 if (handle.equals(store.getMatchedInstructionInstructionHandle())) {
275 break;
276 }
277 Location location = new Location(handle, block);
278
279 // Keep track of whether we saw any instructions
280 // that might actually have created a new object.
281 Instruction ins = handle.getInstruction();
282 if (DEBUG) {
283 System.out.println(location);
284 }
285 if (ins instanceof AllocationInstruction) {
286 sawNEW = true;
287 } else if (ins instanceof InvokeInstruction) {
288 if (ins instanceof INVOKESTATIC
289 && ((INVOKESTATIC) ins).getMethodName(classContext.getConstantPoolGen()).startsWith("new")) {
290 sawNEW = true;
204291 }
205
206 String name = fieldClass.getClassDescriptor().getClassName();
207 if (name.startsWith("java/awt") || name.startsWith("javax/swing"))
208 return;
209 if (name.equals("java/lang/Object"))
210 break;
211 fieldType = fieldClass.getSuperclassDescriptor();
212 }
213 }
214
215 // Get locations matching the beginning of the object creation,
216 // and the final field store.
217 PatternElementMatch createBegin = match.getFirstLabeledMatch("createObject");
218 PatternElementMatch store = match.getFirstLabeledMatch("end");
219 PatternElementMatch test = match.getFirstLabeledMatch("test");
220 InstructionHandle testInstructionHandle = test.getMatchedInstructionInstructionHandle();
221 if (reported.get(testInstructionHandle.getPosition()))
222 return;
223
224 // Get all blocks
225 //
226 // (1) dominated by the wildcard instruction matching
227 // the beginning of the instructions creating the object, and
228 // (2) postdominated by the field store
229 //
230 // Exception edges are not considered in computing
231 // dominators/postdominators.
232 // We will consider this to be all of the code that creates
233 // the object.
234 DominatorsAnalysis domAnalysis = classContext.getNonExceptionDominatorsAnalysis(method);
235 PostDominatorsAnalysis postDomAnalysis = classContext.getNonExceptionPostDominatorsAnalysis(method);
236 BitSet extent = domAnalysis.getAllDominatedBy(createBegin.getBasicBlock());
237 BitSet postDom = postDomAnalysis.getAllDominatedBy(store.getBasicBlock());
238 // System.out.println("Extent: " + extent);
239 if (DEBUG) {
240 System.out.println("test dominates: " + extent);
241 System.out.println("Field store postdominates " + postDom);
242 }
243 extent.and(postDom);
244 if (DEBUG) {
245 System.out.println("extent: " + extent);
246 }
247 // Check all instructions in the object creation extent
248 //
249 // (1) to determine the common lock set, and
250 // (2) to check for NEW and Invoke instructions that might create an
251 // object
252 //
253 // We ignore matches where a lock is held consistently,
254 // or if the extent does not appear to create a new object.
255 LockDataflow lockDataflow = classContext.getLockDataflow(method);
256 LockSet lockSet = null;
257 boolean sawNEW = false, sawINVOKE = false;
258 for (BasicBlock block : cfg.getBlocks(extent)) {
292 sawINVOKE = true;
293 }
294
295 // Compute lock set intersection for all matched
296 // instructions.
297 LockSet insLockSet = lockDataflow.getFactAtLocation(location);
298 if (lockSet == null) {
299 lockSet = new LockSet();
300 lockSet.copyFrom(insLockSet);
301 } else {
302 lockSet.intersectWith(insLockSet);
303 }
304 }
305 }
306
307 if (!(sawNEW || sawINVOKE)) {
308 return;
309 }
310 if (lockSet == null) {
311 throw new IllegalStateException("lock set is null");
312 }
313 if (!lockSet.isEmpty()) {
314 return;
315 }
316
317 boolean sawGetStaticAfterPutStatic = false;
318 check: if (signature.startsWith("[") || signature.startsWith("L")) {
319
320 BitSet postStore = domAnalysis.getAllDominatedBy(store.getBasicBlock());
321 for (BasicBlock block : cfg.getBlocks(postStore)) {
259322 for (Iterator<InstructionHandle> j = block.instructionIterator(); j.hasNext();) {
260323 InstructionHandle handle = j.next();
261 if (handle.equals(store.getMatchedInstructionInstructionHandle()))
262 break;
263 Location location = new Location(handle, block);
264
265 // Keep track of whether we saw any instructions
266 // that might actually have created a new object.
324
325 InstructionHandle nextHandle = handle.getNext();
267326 Instruction ins = handle.getInstruction();
268 if (DEBUG)
269 System.out.println(location);
270 if (ins instanceof AllocationInstruction)
271 sawNEW = true;
272 else if (ins instanceof InvokeInstruction) {
273 if (ins instanceof INVOKESTATIC
274 && ((INVOKESTATIC) ins).getMethodName(classContext.getConstantPoolGen()).startsWith("new"))
275 sawNEW = true;
276 sawINVOKE = true;
277 }
278
279 // Compute lock set intersection for all matched
280 // instructions.
281 LockSet insLockSet = lockDataflow.getFactAtLocation(location);
282 if (lockSet == null) {
283 lockSet = new LockSet();
284 lockSet.copyFrom(insLockSet);
285 } else
286 lockSet.intersectWith(insLockSet);
287 }
288 }
289
290 if (!(sawNEW || sawINVOKE))
291 return;
292 if (lockSet == null)
293 throw new IllegalStateException("lock set is null");
294 if (!lockSet.isEmpty())
295 return;
296
297 boolean sawGetStaticAfterPutStatic = false;
298 check: if (signature.startsWith("[") || signature.startsWith("L")) {
299
300 BitSet postStore = domAnalysis.getAllDominatedBy(store.getBasicBlock());
301 for (BasicBlock block : cfg.getBlocks(postStore)) {
302 for (Iterator<InstructionHandle> j = block.instructionIterator(); j.hasNext();) {
303 InstructionHandle handle = j.next();
304
305 InstructionHandle nextHandle = handle.getNext();
306 Instruction ins = handle.getInstruction();
307
308 if (ins instanceof GETSTATIC && potentialInitialization(nextHandle)) {
309 XField field2 = XFactory.createXField((FieldInstruction) ins, methodGen.getConstantPool());
310 if (xfield.equals(field2)) {
311 sawGetStaticAfterPutStatic = true;
312 break check;
313 }
327
328 if (ins instanceof GETSTATIC && potentialInitialization(nextHandle)) {
329 XField field2 = XFactory.createXField((FieldInstruction) ins, methodGen.getConstantPool());
330 if (xfield.equals(field2)) {
331 sawGetStaticAfterPutStatic = true;
332 break check;
314333 }
315334 }
316335 }
317336 }
318
319 // Compute the priority:
320 // - ignore lazy initialization of instance fields
321 // - when it's done in a public method, emit a high priority warning
322 // - protected or default access method, emit a medium priority
323 // warning
324 // - otherwise, low priority
325
326 if (!sawGetStaticAfterPutStatic && xfield.isVolatile())
327 return;
328 int priority = LOW_PRIORITY;
329 boolean isDefaultAccess = (method.getAccessFlags() & (Constants.ACC_PUBLIC | Constants.ACC_PRIVATE | Constants.ACC_PROTECTED)) == 0;
330 if (method.isPublic())
331 priority = NORMAL_PRIORITY;
332 else if (method.isProtected() || isDefaultAccess)
333 priority = NORMAL_PRIORITY;
334 if (signature.startsWith("[") || signature.startsWith("Ljava/util/"))
335 priority--;
336 if (!sawNEW)
337 priority++;
338 if (!sawGetStaticAfterPutStatic && priority < LOW_PRIORITY)
339 priority = LOW_PRIORITY;
340 if (classContext.getXClass().usesConcurrency())
341 priority--;
342 // Report the bug.
343 InstructionHandle start = match.getLabeledInstruction("start");
344 InstructionHandle end = match.getLabeledInstruction("end");
345 String sourceFile = javaClass.getSourceFileName();
346 bugReporter.reportBug(new BugInstance(this, sawGetStaticAfterPutStatic ? "LI_LAZY_INIT_UPDATE_STATIC"
347 : "LI_LAZY_INIT_STATIC", priority).addClassAndMethod(methodGen, sourceFile).addField(xfield)
348 .describe("FIELD_ON").addSourceLine(classContext, methodGen, sourceFile, start, end));
349 reported.set(testInstructionHandle.getPosition());
350
351 }
352
353 /**
354 * @param nextHandle
355 * @return
356 */
337 }
338
339 // Compute the priority:
340 // - ignore lazy initialization of instance fields
341 // - when it's done in a public method, emit a high priority warning
342 // - protected or default access method, emit a medium priority
343 // warning
344 // - otherwise, low priority
345
346 if (!sawGetStaticAfterPutStatic && xfield.isVolatile()) {
347 return;
348 }
349 int priority = LOW_PRIORITY;
350 boolean isDefaultAccess = (method.getAccessFlags() & (Constants.ACC_PUBLIC | Constants.ACC_PRIVATE | Constants.ACC_PROTECTED)) == 0;
351 if (method.isPublic()) {
352 priority = NORMAL_PRIORITY;
353 } else if (method.isProtected() || isDefaultAccess) {
354 priority = NORMAL_PRIORITY;
355 }
356 if (signature.startsWith("[") || signature.startsWith("Ljava/util/")) {
357 priority--;
358 }
359 if (!sawNEW) {
360 priority++;
361 }
362 if (!sawGetStaticAfterPutStatic && priority < LOW_PRIORITY) {
363 priority = LOW_PRIORITY;
364 }
365 if (classContext.getXClass().usesConcurrency()) {
366 priority--;
367 }
368 // Report the bug.
369 InstructionHandle start = match.getLabeledInstruction("start");
370 InstructionHandle end = match.getLabeledInstruction("end");
371 String sourceFile = javaClass.getSourceFileName();
372 bugReporter.reportBug(new BugInstance(this, sawGetStaticAfterPutStatic ? "LI_LAZY_INIT_UPDATE_STATIC"
373 : "LI_LAZY_INIT_STATIC", priority).addClassAndMethod(methodGen, sourceFile).addField(xfield)
374 .describe("FIELD_ON").addSourceLine(classContext, methodGen, sourceFile, start, end));
375 reported.set(testInstructionHandle.getPosition());
376
377 }
378
357379 private boolean potentialInitialization(InstructionHandle nextHandle) {
358 if (nextHandle == null)
380 if (nextHandle == null) {
359381 return true;
382 }
360383 Instruction instruction = nextHandle.getInstruction();
361 if (instruction instanceof ReturnInstruction)
362 return false;
363 if (instruction instanceof IfInstruction)
364 return false;
384 if (instruction instanceof ReturnInstruction) {
385 return false;
386 }
387 if (instruction instanceof IfInstruction) {
388 return false;
389 }
365390 return true;
366391 }
367392
368393 }
369394
370 // vim:ts=4
99 import org.apache.bcel.generic.ARETURN;
1010 import org.apache.bcel.generic.BranchInstruction;
1111 import org.apache.bcel.generic.GOTO;
12 import org.apache.bcel.generic.IFNULL;
13 import org.apache.bcel.generic.INVOKEVIRTUAL;
1214 import org.apache.bcel.generic.Instruction;
1315 import org.apache.bcel.generic.InstructionHandle;
1416 import org.apache.bcel.generic.MethodGen;
3335 import edu.umd.cs.findbugs.ba.vna.ValueNumber;
3436 import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;
3537 import edu.umd.cs.findbugs.ba.vna.ValueNumberSourceInfo;
38 import edu.umd.cs.findbugs.visitclass.Util;
3639
3740 public class LoadOfKnownNullValue implements Detector {
3841
39 private BugReporter bugReporter;
40
41 private BugAccumulator bugAccumulator;
42 private final BugReporter bugReporter;
43
44 private final BugAccumulator bugAccumulator;
4245
4346 public LoadOfKnownNullValue(BugReporter bugReporter) {
4447 this.bugReporter = bugReporter;
4548 this.bugAccumulator = new BugAccumulator(bugReporter);
4649 }
4750
51 @Override
4852 public void visitClassContext(ClassContext classContext) {
4953 Method[] methodList = classContext.getJavaClass().getMethods();
5054
5155 for (Method method : methodList) {
52 if (method.getCode() == null)
53 continue;
56 if (method.getCode() == null) {
57 continue;
58 }
5459
5560 try {
5661 analyzeMethod(classContext, method);
5762 } catch (MethodUnprofitableException mue) {
58 if (SystemProperties.getBoolean("unprofitable.debug")) // otherwise
59 // don't
60 // report
63 if (SystemProperties.getBoolean("unprofitable.debug")) {
64 // don't
65 // report
6166 bugReporter.logError("skipping unprofitable method in " + getClass().getName());
67 }
6268 } catch (CFGBuilderException e) {
6369 bugReporter.logError("Detector " + this.getClass().getName() + " caught exception", e);
6470 } catch (DataflowAnalysisException e) {
8591
8692 InstructionHandle handle = location.getHandle();
8793 Instruction ins = handle.getInstruction();
88 if (!(ins instanceof ALOAD))
89 continue;
94 if (!(ins instanceof ALOAD)) {
95 continue;
96 }
9097
9198 IsNullValueFrame frame = nullValueDataflow.getFactAtLocation(location);
9299 if (!frame.isValid()) {
102109 IsNullValue v = frame.getValue(index);
103110 if (!v.isDefinitelyNull()) {
104111 int sourceLine = lineNumbers.getSourceLine(handle.getPosition());
105 if (sourceLine > 0)
112 if (sourceLine > 0) {
106113 linesWithLoadsOfNotDefinitelyNullValues.set(sourceLine);
114 }
107115 }
108116 }
109117 }
114122 Location location = i.next();
115123 InstructionHandle handle = location.getHandle();
116124 Instruction ins = handle.getInstruction();
117 if (!(ins instanceof ALOAD))
118 continue;
125 if (!(ins instanceof ALOAD)) {
126 continue;
127 }
119128 IsNullValueFrame frame = nullValueDataflow.getFactAtLocation(location);
120129 if (!frame.isValid()) {
121130 // This basic block is probably dead
128137
129138 int index = load.getIndex();
130139 IsNullValue v = frame.getValue(index);
131 if (!v.isDefinitelyNull())
140 if (!v.isDefinitelyNull()) {
132141 sometimesGood.put(handle, null);
142 }
133143 }
134144
135145 // System.out.println(nullValueDataflow);
138148
139149 InstructionHandle handle = location.getHandle();
140150 Instruction ins = handle.getInstruction();
141 if (!(ins instanceof ALOAD))
142 continue;
143
144 if (sometimesGood.containsKey(handle))
145 continue;
151 if (!(ins instanceof ALOAD)) {
152 continue;
153 }
154
155 if (sometimesGood.containsKey(handle)) {
156 continue;
157 }
146158 IsNullValueFrame frame = nullValueDataflow.getFactAtLocation(location);
147159 if (!frame.isValid()) {
148160 // This basic block is probably dead
156168 int index = load.getIndex();
157169 IsNullValue v = frame.getValue(index);
158170 if (v.isDefinitelyNull()) {
159 Instruction next = handle.getNext().getInstruction();
171 InstructionHandle nextHandle = handle.getNext();
172 Instruction next = nextHandle.getInstruction();
173 int position = location
174 .getHandle().getPosition();
175 int catchSizeANY = Util.getSizeOfSurroundingTryBlock(method, "", position);
176 if (catchSizeANY < Integer.MAX_VALUE && isNullTestedClose( classContext, load, nextHandle, next)) {
177 continue;
178 }
160179 InstructionHandle prevHandle = handle.getPrev();
161180 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(classContext, methodGen,
162181 sourceFile, handle);
180199 }
181200 int startLine = sourceLineAnnotation.getStartLine();
182201 if (startLine > 0 && lineMentionedMultipleTimes.get(startLine)
183 && linesWithLoadsOfNotDefinitelyNullValues.get(startLine))
184 continue;
202 && linesWithLoadsOfNotDefinitelyNullValues.get(startLine)) {
203 continue;
204 }
185205
186206 int previousLine = prevSourceLineAnnotation.getEndLine();
187207 if (startLine < previousLine) {
190210 continue;
191211 }
192212 int priority = NORMAL_PRIORITY;
193 if (!v.isChecked())
213 if (!v.isChecked()) {
194214 priority++;
195
215 }
216
196217 BugAnnotation variableAnnotation = null;
197218 try {
198219 // Get the value number
199220 ValueNumberFrame vnaFrame = classContext.getValueNumberDataflow(method).getFactAfterLocation(location);
200221 if (vnaFrame.isValid()) {
201
222
202223 ValueNumber valueNumber = vnaFrame.getTopValue();
203 if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT))
224 if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) {
204225 return;
226 }
205227 variableAnnotation = ValueNumberSourceInfo.findAnnotationFromValueNumber(method, location, valueNumber, vnaFrame,
206228 "VALUE_OF");
207229 if (variableAnnotation instanceof LocalVariableAnnotation) {
225247
226248 bugAccumulator.accumulateBug(
227249 new BugInstance(this, "NP_LOAD_OF_KNOWN_NULL_VALUE", priority).addClassAndMethod(methodGen, sourceFile)
228 .addOptionalAnnotation(variableAnnotation),
250 .addOptionalAnnotation(variableAnnotation),
229251 sourceLineAnnotation);
230252 }
231253
232254 }
233255 }
234256
257 /**
258 * @param classContext
259 * @param nextHandle
260 * @param next
261 */
262 private boolean isNullTestedClose(ClassContext classContext, ALOAD load, InstructionHandle nextHandle, Instruction next) {
263 if (!(next instanceof IFNULL)) {
264 return false;
265 }
266
267 IFNULL ifNull = (IFNULL) next;
268 InstructionHandle nextNextHandle = nextHandle.getNext(); // aload
269 if (nextNextHandle == null) {
270 return false;
271 }
272 Instruction nextInstruction = nextNextHandle.getInstruction();
273
274 if (!(nextInstruction instanceof ALOAD)) {
275 return false;
276 }
277 ALOAD nextLoad = (ALOAD) nextInstruction;
278 if (load.getIndex() != nextLoad.getIndex()) {
279 return false;
280 }
281 InstructionHandle nextNextNextHandle = nextNextHandle.getNext(); // invoke
282 if (nextNextNextHandle == null) {
283 return false;
284 }
285 Instruction nextNextNextInstruction = nextNextNextHandle.getInstruction();
286 if (!(nextNextNextInstruction instanceof INVOKEVIRTUAL)) {
287 return false;
288 }
289 INVOKEVIRTUAL invokeVirtual = (INVOKEVIRTUAL) nextNextNextInstruction;
290 String methodName = invokeVirtual.getMethodName(classContext.getConstantPoolGen());
291 String methodSig = invokeVirtual.getSignature(classContext.getConstantPoolGen());
292 if (!"close".equals(methodName)) {
293 return false;
294 }
295 if (!"()V".equals(methodSig)) {
296 return false;
297 }
298 InstructionHandle nextNextNextNextHandle = nextNextNextHandle.getNext(); // after
299 if (ifNull.getTarget() != nextNextNextNextHandle) {
300 return false;
301 }
302
303 return true;
304
305 }
306
307 @Override
235308 public void report() {
236309 }
237310
1818
1919 package edu.umd.cs.findbugs.detect;
2020
21 import java.util.Arrays;
2122 import java.util.HashSet;
23 import java.util.List;
2224
2325 import org.apache.bcel.classfile.Code;
2426
2628 import edu.umd.cs.findbugs.BugInstance;
2729 import edu.umd.cs.findbugs.BugReporter;
2830 import edu.umd.cs.findbugs.OpcodeStack;
31 import edu.umd.cs.findbugs.ba.ClassContext;
2932 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
33 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
3034 import edu.umd.cs.findbugs.visitclass.PreorderVisitor;
3135
3236 /**
3943 * that memory, which means that the logger configuration is lost.
4044 */
4145 public class LostLoggerDueToWeakReference extends OpcodeStackDetector {
46 private static final List<MethodDescriptor> methods = Arrays.asList(
47 new MethodDescriptor("java/util/logging/Logger", "getLogger", "(Ljava/lang/String;)Ljava/util/logging/Logger;", true),
48 new MethodDescriptor("java/util/logging/Logger", "getLogger", "(Ljava/lang/String;Ljava/lang/String;)Ljava/util/logging/Logger;", true));
4249
43 final BugReporter bugReporter;
50 // final BugReporter bugReporter;
4451
4552 final BugAccumulator bugAccumulator;
4653
4754 final HashSet<String> namesOfSetterMethods = new HashSet<String>();
4855
4956 public LostLoggerDueToWeakReference(BugReporter bugReporter) {
50 this.bugReporter = bugReporter;
57 // this.bugReporter = bugReporter;
5158 this.bugAccumulator = new BugAccumulator(bugReporter);
5259 namesOfSetterMethods.add("addHandler");
5360 namesOfSetterMethods.add("setUseParentHandlers");
5663 }
5764
5865 @Override
66 public void visitClassContext(ClassContext classContext) {
67 if(hasInterestingMethod(classContext.getJavaClass().getConstantPool(), methods)) {
68 super.visitClassContext(classContext);
69 }
70 }
71
72 @Override
5973 public void visit(Code code) {
6074 if (getMethodSig().indexOf("Logger") == -1) {
6175 sawGetLogger = -1;
6276 loggerEscaped = loggerImported = false;
6377 super.visit(code); // make callbacks to sawOpcode for all opcodes
78 /*
6479 if (false) {
6580 System.out.println(getFullyQualifiedMethodName());
6681 System.out.printf("%d %s %s\n", sawGetLogger, loggerEscaped, loggerImported);
6782 }
83 */
6884 if (sawGetLogger >= 0 && !loggerEscaped && !loggerImported) {
6985 bugAccumulator.reportAccumulatedBugs();
70 } else
86 } else {
7187 bugAccumulator.clearBugs();
88 }
7289 }
7390 }
7491
8097
8198 @Override
8299 public void sawOpcode(int seen) {
83 if (loggerEscaped || loggerImported)
100 if (loggerEscaped || loggerImported) {
84101 return;
102 }
85103 switch (seen) {
86104 case INVOKESTATIC:
87 if (getClassConstantOperand().equals("java/util/logging/Logger") && getNameConstantOperand().equals("getLogger")) {
105 if ("java/util/logging/Logger".equals(getClassConstantOperand()) && "getLogger".equals(getNameConstantOperand())) {
88106 OpcodeStack.Item item = stack.getStackItem(0);
89 if (!"".equals(item.getConstant()))
107 if (!"".equals(item.getConstant())) {
90108 sawGetLogger = getPC();
109 }
91110 break;
92111 }
93112 checkForImport();
94113 break;
95114 case INVOKEVIRTUAL:
96 if (getClassConstantOperand().equals("java/util/logging/Logger")
115 if ("java/util/logging/Logger".equals(getClassConstantOperand())
97116 && namesOfSetterMethods.contains(getNameConstantOperand())) {
98117 int priority = HIGH_PRIORITY;
99 if (getMethod().isStatic() && getMethodName().equals("main") && getMethodSig().equals("([Ljava/lang/String;)V"))
118 if (getMethod().isStatic() && "main".equals(getMethodName()) && "([Ljava/lang/String;)V".equals(getMethodSig())) {
100119 priority = NORMAL_PRIORITY;
120 }
101121
102122 OpcodeStack.Item item = stack.getItemMethodInvokedOn(this);
103123 BugInstance bug = new BugInstance(this, "LG_LOST_LOGGER_DUE_TO_WEAK_REFERENCE", priority)
117137
118138 case CHECKCAST:
119139 String sig = getClassConstantOperand();
120 if (sig.indexOf("Logger") >= 0)
140 if (sig.indexOf("Logger") >= 0) {
121141 loggerImported = true;
142 }
122143 break;
123144
124145 case GETFIELD:
128149 case PUTFIELD:
129150 case PUTSTATIC:
130151 checkForFieldEscape();
152 break;
153 default:
154 break;
131155 }
132156
133157 }
134158
135159 private void checkForImport() {
136 if (getSigConstantOperand().endsWith("Logger;"))
160 if (getSigConstantOperand().endsWith("Logger;")) {
137161 loggerImported = true;
162 }
138163 }
139164
140165 private void checkForMethodExportImport() {
141166 int numArguments = PreorderVisitor.getNumberArguments(getSigConstantOperand());
142167 for (int i = 0; i < numArguments; i++) {
143168 OpcodeStack.Item item = stack.getStackItem(i);
144 if (item.getSignature().endsWith("Logger;"))
169 if (item.getSignature().endsWith("Logger;")) {
145170 loggerEscaped = true;
171 }
146172 }
147173 String sig = getSigConstantOperand();
148 int pos = sig.indexOf(")");
174 int pos = sig.indexOf(')');
149175 int loggerPos = sig.indexOf("Logger");
150 if (0 <= loggerPos && loggerPos < pos)
176 if (0 <= loggerPos && loggerPos < pos) {
151177 loggerEscaped = true;
178 }
152179 }
153180
154181 private void checkForFieldEscape() {
155182 String sig = getSigConstantOperand();
156 if (sig.indexOf("Logger") >= 0)
183 if (sig.indexOf("Logger") >= 0) {
157184 loggerEscaped = true;
185 }
158186 OpcodeStack.Item item = stack.getStackItem(0);
159 if (item.getSignature().endsWith("Logger;"))
187 if (item.getSignature().endsWith("Logger;")) {
160188 loggerEscaped = true;
189 }
161190
162191 }
163192
2121
2222 import org.apache.bcel.Constants;
2323 import org.apache.bcel.classfile.Code;
24 import org.apache.bcel.generic.Type;
2425
2526 import edu.umd.cs.findbugs.BugAccumulator;
2627 import edu.umd.cs.findbugs.BugInstance;
3738 import edu.umd.cs.findbugs.ba.ClassContext;
3839 import edu.umd.cs.findbugs.ba.XFactory;
3940 import edu.umd.cs.findbugs.ba.XMethod;
41 import edu.umd.cs.findbugs.ba.ch.Subtypes2;
4042 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
43 import edu.umd.cs.findbugs.classfile.Global;
44 import edu.umd.cs.findbugs.detect.FindNoSideEffectMethods.NoSideEffectMethodsDatabase;
45 import edu.umd.cs.findbugs.util.ClassName;
4146 import edu.umd.cs.findbugs.visitclass.PreorderVisitor;
4247
4348 /**
7479
7580 private int callPC;
7681
82 private final NoSideEffectMethodsDatabase noSideEffectMethods;
83
84 private boolean sawExcludedNSECall;
85
7786 public MethodReturnCheck(BugReporter bugReporter) {
7887 this.bugAccumulator = new BugAccumulator(bugReporter);
88 this.noSideEffectMethods = Global.getAnalysisCache().getDatabase(NoSideEffectMethodsDatabase.class);
7989 }
8090
8191 @Override
8696
8797 @Override
8898 public void visitAfter(Code code) {
99 if(bugAccumulator.getLastBugLocation() == null && !sawExcludedNSECall && noSideEffectMethods.useless(getMethodDescriptor())) {
100 // Do not report UC_USELESS_VOID_METHOD if something was already reported inside the current method
101 // it's likely that UC_USELESS_VOID_METHOD is just the consequence of the previous report
102 bugAccumulator.accumulateBug(new BugInstance(this, "UC_USELESS_VOID_METHOD",
103 code.getCode().length > 40 ? HIGH_PRIORITY : code.getCode().length > 15 ? NORMAL_PRIORITY : LOW_PRIORITY)
104 .addClassAndMethod(getMethodDescriptor()), this);
105 }
106 sawExcludedNSECall = false;
89107 bugAccumulator.reportAccumulatedBugs();
90108 }
91109
92110 private boolean badUseOfCompareResult(Item left, Item right) {
93111 XMethod m = left.getReturnValueOf();
94112
95 if (m == null)
113 if (m == null) {
96114 return false;
115 }
97116 String name = m.getName();
98
99 if (!name.startsWith("compare"))
117
118 if (!name.startsWith("compare")) {
100119 return false;
101
120 }
121
102122 Object value = right.getConstant();
103 if (!(value instanceof Integer) || ((Integer) value).intValue() == 0)
123 if (!(value instanceof Integer) || ((Integer) value).intValue() == 0) {
104124 return false;
105 if (!m.isPublic() && m.isResolved())
125 }
126 if (!m.isPublic() && m.isResolved()) {
106127 return false;
107
108
128 }
129
130
109131 if (m.isStatic() || !m.isResolved()) {
110 if (name.equals("compare") && m.getClassName().startsWith("com.google.common.primitives."))
132 if ("compare".equals(name) && m.getClassName().startsWith("com.google.common.primitives.")) {
111133 return true;
134 }
112135 }
113136 if (!m.isStatic() || !m.isResolved()) {
114 if (name.equals("compareTo") && m.getSignature().equals("(Ljava/lang/Object;)I"))
137 if ("compareTo".equals(name) && "(Ljava/lang/Object;)I".equals(m.getSignature())) {
115138 return true;
116 if (name.equals("compare") && m.getSignature().equals("(Ljava/lang/Object;Ljava/lang/Object;)I"))
139 }
140 if ("compare".equals(name) && "(Ljava/lang/Object;Ljava/lang/Object;)I".equals(m.getSignature())) {
117141 return true;
142 }
118143 }
119144
120145 return false;
123148 @Override
124149 public void sawOpcode(int seen) {
125150
126 if (DEBUG)
151 if (DEBUG) {
127152 System.out.printf("%3d %10s %3s %s%n", getPC(), OPCODE_NAMES[seen], state, stack);
153 }
128154
129155 switch (seen) {
130156 case Constants.IF_ICMPEQ:
157
131158 case Constants.IF_ICMPNE:
132159 OpcodeStack.Item left = stack.getStackItem(1);
133160 OpcodeStack.Item right = stack.getStackItem(0);
135162 XMethod returnValueOf = left.getReturnValueOf();
136163 assert returnValueOf != null;
137164 bugAccumulator.accumulateBug(new BugInstance(this, "RV_CHECK_COMPARETO_FOR_SPECIFIC_RETURN_VALUE", NORMAL_PRIORITY)
138 .addClassAndMethod(this).addMethod(returnValueOf).describe(MethodAnnotation.METHOD_CALLED).addValueSource(right, this), this);
165 .addClassAndMethod(this).addMethod(returnValueOf).describe(MethodAnnotation.METHOD_CALLED).addValueSource(right, this), this);
139166 } else if (badUseOfCompareResult(right, left)) {
140167 XMethod returnValueOf = right.getReturnValueOf();
141168 assert returnValueOf != null;
142169 bugAccumulator.accumulateBug(new BugInstance(this, "RV_CHECK_COMPARETO_FOR_SPECIFIC_RETURN_VALUE", NORMAL_PRIORITY)
143 .addClassAndMethod(this).addMethod(returnValueOf).describe(MethodAnnotation.METHOD_CALLED).addValueSource(left, this), this);
144 }
145 }
146
147 checkForInitWithoutCopyOnStack: if (seen == INVOKESPECIAL && getNameConstantOperand().equals("<init>")) {
170 .addClassAndMethod(this).addMethod(returnValueOf).describe(MethodAnnotation.METHOD_CALLED).addValueSource(left, this), this);
171 }
172 break;
173 default:
174 break;
175 }
176
177 checkForInitWithoutCopyOnStack: if (seen == INVOKESPECIAL && "<init>".equals(getNameConstantOperand())) {
148178 int arguments = PreorderVisitor.getNumberArguments(getSigConstantOperand());
149179 OpcodeStack.Item invokedOn = stack.getStackItem(arguments);
150 if (invokedOn.isNewlyAllocated() && (!getMethodName().equals("<init>") || invokedOn.getRegisterNumber() != 0)) {
180 if (invokedOn.isNewlyAllocated() && (!"<init>".equals(getMethodName()) || invokedOn.getRegisterNumber() != 0)) {
151181
152182 for (int i = arguments + 1; i < stack.getStackDepth(); i++) {
153183 OpcodeStack.Item item = stack.getStackItem(i);
154 if (item.isNewlyAllocated() && item.getSignature().equals(invokedOn.getSignature()))
184 if (item.isNewlyAllocated() && item.getSignature().equals(invokedOn.getSignature())) {
155185 break checkForInitWithoutCopyOnStack;
186 }
156187 }
157188
158189 callSeen = XFactory.createReferencedXMethod(this);
165196 }
166197 }
167198
168 if (state == SAW_INVOKE && isPop(seen))
199 if (state == SAW_INVOKE && isPop(seen)) {
169200 sawMethodCallWithIgnoredReturnValue();
170 else if (INVOKE_OPCODE_SET.get(seen)) {
201 } else if (INVOKE_OPCODE_SET.get(seen)) {
171202 callPC = getPC();
172203 callSeen = XFactory.createReferencedXMethod(this);
173204 state = SAW_INVOKE;
174 if (DEBUG)
205 if (DEBUG) {
175206 System.out.println(" invoking " + callSeen);
176 } else
207 }
208 } else {
177209 state = SCAN;
210 }
178211
179212 if (seen == NEW) {
180213 previousOpcodeWasNEW = true;
184217 if (annotation != null && annotation != CheckReturnValueAnnotation.CHECK_RETURN_VALUE_IGNORE) {
185218 int priority = annotation.getPriority();
186219 if (!checkReturnAnnotationDatabase.annotationIsDirect(callSeen)
187 && !callSeen.getSignature().endsWith(callSeen.getClassName().replace('.', '/') + ";"))
220 && !callSeen.getSignature().endsWith(callSeen.getClassName().replace('.', '/') + ";")) {
188221 priority++;
222 }
189223 bugAccumulator.accumulateBug(new BugInstance(this, annotation.getPattern(), priority).addClassAndMethod(this)
190224 .addCalledMethod(this), this);
191225 }
203237 {
204238 CheckReturnValueAnnotation annotation = checkReturnAnnotationDatabase.getResolvedAnnotation(callSeen, false);
205239 if (annotation == null) {
206 XFactory xFactory = AnalysisContext.currentXFactory();
207
208 if (xFactory.isFunctionshatMightBeMistakenForProcedures(callSeen.getMethodDescriptor())) {
209 annotation = CheckReturnValueAnnotation.CHECK_RETURN_VALUE_INFERRED;
240 if (noSideEffectMethods.excluded(callSeen.getMethodDescriptor())) {
241 sawExcludedNSECall = true;
242 }
243 if (noSideEffectMethods.hasNoSideEffect(callSeen.getMethodDescriptor())) {
244 int priority = NORMAL_PRIORITY;
245 Type callReturnType = Type.getReturnType(callSeen.getMethodDescriptor().getSignature());
246 Type methodReturnType = Type.getReturnType(getMethodSig());
247 if(callReturnType.equals(methodReturnType) && callReturnType != Type.BOOLEAN && callReturnType != Type.VOID) {
248 priority = HIGH_PRIORITY;
249 } else {
250 String callReturnClass = callSeen.getName().equals("<init>") ?
251 callSeen.getClassDescriptor().getClassName() :
252 ClassName.fromFieldSignature(callReturnType.getSignature());
253
254 String methodReturnClass = ClassName.fromFieldSignature(methodReturnType.getSignature());
255 if(callReturnClass != null && methodReturnClass != null &&
256 Subtypes2.instanceOf(ClassName.toDottedClassName(callReturnClass), ClassName.toDottedClassName(methodReturnClass))) {
257 priority = HIGH_PRIORITY;
258 }
259 }
260 int catchSize = getSizeOfSurroundingTryBlock(getPC());
261 if(catchSize <= 2) {
262 priority++;
263 }
264 BugInstance warning = new BugInstance(this, "RV_RETURN_VALUE_IGNORED_NO_SIDE_EFFECT", priority)
265 .addClassAndMethod(this).addMethod(callSeen).describe(MethodAnnotation.METHOD_CALLED);
266 bugAccumulator.accumulateBug(warning, SourceLineAnnotation.fromVisitedInstruction(this, callPC));
267 } else {
268 XFactory xFactory = AnalysisContext.currentXFactory();
269
270 if (xFactory.isFunctionshatMightBeMistakenForProcedures(callSeen.getMethodDescriptor())) {
271 annotation = CheckReturnValueAnnotation.CHECK_RETURN_VALUE_INFERRED;
272 }
210273 }
211274 }
212275 if (annotation != null && annotation.getPriority() <= LOW_PRIORITY) {
213276 int popPC = getPC();
214 if (DEBUG)
277 if (DEBUG) {
215278 System.out.println("Saw POP @" + popPC);
279 }
216280 int catchSize = getSizeOfSurroundingTryBlock(popPC);
217281
218282 int priority = annotation.getPriority();
219 if (catchSize <= 1)
283 if (catchSize <= 1) {
220284 priority += 2;
221 else if (catchSize <= 2)
285 } else if (catchSize <= 2) {
222286 priority += 1;
287 }
223288 if (!checkReturnAnnotationDatabase.annotationIsDirect(callSeen)
224 && !callSeen.getSignature().endsWith(callSeen.getClassName().replace('.', '/') + ";"))
289 && !callSeen.getSignature().endsWith(callSeen.getClassName().replace('.', '/') + ";")) {
225290 priority++;
226 if (callSeen.isPrivate())
291 }
292 if (callSeen.isPrivate()) {
227293 priority++;
228 if (callSeen.getName().equals("clone") || callSeen.getName().startsWith("get"))
294 }
295 if ("clone".equals(callSeen.getName()) || callSeen.getName().startsWith("get")) {
229296 priority++;
297 }
230298 String pattern = annotation.getPattern();
231 if (callSeen.getName().equals("<init>")
232 && (callSeen.getClassName().endsWith("Exception") || callSeen.getClassName().endsWith("Error")))
299 if ("<init>".equals(callSeen.getName())
300 && (callSeen.getClassName().endsWith("Exception") || callSeen.getClassName().endsWith("Error"))) {
233301 pattern = "RV_EXCEPTION_NOT_THROWN";
302 }
234303 BugInstance warning = new BugInstance(this, pattern, priority).addClassAndMethod(this).addMethod(callSeen)
235304 .describe(MethodAnnotation.METHOD_CALLED);
236305 bugAccumulator.accumulateBug(warning, SourceLineAnnotation.fromVisitedInstruction(this, callPC));
4646 invokeOpcodeSet.set(Constants.INVOKEVIRTUAL);
4747 }
4848
49 private ObjectType baseClassType;
49 private final ObjectType baseClassType;
5050
51 private String methodName;
51 private final String methodName;
5252
53 private String methodSig;
53 private final String methodSig;
5454
55 private boolean isUninteresting;
55 private final boolean isUninteresting;
5656
5757 private String bugType;
5858
5959 /**
6060 * Constructor. The Streams created will be marked as uninteresting.
61 *
61 *
6262 * @param baseClass
6363 * base class through which the method will be called (we check
6464 * instances of the base class and all subtypes)
7676
7777 /**
7878 * Constructor. The Streams created will be marked as interesting.
79 *
79 *
8080 * @param baseClass
8181 * base class through which the method will be called (we check
8282 * instances of the base class and all subtypes)
9696 this.bugType = bugType;
9797 }
9898
99 @Override
99100 public Stream createStream(Location location, ObjectType type, ConstantPoolGen cpg,
100101 RepositoryLookupFailureCallback lookupFailureCallback) {
101102
104105
105106 // For now, just support instance methods
106107 short opcode = ins.getOpcode();
107 if (!invokeOpcodeSet.get(opcode))
108 if (!invokeOpcodeSet.get(opcode)) {
108109 return null;
110 }
109111
110112 // Is invoked class a subtype of the base class we want
111113 // FIXME: should test be different for INVOKESPECIAL and
112114 // INVOKESTATIC?
113115 InvokeInstruction inv = (InvokeInstruction) ins;
114116 ReferenceType classType = inv.getReferenceType(cpg);
115 if (!Hierarchy.isSubtype(classType, baseClassType))
117 if (!Hierarchy.isSubtype(classType, baseClassType)) {
116118 return null;
119 }
117120
118121 // See if method name and signature match
119122 String methodName = inv.getMethodName(cpg);
120123 String methodSig = inv.getSignature(cpg);
121 if (!this.methodName.equals(methodName) || !this.methodSig.equals(methodSig))
124 if (!this.methodName.equals(methodName) || !this.methodSig.equals(methodSig)) {
122125 return null;
126 }
123127
124128 String streamClass = type.getClassName();
125 if (streamClass.equals("java.sql.CallableStatement"))
129 if ("java.sql.CallableStatement".equals(streamClass)) {
126130 streamClass = "java.sql.PreparedStatement";
131 }
127132 Stream result = new Stream(location, streamClass, streamClass).setIgnoreImplicitExceptions(true).setIsOpenOnCreation(
128133 true);
129 if (!isUninteresting)
134 if (!isUninteresting) {
130135 result.setInteresting(bugType);
136 }
131137 return result;
132138 } catch (ClassNotFoundException e) {
133139 lookupFailureCallback.reportMissingClass(e);
137143 }
138144 }
139145
140 // vim:ts=3
2929 public Methods(BugReporter bugReporter) {
3030 }
3131
32 @Override
3233 public void visitClassContext(ClassContext classContext) {
3334 classContext.getJavaClass().accept(this);
3435 }
3536
37 @Override
3638 public void report() {
3739
3840 }
4343
4444 private static final String SERVLET_NAME = "javax.servlet.Servlet";
4545
46 private BugReporter bugReporter;
46 private final BugReporter bugReporter;
4747
4848 private Set<JavaClass> mtClasses;
4949
6060 }
6161
6262 private Set<JavaClass> getMtClasses() {
63 if (mtClasses != null)
63 if (mtClasses != null) {
6464 return mtClasses;
65 }
6566
6667 mtClasses = new HashSet<JavaClass>();
6768 try {
8384 try {
8485 JavaClass cls = classContext.getJavaClass();
8586 String superClsName = cls.getSuperclassName();
86 if ("java.lang.Object".equals(superClsName))
87 if ("java.lang.Object".equals(superClsName)) {
8788 return;
89 }
8890
8991 if (STRUTS_ACTION_NAME.equals(superClsName)) {
9092 mtClassName = STRUTS_ACTION_NAME;
123125 writingField = false;
124126 }
125127
126
128
127129 @Override
128130 public boolean shouldVisitCode(Code code) {
129 return !getMethodName().equals("<init>") && !getMethodName().equals("init");
130
131 return !"<init>".equals(getMethodName()) && !"init".equals(getMethodName());
132
131133 }
132134
133135 @Override
134136 public void sawField() {
135 if ((monitorCount > 0) || (!writingField))
137 if ((monitorCount > 0) || (!writingField)) {
136138 return;
139 }
137140
138141 ConstantFieldref fieldRef;
139142 Constant c = getConstantRefOperand();
155158 ConstantUtf8 nameCons = (ConstantUtf8) cp.getConstant(nameIdx);
156159 ConstantUtf8 typeCons = (ConstantUtf8) cp.getConstant(ntc.getSignatureIndex());
157160
158 if (alreadyReported.contains(nameCons.getBytes()))
161 if (alreadyReported.contains(nameCons.getBytes())) {
159162 return;
163 }
160164 alreadyReported.add(nameCons.getBytes());
161165 bugReporter.reportBug(new BugInstance(this,
162166 STRUTS_ACTION_NAME.equals(mtClassName) ? "MTIA_SUSPECT_STRUTS_INSTANCE_FIELD"
163167 : "MTIA_SUSPECT_SERVLET_INSTANCE_FIELD", LOW_PRIORITY)
164 .addField(
165 new FieldAnnotation(getDottedClassName(), nameCons.getBytes(), typeCons.getBytes(),
166 false)).addClass(this).addSourceLine(this));
168 .addField(
169 new FieldAnnotation(getDottedClassName(), nameCons.getBytes(), typeCons.getBytes(),
170 false)).addClass(this).addSourceLine(this));
167171 }
168172 break;
169173 }
174178
175179 @Override
176180 public void sawOpcode(int seen) {
177 if (seen == MONITORENTER)
181 if (seen == MONITORENTER) {
178182 monitorCount++;
179 else if (seen == MONITOREXIT)
183 } else if (seen == MONITOREXIT) {
180184 monitorCount--;
185 }
181186
182187 writingField = ((seen == PUTFIELD) || (seen == PUTFIELD_QUICK) || (seen == PUTFIELD_QUICK_W));
183188 }
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2008 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.detect;
20
21 import org.apache.bcel.classfile.Code;
22
23 import edu.umd.cs.findbugs.BugInstance;
24 import edu.umd.cs.findbugs.BugReporter;
25 import edu.umd.cs.findbugs.OpcodeStack.Item;
26 import edu.umd.cs.findbugs.ba.ClassContext;
27 import edu.umd.cs.findbugs.ba.XField;
28 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
29
30 /**
31 * @author Tagir Valeev
32 */
33 public class MutableEnum extends OpcodeStackDetector {
34
35 private final BugReporter reporter;
36 private boolean skip;
37
38 public MutableEnum(BugReporter reporter) {
39 this.reporter = reporter;
40 }
41
42 @Override
43 public void visitClassContext(ClassContext classContext) {
44 if(!classContext.getJavaClass().isEnum() || !classContext.getJavaClass().isPublic()) {
45 return;
46 }
47 boolean hasInterestingField = false;
48 for(XField field : classContext.getXClass().getXFields()) {
49 if(!field.isStatic() && !field.isFinal() && !field.isSynthetic()) {
50 if(field.isPublic()) {
51 reporter.reportBug(new BugInstance("ME_MUTABLE_ENUM_FIELD", NORMAL_PRIORITY).addClass(classContext.getJavaClass())
52 .addField(field));
53 } else {
54 hasInterestingField = true;
55 }
56 }
57 }
58 if(hasInterestingField) {
59 super.visitClassContext(classContext);
60 }
61 }
62
63 @Override
64 public boolean shouldVisitCode(Code obj) {
65 skip = false;
66 if(getXMethod().isPublic() && getNumberMethodArguments() > 0) {
67 return true;
68 }
69 return false;
70 }
71
72 @Override
73 public void sawOpcode(int seen) {
74 if(skip) {
75 return;
76 }
77 if(isBranch(seen) || seen == ATHROW || isReturn(seen)) {
78 skip = true;
79 }
80 if(seen == PUTFIELD) {
81 XField xField = getXFieldOperand();
82 if(xField != null && xField.getClassDescriptor().getClassName().equals(getClassName())) {
83 Item val = getStack().getStackItem(0);
84 if(val.isInitialParameter()) {
85 reporter.reportBug(new BugInstance("ME_ENUM_FIELD_SETTER", NORMAL_PRIORITY).addClassAndMethod(this).addField(xField)
86 .addSourceLine(this));
87 }
88 }
89 }
90 }
91 }
3737
3838 boolean thisOnTOS = false;
3939
40 private BugReporter bugReporter;
40 private final BugReporter bugReporter;
4141
4242 public MutableLock(BugReporter bugReporter) {
4343 this.bugReporter = bugReporter;
5252 @Override
5353 public void visit(Field obj) {
5454 super.visit(obj);
55 if (obj.isFinal())
55 if (obj.isFinal()) {
5656 finalFields.add(obj.getName());
57 }
5758 }
5859
5960 @Override
7475 setFields.clear();
7576 break;
7677 case PUTFIELD:
77 if (getClassConstantOperand().equals(getClassName()))
78 if (getClassConstantOperand().equals(getClassName())) {
7879 setFields.add(getNameConstantOperand());
80 }
7981 break;
8082 case GETFIELD:
8183 if (thisOnTOS && getClassConstantOperand().equals(getClassName()) && setFields.contains(getNameConstantOperand())
8284 && asUnsignedByte(codeBytes[getPC() + 3]) == DUP && asUnsignedByte(codeBytes[getPC() + 5]) == MONITORENTER
8385
84 && !finalFields.contains(getNameConstantOperand()))
86 && !finalFields.contains(getNameConstantOperand())) {
8587 bugReporter.reportBug(new BugInstance(this, "ML_SYNC_ON_UPDATED_FIELD", NORMAL_PRIORITY).addClassAndMethod(this)
8688 .addReferencedField(this).addSourceLine(this, getPC() + 5));
89 }
8790 break;
8891 default:
92 break;
8993 }
9094 thisOnTOS = false;
9195 }
1818
1919 package edu.umd.cs.findbugs.detect;
2020
21 import java.util.Arrays;
22 import java.util.Collections;
2123 import java.util.HashMap;
2224 import java.util.HashSet;
2325 import java.util.LinkedList;
3638 import edu.umd.cs.findbugs.ba.AnalysisContext;
3739 import edu.umd.cs.findbugs.ba.XClass;
3840 import edu.umd.cs.findbugs.ba.XField;
41 import edu.umd.cs.findbugs.ba.XMethod;
3942 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
43 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
4044 import edu.umd.cs.findbugs.classfile.Global;
4145
4246 public class MutableStaticFields extends BytecodeScanningDetector {
47 private static final Set<String> COLLECTION_SUPERCLASSES = new HashSet<>(Arrays.asList("java/util/Collection",
48 "java/util/List", "java/util/Set", "java/util/Map", "java/util/AbstractList", "java/util/SortedSet",
49 "java/util/SortedMap", "java/util/NavigableMap", "java/util/Dictionary"));
50
51 private static final Set<String> MUTABLE_COLLECTION_CLASSES = new HashSet<>(Arrays.asList("java/util/ArrayList",
52 "java/util/HashSet", "java/util/HashMap", "java/util/Hashtable", "java/util/IdentityHashMap",
53 "java/util/LinkedHashSet", "java/util/LinkedList", "java/util/LinkedHashMap", "java/util/TreeSet",
54 "java/util/TreeMap", "java/util/Properties"));
55
56 private static enum AllowedParameter {
57 NONE, EMPTY_ARRAY
58 }
59
60 private static final Map<String, Map<String, AllowedParameter>> MUTABLE_COLLECTION_METHODS = new HashMap<>();
61 static {
62 MUTABLE_COLLECTION_METHODS.put("java/util/Arrays", Collections.singletonMap("asList", AllowedParameter.EMPTY_ARRAY));
63 Map<String, AllowedParameter> listsMap = new HashMap<>();
64 listsMap.put("newArrayList", AllowedParameter.NONE);
65 listsMap.put("newLinkedList", AllowedParameter.NONE);
66 MUTABLE_COLLECTION_METHODS.put("com/google/common/collect/Lists", listsMap);
67 Map<String, AllowedParameter> setsMap = new HashMap<>();
68 setsMap.put("newHashSet", AllowedParameter.NONE);
69 setsMap.put("newTreeSet", AllowedParameter.NONE);
70 MUTABLE_COLLECTION_METHODS.put("com/google/common/collect/Sets", setsMap);
71 }
4372
4473 static String extractPackage(String c) {
4574 int i = c.lastIndexOf('/');
5079 }
5180
5281 static boolean mutableSignature(String sig) {
53 return sig.equals("Ljava/util/Hashtable;") || sig.equals("Ljava/util/Date;") ||
54 sig.equals("Ljava/sql/Date;") ||
55 sig.equals("Ljava/sql/Timestamp;") ||
56 sig.charAt(0) == '[';
82 return sig.equals("Ljava/util/Hashtable;") || sig.equals("Ljava/util/Date;") ||
83 sig.equals("Ljava/sql/Date;") ||
84 sig.equals("Ljava/sql/Timestamp;") ||
85 sig.charAt(0) == '[';
5786 }
5887
5988 LinkedList<XField> seen = new LinkedList<XField>();
6089
6190 boolean publicClass;
6291
92 boolean mutableCollectionJustCreated = false;
93
6394 boolean zeroOnTOS;
6495
6596 boolean emptyArrayOnTOS;
72103
73104 Set<XField> unsafeValue = new HashSet<XField>();
74105
106 Set<XField> mutableCollection = new HashSet<XField>();
107
75108 Set<XField> notFinal = new HashSet<XField>();
76109
77110 Set<XField> outsidePackage = new HashSet<XField>();
111
78112 Set<XField> needsRefactoringToBeFinal = new HashSet<XField>();
79
113
80114 Set<XField> writtenInMethod = new HashSet<XField>();
115
81116 Set<XField> writtenTwiceInMethod = new HashSet<XField>();
82117
83118 Map<XField, SourceLineAnnotation> firstFieldUse = new HashMap<XField, SourceLineAnnotation>();
113148 // System.out.println(methodName);
114149 inStaticInitializer = getMethodName().equals("<clinit>");
115150 }
116
151
117152 @Override
118153 public void visit(Code obj) {
119154 writtenInMethod.clear();
120155 writtenTwiceInMethod.clear();
121156 super.visit(obj);
122 if (inStaticInitializer)
157 if (inStaticInitializer) {
123158 needsRefactoringToBeFinal.addAll(writtenTwiceInMethod);
159 }
124160 writtenInMethod.clear();
125161 writtenTwiceInMethod.clear();
126162 }
140176 break;
141177 }
142178
143 boolean samePackage = packageName.equals(extractPackage(getClassConstantOperand()));
179 boolean samePackage = packageName.equals(extractPackage(xField.getFieldDescriptor().getSlashedClassName()));
144180 boolean initOnly = seen == GETSTATIC || getClassName().equals(getClassConstantOperand()) && inStaticInitializer;
145181 boolean safeValue = seen == GETSTATIC || emptyArrayOnTOS
146182 || AnalysisContext.currentXFactory().isEmptyArrayField(xField) || !mutableSignature(getSigConstantOperand());
149185 readAnywhere.add(xField);
150186 }
151187 if (seen == PUTSTATIC) {
152 if (!writtenInMethod.add(xField))
188 if (xField.isFinal() && mutableCollectionJustCreated) {
189 mutableCollection.add(xField);
190 }
191 if (!writtenInMethod.add(xField)) {
153192 writtenTwiceInMethod.add(xField);
193 }
154194 }
155195
156196 if (!samePackage) {
184224 zeroOnTOS = true;
185225 emptyArrayOnTOS = false;
186226 return;
227 case INVOKESPECIAL:
228 if (inStaticInitializer && "<init>".equals(getMethodDescriptorOperand().getName())) {
229 ClassDescriptor classDescriptor = getClassDescriptorOperand();
230 if (MUTABLE_COLLECTION_CLASSES.contains(classDescriptor.getClassName())) {
231 mutableCollectionJustCreated = true;
232 return;
233 }
234 try {
235 /* Check whether it's statically initialized anonymous class like this:
236 * public static final Map map = new HashMap() {{put("a", "b");}}
237 * We do not check whether all modification methods are overridden or not for simplicity:
238 * Skip if there's at least one method is present
239 */
240 XClass xClass = classDescriptor.getXClass();
241 ClassDescriptor superclassDescriptor = xClass.getSuperclassDescriptor();
242 if (superclassDescriptor != null
243 && MUTABLE_COLLECTION_CLASSES.contains(superclassDescriptor.getClassName())) {
244 mutableCollectionJustCreated = true;
245 for (XMethod xMethod : xClass.getXMethods()) {
246 if (xMethod != null && !"<init>".equals(xMethod.getName()) && !"<clinit>".equals(xMethod.getName())) {
247 mutableCollectionJustCreated = false;
248 break;
249 }
250 }
251 return;
252 }
253 } catch (CheckedAnalysisException e) {
254 // ignore
255 }
256 }
257 break;
258 case INVOKESTATIC:
259 if (inStaticInitializer) {
260 Map<String, AllowedParameter> methods = MUTABLE_COLLECTION_METHODS.get(getMethodDescriptorOperand()
261 .getSlashedClassName());
262 if (methods != null) {
263 String name = getMethodDescriptorOperand().getName();
264 AllowedParameter allowedParameter = methods.get(name);
265 if (allowedParameter == AllowedParameter.NONE
266 || (allowedParameter == AllowedParameter.EMPTY_ARRAY && !emptyArrayOnTOS)) {
267 mutableCollectionJustCreated = true;
268 return;
269 }
270 }
271 }
272 break;
187273 }
188274 zeroOnTOS = false;
189275 emptyArrayOnTOS = false;
276 mutableCollectionJustCreated = false;
277 }
278
279 private boolean isCollection(String signature) {
280 if (signature.startsWith("L") && signature.endsWith(";")) {
281 String fieldClass = signature.substring(1, signature.length() - 1);
282 return COLLECTION_SUPERCLASSES.contains(fieldClass) || MUTABLE_COLLECTION_CLASSES.contains(fieldClass);
283 }
284 return false;
190285 }
191286
192287 private boolean interesting(XField f) {
196291 if (!f.isStatic() || f.isSynthetic() || f.isVolatile()) {
197292 return false;
198293 }
199 boolean isHashtable = f.getSignature().equals("Ljava/util/Hashtable;");
294 if (!f.isFinal()) {
295 return true;
296 }
200297 boolean isArray = f.getSignature().charAt(0) == '[';
201 if (f.isFinal() && !(isArray || isHashtable)) {
298 if (!(isArray || isCollection(f.getSignature()))) {
202299 return false;
203300 }
204301 return true;
224321 return;
225322 }
226323
227 boolean isHashtable = getFieldSig().equals("Ljava/util/Hashtable;");
228324 boolean isArray = getFieldSig().charAt(0) == '[';
229325
230 if (isFinal && !(isHashtable || isArray)) {
326 if (isFinal && !(isArray || isCollection(getFieldSig()))) {
231327 return;
232328 }
233329 if (isEclipseNLS && getFieldSig().equals("Ljava/lang/String;")) {
249345 String fieldSig = f.getSignature();
250346 String fieldName = f.getName();
251347 boolean couldBeFinal = !isFinal && !notFinal.contains(f);
252 boolean isPublic = f.isPublic();
348 // boolean isPublic = f.isPublic();
253349 boolean couldBePackage = !outsidePackage.contains(f);
350 boolean isMutableCollection = mutableCollection.contains(f);
254351 boolean movedOutofInterface = false;
255352
256353 try {
262359 boolean isHashtable = fieldSig.equals("Ljava/util/Hashtable;");
263360 boolean isArray = fieldSig.charAt(0) == '[' && unsafeValue.contains(f);
264361 boolean isReadAnywhere = readAnywhere.contains(f);
265 if (false) {
266 System.out.println(className + "." + fieldName + " : " + fieldSig + "\t" + isHashtable + "\t" + isArray);
267 }
362 // if (false) {
363 // System.out.println(className + "." + fieldName + " : " + fieldSig + "\t" + isHashtable + "\t" + isArray);
364 // }
268365
269366 String bugType;
270367 int priority = NORMAL_PRIORITY;
271 if (isFinal && !isHashtable && !isArray) {
368 if (isFinal && !isHashtable && !isArray && !isMutableCollection) {
272369 continue;
273370 } else if (movedOutofInterface) {
274371 bugType = "MS_OOI_PKGPROTECT";
276373 bugType = "MS_FINAL_PKGPROTECT";
277374 } else if (couldBeFinal && !isHashtable && !isArray) {
278375 bugType = "MS_SHOULD_BE_FINAL";
279 if (needsRefactoringToBeFinal.contains(f))
376 if (needsRefactoringToBeFinal.contains(f)) {
280377 bugType = "MS_SHOULD_BE_REFACTORED_TO_BE_FINAL";
378 }
281379 if (fieldName.equals(fieldName.toUpperCase()) || fieldSig.charAt(0) == 'L') {
282380 priority = HIGH_PRIORITY;
283381 }
284382 } else if (couldBePackage) {
285 bugType = "MS_PKGPROTECT";
383 bugType = isMutableCollection ? "MS_MUTABLE_COLLECTION_PKGPROTECT" : "MS_PKGPROTECT";
286384 } else if (isHashtable) {
287385 bugType = "MS_MUTABLE_HASHTABLE";
288386 if (!isFinal) {
290388 }
291389 } else if (isArray) {
292390 bugType = "MS_MUTABLE_ARRAY";
293 if (fieldSig.indexOf("L") >= 0 || !isFinal) {
391 if (fieldSig.indexOf('L') >= 0 || !isFinal) {
294392 priority = HIGH_PRIORITY;
295393 }
394 } else if (isMutableCollection) {
395 bugType = "MS_MUTABLE_COLLECTION";
396 priority = HIGH_PRIORITY;
296397 } else if (!isFinal) {
297398 bugType = "MS_CANNOT_BE_FINAL";
298399 } else {
6060 import edu.umd.cs.findbugs.visitclass.PreorderVisitor;
6161
6262 public class Naming extends PreorderVisitor implements Detector {
63
6364 public static class NamingProperty extends AbstractWarningProperty {
65
6466 private NamingProperty(String name, PriorityAdjustment priorityAdjustment) {
6567 super(name, priorityAdjustment);
6668 }
7072
7173 public static final NamingProperty METHOD_IS_DEPRECATED = new NamingProperty("CONFUSING_METHOD_IS_DEPRECATED",
7274 PriorityAdjustment.LOWER_PRIORITY);
73
7475 }
7576
7677 String baseClassName;
7980
8081 public static @CheckForNull
8182 XMethod definedIn(JavaClass clazz, XMethod m) {
82 for (Method m2 : clazz.getMethods())
83 if (m.getName().equals(m2.getName()) && m.getSignature().equals(m2.getSignature()) && m.isStatic() == m2.isStatic())
83 for (Method m2 : clazz.getMethods()) {
84 if (m.getName().equals(m2.getName()) && m.getSignature().equals(m2.getSignature()) && m.isStatic() == m2.isStatic()) {
8485 return XFactory.createXMethod(clazz, m2);
86 }
87 }
8588 return null;
8689 }
8790
8891 public static boolean confusingMethodNamesWrongCapitalization(XMethod m1, XMethod m2) {
89 if (m1.isStatic() != m2.isStatic())
90 return false;
91 if (m1.getClassName().equals(m2.getClassName()))
92 return false;
93 if (m1.getName().equals(m2.getName()))
94 return false;
92 if (m1.isStatic() != m2.isStatic()) {
93 return false;
94 }
95 if (m1.getClassName().equals(m2.getClassName())) {
96 return false;
97 }
98 if (m1.getName().equals(m2.getName())) {
99 return false;
100 }
95101 if (m1.getName().equalsIgnoreCase(m2.getName())
96 && removePackageNamesFromSignature(m1.getSignature()).equals(removePackageNamesFromSignature(m2.getSignature())))
102 && removePackageNamesFromSignature(m1.getSignature()).equals(removePackageNamesFromSignature(m2.getSignature()))) {
97103 return true;
104 }
98105 return false;
99106 }
100107
101108 public static boolean confusingMethodNamesWrongPackage(XMethod m1, XMethod m2) {
102 if (m1.isStatic() != m2.isStatic())
103 return false;
104 if (m1.getClassName().equals(m2.getClassName()))
105 return false;
106
107 if (!m1.getName().equals(m2.getName()))
108 return false;
109 if (m1.getSignature().equals(m2.getSignature()))
110 return false;
111 if (removePackageNamesFromSignature(m1.getSignature()).equals(removePackageNamesFromSignature(m2.getSignature())))
109 if (m1.isStatic() != m2.isStatic()) {
110 return false;
111 }
112 if (m1.getClassName().equals(m2.getClassName())) {
113 return false;
114 }
115
116 if (!m1.getName().equals(m2.getName())) {
117 return false;
118 }
119 if (m1.getSignature().equals(m2.getSignature())) {
120 return false;
121 }
122 if (removePackageNamesFromSignature(m1.getSignature()).equals(removePackageNamesFromSignature(m2.getSignature()))) {
112123 return true;
124 }
113125 return false;
114126 }
115127
118130
119131 HashSet<String> visited = new HashSet<String>();
120132
121 private BugReporter bugReporter;
133 private final BugReporter bugReporter;
122134
123135 public Naming(BugReporter bugReporter) {
124136 this.bugReporter = bugReporter;
125137 }
126138
139 @Override
127140 public void visitClassContext(ClassContext classContext) {
128141 classContext.getJavaClass().accept(this);
129142 }
130143
131144 private boolean checkSuper(XMethod m, Set<XMethod> others) {
132 if (m.isStatic())
133 return false;
134 if (m.getName().equals("<init>") || m.getName().equals("<clinit>"))
135 return false;
145 if (m.isStatic()) {
146 return false;
147 }
148 if ("<init>".equals(m.getName()) || "<clinit>".equals(m.getName())) {
149 return false;
150 }
136151 for (XMethod m2 : others) {
137152 try {
138153 if ((confusingMethodNamesWrongCapitalization(m, m2) || confusingMethodNamesWrongPackage(m, m2))
156171 }
157172
158173 XFactory xFactory = AnalysisContext.currentXFactory();
159 if (m3 == null && xFactory.isCalled(m))
174 if (m3 == null && xFactory.isCalled(m)) {
160175 propertySet.addProperty(NamingProperty.METHOD_IS_CALLED);
161 else if (m.isDeprecated() || m2.isDeprecated())
176 } else if (m.isDeprecated() || m2.isDeprecated()) {
162177 propertySet.addProperty(NamingProperty.METHOD_IS_DEPRECATED);
178 }
163179
164180 if (!m.getName().equals(m2.getName()) && m.getName().equalsIgnoreCase(m2.getName())) {
165181 String pattern = intentional ? "NM_VERY_CONFUSING_INTENTIONAL" : "NM_VERY_CONFUSING";
166182 Set<XMethod> overrides = Hierarchy2.findSuperMethods(m);
167183 if (!overrides.isEmpty()) {
168 if (intentional || allAbstract(overrides))
184 if (intentional || allAbstract(overrides)) {
169185 break;
186 }
170187 priority++;
171188 }
172189 BugInstance bug = new BugInstance(this, pattern, priority).addClass(m.getClassName()).addMethod(m)
173190 .addClass(m2.getClassName()).describe(ClassAnnotation.SUPERCLASS_ROLE).addMethod(m2)
174191 .describe(MethodAnnotation.METHOD_DID_YOU_MEAN_TO_OVERRIDE);
175 if (m3 != null)
192 if (m3 != null) {
176193 bug.addMethod(m3).describe(MethodAnnotation.METHOD_OVERRIDDEN);
194 }
177195 propertySet.decorateBugInstance(bug);
178196 bugReporter.reportBug(bug);
179197 } else if (!m.getSignature().equals(m2.getSignature())
182200 String pattern = intentional ? "NM_WRONG_PACKAGE_INTENTIONAL" : "NM_WRONG_PACKAGE";
183201 Set<XMethod> overrides = Hierarchy2.findSuperMethods(m);
184202 if (!overrides.isEmpty()) {
185 if (intentional || allAbstract(overrides))
203 if (intentional || allAbstract(overrides)) {
186204 break;
205 }
187206 priority++;
188207 }
189208 Iterator<String> s = new SignatureParser(m.getSignature()).parameterSignatureIterator();
196215 .addMethod(m).addClass(m2.getClassName()).describe(ClassAnnotation.SUPERCLASS_ROLE)
197216 .addMethod(m2).describe(MethodAnnotation.METHOD_DID_YOU_MEAN_TO_OVERRIDE)
198217 .addFoundAndExpectedType(p, p2);
199 if (m3 != null)
218 if (m3 != null) {
200219 bug.addMethod(m3).describe(MethodAnnotation.METHOD_OVERRIDDEN);
220 }
201221 propertySet.decorateBugInstance(bug);
202222 bugReporter.reportBug(bug);
203223
215235 return false;
216236 }
217237
218 /**
219 * @param overrides
220 * @return
221 */
222238 private boolean allAbstract(Set<XMethod> overrides) {
223239 boolean allAbstract = true;
224240 for (XMethod m4 : overrides) {
225 if (!m4.isAbstract())
241 if (!m4.isAbstract()) {
226242 allAbstract = false;
243 }
227244 }
228245 return allAbstract;
229246 }
230247
231 @SuppressWarnings("unchecked")
232248 private boolean checkNonSuper(XMethod m, Set<XMethod> others) {
233 if (m.isStatic())
234 return false;
235 if (m.getName().startsWith("<init>") || m.getName().startsWith("<clinit>"))
236 return false;
249 if (m.isStatic()) {
250 return false;
251 }
252 if (m.getName().startsWith("<init>") || m.getName().startsWith("<clinit>")) {
253 return false;
254 }
237255 for (XMethod m2 : others) {
238256 if (confusingMethodNamesWrongCapitalization(m, m2)) {
239 XMethod mm1 = m;
240 XMethod mm2 = m2;
257 XMethod mm1;
258 XMethod mm2;
241259 if (m.compareTo(m2) < 0) {
242260 mm1 = m;
243261 mm2 = m2;
253271 return false;
254272 }
255273
274 @Override
256275 public void report() {
257276
258277 for (Map.Entry<String, TreeSet<XMethod>> e : canonicalToXMethod.entrySet()) {
259278 TreeSet<XMethod> conflictingMethods = e.getValue();
260279 HashSet<String> trueNames = new HashSet<String>();
261280
262 for (XMethod m : conflictingMethods)
281 for (XMethod m : conflictingMethods) {
263282 trueNames.add(m.getName() + m.getSignature());
264 if (trueNames.size() <= 1)
283 }
284 if (trueNames.size() <= 1) {
265285 continue;
286 }
266287 for (Iterator<XMethod> j = conflictingMethods.iterator(); j.hasNext();) {
267 if (checkSuper(j.next(), conflictingMethods))
288 if (checkSuper(j.next(), conflictingMethods)) {
268289 j.remove();
290 }
269291 }
270292 for (XMethod conflictingMethod : conflictingMethods) {
271 if (checkNonSuper(conflictingMethod, conflictingMethods))
293 if (checkNonSuper(conflictingMethod, conflictingMethods)) {
272294 break;
295 }
273296 }
274297 }
275298 }
276299
277300 public String stripPackageName(String className) {
278 if (className.indexOf('.') >= 0)
301 if (className.indexOf('.') >= 0) {
279302 return className.substring(className.lastIndexOf('.') + 1);
280 else if (className.indexOf('/') >= 0)
303 } else if (className.indexOf('/') >= 0) {
281304 return className.substring(className.lastIndexOf('/') + 1);
282 else
305 } else {
283306 return className;
307 }
284308 }
285309
286310 public boolean sameSimpleName(String class1, String class2) {
289313
290314 @Override
291315 public void visitJavaClass(JavaClass obj) {
292 if (BCELUtil.isSynthetic(obj))
293 return;
316 if (BCELUtil.isSynthetic(obj)) {
317 return;
318 }
294319 String name = obj.getClassName();
295 if (!visited.add(name))
296 return;
320 if (!visited.add(name)) {
321 return;
322 }
297323
298324 String superClassName = obj.getSuperclassName();
299 if (!name.equals("java.lang.Object")) {
325 if (!"java.lang.Object".equals(name)) {
300326 if (sameSimpleName(superClassName, name)) {
301327 bugReporter.reportBug(new BugInstance(this, "NM_SAME_SIMPLE_NAME_AS_SUPERCLASS", HIGH_PRIORITY).addClass(name)
302328 .addClass(superClassName));
303329 }
304 for (String interfaceName : obj.getInterfaceNames())
330 for (String interfaceName : obj.getInterfaceNames()) {
305331 if (sameSimpleName(interfaceName, name)) {
306332 bugReporter.reportBug(new BugInstance(this, "NM_SAME_SIMPLE_NAME_AS_INTERFACE", NORMAL_PRIORITY).addClass(
307333 name).addClass(interfaceName));
308334 }
309 }
310 if (obj.isInterface())
311 return;
312
313 if (superClassName.equals("java.lang.Object") && !visited.contains(superClassName))
335 }
336 }
337 if (obj.isInterface()) {
338 return;
339 }
340
341 if ("java.lang.Object".equals(superClassName) && !visited.contains(superClassName)) {
314342 try {
315343 visitJavaClass(obj.getSuperClass());
316344 } catch (ClassNotFoundException e) {
317345 // ignore it
318346 }
347 }
319348 super.visitJavaClass(obj);
320349 }
321350
322351 /**
323352 * Determine whether the class descriptor ultimately inherits from
324353 * java.lang.Exception
325 *
354 *
326355 * @param d
327356 * class descriptor we want to check
328357 * @return true iff the descriptor ultimately inherits from Exception
353382 * message constants. Unfortunately these fields often has bad names which
354383 * does not follow Java code convention, so FB reports tons of warnings for
355384 * such Eclipse message fields.
356 *
385 *
357386 * @see edu.umd.cs.findbugs.detect.MutableStaticFields
358387 */
359388 private boolean isEclipseNLS;
363392 String name = obj.getClassName();
364393 String[] parts = name.split("[$+.]");
365394 baseClassName = parts[parts.length - 1];
366 for (String p : name.split("[.]"))
367 if (p.length() == 1)
395 for (String p : name.split("[.]")) {
396 if (p.length() == 1) {
368397 return;
369 if (name.indexOf("Proto$") >= 0)
370 return;
398 }
399 }
400 if (name.indexOf("Proto$") >= 0) {
401 return;
402 }
371403 classIsPublicOrProtected = obj.isPublic() || obj.isProtected();
372404 if (Character.isLetter(baseClassName.charAt(0)) && !Character.isUpperCase(baseClassName.charAt(0))
373 && baseClassName.indexOf("_") == -1) {
405 && baseClassName.indexOf('_') == -1) {
374406 int priority = classIsPublicOrProtected ? NORMAL_PRIORITY : LOW_PRIORITY;
375407
376408 bugReporter.reportBug(new BugInstance(this, "NM_CLASS_NAMING_CONVENTION", priority).addClass(this));
384416 }
385417
386418 int badFieldNames = 0;
387 for (Field f : obj.getFields())
388 if (f.getName().length() >= 2 && badFieldName(f))
419 for (Field f : obj.getFields()) {
420 if (f.getName().length() >= 2 && badFieldName(f)) {
389421 badFieldNames++;
422 }
423 }
390424 hasBadFieldNames = badFieldNames > 3 && badFieldNames > obj.getFields().length / 3;
391425 int badMethodNames = 0;
392 for (Method m : obj.getMethods())
393 if (badMethodName(m.getName()))
426 for (Method m : obj.getMethods()) {
427 if (badMethodName(m.getName())) {
394428 badMethodNames++;
429 }
430 }
395431 hasBadMethodNames = badMethodNames > 3 && badMethodNames > obj.getMethods().length / 3;
396432 isEclipseNLS = "org.eclipse.osgi.util.NLS".equals(obj.getSuperclassName());
397433 super.visit(obj);
399435
400436 @Override
401437 public void visit(Field obj) {
402 if (getFieldName().length() == 1)
403 return;
438 if (getFieldName().length() == 1) {
439 return;
440 }
404441
405442 if (isEclipseNLS) {
406443 int flags = obj.getAccessFlags();
407 if ((flags & ACC_STATIC) != 0 && ((flags & ACC_PUBLIC) != 0) && getFieldSig().equals("Ljava/lang/String;")) {
444 if ((flags & ACC_STATIC) != 0 && ((flags & ACC_PUBLIC) != 0) && "Ljava/lang/String;".equals(getFieldSig())) {
408445 // ignore "public statis String InstallIUCommandTooltip;"
409446 // messages from Eclipse NLS bundles
410447 return;
413450 if (badFieldName(obj)) {
414451 bugReporter.reportBug(new BugInstance(this, "NM_FIELD_NAMING_CONVENTION", classIsPublicOrProtected
415452 && (obj.isPublic() || obj.isProtected()) && !hasBadFieldNames ? NORMAL_PRIORITY : LOW_PRIORITY)
416 .addClass(this).addVisitedField(this));
417 }
418 }
419
420 /**
421 * @param obj
422 * @return
423 */
453 .addClass(this).addVisitedField(this));
454 }
455 }
456
424457 private boolean badFieldName(Field obj) {
425458 String fieldName = obj.getName();
426459 return !obj.isFinal() && Character.isLetter(fieldName.charAt(0)) && !Character.isLowerCase(fieldName.charAt(0))
427 && fieldName.indexOf("_") == -1 && Character.isLetter(fieldName.charAt(1))
460 && fieldName.indexOf('_') == -1 && Character.isLetter(fieldName.charAt(1))
428461 && Character.isLowerCase(fieldName.charAt(1));
429462 }
430463
431464 private final static Pattern sigType = Pattern.compile("L([^;]*/)?([^/]+;)");
432465
433
466
434467
435468 private static @CheckForNull
436469 String getSignatureOfOuterClass(JavaClass obj) {
437 for (Field f : obj.getFields())
438 if (f.getName().startsWith("this$"))
470 for (Field f : obj.getFields()) {
471 if (f.getName().startsWith("this$")) {
439472 return f.getSignature();
473 }
474 }
440475 return null;
441476 }
442477
443478 private boolean markedAsNotUsable(Method obj) {
444 for (Attribute a : obj.getAttributes())
445 if (a instanceof Deprecated)
479 for (Attribute a : obj.getAttributes()) {
480 if (a instanceof Deprecated) {
446481 return true;
482 }
483 }
447484 Code code = obj.getCode();
448 if (code == null)
449 return false;
485 if (code == null) {
486 return false;
487 }
450488 byte[] codeBytes = code.getCode();
451489 if (codeBytes.length > 1 && codeBytes.length < 10) {
452490 int lastOpcode = codeBytes[codeBytes.length - 1] & 0xff;
453 if (lastOpcode != ATHROW)
491 if (lastOpcode != ATHROW) {
454492 return false;
455 for (int b : codeBytes)
456 if ((b & 0xff) == RETURN)
493 }
494 for (int b : codeBytes) {
495 if ((b & 0xff) == RETURN) {
457496 return false;
497 }
498 }
458499 return true;
459500 }
460501 return false;
462503
463504 private static @CheckForNull
464505 Method findVoidConstructor(JavaClass clazz) {
465 for (Method m : clazz.getMethods())
466 if (isVoidConstructor(clazz, m))
506 for (Method m : clazz.getMethods()) {
507 if (isVoidConstructor(clazz, m)) {
467508 return m;
509 }
510 }
468511 return null;
469512
470513 }
472515 @Override
473516 public void visit(Method obj) {
474517 String mName = getMethodName();
475 if (mName.length() == 1)
476 return;
477 if (mName.equals("isRequestedSessionIdFromURL") || mName.equals("isRequestedSessionIdFromUrl"))
478 return;
518 if (mName.length() == 1) {
519 return;
520 }
521 if ("isRequestedSessionIdFromURL".equals(mName) || "isRequestedSessionIdFromUrl".equals(mName)) {
522 return;
523 }
479524 String sig = getMethodSig();
480 if (mName.equals(baseClassName) && sig.equals("()V")) {
525 if (mName.equals(baseClassName) && "()V".equals(sig)) {
481526 Code code = obj.getCode();
482527 Method realVoidConstructor = findVoidConstructor(getThisClass());
483528 if (code != null && !markedAsNotUsable(obj)) {
484529 int priority = NORMAL_PRIORITY;
485 if (codeDoesSomething(code))
530 if (codeDoesSomething(code)) {
486531 priority--;
487 else if (!obj.isPublic() && getThisClass().isPublic())
532 } else if (!obj.isPublic() && getThisClass().isPublic()) {
488533 priority--;
534 }
489535 boolean instanceMembers = false;
490 for (Method m : this.getThisClass().getMethods())
491 if (!m.isStatic() && m != obj && !isVoidConstructor(getThisClass(), m))
536 for (Method m : this.getThisClass().getMethods()) {
537 if (!m.isStatic() && m != obj && !isVoidConstructor(getThisClass(), m)) {
492538 instanceMembers = true;
493 for (Field f : this.getThisClass().getFields())
494 if (!f.isStatic())
539 }
540 }
541 for (Field f : this.getThisClass().getFields()) {
542 if (!f.isStatic()) {
495543 instanceMembers = true;
496 if (!codeDoesSomething(code) && !instanceMembers && getSuperclassName().equals("java/lang/Object"))
544 }
545 }
546 if (!codeDoesSomething(code) && !instanceMembers && "java/lang/Object".equals(getSuperclassName())) {
497547 priority += 2;
498 if (hasBadMethodNames)
548 }
549 if (hasBadMethodNames) {
499550 priority++;
500 if (!getXClass().getAnnotations().isEmpty())
551 }
552 if (!getXClass().getAnnotations().isEmpty()) {
501553 priority++;
502 if (realVoidConstructor != null)
554 }
555 if (realVoidConstructor != null) {
503556 priority = LOW_PRIORITY;
557 }
504558
505559 bugReporter.reportBug(new BugInstance(this, "NM_METHOD_CONSTRUCTOR_CONFUSION", priority).addClassAndMethod(this)
506560 .lowerPriorityIfDeprecated());
507561 return;
508562 }
509 } else if (badMethodName(mName))
563 } else if (badMethodName(mName)) {
510564 bugReporter.reportBug(new BugInstance(this, "NM_METHOD_NAMING_CONVENTION", classIsPublicOrProtected
511565 && (obj.isPublic() || obj.isProtected()) && !hasBadMethodNames ? NORMAL_PRIORITY : LOW_PRIORITY)
512 .addClassAndMethod(this));
513
514 if (obj.isAbstract())
515 return;
516 if (obj.isPrivate())
517 return;
518
519 if (mName.equals("equal") && sig.equals("(Ljava/lang/Object;)Z")) {
566 .addClassAndMethod(this));
567 }
568
569 if (obj.isAbstract()) {
570 return;
571 }
572 if (obj.isPrivate()) {
573 return;
574 }
575
576 if ("equal".equals(mName) && "(Ljava/lang/Object;)Z".equals(sig)) {
520577 bugReporter.reportBug(new BugInstance(this, "NM_BAD_EQUAL", HIGH_PRIORITY).addClassAndMethod(this)
521578 .lowerPriorityIfDeprecated());
522579 return;
523580 }
524 if (mName.equals("hashcode") && sig.equals("()I")) {
581 if ("hashcode".equals(mName) && "()I".equals(sig)) {
525582 bugReporter.reportBug(new BugInstance(this, "NM_LCASE_HASHCODE", HIGH_PRIORITY).addClassAndMethod(this)
526583 .lowerPriorityIfDeprecated());
527584 return;
528585 }
529 if (mName.equals("tostring") && sig.equals("()Ljava/lang/String;")) {
586 if ("tostring".equals(mName) && "()Ljava/lang/String;".equals(sig)) {
530587 bugReporter.reportBug(new BugInstance(this, "NM_LCASE_TOSTRING", HIGH_PRIORITY).addClassAndMethod(this)
531588 .lowerPriorityIfDeprecated());
532589 return;
533590 }
534591
535 if (obj.isPrivate() || obj.isStatic() || mName.equals("<init>"))
536 return;
592 if (obj.isPrivate() || obj.isStatic() || "<init>".equals(mName)) {
593 return;
594 }
537595
538596 String sig2 = removePackageNamesFromSignature(sig);
539597 String allSmall = mName.toLowerCase() + sig2;
552610
553611 private static boolean isVoidConstructor(JavaClass clazz, Method m) {
554612 String outerClassSignature = getSignatureOfOuterClass(clazz);
555 if (outerClassSignature == null)
613 if (outerClassSignature == null) {
556614 outerClassSignature = "";
557 return m.getName().equals("<init>") && m.getSignature().equals("(" + outerClassSignature + ")V");
558 }
559
560 /**
561 * @param mName
562 * @return
563 */
615 }
616 return "<init>".equals(m.getName()) && m.getSignature().equals("(" + outerClassSignature + ")V");
617 }
618
564619 private boolean badMethodName(String mName) {
565620 return mName.length() >= 2 && Character.isLetter(mName.charAt(0)) && !Character.isLowerCase(mName.charAt(0))
566 && Character.isLetter(mName.charAt(1)) && Character.isLowerCase(mName.charAt(1)) && mName.indexOf("_") == -1;
621 && Character.isLetter(mName.charAt(1)) && Character.isLowerCase(mName.charAt(1)) && mName.indexOf('_') == -1;
567622 }
568623
569624 private boolean codeDoesSomething(Code code) {
572627 }
573628
574629 private static String removePackageNamesFromSignature(String sig) {
575 int end = sig.indexOf(")");
630 int end = sig.indexOf(')');
576631 Matcher m = sigType.matcher(sig.substring(0, end));
577632 return m.replaceAll("L$2") + sig.substring(end);
578633 }
5050 // data is next..size-1, 0..next-1
5151 public void push(byte b) {
5252 data[next++] = b;
53 if (next == size)
53 if (next == size) {
5454 next = 0;
55 }
5556 }
5657
5758 public void reset() {
5859 next = 0;
59 for (int i = 0; i < size; i++)
60 for (int i = 0; i < size; i++) {
6061 data[i] = 0;
62 }
6163 }
6264
6365 public void push(String s) {
64 for (byte b : UTF8.getBytes(s))
66 for (byte b : UTF8.getBytes(s)) {
6567 push(b);
68 }
6669 }
6770
6871 public void pushHash(Object x) {
9093
9194 if ((hash & 0x1ff0) == 0) {
9295 hash = hash & 0xf;
93 if (hash < 1)
96 if (hash < 1) {
9497 return Priorities.HIGH_PRIORITY;
95 else if (hash < 1 + 2)
98 } else if (hash < 1 + 2) {
9699 return Priorities.NORMAL_PRIORITY;
97 else if (hash < 1 + 2 + 4)
100 } else if (hash < 1 + 2 + 4) {
98101 return Priorities.LOW_PRIORITY;
99 else
102 } else {
100103 return Priorities.IGNORE_PRIORITY;
101 } else
104 }
105 } else {
102106 return Priorities.IGNORE_PRIORITY + 1;
103 }
104 }
105
106 final BugReporter bugReporter;
107 }
108 }
109 }
110
111 // final BugReporter bugReporter;
107112
108113 final BugAccumulator accumulator;
109114
112117 byte[] primer;
113118
114119 public Noise(BugReporter bugReporter) throws NoSuchAlgorithmException {
115 this.bugReporter = bugReporter;
120 // this.bugReporter = bugReporter;
116121 this.accumulator = new BugAccumulator(bugReporter);
117122 hq = new HashQueue(24);
118123 }
145150 public void sawClass() {
146151 hq.push(getClassConstantOperand());
147152 }
148
149 /*
150 * (non-Javadoc)
151 *
152 * @see edu.umd.cs.findbugs.bcel.OpcodeStackDetector#sawOpcode(int)
153 */
154153
155154 @Override
156155 public void sawOpcode(int seen) {
161160 case INVOKESPECIAL:
162161 case INVOKESTATIC:
163162 hq.pushHash(getClassConstantOperand());
164 if (getNameConstantOperand().indexOf('$') == -1)
163 if (getNameConstantOperand().indexOf('$') == -1) {
165164 hq.pushHash(getNameConstantOperand());
165 }
166166 hq.pushHash(getSigConstantOperand());
167167
168168 priority = hq.getPriority();
169 if (priority <= Priorities.LOW_PRIORITY)
169 if (priority <= Priorities.LOW_PRIORITY) {
170170 accumulator.accumulateBug(new BugInstance(this, "NOISE_METHOD_CALL", priority).addClassAndMethod(this)
171171 .addCalledMethod(this), this);
172
172 }
173173 break;
174174 case GETFIELD:
175175 case PUTFIELD:
176176 case GETSTATIC:
177177 case PUTSTATIC:
178178 hq.pushHash(getClassConstantOperand());
179 if (getNameConstantOperand().indexOf('$') == -1)
179 if (getNameConstantOperand().indexOf('$') == -1) {
180180 hq.pushHash(getNameConstantOperand());
181 }
181182 hq.pushHash(getSigConstantOperand());
182183 priority = hq.getPriority();
183 if (priority <= Priorities.LOW_PRIORITY)
184 if (priority <= Priorities.LOW_PRIORITY) {
184185 accumulator.accumulateBug(new BugInstance(this, "NOISE_FIELD_REFERENCE", priority).addClassAndMethod(this)
185186 .addReferencedField(this), this);
187 }
186188 break;
187189 case CHECKCAST:
188190 case INSTANCEOF:
240242 case BASTORE:
241243 hq.push(seen);
242244 priority = hq.getPriority();
243 if (priority <= Priorities.LOW_PRIORITY)
245 if (priority <= Priorities.LOW_PRIORITY) {
244246 accumulator.accumulateBug(
245247 new BugInstance(this, "NOISE_OPERATION", priority).addClassAndMethod(this).addString(OPCODE_NAMES[seen]),
246248 this);
249 }
250 break;
251 default:
252 break;
247253 }
248254 }
249255
8989 * A Detector to find instructions where a NullPointerException might be raised.
9090 * We also look for useless reference comparisons involving null and non-null
9191 * values.
92 *
92 *
9393 * @author David Hovemeyer
9494 * @author William Pugh
9595 * @see edu.umd.cs.findbugs.ba.npe.IsNullValueAnalysis
100100
101101 private static final boolean DEBUG_NULLARG = SystemProperties.getBoolean("fnd.debug.nullarg");
102102
103 private static final boolean DEBUG_NULLRETURN = SystemProperties.getBoolean("fnd.debug.nullreturn");
103 // private static final boolean DEBUG_NULLRETURN = SystemProperties.getBoolean("fnd.debug.nullreturn");
104104
105105 private static final boolean MARK_DOOMED = SystemProperties.getBoolean("fnd.markdoomed", true);
106106
125125 this.bugAccumulator = new BugAccumulator(bugReporter);
126126 }
127127
128 @Override
128129 public void visitClassContext(ClassContext classContext) {
129130 this.classContext = classContext;
130131
133134 JavaClass jclass = classContext.getJavaClass();
134135 String className = jclass.getClassName();
135136 String superClassName = jclass.getSuperclassName();
136 if (superClassName.endsWith("ProtocolMessage"))
137 return;
138 if (CLASS != null && !className.equals(CLASS))
139 return;
137 if (superClassName.endsWith("ProtocolMessage")) {
138 return;
139 }
140 if (CLASS != null && !className.equals(CLASS)) {
141 return;
142 }
140143 Method[] methodList = jclass.getMethods();
141144 for (Method method : methodList) {
142145 try {
143 if (method.isAbstract() || method.isNative() || method.getCode() == null)
146 if (method.isAbstract() || method.isNative() || method.getCode() == null) {
144147 continue;
148 }
145149
146150 currentMethod = SignatureConverter.convertMethodSignature(jclass, method);
147151
148 if (METHOD != null && !method.getName().equals(METHOD))
152 if (METHOD != null && !method.getName().equals(METHOD)) {
149153 continue;
150 if (DEBUG || DEBUG_NULLARG)
154 }
155 if (DEBUG || DEBUG_NULLARG) {
151156 System.out.println("Checking for NP in " + currentMethod);
157 }
152158 analyzeMethod(classContext, method);
153159 } catch (MissingClassException e) {
154160 bugReporter.reportMissingClass(e.getClassNotFoundException());
161167 }
162168 }
163169
164 private void analyzeMethod(ClassContext classContext, Method method) throws DataflowAnalysisException, CFGBuilderException
165
166 {
167 if (DEBUG || DEBUG_NULLARG)
170 private void analyzeMethod(ClassContext classContext, Method method) throws DataflowAnalysisException, CFGBuilderException {
171 if (DEBUG || DEBUG_NULLARG) {
168172 System.out.println("Pre FND ");
173 }
169174
170175 MethodGen methodGen = classContext.getMethodGen(method);
171 if (methodGen == null)
172 return;
176 if (methodGen == null) {
177 return;
178 }
173179
174180 // UsagesRequiringNonNullValues uses =
175181 // classContext.getUsagesRequiringNonNullValues(method);
176182 this.method = method;
177183
178 if (DEBUG || DEBUG_NULLARG)
184 if (DEBUG || DEBUG_NULLARG) {
179185 System.out.println("FND: " + SignatureConverter.convertMethodSignature(methodGen));
186 }
180187
181188 findPreviouslyDeadBlocks();
182189
196203 /**
197204 * Find set of blocks which were known to be dead before doing the null
198205 * pointer analysis.
199 *
206 *
200207 * @return set of previously dead blocks, indexed by block id
201208 * @throws CFGBuilderException
202209 * @throws DataflowAnalysisException
218225 static class CheckCallSitesAndReturnInstructions {
219226 }
220227
228 @Override
221229 public void report() {
222230 }
223231
231239 * {@link #foundNullDeref(Location,ValueNumber,IsNullValue,ValueNumberFrame,boolean)}
232240 * instead
233241 */
242 @Override
234243 @Deprecated
235244 public void foundNullDeref(Location location, ValueNumber valueNumber, IsNullValue refValue, ValueNumberFrame vnaFrame) {
236245 foundNullDeref(location, valueNumber, refValue, vnaFrame, true);
237246 }
238247
248 @Override
239249 public void foundNullDeref(Location location, ValueNumber valueNumber, IsNullValue refValue, ValueNumberFrame vnaFrame,
240250 boolean isConsistent) {
241 if (!refValue.isNullOnComplicatedPath23())
242 return;
251 if (!refValue.isNullOnComplicatedPath23()) {
252 return;
253 }
243254 WarningPropertySet<WarningProperty> propertySet = new WarningPropertySet<WarningProperty>();
244 if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT))
245 return;
255 if (valueNumber.hasFlag(ValueNumber.CONSTANT_CLASS_OBJECT)) {
256 return;
257 }
246258
247259 boolean onExceptionPath = refValue.isException();
248260 if (onExceptionPath) {
261273 cause = MethodAnnotation.fromXMethod(invokedMethod);
262274 cause.setDescription(MethodAnnotation.METHOD_CALLED);
263275
264 if (iins.getMethodName(cpg).equals("close") && iins.getSignature(cpg).equals("()V"))
276 if ("close".equals(iins.getMethodName(cpg)) && "()V".equals(iins.getSignature(cpg))) {
265277 propertySet.addProperty(NullDerefProperty.CLOSING_NULL);
278 }
266279 } else if (ins instanceof FieldInstruction) {
267280 FieldInstruction fins = (FieldInstruction) ins;
268281 XField referencedField = XFactory.createXField(fins, cpg);
273286 }
274287
275288 boolean caught = inCatchNullBlock(location);
276 if (caught && skipIfInsideCatchNull())
277 return;
289 if (caught && skipIfInsideCatchNull()) {
290 return;
291 }
278292
279293 int basePriority = Priorities.NORMAL_PRIORITY;
280294
281 if (!refValue.isNullOnComplicatedPath2())
295 if (!refValue.isNullOnComplicatedPath2()) {
282296 basePriority--;
297 }
283298 reportNullDeref(propertySet, location, "NOISE_NULL_DEREFERENCE", basePriority, cause, variable);
284299
285300 }
289304
290305 BugInstance bugInstance = new BugInstance(this, type, priority).addClassAndMethod(classContext.getJavaClass(), method);
291306 bugInstance.add(cause);
292 if (variable != null)
307 if (variable != null) {
293308 bugInstance.add(variable);
294 else
309 } else {
295310 bugInstance.add(new LocalVariableAnnotation("?", -1, -1));
311 }
296312 bugInstance.addSourceLine(classContext, method, location).describe("SOURCE_LINE_DEREF");
297313
298314 if (FindBugsAnalysisFeatures.isRelaxedMode()) {
309325 InstructionHandle ins = target.getFirstInstruction();
310326 int maxCount = 7;
311327 while (ins != null) {
312 if (maxCount-- <= 0)
328 if (maxCount-- <= 0) {
313329 break;
330 }
314331 Instruction i = ins.getInstruction();
315332 if (i instanceof ATHROW) {
316333 return true;
317334 }
318 if (i instanceof InstructionTargeter || i instanceof ReturnInstruction)
335 if (i instanceof InstructionTargeter || i instanceof ReturnInstruction) {
319336 return false;
337 }
320338 ins = ins.getNext();
321339 }
322340 return false;
323341 }
324342
343 @Override
325344 public void foundRedundantNullCheck(Location location, RedundantBranch redundantBranch) {
326345
327346 }
328347
329 /*
330 * (non-Javadoc)
331 *
332 * @see edu.umd.cs.findbugs.ba.npe.NullDerefAndRedundantComparisonCollector#
333 * foundGuaranteedNullDeref(java.util.Set, java.util.Set,
334 * edu.umd.cs.findbugs.ba.vna.ValueNumber, boolean)
335 */
348 @Override
336349 public void foundGuaranteedNullDeref(@Nonnull Set<Location> assignedNullLocationSet, @Nonnull Set<Location> derefLocationSet,
337350 SortedSet<Location> doomedLocations, ValueNumberDataflow vna, ValueNumber refValue,
338351 @CheckForNull BugAnnotation variableAnnotation, NullValueUnconditionalDeref deref, boolean npeIfStatementCovered) {
339352 }
340353
341 /**
342 * @param propertySet
343 * @param derefLocationSet
344 */
345354 private void addPropertiesForDereferenceLocations(WarningPropertySet<WarningProperty> propertySet,
346355 Collection<Location> derefLocationSet) {
347356 boolean derefOutsideCatchBlock = false;
348357 boolean allDerefsAtDoomedLocations = true;
349358 for (Location loc : derefLocationSet) {
350 if (!inCatchNullBlock(loc))
359 if (!inCatchNullBlock(loc)) {
351360 derefOutsideCatchBlock = true;
352
353 if (!isDoomed(loc))
361 }
362
363 if (!isDoomed(loc)) {
354364 allDerefsAtDoomedLocations = false;
365 }
355366 }
356367
357368 if (allDerefsAtDoomedLocations) {
361372 boolean uniqueDereferenceLocations = uniqueLocations(derefLocationSet);
362373
363374 if (!derefOutsideCatchBlock) {
364 if (!uniqueDereferenceLocations || skipIfInsideCatchNull())
375 if (!uniqueDereferenceLocations || skipIfInsideCatchNull()) {
365376 propertySet.addProperty(GeneralWarningProperty.FALSE_POSITIVE);
366 else
377 } else {
367378 propertySet.addProperty(NullDerefProperty.DEREFS_IN_CATCH_BLOCKS);
368 }
369 if (!uniqueDereferenceLocations)
379 }
380 }
381 if (!uniqueDereferenceLocations) {
370382 // Add a WarningProperty
371383 propertySet.addProperty(NullDerefProperty.DEREFS_ARE_CLONED);
384 }
372385
373386 addPropertiesForMethodContainingWarning(propertySet);
374387 }
375388
376 /**
377 * @param derefLocationSet
378 * @return
379 */
380389 private boolean uniqueLocations(Collection<Location> derefLocationSet) {
381390 boolean uniqueDereferenceLocations = false;
382391 LineNumberTable table = method.getLineNumberTable();
383 if (table == null)
392 if (table == null) {
384393 uniqueDereferenceLocations = true;
385 else {
394 } else {
386395 BitSet linesMentionedMultipleTimes = classContext.linesMentionedMultipleTimes(method);
387396 for (Location loc : derefLocationSet) {
388397 int lineNumber = table.getSourceLine(loc.getHandle().getPosition());
389 if (!linesMentionedMultipleTimes.get(lineNumber))
398 if (!linesMentionedMultipleTimes.get(lineNumber)) {
390399 uniqueDereferenceLocations = true;
400 }
391401 }
392402 }
393403 return uniqueDereferenceLocations;
394404 }
395405
396 /**
397 * @param propertySet
398 * @param xMethod
399 */
400406 private void addPropertiesForMethodContainingWarning(WarningPropertySet<WarningProperty> propertySet) {
401407 XMethod xMethod = XFactory.createXMethod(classContext.getJavaClass(), method);
402408
403409 boolean uncallable = !AnalysisContext.currentXFactory().isCalledDirectlyOrIndirectly(xMethod) && xMethod.isPrivate();
404410
405 if (uncallable)
411 if (uncallable) {
406412 propertySet.addProperty(GeneralWarningProperty.IN_UNCALLABLE_METHOD);
413 }
407414 }
408415
409416 private boolean isDoomed(Location loc) {
429436 try {
430437 UsagesRequiringNonNullValues usages = classContext.getUsagesRequiringNonNullValues(method);
431438 pu = usages.get(loc, refValue, vnaDataflow);
432 if (pu == null)
439 if (pu == null) {
433440 return "SOURCE_LINE_DEREF";
441 }
434442 return pu.getDescription();
435443 } catch (DataflowAnalysisException e) {
436444 AnalysisContext.logError("Error getting UsagesRequiringNonNullValues for " + method, e);
446454 int pc = loc.getHandle().getPosition();
447455 int catchSize = Util.getSizeOfSurroundingTryBlock(classContext.getJavaClass().getConstantPool(), method.getCode(),
448456 "java/lang/NullPointerException", pc);
449 if (catchSize < Integer.MAX_VALUE)
457 if (catchSize < Integer.MAX_VALUE) {
450458 return true;
459 }
451460 catchSize = Util.getSizeOfSurroundingTryBlock(classContext.getJavaClass().getConstantPool(), method.getCode(),
452461 "java/lang/Exception", pc);
453 if (catchSize < 5)
462 if (catchSize < 5) {
454463 return true;
464 }
455465 catchSize = Util.getSizeOfSurroundingTryBlock(classContext.getJavaClass().getConstantPool(), method.getCode(),
456466 "java/lang/RuntimeException", pc);
457 if (catchSize < 5)
467 if (catchSize < 5) {
458468 return true;
469 }
459470 catchSize = Util.getSizeOfSurroundingTryBlock(classContext.getJavaClass().getConstantPool(), method.getCode(),
460471 "java/lang/Throwable", pc);
461 if (catchSize < 5)
472 if (catchSize < 5) {
462473 return true;
474 }
463475 return false;
464476
465477 }
466478 }
467
468 // vim:ts=4
4343 @Override
4444 public void visitAnnotation(String annotationClass, Map<String, ElementValue> map, boolean runtimeVisible) {
4545
46 if (!annotationClass.equals("java.lang.annotation.Retention"))
46 if (!"java.lang.annotation.Retention".equals(annotationClass)) {
4747 return;
48 }
4849 EnumElementValue v = (EnumElementValue) map.get("value");
4950
50 if ("RUNTIME".equals(v.getEnumValueString()))
51 if ("RUNTIME".equals(v.getEnumValueString())) {
5152 runtimeRetention = true;
53 }
5254 }
5355
5456 @Override
5860
5961 @Override
6062 public void visitAfter(JavaClass obj) {
61 for (String i : obj.getInterfaceNames())
62 if (i.equals("java.lang.annotation.Annotation"))
63 for (String i : obj.getInterfaceNames()) {
64 if ("java.lang.annotation.Annotation".equals(i)) {
6365 AnalysisContext.currentAnalysisContext().getAnnotationRetentionDatabase()
64 .setRuntimeRetention(getDottedClassName(), runtimeRetention);
66 .setRuntimeRetention(getDottedClassName(), runtimeRetention);
67 }
68 }
6569
6670 }
6771
72 @Override
6873 public void visitClassContext(ClassContext classContext) {
6974 JavaClass javaClass = classContext.getJavaClass();
70 if (!BCELUtil.preTiger(javaClass))
75 if (!BCELUtil.preTiger(javaClass)) {
7176 javaClass.accept(this);
77 }
7278
7379 }
7480
81 @Override
7582 public void report() {
7683
7784 }
3434 public NoteCheckReturnValueAnnotations(BugReporter bugReporter) {
3535 }
3636
37 @Override
3738 public void visitClassContext(ClassContext classContext) {
3839
3940 JavaClass javaClass = classContext.getJavaClass();
40 if (!BCELUtil.preTiger(javaClass))
41 if (!BCELUtil.preTiger(javaClass)) {
4142 javaClass.accept(this);
43 }
4244 }
4345
46 @Override
4447 public void report() {
4548 }
4649 }
5252 public NoteDirectlyRelevantTypeQualifiers(BugReporter bugReporter) {
5353 }
5454
55 @Override
5556 public void visitClassContext(ClassContext classContext) {
5657 if (qualifiers == null) {
5758 qualifiers = AnalysisContext.currentAnalysisContext().getDirectlyRelevantTypeQualifiersDatabase();
9697 // stemming from called methods.
9798
9899 if (!Analysis.FIND_EFFECTIVE_RELEVANT_QUALIFIERS) {
99 XMethod m = getXMethodOperand();
100 if (m != null)
101 updateApplicableAnnotations(m);
100 XMethod m = getXMethodOperand();
101 if (m != null) {
102 updateApplicableAnnotations(m);
103 }
102104 }
103105 break;
104106
114116
115117 break;
116118 }
119 default:
120 break;
117121 }
118122 }
119123
126130 Analysis.addKnownTypeQualifiersForParameters(applicableApplications, m);
127131 }
128132
133 @Override
129134 public void report() {
130135 }
131136 }
5353 return;
5454 }
5555 JCIPAnnotationDatabase annotationDatabase = AnalysisContext.currentAnalysisContext()
56 .getJCIPAnnotationDatabase();
56 .getJCIPAnnotationDatabase();
5757 ElementValue value = map.get("value");
5858 ClassMember member;
5959 if (visitingField()) {
6767 annotationDatabase.addEntryForClassMember(member, annotationClass, value);
6868 }
6969
70 @Override
7071 public void visitClassContext(ClassContext classContext) {
7172 JavaClass javaClass = classContext.getJavaClass();
7273 if (!BCELUtil.preTiger(javaClass)) {
7576
7677 }
7778
79 @Override
7880 public void report() {
7981 // noop
8082 }
3030 /**
3131 * Scan classes for @NonNull, @PossiblyNull and @CheckForNull annotations, and
3232 * convey them to FindNullDeref.
33 *
33 *
3434 * @deprecated AnnotationDatabases are being phased out, since annotations are
3535 * now stored directly in the XClass/XMethod/XField objects.
3636 * Resolving nullness annotations will be handled through the
4343 super(getDatabase());
4444 }
4545
46 /**
47 * @return
48 */
4946 private static NullnessAnnotationDatabase getDatabase() {
5047 return null;
51 }
48 }
5249
50 @Override
5351 public void visitClassContext(ClassContext classContext) {
5452
5553 JavaClass javaClass = classContext.getJavaClass();
56 if (!BCELUtil.preTiger(javaClass))
54 if (!BCELUtil.preTiger(javaClass)) {
5755 javaClass.accept(this);
56 }
5857 }
5958
59 @Override
6060 public void report() {
6161 }
6262 }
2727 /**
2828 * As a first scanning pass, make a note of unconditionally dereferenced
2929 * parameters for later use by FindNullDerefs.
30 *
30 *
3131 * @author David Hovemeyer
3232 */
3333 public class NoteNonnullReturnValues extends BuildNonnullReturnDatabase implements Detector, NonReportingDetector,
34 InterproceduralFirstPassDetector {
34 InterproceduralFirstPassDetector {
3535
3636 public NoteNonnullReturnValues(BugReporter bugReporter) {
3737 }
3838
3939 /*
4040 * (non-Javadoc)
41 *
41 *
4242 * @see edu.umd.cs.findbugs.Detector#report()
4343 */
44 @Override
4445 public void report() {
45 if (SystemProperties.getBoolean("findbugs.statistics"))
46 if (SystemProperties.getBoolean("findbugs.statistics")) {
4647 System.err.println(returnsNonNull + "/" + returnsReference + " methods return nonnull values");
48 }
4749 }
4850
4951 }
5454 suppressionMatcher = AnalysisContext.currentAnalysisContext().getSuppressionMatcher();
5555 }
5656
57 @Override
5758 public void visitClassContext(ClassContext classContext) {
5859 JavaClass javaClass = classContext.getJavaClass();
5960 if (!BCELUtil.preTiger(javaClass)) {
6263 int i = name.lastIndexOf('.');
6364 String packageName = i < 0 ? "" : name.substring(0, i);
6465 if (name.endsWith(".package-info")) {
65 if (!packages.add(packageName))
66 if (!packages.add(packageName)) {
6667 return;
68 }
6769 } else if (packages.add(packageName)) {
6870 JavaClass packageInfoClass;
6971 try {
7981
8082 @Override
8183 public void visitAnnotation(String annotationClass, Map<String, ElementValue> map, boolean runtimeVisible) {
82 if (!isSuppressWarnings(annotationClass))
84 if (!isSuppressWarnings(annotationClass)) {
8385 return;
86 }
8487 String[] suppressed = getAnnotationParameterAsStringArray(map, "value");
85 if (suppressed == null || suppressed.length == 0)
88 if (suppressed == null || suppressed.length == 0) {
8689 suppressWarning(null);
87 else
88 for (String s : suppressed)
90 } else {
91 for (String s : suppressed) {
8992 suppressWarning(s);
93 }
94 }
9095 }
9196
92 /**
93 * @param annotationClass
94 * @return
95 */
9697 public boolean isSuppressWarnings(String annotationClass) {
9798 return annotationClass.endsWith("SuppressWarnings")
9899 || annotationClass.endsWith("SuppressFBWarnings");
100101
101102 @Override
102103 public void visitParameterAnnotation(int p, String annotationClass, Map<String, ElementValue> map, boolean runtimeVisible) {
103 if (!isSuppressWarnings(annotationClass))
104 if (!isSuppressWarnings(annotationClass)) {
104105 return;
105 if (!getMethod().isStatic())
106 }
107 if (!getMethod().isStatic()) {
106108 p++;
109 }
107110
108111 String[] suppressed = getAnnotationParameterAsStringArray(map, "value");
109 if (suppressed == null || suppressed.length == 0)
112 if (suppressed == null || suppressed.length == 0) {
110113 suppressWarning(p, null);
111 else
112 for (String s : suppressed)
114 } else {
115 for (String s : suppressed) {
113116 suppressWarning(p, s);
117 }
118 }
114119 }
115120
116121 private void suppressWarning(int parameter, String pattern) {
124129 private void suppressWarning(String pattern) {
125130 String className = getDottedClassName();
126131 ClassAnnotation clazz = new ClassAnnotation(className);
127 if (className.endsWith(".package-info"))
132 if (className.endsWith(".package-info")) {
128133 suppressionMatcher.addPackageSuppressor(new PackageWarningSuppressor(pattern, getPackageName().replace('/', '.')));
129 else if (visitingMethod())
134 } else if (visitingMethod()) {
130135 suppressionMatcher
131 .addSuppressor(new MethodWarningSuppressor(pattern, clazz, MethodAnnotation.fromVisitedMethod(this)));
132 else if (visitingField())
136 .addSuppressor(new MethodWarningSuppressor(pattern, clazz, MethodAnnotation.fromVisitedMethod(this)));
137 } else if (visitingField()) {
133138 suppressionMatcher.addSuppressor(new FieldWarningSuppressor(pattern, clazz, FieldAnnotation.fromVisitedField(this)));
134 else
139 } else {
135140 suppressionMatcher.addSuppressor(new ClassWarningSuppressor(pattern, clazz));
141 }
136142 }
137143
144 @Override
138145 public void report() {
139146
140147 }
3030 * @author David Hovemeyer
3131 */
3232 public class NoteUnconditionalParamDerefs extends BuildUnconditionalParamDerefDatabase implements NonReportingDetector,
33 InterproceduralFirstPassDetector {
33 InterproceduralFirstPassDetector {
3434
3535 final BugReporter reporter;
3636
4343 *
4444 * @see edu.umd.cs.findbugs.Detector#report()
4545 */
46 @Override
4647 public void report() {
4748 }
4849
2424 /**
2525 * Warning property for a null argument being passed to a method which might
2626 * dereference it.
27 *
27 *
2828 * @author David Hovemeyer
2929 */
3030 public class NullArgumentWarningProperty extends AbstractWarningProperty {
1818
1919 package edu.umd.cs.findbugs.detect;
2020
21 import java.util.ArrayList;
2122 import java.util.HashMap;
23 import java.util.List;
2224 import java.util.Map;
2325
2426 import javax.annotation.CheckForNull;
2931 import edu.umd.cs.findbugs.BugInstance;
3032 import edu.umd.cs.findbugs.BugReporter;
3133 import edu.umd.cs.findbugs.ba.ClassContext;
32 import edu.umd.cs.findbugs.ba.XFactory;
33 import edu.umd.cs.findbugs.ba.XMethod;
3434 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
35 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
3536 import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName;
36 import edu.umd.cs.findbugs.util.ClassName;
3737
3838 /**
3939 * Detector to find calls to Number constructors with base type argument in Java
5555 public class NumberConstructor extends OpcodeStackDetector {
5656
5757 static class Pair {
58 final XMethod boxingMethod;
59 public Pair(XMethod boxingMethod, XMethod parsingMethod) {
58 final MethodDescriptor boxingMethod;
59 final MethodDescriptor parsingMethod;
60 public Pair(MethodDescriptor boxingMethod, MethodDescriptor parsingMethod) {
6061 this.boxingMethod = boxingMethod;
6162 this.parsingMethod = parsingMethod;
6263 }
63 final XMethod parsingMethod;
6464 }
6565 private final Map<String, Pair> boxClasses = new HashMap<String, Pair>();
66
67 private final List<MethodDescriptor> methods = new ArrayList<>();
6668
6769 private final BugAccumulator bugAccumulator;
6870
8587 }
8688
8789 private void handle(@SlashedClassName String className, boolean isFloatingPoint, String sig) {
88 XMethod boxingMethod = XFactory.createXMethod(ClassName.toDottedClassName(className), "valueOf", sig + "L" + className +";", true);
89 XMethod parsingMethod = XFactory.createXMethod(ClassName.toDottedClassName(className), "valueOf", "(Ljava/lang/String;)" + "L" + className +";", true);
90 MethodDescriptor boxingMethod = new MethodDescriptor(className, "valueOf", sig + "L" + className +";", true);
91 MethodDescriptor parsingMethod = new MethodDescriptor(className, "valueOf", "(Ljava/lang/String;)" + "L" + className +";", true);
9092 boxClasses.put(className, new Pair(boxingMethod, parsingMethod));
93 methods.add(new MethodDescriptor(className, "<init>", "(Ljava/lang/String;)V"));
94 methods.add(new MethodDescriptor(className, "<init>", sig+"V"));
9195 }
9296
9397 /**
99103 @Override
100104 public void visitClassContext(ClassContext classContext) {
101105 int majorVersion = classContext.getJavaClass().getMajor();
102 if (majorVersion >= MAJOR_1_5) {
106 if (majorVersion >= MAJOR_1_5 && hasInterestingMethod(classContext.getJavaClass().getConstantPool(), methods)) {
103107 super.visitClassContext(classContext);
104108 }
105109 }
115119 String args = sig1.substring(0, lastParen+1);
116120 return sig2.startsWith(args);
117121 }
118
119 private @CheckForNull XMethod getShouldCall() {
122
123 private @CheckForNull MethodDescriptor getShouldCall() {
120124 String cls = getClassConstantOperand();
121125 Pair pair = boxClasses.get(cls);
122 if (pair == null)
126 if (pair == null) {
123127 return null;
124 XMethod shouldCall ;
125 if (getSigConstantOperand().startsWith("(Ljava/lang/String;)"))
128 }
129 MethodDescriptor shouldCall;
130 if (getSigConstantOperand().startsWith("(Ljava/lang/String;)")) {
126131 shouldCall = pair.parsingMethod;
127 else
132 } else {
128133 shouldCall = pair.boxingMethod;
129
134 }
135
130136 if (shouldCall == null) {
131137 return null;
132138 }
133139
134 if (matchArguments(getSigConstantOperand(), shouldCall.getSignature()))
140 if (matchArguments(getSigConstantOperand(), shouldCall.getSignature())) {
135141 return shouldCall;
136
142 }
143
137144 return null;
138145 }
139146 @Override
147154 return;
148155 }
149156 @SlashedClassName String cls = getClassConstantOperand();
150 XMethod shouldCall = getShouldCall();
151 if (shouldCall == null)
157 MethodDescriptor shouldCall = getShouldCall();
158 if (shouldCall == null) {
152159 return;
160 }
153161
154162 int prio;
155163 String type;
156 if (cls.equals("java/lang/Float") || cls.equals("java/lang/Double")) {
164 if ("java/lang/Float".equals(cls) || "java/lang/Double".equals(cls)) {
157165 prio = LOW_PRIORITY;
158166 type = "DM_FP_NUMBER_CTOR";
159167 } else {
161169 Object constantValue = stack.getStackItem(0).getConstant();
162170 if (constantValue instanceof Number) {
163171 long value = ((Number) constantValue).longValue();
164 if (value < -128 || value > 127)
172 if (value < -128 || value > 127) {
165173 prio = LOW_PRIORITY;
174 }
166175 }
167176 type = "DM_NUMBER_CTOR";
168177 }
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2007 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 package edu.umd.cs.findbugs.detect;
19
20 import edu.umd.cs.findbugs.BugInstance;
21 import edu.umd.cs.findbugs.BugReporter;
22
23 /**
24 * Methods with "Optional" return type should never return null.
25 *
26 * @author Andrey Loskutov
27 */
28 public class OptionalReturnNull extends TypeReturnNull {
29
30 public OptionalReturnNull(BugReporter bugReporter) {
31 super(bugReporter);
32 }
33
34 @Override
35 protected boolean matchesReturnSignature(String returnSignature) {
36 return "Ljava/util/Optional;".equals(returnSignature)
37 || "Lcom/google/common/base/Optional;".equals(returnSignature);
38 }
39
40 @Override
41 protected void accumulateBug() {
42 bugAccumulator.accumulateBug(new BugInstance(this, "NP_OPTIONAL_RETURN_NULL",
43 HIGH_PRIORITY).addClassAndMethod(this), this);
44 }
45
46
47 }
7777
7878 @Override
7979 public void visit(Code obj) {
80 if (getMethodName().equals(EQUALS_NAME) && !getMethod().isStatic() && getMethod().isPublic()
81 && getMethodSig().equals(EQUALS_SIGNATURE)) {
80 if (EQUALS_NAME.equals(getMethodName()) && !getMethod().isStatic() && getMethod().isPublic()
81 && EQUALS_SIGNATURE.equals(getMethodSig())) {
8282 sawCheckedCast = sawSuperEquals = sawInstanceOf = sawGetClass = sawReturnSuper = sawCompare = sawReturnNonSuper = prevWasSuperEquals = sawGoodEqualsClass = sawBadEqualsClass = dangerDanger = sawInstanceOfSupertype = alwaysTrue = alwaysFalse = sawStaticDelegate = sawEqualsBuilder = false;
8383 sawInitialIdentityCheck = obj.getCode().length == 11 || obj.getCode().length == 9;
8484 equalsCalls = 0;
8585 super.visit(obj);
8686 EqualsKindSummary.KindOfEquals kind = EqualsKindSummary.KindOfEquals.UNKNOWN;
87 if (alwaysTrue)
87 if (alwaysTrue) {
8888 kind = EqualsKindSummary.KindOfEquals.ALWAYS_TRUE;
89 else if (alwaysFalse)
89 } else if (alwaysFalse) {
9090 kind = EqualsKindSummary.KindOfEquals.ALWAYS_FALSE;
91 else if (sawReturnSuper && !sawReturnNonSuper)
91 } else if (sawReturnSuper && !sawReturnNonSuper) {
9292 kind = EqualsKindSummary.KindOfEquals.RETURNS_SUPER;
93 else if (sawSuperEquals)
93 } else if (sawSuperEquals) {
9494 kind = EqualsKindSummary.KindOfEquals.INVOKES_SUPER;
95 else if (sawInstanceOfSupertype)
95 } else if (sawInstanceOfSupertype) {
9696 kind = EqualsKindSummary.KindOfEquals.INSTANCE_OF_SUPERCLASS_EQUALS;
97 else if (sawInstanceOf)
97 } else if (sawInstanceOf) {
9898 kind = getThisClass().isAbstract() ? EqualsKindSummary.KindOfEquals.ABSTRACT_INSTANCE_OF
9999 : EqualsKindSummary.KindOfEquals.INSTANCE_OF_EQUALS;
100 else if (sawGetClass && sawGoodEqualsClass)
100 } else if (sawGetClass && sawGoodEqualsClass) {
101101 kind = getThisClass().isAbstract() ? EqualsKindSummary.KindOfEquals.ABSTRACT_GETCLASS_GOOD_EQUALS
102102 : EqualsKindSummary.KindOfEquals.GETCLASS_GOOD_EQUALS;
103 else if (sawGetClass && sawBadEqualsClass)
103 } else if (sawGetClass && sawBadEqualsClass) {
104104 kind = EqualsKindSummary.KindOfEquals.GETCLASS_BAD_EQUALS;
105 else if (equalsCalls == 1 || sawStaticDelegate || sawEqualsBuilder)
105 } else if (equalsCalls == 1 || sawStaticDelegate || sawEqualsBuilder) {
106106 kind = EqualsKindSummary.KindOfEquals.DELEGATE_EQUALS;
107 else if (sawInitialIdentityCheck)
107 } else if (sawInitialIdentityCheck) {
108108 kind = EqualsKindSummary.KindOfEquals.TRIVIAL_EQUALS;
109 else if (sawCheckedCast)
109 } else if (sawCheckedCast) {
110110 kind = EqualsKindSummary.KindOfEquals.CHECKED_CAST_EQUALS;
111 else if (sawCompare)
111 } else if (sawCompare) {
112112 kind = EqualsKindSummary.KindOfEquals.COMPARE_EQUALS;
113 else {
114 if (AnalysisContext.currentAnalysisContext().isApplicationClass(getThisClass()))
113 } else {
114 if (AnalysisContext.currentAnalysisContext().isApplicationClass(getThisClass())) {
115115 bugReporter
116 .reportBug(new BugInstance(this, "EQ_UNUSUAL", Priorities.NORMAL_PRIORITY).addClassAndMethod(this));
116 .reportBug(new BugInstance(this, "EQ_UNUSUAL", Priorities.NORMAL_PRIORITY).addClassAndMethod(this));
117 }
117118 }
118119 ClassAnnotation classAnnotation = new ClassAnnotation(getDottedClassName());
119120 equalsKindSummary.put(classAnnotation, kind);
152153 }
153154
154155 String superClassName = getSuperclassName().replace('/', '.');
155 if (!superClassName.equals("java.lang.Object"))
156 if (!"java.lang.Object".equals(superClassName)) {
156157 parentMap.put(classAnnotation, new ClassAnnotation(superClassName));
158 }
157159 equalsMethod.put(classAnnotation, getMethodDescriptor());
158160
159161 }
193195
194196 private void count(EqualsKindSummary.KindOfEquals k) {
195197 Integer v = count.get(k);
196 if (v == null)
198 if (v == null) {
197199 count.put(k, 1);
198 else
200 } else {
199201 count.put(k, v + 1);
202 }
200203 }
201204
202205 @Override
209212 && seen == INVOKESTATIC
210213 && getCode().getCode().length == 6
211214 && (getPrevOpcode(1) == ALOAD_0 && getPrevOpcode(2) == ALOAD_1 || getPrevOpcode(1) == ALOAD_1
212 && getPrevOpcode(2) == ALOAD_0))
215 && getPrevOpcode(2) == ALOAD_0)) {
213216 sawStaticDelegate = true;
217 }
214218
215219 if ((seen == INVOKESTATIC || seen == INVOKESPECIAL || seen == INVOKEVIRTUAL)
216 && (getClassConstantOperand().equals("org/apache/commons/lang/builder/EqualsBuilder")
217 || getClassConstantOperand().equals("org/apache/commons/lang3/builder/EqualsBuilder")))
220 && ("org/apache/commons/lang/builder/EqualsBuilder".equals(getClassConstantOperand())
221 || "org/apache/commons/lang3/builder/EqualsBuilder".equals(getClassConstantOperand()))) {
218222 sawEqualsBuilder = true;
223 }
219224
220225 if (seen == IRETURN && getPC() == 1 && getPrevOpcode(1) == ICONST_0) {
221226 alwaysFalse = true;
222 if (AnalysisContext.currentAnalysisContext().isApplicationClass(getThisClass()))
227 if (AnalysisContext.currentAnalysisContext().isApplicationClass(getThisClass())) {
223228 bugReporter.reportBug(new BugInstance(this, "EQ_ALWAYS_FALSE", Priorities.HIGH_PRIORITY).addClassAndMethod(this)
224229 .addSourceLine(this));
230 }
225231
226232 }
227233 if (seen == IRETURN && getPC() == 1 && getPrevOpcode(1) == ICONST_1) {
228234 alwaysTrue = true;
229 if (AnalysisContext.currentAnalysisContext().isApplicationClass(getThisClass()))
230
235 if (AnalysisContext.currentAnalysisContext().isApplicationClass(getThisClass())) {
231236 bugReporter.reportBug(new BugInstance(this, "EQ_ALWAYS_TRUE", Priorities.HIGH_PRIORITY).addClassAndMethod(this)
232237 .addSourceLine(this));
238 }
233239
234240 }
235241 if (seen == IF_ACMPEQ || seen == IF_ACMPNE) {
238244 if (callToInvoke(seen)) {
239245 equalsCalls++;
240246 checkForComparingClasses();
241 if (AnalysisContext.currentAnalysisContext().isApplicationClass(getThisClass()) && dangerDanger)
247 if (AnalysisContext.currentAnalysisContext().isApplicationClass(getThisClass()) && dangerDanger) {
242248 bugReporter.reportBug(new BugInstance(this, "EQ_COMPARING_CLASS_NAMES", Priorities.NORMAL_PRIORITY)
243 .addClassAndMethod(this).addSourceLine(this));
244 }
245
246 if ((seen == INVOKEINTERFACE || seen == INVOKEVIRTUAL) && getNameConstantOperand().equals("compare")
249 .addClassAndMethod(this).addSourceLine(this));
250 }
251 }
252
253 if ((seen == INVOKEINTERFACE || seen == INVOKEVIRTUAL) && "compare".equals(getNameConstantOperand())
247254 && stack.getStackDepth() >= 2) {
248255 Item left = stack.getStackItem(1);
249256 Item right = stack.getStackItem(0);
250 if (left.getRegisterNumber() + right.getRegisterNumber() == 1)
257 if (left.getRegisterNumber() + right.getRegisterNumber() == 1) {
251258 sawCompare = true;
259 }
252260 }
253261 dangerDanger = false;
254262
255 if (seen == INVOKEVIRTUAL && getClassConstantOperand().equals("java/lang/Class")
256 && getNameConstantOperand().equals("getName") && getSigConstantOperand().equals("()Ljava/lang/String;")
263 if (seen == INVOKEVIRTUAL && "java/lang/Class".equals(getClassConstantOperand())
264 && "getName".equals(getNameConstantOperand()) && "()Ljava/lang/String;".equals(getSigConstantOperand())
257265 && stack.getStackDepth() >= 2) {
258266 Item left = stack.getStackItem(1);
259267 XMethod leftM = left.getReturnValueOf();
260268 Item right = stack.getStackItem(0);
261269 XMethod rightM = right.getReturnValueOf();
262 if (leftM != null && rightM != null && leftM.getName().equals("getName") && rightM.getName().equals("getClass")) {
270 if (leftM != null && rightM != null && "getName".equals(leftM.getName()) && "getClass".equals(rightM.getName())) {
263271 dangerDanger = true;
264272 }
265273
266274 }
267 if (seen == INVOKESPECIAL && getNameConstantOperand().equals(EQUALS_NAME)
268 && getSigConstantOperand().equals(EQUALS_SIGNATURE)) {
275 if (seen == INVOKESPECIAL && EQUALS_NAME.equals(getNameConstantOperand())
276 && EQUALS_SIGNATURE.equals(getSigConstantOperand())) {
269277 sawSuperEquals = prevWasSuperEquals = true;
270278 } else {
271279 if (seen == IRETURN) {
272 if (prevWasSuperEquals)
280 if (prevWasSuperEquals) {
273281 sawReturnSuper = true;
274 else
282 } else {
275283 sawReturnNonSuper = true;
284 }
276285 }
277286 prevWasSuperEquals = false;
278287 }
279288
280289 if (seen == INSTANCEOF && stack.getStackDepth() > 0 && stack.getStackItem(0).getRegisterNumber() == 1) {
281290 ClassDescriptor instanceOfCheck = getClassDescriptorOperand();
282 if (instanceOfCheck.equals(getClassDescriptor()))
291 if (instanceOfCheck.equals(getClassDescriptor())) {
283292 sawInstanceOf = true;
284 else
293 } else {
285294 try {
286 if (AnalysisContext.currentAnalysisContext().getSubtypes2().isSubtype(getClassDescriptor(), instanceOfCheck))
295 if (AnalysisContext.currentAnalysisContext().getSubtypes2().isSubtype(getClassDescriptor(), instanceOfCheck)) {
287296 sawInstanceOfSupertype = true;
297 }
288298 } catch (ClassNotFoundException e) {
289299 sawInstanceOfSupertype = true;
290300 }
301 }
291302 }
292303
293304 if (seen == CHECKCAST && stack.getStackDepth() > 0 && stack.getStackItem(0).getRegisterNumber() == 1) {
294305 ClassDescriptor castTo = getClassDescriptorOperand();
295 if (castTo.equals(getClassDescriptor()))
306 if (castTo.equals(getClassDescriptor())) {
296307 sawCheckedCast = true;
308 }
297309 try {
298 if (AnalysisContext.currentAnalysisContext().getSubtypes2().isSubtype(getClassDescriptor(), castTo))
310 if (AnalysisContext.currentAnalysisContext().getSubtypes2().isSubtype(getClassDescriptor(), castTo)) {
299311 sawCheckedCast = true;
312 }
300313 } catch (ClassNotFoundException e) {
301314 sawCheckedCast = true;
302315 }
303316 }
304 if (seen == INVOKEVIRTUAL && getNameConstantOperand().equals("getClass")
305 && getSigConstantOperand().equals("()Ljava/lang/Class;")) {
317 if (seen == INVOKEVIRTUAL && "getClass".equals(getNameConstantOperand())
318 && "()Ljava/lang/Class;".equals(getSigConstantOperand())) {
306319 sawGetClass = true;
307320 }
308321
309322 }
310323
311 /**
312 * @param seen
313 * @return
314 */
315324 private boolean callToInvoke(int seen) {
316 if (seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE || seen == INVOKESPECIAL)
317 return invokesMethodWithEqualLikeName() && getSigConstantOperand().equals(EQUALS_SIGNATURE);
325 if (seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE || seen == INVOKESPECIAL) {
326 return invokesMethodWithEqualLikeName() && EQUALS_SIGNATURE.equals(getSigConstantOperand());
327 }
318328 if (seen == INVOKESTATIC) {
319329 String sig = getSigConstantOperand();
320330 return invokesMethodWithEqualLikeName() && sig.endsWith("Ljava/lang/Object;)Z");
337347 XMethod leftM = left.getReturnValueOf();
338348 Item right = stack.getStackItem(0);
339349 XMethod rightM = right.getReturnValueOf();
340 if (left.getSignature().equals("Ljava/lang/Class;") && right.getSignature().equals("Ljava/lang/Class;")) {
341 boolean leftMatch = leftM != null && leftM.getName().equals("getClass");
342 boolean rightMatch = rightM != null && rightM.getName().equals("getClass");
350 if ("Ljava/lang/Class;".equals(left.getSignature()) && "Ljava/lang/Class;".equals(right.getSignature())) {
351 boolean leftMatch = leftM != null && "getClass".equals(leftM.getName());
352 boolean rightMatch = rightM != null && "getClass".equals(rightM.getName());
343353 if (leftMatch && rightMatch) {
344354 sawGoodEqualsClass = true;
345355 } else {
354364 int priority = Priorities.NORMAL_PRIORITY;
355365
356366 BugInstance bug = new BugInstance(this, "EQ_GETCLASS_AND_CLASS_CONSTANT", priority)
357 .addClassAndMethod(this);
367 .addClassAndMethod(this);
358368
359369 try {
360370
395405 for (Map.Entry<ClassDescriptor, Set<ClassDescriptor>> e : classesWithGetClassBasedEquals.entrySet()) {
396406 ClassAnnotation parentClass = ClassAnnotation.fromClassDescriptor(e.getKey());
397407 XClass xParent = AnalysisContext.currentXFactory().getXClass(e.getKey());
398 if (xParent == null)
408 if (xParent == null) {
399409 continue;
410 }
400411 EqualsKindSummary.KindOfEquals parentKind = equalsKindSummary.get(parentClass);
401412 for (ClassDescriptor child : e.getValue()) {
402 if (child.equals(e.getKey()))
413 if (child.equals(e.getKey())) {
403414 continue;
415 }
404416 XClass xChild = AnalysisContext.currentXFactory().getXClass(child);
405 if (xChild == null)
417 if (xChild == null) {
406418 continue;
419 }
407420 ClassAnnotation childClass = ClassAnnotation.fromClassDescriptor(child);
408421 EqualsKindSummary.KindOfEquals childKind = equalsKindSummary.get(childClass);
409422 int fieldsOfInterest = 0;
410 for (XField f : xChild.getXFields())
411 if (!f.isStatic() && !f.isSynthetic())
423 for (XField f : xChild.getXFields()) {
424 if (!f.isStatic() && !f.isSynthetic()) {
412425 fieldsOfInterest++;
426 }
427 }
413428 int grandchildren = -1;
414429 try {
415430
421436 + fieldsOfInterest + " " + grandchildren);
422437 try {
423438 if (grandchildren >= 2) {
424 for (ClassDescriptor g : subtypes2.getSubtypes(child))
425 if (!g.equals(child))
439 for (ClassDescriptor g : subtypes2.getSubtypes(child)) {
440 if (!g.equals(child)) {
426441 System.out.println(" " + g);
442 }
443 }
427444 }
428445 } catch (ClassNotFoundException e1) {
429446 assert true;
436453 for (Map.Entry<ClassDescriptor, Set<ClassDescriptor>> e : classesWithInstanceOfBasedEquals.entrySet()) {
437454 ClassAnnotation parentClass = ClassAnnotation.fromClassDescriptor(e.getKey());
438455 XClass xParent = AnalysisContext.currentXFactory().getXClass(e.getKey());
439 if (xParent == null)
456 if (xParent == null) {
440457 continue;
458 }
441459 EqualsKindSummary.KindOfEquals parentKind = equalsKindSummary.get(parentClass);
442460 boolean isOverridden = false;
443461 for (ClassDescriptor child : e.getValue()) {
444 if (child.equals(e.getKey()))
462 if (child.equals(e.getKey())) {
445463 continue;
464 }
446465 XClass xChild = AnalysisContext.currentXFactory().getXClass(child);
447 if (xChild == null)
466 if (xChild == null) {
448467 continue;
468 }
449469 ClassAnnotation childClass = ClassAnnotation.fromClassDescriptor(child);
450470 EqualsKindSummary.KindOfEquals childKind = equalsKindSummary.get(childClass);
451 if (childKind != null)
471 if (childKind != null) {
452472 isOverridden = true;
473 }
453474 }
454475 total++;
455 if (isOverridden)
476 if (isOverridden) {
456477 overridden++;
478 }
457479 System.out.println("IS_OVERRIDDEN: " + e.getKey().getClassName());
458480 }
459481 System.out.println("Instance of equals: " + total + " subclassed, " + overridden + " overrridden");
470492 EqualsKindSummary.KindOfEquals parentKind = equalsKindSummary.get(parentClass);
471493
472494 if (childKind == EqualsKindSummary.KindOfEquals.INSTANCE_OF_EQUALS
473 && parentKind == EqualsKindSummary.KindOfEquals.INSTANCE_OF_EQUALS)
495 && parentKind == EqualsKindSummary.KindOfEquals.INSTANCE_OF_EQUALS) {
474496 bugReporter.reportBug(new BugInstance(this, "EQ_OVERRIDING_EQUALS_NOT_SYMMETRIC", NORMAL_PRIORITY)
475 .add(childClass).addMethod(equalsMethod.get(childClass)).addMethod(equalsMethod.get(parentClass))
476 .describe(MethodAnnotation.METHOD_OVERRIDDEN));
497 .add(childClass).addMethod(equalsMethod.get(childClass)).addMethod(equalsMethod.get(parentClass))
498 .describe(MethodAnnotation.METHOD_OVERRIDDEN));
499 }
477500
478501 }
479502
3232 public class PreferZeroLengthArrays extends BytecodeScanningDetector implements StatelessDetector {
3333 boolean nullOnTOS = false;
3434
35 private BugReporter bugReporter;
35 private final BugReporter bugReporter;
3636
3737 public PreferZeroLengthArrays(BugReporter bugReporter) {
3838 this.bugReporter = bugReporter;
4545 found.clear();
4646 // Solution to sourceforge bug 1765925; returning null is the
4747 // convention used by java.io.File.listFiles()
48 if (getMethodName().equals("listFiles")) {
48 if ("listFiles".equals(getMethodName())) {
4949 return;
5050 }
51 String returnType = getMethodSig().substring(getMethodSig().indexOf(")") + 1);
51 String returnType = getMethodSig().substring(getMethodSig().indexOf(')') + 1);
5252 if (returnType.startsWith("[")) {
5353 nullOnTOS = false;
5454 super.visit(obj);
5555 if (!found.isEmpty()) {
5656 BugInstance bug = new BugInstance(this, "PZLA_PREFER_ZERO_LENGTH_ARRAYS", LOW_PRIORITY).addClassAndMethod(this);
57 for (SourceLineAnnotation s : found)
57 for (SourceLineAnnotation s : found) {
5858 bug.add(s);
59 }
5960 bugReporter.reportBug(bug);
6061 found.clear();
6162 }
7374 if (nullOnTOS) {
7475 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(getClassContext(), this,
7576 getPC());
76 if (sourceLineAnnotation != null)
77 if (sourceLineAnnotation != null) {
7778 found.add(sourceLineAnnotation);
79 }
7880 }
79
81 break;
82 default:
8083 break;
8184 }
8285 nullOnTOS = false;
4040
4141 private static final int SEEN_ALOAD_0 = 1;
4242
43 private BugReporter bugReporter;
43 private final BugReporter bugReporter;
4444
4545 private int state;
4646
5353 @Override
5454 public void visitClassContext(ClassContext classContext) {
5555 JavaClass cls = classContext.getJavaClass();
56 if ((!cls.isPublic()) || (cls.getClassName().indexOf("$") >= 0))
56 if ((!cls.isPublic()) || (cls.getClassName().indexOf('$') >= 0)) {
5757 return;
58 }
5859
5960 alreadyReported = false;
6061 super.visitClassContext(classContext);
6364 @Override
6465 public void visit(Code obj) {
6566 Method m = getMethod();
66 if (m.isStatic() || alreadyReported)
67 if (m.isStatic() || alreadyReported) {
6768 return;
69 }
6870
6971 state = SEEN_NOTHING;
7072 super.visit(obj);
7274
7375 @Override
7476 public void sawOpcode(int seen) {
75 if (alreadyReported)
77 if (alreadyReported) {
7678 return;
79 }
7780
7881 switch (state) {
7982 case SEEN_NOTHING:
80 if (seen == ALOAD_0)
83 if (seen == ALOAD_0) {
8184 state = SEEN_ALOAD_0;
85 }
8286 break;
8387
8488 case SEEN_ALOAD_0:
85 if ((seen == INVOKEVIRTUAL) && getClassConstantOperand().equals("java/lang/Object")) {
89 if ((seen == INVOKEVIRTUAL) && "java/lang/Object".equals(getClassConstantOperand())) {
8690 String methodName = getNameConstantOperand();
8791 if ("wait".equals(methodName) || "notify".equals(methodName) || "notifyAll".equals(methodName)) {
8892 bugReporter.reportBug(new BugInstance(this, "PS_PUBLIC_SEMAPHORES", NORMAL_PRIORITY).addClassAndMethod(this)
9296 }
9397 state = SEEN_NOTHING;
9498 break;
99 default:
100 break;
95101 }
96102
97103 }
3838
3939 public static final int SEEN_IF = 5;
4040
41 private BugReporter bugReporter;
41 private final BugReporter bugReporter;
4242
4343 private int state;
4444
5959 public void sawOpcode(int seen) {
6060 if (seen == GOTO && getBranchOffset() == 4) {
6161 state = SEEN_GOTO;
62 } else
62 } else {
6363 switch (state) {
6464 case SEEN_NOTHING:
65 if ((seen == ICONST_1) || (seen == ICONST_0))
65 if ((seen == ICONST_1) || (seen == ICONST_0)) {
6666 state = SEEN_ICONST_0_OR_1;
67 }
6768 break;
6869
6970 case SEEN_ICONST_0_OR_1:
70 if (seen == DUP)
71 if (seen == DUP) {
7172 state = SEEN_DUP;
72 else
73 } else {
7374 state = SEEN_NOTHING;
75 }
7476 break;
7577
7678 case SEEN_DUP:
77 if (((seen >= ISTORE_0) && (seen <= ISTORE_3)) || (seen == ISTORE))
79 if (((seen >= ISTORE_0) && (seen <= ISTORE_3)) || (seen == ISTORE)) {
7880 state = SEEN_ISTORE;
79 else
81 } else {
8082 state = SEEN_NOTHING;
83 }
8184 break;
8285
8386 case SEEN_ISTORE:
8588 bug = new BugInstance(this, "QBA_QUESTIONABLE_BOOLEAN_ASSIGNMENT", HIGH_PRIORITY).addClassAndMethod(this)
8689 .addSourceLine(this);
8790 state = SEEN_IF;
88 } else
91 } else {
8992 state = SEEN_NOTHING;
93 }
9094 break;
9195
9296 case SEEN_IF:
9397 state = SEEN_NOTHING;
9498 if (seen == NEW) {
9599 String cName = getClassConstantOperand();
96 if (cName.equals("java/lang/AssertionError"))
100 if ("java/lang/AssertionError".equals(cName)) {
97101 break;
102 }
98103 }
99104 bugReporter.reportBug(bug);
100
101105 break;
102106 case SEEN_GOTO:
103107 state = SEEN_NOTHING;
104108 break;
109 default:
110 break;
105111 }
112 }
106113 }
107114 }
5555
5656 @Override
5757 public void visit(Code obj) {
58 if (getMethod().isStatic())
58 if (getMethod().isStatic()) {
5959 return;
60 }
6061 initializedFields = new HashSet<XField>();
6162 nullCheckedFields = new HashSet<XField>();
6263 super.visit(obj);
6869 if (opcode == PUTFIELD) {
6970 XField f = getXFieldOperand();
7071 OpcodeStack.Item item = stack.getStackItem(1);
71 if (item.getRegisterNumber() != 0)
72 if (item.getRegisterNumber() != 0) {
7273 return;
74 }
7375 initializedFields.add(f);
7476 return;
7577 }
76 if (opcode != GETFIELD)
78 if (opcode != GETFIELD) {
7779 return;
80 }
7881 OpcodeStack.Item item = stack.getStackItem(0);
79 if (item.getRegisterNumber() != 0)
82 if (item.getRegisterNumber() != 0) {
8083 return;
84 }
8185 XField f = getXFieldOperand();
8286
83 if (f == null || !f.getClassDescriptor().equals(getClassDescriptor()))
87 if (f == null || !f.getClassDescriptor().equals(getClassDescriptor())) {
8488 return;
85 if (f.isSynthetic() || f.getName().startsWith("this$"))
89 }
90 if (f.isSynthetic() || f.getName().startsWith("this$")) {
8691 return;
87 if (initializedFields.contains(f))
92 }
93 if (initializedFields.contains(f)) {
8894 return;
95 }
8996 FieldSummary fieldSummary = AnalysisContext.currentAnalysisContext().getFieldSummary();
9097
9198 ClassDescriptor superClassDescriptor = DescriptorFactory.createClassDescriptor(getSuperclassName());
9299 Set<ProgramPoint> calledFrom = fieldSummary.getCalledFromSuperConstructor(superClassDescriptor, getXMethod());
93 if (calledFrom.isEmpty())
100 if (calledFrom.isEmpty()) {
94101 return;
102 }
95103 UnreadFieldsData unreadFields = AnalysisContext.currentAnalysisContext().getUnreadFieldsData();
96104
97105 int priority;
98 if (!unreadFields.isWrittenInConstructor(f))
106 if (!unreadFields.isWrittenInConstructor(f)) {
99107 return;
108 }
100109
101 if (f.isFinal())
110 if (f.isFinal()) {
102111 priority = HIGH_PRIORITY;
103 else if (unreadFields.isWrittenDuringInitialization(f) || unreadFields.isWrittenOutsideOfInitialization(f))
112 } else if (unreadFields.isWrittenDuringInitialization(f) || unreadFields.isWrittenOutsideOfInitialization(f)) {
104113 priority = NORMAL_PRIORITY;
105 else
114 } else {
106115 priority = HIGH_PRIORITY;
116 }
107117
108118 int nextOpcode = getNextOpcode();
109119 if (nullCheckedFields.contains(f) || nextOpcode == IFNULL || nextOpcode == IFNONNULL || nextOpcode == IFEQ
114124
115125 for (ProgramPoint p : calledFrom) {
116126 XMethod upcall = getConstructorThatCallsSuperConstructor(p.method);
117 if (upcall == null)
127 if (upcall == null) {
118128 continue;
129 }
119130 Method upcallMethod = null;
120131 for (Method m : getThisClass().getMethods()) {
121132 if (m.getName().equals(upcall.getName()) && m.getSignature().equals(upcall.getSignature())) {
123134 break;
124135 }
125136 }
126 if (upcallMethod == null)
137 if (upcallMethod == null) {
127138 continue;
139 }
128140 Map<Integer, OpcodeStack.Item> putfieldsAt = PutfieldScanner.getPutfieldsFor(getThisClass(), upcallMethod, f);
129 if (putfieldsAt.isEmpty())
141 if (putfieldsAt.isEmpty()) {
130142 continue;
143 }
131144 Map.Entry<Integer, OpcodeStack.Item> e = putfieldsAt.entrySet().iterator().next();
132145 int pc = e.getKey();
133146 OpcodeStack.Item value = e.getValue();
134 if (value.isNull() || value.hasConstantValue(0))
147 if (value.isNull() || value.hasConstantValue(0)) {
135148 priority++;
149 }
136150
137151 SourceLineAnnotation fieldSetAt = SourceLineAnnotation.fromVisitedInstruction(getThisClass(), upcallMethod, pc);
138152
139153 BugInstance bug = new BugInstance(this, "UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR", priority).addClassAndMethod(
140154 this).addField(f);
141155 bug.addMethod(p.method).describe(MethodAnnotation.METHOD_SUPERCLASS_CONSTRUCTOR)
142 .addSourceLine(p.getSourceLineAnnotation()).describe(SourceLineAnnotation.ROLE_CALLED_FROM_SUPERCLASS_AT)
143 .addMethod(upcall).describe(MethodAnnotation.METHOD_CONSTRUCTOR).add(fieldSetAt)
144 .describe(SourceLineAnnotation.ROLE_FIELD_SET_TOO_LATE_AT);
156 .addSourceLine(p.getSourceLineAnnotation()).describe(SourceLineAnnotation.ROLE_CALLED_FROM_SUPERCLASS_AT)
157 .addMethod(upcall).describe(MethodAnnotation.METHOD_CONSTRUCTOR).add(fieldSetAt)
158 .describe(SourceLineAnnotation.ROLE_FIELD_SET_TOO_LATE_AT);
145159
146160 accumulator.accumulateBug(bug, this);
147161 }
152166 XMethod getConstructorThatCallsSuperConstructor(XMethod superConstructor) {
153167 FieldSummary fieldSummary = AnalysisContext.currentAnalysisContext().getFieldSummary();
154168
155 XMethod lookfor = superConstructor.getSignature().equals("()V") ? null : superConstructor;
156 for (XMethod m : getXClass().getXMethods())
157 if (m.getName().equals("<init>")) {
158 if (fieldSummary.getSuperCall(m) == lookfor)
169 XMethod lookfor = "()V".equals(superConstructor.getSignature()) ? null : superConstructor;
170 for (XMethod m : getXClass().getXMethods()) {
171 if ("<init>".equals(m.getName())) {
172 if (fieldSummary.getSuperCall(m) == lookfor) {
159173 return m;
174 }
160175 }
176 }
161177 return null;
162178 }
163179
6262
6363 private boolean isInputStream() {
6464
65 if (lastCallClass.startsWith("["))
65 if (lastCallClass.startsWith("[")) {
6666 return false;
67 }
6768 return (Subtypes2.instanceOf(lastCallClass, "java.io.InputStream")
6869 || Subtypes2.instanceOf(lastCallClass, "java.io.DataInput") || Subtypes2.instanceOf(lastCallClass,
69 "java.io.Reader")) && !Subtypes2.instanceOf(lastCallClass, "java.io.ByteArrayInputStream");
70 "java.io.Reader")) && !Subtypes2.instanceOf(lastCallClass, "java.io.ByteArrayInputStream");
7071
7172 }
7273
7374 private boolean isBufferedInputStream() {
7475 try {
75 if (lastCallClass.startsWith("["))
76 if (lastCallClass.startsWith("[")) {
7677 return false;
78 }
7779 return Repository.instanceOf(lastCallClass, "java.io.BufferedInputStream");
7880 } catch (ClassNotFoundException e) {
7981 return false;
8284
8385 private boolean isImageIOInputStream() {
8486 try {
85 if (lastCallClass.startsWith("["))
87 if (lastCallClass.startsWith("[")) {
8688 return false;
89 }
8790 return Repository.instanceOf(lastCallClass, "javax.imageio.stream.ImageInputStream");
8891 } catch (ClassNotFoundException e) {
8992 return false;
99102 lastCallSig = getSigConstantOperand();
100103 }
101104
102 if (seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE)
103 if (getNameConstantOperand().equals("available") && getSigConstantOperand().equals("()I")
105 if (seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE) {
106 if ("available".equals(getNameConstantOperand()) && "()I".equals(getSigConstantOperand())
104107 || getNameConstantOperand().startsWith("get") && getNameConstantOperand().endsWith("Length")
105 && getSigConstantOperand().equals("()I") || getClassConstantOperand().equals("java/io/File")
106 && getNameConstantOperand().equals("length") && getSigConstantOperand().equals("()J")) {
108 && "()I".equals(getSigConstantOperand()) || "java/io/File".equals(getClassConstantOperand())
109 && "length".equals(getNameConstantOperand()) && "()J".equals(getSigConstantOperand())) {
107110 sawAvailable = 70;
108111 return;
109112 }
113 }
110114 sawAvailable--;
111115 if ((seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE)
112 && getNameConstantOperand().equals("read")
116 && "read".equals(getNameConstantOperand())
113117
114 && (getSigConstantOperand().equals("([B)I") || getSigConstantOperand().equals("([BII)I")
115 || getSigConstantOperand().equals("([C)I") || getSigConstantOperand().equals("([CII)I"))
116 && isInputStream()) {
118 && ("([B)I".equals(getSigConstantOperand()) || "([BII)I".equals(getSigConstantOperand())
119 || "([C)I".equals(getSigConstantOperand()) || "([CII)I".equals(getSigConstantOperand()))
120 && isInputStream()) {
117121 sawRead = true;
118122 recentCallToAvailable = sawAvailable > 0;
119123 locationOfCall = getPC();
120124 return;
121125 }
122126 if ((seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE)
123 && (getNameConstantOperand().equals("skip") && getSigConstantOperand().equals("(J)J") || getNameConstantOperand()
124 .equals("skipBytes") && getSigConstantOperand().equals("(I)I")) && isInputStream()
125 && !isImageIOInputStream()) {
127 && ("skip".equals(getNameConstantOperand()) && "(J)J".equals(getSigConstantOperand()) || "skipBytes".equals(getNameConstantOperand()) && "(I)I".equals(getSigConstantOperand())) && isInputStream()
128 && !isImageIOInputStream()) {
126129 // if not ByteArrayInput Stream
127130 // and either no recent calls to length
128131 // or it is a BufferedInputStream
140143 if (sawRead) {
141144 accumulator.accumulateBug(
142145 new BugInstance(this, "RR_NOT_CHECKED", recentCallToAvailable ? LOW_PRIORITY : NORMAL_PRIORITY)
143 .addClassAndMethod(this).addCalledMethod(lastCallClass, lastCallMethod, lastCallSig, false),
146 .addClassAndMethod(this).addCalledMethod(lastCallClass, lastCallMethod, lastCallSig, false),
144147 SourceLineAnnotation.fromVisitedInstruction(getClassContext(), this, locationOfCall));
145148
146149 } else if (sawSkip) {
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2008 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.detect;
20
21 import org.apache.bcel.Constants;
22 import org.apache.bcel.classfile.Method;
23 import org.apache.bcel.generic.BranchInstruction;
24 import org.apache.bcel.generic.ConstantPoolGen;
25 import org.apache.bcel.generic.GOTO;
26 import org.apache.bcel.generic.GotoInstruction;
27 import org.apache.bcel.generic.ICONST;
28 import org.apache.bcel.generic.IfInstruction;
29 import org.apache.bcel.generic.Instruction;
30 import org.apache.bcel.generic.InstructionHandle;
31 import org.apache.bcel.generic.InvokeInstruction;
32 import org.apache.bcel.generic.MethodGen;
33
34 import edu.umd.cs.findbugs.BugAccumulator;
35 import edu.umd.cs.findbugs.BugInstance;
36 import edu.umd.cs.findbugs.BugReporter;
37 import edu.umd.cs.findbugs.Detector;
38 import edu.umd.cs.findbugs.SourceLineAnnotation;
39 import edu.umd.cs.findbugs.StringAnnotation;
40 import edu.umd.cs.findbugs.ba.ClassContext;
41 import edu.umd.cs.findbugs.bcel.BCELUtil;
42 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
43 import edu.umd.cs.findbugs.classfile.Global;
44 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
45 import edu.umd.cs.findbugs.classfile.engine.bcel.ValueRangeAnalysisFactory.RedundantCondition;
46 import edu.umd.cs.findbugs.classfile.engine.bcel.ValueRangeAnalysisFactory.ValueRangeAnalysis;
47
48 /**
49 * @author Tagir Valeev
50 */
51 public class RedundantConditions implements Detector {
52 private final BugAccumulator bugAccumulator;
53 private final BugReporter bugReporter;
54
55 public RedundantConditions(BugReporter bugReporter) {
56 this.bugReporter = bugReporter;
57 this.bugAccumulator = new BugAccumulator(bugReporter);
58 }
59
60 @Override
61 public void visitClassContext(ClassContext classContext) {
62 for(Method method : classContext.getJavaClass().getMethods()) {
63 MethodDescriptor methodDescriptor = BCELUtil.getMethodDescriptor(classContext.getJavaClass(), method);
64 ValueRangeAnalysis analysis;
65 try {
66 analysis = Global.getAnalysisCache().getMethodAnalysis(ValueRangeAnalysis.class, methodDescriptor);
67 } catch (CheckedAnalysisException e) {
68 bugReporter.logError("ValueRangeAnalysis failed for "+methodDescriptor, e);
69 continue;
70 }
71 if(analysis == null) {
72 continue;
73 }
74 for(RedundantCondition condition : analysis.getRedundantConditions()) {
75 int priority = getPriority(methodDescriptor, condition);
76 SourceLineAnnotation sourceLineAnnotation = SourceLineAnnotation.fromVisitedInstruction(classContext, method,
77 condition.getLocation().getHandle().getPosition());
78 BugInstance bug = new BugInstance(condition.isByType()?"UC_USELESS_CONDITION_TYPE":"UC_USELESS_CONDITION", priority)
79 .addClassAndMethod(methodDescriptor).add(new StringAnnotation(normalize(condition.getTrueCondition())));
80 if(condition.isByType()) {
81 bug.addType(condition.getSignature());
82 }
83 if(condition.isDeadCodeUnreachable() && condition.getDeadCodeLocation() != null && priority == HIGH_PRIORITY) {
84 bug.addSourceLine(methodDescriptor, condition.getDeadCodeLocation()).describe(SourceLineAnnotation.ROLE_UNREACHABLE_CODE);
85 }
86 bugAccumulator.accumulateBug(bug, sourceLineAnnotation);
87 }
88 bugAccumulator.reportAccumulatedBugs();
89 }
90
91 }
92
93 private String normalize(String condition) {
94 if(condition.startsWith("this.this$")) {
95 return condition.substring("this.".length());
96 }
97 if(condition.startsWith("this.val$")) {
98 return condition.substring("this.val$".length());
99 }
100 return condition;
101 }
102
103 private int getPriority(MethodDescriptor methodDescriptor, RedundantCondition condition) {
104 if(condition.isByType()) {
105 // Skip reports which should be reported by another detector
106 long number = condition.getNumber().longValue();
107 switch(condition.getSignature()) {
108 case "I":
109 if(number == Integer.MIN_VALUE || number == Integer.MAX_VALUE) {
110 // Will be reported as INT_VACUOUS_COMPARISON
111 return IGNORE_PRIORITY;
112 }
113 break;
114 case "C":
115 if(number <= 0) {
116 // Will be reported as INT_BAD_COMPARISON_WITH_NONNEGATIVE_VALUE
117 return IGNORE_PRIORITY;
118 }
119 break;
120 case "B":
121 if(number < Byte.MIN_VALUE || number >= Byte.MAX_VALUE) {
122 // Will be reported as INT_BAD_COMPARISON_WITH_SIGNED_BYTE
123 return IGNORE_PRIORITY;
124 }
125 break;
126 default:
127 break;
128 }
129 }
130 int priority = condition.isDeadCodeUnreachable() ? HIGH_PRIORITY : condition.isBorder()
131 || condition.getSignature().equals("Z") ? LOW_PRIORITY : NORMAL_PRIORITY;
132 // check for boolean conversion
133 if(condition.getDeadCodeLocation() != null && condition.getLiveCodeLocation() != null && condition.isDeadCodeUnreachable()) {
134 InstructionHandle deadHandle = condition.getDeadCodeLocation().getHandle();
135 InstructionHandle liveHandle = condition.getLiveCodeLocation().getHandle();
136 int deadValue = getIntValue(deadHandle);
137 int liveValue = getIntValue(liveHandle);
138 if((deadValue == 0 && liveValue == 1) || (deadValue == 1 && liveValue == 0)) {
139 InstructionHandle deadNext = deadHandle.getNext();
140 InstructionHandle liveNext = liveHandle.getNext();
141 if(deadNext != null && liveNext != null) {
142 InstructionHandle middle, after;
143 if(deadNext.getNext() == liveHandle) {
144 middle = deadNext;
145 after = liveNext;
146 } else if(liveNext.getNext() == deadHandle) {
147 middle = liveNext;
148 after = deadNext;
149 } else {
150 return priority;
151 }
152 if(!(middle.getInstruction() instanceof GOTO) || ((GOTO)middle.getInstruction()).getTarget() != after) {
153 return priority;
154 }
155 MethodGen methodGen;
156 try {
157 methodGen = Global.getAnalysisCache().getMethodAnalysis(MethodGen.class, methodDescriptor);
158 } catch (CheckedAnalysisException e) {
159 return priority;
160 }
161 InstructionHandle consumer = getConsumer(methodGen, after);
162 Instruction consumerInst = consumer == null ? null : consumer.getInstruction();
163 if(consumerInst != null) {
164 short opcode = consumerInst.getOpcode();
165 if(opcode == Constants.IADD || opcode == Constants.ISUB || opcode == Constants.IMUL
166 || opcode == Constants.ISHR || opcode == Constants.ISHL || opcode == Constants.IUSHR) {
167 // It's actually integer expression with explicit ? 1 : 0 or ? 0 : 1 operation
168 return priority;
169 }
170 }
171 if(condition.getSignature().equals("Z")) {
172 // Ignore !flag when flag value is known
173 return IGNORE_PRIORITY;
174 }
175 priority = condition.isBorder() ? LOW_PRIORITY : NORMAL_PRIORITY;
176 if(consumerInst instanceof InvokeInstruction) {
177 ConstantPoolGen constantPool = methodGen.getConstantPool();
178 String methodName = ((InvokeInstruction)consumerInst).getMethodName(constantPool);
179 // Ignore values conditions used in assertion methods
180 if((methodName.equals("assertTrue") || methodName.equals("checkArgument") || methodName.equals("isLegal")
181 || methodName.equals("isTrue"))) {
182 return liveValue == 1 ? condition.isBorder() ? IGNORE_PRIORITY : LOW_PRIORITY : HIGH_PRIORITY;
183 }
184 if((methodName.equals("assertFalse") || methodName.equals("isFalse"))) {
185 return liveValue == 0 ? condition.isBorder() ? IGNORE_PRIORITY : LOW_PRIORITY : HIGH_PRIORITY;
186 }
187 }
188 }
189 }
190 }
191 return priority;
192 }
193
194 /**
195 * @param methodGen method
196 * @param start instruction to scan
197 * @return instruction which consumes value which was on top of stack before start instruction
198 * or null if cannot be determined
199 */
200 private InstructionHandle getConsumer(MethodGen methodGen, InstructionHandle start) {
201 int depth = 1;
202 InstructionHandle cur = start;
203 while(cur != null) {
204 Instruction inst = cur.getInstruction();
205 depth -= inst.consumeStack(methodGen.getConstantPool());
206 if(depth <= 0) {
207 return cur;
208 }
209 depth += inst.produceStack(methodGen.getConstantPool());
210 if(inst instanceof BranchInstruction) {
211 if(inst instanceof GotoInstruction) {
212 cur = ((GotoInstruction)inst).getTarget();
213 continue;
214 }
215 if(!(inst instanceof IfInstruction)) {
216 return null;
217 }
218 }
219 cur = cur.getNext();
220 }
221 return null;
222 }
223
224 private int getIntValue(InstructionHandle handle) {
225 Instruction instruction = handle.getInstruction();
226 if(instruction instanceof ICONST) {
227 return ((ICONST)instruction).getValue().intValue();
228 }
229 return -1;
230 }
231
232 @Override
233 public void report() {
234 }
235 }
3232 import edu.umd.cs.findbugs.visitclass.PreorderVisitor;
3333
3434 public class RedundantInterfaces extends PreorderVisitor implements Detector, StatelessDetector {
35 private BugReporter bugReporter;
35 private final BugReporter bugReporter;
3636
3737 public RedundantInterfaces(BugReporter bugReporter) {
3838 this.bugReporter = bugReporter;
3939 }
4040
41 @Override
4142 public void visitClassContext(ClassContext classContext) {
4243 JavaClass obj = classContext.getJavaClass();
4344
4445 String superClassName = obj.getSuperclassName();
45 if (superClassName.equals("java.lang.Object"))
46 if ("java.lang.Object".equals(superClassName)) {
4647 return;
48 }
4749
4850 String[] interfaceNames = obj.getInterfaceNames();
49 if ((interfaceNames == null) || (interfaceNames.length == 0))
51 if ((interfaceNames == null) || (interfaceNames.length == 0)) {
5052 return;
53 }
5154
5255 try {
5356 JavaClass superObj = obj.getSuperClass();
5659 for (String interfaceName : interfaceNames) {
5760 if (!"java/io/Serializable".equals(interfaceName)) {
5861 JavaClass inf = Repository.lookupClass(interfaceName.replace('/', '.'));
59 if (superObj.instanceOf(inf))
62 if (superObj.instanceOf(inf)) {
6063 redundantInfNames.add(inf.getClassName());
64 }
6165 }
6266 }
6367
6468 if (redundantInfNames.size() > 0) {
6569 BugInstance bug = new BugInstance(this, "RI_REDUNDANT_INTERFACES", LOW_PRIORITY).addClass(obj);
66 for (String redundantInfName : redundantInfNames)
70 for (String redundantInfName : redundantInfNames) {
6771 bug.addClass(redundantInfName).describe("INTERFACE_TYPE");
72 }
6873
6974 bugReporter.reportBug(bug);
7075 }
7479 }
7580 }
7681
82 @Override
7783 public void report() {
7884 }
7985 }
2222
2323 /**
2424 * Warning properties for FindRefComparison detector.
25 *
25 *
2626 * @author David Hovemeyer
2727 */
2828 public class RefComparisonWarningProperty extends AbstractWarningProperty {
4646 @Override
4747 public void sawClass() {
4848 int opcode = getOpcode();
49 if ((opcode == LDC) || (opcode == LDC_W))
49 if ((opcode == LDC) || (opcode == LDC_W)) {
5050 process(getClassConstantOperand());
51 }
5152 }
5253
5354 @Override
5556 if (seen == INVOKESTATIC) {
5657 // System.out.println(getClassConstantOperand()+ "." +
5758 // getNameConstantOperand());
58 if (constantString != null && getClassConstantOperand().equals("java/lang/Class")
59 && getNameConstantOperand().equals("forName")) {
59 if (constantString != null && "java/lang/Class".equals(getClassConstantOperand())
60 && "forName".equals(getNameConstantOperand())) {
6061 process(ClassName.toSlashedClassName(constantString));
6162 }
6263
7071 }
7172 }
7273
73 // vim:ts=4
2323 import java.util.Map;
2424
2525 import org.apache.bcel.classfile.Code;
26 import org.apache.bcel.generic.BranchInstruction;
27 import org.apache.bcel.generic.Instruction;
28 import org.apache.bcel.generic.InstructionHandle;
29 import org.apache.bcel.generic.MethodGen;
2630 import org.objectweb.asm.Opcodes;
2731
2832 import edu.umd.cs.findbugs.BugInstance;
2933 import edu.umd.cs.findbugs.BugReporter;
3034 import edu.umd.cs.findbugs.SourceLineAnnotation;
3135 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
36 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
37 import edu.umd.cs.findbugs.classfile.Global;
38 import edu.umd.cs.findbugs.detect.FindNoSideEffectMethods.MethodSideEffectStatus;
39 import edu.umd.cs.findbugs.detect.FindNoSideEffectMethods.NoSideEffectMethodsDatabase;
3240
3341 public class RepeatedConditionals extends OpcodeStackDetector {
34
3542 BugReporter bugReporter;
43
44 private final NoSideEffectMethodsDatabase noSideEffectMethods;
3645
3746 public RepeatedConditionals(BugReporter bugReporter) {
3847 this.bugReporter = bugReporter;
48 this.noSideEffectMethods = Global.getAnalysisCache().getDatabase(NoSideEffectMethodsDatabase.class);
3949 reset();
4050 }
4151
5161
5262 /*
5363 * (non-Javadoc)
54 *
64 *
5565 * @see edu.umd.cs.findbugs.bcel.OpcodeStackDetector#sawOpcode(int)
5666 */
5767 int oldPC;
6979
7080 @Override
7181 public void sawOpcode(int seen) {
72 if (isRegisterStore() || isReturn(seen) || isSwitch(seen) || seen == INVOKEINTERFACE || seen == INVOKESPECIAL
73 || seen == INVOKESTATIC || seen == INVOKEVIRTUAL || seen == PUTFIELD || seen == PUTSTATIC) {
82 if (hasSideEffect(seen)) {
7483 reset();
7584 } else if (stack.getStackDepth() == 0) {
76 check: if (emptyStackLocations.size() > 1) {
77 int first = emptyStackLocations.get(emptyStackLocations.size() - 2);
78 int second = emptyStackLocations.get(emptyStackLocations.size() - 1);
79 int third = getPC();
80 if (third - second == second - first) {
81 int endOfFirstSegment = prevOpcodeLocations.get(emptyStackLocations.size() - 1);
82 int endOfSecondSegment = oldPC;
83 int opcodeAtEndOfFirst = getCodeByte(endOfFirstSegment);
84 int opcodeAtEndOfSecond = getCodeByte(endOfSecondSegment);
85
86 if (!isBranch(opcodeAtEndOfFirst) || !isBranch(opcodeAtEndOfSecond)) {
87 break check;
85 if (emptyStackLocations.size() > 1) {
86 for(int n=1; n<=emptyStackLocations.size()/2; n++) {
87 int first = emptyStackLocations.get(emptyStackLocations.size() - 2*n);
88 int second = emptyStackLocations.get(emptyStackLocations.size() - n);
89 int third = getPC();
90 if (third - second == second - first) {
91 int endOfFirstSegment = prevOpcodeLocations.get(emptyStackLocations.size() - n);
92 int endOfSecondSegment = oldPC;
93 int opcodeAtEndOfFirst = getCodeByte(endOfFirstSegment);
94 int opcodeAtEndOfSecond = getCodeByte(endOfSecondSegment);
95
96 if (!isBranch(opcodeAtEndOfFirst) || !isBranch(opcodeAtEndOfSecond)) {
97 continue;
98 }
99 if (opcodeAtEndOfFirst == Opcodes.GOTO || opcodeAtEndOfSecond == Opcodes.GOTO) {
100 continue;
101 }
102 if (opcodeAtEndOfFirst != opcodeAtEndOfSecond
103 && !areOppositeBranches(opcodeAtEndOfFirst, opcodeAtEndOfSecond)) {
104 continue;
105 }
106
107 if (first == endOfFirstSegment) {
108 continue;
109 }
110 Integer firstTarget = branchTargets.get(endOfFirstSegment);
111 Integer secondTarget = branchTargets.get(endOfSecondSegment);
112 if (firstTarget == null || secondTarget == null) {
113 continue;
114 }
115 if (firstTarget >= second && firstTarget <= endOfSecondSegment) {
116 // first jumps inside second
117 continue;
118 }
119 boolean identicalCheck = firstTarget.equals(secondTarget) && opcodeAtEndOfFirst == opcodeAtEndOfSecond
120 || (firstTarget.intValue() == getPC() && opcodeAtEndOfFirst != opcodeAtEndOfSecond);
121 if(!compareCode(first, endOfFirstSegment, second, endOfSecondSegment, !identicalCheck)) {
122 continue;
123 }
124 SourceLineAnnotation firstSourceLine = SourceLineAnnotation.fromVisitedInstructionRange(getClassContext(),
125 this, first, endOfFirstSegment - 1);
126 SourceLineAnnotation secondSourceLine = SourceLineAnnotation.fromVisitedInstructionRange(getClassContext(),
127 this, second, endOfSecondSegment - 1);
128
129 int priority = HIGH_PRIORITY;
130 if (firstSourceLine.getStartLine() == -1 || firstSourceLine.getStartLine() != secondSourceLine.getEndLine()) {
131 priority++;
132 }
133 if (stack.isJumpTarget(second)) {
134 priority++;
135 }
136 if (!identicalCheck) {
137 // opposite checks
138 priority += 2;
139 }
140
141 BugInstance bug = new BugInstance(this, "RpC_REPEATED_CONDITIONAL_TEST", priority).addClassAndMethod(this)
142 .add(firstSourceLine).add(secondSourceLine);
143 bugReporter.reportBug(bug);
88144 }
89 if (opcodeAtEndOfFirst == Opcodes.GOTO || opcodeAtEndOfSecond == Opcodes.GOTO) {
90 break check;
91 }
92 if (opcodeAtEndOfFirst != opcodeAtEndOfSecond
93 && !areOppositeBranches(opcodeAtEndOfFirst, opcodeAtEndOfSecond)) {
94 break check;
95 }
96
97 byte[] code = getCode().getCode();
98 if (first == endOfFirstSegment) {
99 break check;
100 }
101 for (int i = first; i < endOfFirstSegment; i++) {
102 if (code[i] != code[i - first + second]) {
103 break check;
104 }
105 }
106 if (false) {
107 System.out.println(getFullyQualifiedMethodName());
108 System.out.println(first + " ... " + endOfFirstSegment + " : " + OPCODE_NAMES[opcodeAtEndOfFirst]);
109 System.out.println(second + " ... " + endOfSecondSegment + " : " + OPCODE_NAMES[opcodeAtEndOfSecond]);
110 }
111 SourceLineAnnotation firstSourceLine = SourceLineAnnotation.fromVisitedInstructionRange(getClassContext(),
112 this, first, endOfFirstSegment - 1);
113 SourceLineAnnotation secondSourceLine = SourceLineAnnotation.fromVisitedInstructionRange(getClassContext(),
114 this, second, endOfSecondSegment - 1);
115
116 int priority = HIGH_PRIORITY;
117 if (firstSourceLine.getStartLine() == -1 || firstSourceLine.getStartLine() != secondSourceLine.getEndLine()) {
118 priority++;
119 }
120 if (stack.isJumpTarget(second)) {
121 priority++;
122 }
123 Integer firstTarget = branchTargets.get(endOfFirstSegment);
124 Integer secondTarget = branchTargets.get(endOfSecondSegment);
125 if (firstTarget == null || secondTarget == null) {
126 break check;
127 }
128 if (firstTarget.equals(secondTarget) && opcodeAtEndOfFirst == opcodeAtEndOfSecond
129 || firstTarget.intValue() == getPC()) {
130 // identical checks;
131 } else {
132 // opposite checks
133 priority += 2;
134 }
135
136 BugInstance bug = new BugInstance(this, "RpC_REPEATED_CONDITIONAL_TEST", priority).addClassAndMethod(this)
137 .add(firstSourceLine).add(secondSourceLine);
138 bugReporter.reportBug(bug);
139145 }
140146 }
141147 emptyStackLocations.add(getPC());
143149
144150 }
145151 oldPC = getPC();
152 }
153
154 private boolean compareCode(int first, int endOfFirstSegment, int second,
155 int endOfSecondSegment, boolean oppositeChecks) {
156 if(endOfFirstSegment-first != endOfSecondSegment-second) {
157 return false;
158 }
159 MethodGen methodGen = null;
160 try {
161 methodGen = Global.getAnalysisCache().getMethodAnalysis(MethodGen.class, getMethodDescriptor());
162 } catch (CheckedAnalysisException e) {
163 // Ignore
164 }
165 if(methodGen == null) {
166 // MethodGen is absent for some reason: fallback to byte-to-byte comparison
167 byte[] code = getCode().getCode();
168 for (int i = first; i < endOfFirstSegment; i++) {
169 if (code[i] != code[i - first + second]) {
170 return false;
171 }
172 }
173 return true;
174 }
175 InstructionHandle firstHandle = methodGen.getInstructionList().findHandle(first);
176 InstructionHandle secondHandle = methodGen.getInstructionList().findHandle(second);
177 while(true) {
178 if(firstHandle == null || secondHandle == null) {
179 return false;
180 }
181 if(firstHandle.getPosition() >= endOfFirstSegment) {
182 return secondHandle.getPosition() >= endOfSecondSegment;
183 }
184 if(secondHandle.getPosition() >= endOfSecondSegment) {
185 return firstHandle.getPosition() >= endOfFirstSegment;
186 }
187 Instruction firstInstruction = firstHandle.getInstruction();
188 Instruction secondInstruction = secondHandle.getInstruction();
189 if(firstInstruction instanceof BranchInstruction && secondInstruction instanceof BranchInstruction) {
190 int firstOpcode = firstInstruction.getOpcode();
191 int secondOpcode = secondInstruction.getOpcode();
192 if(firstOpcode != secondOpcode) {
193 return false;
194 }
195 int firstTarget = ((BranchInstruction)firstInstruction).getTarget().getPosition();
196 int secondTarget = ((BranchInstruction)secondInstruction).getTarget().getPosition();
197 if(firstTarget == second) {
198 if(oppositeChecks || secondTarget <= endOfSecondSegment) {
199 return false;
200 }
201 } else {
202 if(!((firstTarget >= first && firstTarget <= endOfFirstSegment && firstTarget - first == secondTarget - second)
203 || firstTarget == secondTarget)) {
204 return false;
205 }
206 }
207 } else {
208 if(!firstInstruction.equals(secondInstruction)) {
209 return false;
210 }
211 }
212 firstHandle = firstHandle.getNext();
213 secondHandle = secondHandle.getNext();
214 }
215 }
216
217 private boolean hasSideEffect(int seen) {
218 if(seen == INVOKEVIRTUAL || seen == INVOKESPECIAL || seen == INVOKEINTERFACE || seen == INVOKESTATIC) {
219 return noSideEffectMethods.is(getMethodDescriptorOperand(), MethodSideEffectStatus.SE, MethodSideEffectStatus.OBJ);
220 }
221 return isRegisterStore() || isReturn(seen) || isSwitch(seen) || seen == INVOKEDYNAMIC || seen == PUTFIELD
222 || seen == PUTSTATIC;
146223 }
147224
148225 private void reset() {
3333
3434 public class ResolveAllReferences extends PreorderVisitor implements Detector {
3535
36 private BugReporter bugReporter;
36 private final BugReporter bugReporter;
3737
3838 public ResolveAllReferences(BugReporter bugReporter) {
3939 this.bugReporter = bugReporter;
6767 }
6868 }
6969
70 @Override
7071 public void visitClassContext(ClassContext classContext) {
7172 classContext.getJavaClass().accept(this);
7273
7374 }
7475
76 @Override
7577 public void report() {
7678 }
7779
7981 String className2 = obj.getClassName();
8082
8183 defined.add(className2);
82 for (Method m : obj.getMethods())
84 for (Method m : obj.getMethods()) {
8385 if (!m.isPrivate()) {
8486 String name = getMemberName(obj, className2, m.getNameIndex(), m.getSignatureIndex());
8587 defined.add(name);
8688 }
87 for (Field f : obj.getFields())
89 }
90 for (Field f : obj.getFields()) {
8891 if (!f.isPrivate()) {
8992 String name = getMemberName(obj, className2, f.getNameIndex(), f.getSignatureIndex());
9093 defined.add(name);
9194 }
95 }
9296 }
9397
9498 private String getClassName(JavaClass c, int classIndex) {
106110 }
107111
108112 private boolean find(JavaClass target, String name, String signature) throws ClassNotFoundException {
109 if (target == null)
113 if (target == null) {
110114 return false;
115 }
111116 String ref = getMemberName(target.getClassName(), name, signature);
112 if (defined.contains(ref))
117 if (defined.contains(ref)) {
113118 return true;
114 if (find(target.getSuperClass(), name, signature))
119 }
120 if (find(target.getSuperClass(), name, signature)) {
115121 return true;
116 for (JavaClass i : target.getInterfaces())
117 if (find(i, name, signature))
122 }
123 for (JavaClass i : target.getInterfaces()) {
124 if (find(i, name, signature)) {
118125 return true;
126 }
127 }
119128 return false;
120129 }
121130
126135 Constant[] constants = cp.getConstantPool();
127136 checkConstant: for (int i = 0; i < constants.length; i++) {
128137 Constant co = constants[i];
129 if (co instanceof ConstantDouble || co instanceof ConstantLong)
138 if (co instanceof ConstantDouble || co instanceof ConstantLong) {
130139 i++;
140 }
131141 if (co instanceof ConstantClass) {
132142 String ref = getClassName(obj, i);
133 if ((ref.startsWith("java") || ref.startsWith("org.w3c.dom")) && !defined.contains(ref))
143 if ((ref.startsWith("java") || ref.startsWith("org.w3c.dom")) && !defined.contains(ref)) {
134144 bugReporter.reportBug(new BugInstance(this, "VR_UNRESOLVABLE_REFERENCE", NORMAL_PRIORITY).addClass(obj)
135145 .addString(ref));
146 }
136147
137148 } else if (co instanceof ConstantFieldref) {
138149 // do nothing until we handle static fields defined in
153164
154165 try {
155166 JavaClass target = Repository.lookupClass(className);
156 if (!find(target, name, signature))
167 if (!find(target, name, signature)) {
157168 bugReporter.reportBug(new BugInstance(this, "VR_UNRESOLVABLE_REFERENCE", NORMAL_PRIORITY).addClass(obj)
158169 .addString(getMemberName(target.getClassName(), name, signature)));
170 }
159171
160172 } catch (ClassNotFoundException e) {
161173 bugReporter.reportMissingClass(e);
6161
6262 /**
6363 * RuntimeExceptionCapture
64 *
64 *
6565 * @author Brian Goetz
6666 * @author Bill Pugh
6767 * @author David Hovemeyer
6969 public class RuntimeExceptionCapture extends OpcodeStackDetector implements StatelessDetector {
7070 private static final boolean DEBUG = SystemProperties.getBoolean("rec.debug");
7171
72 private BugReporter bugReporter;
73
74 private List<ExceptionCaught> catchList = new ArrayList<ExceptionCaught>();
75
76 private List<ExceptionThrown> throwList = new ArrayList<ExceptionThrown>();
77
78 private BugAccumulator accumulator;
72 private final BugReporter bugReporter;
73
74 private final List<ExceptionCaught> catchList = new ArrayList<ExceptionCaught>();
75
76 private final List<ExceptionThrown> throwList = new ArrayList<ExceptionThrown>();
77
78 private final BugAccumulator accumulator;
7979
8080 private static class ExceptionCaught {
8181 public String exceptionClass;
117117 accumulator.reportAccumulatedBugs();
118118 }
119119
120 @Override
121 public void visitAfter(Code obj) {
120 @Override
121 public void visitAfter(Code obj) {
122122 for (ExceptionCaught caughtException : catchList) {
123123 Set<String> thrownSet = new HashSet<String>();
124124 for (ExceptionThrown thrownException : throwList) {
125125 if (thrownException.offset >= caughtException.startOffset && thrownException.offset < caughtException.endOffset) {
126126 thrownSet.add(thrownException.exceptionClass);
127 if (thrownException.exceptionClass.equals(caughtException.exceptionClass))
127 if (thrownException.exceptionClass.equals(caughtException.exceptionClass)) {
128128 caughtException.seen = true;
129 }
129130 }
130131 }
131132 int catchClauses = 0;
132 if (caughtException.exceptionClass.equals("java.lang.Exception") && !caughtException.seen) {
133 if ("java.lang.Exception".equals(caughtException.exceptionClass) && !caughtException.seen) {
133134 // Now we have a case where Exception is caught, but not thrown
134135 boolean rteCaught = false;
135136 for (ExceptionCaught otherException : catchList) {
136137 if (otherException.startOffset == caughtException.startOffset
137138 && otherException.endOffset == caughtException.endOffset) {
138139 catchClauses++;
139 if (otherException.exceptionClass.equals("java.lang.RuntimeException"))
140 if ("java.lang.RuntimeException".equals(otherException.exceptionClass)) {
140141 rteCaught = true;
142 }
141143 }
142144 }
143145 int range = caughtException.endOffset - caughtException.startOffset;
144146 if (!rteCaught) {
145147 int priority = LOW_PRIORITY + 1;
146 if (range > 300)
148 if (range > 300) {
147149 priority--;
148 else if (range < 30)
150 } else if (range < 30) {
149151 priority++;
150 if (catchClauses > 1)
152 }
153 if (catchClauses > 1) {
151154 priority++;
152 if (thrownSet.size() > 1)
155 }
156 if (thrownSet.size() > 1) {
153157 priority--;
154 if (caughtException.dead)
158 }
159 if (caughtException.dead) {
155160 priority--;
161 }
156162 accumulator.accumulateBug(new BugInstance(this, "REC_CATCH_EXCEPTION", priority).addClassAndMethod(this),
157163 SourceLineAnnotation.fromVisitedInstruction(getClassContext(), this, caughtException.sourcePC));
158164 }
167173 try {
168174 super.visit(obj);
169175 int type = obj.getCatchType();
170 if (type == 0)
176 if (type == 0) {
171177 return;
178 }
172179 String name = getConstantPool().constantToString(getConstantPool().getConstant(type));
173180
174181 ExceptionCaught caughtException = new ExceptionCaught(name, obj.getStartPC(), obj.getEndPC(), obj.getHandlerPC());
215222 OpcodeStack.Item item = stack.getStackItem(0);
216223 String signature = item.getSignature();
217224 if (signature != null && signature.length() > 0) {
218 if (signature.startsWith("L"))
225 if (signature.startsWith("L")) {
219226 signature = SignatureConverter.convert(signature);
220 else
227 } else {
221228 signature = signature.replace('/', '.');
229 }
222230 throwList.add(new ExceptionThrown(signature, getPC()));
223231 }
224232 }
228236 case INVOKESPECIAL:
229237 case INVOKESTATIC:
230238 String className = getClassConstantOperand();
231 if (!className.startsWith("["))
239 if (!className.startsWith("[")) {
232240 try {
233241 XClass c = Global.getAnalysisCache().getClassAnalysis(XClass.class,
234242 DescriptorFactory.createClassDescriptor(className));
235243 XMethod m = Hierarchy2.findInvocationLeastUpperBound(c, getNameConstantOperand(), getSigConstantOperand(),
236244 seen == INVOKESTATIC, seen == INVOKEINTERFACE);
237 if (m == null)
245 if (m == null) {
238246 break;
247 }
239248 String[] exceptions = m.getThrownExceptions();
240 if (exceptions != null)
241 for (String name : exceptions)
249 if (exceptions != null) {
250 for (String name : exceptions) {
242251 throwList.add(new ExceptionThrown(ClassName.toDottedClassName(name), getPC()));
252 }
253 }
243254 } catch (MissingClassException e) {
244255 bugReporter.reportMissingClass(e.getClassDescriptor());
245256 } catch (CheckedAnalysisException e) {
246257 bugReporter.logError("Error looking up " + className, e);
247258 }
259 }
248260 break;
249261 default:
250262 break;
254266
255267 }
256268
257 // vim:ts=4
7373 boolean isGUIClass;
7474
7575 boolean isEjbImplClass;
76
76
7777 boolean isJSPClass;
7878
7979 boolean foundSynthetic;
117117 private boolean superClassHasVoidConstructor;
118118
119119 private boolean directlyImplementsExternalizable;
120
121 private final boolean testingEnabled;
120122
121123 // private JavaClass serializable;
122124 // private JavaClass collection;
125127
126128 public SerializableIdiom(BugReporter bugReporter) {
127129 this.bugReporter = bugReporter;
130 testingEnabled = SystemProperties.getBoolean("report_TESTING_pattern_in_standard_detectors");
128131 }
129132
130133 @Override
135138
136139 private void flush() {
137140 if (!isAbstract && !((sawReadExternal && sawWriteExternal) || (sawReadObject && sawWriteObject))) {
138 for (BugInstance aFieldWarningList : fieldWarningList)
141 for (BugInstance aFieldWarningList : fieldWarningList) {
139142 bugReporter.reportBug(aFieldWarningList);
143 }
140144 }
141145 fieldWarningList.clear();
142146 }
154158 String superClassname = obj.getSuperclassName();
155159 // System.out.println("superclass of " + getClassName() + " is " +
156160 // superClassname);
157 isEnum = superClassname.equals("java.lang.Enum");
158 if (isEnum)
161 isEnum = "java.lang.Enum".equals(superClassname);
162 if (isEnum) {
159163 return;
164 }
160165 int flags = obj.getAccessFlags();
161166 isAbstract = (flags & ACC_ABSTRACT) != 0 || (flags & ACC_INTERFACE) != 0;
162167 isAnonymousInnerClass = anonymousInnerClassNamePattern.matcher(getClassName()).matches();
163168 innerClassHasOuterInstance = false;
164169 for (Field f : obj.getFields()) {
165 if (f.getName().equals("this$0")) {
170 if ("this$0".equals(f.getName())) {
166171 innerClassHasOuterInstance = true;
167172 break;
168173 }
186191 // Does this class directly implement Serializable?
187192 String[] interface_names = obj.getInterfaceNames();
188193 for (String interface_name : interface_names) {
189 if (interface_name.equals("java.io.Externalizable")) {
194 if ("java.io.Externalizable".equals(interface_name)) {
190195 directlyImplementsExternalizable = true;
191196 isExternalizable = true;
192197 if (DEBUG) {
193198 System.out.println("Directly implements Externalizable: " + getClassName());
194199 }
195 } else if (interface_name.equals("java.io.Serializable")) {
200 } else if ("java.io.Serializable".equals(interface_name)) {
196201 implementsSerializableDirectly = true;
197202 isSerializable = true;
198203 if (DEBUG) {
223228 superClassHasReadObject = false;
224229 superClassImplementsSerializable = isSerializable && !implementsSerializableDirectly;
225230 ClassDescriptor superclassDescriptor = getXClass().getSuperclassDescriptor();
226 if (superclassDescriptor != null)
231 if (superclassDescriptor != null) {
227232 try {
228233 XClass superXClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, superclassDescriptor);
229234 if (superXClass != null) {
234239 DescriptorFactory.createClassDescriptor(java.io.Serializable.class));
235240 superClassHasVoidConstructor = false;
236241 for (XMethod m : superXClass.getXMethods()) {
237 if (m.getName().equals("<init>") && m.getSignature().equals("()V") && !m.isPrivate()) {
242 if ("<init>".equals(m.getName()) && "()V".equals(m.getSignature()) && !m.isPrivate()) {
238243 superClassHasVoidConstructor = true;
239244 }
240 if (m.getName().equals("readObject") && m.getSignature().equals("(Ljava/io/ObjectInputStream;)V")
241 && m.isPrivate())
245 if ("readObject".equals(m.getName()) && "(Ljava/io/ObjectInputStream;)V".equals(m.getSignature())
246 && m.isPrivate()) {
242247 superClassHasReadObject = true;
248 }
243249 }
244250 }
245251 } catch (ClassNotFoundException e) {
247253 } catch (CheckedAnalysisException e) {
248254 AnalysisContext.logError("Error while analyzing " + obj.getClassName(), e);
249255 }
256 }
250257
251258 // Is this a GUI or other class that is rarely serialized?
252259
253260 isGUIClass = false;
254261 isEjbImplClass = false;
255 if (true || !directlyImplementsExternalizable && !implementsSerializableDirectly) {
262 if (true /*|| !directlyImplementsExternalizable && !implementsSerializableDirectly*/) {
256263 isEjbImplClass = Subtypes2.instanceOf(obj, "javax.ejb.SessionBean");
257264 isJSPClass = Subtypes2.isJSP(obj);
258265 isGUIClass = (Subtypes2.instanceOf(obj, "java.lang.Throwable") || Subtypes2.instanceOf(obj, "java.awt.Component")
259266 || Subtypes2.instanceOf(obj, "java.awt.Component$AccessibleAWTComponent")
260267 || Subtypes2.instanceOf(obj, "java.awt.event.ActionListener") || Subtypes2.instanceOf(obj,
261 "java.util.EventListener"));
268 "java.util.EventListener"));
262269 if (!isGUIClass) {
263270 JavaClass o = obj;
264271 while (o != null) {
284291 if (isSerializable) {
285292 for (Method m : obj.getMethods()) {
286293
287 if (m.getName().equals("readObject") && m.getSignature().equals("(Ljava/io/ObjectInputStream;)V"))
294 if ("readObject".equals(m.getName()) && "(Ljava/io/ObjectInputStream;)V".equals(m.getSignature())) {
288295 sawReadObject = true;
289 else if (m.getName().equals("readResolve") && m.getSignature().startsWith("()"))
296 } else if ("readResolve".equals(m.getName()) && m.getSignature().startsWith("()")) {
290297 sawReadResolve = true;
291 else if (m.getName().equals("readObjectNoData") && m.getSignature().equals("()V"))
298 } else if ("readObjectNoData".equals(m.getName()) && "()V".equals(m.getSignature())) {
292299 sawReadObject = true;
293 else if (m.getName().equals("writeObject") && m.getSignature().equals("(Ljava/io/ObjectOutputStream;)V"))
300 } else if ("writeObject".equals(m.getName()) && "(Ljava/io/ObjectOutputStream;)V".equals(m.getSignature())) {
294301 sawWriteObject = true;
302 }
295303 }
296304 for (Field f : obj.getFields()) {
297 if (f.isTransient())
305 if (f.isTransient()) {
298306 seenTransientField = true;
307 }
299308 }
300309 }
301310 }
307316 || sawWriteObject
308317 || seenTransientField
309318 || AnalysisContext.currentAnalysisContext().getUnreadFieldsData()
310 .existsStrongEvidenceForIntendedSerialization(this.getClassDescriptor());
319 .existsStrongEvidenceForIntendedSerialization(this.getClassDescriptor());
311320 }
312321
313322 @Override
314323 public void visitAfter(JavaClass obj) {
315 if (isEnum)
324 if (isEnum) {
316325 return;
326 }
317327 if (DEBUG) {
318328 System.out.println(getDottedClassName());
319329 System.out.println(" hasPublicVoidConstructor: " + hasPublicVoidConstructor);
331341
332342 XField fieldX = e.getKey();
333343 int priority = NORMAL_PRIORITY;
334 if (transientFieldsSetInConstructor.contains(e.getKey()))
344 if (transientFieldsSetInConstructor.contains(e.getKey())) {
335345 priority--;
336
337 if (isGUIClass)
346 }
347
348 if (isGUIClass) {
338349 priority++;
339 if (isEjbImplClass)
350 }
351 if (isEjbImplClass) {
340352 priority++;
341 if (isJSPClass)
353 }
354 if (isJSPClass) {
342355 priority++;
343 if (e.getValue() < 3)
356 }
357 if (e.getValue() < 3) {
344358 priority++;
345 if (transientFieldsSetToDefaultValueInConstructor.contains(e.getKey()))
359 }
360 if (transientFieldsSetToDefaultValueInConstructor.contains(e.getKey())) {
346361 priority++;
362 }
347363 if (obj.isAbstract()) {
348364 priority++;
349 if (priority < Priorities.LOW_PRIORITY)
365 if (priority < Priorities.LOW_PRIORITY) {
350366 priority = Priorities.LOW_PRIORITY;
367 }
351368 }
352369
353370 try {
354371 double isSerializable = DeepSubtypeAnalysis.isDeepSerializable(fieldX.getSignature());
355 if (isSerializable < 0.6)
372 if (isSerializable < 0.6) {
356373 priority++;
374 }
357375 } catch (ClassNotFoundException e1) {
358376 // ignore it
359377 }
367385 if (isSerializable && !isExternalizable && !superClassHasVoidConstructor && !superClassImplementsSerializable) {
368386 int priority = implementsSerializableDirectly || seenTransientField ? HIGH_PRIORITY
369387 : (sawSerialVersionUID ? NORMAL_PRIORITY : LOW_PRIORITY);
370 if (isGUIClass || isEjbImplClass || isJSPClass)
388 if (isGUIClass || isEjbImplClass || isJSPClass) {
371389 priority++;
390 }
372391 bugReporter.reportBug(new BugInstance(this, "SE_NO_SUITABLE_CONSTRUCTOR", priority).addClass(getThisClass()
373392 .getClassName()));
374393 }
375394 // Downgrade class-level warnings if it's a GUI or EJB-implementation
376395 // class.
377396 int priority = (isGUIClass || isEjbImplClass || isJSPClass) ? LOW_PRIORITY : NORMAL_PRIORITY;
378 if (obj.getClassName().endsWith("_Stub"))
397 if (obj.getClassName().endsWith("_Stub")) {
379398 priority++;
380
381 if (isExternalizable && !hasPublicVoidConstructor && !isAbstract)
399 }
400
401 if (isExternalizable && !hasPublicVoidConstructor && !isAbstract) {
382402 bugReporter.reportBug(new BugInstance(this, "SE_NO_SUITABLE_CONSTRUCTOR_FOR_EXTERNALIZATION",
383403 directlyImplementsExternalizable ? HIGH_PRIORITY : NORMAL_PRIORITY).addClass(getThisClass().getClassName()));
384 if (!foundSynthetic)
404 }
405 if (!foundSynthetic) {
385406 priority++;
386 if (seenTransientField)
407 }
408 if (seenTransientField) {
387409 priority--;
410 }
388411 if (!isAnonymousInnerClass && !isExternalizable && !isGUIClass && !obj.isAbstract() && isSerializable && !isAbstract
389 && !sawSerialVersionUID && !isEjbImplClass && !isJSPClass)
412 && !sawSerialVersionUID && !isEjbImplClass && !isJSPClass) {
390413 bugReporter.reportBug(new BugInstance(this, "SE_NO_SERIALVERSIONID", priority).addClass(this));
391
392 if (writeObjectIsSynchronized && !foundSynchronizedMethods)
414 }
415
416 if (writeObjectIsSynchronized && !foundSynchronizedMethods) {
393417 bugReporter.reportBug(new BugInstance(this, "WS_WRITEOBJECT_SYNC", LOW_PRIORITY).addClass(this));
418 }
394419 }
395420
396421 @Override
398423
399424 int accessFlags = obj.getAccessFlags();
400425 boolean isSynchronized = (accessFlags & ACC_SYNCHRONIZED) != 0;
401 if (getMethodName().equals("<init>") && getMethodSig().equals("()V") && (accessFlags & ACC_PUBLIC) != 0)
426 if ("<init>".equals(getMethodName()) && "()V".equals(getMethodSig()) && (accessFlags & ACC_PUBLIC) != 0) {
402427 hasPublicVoidConstructor = true;
403 if (!getMethodName().equals("<init>") && isSynthetic(obj))
428 }
429 if (!"<init>".equals(getMethodName()) && isSynthetic(obj))
430 {
404431 foundSynthetic = true;
405 // System.out.println(methodName + isSynchronized);
406
407 if (getMethodName().equals("readExternal") && getMethodSig().equals("(Ljava/io/ObjectInput;)V")) {
432 // System.out.println(methodName + isSynchronized);
433 }
434
435 if ("readExternal".equals(getMethodName()) && "(Ljava/io/ObjectInput;)V".equals(getMethodSig())) {
408436 sawReadExternal = true;
409 if (DEBUG && !obj.isPrivate())
437 if (DEBUG && !obj.isPrivate()) {
410438 System.out.println("Non-private readExternal method in: " + getDottedClassName());
411 } else if (getMethodName().equals("writeExternal") && getMethodSig().equals("(Ljava/io/Objectoutput;)V")) {
439 }
440 } else if ("writeExternal".equals(getMethodName()) && "(Ljava/io/Objectoutput;)V".equals(getMethodSig())) {
412441 sawWriteExternal = true;
413 if (DEBUG && !obj.isPrivate())
442 if (DEBUG && !obj.isPrivate()) {
414443 System.out.println("Non-private writeExternal method in: " + getDottedClassName());
415 } else if (getMethodName().equals("readResolve") && getMethodSig().startsWith("()") && isSerializable) {
444 }
445 } else if ("readResolve".equals(getMethodName()) && getMethodSig().startsWith("()") && isSerializable) {
416446 sawReadResolve = true;
417 if (!getMethodSig().equals("()Ljava/lang/Object;"))
447 if (!"()Ljava/lang/Object;".equals(getMethodSig())) {
418448 bugReporter.reportBug(new BugInstance(this, "SE_READ_RESOLVE_MUST_RETURN_OBJECT", HIGH_PRIORITY)
419 .addClassAndMethod(this));
420 else if (obj.isStatic())
449 .addClassAndMethod(this));
450 } else if (obj.isStatic()) {
421451 bugReporter.reportBug(new BugInstance(this, "SE_READ_RESOLVE_IS_STATIC", HIGH_PRIORITY).addClassAndMethod(this));
422 else if (obj.isPrivate())
452 } else if (obj.isPrivate()) {
423453 try {
424454 Set<ClassDescriptor> subtypes = AnalysisContext.currentAnalysisContext().getSubtypes2()
425455 .getSubtypes(getClassDescriptor());
426456 if (subtypes.size() > 1) {
427457 BugInstance bug = new BugInstance(this, "SE_PRIVATE_READ_RESOLVE_NOT_INHERITED", NORMAL_PRIORITY)
428 .addClassAndMethod(this);
458 .addClassAndMethod(this);
429459 boolean nasty = false;
430 for (ClassDescriptor subclass : subtypes)
460 for (ClassDescriptor subclass : subtypes) {
431461 if (!subclass.equals(getClassDescriptor())) {
432462
433463 XClass xSub = AnalysisContext.currentXFactory().getXClass(subclass);
437467 nasty = true;
438468 }
439469 }
440 if (nasty)
470 }
471 if (nasty) {
441472 bug.setPriority(HIGH_PRIORITY);
442 else if (!getThisClass().isPublic())
473 } else if (!getThisClass().isPublic()) {
443474 bug.setPriority(LOW_PRIORITY);
475 }
444476 bugReporter.reportBug(bug);
445477 }
446478
447479 } catch (ClassNotFoundException e) {
448480 bugReporter.reportMissingClass(e);
449481 }
450
451 } else if (getMethodName().equals("readObject") && getMethodSig().equals("(Ljava/io/ObjectInputStream;)V")
482 }
483
484 } else if ("readObject".equals(getMethodName()) && "(Ljava/io/ObjectInputStream;)V".equals(getMethodSig())
452485 && isSerializable) {
453486 sawReadObject = true;
454 if (!obj.isPrivate())
487 if (!obj.isPrivate()) {
455488 bugReporter.reportBug(new BugInstance(this, "SE_METHOD_MUST_BE_PRIVATE", isExternalizable ? NORMAL_PRIORITY : HIGH_PRIORITY).addClassAndMethod(this));
456
457 } else if (getMethodName().equals("readObjectNoData") && getMethodSig().equals("()V") && isSerializable) {
458
459 if (!obj.isPrivate())
489 }
490
491 } else if ("readObjectNoData".equals(getMethodName()) && "()V".equals(getMethodSig()) && isSerializable) {
492
493 if (!obj.isPrivate()) {
460494 bugReporter.reportBug(new BugInstance(this, "SE_METHOD_MUST_BE_PRIVATE", isExternalizable ? NORMAL_PRIORITY : HIGH_PRIORITY).addClassAndMethod(this));
461
462 } else if (getMethodName().equals("writeObject") && getMethodSig().equals("(Ljava/io/ObjectOutputStream;)V")
495 }
496
497 } else if ("writeObject".equals(getMethodName()) && "(Ljava/io/ObjectOutputStream;)V".equals(getMethodSig())
463498 && isSerializable) {
464499 sawWriteObject = true;
465 if (!obj.isPrivate())
500 if (!obj.isPrivate()) {
466501 bugReporter.reportBug(new BugInstance(this, "SE_METHOD_MUST_BE_PRIVATE", isExternalizable ? NORMAL_PRIORITY : HIGH_PRIORITY).addClassAndMethod(this));
502 }
467503 }
468504
469505 if (isSynchronized) {
470 if (getMethodName().equals("readObject") && getMethodSig().equals("(Ljava/io/ObjectInputStream;)V") && isSerializable)
506 if ("readObject".equals(getMethodName()) && "(Ljava/io/ObjectInputStream;)V".equals(getMethodSig()) && isSerializable) {
471507 bugReporter.reportBug(new BugInstance(this, "RS_READOBJECT_SYNC",isExternalizable ? LOW_PRIORITY : NORMAL_PRIORITY).addClassAndMethod(this));
472 else if (getMethodName().equals("writeObject") && getMethodSig().equals("(Ljava/io/ObjectOutputStream;)V")
473 && isSerializable)
508 } else if ("writeObject".equals(getMethodName()) && "(Ljava/io/ObjectOutputStream;)V".equals(getMethodSig())
509 && isSerializable) {
474510 writeObjectIsSynchronized = true;
475 else
511 } else {
476512 foundSynchronizedMethods = true;
513 }
477514 }
478515 super.visit(obj);
479516
481518
482519 boolean isSynthetic(FieldOrMethod obj) {
483520 Attribute[] a = obj.getAttributes();
484 for (Attribute aA : a)
485 if (aA instanceof Synthetic)
521 for (Attribute aA : a) {
522 if (aA instanceof Synthetic) {
486523 return true;
524 }
525 }
487526 return false;
488527 }
489528
500539 XField xField = getXFieldOperand();
501540 if (xField != null && xField.getClassDescriptor().equals(getClassDescriptor())) {
502541 Item first = stack.getStackItem(0);
503
542
504543 boolean isPutOfDefaultValue = first.isNull(); // huh?? ||
505 // first.isInitialParameter();
506 if (!isPutOfDefaultValue && first.getConstant() != null) {
544 // first.isInitialParameter();
545 if (!isPutOfDefaultValue && first.getConstant() != null && !first.isArray()) {
507546 Object constant = first.getConstant();
508 if (constant instanceof Number && ((Number) constant).intValue() == 0 || constant.equals(Boolean.FALSE))
547 if (constant instanceof Number && ((Number) constant).intValue() == 0 || constant.equals(Boolean.FALSE)) {
509548 isPutOfDefaultValue = true;
549 }
510550 }
511551
512552 if (isPutOfDefaultValue) {
513 if (getMethodName().equals("<init>"))
553 if ("<init>".equals(getMethodName())) {
514554 transientFieldsSetToDefaultValueInConstructor.add(xField);
555 }
515556 } else {
516557 String nameOfField = getNameConstantOperand();
517558
518559 if (transientFieldsUpdates.containsKey(xField)) {
519 if (getMethodName().equals("<init>"))
560 if ("<init>".equals(getMethodName())) {
520561 transientFieldsSetInConstructor.add(xField);
521 else
562 } else {
522563 transientFieldsUpdates.put(xField, transientFieldsUpdates.get(xField) + 1);
564 }
523565 } else if (fieldsThatMightBeAProblem.containsKey(nameOfField)) {
524566 try {
525567
531573 if (isSerializable <= 0.2) {
532574 XField f = fieldsThatMightBeAProblem.get(nameOfField);
533575
534 String sig = f.getSignature();
576 String sig = f.getSignature();
535577 // System.out.println("Field signature: " +
536578 // sig);
537579 // System.out.println("Class stored: " +
539581 String genSig = "L" + classStored.getClassName().replace('.', '/') + ";";
540582 if (!sig.equals(genSig)) {
541583 double bias = 0.0;
542 if (!getMethodName().equals("<init>"))
584 if (!"<init>".equals(getMethodName())) {
543585 bias = 1.0;
586 }
544587 int priority = computePriority(isSerializable, bias);
545588
546589 fieldWarningList.add(new BugInstance(this, "SE_BAD_FIELD_STORE", priority)
547 .addClass(getThisClass().getClassName()).addField(f).addType(genSig)
548 .describe("TYPE_FOUND").addSourceLine(this));
590 .addClass(getThisClass().getClassName()).addField(f).addType(genSig)
591 .describe("TYPE_FOUND").addSourceLine(this));
549592 }
550593 }
551 } catch (Exception e) {
594 } catch (ClassNotFoundException e) {
552595 // ignore it
553596 }
554597 }
563606 public void visit(Field obj) {
564607 int flags = obj.getAccessFlags();
565608 String genericSignature = obj.getGenericSignature();
566 if (genericSignature != null && genericSignature.startsWith("T"))
609 if (genericSignature != null && genericSignature.startsWith("T")) {
567610 return;
611 }
568612 FieldSummary fieldSummary = AnalysisContext.currentAnalysisContext().getFieldSummary();
569613 Item summary = fieldSummary.getSummary(getXField());
570614 String fieldSig = summary.getSignature();
577621 || Subtypes2.instanceOf(fieldType, "javax.ejb.EJBHome")
578622 || Subtypes2.instanceOf(fieldType, "javax.ejb.EJBObject")
579623 || Subtypes2.instanceOf(fieldType, "javax.naming.Context")) {
580 if (obj.isTransient())
624 if (testingEnabled && obj.isTransient()) {
581625 bugReporter.reportBug(new BugInstance(this, "TESTING", NORMAL_PRIORITY).addClass(this)
582626 .addVisitedField(this)
583627 .addString("EJB implementation classes should not have fields of this type"));
628 }
584629 return;
585630 }
586631 }
592637 transientFieldsUpdates.put(getXField(), 0);
593638 } else if (reportTransientFieldOfNonSerializableClass) {
594639 bugReporter.reportBug(new BugInstance(this, "SE_TRANSIENT_FIELD_OF_NONSERIALIZABLE_CLASS", NORMAL_PRIORITY)
595 .addClass(this).addVisitedField(this));
640 .addClass(this).addVisitedField(this));
596641 }
597642 } else if (getClassName().indexOf("ObjectStreamClass") == -1 && isSerializable && !isExternalizable
598 && fieldSig.indexOf("L") >= 0 && !obj.isTransient() && !obj.isStatic()) {
643 && fieldSig.indexOf('L') >= 0 && !obj.isTransient() && !obj.isStatic()) {
599644 if (DEBUG) {
600645 System.out.println("Examining non-transient field with name: " + getFieldName() + ", sig: " + fieldSig);
601646 }
602647 XField xfield = getXField();
603648 Type type = TypeFrameModelingVisitor.getType(xfield);
604 if (type instanceof ReferenceType)
605 try {
606 ReferenceType rtype = (ReferenceType) type;
607
608 double isSerializable = DeepSubtypeAnalysis.isDeepSerializable(rtype);
609 if (DEBUG) {
610 System.out.println(" isSerializable: " + isSerializable);
611 }
612 if (isSerializable < 1.0)
613 fieldsThatMightBeAProblem.put(obj.getName(), xfield);
614 if (isSerializable < 0.9) {
615 ReferenceType problemType = DeepSubtypeAnalysis.getLeastSerializableTypeComponent(rtype);
616
617 // Priority is LOW for GUI classes (unless explicitly marked
618 // Serializable),
619 // HIGH if the class directly implements Serializable,
620 // NORMAL otherwise.
621 int priority = computePriority(isSerializable, 0);
622 if (!strongEvidenceForIntendedSerialization()) {
623 if (obj.getName().startsWith("this$"))
649 if (type instanceof ReferenceType) {
650 try {
651 ReferenceType rtype = (ReferenceType) type;
652
653 double isSerializable = DeepSubtypeAnalysis.isDeepSerializable(rtype);
654 if (DEBUG) {
655 System.out.println(" isSerializable: " + isSerializable);
656 }
657 if (isSerializable < 1.0) {
658 fieldsThatMightBeAProblem.put(obj.getName(), xfield);
659 }
660 if (isSerializable < 0.9) {
661 ReferenceType problemType = DeepSubtypeAnalysis.getLeastSerializableTypeComponent(rtype);
662
663 // Priority is LOW for GUI classes (unless explicitly marked
664 // Serializable),
665 // HIGH if the class directly implements Serializable,
666 // NORMAL otherwise.
667 int priority = computePriority(isSerializable, 0);
668 if (!strongEvidenceForIntendedSerialization()) {
669 if (obj.getName().startsWith("this$")) {
670 priority = Math.max(priority, NORMAL_PRIORITY);
671 }
672 if (innerClassHasOuterInstance) {
673 if (isAnonymousInnerClass) {
674 priority += 2;
675 } else {
676 priority += 1;
677 }
678 }
679 if (isGUIClass || isEjbImplClass || isJSPClass) {
680 priority++;
681 }
682 } else if (isGUIClass || isEjbImplClass || isJSPClass) {
624683 priority = Math.max(priority, NORMAL_PRIORITY);
625 if (innerClassHasOuterInstance) {
626 if (isAnonymousInnerClass)
627 priority += 2;
628 else
629 priority += 1;
630 }
631 if (isGUIClass || isEjbImplClass || isJSPClass)
632 priority++;
633 } else if (isGUIClass || isEjbImplClass || isJSPClass)
634 priority = Math.max(priority, NORMAL_PRIORITY);
635 if (DEBUG)
636 System.out.println("SE_BAD_FIELD: " + getThisClass().getClassName() + " " + obj.getName() + " "
637 + isSerializable + " " + implementsSerializableDirectly + " " + sawSerialVersionUID + " "
638 + isGUIClass + " " + isEjbImplClass);
639 // Report is queued until after the entire class has been
640 // seen.
641
642 if (obj.getName().equals("this$0"))
643 fieldWarningList.add(new BugInstance(this, "SE_BAD_FIELD_INNER_CLASS", priority).addClass(getThisClass()
644 .getClassName()));
645 else if (isSerializable < 0.9)
646 fieldWarningList.add(new BugInstance(this, "SE_BAD_FIELD", priority)
647 .addClass(getThisClass().getClassName())
648 .addField(xfield).addType(problemType)
649 .describe("TYPE_FOUND"));
650 } else if (!isGUIClass && !isEjbImplClass && !isJSPClass && obj.getName().equals("this$0"))
651 fieldWarningList.add(new BugInstance(this, "SE_INNER_CLASS", implementsSerializableDirectly ? NORMAL_PRIORITY
652 : LOW_PRIORITY).addClass(getThisClass().getClassName()));
653 } catch (ClassNotFoundException e) {
654 if (DEBUG) {
655 System.out.println("Caught ClassNotFoundException");
656 }
657 bugReporter.reportMissingClass(e);
658 }
659 }
660
661 if (!getFieldName().startsWith("this") && isSynthetic(obj))
684 }
685 if (DEBUG)
686 {
687 System.out.println("SE_BAD_FIELD: " + getThisClass().getClassName() + " " + obj.getName() + " "
688 + isSerializable + " " + implementsSerializableDirectly + " " + sawSerialVersionUID + " "
689 + isGUIClass + " " + isEjbImplClass);
690 // Report is queued until after the entire class has been
691 // seen.
692 }
693
694 if ("this$0".equals(obj.getName())) {
695 fieldWarningList.add(new BugInstance(this, "SE_BAD_FIELD_INNER_CLASS", priority).addClass(getThisClass()
696 .getClassName()));
697 } else if (isSerializable < 0.9) {
698 fieldWarningList.add(new BugInstance(this, "SE_BAD_FIELD", priority)
699 .addClass(getThisClass().getClassName())
700 .addField(xfield).addType(problemType)
701 .describe("TYPE_FOUND"));
702 }
703 } else if (!isGUIClass && !isEjbImplClass && !isJSPClass && "this$0".equals(obj.getName())) {
704 fieldWarningList.add(new BugInstance(this, "SE_INNER_CLASS", implementsSerializableDirectly ? NORMAL_PRIORITY
705 : LOW_PRIORITY).addClass(getThisClass().getClassName()));
706 }
707 } catch (ClassNotFoundException e) {
708 if (DEBUG) {
709 System.out.println("Caught ClassNotFoundException");
710 }
711 bugReporter.reportMissingClass(e);
712 }
713 }
714 }
715
716 if (!getFieldName().startsWith("this") && isSynthetic(obj)) {
662717 foundSynthetic = true;
663 if (!getFieldName().equals("serialVersionUID"))
718 }
719 if (!"serialVersionUID".equals(getFieldName())) {
664720 return;
721 }
665722 int mask = ACC_STATIC | ACC_FINAL;
666 if (!fieldSig.equals("I") && !fieldSig.equals("J"))
723 if (!"I".equals(fieldSig) && !"J".equals(fieldSig)) {
667724 return;
668 if ((flags & mask) == mask && fieldSig.equals("I")) {
725 }
726 if ((flags & mask) == mask && "I".equals(fieldSig)) {
669727 bugReporter.reportBug(new BugInstance(this, "SE_NONLONG_SERIALVERSIONID", LOW_PRIORITY).addClass(this)
670728 .addVisitedField(this));
671729 sawSerialVersionUID = true;
685743 private int computePriority(double isSerializable, double bias) {
686744 int priority = (int) (1.9 + isSerializable * 3 + bias);
687745
688 if (strongEvidenceForIntendedSerialization())
746 if (strongEvidenceForIntendedSerialization()) {
689747 priority--;
690 else if (sawSerialVersionUID && priority > NORMAL_PRIORITY)
748 } else if (sawSerialVersionUID && priority > NORMAL_PRIORITY) {
691749 priority--;
692 else
750 } else {
693751 priority = Math.max(priority, NORMAL_PRIORITY);
752 }
694753 return priority;
695754 }
696755
3636 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
3737
3838 public class StartInConstructor extends BytecodeScanningDetector implements StatelessDetector {
39 private BugReporter bugReporter;
39 private final BugReporter bugReporter;
4040
4141 private final BugAccumulator bugAccumulator;
4242
5353
5454 @Override
5555 public void visit(Code obj) {
56 if (getMethodName().equals("<init>") && (getMethod().isPublic() || getMethod().isProtected())) {
56 if ("<init>".equals(getMethodName()) && (getMethod().isPublic() || getMethod().isProtected())) {
5757 super.visit(obj);
5858 bugAccumulator.reportAccumulatedBugs();
5959 }
6161
6262 @Override
6363 public void sawOpcode(int seen) {
64 if (seen == INVOKEVIRTUAL && getNameConstantOperand().equals("start") && getSigConstantOperand().equals("()V")) {
64 if (seen == INVOKEVIRTUAL && "start".equals(getNameConstantOperand()) && "()V".equals(getSigConstantOperand())) {
6565 try {
6666 if (Hierarchy.isSubtype(getDottedClassConstantOperand(), "java.lang.Thread")) {
6767 int priority = Priorities.NORMAL_PRIORITY;
68 if (getPC() + 4 >= getCode().getCode().length)
68 if (getPC() + 4 >= getCode().getCode().length) {
6969 priority = Priorities.LOW_PRIORITY;
70 }
7071 BugInstance bug = new BugInstance(this, "SC_START_IN_CTOR", priority).addClassAndMethod(this)
7172 .addCalledMethod(this);
7273 Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
7374 Set<ClassDescriptor> directSubtypes = subtypes2.getDirectSubtypes(getClassDescriptor());
7475 if (!directSubtypes.isEmpty()) {
75 for (ClassDescriptor sub : directSubtypes)
76 for (ClassDescriptor sub : directSubtypes) {
7677 bug.addClass(sub).describe(ClassAnnotation.SUBCLASS_ROLE);
78 }
7779 bug.setPriority(Priorities.HIGH_PRIORITY);
7880 }
7981 bugAccumulator.accumulateBug(bug, this);
5555 * {@link java.util.Calendar} is unsafe for multithreaded use, static fields
5656 * look suspicous. To work correctly, all access would need to be synchronized
5757 * by the client which cannot be guaranteed.
58 *
58 *
5959 * @author Daniel Schneller
6060 */
6161 public class StaticCalendarDetector extends OpcodeStackDetector {
101101 /** Stores current LDF */
102102 private LockDataflow currentLockDataFlow;
103103
104 private Map<XField, BugInstance> pendingBugs = new HashMap<XField, BugInstance>();
104 private final Map<XField, BugInstance> pendingBugs = new HashMap<XField, BugInstance>();
105105
106106 /**
107107 * Creates a new instance of this Detector.
108 *
108 *
109109 * @param aReporter
110110 * {@link BugReporter} instance to report found problems to.
111111 */
137137 if (constant instanceof ConstantClass) {
138138 ConstantClass cc = (ConstantClass) constant;
139139 @SlashedClassName String className = cc.getBytes(pool);
140 if (className.equals("java/util/Calendar") || className.equals("java/text/DateFormat")) {
140 if ("java/util/Calendar".equals(className) || "java/text/DateFormat".equals(className)) {
141141 sawDateClass = true;
142142 break;
143143 }
144144 try {
145145 ClassDescriptor cDesc = DescriptorFactory.createClassDescriptor(className);
146
146
147147 if (subtypes2.isSubtype(cDesc, calendarType) || subtypes2.isSubtype(cDesc, dateFormatType)) {
148148 sawDateClass = true;
149149 break;
150150 }
151151 } catch (ClassNotFoundException e) {
152 reporter.reportMissingClass(e);
153 }
154
155
152 reporter.reportMissingClass(e);
153 }
154
155
156156 }
157157 }
158158 }
174174 return;
175175 }
176176 String superclassName = getSuperclassName();
177 if (!aField.isStatic() && !superclassName.equals("java/lang/Enum"))
177 if (!aField.isStatic() && !"java/lang/Enum".equals(superclassName)) {
178178 return;
179 if (!aField.isPublic() && !aField.isProtected())
179 }
180 if (!aField.isPublic() && !aField.isProtected()) {
180181 return;
182 }
181183 ClassDescriptor classOfField = DescriptorFactory.createClassDescriptorFromFieldSignature(aField.getSignature());
182184 String tBugType = null;
183185 int priority = aField.isPublic() && aField.isFinal() && aField.getName().equals(aField.getName().toUpperCase())
184186 && getThisClass().isPublic() ? HIGH_PRIORITY : NORMAL_PRIORITY;
185 if (classOfField != null)
187 if (classOfField != null) {
186188 try {
187189 if (subtypes2.isSubtype(classOfField, calendarType)) {
188190 tBugType = "STCAL_STATIC_CALENDAR_INSTANCE";
190192 } else if (subtypes2.isSubtype(classOfField, dateFormatType)) {
191193 tBugType = "STCAL_STATIC_SIMPLE_DATE_FORMAT_INSTANCE";
192194 }
193 if (getClassContext().getXClass().usesConcurrency())
195 if (getClassContext().getXClass().usesConcurrency()) {
194196 priority--;
197 }
195198 if (tBugType != null) {
196199
197200 pendingBugs.put(getXField(), new BugInstance(this, tBugType, priority).addClass(currentClass).addField(this));
199202 } catch (ClassNotFoundException e) {
200203 AnalysisContext.reportMissingClass(e);
201204 }
205 }
202206
203207 }
204208
205209 /*
206210 * (non-Javadoc)
207 *
211 *
208212 * @see
209213 * edu.umd.cs.findbugs.visitclass.BetterVisitor#visitMethod(org.apache.bcel
210214 * .classfile.Method)
211215 */
212216 @Override
213217 public void visitMethod(Method obj) {
214 if (sawDateClass)
218 if (sawDateClass) {
215219 try {
216220 super.visitMethod(obj);
217221 currentMethod = obj;
222226 } catch (DataflowAnalysisException e) {
223227 reporter.logError("Synchronization check in Static Calendar Detector caught an error.", e);
224228 }
229 }
225230 }
226231
227232 @Override
238243 * {@link java.util.Calendar} or {@link java.text.DateFormat} fields. The
239244 * {@link OpcodeStack} is used to determine if an invocation is done on such
240245 * a static field.
241 *
246 *
242247 * @param seen
243248 * An opcode to be analyzed
244249 * @see edu.umd.cs.findbugs.visitclass.DismantleBytecode#sawOpcode(int)
289294 return;
290295 }
291296
292 if (getMethodName().equals("<clinit>") && field.getClassName().equals(getDottedClassName()))
293 return;
297 if ("<clinit>".equals(getMethodName()) && field.getClassName().equals(getDottedClassName())) {
298 return;
299 }
294300 String invokedName = getNameConstantOperand();
295 if (invokedName.startsWith("get"))
296 return;
297 if (invokedName.equals("equals") && numArguments == 1) {
301 if (invokedName.startsWith("get")) {
302 return;
303 }
304 if ("equals".equals(invokedName) && numArguments == 1) {
298305 OpcodeStack.Item passedAsArgument = stack.getStackItem(0);
299306 field = passedAsArgument.getXField();
300307 if (field == null || !field.isStatic()) {
304311
305312 if (!SystemProperties.getBoolean(PROP_SKIP_SYNCHRONIZED_CHECK)) {
306313 // check synchronization
307 if (isLocked())
314 if (isLocked()) {
308315 return;
316 }
309317 }
310318
311319 // if we get here, we want to generate a report, depending on the
315323 tBugType = "STCAL_INVOKE_ON_STATIC_CALENDAR_INSTANCE";
316324 } else if (isDateFormat) {
317325 tBugType = "STCAL_INVOKE_ON_STATIC_DATE_FORMAT_INSTANCE";
318 } else
326 } else {
319327 throw new IllegalStateException("Not possible");
328 }
320329 int priority;
321 if (amVisitingMainMethod())
330 if (amVisitingMainMethod()) {
322331 priority = LOW_PRIORITY;
323 else {
324 if (getClassContext().getXClass().usesConcurrency())
332 } else {
333 if (getClassContext().getXClass().usesConcurrency()) {
325334 priority = NORMAL_PRIORITY;
326 else
335 } else {
327336 priority = LOW_PRIORITY;
328 if (invokedName.startsWith("set") || invokedName.equals("format") || invokedName.equals("add")
329 || invokedName.equals("clear") || invokedName.equals("parse") || invokedName.equals("applyPattern"))
337 }
338 if (invokedName.startsWith("set") || "format".equals(invokedName) || "add".equals(invokedName)
339 || "clear".equals(invokedName) || "parse".equals(invokedName) || "applyPattern".equals(invokedName)) {
330340 priority--;
341 }
331342 }
332343 bugAccumulator.accumulateBug(new BugInstance(this, tBugType, priority).addClassAndMethod(this).addCalledMethod(this)
333344 .addOptionalField(field), this);
337348 }
338349 }
339350
340 /**
341 * @return
342 */
343351 private boolean isLocked() {
344352 try {
345353 if (currentMethod != null && currentLockDataFlow != null && currentCFG != null) {
4242
4343 /**
4444 * Constructor. Created Stream objects will be marked as uninteresting.
45 *
45 *
4646 * @param streamBaseClass
4747 * the base class of the stream objects created by the factory
4848 * @param className
5959 this.fieldSig = fieldSig;
6060 }
6161
62 @Override
6263 public Stream createStream(Location location, ObjectType type, ConstantPoolGen cpg,
6364 RepositoryLookupFailureCallback lookupFailureCallback) {
6465
6566 Instruction ins = location.getHandle().getInstruction();
66 if (ins.getOpcode() != Constants.GETSTATIC)
67 if (ins.getOpcode() != Constants.GETSTATIC) {
6768 return null;
69 }
6870
6971 GETSTATIC getstatic = (GETSTATIC) ins;
7072 if (!className.equals(getstatic.getClassName(cpg)) || !fieldName.equals(getstatic.getName(cpg))
71 || !fieldSig.equals(getstatic.getSignature(cpg)))
73 || !fieldSig.equals(getstatic.getSignature(cpg))) {
7274 return null;
75 }
7376
7477 return new Stream(location, type.getClassName(), streamBaseClass).setIgnoreImplicitExceptions(true).setIsOpenOnCreation(
7578 true);
7679 }
7780 }
7881
79 // vim:ts=3
4949 * can customize how they work for different kinds of streams
5050 */
5151 public class Stream extends ResourceCreationPoint implements Comparable<Stream> {
52 private String streamBase;
52 private final String streamBase;
5353
5454 private boolean isUninteresting;
5555
7474 * Constructor. By default, Stream objects are marked as uninteresting.
7575 * setInteresting("BUG_TYPE") must be called explicitly to mark the Stream
7676 * as interesting.
77 *
77 *
7878 * @param location
7979 * where the stream is created
8080 * @param streamClass
9292
9393 /**
9494 * Mark this Stream as interesting.
95 *
95 *
9696 * @param bugType
9797 * the bug type that should be reported if the stream is not
9898 * closed on all paths out of the method
124124
125125 /**
126126 * Set the number of the parameter which passes the stream instance.
127 *
127 *
128128 * @param instanceParam
129129 * number of the parameter passing the stream instance
130130 */
180180 }
181181
182182 public boolean isStreamOpen(BasicBlock basicBlock, InstructionHandle handle, ConstantPoolGen cpg, ResourceValueFrame frame) {
183 if (isOpenOnCreation)
184 return false;
183 if (isOpenOnCreation) {
184 return false;
185 }
185186
186187 Instruction ins = handle.getInstruction();
187 if (!(ins instanceof INVOKESPECIAL))
188 return false;
188 if (!(ins instanceof INVOKESPECIAL)) {
189 return false;
190 }
189191
190192 // Does this instruction open the stream?
191193 INVOKESPECIAL inv = (INVOKESPECIAL) ins;
207209 // (Basically, we may not see the exact original stream class,
208210 // even though it's the same instance.)
209211
210 return inv.getName(cpg).equals("close") && inv.getSignature(cpg).equals("()V");
212 return "close".equals(inv.getName(cpg)) && "()V".equals(inv.getSignature(cpg));
211213
212214 }
213215
216218
217219 public boolean isStreamClose(BasicBlock basicBlock, InstructionHandle handle, ConstantPoolGen cpg, ResourceValueFrame frame,
218220 RepositoryLookupFailureCallback lookupFailureCallback) {
219 if (!mightCloseStream(basicBlock, handle, cpg))
220 return false;
221 if (!mightCloseStream(basicBlock, handle, cpg)) {
222 return false;
223 }
221224
222225 Instruction ins = handle.getInstruction();
223226
225228 // Does this instruction close the stream?
226229 InvokeInstruction inv = (InvokeInstruction) ins;
227230
228 if (!frame.isValid() || !getInstanceValue(frame, inv, cpg).isInstance())
231 if (!frame.isValid() || !getInstanceValue(frame, inv, cpg).isInstance()) {
229232 return false;
233 }
230234
231235 // It's a close if the invoked class is any subtype of the stream
232236 // base class.
234238 // even though it's the same instance.)
235239 try {
236240 String classClosed = inv.getClassName(cpg);
237 return Hierarchy.isSubtype(classClosed, streamBase) || Hierarchy.isSubtype(streamBase, classClosed) ;
241
242 if (relatedType(classClosed) ) {
243 return true;
244 }
245 if ("java.io.ObjectOutput".equals(classClosed)) {
246 return relatedType("java.io.ObjectOutputStream");
247 } else if ("java.io.ObjectInput".equals(classClosed)) {
248 return relatedType("java.io.ObjectInputStream");
249 }
250 return false;
238251 } catch (ClassNotFoundException e) {
239252 lookupFailureCallback.reportMissingClass(e);
240253 return false;
244257 return false;
245258 }
246259
260 private boolean relatedType(String classClosed) throws ClassNotFoundException {
261 return Hierarchy.isSubtype(classClosed, streamBase) || Hierarchy.isSubtype(streamBase, classClosed);
262 }
263
247264 private ResourceValue getInstanceValue(ResourceValueFrame frame, InvokeInstruction inv, ConstantPoolGen cpg) {
248265 int numConsumed = inv.consumeStack(cpg);
249 if (numConsumed == Constants.UNPREDICTABLE)
266 if (numConsumed == Constants.UNPREDICTABLE) {
250267 throw new IllegalStateException();
268 }
251269 return frame.getValue(frame.getNumSlots() - numConsumed);
252270 }
253271
262280
263281 @Override
264282 public boolean equals(Object o) {
265 if (!(o instanceof Stream))
266 return false;
283 if (!(o instanceof Stream)) {
284 return false;
285 }
267286 Stream other = (Stream) o;
268 if (!getLocation().equals(other.getLocation()))
269 return false;
270 if (!streamBase.equals(other.streamBase))
271 return false;
272 if (!getResourceClass().equals(other.getResourceClass()))
273 return false;
274 if (instanceParam != other.instanceParam)
275 return false;
287 if (!getLocation().equals(other.getLocation())) {
288 return false;
289 }
290 if (!streamBase.equals(other.streamBase)) {
291 return false;
292 }
293 if (!getResourceClass().equals(other.getResourceClass())) {
294 return false;
295 }
296 if (instanceParam != other.instanceParam) {
297 return false;
298 }
276299 return true;
277300 }
278301
302 @Override
279303 public int compareTo(Stream other) {
280304 int cmp;
281305
286310 // different parameters to be distinguished.
287311
288312 cmp = getLocation().compareTo(other.getLocation());
289 if (cmp != 0)
313 if (cmp != 0) {
290314 return cmp;
315 }
291316 cmp = streamBase.compareTo(other.streamBase);
292 if (cmp != 0)
317 if (cmp != 0) {
293318 return cmp;
319 }
294320 cmp = getResourceClass().compareTo(other.getResourceClass());
295 if (cmp != 0)
321 if (cmp != 0) {
296322 return cmp;
323 }
297324 cmp = instanceParam - other.instanceParam;
298 if (cmp != 0)
325 if (cmp != 0) {
299326 return cmp;
327 }
300328
301329 return 0;
302330 }
303331 }
304332
305 // vim:ts=3
2323
2424 /**
2525 * Set of streams that are in an equivalence class.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class StreamEquivalenceClass {
30 private HashSet<Stream> memberSet;
30 private final HashSet<Stream> memberSet;
3131
3232 private boolean isClosed;
3333
4141
4242 /**
4343 * Add a single member to the equivalence class.
44 *
44 *
4545 * @param member
4646 * the member Stream
4747 */
5858
5959 /**
6060 * Add all members of other StreamEquivalenceClass to this one.
61 *
61 *
6262 * @param other
6363 * the other StreamEquivalenceClass
6464 */
7979 }
8080 }
8181
82 // vim:ts=4
3232
3333 /**
3434 * Constructor.
35 *
35 *
3636 * @param source
3737 * Location where stream is opened
3838 * @param target
4343 this.target = target;
4444 }
4545
46 @Override
4647 public int compareTo(StreamEscape other) {
4748 int cmp = source.compareTo(other.source);
48 if (cmp != 0)
49 if (cmp != 0) {
4950 return cmp;
51 }
5052 return target.compareTo(other.target);
5153 }
5254
5759
5860 @Override
5961 public boolean equals(Object o) {
60 if (!(o instanceof StreamEscape))
62 if (!(o instanceof StreamEscape)) {
6163 return false;
64 }
6265 StreamEscape other = (StreamEscape) o;
6366 return source.equals(other.source) && target.equals(other.target);
6467 }
6972 }
7073 }
7174
72 // vim:ts=3
3131 public interface StreamFactory {
3232 /**
3333 * Determine if a Stream is created at given location.
34 *
34 *
3535 * @param location
3636 * the Location
3737 * @param type
5151 RepositoryLookupFailureCallback lookupFailureCallback);
5252 }
5353
54 // vim:ts=3
3636 * (in this case, Streams).
3737 */
3838 public class StreamFrameModelingVisitor extends ResourceValueFrameModelingVisitor {
39 private StreamResourceTracker resourceTracker;
39 private final StreamResourceTracker resourceTracker;
4040
41 private Stream stream;
41 private final Stream stream;
4242
4343 private Location location;
4444
8787 // If needed, update frame status
8888 if (status != -1) {
8989 frame.setStatus(status);
90 if (created)
90 if (created) {
9191 frame.setValue(frame.getNumSlots() - 1, ResourceValue.instance());
92 }
9293 }
9394
9495 }
105106 String methodName = inv.getMethodName(cpg);
106107 String methodSig = inv.getSignature(cpg);
107108 if (inv.getOpcode() == Constants.INVOKEVIRTUAL
108 && (methodName.equals("load") || methodName.equals("loadFromXml") || methodName.equals("store") || methodName
109 .equals("save")) && className.equals("java.util.Properties"))
109 && ("load".equals(methodName) || "loadFromXml".equals(methodName) || "store".equals(methodName) || "save".equals(methodName)) && "java.util.Properties".equals(className)) {
110110 escapes = false;
111 if (inv.getOpcode() == Constants.INVOKEVIRTUAL && (methodName.equals("load") || methodName.equals("store"))
112 && className.equals("java.security.KeyStore"))
111 }
112 if (inv.getOpcode() == Constants.INVOKEVIRTUAL && ("load".equals(methodName) || "store".equals(methodName))
113 && "java.security.KeyStore".equals(className)) {
113114 escapes = false;
115 }
114116 if (inv.getOpcode() == Constants.INVOKEVIRTUAL && "getChannel".equals(methodName)
115 && "()Ljava/nio/channels/FileChannel;".equals(methodSig))
117 && "()Ljava/nio/channels/FileChannel;".equals(methodSig)) {
116118 escapes = true;
119 }
117120
118121 if (FindOpenStream.DEBUG && escapes) {
119122 System.out.println("ESCAPE at " + location + " at call to " + className + "." + methodName + ":" + methodSig);
120123 }
121124
122125 // Record the fact that this might be a stream escape
123 if (stream.getOpenLocation() != null)
126 if (stream.getOpenLocation() != null) {
124127 resourceTracker.addStreamEscape(stream, location);
128 }
125129
126130 return escapes;
127131 }
128132 }
129133
130 // vim:ts=3
4545 /**
4646 * Resource tracker which determines where streams are created, and how they are
4747 * used within the method.
48 *
48 *
4949 * @author David Hovemeyer
5050 */
5151 public class StreamResourceTracker implements ResourceTracker<Stream> {
52 private StreamFactory[] streamFactoryList;
53
54 private RepositoryLookupFailureCallback lookupFailureCallback;
52 private final StreamFactory[] streamFactoryList;
53
54 private final RepositoryLookupFailureCallback lookupFailureCallback;
5555
5656 private ResourceCollection<Stream> resourceCollection;
5757
5858 /**
5959 * Map of locations where streams are opened to the actual Stream objects.
6060 */
61 private Map<Location, Stream> streamOpenLocationMap;
61 private final Map<Location, Stream> streamOpenLocationMap;
6262
6363 /**
6464 * Set of all open locations and escapes of uninteresting streams.
6565 */
6666 // private HashSet<Location> uninterestingStreamEscapeSet;
67 private HashSet<Stream> uninterestingStreamEscapeSet;
67 private final HashSet<Stream> uninterestingStreamEscapeSet;
6868
6969 /**
7070 * Set of all (potential) stream escapes.
7171 */
72 private TreeSet<StreamEscape> streamEscapeSet;
72 private final TreeSet<StreamEscape> streamEscapeSet;
7373
7474 /**
7575 * Map of individual streams to equivalence classes. Any time a stream "A"
7777 * class. If any stream in an equivalence class is closed, then we consider
7878 * all of the streams in the equivalence class as having been closed.
7979 */
80 private Map<Stream, StreamEquivalenceClass> streamEquivalenceMap;
80 private final Map<Stream, StreamEquivalenceClass> streamEquivalenceMap;
8181
8282 /**
8383 * Constructor.
84 *
84 *
8585 * @param streamFactoryList
8686 * array of StreamFactory objects which determine where streams
8787 * are created
108108
109109 /**
110110 * Indicate that a stream escapes at the given target Location.
111 *
111 *
112112 * @param source
113113 * the Stream that is escaping
114114 * @param target
117117 public void addStreamEscape(Stream source, Location target) {
118118 StreamEscape streamEscape = new StreamEscape(source, target);
119119 streamEscapeSet.add(streamEscape);
120 if (FindOpenStream.DEBUG)
120 if (FindOpenStream.DEBUG) {
121121 System.out.println("Adding potential stream escape " + streamEscape);
122 }
122123 }
123124
124125 /**
132133 for (Iterator<StreamEscape> i = streamEscapeSet.iterator(); i.hasNext();) {
133134 StreamEscape streamEscape = i.next();
134135 if (!isStreamOpenLocation(streamEscape.target)) {
135 if (FindOpenStream.DEBUG)
136 if (FindOpenStream.DEBUG) {
136137 System.out.println("Eliminating false stream escape " + streamEscape);
138 }
137139 i.remove();
138140 }
139141 }
158160
159161 for (StreamEscape streamEscape : streamEscapeSet) {
160162 if (isUninterestingStreamEscape(streamEscape.source)) {
161 if (FindOpenStream.DEBUG)
163 if (FindOpenStream.DEBUG) {
162164 System.out.println("Propagating stream escape " + streamEscape);
165 }
163166 Stream target = streamOpenLocationMap.get(streamEscape.target);
164 if (target == null)
167 if (target == null) {
165168 throw new IllegalStateException();
169 }
166170 uninterestingStreamEscapeSet.add(target);
167171
168172 // Combine equivalence classes for source and target
183187 /**
184188 * Determine if an uninteresting stream escapes at given location.
185189 * markTransitiveUninterestingStreamEscapes() should be called first.
186 *
190 *
187191 * @param stream
188192 * the stream
189193 * @return true if an uninteresting stream escapes at the location
194198
195199 /**
196200 * Indicate that a stream is constructed at this Location.
197 *
201 *
198202 * @param streamOpenLocation
199203 * the Location
200204 * @param stream
201205 * the Stream opened at this Location
202206 */
203207 public void addStreamOpenLocation(Location streamOpenLocation, Stream stream) {
204 if (FindOpenStream.DEBUG)
208 if (FindOpenStream.DEBUG) {
205209 System.out.println("Stream open location at " + streamOpenLocation);
210 }
206211 streamOpenLocationMap.put(streamOpenLocation, stream);
207 if (stream.isUninteresting())
212 if (stream.isUninteresting()) {
208213 uninterestingStreamEscapeSet.add(stream);
214 }
209215 }
210216
211217 /**
212218 * Get the equivalence class for given stream. May only be called if
213219 * markTransitiveUninterestingStreamEscapes() has been called.
214 *
220 *
215221 * @param stream
216222 * the stream
217223 * @return the set containing the equivalence class for the given stream
222228
223229 /**
224230 * Determine if given Location is a stream open location point.
225 *
231 *
226232 * @param location
227233 * the Location
228234 */
230236 return streamOpenLocationMap.get(location) != null;
231237 }
232238
239 @Override
233240 public Stream isResourceCreation(BasicBlock basicBlock, InstructionHandle handle, ConstantPoolGen cpg) {
234241
235242 // Use precomputed map of Locations to Stream creations,
236243 // if present. Note that we don't care about preexisting
237244 // resources here.
238 if (resourceCollection != null)
245 if (resourceCollection != null) {
239246 return resourceCollection.getCreatedResource(new Location(handle, basicBlock));
247 }
240248
241249 Instruction ins = handle.getInstruction();
242 if (!(ins instanceof TypedInstruction))
250 if (!(ins instanceof TypedInstruction)) {
243251 return null;
252 }
244253
245254 Type type = ((TypedInstruction) ins).getType(cpg);
246 if (!(type instanceof ObjectType))
255 if (!(type instanceof ObjectType)) {
247256 return null;
257 }
248258
249259 Location location = new Location(handle, basicBlock);
250260
252262 // look at the location and possibly identify a created stream.
253263 for (StreamFactory aStreamFactoryList : streamFactoryList) {
254264 Stream stream = aStreamFactoryList.createStream(location, (ObjectType) type, cpg, lookupFailureCallback);
255 if (stream != null)
265 if (stream != null) {
256266 return stream;
267 }
257268 }
258269
259270 return null;
264275 return resource.isStreamOpen(basicBlock, handle, cpg, frame);
265276 }
266277
278 @Override
267279 public boolean isResourceClose(BasicBlock basicBlock, InstructionHandle handle, ConstantPoolGen cpg, Stream resource,
268280 ResourceValueFrame frame) {
269281 return resource.isStreamClose(basicBlock, handle, cpg, frame, lookupFailureCallback);
270282 }
271283
284 @Override
272285 public boolean mightCloseResource(BasicBlock basicBlock, InstructionHandle handle, ConstantPoolGen cpg)
273286 throws DataflowAnalysisException {
274287 return Stream.mightCloseStream(basicBlock, handle, cpg);
275288 }
276289
290 @Override
277291 public ResourceValueFrameModelingVisitor createVisitor(Stream resource, ConstantPoolGen cpg) {
278292 return new StreamFrameModelingVisitor(cpg, this, resource);
279293 }
280294
295 @Override
281296 public boolean ignoreImplicitExceptions(Stream resource) {
282297 return resource.ignoreImplicitExceptions();
283298 }
284299
300 @Override
285301 public boolean ignoreExceptionEdge(Edge edge, Stream resource, ConstantPoolGen cpg) {
286302 return false;
287303 }
288304
305 @Override
289306 public boolean isParamInstance(Stream resource, int slot) {
290307 return resource.getInstanceParam() == slot;
291308 }
292309
293310 }
294311
295 // vim:ts=3
2121
2222 import java.util.HashMap;
2323 import java.util.Map;
24 import java.util.Map.Entry;
2425
2526 import org.apache.bcel.classfile.Method;
2627
3536 * Find occurrences of using the String "+" or "+=" operators within a loop.
3637 * This is much less efficient than creating a dedicated StringBuffer object
3738 * outside the loop, and then appending to it.
38 *
39 *
3940 * @author Dave Brosius
4041 * @author William Pugh
4142 */
5455
5556 static final int POSSIBLE_CASE = 5;
5657
57 private BugReporter bugReporter;
58 private final BugReporter bugReporter;
5859
5960 private boolean reportedThisMethod;
6061
7677
7778 @Override
7879 public void visit(Method obj) {
79 if (DEBUG)
80 if (DEBUG) {
8081 System.out.println("------------------- Analyzing " + obj.getName() + " ----------------");
82 }
8183 reset();
8284 clobberedRegisters = new HashMap<Integer, Integer>();
8385 reportedThisMethod = false;
9395 // For debugging: print what call to reset() is being invoked.
9496 // This helps figure out why the detector is failing to
9597 // recognize a particular idiom.
96 if (DEBUG)
98 if (DEBUG) {
9799 System.out.println("Reset from: " + new Throwable().getStackTrace()[1]);
100 }
98101 }
99102
100103 private boolean storeIntoRegister(int seen, int reg) {
116119
117120 @Override
118121 public void sawOpcode(int seen) {
119 if (reportedThisMethod)
122 if (reportedThisMethod) {
120123 return;
124 }
121125 int oldState = state;
122126 if (DEBUG) {
123127 System.out.print("Opcode: ");
143147 case ASTORE:
144148 storeTo = getRegisterOperand();
145149 break;
150 default:
151 break;
146152 }
147153 if (storeTo >= 0 && state != CONSTRUCTED_STRING_ON_STACK) {
148154 clobberedRegisters.put(storeTo, getPC());
158164
159165 case SEEN_NEW:
160166 if (seen == INVOKESPECIAL && "<init>".equals(getNameConstantOperand())
161 && "(Ljava/lang/String;)V".equals(getSigConstantOperand())
162 && getClassConstantOperand().startsWith("java/lang/StringBu") && registerOnStack >= 0) {
167 && "(Ljava/lang/String;)V".equals(getSigConstantOperand())
168 && getClassConstantOperand().startsWith("java/lang/StringBu") && registerOnStack >= 0) {
163169 state = SEEN_APPEND1;
164170 stringSource = registerOnStack;
165171 } else if (seen == INVOKEVIRTUAL && "append".equals(getNameConstantOperand())
166172 && getClassConstantOperand().startsWith("java/lang/StringBu")) {
167 if (DEBUG)
173 if (DEBUG) {
168174 System.out.println("Saw string being appended from register " + registerOnStack);
175 }
169176 if (getSigConstantOperand().startsWith("(Ljava/lang/String;)") && registerOnStack >= 0) {
170 if (DEBUG)
177 if (DEBUG) {
171178 System.out.println("Saw string being appended, source = " + registerOnStack);
179 }
172180 state = SEEN_APPEND1;
173181 stringSource = registerOnStack;
174 } else
182 } else {
175183 reset();
184 }
176185 }
177186 break;
178187 case SEEN_APPEND1:
179 if (storeIntoRegister(seen, stringSource))
180 reset();
181 else if (seen == INVOKEVIRTUAL && "append".equals(getNameConstantOperand())
188 if (storeIntoRegister(seen, stringSource)) {
189 reset();
190 } else if (seen == INVOKEVIRTUAL && "append".equals(getNameConstantOperand())
182191 && getClassConstantOperand().startsWith("java/lang/StringBu")) {
183192 state = SEEN_APPEND2;
184193 }
185194 break;
186195
187196 case SEEN_APPEND2:
188 if (storeIntoRegister(seen, stringSource))
189 reset();
190 else if (seen == INVOKEVIRTUAL && "toString".equals(getNameConstantOperand())
197 if (storeIntoRegister(seen, stringSource)) {
198 reset();
199 } else if (seen == INVOKEVIRTUAL && "toString".equals(getNameConstantOperand())
191200 && getClassConstantOperand().startsWith("java/lang/StringBu")) {
192201 state = CONSTRUCTED_STRING_ON_STACK;
193202 }
194203 break;
195204
196205 case CONSTRUCTED_STRING_ON_STACK:
197 if (storeIntoRegister(seen, stringSource))
206 if (storeIntoRegister(seen, stringSource)) {
198207 state = POSSIBLE_CASE;
199 else
200 reset();
208 } else {
209 reset();
210 }
201211 break;
202212
203213 case POSSIBLE_CASE:
210220 // Next check: was the destination register clobbered
211221 // elsewhere in this loop?
212222 boolean clobberedInLoop = false;
213 for (int reg : clobberedRegisters.keySet()) {
223 for (Entry<Integer, Integer> entry : clobberedRegisters.entrySet()) {
224 int reg = entry.getKey();
214225 if (reg != stringSource) {
215226 continue;
216227 }
217 int pc = clobberedRegisters.get(reg);
228 int pc = entry.getValue();
218229 if (pc >= getBranchTarget()) {
219230 clobberedInLoop = true;
220231 break;
226237 }
227238
228239 bugReporter.reportBug(new BugInstance(this, "SBSC_USE_STRINGBUFFER_CONCATENATION", NORMAL_PRIORITY)
229 .addClassAndMethod(this).addSourceLine(this, createPC));
240 .addClassAndMethod(this).addSourceLine(this, createPC));
230241 // System.out.println("SBSC spread: " + (getPC() -
231242 // getBranchTarget()));
232243 reset();
235246 state = SEEN_NEW;
236247 createPC = getPC();
237248 } else {
238 if (DismantleBytecode.isBranch(seen) && DEBUG) {
249 if (DEBUG && DismantleBytecode.isBranch(seen)) {
239250 System.out.println("Rejecting branch:");
240251 System.out.println(" spread: " + (getPC() - getBranchTarget()));
241252 System.out.println(" getBranchTarget(): " + getBranchTarget());
243254 }
244255 }
245256 break;
246 }
247
248 if (seen == INVOKESTATIC && getNameConstantOperand().equals("valueOf")
249 && getClassConstantOperand().equals("java/lang/String")
250 && getSigConstantOperand().equals("(Ljava/lang/Object;)Ljava/lang/String;")) {
257 default:
258 break;
259 }
260
261 if (seen == INVOKESTATIC && "valueOf".equals(getNameConstantOperand())
262 && "java/lang/String".equals(getClassConstantOperand())
263 && "(Ljava/lang/Object;)Ljava/lang/String;".equals(getSigConstantOperand())) {
251264 // leave registerOnStack unchanged
252265 } else {
253266 registerOnStack = -1;
267280 case ALOAD:
268281 registerOnStack = getRegisterOperand();
269282 break;
270 }
271 }
272 if (DEBUG && state != oldState)
283 default:
284 break;
285 }
286 }
287 if (DEBUG && state != oldState) {
273288 System.out.println("At PC " + getPC() + " changing from state " + oldState + " to state " + state + ", regOnStack = "
274289 + registerOnStack);
290 }
275291 }
276292 }
277293
278 // vim:ts=4
3434 * Find occurrences of a instanceof b where it can be determined statically
3535 * whether this is true or false. This may signal a misunderstanding of the
3636 * inheritance hierarchy in use, and potential bugs.
37 *
37 *
3838 * @author Dave Brosius
3939 */
4040 public class SuperfluousInstanceOf extends BytecodeScanningDetector implements StatelessDetector {
4343
4444 private static final int SEEN_ALOAD = 1;
4545
46 private BugReporter bugReporter;
46 private final BugReporter bugReporter;
4747
4848 private LocalVariableTable varTable;
4949
5959 public void visit(Method obj) {
6060 state = SEEN_NOTHING;
6161 varTable = obj.getLocalVariableTable();
62 if (varTable != null)
62 if (varTable != null) {
6363 super.visit(obj);
64 }
6465 }
6566
6667 @Override
6768 public void visit(Code obj) {
68 if (varTable != null)
69 if (varTable != null) {
6970 super.visit(obj);
71 }
7072 }
7173
7274 @Override
7375 public void sawOpcode(int seen) {
7476 switch (state) {
7577 case SEEN_NOTHING:
76 if (seen == ALOAD)
78 if (seen == ALOAD) {
7779 register = getRegisterOperand();
78 else if ((seen >= ALOAD_0) && (seen <= ALOAD_3))
80 } else if ((seen >= ALOAD_0) && (seen <= ALOAD_3)) {
7981 register = seen - ALOAD_0;
80 else
82 } else {
8183 return;
84 }
8285 state = SEEN_ALOAD;
8386 break;
8487
9598 if (clsSignature.charAt(0) != '[') {
9699 if (org.apache.bcel.Repository.instanceOf(objSignature, clsSignature)) {
97100 bugReporter.reportBug(new BugInstance(this, "SIO_SUPERFLUOUS_INSTANCEOF", LOW_PRIORITY)
98 .addClassAndMethod(this).addSourceLine(this));
101 .addClassAndMethod(this).addSourceLine(this));
99102 }
100103 }
101104 }
107110
108111 state = SEEN_NOTHING;
109112 break;
113 default:
114 break;
110115 }
111116
112117 }
113118 }
114
115 // vim:ts=4
2020 package edu.umd.cs.findbugs.detect;
2121
2222 import java.util.BitSet;
23 import java.util.Collections;
2324
2425 import org.apache.bcel.classfile.Method;
2526
2728 import edu.umd.cs.findbugs.BugReporter;
2829 import edu.umd.cs.findbugs.BytecodeScanningDetector;
2930 import edu.umd.cs.findbugs.StatelessDetector;
31 import edu.umd.cs.findbugs.ba.ClassContext;
3032
3133 /**
3234 * looks for calls to Thread.interrupted from a non static context, especially
4345
4446 public static final int SEEN_POSSIBLE_THREAD = 4;
4547
46 private BugReporter bugReporter;
48 private final BugReporter bugReporter;
4749
4850 private BitSet localsWithCurrentThreadValue;
4951
5153
5254 public SuspiciousThreadInterrupted(BugReporter bugReporter) {
5355 this.bugReporter = bugReporter;
56 }
57
58 @Override
59 public void visitClassContext(ClassContext classContext) {
60 if(hasInterestingClass(classContext.getJavaClass().getConstantPool(), Collections.singleton("java/lang/Thread"))) {
61 super.visitClassContext(classContext);
62 }
5463 }
5564
5665 @Override
6776 if (seen == POP) {
6877 state = SEEN_UNKNOWNCONTEXT_POP;
6978 return;
70 } else
79 } else {
7180 state = SEEN_NOTHING;
81 }
7282 }
7383 switch (state) {
7484 case SEEN_NOTHING:
75 if ((seen == INVOKESTATIC) && getClassConstantOperand().equals("java/lang/Thread")
76 && getNameConstantOperand().equals("currentThread") && getSigConstantOperand().equals("()Ljava/lang/Thread;"))
85 if ((seen == INVOKESTATIC) && "java/lang/Thread".equals(getClassConstantOperand())
86 && "currentThread".equals(getNameConstantOperand()) && "()Ljava/lang/Thread;".equals(getSigConstantOperand())) {
7787 state = SEEN_CURRENTTHREAD;
78 else if ((seen == INVOKESTATIC || seen == INVOKEINTERFACE || seen == INVOKEVIRTUAL || seen == INVOKESPECIAL)
79 && getSigConstantOperand().endsWith("Ljava/lang/Thread;"))
88 } else if ((seen == INVOKESTATIC || seen == INVOKEINTERFACE || seen == INVOKEVIRTUAL || seen == INVOKESPECIAL)
89 && getSigConstantOperand().endsWith("Ljava/lang/Thread;")) {
8090 state = SEEN_POSSIBLE_THREAD;
81 else if (seen == ALOAD) {
91 } else if (seen == ALOAD) {
8292 if (localsWithCurrentThreadValue.get(getRegisterOperand())) {
8393 state = SEEN_CURRENTTHREAD;
84 } else
94 } else {
8595 state = SEEN_POSSIBLE_THREAD;
96 }
8697 } else if ((seen >= ALOAD_0) && (seen <= ALOAD_3)) {
8798 if (localsWithCurrentThreadValue.get(seen - ALOAD_0)) {
8899 state = SEEN_CURRENTTHREAD;
89 } else
100 } else {
90101 state = SEEN_POSSIBLE_THREAD;
91 } else if ((seen == GETFIELD || seen == GETSTATIC) && getSigConstantOperand().equals("Ljava/lang/Thread;"))
102 }
103 } else if ((seen == GETFIELD || seen == GETSTATIC) && "Ljava/lang/Thread;".equals(getSigConstantOperand())) {
92104 state = SEEN_POSSIBLE_THREAD;
105 }
93106 break;
94107
95108 case SEEN_CURRENTTHREAD:
107120 break;
108121
109122 default:
110 if ((seen == INVOKESTATIC) && getClassConstantOperand().equals("java/lang/Thread")
111 && getNameConstantOperand().equals("interrupted") && getSigConstantOperand().equals("()Z")) {
123 if ((seen == INVOKESTATIC) && "java/lang/Thread".equals(getClassConstantOperand())
124 && "interrupted".equals(getNameConstantOperand()) && "()Z".equals(getSigConstantOperand())) {
112125 if (state == SEEN_POP_AFTER_CURRENTTHREAD) {
113126 bugReporter.reportBug(new BugInstance(this, "STI_INTERRUPTED_ON_CURRENTTHREAD", LOW_PRIORITY)
114 .addClassAndMethod(this).addSourceLine(this));
127 .addClassAndMethod(this).addSourceLine(this));
115128 } else if (state == SEEN_UNKNOWNCONTEXT_POP) {
116129 bugReporter.reportBug(new BugInstance(this, "STI_INTERRUPTED_ON_UNKNOWNTHREAD", NORMAL_PRIORITY)
117 .addClassAndMethod(this).addSourceLine(this));
130 .addClassAndMethod(this).addSourceLine(this));
118131 }
119132 }
120133 state = SEEN_NOTHING;
123136 }
124137 }
125138
126 // vim:ts=4
9090
9191 @Override
9292 public void visit(Code obj) {
93 if (DEBUG)
93 if (DEBUG) {
9494 System.out.printf("%nVisiting %s%n", getMethodDescriptor());
95 }
9596 reachable = false;
9697 lastPC = 0;
9798 biggestJumpTarget = -1;
105106 super.visit(obj);
106107 enumType = null;
107108 if (!found.isEmpty()) {
108 if (found.size() >= 4 && priority == NORMAL_PRIORITY)
109 if (found.size() >= 4 && priority == NORMAL_PRIORITY) {
109110 priority = LOW_PRIORITY;
110 for (SourceLineAnnotation s : found)
111 }
112 for (SourceLineAnnotation s : found) {
111113 bugAccumulator.accumulateBug(new BugInstance(this, "SF_SWITCH_FALLTHROUGH", priority).addClassAndMethod(this), s);
114 }
112115 }
113116
114117 bugAccumulator.reportAccumulatedBugs();
122125 int prev = Integer.MIN_VALUE;
123126 for (LineNumber ln : table.getLineNumberTable()) {
124127 int thisLineNumber = ln.getLineNumber();
125 if (thisLineNumber < startLine && thisLineNumber > prev && ln.getStartPC() < s.getStartBytecode())
128 if (thisLineNumber < startLine && thisLineNumber > prev && ln.getStartPC() < s.getStartBytecode()) {
126129 prev = thisLineNumber;
130 }
127131 }
128132 int diff = startLine - prev;
129 if (diff > 5)
133 if (diff > 5) {
130134 return;
135 }
131136
132137 bugAccumulator.accumulateBug(new BugInstance(this, "SF_SWITCH_NO_DEFAULT", NORMAL_PRIORITY).addClassAndMethod(this),
133138 s);
143148 boolean isCaseOffset = switchHdlr.isOnSwitchOffset(this);
144149
145150 if (DEBUG) {
146 if (seen == GOTO)
151 if (seen == GOTO) {
147152 System.out.printf("%4d: goto %-7d %s %s %s %d%n", getPC(), getBranchTarget(), reachable, isCaseOffset,
148153 isDefaultOffset, switchHdlr.stackSize());
149
150 else
154 } else {
151155 System.out.printf("%4d: %-12s %s %s %s %d%n", getPC(), OPCODE_NAMES[seen], reachable, isCaseOffset,
152156 isDefaultOffset, switchHdlr.stackSize());
157 }
153158 }
154159
155160 if (reachable && (isDefaultOffset || isCaseOffset)) {
166171 found.add(sourceLineAnnotation);
167172 } else if ( getPC() >= biggestJumpTarget) {
168173 SourceLineAnnotation sourceLineAnnotation = switchHdlr.getCurrentSwitchStatement(this);
169 if (DEBUG)
174 if (DEBUG) {
170175 System.out.printf("Found fallthrough to default offset at %d (BJT is %d)%n", getPC(), biggestJumpTarget);
176 }
171177
172178 foundSwitchNoDefault(sourceLineAnnotation);
173179 }
182188
183189 if (seen == GETFIELD && stack.getStackDepth() > 0) {
184190 OpcodeStack.Item top = stack.getStackItem(0);
185 if (top.getRegisterNumber() == 0)
191 if (top.getRegisterNumber() == 0) {
186192 potentiallyDeadFields.remove(getXFieldOperand());
193 }
187194 }
188195
189196 else if (seen == PUTFIELD && stack.getStackDepth() >= 2) {
194201 // killed store
195202 priority = HIGH_PRIORITY;
196203 bugAccumulator.accumulateBug(new BugInstance(this, "SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH", priority)
197 .addClassAndMethod(this).addField(f), this);
204 .addClassAndMethod(this).addField(f), this);
198205
199206 }
200207 potentiallyDeadFields.add(f);
219226 clearAllDeadStores();
220227 }
221228
222 if (isRegisterLoad())
229 if (isRegisterLoad()) {
223230 potentiallyDeadStores.clear(getRegisterOperand());
224
225 else if (isRegisterStore() && !atCatchBlock()) {
231 } else if (isRegisterStore() && !atCatchBlock()) {
226232 int register = getRegisterOperand();
227233 if (potentiallyDeadStores.get(register) && (potentiallyDeadStoresFromBeforeFallthrough.get(register))) {
228234 // killed store
229235 priority = HIGH_PRIORITY;
230236 deadStore = LocalVariableAnnotation.getLocalVariableAnnotation(getMethod(), register, getPC() - 1, getPC());
231237 bugAccumulator.accumulateBug(new BugInstance(this, "SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH", priority)
232 .addClassAndMethod(this).add(deadStore), this);
238 .addClassAndMethod(this).add(deadStore), this);
233239
234240 }
235241 potentiallyDeadStores.set(register);
236242 }
237243
238244
239 if (seen == INVOKEVIRTUAL && getNameConstantOperand().equals("ordinal") && getSigConstantOperand().equals("()I")) {
245 if (seen == INVOKEVIRTUAL && "ordinal".equals(getNameConstantOperand()) && "()I".equals(getSigConstantOperand())) {
240246 XClass c = getXClassOperand();
241247 if (c != null) {
242248 ClassDescriptor superclassDescriptor = c.getSuperclassDescriptor();
243 if (superclassDescriptor != null && superclassDescriptor.getClassName().equals("java/lang/Enum"))
249 if (superclassDescriptor != null && "java/lang/Enum".equals(superclassDescriptor.getClassName())) {
244250 enumType = c;
245 if (DEBUG)
251 }
252 if (DEBUG) {
246253 System.out.println("Saw " + enumType + ".ordinal()");
247 }
248 } else if (seen != TABLESWITCH && seen != LOOKUPSWITCH && seen != IALOAD)
254 }
255 }
256 } else if (seen != TABLESWITCH && seen != LOOKUPSWITCH && seen != IALOAD) {
249257 enumType = null;
258 }
250259
251260 switch (seen) {
252261 case TABLESWITCH:
253262 case LOOKUPSWITCH:
254263 if (justSawHashcode)
264 {
255265 break; // javac compiled switch statement
266 }
256267 reachable = false;
257268 biggestJumpTarget = -1;
258269 switchHdlr.enterSwitch(this, enumType);
259 if (DEBUG)
270 if (DEBUG) {
260271 System.out.printf(" entered switch, default is %d%n", switchHdlr.getDefaultOffset());
272 }
261273 break;
262274
263275 case GOTO_W:
264276 case GOTO:
265277 if (biggestJumpTarget < getBranchTarget()) {
266278 biggestJumpTarget = getBranchTarget();
267 if (DEBUG)
279 if (DEBUG) {
268280 System.out.printf(" Setting BJT to %d%n", biggestJumpTarget);
281 }
269282 }
270283
271284 reachable = false;
289302 reachable = true;
290303 }
291304
292 justSawHashcode = seen == INVOKEVIRTUAL && getNameConstantOperand().equals("hashCode") && getSigConstantOperand().equals("()I");
305 justSawHashcode = seen == INVOKEVIRTUAL && "hashCode".equals(getNameConstantOperand()) && "()I".equals(getSigConstantOperand());
293306 lastPC = getPC();
294307 fallthroughDistance++;
295308 }
315328
316329 int startLine = srcLine.getStartLine();
317330 int numLines = srcLine.getEndLine() - startLine - 1;
318 if (numLines <= 0)
331 if (numLines <= 0) {
319332 return false;
333 }
320334 r = UTF8.bufferedReader(sourceFile.getInputStream());
321335 for (int i = 0; i < startLine; i++) {
322336 String line = r.readLine();
323 if (line == null)
337 if (line == null) {
324338 return false;
339 }
325340 }
326341 for (int i = 0; i < numLines; i++) {
327342 String line = r.readLine();
328 if (line == null)
343 if (line == null) {
329344 return false;
345 }
330346 line = line.toLowerCase();
331347 if (line.indexOf("fall") >= 0 || line.indexOf("nobreak") >= 0) {
332348 return true;
336352 // Problems with source file, mean report the bug
337353 } finally {
338354 try {
339 if (r != null)
355 if (r != null) {
340356 r.close();
357 }
341358 } catch (IOException ioe) {
342359 }
343360 }
5252
5353 private static boolean newlyConstructedObject(OpcodeStack.Item item) {
5454 XMethod method = item.getReturnValueOf();
55 if (method == null)
55 if (method == null) {
5656 return false;
57 return method.getName().equals("<init>");
57 }
58 return "<init>".equals(method.getName());
5859 }
5960
6061 private static final Pattern identified = Pattern.compile("\\p{Alnum}+");
8889 syncSignature = top.getSignature();
8990 isSyncOnBoolean = false;
9091 Object constant = top.getConstant();
91 if (syncSignature.equals("Ljava/lang/String;") && constant instanceof String) {
92 if ("Ljava/lang/String;".equals(syncSignature) && constant instanceof String) {
9293
9394 pendingBug = new BugInstance(this, "DL_SYNCHRONIZATION_ON_SHARED_CONSTANT", NORMAL_PRIORITY)
94 .addClassAndMethod(this);
95 .addClassAndMethod(this);
9596
9697 String value = (String) constant;
97 if (identified.matcher(value).matches())
98 if (identified.matcher(value).matches()) {
9899 pendingBug.addString(value).describe(StringAnnotation.STRING_CONSTANT_ROLE);
100 }
99101
100102 } else if (badSignatures.contains(syncSignature)) {
101 isSyncOnBoolean = syncSignature.equals("Ljava/lang/Boolean;");
103 isSyncOnBoolean = "Ljava/lang/Boolean;".equals(syncSignature);
102104 XField field = top.getXField();
103105 FieldSummary fieldSummary = AnalysisContext.currentAnalysisContext().getFieldSummary();
104106 OpcodeStack.Item summary = fieldSummary.getSummary(field);
105107 int priority = NORMAL_PRIORITY;
106 if (isSyncOnBoolean)
108 if (isSyncOnBoolean) {
107109 priority--;
108 if (newlyConstructedObject(summary))
110 }
111 if (newlyConstructedObject(summary)) {
109112 pendingBug = new BugInstance(this, "DL_SYNCHRONIZATION_ON_UNSHARED_BOXED_PRIMITIVE", NORMAL_PRIORITY)
110 .addClassAndMethod(this).addType(syncSignature).addOptionalField(field)
111 .addOptionalLocalVariable(this, top);
112 else if (isSyncOnBoolean)
113 .addClassAndMethod(this).addType(syncSignature).addOptionalField(field)
114 .addOptionalLocalVariable(this, top);
115 } else if (isSyncOnBoolean) {
113116 pendingBug = new BugInstance(this, "DL_SYNCHRONIZATION_ON_BOOLEAN", priority).addClassAndMethod(this)
114117 .addOptionalField(field).addOptionalLocalVariable(this, top);
115 else
118 } else {
116119 pendingBug = new BugInstance(this, "DL_SYNCHRONIZATION_ON_BOXED_PRIMITIVE", priority).addClassAndMethod(this)
117120 .addType(syncSignature).addOptionalField(field).addOptionalLocalVariable(this, top);
121 }
118122 }
119123 break;
120124 case MONITOREXIT:
121
122125 accumulateBug();
123
124126 break;
125
127 default:
128 break;
126129 }
127130 }
128131
129 /**
130 *
131 */
132132 private void accumulateBug() {
133 if (pendingBug == null)
133 if (pendingBug == null) {
134134 return;
135 }
135136 bugAccumulator.accumulateBug(pendingBug, SourceLineAnnotation.fromVisitedInstruction(this, monitorEnterPC));
136137 pendingBug = null;
137138 }
6868 case 1:
6969 if (seen == DUP) {
7070 currState = 2;
71 } else
71 } else {
7272 currState = 0;
73 }
7374 break;
7475 case 2:
75 if (seen == ASTORE || seen == ASTORE_0 || seen == ASTORE_1 || seen == ASTORE_2 || seen == ASTORE_3)
76 if (seen == ASTORE || seen == ASTORE_0 || seen == ASTORE_1 || seen == ASTORE_2 || seen == ASTORE_3) {
7677 currState = 3;
77 else
78 } else {
7879 currState = 0;
80 }
7981 break;
8082 case 3:
8183 if (seen == MONITORENTER) {
8284 currState = 4;
83 } else
85 } else {
8486 currState = 0;
87 }
8588 break;
8689 case 4:
8790 if (seen == GETFIELD || seen == GETSTATIC) {
8891 gottenField = FieldAnnotation.fromReferencedField(this);
8992 currState = 5;
90 } else
93 } else {
9194 currState = 0;
95 }
9296 break;
9397 case 5:
9498 if ((seen == IFNONNULL || seen == IFNULL) && gottenField.equals(syncField)) {
9599 BugInstance bug = new BugInstance(this, "NP_SYNC_AND_NULL_CHECK_FIELD", NORMAL_PRIORITY).addClass(this)
96100 .addMethod(this).addField(syncField).addSourceLine(this);
97101 bugReporter.reportBug(bug);
98 } else
102 } else {
99103 currState = 0;
104 }
100105 break;
101106 default:
102107 currState = 0;
5151 }
5252
5353 /*
54 * (non-Javadoc)
55 *
56 * @see edu.umd.cs.findbugs.bcel.OpcodeStackDetector#sawOpcode(int)
57 */
58
59 /*
6054 * Looking for ALOAD 0 INVOKEVIRTUAL
6155 * java/lang/Object.getClass()Ljava/lang/Class; DUP ASTORE 1 MONITORENTER
6256 */
7266 if (seen == PUTSTATIC) {
7367 String classConstantOperand = getClassConstantOperand();
7468 String thisClassName = getThisClass().getClassName().replace('.', '/');
75 if (classConstantOperand.equals(thisClassName))
69 if (classConstantOperand.equals(thisClassName)) {
7670 seenPutStatic = true;
71 }
7772 } else if (seen == GETSTATIC) {
7873 String classConstantOperand = getClassConstantOperand();
7974 String thisClassName = getThisClass().getClassName().replace('.', '/');
80 if (classConstantOperand.equals(thisClassName))
75 if (classConstantOperand.equals(thisClassName)) {
8176 seenGetStatic = true;
77 }
8278 } else if (seen == MONITOREXIT) {
8379 int priority = LOW_PRIORITY;
84 if (seenPutStatic || seenGetStatic)
80 if (seenPutStatic || seenGetStatic) {
8581 priority--;
82 }
8683 try {
8784 Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
8885 Set<ClassDescriptor> directSubtypes = subtypes2.getDirectSubtypes(getClassDescriptor());
103100 }
104101 switch (state) {
105102 case 0:
106 if (seen == ALOAD_0)
103 if (seen == ALOAD_0) {
107104 state = 1;
105 }
108106 break;
109107 case 1:
110 if (seen == INVOKEVIRTUAL && getNameConstantOperand().equals("getClass")
111 && getSigConstantOperand().equals("()Ljava/lang/Class;"))
108 if (seen == INVOKEVIRTUAL && "getClass".equals(getNameConstantOperand())
109 && "()Ljava/lang/Class;".equals(getSigConstantOperand())) {
112110 state = 2;
113 else
111 } else {
114112 state = 0;
113 }
115114 break;
116115 case 2:
117 if (seen == DUP)
116 if (seen == DUP) {
118117 state = 3;
119 else
118 } else {
120119 state = 0;
120 }
121121 break;
122122 case 3:
123 if (isRegisterStore())
123 if (isRegisterStore()) {
124124 state = 4;
125 else
125 } else {
126126 state = 0;
127 }
127128 break;
128129 case 4:
129 if (seen == MONITORENTER)
130 if (seen == MONITORENTER) {
130131 pendingBug = new BugInstance(this, "WL_USING_GETCLASS_RATHER_THAN_CLASS_LITERAL", NORMAL_PRIORITY)
131 .addClassAndMethod(this).addSourceLine(this);
132 .addClassAndMethod(this).addSourceLine(this);
133 }
132134 state = 0;
133135 seenGetStatic = seenPutStatic = false;
136 break;
137 default:
138 break;
134139 }
135140 }
136141
6363 // System.out.println(state + " " + getPC() + " " + OPCODE_NAMES[seen]);
6464 if (countDown == 2 && seen == GOTO) {
6565 CodeException tryBlock = getSurroundingTryBlock(getPC());
66 if (tryBlock != null && tryBlock.getEndPC() == getPC())
66 if (tryBlock != null && tryBlock.getEndPC() == getPC()) {
6767 pendingBug.lowerPriority();
68 }
6869 }
6970 if (countDown > 0) {
7071 countDown--;
7172 if (countDown == 0) {
72 if (seen == MONITOREXIT)
73 if (seen == MONITOREXIT) {
7374 pendingBug.lowerPriority();
75 }
7476
7577 bugReporter.reportBug(pendingBug);
7678 pendingBug = null;
8183 if (syncField != null && getPrevOpcode(1) != ALOAD_0 && syncField.equals(getXFieldOperand())) {
8284 OpcodeStack.Item value = stack.getStackItem(0);
8385 int priority = Priorities.HIGH_PRIORITY;
84 if (value.isNull())
86 if (value.isNull()) {
8587 priority = Priorities.NORMAL_PRIORITY;
88 }
8689 pendingBug = new BugInstance(this, "ML_SYNC_ON_FIELD_TO_GUARD_CHANGING_THAT_FIELD", priority)
87 .addClassAndMethod(this).addField(syncField).addSourceLine(this);
90 .addClassAndMethod(this).addField(syncField).addSourceLine(this);
8891 countDown = 2;
8992
9093 }
9598 countDown = 0;
9699 }
97100
98 if (seen == MONITORENTER)
101 if (seen == MONITORENTER) {
99102 syncField = null;
103 }
100104
101105 switch (state) {
102106 case 0:
103 if (seen == ALOAD_0)
107 if (seen == ALOAD_0) {
104108 state = 1;
109 }
105110 break;
106111 case 1:
107112 if (seen == GETFIELD) {
108113 state = 2;
109114 field = getXFieldOperand();
110 } else
115 } else {
111116 state = 0;
117 }
112118 break;
113119 case 2:
114 if (seen == DUP)
120 if (seen == DUP) {
115121 state = 3;
116 else
122 } else {
117123 state = 0;
124 }
118125 break;
119126 case 3:
120 if (isRegisterStore())
127 if (isRegisterStore()) {
121128 state = 4;
122 else
129 } else {
123130 state = 0;
131 }
124132 break;
125133 case 4:
126134 if (seen == MONITORENTER) {
127135 state = 0;
128136 syncField = field;
129 } else
137 } else {
130138 state = 0;
139 }
131140 break;
132
141 default:
142 break;
133143 }
134144
135145 }
1818
1919 package edu.umd.cs.findbugs.detect;
2020
21 import static org.apache.bcel.Constants.I2D;
22 import static org.apache.bcel.Constants.INVOKESTATIC;
21 import static org.apache.bcel.Constants.*;
2322
2423 import org.objectweb.asm.FieldVisitor;
2524 import org.objectweb.asm.MethodVisitor;
3433
3534 /**
3635 * Sample detector, using ASM
37 *
36 *
3837 * @author David Hovemeyer
3938 */
4039 public class TestASM extends ClassNodeDetector {
6362 }
6463
6564 @Override
66 public void visitMethodInsn(int opcode, String owner, String invokedName, String invokedDesc) {
67 if (prevPC + 1 == getPC() && prevOpcode == I2D && opcode == INVOKESTATIC && owner.equals("java/lang/Math")
68 && invokedName.equals("ceil") && invokedDesc.equals("(D)D")) {
65 public void visitMethodInsn(int opcode, String owner, String invokedName, String invokedDesc, boolean itf) {
66 if (prevPC + 1 == getPC() && prevOpcode == I2D && opcode == INVOKESTATIC && "java/lang/Math".equals(owner)
67 && "ceil".equals(invokedName) && "(D)D".equals(invokedDesc)) {
6968 BugInstance bug0 = new BugInstance(TestASM.this, "ICAST_INT_CAST_TO_DOUBLE_PASSED_TO_CEIL", NORMAL_PRIORITY);
7069 MethodAnnotation methodAnnotation = MethodAnnotation.fromForeignMethod(TestASM.this.name, name, desc, access);
7170 bug0.addClass(TestASM.this).addMethod(methodAnnotation);
7978 @Override
8079 public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) {
8180 if ((access & Opcodes.ACC_STATIC) != 0 && (access & Opcodes.ACC_FINAL) != 0 && (access & Opcodes.ACC_PUBLIC) != 0
82 && !name.equals(name.toUpperCase()))
81 && !name.equals(name.toUpperCase())) {
8382 bugReporter.reportBug(new BugInstance(this, "NM_FIELD_NAMING_CONVENTION", Priorities.LOW_PRIORITY).addClass(this)
8483 .addField(this.name, name, desc, access));
84 }
8585 return null;
8686 }
8787
6464 *
6565 * @see edu.umd.cs.findbugs.Detector2#finishPass()
6666 */
67 @Override
6768 public void finishPass() {
6869 }
6970
7273 *
7374 * @see edu.umd.cs.findbugs.Detector2#getDetectorClassName()
7475 */
76 @Override
7577 public String getDetectorClassName() {
7678 return getClass().getName();
7779 }
8385 * edu.umd.cs.findbugs.Detector2#visitClass(edu.umd.cs.findbugs.classfile
8486 * .ClassDescriptor)
8587 */
88 @Override
8689 @SuppressWarnings("unchecked")
8790 public void visitClass(ClassDescriptor classDescriptor) throws CheckedAnalysisException {
8891 if (dataflowClassName == null) {
118121 System.out.println("Dataflow finished after " + dataflow.getNumIterations());
119122
120123 if (SystemProperties.getBoolean("dataflow.printcfg")) {
121 DataflowCFGPrinter<Fact,AnalysisType> cfgPrinter
122 = new DataflowCFGPrinter<Fact,AnalysisType>(dataflow);
124 DataflowCFGPrinter<Fact,AnalysisType> cfgPrinter
125 = new DataflowCFGPrinter<Fact,AnalysisType>(dataflow);
123126 cfgPrinter.print(System.out);
124127 }
125128
137140 try {
138141 Class<?> c = getClass().getClassLoader().loadClass(dataflowClassName);
139142 cls = asDataflowClass(c);
140
143
141144 } catch (ClassNotFoundException e) {
142145 assert true;
143146 }
164167 return;
165168 }
166169
167
170
168171 dataflowClass = cls;
169172 }
170173
2020
2121 import org.apache.bcel.classfile.Code;
2222
23 import edu.umd.cs.findbugs.BugAccumulator;
2423 import edu.umd.cs.findbugs.BugReporter;
2524 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
2625
2726 public class TestingGround extends OpcodeStackDetector {
2827
29 final BugReporter bugReporter;
28 // final BugReporter bugReporter;
3029
31 final BugAccumulator accumulator;
30 // final BugAccumulator accumulator;
3231
3332 public TestingGround(BugReporter bugReporter) {
34 this.bugReporter = bugReporter;
35 this.accumulator = new BugAccumulator(bugReporter);
33 // this.bugReporter = bugReporter;
34 // this.accumulator = new BugAccumulator(bugReporter);
3635 }
3736
3837 @Override
3938 public void visit(Code code) {
40 System.out.println(getFullyQualifiedMethodName());
41 super.visit(code);
42 System.out.println();
43 }
39 System.out.println(getFullyQualifiedMethodName());
40 super.visit(code);
41 System.out.println();
42 }
4443
4544 @Override
4645 public void sawOpcode(int seen) {
2121 import org.apache.bcel.classfile.Code;
2222
2323 import edu.umd.cs.findbugs.BugReporter;
24 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
2425 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
2526
2627 public class TestingGround2 extends OpcodeStackDetector {
2728
2829 BugReporter bugReporter;
2930
31 @SuppressFBWarnings("URF_UNREAD_FIELD")
3032 public TestingGround2(BugReporter bugReporter) {
3133 this.bugReporter = bugReporter;
3234 }
4244
4345 @Override
4446 public void sawOpcode(int seen) {
45
47
4648 }
4749
4850 }
4848 * Build a database of reference types stored into fields. This can be used in
4949 * the future to improve the precision of type analysis when values are loaded
5050 * from fields.
51 *
51 *
5252 * @author David Hovemeyer
5353 */
5454 public class TrainFieldStoreTypes implements Detector, TrainingDetector {
55 private BugReporter bugReporter;
55 private final BugReporter bugReporter;
5656
57 private FieldStoreTypeDatabase database;
57 private final FieldStoreTypeDatabase database;
5858
5959 public TrainFieldStoreTypes(BugReporter bugReporter) {
6060 this.bugReporter = bugReporter;
6161 this.database = new FieldStoreTypeDatabase();
6262 }
6363
64 @Override
6465 public void visitClassContext(ClassContext classContext) {
6566 Method[] methodList = classContext.getJavaClass().getMethods();
6667 for (Method method : methodList) {
67 if (method.getCode() == null)
68 if (method.getCode() == null) {
6869 continue;
70 }
6971
7072 try {
7173 analyzeMethod(classContext, method);
7375 bugReporter.logError("Error compting field store types", e);
7476 } catch (DataflowAnalysisException e) {
7577 bugReporter.logError("Error compting field store types", e);
76 }
78 }
7779 }
7880 }
7981
8082 private void analyzeMethod(ClassContext classContext, Method method) throws CFGBuilderException, DataflowAnalysisException
81 {
83 {
8284 CFG cfg = classContext.getCFG(method);
8385 TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
8486 ConstantPoolGen cpg = classContext.getConstantPoolGen();
8991 short opcode = ins.getOpcode();
9092
9193 // Field store instruction?
92 if (opcode != Constants.PUTFIELD && opcode != Constants.PUTSTATIC)
94 if (opcode != Constants.PUTFIELD && opcode != Constants.PUTSTATIC) {
9395 continue;
96 }
9497
9598 // Check if field type is a reference type
9699 FieldInstruction fins = (FieldInstruction) ins;
97100 Type fieldType = fins.getType(cpg);
98 if (!(fieldType instanceof ReferenceType))
101 if (!(fieldType instanceof ReferenceType)) {
99102 continue;
103 }
100104
101105 // Find the exact field being stored into
102106 XField xfield = Hierarchy.findXField(fins, cpg);
103 if (xfield == null)
107 if (xfield == null) {
104108 continue;
109 }
105110
106111 // Skip public and protected fields, since it is reasonable to
107112 // assume
108113 // we won't see every store to those fields
109 if (xfield.isPublic() || xfield.isProtected())
114 if (xfield.isPublic() || xfield.isProtected()) {
110115 continue;
116 }
111117
112118 // The top value on the stack is the one which will be stored
113119 // into the field
114120 TypeFrame frame = typeDataflow.getFactAtLocation(location);
115 if (!frame.isValid())
121 if (!frame.isValid()) {
116122 continue;
123 }
117124 Type storeType = frame.getTopValue();
118 if (!(storeType instanceof ReferenceType))
125 if (!(storeType instanceof ReferenceType)) {
119126 continue;
127 }
120128
121129 // Get or create the field store type set
122130 FieldStoreType property = database.getProperty(xfield.getFieldDescriptor());
130138 }
131139 }
132140
141 @Override
133142 public void report() {
134143 database.purgeBoringEntries();
135144 AnalysisContext.currentAnalysisContext().storePropertyDatabase(database, FieldStoreTypeDatabase.DEFAULT_FILENAME,
5151
5252 /*
5353 * (non-Javadoc)
54 *
54 *
5555 * @see
5656 * edu.umd.cs.findbugs.ba.interproc.MethodPropertyDatabase#encodeProperty
5757 * (Property)
7272 @Override
7373 public void visit(Code obj) {
7474
75 if (!getMethod().isPublic() && !getMethod().isProtected())
75 if (!getMethod().isPublic() && !getMethod().isProtected()) {
7676 return;
77 }
7778 SignatureParser p = new SignatureParser(getMethodSig());
7879 LocalVariableTable t = obj.getLocalVariableTable();
7980
80 if (t == null)
81 if (t == null) {
8182 return;
83 }
8284 ParameterProperty property = new ParameterProperty();
8385
8486 int index = getMethod().isStatic() ? 0 : 1;
8890 LocalVariable localVariable = t.getLocalVariable(index, 0);
8991 if (localVariable != null) {
9092 String name = localVariable.getName();
91 if (s.equals("J") && (name.toLowerCase().indexOf("instant") >= 0 || name.startsWith("date"))) {
93 if ("J".equals(s) && (name.toLowerCase().indexOf("instant") >= 0 || name.startsWith("date"))) {
9294
9395 // System.out.println(getFullyQualifiedMethodName() + " " + s + " " + index + " " + name);
9496 property.setParamWithProperty(parameterNumber, true);
9597 }
9698 }
97 if (s.equals("J") || s.equals("D"))
99 if ("J".equals(s) || "D".equals(s)) {
98100 index += 2;
99 else
101 } else {
100102 index += 1;
103 }
101104 parameterNumber++;
102105 }
103106 if (!property.isEmpty()) {
108111
109112 /*
110113 * (non-Javadoc)
111 *
114 *
112115 * @see edu.umd.cs.findbugs.Detector#report()
113116 */
117 @Override
114118 public void report() {
115119 // System.out.println(database.entrySet().size() + " methods");
116120 AnalysisContext.currentAnalysisContext().storePropertyDatabase(database, "longInstant.db", "long instant database");
117121
118122 }
119123
124 @Override
120125 public void visitClassContext(ClassContext classContext) {
121126 classContext.getJavaClass().accept(this);
122127 }
2626 /**
2727 * Training detector to store NonNull, PossiblyNull and CheckForNull annotations
2828 * to database files.
29 *
29 *
3030 * @author David Hovemeyer
31 *
31 *
3232 * @deprecated AnnotationDatabases are being phased out, since annotations are
3333 * now stored directly in the XClass/XMethod/XField objects.
3434 * Resolving nullness annotations will be handled through the
4343
4444 /*
4545 * (non-Javadoc)
46 *
46 *
4747 * @see
4848 * edu.umd.cs.findbugs.Detector#visitClassContext(edu.umd.cs.findbugs.ba
4949 * .ClassContext)
5050 */
51 @Override
5152 public void visitClassContext(ClassContext classContext) {
5253 classContext.getJavaClass().accept(this);
5354 }
5455
5556 /*
5657 * (non-Javadoc)
57 *
58 *
5859 * @see edu.umd.cs.findbugs.Detector#report()
5960 */
61 @Override
6062 public void report() {
6163 // TODO: FIX for new version of annnotations
6264 // AnalysisContext.currentAnalysisContext().storePropertyDatabase(
2020
2121 import edu.umd.cs.findbugs.BugInstance;
2222 import edu.umd.cs.findbugs.BugReporter;
23 import edu.umd.cs.findbugs.Detector;
2423 import edu.umd.cs.findbugs.TrainingDetector;
2524 import edu.umd.cs.findbugs.ba.AnalysisContext;
2625
2928 * dereferenced. We do this by performing a backwards dataflow analysis which
3029 * sees which params are dereferenced on all non-implicit-exception paths from
3130 * the CFG entry.
32 *
31 *
3332 * @author David Hovemeyer
3433 */
35 public class TrainUnconditionalDerefParams extends BuildUnconditionalParamDerefDatabase implements Detector, TrainingDetector {
36
34 public class TrainUnconditionalDerefParams extends BuildUnconditionalParamDerefDatabase implements TrainingDetector {
35
3736 public TrainUnconditionalDerefParams(BugReporter bugReporter) {
3837 }
3938
40 /*
41 * (non-Javadoc)
42 *
43 * @see edu.umd.cs.findbugs.Detector#report()
44 */
39 @Override
4540 public void report() {
4641 AnalysisContext.currentAnalysisContext().storePropertyDatabase(
4742 AnalysisContext.currentAnalysisContext().getUnconditionalDerefParamDatabase(),
5449 @Override
5550 protected void reportBug(BugInstance bug) {
5651 // Ignore it
57
5852 }
5953
6054 }
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2007 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.detect;
20
21 import org.apache.bcel.classfile.Code;
22
23 import edu.umd.cs.findbugs.BugAccumulator;
24 import edu.umd.cs.findbugs.BugReporter;
25 import edu.umd.cs.findbugs.ba.AnalysisContext;
26 import edu.umd.cs.findbugs.ba.INullnessAnnotationDatabase;
27 import edu.umd.cs.findbugs.ba.NullnessAnnotation;
28 import edu.umd.cs.findbugs.ba.SignatureParser;
29 import edu.umd.cs.findbugs.ba.XMethod;
30 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
31
32 /**
33 * Base class for simple type checking detectors which tests if the method
34 * returns null references for specific types.
35 *
36 * @author alison
37 * @author Andrey Loskutov
38 */
39 public abstract class TypeReturnNull extends OpcodeStackDetector {
40
41 protected final BugAccumulator bugAccumulator;
42
43 public TypeReturnNull(BugReporter bugReporter) {
44 this.bugAccumulator = new BugAccumulator(bugReporter);
45 }
46
47 @Override
48 public void visit(Code code) {
49 SignatureParser sp = new SignatureParser(getMethodSig());
50 // Check to see if the method has expected return type
51 String returnSignature = sp.getReturnTypeSignature();
52 if (!matchesReturnSignature(returnSignature)){
53 return;
54 }
55
56 if (isExplicitlyNullable()){
57 return;
58 }
59
60 super.visit(code); // make callbacks to sawOpcode for all opcodes
61 bugAccumulator.reportAccumulatedBugs();
62 }
63
64 private boolean isExplicitlyNullable() {
65 AnalysisContext analysisContext = AnalysisContext.currentAnalysisContext();
66 INullnessAnnotationDatabase nullnessAnnotationDatabase = analysisContext.getNullnessAnnotationDatabase();
67 XMethod xMethod = getXMethod();
68 NullnessAnnotation na = nullnessAnnotationDatabase.getResolvedAnnotation(xMethod, true);
69 return na != null && na != NullnessAnnotation.NONNULL;
70 }
71
72 @Override
73 public void sawOpcode(int seen) {
74 if (seen == ARETURN && getPrevOpcode(1) == ACONST_NULL){
75 accumulateBug();
76 }
77 }
78
79 /**
80 * @return true if the given return signature matches expected type
81 */
82 protected abstract boolean matchesReturnSignature(String returnSignature);
83
84 /**
85 * creates individual bug instance on match
86 */
87 protected abstract void accumulateBug();
88
89 }
1717 */
1818 package edu.umd.cs.findbugs.detect;
1919
20 import java.util.regex.Pattern;
20 import java.util.Arrays;
21 import java.util.List;
2122
23 import org.apache.bcel.classfile.Code;
2224 import org.apache.bcel.classfile.JavaClass;
2325 import org.apache.bcel.classfile.Signature;
2426
2628 import edu.umd.cs.findbugs.BugInstance;
2729 import edu.umd.cs.findbugs.BugReporter;
2830 import edu.umd.cs.findbugs.OpcodeStack;
31 import edu.umd.cs.findbugs.ba.ClassContext;
2932 import edu.umd.cs.findbugs.bcel.OpcodeStackDetector;
33 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
3034
3135 /**
3236 * equals and hashCode are blocking methods on URL's. Warn about invoking equals
3438 */
3539 public class URLProblems extends OpcodeStackDetector {
3640
41 private static final MethodDescriptor URL_EQUALS = new MethodDescriptor("java/net/URL", "equals", "(Ljava/lang/Object;)Z");
42 private static final MethodDescriptor URL_HASHCODE = new MethodDescriptor("java/net/URL", "hashCode", "()I");
43
3744 final static String[] BAD_SIGNATURES = { "Hashtable<Ljava/net/URL", "Map<Ljava/net/URL", "Set<Ljava/net/URL" };
45
46 // Must be sorted
47 private static final String[] HASHSET_KEY_METHODS = {"add", "contains", "remove"};
48 private static final String[] HASHMAP_KEY_METHODS = {"containsKey", "get", "remove"};
49 private static final String[] HASHMAP_TWO_ARG_KEY_METHODS = {"put"};
50
51 private static final List<MethodDescriptor> methods = Arrays.asList(URL_EQUALS, URL_HASHCODE,
52 new MethodDescriptor("", "add", "(Ljava/lang/Object;)Z"),
53 new MethodDescriptor("", "contains", "(Ljava/lang/Object;)Z"),
54 new MethodDescriptor("", "remove", "(Ljava/lang/Object;)Z"),
55 new MethodDescriptor("", "containsKey", "(Ljava/lang/Object;)Z"),
56 new MethodDescriptor("", "get", "(Ljava/lang/Object;)Ljava/lang/Object;"),
57 new MethodDescriptor("", "remove", "(Ljava/lang/Object;)Ljava/lang/Object;"),
58 new MethodDescriptor("", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"));
3859
3960 final private BugReporter bugReporter;
4061
4162 final private BugAccumulator accumulator;
4263
64 private boolean hasInterestingMethodCalls;
65
4366 public URLProblems(BugReporter bugReporter) {
4467 this.bugReporter = bugReporter;
4568 this.accumulator = new BugAccumulator(bugReporter);
69 }
70
71 @Override
72 public void visitClassContext(ClassContext classContext) {
73 this.hasInterestingMethodCalls = hasInterestingMethod(classContext.getJavaClass().getConstantPool(), methods);
74 super.visitClassContext(classContext);
4675 }
4776
4877 @Override
5180 }
5281
5382 @Override
83 public void visit(Code obj) {
84 if(this.hasInterestingMethodCalls) {
85 super.visit(obj);
86 }
87 }
88
89 @Override
5490 public void visit(Signature obj) {
5591 String sig = obj.getSignature();
56 for (String s : BAD_SIGNATURES)
92 for (String s : BAD_SIGNATURES) {
5793 if (sig.indexOf(s) >= 0) {
58 if (visitingField())
94 if (visitingField()) {
5995 bugReporter.reportBug(new BugInstance(this, "DMI_COLLECTION_OF_URLS", HIGH_PRIORITY).addClass(this)
6096 .addVisitedField(this));
61 else if (visitingMethod())
97 } else if (visitingMethod()) {
6298 bugReporter.reportBug(new BugInstance(this, "DMI_COLLECTION_OF_URLS", HIGH_PRIORITY).addClassAndMethod(this));
63 else
99 } else {
64100 bugReporter.reportBug(new BugInstance(this, "DMI_COLLECTION_OF_URLS", HIGH_PRIORITY).addClass(this).addClass(
65101 this));
102 }
66103 }
104 }
67105 }
68106
69 void check(String className, Pattern name, int target, int url) {
70 if (!name.matcher(getNameConstantOperand()).matches())
107 void check(String className, String[] methodNames, int target, int url) {
108 if (Arrays.binarySearch(methodNames, getNameConstantOperand()) < 0) {
71109 return;
72 if (stack.getStackDepth() <= target)
110 }
111 if (stack.getStackDepth() <= target) {
73112 return;
113 }
74114 OpcodeStack.Item targetItem = stack.getStackItem(target);
75115 OpcodeStack.Item urlItem = stack.getStackItem(url);
76 if (!urlItem.getSignature().equals("Ljava/net/URL;"))
116 if (!"Ljava/net/URL;".equals(urlItem.getSignature())) {
77117 return;
78 if (!targetItem.getSignature().equals(className))
118 }
119 if (!targetItem.getSignature().equals(className)) {
79120 return;
121 }
80122 accumulator.accumulateBug(new BugInstance(this, "DMI_COLLECTION_OF_URLS", HIGH_PRIORITY).addClassAndMethod(this)
81123 .addCalledMethod(this), this);
82124 }
84126 @Override
85127 public void sawOpcode(int seen) {
86128
87 // System.out.println(getPC() + " " + OPCODE_NAMES[seen] + " " + stack);
88129 if (seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE) {
89 check("Ljava/util/HashSet;", Pattern.compile("add|remove|contains"), 1, 0);
90 check("Ljava/util/HashMap;", Pattern.compile("remove|containsKey|get"), 1, 0);
91 check("Ljava/util/HashMap;", Pattern.compile("put"), 2, 1);
92
130 check("Ljava/util/HashSet;", HASHSET_KEY_METHODS, 1, 0);
131 check("Ljava/util/HashMap;", HASHMAP_KEY_METHODS, 1, 0);
132 check("Ljava/util/HashMap;", HASHMAP_TWO_ARG_KEY_METHODS, 2, 1);
93133 }
94134
95 if (seen == INVOKEVIRTUAL && getClassConstantOperand().equals("java/net/URL")) {
96 if (getNameConstantOperand().equals("equals") && getSigConstantOperand().equals("(Ljava/lang/Object;)Z")
97 || getNameConstantOperand().equals("hashCode") && getSigConstantOperand().equals("()I")) {
98 accumulator.accumulateBug(
99 new BugInstance(this, "DMI_BLOCKING_METHODS_ON_URL", HIGH_PRIORITY).addClassAndMethod(this)
100 .addCalledMethod(this), this);
101 }
135 if (seen == INVOKEVIRTUAL && (getMethodDescriptorOperand().equals(URL_EQUALS)
136 || getMethodDescriptorOperand().equals(URL_HASHCODE))) {
137 accumulator.accumulateBug(
138 new BugInstance(this, "DMI_BLOCKING_METHODS_ON_URL", HIGH_PRIORITY).addClassAndMethod(this)
139 .addCalledMethod(this), this);
102140 }
103141 }
104142 }
6464 }
6565
6666 String superclassName2 = getSuperclassName();
67 boolean weird = superclassName2.equals("java.lang.Object") && obj.getInterfaceIndices().length == 0;
67 boolean weird = "java.lang.Object".equals(superclassName2) && obj.getInterfaceIndices().length == 0;
6868 boolean hasAnonymousName = ClassName.isAnonymous(obj.getClassName());
6969 boolean isAnonymousInnerClass = hasAnonymousName && !weird;
70 if (isAnonymousInnerClass)
70 if (isAnonymousInnerClass) {
7171 super.visitJavaClass(obj);
72 }
7273 }
7374
7475 boolean definedInThisClassOrSuper(JavaClass clazz, String method) throws ClassNotFoundException {
75 if (clazz == null)
76 if (clazz == null) {
7677 return false;
78 }
7779 // System.out.println("Checking to see if " + method + " is defined in "
7880 // + clazz.getClassName());
7981 for (Method m : clazz.getMethods()) {
8082 String key = m.getName() + ":" + m.getSignature();
81 if (!m.isStatic() && method.equals(key))
82 return true;
83 if (!m.isStatic() && method.equals(key)) {
84 return true;
85 }
8386 }
8487
8588 return definedInSuperClassOrInterface(clazz, method);
9093 public void sawOpcode(int seen) {
9194 if (seen == INVOKESPECIAL) {
9295 XMethod m = getXMethodOperand();
93 if (m == null)
96 if (m == null) {
9497 return;
98 }
9599 XClass c = getXClass();
96100 int nameDistance = EditDistance.editDistance(m.getName(), getMethodName());
97 if (nameDistance < 4 && c.findMatchingMethod(m.getMethodDescriptor()) == null && !m.isFinal())
101 if (nameDistance < 4 && c.findMatchingMethod(m.getMethodDescriptor()) == null && !m.isFinal()) {
98102 potentialSuperCall = m;
103 }
99104 }
100105 }
101106
102107 boolean definedInSuperClassOrInterface(JavaClass clazz, String method) throws ClassNotFoundException {
103 if (clazz == null)
108 if (clazz == null) {
104109 return false;
110 }
105111 JavaClass superClass = clazz.getSuperClass();
106 if (superClass == null)
112 if (superClass == null) {
107113 return false;
114 }
108115 try {
109116 XClass xClass = Global.getAnalysisCache().getClassAnalysis(XClass.class,
110117 DescriptorFactory.createClassDescriptorFromDottedClassName(superClass.getClassName()));
111 if (xClass.hasStubs())
112 return true;
118 if (xClass.hasStubs()) {
119 return true;
120 }
113121 } catch (CheckedAnalysisException e) {
114122 return true;
115123 }
116
117 if (definedInThisClassOrSuper(superClass, method))
118 return true;
119 for (JavaClass i : clazz.getInterfaces())
120 if (definedInThisClassOrSuper(i, method))
121 return true;
124
125 if (definedInThisClassOrSuper(superClass, method)) {
126 return true;
127 }
128 for (JavaClass i : clazz.getInterfaces()) {
129 if (definedInThisClassOrSuper(i, method)) {
130 return true;
131 }
132 }
122133 return false;
123134 }
124135
125136 Set<String> definedInClass(JavaClass clazz) {
126137 HashSet<String> result = new HashSet<String>();
127138 for (Method m : clazz.getMethods()) {
128 if (!skip(m))
139 if (!skip(m)) {
129140 result.add(m.getName() + m.getSignature());
141 }
130142 }
131143 return result;
132144 }
133145
134146 private boolean skip(Method obj) {
135 if (BCELUtil.isSynthetic(obj))
136 return true;
137 if (obj.isPrivate())
138 return true;
139 if (obj.isAbstract())
140 return true;
147 if (BCELUtil.isSynthetic(obj)) {
148 return true;
149 }
150 if (obj.isPrivate()) {
151 return true;
152 }
153 if (obj.isAbstract()) {
154 return true;
155 }
141156
142157 String methodName = obj.getName();
143158 String sig = obj.getSignature();
144 if (methodName.equals("<init>"))
145 return true;
146 if (methodName.equals("<clinit>"))
147 return true;
148 if (sig.equals("()Ljava/lang/Object;") && (methodName.equals("readResolve") || methodName.equals("writeReplace")))
149 return true;
150 if (methodName.startsWith("access$"))
151 return true;
152 if (methodName.length() < 2 || methodName.indexOf('$') >= 0)
153 return true;
159 if ("<init>".equals(methodName)) {
160 return true;
161 }
162 if ("<clinit>".equals(methodName)) {
163 return true;
164 }
165 if ("()Ljava/lang/Object;".equals(sig) && ("readResolve".equals(methodName) || "writeReplace".equals(methodName))) {
166 return true;
167 }
168 if (methodName.startsWith("access$")) {
169 return true;
170 }
171 if (methodName.length() < 2 || methodName.indexOf('$') >= 0) {
172 return true;
173 }
154174 XMethod m = getXMethod();
155 for (ClassDescriptor c : m.getAnnotationDescriptors())
156 if (c.getClassName().indexOf("inject") >= 0)
157 return true;
175 for (ClassDescriptor c : m.getAnnotationDescriptors()) {
176 if (c.getClassName().indexOf("inject") >= 0) {
177 return true;
178 }
179 }
158180 return false;
159181 }
160182
168190 String role = ClassAnnotation.SUPERCLASS_ROLE;
169191
170192 @DottedClassName String superclassName = ClassName.toDottedClassName(getSuperclassName());
171 if (superclassName.equals("java.lang.Object")) {
193 if ("java.lang.Object".equals(superclassName)) {
172194
173195 try {
174196 JavaClass interfaces[] = getThisClass().getInterfaces();
185207 XClass from = Global.getAnalysisCache().getClassAnalysis(XClass.class,
186208 DescriptorFactory.createClassDescriptorFromDottedClassName(superclassName));
187209 XMethod potentialMatch = null;
188 for(XMethod m : from.getXMethods())
210 for(XMethod m : from.getXMethods()) {
189211 if (!m.isStatic() && !m.isPrivate() && m.getName().toLowerCase().equals(obj.getName().toLowerCase())) {
190 if (potentialMatch == null)
212 if (potentialMatch == null) {
191213 potentialMatch = m;
192 else {
214 } else {
193215 // multiple matches; ignore all
194216 potentialMatch = null;
195217 break;
196218 }
197 }
198 if (potentialMatch != null)
199 pendingBug.addMethod(potentialMatch)
200 .describe(MethodAnnotation.METHOD_DID_YOU_MEAN_TO_OVERRIDE);
219 }
220 }
221 if (potentialMatch != null) {
222 pendingBug.addMethod(potentialMatch)
223 .describe(MethodAnnotation.METHOD_DID_YOU_MEAN_TO_OVERRIDE);
224 }
201225
202226 } catch (CheckedAnalysisException e) {
203227 AnalysisContext.logError("Error: ", e);
215239
216240 @Override
217241 public void visit(Code obj) {
218 if (pendingBug != null)
242 if (pendingBug != null) {
219243 super.visit(obj);
244 }
220245 }
221246
222247 @Override
223248 public void visit(Method obj) {
224249 try {
225250
226 if (skip(obj))
251 if (skip(obj)) {
227252 return;
253 }
228254
229255 JavaClass clazz = getThisClass();
230256 XMethod xmethod = XFactory.createXMethod(clazz, obj);
234260 int priority = NORMAL_PRIORITY;
235261 JavaClass superClass = clazz.getSuperClass();
236262 String superClassName = superClass.getClassName();
237 if (superClassName.equals("java.lang.Object")) {
263 if ("java.lang.Object".equals(superClassName)) {
238264 priority = NORMAL_PRIORITY;
239265
240 } else if (definedInClass(superClass).containsAll(definedInClass(clazz)))
266 } else if (definedInClass(superClass).containsAll(definedInClass(clazz))) {
241267 priority = NORMAL_PRIORITY;
242 else
268 } else {
243269 priority = HIGH_PRIORITY;
270 }
244271 Code code = null;
245 for (Attribute a : obj.getAttributes())
272 for (Attribute a : obj.getAttributes()) {
246273 if (a instanceof Code) {
247274 code = (Code) a;
248275 break;
249276 }
250 if (code != null && code.getLength() == 1)
277 }
278 if (code != null && code.getLength() == 1) {
251279 priority++;
280 }
252281
253282 pendingBug = new BugInstance(this, "UMAC_UNCALLABLE_METHOD_OF_ANONYMOUS_CLASS", priority).addClassAndMethod(this);
254283 potentialSuperCall = null;
1919
2020 package edu.umd.cs.findbugs.detect;
2121
22 import java.util.Collections;
2223 import java.util.HashSet;
2324 import java.util.Set;
2425
3233 import edu.umd.cs.findbugs.BugReporter;
3334 import edu.umd.cs.findbugs.BytecodeScanningDetector;
3435 import edu.umd.cs.findbugs.StatelessDetector;
36 import edu.umd.cs.findbugs.ba.ClassContext;
3537
3638 /**
3739 * Find occurrences of Math using constants, where the result of the calculation
108110 }
109111
110112 @Override
113 public void visitClassContext(ClassContext classContext) {
114 if(hasInterestingClass(classContext.getJavaClass().getConstantPool(), Collections.singleton("java/lang/Math"))) {
115 super.visitClassContext(classContext);
116 }
117 }
118
119 @Override
111120 public void visit(Code obj) {
112121 // Don't complain about unnecessary math calls in class initializers,
113122 // since they may be there to improve readability.
114 if (getMethod().getName().equals("<clinit>"))
123 if ("<clinit>".equals(getMethod().getName())) {
115124 return;
125 }
116126
117127 state = SEEN_NOTHING;
118128 super.visit(obj);
127137 } else if ((seen == LDC2_W) || (seen == LDC_W)) {
128138 state = SEEN_DCONST;
129139 Constant c = this.getConstantRefOperand();
130 if (c instanceof ConstantDouble)
140 if (c instanceof ConstantDouble) {
131141 constValue = ((ConstantDouble) c).getBytes();
132 else if (c instanceof ConstantFloat)
142 } else if (c instanceof ConstantFloat) {
133143 constValue = ((ConstantFloat) c).getBytes();
134 else if (c instanceof ConstantLong)
144 } else if (c instanceof ConstantLong) {
135145 constValue = ((ConstantLong) c).getBytes();
136 else
146 } else {
137147 state = SEEN_NOTHING;
148 }
138149 }
139150 } else if (state == SEEN_DCONST) {
140151 if (seen == INVOKESTATIC) {
141152 state = SEEN_NOTHING;
142 if (getDottedClassConstantOperand().equals("java.lang.Math")) {
153 if ("java.lang.Math".equals(getDottedClassConstantOperand())) {
143154 String methodName = getNameConstantOperand();
144155
145156 if (((constValue == 0.0) && zeroMethods.contains(methodName))
154165 }
155166 }
156167
157 // vim:ts=4
189189 data.abstractClasses.add(getDottedClassName());
190190 } else {
191191 String superClass = obj.getSuperclassName();
192 if (superClass != null)
192 if (superClass != null) {
193193 data.hasNonAbstractSubClass.add(superClass);
194 }
194195 }
195196 data.classesScanned.add(getDottedClassName());
196197 boolean superClassIsObject = "java.lang.Object".equals(obj.getSuperclassName());
197 if (getSuperclassName().indexOf("$") >= 0 || getSuperclassName().indexOf("+") >= 0
198 if (getSuperclassName().indexOf('$') >= 0 || getSuperclassName().indexOf('+') >= 0
198199 || withinAnonymousClass.matcher(getDottedClassName()).find()) {
199200 // System.out.println("hicfsc: " + betterClassName);
200201 data.innerClassCannotBeStatic.add(getDottedClassName());
204205 // Does this class directly implement Serializable?
205206 String[] interface_names = obj.getInterfaceNames();
206207 for (String interface_name : interface_names) {
207 if (interface_name.equals("java.io.Externalizable")) {
208 if ("java.io.Externalizable".equals(interface_name)) {
208209 isSerializable = true;
209 } else if (interface_name.equals("java.io.Serializable")) {
210 } else if ("java.io.Serializable".equals(interface_name)) {
210211 isSerializable = true;
211212 break;
212213 }
232233 }
233234
234235 public static boolean classHasParameter(JavaClass obj) {
235 for (Attribute a : obj.getAttributes())
236 for (Attribute a : obj.getAttributes()) {
236237 if (a instanceof Signature) {
237238 String sig = ((Signature) a).getSignature();
238239 return sig.charAt(0) == '<';
239240 }
241 }
240242 return false;
241243 }
242244
264266 XField f = XFactory.createXField(this);
265267 data.allMyFields.add(f);
266268 String signature = obj.getSignature();
267 if (!getFieldName().equals("serialVersionUID")) {
269 if (!"serialVersionUID".equals(getFieldName())) {
268270
269271 data.myFields.add(f);
270 if (obj.getName().equals("_jspx_dependants"))
272 if ("_jspx_dependants".equals(obj.getName())) {
271273 data.containerFields.add(f);
272 }
273 if (isSeleniumWebElement(signature))
274 }
275 }
276 if (isSeleniumWebElement(signature)) {
274277 data.containerFields.add(f);
275 }
276
277 /**
278 * @param signature
279 * @return
280 */
278 }
279 }
280
281281 public static boolean isSeleniumWebElement(String signature) {
282 return signature.equals("Lorg/openqa/selenium/RenderedWebElement;")
283 || signature.equals("Lorg/openqa/selenium/WebElement;");
282 return "Lorg/openqa/selenium/RenderedWebElement;".equals(signature)
283 || "Lorg/openqa/selenium/WebElement;".equals(signature);
284284 }
285285
286286 @Override
287287 public void visitAnnotation(String annotationClass, Map<String, ElementValue> map, boolean runtimeVisible) {
288 if (!visitingField())
288 if (!visitingField()) {
289289 return;
290 }
290291 if (isInjectionAttribute(annotationClass)) {
291292 data.containerFields.add(XFactory.createXField(this));
292293 }
293 if (!annotationClass.startsWith("edu.umd.cs.findbugs") && !annotationClass.startsWith("javax.lang"))
294 if (!annotationClass.startsWith("edu.umd.cs.findbugs") && !annotationClass.startsWith("javax.lang")) {
294295 data.unknownAnnotation.add(XFactory.createXField(this), annotationClass);
296 }
295297
296298 }
297299
298300 public static boolean isInjectionAttribute(@DottedClassName String annotationClass) {
299301 if (annotationClass.startsWith("javax.annotation.")
300302 || annotationClass.startsWith("javax.ejb")
301 || annotationClass.equals("org.apache.tapestry5.annotations.Persist")
302 || annotationClass.equals("org.jboss.seam.annotations.In")
303 || "org.apache.tapestry5.annotations.Persist".equals(annotationClass)
304 || "org.jboss.seam.annotations.In".equals(annotationClass)
303305 || annotationClass.startsWith("javax.persistence")
304306 || annotationClass.endsWith("SpringBean")
305 || annotationClass.equals("com.google.inject.Inject")
307 || "com.google.inject.Inject".equals(annotationClass)
306308 || annotationClass.startsWith("com.google.") && annotationClass.endsWith(".Bind")
307309 && annotationClass.hashCode() == -243168318
308310 || annotationClass.startsWith("org.nuxeo.common.xmap.annotation")
309311 || annotationClass.startsWith("com.google.gwt.uibinder.client")
310312 || annotationClass.startsWith("org.springframework.beans.factory.annotation")
311 || annotationClass.equals("javax.ws.rs.core.Context"))
313 || "javax.ws.rs.core.Context".equals(annotationClass)) {
312314 return true;
315 }
313316 int lastDot = annotationClass.lastIndexOf('.');
314317 String lastPart = annotationClass.substring(lastDot + 1);
315 if (lastPart.startsWith("Inject"))
318 if (lastPart.startsWith("Inject")) {
316319 return true;
320 }
317321 return false;
318322 }
319323
323327 // set during visitation of the Field are still valid here
324328 XField f = XFactory.createXField(this);
325329 data.constantFields.add(f);
330 data.writtenFields.add(f);
326331 }
327332
328333 int count_aload_1;
342347 seenMonitorEnter = getMethod().isSynchronized();
343348 data.staticFieldsReadInThisMethod.clear();
344349 super.visit(obj);
345 if (getMethodName().equals("<init>") && count_aload_1 > 1
350 if ("<init>".equals(getMethodName()) && count_aload_1 > 1
346351 && (getClassName().indexOf('$') >= 0 || getClassName().indexOf('+') >= 0)) {
347352 data.needsOuterObjectInConstructor.add(getDottedClassName());
348353 // System.out.println(betterClassName +
353358
354359 @Override
355360 public void visit(Method obj) {
356 if (DEBUG)
361 if (DEBUG) {
357362 System.out.println("Checking " + getClassName() + "." + obj.getName());
358 if (getMethodName().equals("<init>") && (obj.isPublic() || obj.isProtected()))
363 }
364 if ("<init>".equals(getMethodName()) && (obj.isPublic() || obj.isProtected())) {
359365 publicOrProtectedConstructor = true;
366 }
360367 pendingGetField = null;
361368 saState = 0;
362369 super.visit(obj);
363370 int flags = obj.getAccessFlags();
364 if ((flags & ACC_NATIVE) != 0)
371 if ((flags & ACC_NATIVE) != 0) {
365372 hasNativeMethods = true;
373 }
366374 }
367375
368376 boolean seenInvokeStatic;
376384
377385 @Override
378386 public void sawOpcode(int seen) {
379 if (DEBUG)
387 if (DEBUG) {
380388 System.out.println(getPC() + ": " + OPCODE_NAMES[seen] + " " + saState);
381 if (seen == MONITORENTER)
389 }
390 if (seen == MONITORENTER) {
382391 seenMonitorEnter = true;
392 }
383393 switch (saState) {
384394 case 0:
385 if (seen == ALOAD_0)
395 if (seen == ALOAD_0) {
386396 saState = 1;
397 }
387398 break;
388399 case 1:
389 if (seen == ALOAD_0)
400 if (seen == ALOAD_0) {
390401 saState = 2;
391 else
402 } else {
392403 saState = 0;
404 }
393405 break;
394406 case 2:
395 if (seen == GETFIELD)
407 if (seen == GETFIELD) {
396408 saState = 3;
397 else
409 } else {
398410 saState = 0;
411 }
399412 break;
400413 case 3:
401 if (seen == PUTFIELD)
414 if (seen == PUTFIELD) {
402415 saState = 4;
403 else
416 } else {
404417 saState = 0;
418 }
419 break;
420 default:
405421 break;
406422 }
407423 boolean selfAssignment = false;
408424 if (pendingGetField != null) {
409 if (seen != PUTFIELD && seen != PUTSTATIC)
425 if (seen != PUTFIELD && seen != PUTSTATIC) {
410426 data.readFields.add(pendingGetField);
411 else if (XFactory.createReferencedXField(this).equals(pendingGetField) && (saState == 4 || seen == PUTSTATIC))
427 } else if (XFactory.createReferencedXField(this).equals(pendingGetField) && (saState == 4 || seen == PUTSTATIC)) {
412428 selfAssignment = true;
413 else
429 } else {
414430 data.readFields.add(pendingGetField);
431 }
415432 pendingGetField = null;
416433 }
417 if (saState == 4)
434 if (saState == 4) {
418435 saState = 0;
419
420 if (seen == INVOKESTATIC && getClassConstantOperand().equals("java/util/concurrent/atomic/AtomicReferenceFieldUpdater")
421 && getNameConstantOperand().equals("newUpdater")) {
436 }
437
438 if (seen == INVOKESTATIC && "java/util/concurrent/atomic/AtomicReferenceFieldUpdater".equals(getClassConstantOperand())
439 && "newUpdater".equals(getNameConstantOperand())) {
422440 String fieldName = (String) stack.getStackItem(0).getConstant();
423441 String fieldSignature = (String) stack.getStackItem(1).getConstant();
424442 String fieldClass = (String) stack.getStackItem(2).getConstant();
429447 }
430448
431449 }
432 if (seen == INVOKESTATIC && getClassConstantOperand().equals("java/util/concurrent/atomic/AtomicIntegerFieldUpdater")
433 && getNameConstantOperand().equals("newUpdater")) {
450 if (seen == INVOKESTATIC && "java/util/concurrent/atomic/AtomicIntegerFieldUpdater".equals(getClassConstantOperand())
451 && "newUpdater".equals(getNameConstantOperand())) {
434452 String fieldName = (String) stack.getStackItem(0).getConstant();
435453 String fieldClass = (String) stack.getStackItem(1).getConstant();
436454 if (fieldName != null && fieldClass != null) {
439457 }
440458
441459 }
442 if (seen == INVOKESTATIC && getClassConstantOperand().equals("java/util/concurrent/atomic/AtomicLongFieldUpdater")
443 && getNameConstantOperand().equals("newUpdater")) {
460 if (seen == INVOKESTATIC && "java/util/concurrent/atomic/AtomicLongFieldUpdater".equals(getClassConstantOperand())
461 && "newUpdater".equals(getNameConstantOperand())) {
444462 String fieldName = (String) stack.getStackItem(0).getConstant();
445463 String fieldClass = (String) stack.getStackItem(1).getConstant();
446464 if (fieldName != null && fieldClass != null) {
461479
462480 checkWriteToStaticFromInstanceMethod: if (f.getName().indexOf("class$") != 0) {
463481 int priority = LOW_PRIORITY;
464 if (f.isReferenceType())
482 if (f.isReferenceType()) {
465483 try {
466484 ValueNumberDataflow vnaDataflow = getClassContext().getValueNumberDataflow(getMethod());
467485 IsNullValueDataflow invDataflow = getClassContext().getIsNullValueDataflow(getMethod());
469487 IsNullValueFrame iFrame = invDataflow.getAnalysis().getFactAtPC(invDataflow.getCFG(), getPC());
470488 AvailableLoad l = new AvailableLoad(f);
471489 ValueNumber[] availableLoads = vFrame.getAvailableLoad(l);
472 if (availableLoads != null && iFrame.isTrackValueNumbers())
490 if (availableLoads != null && iFrame.isTrackValueNumbers()) {
473491 for (ValueNumber v : availableLoads) {
474492 IsNullValue knownValue = iFrame.getKnownValue(v);
475 if (knownValue == null)
493 if (knownValue == null) {
476494 continue;
495 }
477496 if (knownValue.isDefinitelyNotNull()) {
478 if (valuePut.isNull())
497 if (valuePut.isNull()) {
479498 priority++;
480 else
499 } else {
481500 priority--;
501 }
482502 break;
483503 } else if (knownValue.isDefinitelyNull()) {
484504 break checkWriteToStaticFromInstanceMethod;
485505 }
486506 }
507 }
487508
488509 } catch (CheckedAnalysisException e) {
489510 AnalysisContext.logError("foo", e);
490511 }
512 }
491513
492514 if (!publicOrProtectedConstructor) {
493515 priority--;
494516 }
495 if (seenMonitorEnter)
517 if (seenMonitorEnter) {
496518 priority++;
497 if (!seenInvokeStatic && data.staticFieldsReadInThisMethod.isEmpty())
519 }
520 if (!seenInvokeStatic && data.staticFieldsReadInThisMethod.isEmpty()) {
498521 priority--;
499 if (getThisClass().isPublic() && getMethod().isPublic())
522 }
523 if (getThisClass().isPublic() && getMethod().isPublic()) {
500524 priority--;
501 if (getThisClass().isPrivate() || getMethod().isPrivate())
525 }
526 if (getThisClass().isPrivate() || getMethod().isPrivate()) {
502527 priority++;
528 }
503529 if (getClassName().indexOf('$') != -1 || BCELUtil.isSynthetic(getMethod()) || f.isSynthetic()
504 || f.getName().indexOf('$') >= 0)
530 || f.getName().indexOf('$') >= 0) {
505531 priority++;
532 }
506533
507534 // Decrease priority for boolean fields used to control
508535 // debug/test settings
509 if (f.getName().indexOf("DEBUG") >= 0 || f.getName().indexOf("VERBOSE") >= 0 && f.getSignature().equals("Z")) {
536 if (f.getName().indexOf("DEBUG") >= 0 || f.getName().indexOf("VERBOSE") >= 0 && "Z".equals(f.getSignature())) {
510537 priority++;
511538 priority++;
512539 }
513540 // Eclipse bundles which implements start/stop *very* often
514541 // assigns static instances there
515 if ((getMethodName().equals("start") || getMethodName().equals("stop"))
516 && getMethodSig().equals("(Lorg/osgi/framework/BundleContext;)V")) {
542 if (("start".equals(getMethodName()) || "stop".equals(getMethodName()))
543 && "(Lorg/osgi/framework/BundleContext;)V".equals(getMethodSig())) {
517544 try {
518545 JavaClass bundleClass = Repository.lookupClass("org.osgi.framework.BundleActivator");
519546 if (getThisClass().instanceOf(bundleClass)) {
538565 }
539566 }
540567 bugAccumulator.accumulateBug(new BugInstance(this, "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD", priority)
541 .addClassAndMethod(this).addField(f), this);
568 .addClassAndMethod(this).addField(f), this);
542569
543570 }
544571 }
548575 String sig = getSigConstantOperand();
549576 String invokedClassName = getClassConstantOperand();
550577 if (invokedClassName.equals(getClassName())
551 && (getMethodName().equals("<init>") || getMethodName().equals("<clinit>"))) {
578 && ("<init>".equals(getMethodName()) || "<clinit>".equals(getMethodName()))) {
552579
553580 data.calledFromConstructors.add(getNameConstantOperand() + ":" + sig);
554581 }
557584 OpcodeStack.Item item = stack.getStackItem(pos);
558585 boolean superCall = seen == INVOKESPECIAL && !invokedClassName.equals(getClassName());
559586
560 if (DEBUG)
587 if (DEBUG) {
561588 System.out.println("In " + getFullyQualifiedMethodName() + " saw call on " + item);
589 }
562590
563591 boolean selfCall = item.getRegisterNumber() == 0 && !superCall;
564 if (selfCall && getMethodName().equals("<init>")) {
592 if (selfCall && "<init>".equals(getMethodName())) {
565593 sawSelfCallInConstructor = true;
566 if (DEBUG)
594 if (DEBUG) {
567595 System.out.println("Saw self call in " + getFullyQualifiedMethodName() + " to " + invokedClassName + "."
568596 + getNameConstantOperand());
597 }
569598 }
570599 }
571600 }
575604 XField f = item.getXField();
576605 if (f != null) {
577606 data.nullTested.add(f);
578 if (DEBUG)
607 if (DEBUG) {
579608 System.out.println(f + " null checked in " + getFullyQualifiedMethodName());
609 }
580610 }
581611 }
582612
584614 OpcodeStack.Item item0 = stack.getStackItem(0);
585615 OpcodeStack.Item item1 = stack.getStackItem(1);
586616 XField field1 = item1.getXField();
587 if (item0.isNull() && field1 != null)
617 if (item0.isNull() && field1 != null) {
588618 data.nullTested.add(field1);
589 else {
619 } else {
590620 XField field0 = item0.getXField();
591 if (item1.isNull() && field0 != null)
621 if (item1.isNull() && field0 != null) {
592622 data.nullTested.add(field0);
623 }
593624 }
594625 }
595626
639670 IsNullValueDataflow invDataflow = getClassContext().getIsNullValueDataflow(getMethod());
640671 IsNullValueFrame iFrame = invDataflow.getAnalysis().getFactBeforeExceptionCheck(invDataflow.getCFG(),
641672 getPC());
642 if (!iFrame.isValid() || iFrame.getStackValue(pos).isDefinitelyNotNull())
673 if (!iFrame.isValid() || iFrame.getStackValue(pos).isDefinitelyNotNull()) {
643674 break computePlacesAssumedNonnull;
675 }
644676
645677 } catch (CheckedAnalysisException e) {
646 AnalysisContext.logError("foo", e);
647 }
648 if (DEBUG)
678 AnalysisContext.logError("INV dataflow error when analyzing "+getMethodDescriptor(), e);
679 }
680 if (DEBUG) {
649681 System.out.println("RRR: " + f + " " + data.nullTested.contains(f) + " "
650682 + data.writtenInConstructorFields.contains(f) + " " + data.writtenNonNullFields.contains(f));
683 }
651684
652685 ProgramPoint p = new ProgramPoint(this);
653686 Set<ProgramPoint> s = data.assumedNonNull.get(f);
654 if (s == null)
687 if (s == null) {
655688 s = Collections.singleton(p);
656 else
689 } else {
657690 s = Util.addTo(s, p);
691 }
658692 data.assumedNonNull.put(f, s);
659 if (DEBUG)
693 if (DEBUG) {
660694 System.out.println(f + " assumed non-null in " + getFullyQualifiedMethodName());
695 }
661696 }
662697 }
663698 }
667702 } else if (seen == GETFIELD || seen == GETSTATIC) {
668703 XField f = XFactory.createReferencedXField(this);
669704 pendingGetField = f;
670 if (getMethodName().equals("readResolve") && seen == GETFIELD) {
705 if ("readResolve".equals(getMethodName()) && seen == GETFIELD) {
671706 data.writtenFields.add(f);
672707 data.writtenNonNullFields.add(f);
673708 }
674 if (DEBUG)
709 if (DEBUG) {
675710 System.out.println("get: " + f);
676 if (data.writtenFields.contains(f))
711 }
712 if (data.writtenFields.contains(f)) {
677713 data.fieldAccess.remove(f);
678 else if (!data.fieldAccess.containsKey(f))
714 } else if (!data.fieldAccess.containsKey(f)) {
679715 data.fieldAccess.put(f, SourceLineAnnotation.fromVisitedInstruction(this));
716 }
680717 } else if ((seen == PUTFIELD || seen == PUTSTATIC) && !selfAssignment) {
681718 XField f = XFactory.createReferencedXField(this);
682719 OpcodeStack.Item item = null;
683720 if (stack.getStackDepth() > 0) {
684721 item = stack.getStackItem(0);
685 if (!item.isNull())
722 if (!item.isNull()) {
686723 data.nullTested.add(f);
724 }
687725 }
688726 data.writtenFields.add(f);
689727
690728 boolean writtingNonNull = previousOpcode != ACONST_NULL || previousPreviousOpcode == GOTO;
691729 if (writtingNonNull) {
692730 data.writtenNonNullFields.add(f);
693 if (DEBUG)
731 if (DEBUG) {
694732 System.out.println("put nn: " + f);
695 } else if (DEBUG)
733 }
734 } else if (DEBUG) {
696735 System.out.println("put: " + f);
697 if (writtingNonNull && data.readFields.contains(f))
736 }
737 if (writtingNonNull && data.readFields.contains(f)) {
698738 data.fieldAccess.remove(f);
699 else if (!data.fieldAccess.containsKey(f))
739 } else if (!data.fieldAccess.containsKey(f)) {
700740 data.fieldAccess.put(f, SourceLineAnnotation.fromVisitedInstruction(this));
701
702 boolean isConstructor = getMethodName().equals("<init>") || getMethodName().equals("<clinit>");
741 }
742
743 boolean isConstructor = "<init>".equals(getMethodName()) || "<clinit>".equals(getMethodName());
703744 if (getMethod().isStatic() == f.isStatic()
704745 && (isConstructor || data.calledFromConstructors.contains(getMethodName() + ":" + getMethodSig())
705 || getMethodName().equals("init") || getMethodName().equals("init")
706 || getMethodName().equals("initialize") || getMethod().isPrivate())) {
746 || "init".equals(getMethodName()) || "init".equals(getMethodName())
747 || "initialize".equals(getMethodName()) || getMethod().isPrivate())) {
707748
708749 if (isConstructor) {
709750 data.writtenInConstructorFields.add(f);
710 if (f.getSignature().equals("Ljava/lang/ThreadLocal;") && item != null && item.isNewlyAllocated())
751 if ("Ljava/lang/ThreadLocal;".equals(f.getSignature()) && item != null && item.isNewlyAllocated()) {
711752 data.threadLocalAssignedInConstructor.put(f, new ProgramPoint(this));
712 } else
753 }
754 } else {
713755 data.writtenInInitializationFields.add(f);
714 if (writtingNonNull)
756 }
757 if (writtingNonNull) {
715758 data.assumedNonNull.remove(f);
759 }
716760 } else {
717761 data.writtenOutsideOfInitializationFields.add(f);
718762 }
737781 @Override
738782 public void report() {
739783 Set<String> fieldNamesSet = new HashSet<String>();
740 for (XField f : data.writtenNonNullFields)
784 for (XField f : data.writtenNonNullFields) {
741785 fieldNamesSet.add(f.getName());
786 }
742787 if (DEBUG) {
743788 System.out.println("read fields:");
744 for (XField f : data.readFields)
789 for (XField f : data.readFields) {
745790 System.out.println(" " + f);
791 }
746792 if (!data.containerFields.isEmpty()) {
747793 System.out.println("ejb3 fields:");
748 for (XField f : data.containerFields)
794 for (XField f : data.containerFields) {
749795 System.out.println(" " + f);
796 }
750797 }
751798 if (!data.reflectiveFields.isEmpty()) {
752799 System.out.println("reflective fields:");
753 for (XField f : data.reflectiveFields)
800 for (XField f : data.reflectiveFields) {
754801 System.out.println(" " + f);
802 }
755803 }
756804
757805 System.out.println("written fields:");
758 for (XField f : data.writtenFields)
806 for (XField f : data.writtenFields) {
759807 System.out.println(" " + f);
808 }
760809 System.out.println("written nonnull fields:");
761 for (XField f : data.writtenNonNullFields)
810 for (XField f : data.writtenNonNullFields) {
762811 System.out.println(" " + f);
812 }
763813
764814 System.out.println("assumed nonnull fields:");
765 for (XField f : data.assumedNonNull.keySet())
815 for (XField f : data.assumedNonNull.keySet()) {
766816 System.out.println(" " + f);
817 }
767818 }
768819 Set<XField> declaredFields = new HashSet<XField>();
769820 AnalysisContext currentAnalysisContext = AnalysisContext.currentAnalysisContext();
771822 for (XField f : AnalysisContext.currentXFactory().allFields()) {
772823 ClassDescriptor classDescriptor = f.getClassDescriptor();
773824 if (currentAnalysisContext.isApplicationClass(classDescriptor) && !currentAnalysisContext.isTooBig(classDescriptor)
774 && !xFactory.isReflectiveClass(classDescriptor))
825 && !xFactory.isReflectiveClass(classDescriptor)) {
775826 declaredFields.add(f);
827 }
776828 }
777829 // Don't report anything about ejb3Fields
778830 HashSet<XField> unknownAnotationAndUnwritten = new HashSet<XField>(data.unknownAnnotation.keySet());
782834 declaredFields.removeAll(data.reflectiveFields);
783835 for (Iterator<XField> i = declaredFields.iterator(); i.hasNext();) {
784836 XField f = i.next();
785 if (f.isSynthetic() && !f.getName().startsWith("this$") || f.getName().startsWith("_"))
837 if (f.isSynthetic() && !f.getName().startsWith("this$") || f.getName().startsWith("_")) {
786838 i.remove();
839 }
787840 }
788841
789842 TreeSet<XField> notInitializedInConstructors = new TreeSet<XField>(declaredFields);
794847 notInitializedInConstructors.removeAll(data.writtenInInitializationFields);
795848
796849 for (Iterator<XField> i = notInitializedInConstructors.iterator(); i.hasNext();) {
797 if (i.next().isStatic())
850 if (i.next().isStatic()) {
798851 i.remove();
852 }
799853 }
800854
801855 TreeSet<XField> readOnlyFields = new TreeSet<XField>(declaredFields);
820874 classContainingNullOnlyFields.add(f.getClassDescriptor());
821875 int increment = 3;
822876 Collection<ProgramPoint> assumedNonNullAt = data.assumedNonNull.get(f);
823 if (assumedNonNullAt != null)
877 if (assumedNonNullAt != null) {
824878 increment += assumedNonNullAt.size();
879 }
825880 for (String s : data.unknownAnnotation.get(f)) {
826881 Integer value = count.get(s);
827 if (value == null)
882 if (value == null) {
828883 count.put(s, increment);
829 else
884 } else {
830885 count.put(s, value + increment);
886 }
831887 }
832888 }
833889 Map<XField, Integer> maxCount = new HashMap<XField, Integer>();
837893 int myMaxCount = 0;
838894 for (String s : data.unknownAnnotation.get(f)) {
839895 Integer value = count.get(s);
840 if (value != null && myMaxCount < value)
896 if (value != null && myMaxCount < value) {
841897 myMaxCount = value;
842 }
843 if (myMaxCount > 0)
898 }
899 }
900 if (myMaxCount > 0) {
844901 maxCount.put(f, myMaxCount);
845 if (myMaxCount > 15)
902 }
903 if (myMaxCount > 15) {
846904 assumeReflective.add(f);
847 else if (nullOnlyFieldNames.getCount(f.getName()) > 8)
905 } else if (nullOnlyFieldNames.getCount(f.getName()) > 8) {
848906 assumeReflective.add(f);
849 else if (classContainingNullOnlyFields.getCount(f.getClassDescriptor()) > 4)
907 } else if (classContainingNullOnlyFields.getCount(f.getClassDescriptor()) > 4) {
850908 assumeReflective.add(f);
851 else if (classContainingNullOnlyFields.getCount(f.getClassDescriptor()) > 2 && f.getName().length() == 1)
909 } else if (classContainingNullOnlyFields.getCount(f.getClassDescriptor()) > 2 && f.getName().length() == 1) {
852910 assumeReflective.add(f);
911 }
853912
854913 }
855914
865924 }
866925 for (XField f : notInitializedInConstructors) {
867926 String className = f.getClassName();
868 if (notInitializedUses.getCount(className) >= 8)
927 if (notInitializedUses.getCount(className) >= 8) {
869928 continue;
929 }
870930 String fieldSignature = f.getSignature();
871931 if (f.isResolved() && !data.fieldsOfNativeClasses.contains(f)
872932 && (fieldSignature.charAt(0) == 'L' || fieldSignature.charAt(0) == '[')) {
873933 int priority = LOW_PRIORITY;
874934
875935 Set<ProgramPoint> assumedNonnullAt = data.assumedNonNull.get(f);
876 if (assumedNonnullAt.size() < 4)
936 if (assumedNonnullAt.size() < 4) {
877937 for (ProgramPoint p : assumedNonnullAt) {
878938 BugInstance bug = new BugInstance(this, "UWF_FIELD_NOT_INITIALIZED_IN_CONSTRUCTOR", priority)
879 .addClass(className).addField(f).addMethod(p.getMethodAnnotation());
939 .addClass(className).addField(f).addMethod(p.getMethodAnnotation());
880940 bugAccumulator.accumulateBug(bug, p.getSourceLineAnnotation());
881941 }
942 }
882943
883944 }
884945 }
885946
886947 for (XField f : readOnlyFields) {
887 String fieldName = f.getName();
888 String className = f.getClassName();
948 // String fieldName = f.getName();
949 // String className = f.getClassName();
889950 String fieldSignature = f.getSignature();
890951 if (f.isResolved() && !data.fieldsOfNativeClasses.contains(f)) {
891952 int priority = NORMAL_PRIORITY;
892 if (!(fieldSignature.charAt(0) == 'L' || fieldSignature.charAt(0) == '['))
953 if (!(fieldSignature.charAt(0) == 'L' || fieldSignature.charAt(0) == '[')) {
893954 priority++;
894 if (maxCount.containsKey(f))
955 }
956 if (maxCount.containsKey(f)) {
895957 priority++;
958 }
896959 String pattern = "UWF_UNWRITTEN_FIELD";
897 if (f.isProtected() || f.isPublic())
960 if (f.isProtected() || f.isPublic()) {
898961 pattern = "UWF_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD";
962 }
899963 bugReporter.reportBug(addClassFieldAndAccess(new BugInstance(this, pattern, priority), f));
900964 }
901965
902966 }
903967 for (XField f : nullOnlyFields) {
904 String fieldName = f.getName();
905 String className = f.getClassName();
906 String fieldSignature = f.getSignature();
968 // String fieldName = f.getName();
969 // String className = f.getClassName();
970 // String fieldSignature = f.getSignature();
907971 if (DEBUG) {
908972 System.out.println("Null only: " + f);
909973 System.out.println(" : " + data.assumedNonNull.containsKey(f));
913977 System.out.println(" : " + data.hasNonAbstractSubClass.contains(f.getClassName()));
914978 System.out.println(" : " + f.isResolved());
915979 }
916 if (!f.isResolved())
980 if (!f.isResolved()) {
917981 continue;
918 if (data.fieldsOfNativeClasses.contains(f))
982 }
983 if (data.fieldsOfNativeClasses.contains(f)) {
919984 continue;
985 }
920986 if (DEBUG) {
921987 System.out.println("Ready to report");
922988 }
923989 int priority = NORMAL_PRIORITY;
924 if (maxCount.containsKey(f))
990 if (maxCount.containsKey(f)) {
925991 priority++;
992 }
926993 if (data.abstractClasses.contains(f.getClassName())) {
927994 priority++;
928 if (!data.hasNonAbstractSubClass.contains(f.getClassName()))
995 if (!data.hasNonAbstractSubClass.contains(f.getClassName())) {
929996 priority++;
997 }
930998 }
931999 // if (fieldNamesSet.contains(f.getName())) priority++;
9321000 if (data.assumedNonNull.containsKey(f)) {
9421010 }
9431011 String pattern = (f.isPublic() || f.isProtected()) ? "NP_UNWRITTEN_PUBLIC_OR_PROTECTED_FIELD"
9441012 : "NP_UNWRITTEN_FIELD";
945 for (ProgramPoint p : assumedNonNullAt)
1013 for (ProgramPoint p : assumedNonNullAt) {
9461014 bugAccumulator.accumulateBug(
9471015 new BugInstance(this, pattern, npPriority).addClassAndMethod(p.method).addField(f),
9481016 p.getSourceLineAnnotation());
1017 }
9491018
9501019 } else {
951 if (f.isStatic())
1020 if (f.isStatic()) {
9521021 priority++;
953 if (f.isFinal())
1022 }
1023 if (f.isFinal()) {
9541024 priority++;
955 if (data.fieldsOfSerializableOrNativeClassed.contains(f))
1025 }
1026 if (data.fieldsOfSerializableOrNativeClassed.contains(f)) {
9561027 priority++;
957 }
958 if (!readOnlyFields.contains(f))
1028 }
1029 }
1030 if (!readOnlyFields.contains(f)) {
9591031 bugReporter.reportBug(addClassFieldAndAccess(new BugInstance(this, "UWF_NULL_FIELD", priority), f)
9601032 .lowerPriorityIfDeprecated());
1033 }
9611034 }
9621035
9631036 writeOnlyFields: for (XField f : writeOnlyFields) {
9711044 System.out.println("Checking write only field " + className + "." + fieldName + "\t" + data.constantFields.contains(f)
9721045 + "\t" + f.isStatic());
9731046 }
974 if (!f.isResolved())
1047 if (!f.isResolved()) {
9751048 continue;
976 if (dontComplainAbout.matcher(fieldName).find())
1049 }
1050 if (dontComplainAbout.matcher(fieldName).find()) {
9771051 continue;
1052 }
9781053 if (lastDollar >= 0 && (fieldName.startsWith("this$") || fieldName.startsWith("this+"))) {
9791054 String outerClassName = className.substring(0, lastDollar);
9801055
9811056 try {
9821057 XClass thisClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, f.getClassDescriptor());
9831058
984 if (isAnonymousInnerClass)
1059 if (isAnonymousInnerClass) {
9851060 for (XField f2 : thisClass.getXFields()) {
9861061 if (f2 != f && f2.isPrivate() && f2.isSynthetic() && !f2.getName().startsWith("this$")
987 && f2.getName().contains("$"))
1062 && f2.getName().contains("$")) {
9881063 continue writeOnlyFields;
1064 }
9891065 }
1066 }
9901067 JavaClass outerClass = Repository.lookupClass(outerClassName);
991 if (classHasParameter(outerClass))
1068 if (classHasParameter(outerClass)) {
9921069 continue;
1070 }
9931071
9941072 ClassDescriptor cDesc = DescriptorFactory.createClassDescriptorFromDottedClassName(outerClassName);
9951073
9991077
10001078 Subtypes2 subtypes2 = analysisContext.getSubtypes2();
10011079
1002 for (XField of : outerXClass.getXFields())
1080 for (XField of : outerXClass.getXFields()) {
10031081 if (!of.isStatic()) {
10041082 String sourceSignature = of.getSourceSignature();
1005 if (sourceSignature != null && of.getSignature().equals("Ljava/lang/ThreadLocal;")) {
1083 if (sourceSignature != null && "Ljava/lang/ThreadLocal;".equals(of.getSignature())) {
10061084 Type ofType = GenericUtilities.getType(sourceSignature);
10071085 if (ofType instanceof GenericObjectType) {
10081086 GenericObjectType gType = (GenericObjectType) ofType;
10151093 int priority = p == null ? NORMAL_PRIORITY : HIGH_PRIORITY;
10161094 BugInstance bug = new BugInstance(this, "SIC_THREADLOCAL_DEADLY_EMBRACE",
10171095 priority).addClass(className).addField(of);
1018 if (p != null)
1096 if (p != null) {
10191097 bug.addMethod(p.method).add(p.getSourceLineAnnotation());
1098 }
10201099 bugReporter.reportBug(bug);
10211100 }
10221101 }
10251104 }
10261105
10271106 }
1107 }
10281108
10291109 boolean outerClassIsInnerClass = false;
1030 for (Field field : outerClass.getFields())
1031 if (field.getName().equals("this$0"))
1110 for (Field field : outerClass.getFields()) {
1111 if ("this$0".equals(field.getName())) {
10321112 outerClassIsInnerClass = true;
1033 if (outerClassIsInnerClass)
1113 }
1114 }
1115 if (outerClassIsInnerClass) {
10341116 continue;
1117 }
10351118 } catch (ClassNotFoundException e) {
10361119 bugReporter.reportMissingClass(e);
10371120 } catch (CheckedAnalysisException e) {
10471130 // false true not reported
10481131 // false false low, SIC_THIS
10491132 int priority = LOW_PRIORITY;
1050 if (easyChange && !isAnonymousInnerClass)
1133 if (easyChange && !isAnonymousInnerClass) {
10511134 priority = NORMAL_PRIORITY;
1135 }
10521136
10531137 String bug = "SIC_INNER_SHOULD_BE_STATIC";
1054 if (isAnonymousInnerClass)
1138 if (isAnonymousInnerClass) {
10551139 bug = "SIC_INNER_SHOULD_BE_STATIC_ANON";
1056 else if (!easyChange)
1140 } else if (!easyChange) {
10571141 bug = "SIC_INNER_SHOULD_BE_STATIC_NEEDS_THIS";
1142 }
10581143
10591144 bugReporter.reportBug(new BugInstance(this, bug, priority).addClass(className));
10601145
10621147 }
10631148 } else if (f.isResolved()) {
10641149 if (data.constantFields.contains(f)) {
1065 if (!f.isStatic())
1150 if (!f.isStatic()) {
10661151 bugReporter.reportBug(addClassFieldAndAccess(
10671152 new BugInstance(this, "SS_SHOULD_BE_STATIC", NORMAL_PRIORITY), f));
1153 }
10681154 } else if (data.fieldsOfSerializableOrNativeClassed.contains(f)) {
10691155 // ignore it
1070 } else if (!data.writtenFields.contains(f))
1156 } else if (!data.writtenFields.contains(f)) {
10711157 bugReporter.reportBug(new BugInstance(this,
10721158 (f.isPublic() || f.isProtected()) ? "UUF_UNUSED_PUBLIC_OR_PROTECTED_FIELD" : "UUF_UNUSED_FIELD",
1073 NORMAL_PRIORITY).addClass(className).addField(f).lowerPriorityIfDeprecated());
1074 else if (f.getName().toLowerCase().indexOf("guardian") < 0) {
1159 NORMAL_PRIORITY).addClass(className).addField(f).lowerPriorityIfDeprecated());
1160 } else if (f.getName().toLowerCase().indexOf("guardian") < 0) {
10751161 int priority = NORMAL_PRIORITY;
1076 if (f.isStatic())
1162 if (f.isStatic()) {
10771163 priority++;
1078 if (f.isFinal())
1164 }
1165 if (f.isFinal()) {
10791166 priority++;
1167 }
10801168 bugReporter.reportBug(addClassFieldAndAccess(new BugInstance(this,
10811169 (f.isPublic() || f.isProtected()) ? "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD" : "URF_UNREAD_FIELD",
1082 priority), f));
1170 priority), f));
10831171 }
10841172 }
10851173 }
10861174 bugAccumulator.reportAccumulatedBugs();
10871175 }
10881176
1089 /**
1090 * @param instance
1091 * @return
1092 */
10931177 private BugInstance addClassFieldAndAccess(BugInstance instance, XField f) {
1094 if (data.writtenNonNullFields.contains(f) && data.readFields.contains(f))
1178 if (data.writtenNonNullFields.contains(f) && data.readFields.contains(f)) {
10951179 throw new IllegalArgumentException("No information for fields that are both read and written nonnull");
1180 }
10961181
10971182 instance.addClass(f.getClassName()).addField(f);
1098 if (data.fieldAccess.containsKey(f))
1183 if (data.fieldAccess.containsKey(f)) {
10991184 instance.add(data.fieldAccess.get(f));
1185 }
11001186 return instance;
11011187 }
11021188
9797 if ((interfaceMethods != null) && ((obj.getAccessFlags() & Constants.ACC_ABSTRACT) != 0)) {
9898 String curDetail = obj.getName() + obj.getSignature();
9999 for (String infMethodDetail : interfaceMethods) {
100 if (curDetail.equals(infMethodDetail))
100 if (curDetail.equals(infMethodDetail)) {
101101 bugReporter.reportBug(new BugInstance(this, "USM_USELESS_ABSTRACT_METHOD", LOW_PRIORITY).addClassAndMethod(
102102 getClassContext().getJavaClass(), obj));
103 }
103104 }
104105 }
105106 super.visitMethod(obj);
110111 try {
111112 String methodName = getMethodName();
112113
113 if (!methodName.equals("<init>") && !methodName.equals("clone")
114 if (!"<init>".equals(methodName) && !"clone".equals(methodName)
114115 && ((getMethod().getAccessFlags() & (Constants.ACC_STATIC | Constants.ACC_SYNTHETIC)) == 0)) {
115116
116117 /*
119120 */
120121 Attribute[] atts = getMethod().getAttributes();
121122 for (Attribute att : atts) {
122 if (att.getClass().equals(Synthetic.class))
123 if (att.getClass().equals(Synthetic.class)) {
123124 return;
125 }
124126 }
125127
126128 byte[] codeBytes = obj.getCode();
127 if ((codeBytes.length == 0) || (codeBytes[0] != ALOAD_0))
129 if ((codeBytes.length == 0) || (codeBytes[0] != ALOAD_0)) {
128130 return;
131 }
129132
130133 state = State.SEEN_NOTHING;
131134 invokePC = 0;
135138 Method superMethod = findSuperclassMethod(superclassName, getMethod());
136139 if ((superMethod == null) || differentAttributes(getMethod(), superMethod)
137140 || getMethod().isProtected()
138 && !samePackage(getDottedClassName(), superclassName))
141 && !samePackage(getDottedClassName(), superclassName)) {
139142 return;
143 }
140144
141145 bugReporter.reportBug(new BugInstance(this, "USM_USELESS_SUBCLASS_METHOD", LOW_PRIORITY).addClassAndMethod(
142146 this).addSourceLine(this, invokePC));
149153
150154 public String getPackage(@DottedClassName String classname) {
151155 int i = classname.lastIndexOf('.');
152 if (i < 0)
156 if (i < 0) {
153157 return "";
158 }
154159 return classname.substring(0,i);
155160 }
156161 public boolean samePackage(@DottedClassName String classname1, @DottedClassName String classname2) {
157162 return getPackage(classname1).equals(getPackage(classname2));
158
163
159164 }
160165 @Override
161166 public void sawOpcode(int seen) {
165170 argTypes = Type.getArgumentTypes(this.getMethodSig());
166171 curParm = 0;
167172 curParmOffset = 1;
168 if (argTypes.length > 0)
173 if (argTypes.length > 0) {
169174 state = State.SEEN_PARM;
170 else
175 } else {
171176 state = State.SEEN_LAST_PARM;
172 } else
173 state = State.SEEN_INVALID;
177 }
178 } else {
179 state = State.SEEN_INVALID;
180 }
174181 break;
175182
176183 case SEEN_PARM:
177 if (curParm >= argTypes.length)
178 state = State.SEEN_INVALID;
179 else {
184 if (curParm >= argTypes.length) {
185 state = State.SEEN_INVALID;
186 } else {
180187 String signature = argTypes[curParm++].getSignature();
181188 char typeChar0 = signature.charAt(0);
182189 if ((typeChar0 == 'L') || (typeChar0 == '[')) {
189196 checkParm(seen, ILOAD_0, ILOAD, 1);
190197 } else if (typeChar0 == 'J') {
191198 checkParm(seen, LLOAD_0, LLOAD, 2);
192 } else
199 } else {
193200 state = State.SEEN_INVALID;
194
195 if ((state != State.SEEN_INVALID) && (curParm >= argTypes.length))
201 }
202
203 if ((state != State.SEEN_INVALID) && (curParm >= argTypes.length)) {
196204 state = State.SEEN_LAST_PARM;
205 }
197206
198207 }
199208 break;
203212 && getMethodSig().equals(getSigConstantOperand())) {
204213 invokePC = getPC();
205214 state = State.SEEN_INVOKE;
206 } else
207 state = State.SEEN_INVALID;
215 } else {
216 state = State.SEEN_INVALID;
217 }
208218 break;
209219
210220 case SEEN_INVOKE:
211221 Type returnType = getMethod().getReturnType();
212222 char retSigChar0 = returnType.getSignature().charAt(0);
213 if ((retSigChar0 == 'V') && (seen == RETURN))
214 state = State.SEEN_RETURN;
215 else if (((retSigChar0 == 'L') || (retSigChar0 == '[')) && (seen == ARETURN))
216 state = State.SEEN_RETURN;
217 else if ((retSigChar0 == 'D') && (seen == DRETURN))
218 state = State.SEEN_RETURN;
219 else if ((retSigChar0 == 'F') && (seen == FRETURN))
220 state = State.SEEN_RETURN;
221 else if ((retSigChar0 == 'I' || retSigChar0 == 'S' || retSigChar0 == 'C' || retSigChar0 == 'B' || retSigChar0 == 'Z')
222 && (seen == IRETURN))
223 state = State.SEEN_RETURN;
224 else if ((retSigChar0 == 'J') && (seen == LRETURN))
225 state = State.SEEN_RETURN;
226 else
227 state = State.SEEN_INVALID;
223 if ((retSigChar0 == 'V') && (seen == RETURN)) {
224 state = State.SEEN_RETURN;
225 } else if (((retSigChar0 == 'L') || (retSigChar0 == '[')) && (seen == ARETURN)) {
226 state = State.SEEN_RETURN;
227 } else if ((retSigChar0 == 'D') && (seen == DRETURN)) {
228 state = State.SEEN_RETURN;
229 } else if ((retSigChar0 == 'F') && (seen == FRETURN)) {
230 state = State.SEEN_RETURN;
231 } else if ((retSigChar0 == 'I' || retSigChar0 == 'S' || retSigChar0 == 'C' || retSigChar0 == 'B' || retSigChar0 == 'Z')
232 && (seen == IRETURN)) {
233 state = State.SEEN_RETURN;
234 } else if ((retSigChar0 == 'J') && (seen == LRETURN)) {
235 state = State.SEEN_RETURN;
236 } else {
237 state = State.SEEN_INVALID;
238 }
228239 break;
229240
230241 case SEEN_RETURN:
231242 state = State.SEEN_INVALID;
232243 break;
244 default:
245 break;
233246 }
234247 }
235248
236249 private void checkParm(int seen, int fastOpBase, int slowOp, int parmSize) {
237250 if ((curParmOffset >= 1) && (curParmOffset <= 3)) {
238 if (seen == (fastOpBase + curParmOffset))
251 if (seen == (fastOpBase + curParmOffset)) {
239252 curParmOffset += parmSize;
240 else
241 state = State.SEEN_INVALID;
242 } else if (curParmOffset == 0)
253 } else {
254 state = State.SEEN_INVALID;
255 }
256 } else if (curParmOffset == 0) {
243257 state = State.SEEN_INVALID;
244 else if ((seen == slowOp) && (getRegisterOperand() == curParmOffset))
258 } else if ((seen == slowOp) && (getRegisterOperand() == curParmOffset)) {
245259 curParmOffset += parmSize;
246 else
260 } else {
247261 state = State.SEEN_INVALID;
262 }
248263 }
249264
250265 private Method findSuperclassMethod(@DottedClassName String superclassName, Method subclassMethod) throws ClassNotFoundException {
255270 Method[] methods = superClass.getMethods();
256271 outer: for (Method m : methods) {
257272 if (m.getName().equals(methodName)) {
258 if (subArgs == null)
273 if (subArgs == null) {
259274 subArgs = Type.getArgumentTypes(subclassMethod.getSignature());
275 }
260276 Type[] superArgs = Type.getArgumentTypes(m.getSignature());
261277 if (subArgs.length == superArgs.length) {
262278 for (int j = 0; j < subArgs.length; j++) {
263 if (!superArgs[j].equals(subArgs[j]))
279 if (!superArgs[j].equals(subArgs[j])) {
264280 continue outer;
281 }
265282 }
266283 return m;
267284 }
268285 }
269286 }
270287
271 if (!superclassName.equals("Object")) {
288 if (!"Object".equals(superclassName)) {
272289 @DottedClassName String superSuperClassName = superClass.getSuperclassName();
273290 if (superSuperClassName.equals(superclassName)) {
274291 throw new ClassNotFoundException("superclass of " + superclassName + " is itself");
282299 HashSet<String> thrownExceptions(Method m) {
283300 HashSet<String> result = new HashSet<String>();
284301 ExceptionTable exceptionTable = m.getExceptionTable();
285 if (exceptionTable != null)
286 for (String e : exceptionTable.getExceptionNames())
302 if (exceptionTable != null) {
303 for (String e : exceptionTable.getExceptionNames()) {
287304 result.add(e);
305 }
306 }
288307 return result;
289308 }
290309 private boolean differentAttributes(Method m1, Method m2) {
291 if (m1.getAnnotationEntries().length > 0 || m2.getAnnotationEntries().length > 0)
310 if (m1.getAnnotationEntries().length > 0 || m2.getAnnotationEntries().length > 0) {
292311 return true;
312 }
293313 int access1 = m1.getAccessFlags()
294314 & (Constants.ACC_PRIVATE | Constants.ACC_PROTECTED | Constants.ACC_PUBLIC | Constants.ACC_FINAL);
295315 int access2 = m2.getAccessFlags()
296316 & (Constants.ACC_PRIVATE | Constants.ACC_PROTECTED | Constants.ACC_PUBLIC | Constants.ACC_FINAL);
297317
298
318
299319 m1.getAnnotationEntries();
300 if (access1 != access2)
320 if (access1 != access2) {
301321 return true;
302 if (!thrownExceptions(m1).equals(thrownExceptions(m2)))
322 }
323 if (!thrownExceptions(m1).equals(thrownExceptions(m2))) {
303324 return false;
304 m1.getExceptionTable();
325 }
305326 return false;
306327 }
307328 }
4040
4141 public class VarArgsProblems extends BytecodeScanningDetector implements StatelessDetector {
4242
43 private BugReporter bugReporter;
43 private final BugReporter bugReporter;
4444
4545 private int state;
4646
7979 // System.out.println("State:" + state);
8080 if (seen == GOTO && getBranchOffset() == 4) {
8181 state = SEEN_GOTO;
82 } else
82 } else {
8383 switch (state) {
8484 case SEEN_NOTHING:
85 if ((seen == ICONST_1))
85 if ((seen == ICONST_1)) {
8686 state = SEEN_ICONST_1;
87 }
8788 break;
8889
8990 case SEEN_ICONST_1:
9293 // getClassConstantOperand());
9394 primitiveArraySig = getClassConstantOperand();
9495 state = SEEN_ANEWARRAY;
95 } else
96 } else {
9697 state = SEEN_NOTHING;
98 }
9799 break;
98100
99101 case SEEN_ANEWARRAY:
100 if (seen == DUP)
102 if (seen == DUP) {
101103 state = SEEN_DUP;
102 else
104 } else {
103105 state = SEEN_NOTHING;
106 }
104107 break;
105108 case SEEN_DUP:
106 if (seen == ICONST_0)
109 if (seen == ICONST_0) {
107110 state = SEEN_ICONST_0;
108 else
111 } else {
109112 state = SEEN_NOTHING;
113 }
110114 break;
111115 case SEEN_ICONST_0:
112 if (((seen >= ALOAD_0) && (seen < ALOAD_3)) || (seen == ALOAD))
116 if (((seen >= ALOAD_0) && (seen < ALOAD_3)) || (seen == ALOAD)) {
113117 state = SEEN_ALOAD;
114 else
118 } else {
115119 state = SEEN_NOTHING;
120 }
116121 break;
117122
118123 case SEEN_ALOAD:
119 if (seen == AASTORE)
124 if (seen == AASTORE) {
120125 state = SEEN_AASTORE;
121 else
126 } else {
122127 state = SEEN_NOTHING;
128 }
123129 break;
124130
125131 case SEEN_AASTORE:
127133 // System.out.println(getClassConstantOperand());
128134 // System.out.println(getNameConstantOperand());
129135 // System.out.println(getSigConstantOperand());
130 if (getSigConstantOperand().indexOf("Ljava/lang/Object;)") == -1)
136 if (getSigConstantOperand().indexOf("Ljava/lang/Object;)") == -1) {
131137 break;
138 }
132139 int priority = NORMAL_PRIORITY;
133 if (getNameConstantOperand().equals("asList") && getClassConstantOperand().equals("java/util/Arrays"))
140 if ("asList".equals(getNameConstantOperand()) && "java/util/Arrays".equals(getClassConstantOperand())) {
134141 priority = HIGH_PRIORITY;
142 }
135143 bugReporter.reportBug(new BugInstance(this, "VA_PRIMITIVE_ARRAY_PASSED_TO_OBJECT_VARARG", priority)
136 .addClassAndMethod(this).addType(primitiveArraySig).describe(TypeAnnotation.FOUND_ROLE)
137 .addCalledMethod(this).addSourceLine(this));
144 .addClassAndMethod(this).addType(primitiveArraySig).describe(TypeAnnotation.FOUND_ROLE)
145 .addCalledMethod(this).addSourceLine(this));
138146 }
139147 state = SEEN_NOTHING;
140148 break;
146154 throw new IllegalStateException("State " + state + " not expected");
147155
148156 }
157 }
149158 }
150159 }
7575 }
7676 break;
7777 case GETFIELD:
78 if (seen == ICONST_1 || seen == LCONST_1 || seen == ICONST_M1)
78 if (seen == ICONST_1 || seen == LCONST_1 || seen == ICONST_M1) {
7979 state = IncrementState.LOADCONSTANT;
80 else
80 } else {
8181 resetIncrementState();
82 }
8283
8384 break;
8485 case LOADCONSTANT:
8586 if (seen == IADD || seen == ISUB || seen == LADD || seen == LSUB) {
8687 state = IncrementState.ADD;
87 } else
88 } else {
8889 resetIncrementState();
90 }
8991 break;
9092 case ADD:
91 if (seen == PUTFIELD && incrementField.equals(getXFieldOperand()))
93 if (seen == PUTFIELD && incrementField.equals(getXFieldOperand())) {
9294 bugReporter.reportBug(new BugInstance(this, "VO_VOLATILE_INCREMENT",
93 incrementField.getSignature().equals("J") ? Priorities.HIGH_PRIORITY : Priorities.NORMAL_PRIORITY)
94 .addClassAndMethod(this).addField(incrementField).addSourceLine(this));
95 "J".equals(incrementField.getSignature()) ? Priorities.HIGH_PRIORITY : Priorities.NORMAL_PRIORITY)
96 .addClassAndMethod(this).addField(incrementField).addSourceLine(this));
97 }
9598 resetIncrementState();
9699 break;
97100 }
98101 switch (seen) {
99102 case PUTSTATIC: {
100103 XField f = getXFieldOperand();
101 if (!isVolatileArray(f))
104 if (!isVolatileArray(f)) {
102105 return;
103 if (getMethodName().equals("<clinit>"))
106 }
107 if ("<clinit>".equals(getMethodName())) {
104108 initializationWrites.add(f);
105 else
109 } else {
106110 otherWrites.add(f);
111 }
107112 break;
108113 }
109114 case PUTFIELD: {
110115 XField f = getXFieldOperand();
111 if (!isVolatileArray(f))
116 if (!isVolatileArray(f)) {
112117 return;
118 }
113119
114 if (getMethodName().equals("<init>"))
120 if ("<init>".equals(getMethodName())) {
115121 initializationWrites.add(f);
116 else
122 } else {
117123 otherWrites.add(f);
124 }
118125 break;
119126 }
127 default:
128 break;
120129 }
121130 }
122131
132141 public void report() {
133142 Subtypes2 subtypes2 = AnalysisContext.currentAnalysisContext().getSubtypes2();
134143
135 for (XField f : AnalysisContext.currentXFactory().allFields())
144 for (XField f : AnalysisContext.currentXFactory().allFields()) {
136145 if (isVolatileArray(f) && subtypes2.isApplicationClass(f.getClassDescriptor())) {
137146 int priority = LOW_PRIORITY;
138 if (initializationWrites.contains(f) && !otherWrites.contains(f))
147 if (initializationWrites.contains(f) && !otherWrites.contains(f)) {
139148 priority = NORMAL_PRIORITY;
149 }
140150 bugReporter.reportBug(new BugInstance(this, "VO_VOLATILE_REFERENCE_TO_ARRAY", priority).addClass(
141151 f.getClassDescriptor()).addField(f));
142152 }
153 }
143154 }
144155
145 /**
146 * @param f
147 * @return
148 */
149156 private boolean isVolatile(XField f) {
150157 return f != null && f.isVolatile();
151158 }
4141
4242 int waitAt = 0;
4343
44 private BugReporter bugReporter;
44 private final BugReporter bugReporter;
4545
4646 public WaitInLoop(BugReporter bugReporter) {
4747 this.bugReporter = bugReporter;
5858 if ((sawWait || sawAwait) && waitAt < earliestJump) {
5959 String bugType = sawWait ? "WA_NOT_IN_LOOP" : "WA_AWAIT_NOT_IN_LOOP";
6060 bugReporter.reportBug(new BugInstance(this, bugType, waitHasTimeout ? LOW_PRIORITY : NORMAL_PRIORITY)
61 .addClassAndMethod(this).addSourceLine(this, waitAt));
61 .addClassAndMethod(this).addSourceLine(this, waitAt));
6262 }
63 if (sawNotify)
63 if (sawNotify) {
6464 bugReporter.reportBug(new BugInstance(this, "NO_NOTIFY_NOT_NOTIFYALL", LOW_PRIORITY).addClassAndMethod(this)
6565 .addSourceLine(this, notifyPC));
66 }
6667 }
6768
6869 @Override
6970 public void sawOpcode(int seen) {
7071
71 if ((seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE) && getNameConstantOperand().equals("notify")
72 && getSigConstantOperand().equals("()V")) {
72 if ((seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE) && "notify".equals(getNameConstantOperand())
73 && "()V".equals(getSigConstantOperand())) {
7374 sawNotify = true;
7475 notifyPC = getPC();
7576 }
7677 if (!(sawWait || sawAwait) && (seen == INVOKEVIRTUAL || seen == INVOKEINTERFACE)
7778 && (isMonitorWait() || isConditionAwait())) {
7879
79 if (getNameConstantOperand().equals("wait")) {
80 if ("wait".equals(getNameConstantOperand())) {
8081 sawWait = true;
8182 } else {
8283 sawAwait = true;
8384 }
84 waitHasTimeout = !getSigConstantOperand().equals("()V");
85 waitHasTimeout = !"()V".equals(getSigConstantOperand());
8586 waitAt = getPC();
8687 earliestJump = getPC() + 1;
8788 return;
8889 }
89 if (seen >= IFEQ && seen <= GOTO || seen >= IFNULL && seen <= GOTO_W)
90 if (seen >= IFEQ && seen <= GOTO || seen >= IFNULL && seen <= GOTO_W) {
9091 earliestJump = Math.min(earliestJump, getBranchTarget());
92 }
9193 }
9294
9395 private boolean isConditionAwait() {
9597 String name = getNameConstantOperand();
9698 String sig = getSigConstantOperand();
9799
98 if (!className.equals("java/util/concurrent/locks/Condition"))
100 if (!"java/util/concurrent/locks/Condition".equals(className)) {
99101 return false;
102 }
100103
101 if (!name.startsWith("await"))
104 if (!name.startsWith("await")) {
102105 return false;
106 }
103107
104 if (name.equals("await") && (sig.equals("()V") || sig.equals("(JLjava/util/concurrent/TimeUnit;)V")))
108 if ("await".equals(name) && ("()V".equals(sig) || "(JLjava/util/concurrent/TimeUnit;)V".equals(sig))) {
105109 return true;
106 if (name.equals("awaitNanos") && sig.equals("(J)V"))
110 }
111 if ("awaitNanos".equals(name) && "(J)V".equals(sig)) {
107112 return true;
108 if (name.equals("awaitUninterruptibly") && sig.equals("()V"))
113 }
114 if ("awaitUninterruptibly".equals(name) && "()V".equals(sig)) {
109115 return true;
110 if (name.equals("awaitUntil") && sig.equals("(Ljava/util/Date;)V"))
116 }
117 if ("awaitUntil".equals(name) && "(Ljava/util/Date;)V".equals(sig)) {
111118 return true;
119 }
112120
113121 return false;
114122 }
117125 String name = getNameConstantOperand();
118126 String sig = getSigConstantOperand();
119127
120 return name.equals("wait") && (sig.equals("()V") || sig.equals("(J)V") || sig.equals("(JI)V"));
128 return "wait".equals(name) && ("()V".equals(sig) || "(J)V".equals(sig) || "(JI)V".equals(sig));
121129 }
122130
123131 }
1919
2020 package edu.umd.cs.findbugs.detect;
2121
22 import java.util.Collections;
23 import java.util.Set;
24
2225 import org.apache.bcel.classfile.Code;
2326 import org.apache.bcel.classfile.Method;
2427
2831 import edu.umd.cs.findbugs.BytecodeScanningDetector;
2932 import edu.umd.cs.findbugs.MethodAnnotation;
3033 import edu.umd.cs.findbugs.StatelessDetector;
34 import edu.umd.cs.findbugs.ba.ClassContext;
3135 import edu.umd.cs.findbugs.ba.XClass;
3236 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
3337 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
38 import edu.umd.cs.findbugs.classfile.FieldDescriptor;
3439 import edu.umd.cs.findbugs.classfile.Global;
40 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
3541
3642 public class WrongMapIterator extends BytecodeScanningDetector implements StatelessDetector {
37 private BugAccumulator bugAccumulator;
38
39 private static final int SAW_NOTHING = 0;
40
41 private static final int SAW_MAP_LOAD1 = 1;
42
43 private static final int SAW_KEYSET = 2;
44
45 private static final int SAW_KEYSET_STORE = 3;
46
47 private static final int SAW_ITERATOR = 4;
48
49 private static final int SAW_ITERATOR_STORE = 5;
50
51 private static final int SAW_ITERATOR_LOAD = 6;
52
53 private static final int SAW_NEXT = 7;
54
55 private static final int SAW_CHECKCAST_ON_NEXT = 8;
56
57 private static final int SAW_KEY_STORE = 9;
58
59 private static final int NEED_KEYSET_LOAD = 10;
60
61 private static final int SAW_MAP_LOAD2 = 11;
62
63 private static final int SAW_KEY_LOAD = 12;
64
65 private int state;
66
67 private int loadedRegister;
68
69 private int mapRegister;
43 private static final Set<MethodDescriptor> methods = Collections.singleton(new MethodDescriptor("", "keySet", "()Ljava/util/Set;"));
44
45 static enum LoadedVariableState {
46 NOTHING, LOCAL, FIELD
47 }
48
49 final LoadedVariable NONE = new LoadedVariable(LoadedVariableState.NOTHING, 0, null);
50
51 final class LoadedVariable {
52 private final LoadedVariableState lvState;
53 private final int num;
54 private final FieldDescriptor fd;
55
56 private LoadedVariable(LoadedVariableState state, int num, FieldDescriptor fd) {
57 this.lvState = state;
58 this.num = num;
59 this.fd = fd;
60 }
61
62 public boolean none() {
63 return lvState == LoadedVariableState.NOTHING;
64 }
65
66 public boolean isRegister(int register) {
67 return lvState == LoadedVariableState.LOCAL && num == register;
68 }
69
70 public LoadedVariable seen(int opcode) {
71 if(isRegisterLoad() && !isRegisterStore()) {
72 return new LoadedVariable(LoadedVariableState.LOCAL, getRegisterOperand(), null);
73 }
74 switch(opcode) {
75 case GETSTATIC:
76 return new LoadedVariable(LoadedVariableState.FIELD, 0, getFieldDescriptorOperand());
77 case GETFIELD:
78 if(lvState == LoadedVariableState.LOCAL && num == 0) {
79 // Ignore fields from other classes
80 return new LoadedVariable(LoadedVariableState.FIELD, 0, getFieldDescriptorOperand());
81 }
82 return NONE;
83 default:
84 return NONE;
85 }
86 }
87
88 public boolean same(LoadedVariable other) {
89 if(other.lvState != lvState) {
90 return false;
91 }
92 if(lvState == LoadedVariableState.LOCAL && num != other.num) {
93 return false;
94 }
95 if ((lvState == LoadedVariableState.FIELD) && !fd.equals(other.fd)) {
96 return false;
97 }
98 return true;
99 }
100
101 public BugInstance annotate(BugInstance bug) {
102 if(lvState == LoadedVariableState.FIELD) {
103 bug.addField(fd);
104 }
105 return bug;
106 }
107 }
108
109 private final BugAccumulator bugAccumulator;
110
111 private static final int NOT_FOUND = -2;
112 private static final int IN_STACK = -1;
113
114 private LoadedVariable loadedVariable = NONE;
115
116 private LoadedVariable mapVariable = NONE;
70117
71118 private int keySetRegister;
72119
73120 private int iteratorRegister;
74121
75122 private int keyRegister;
123
124 private boolean mapAndKeyLoaded;
76125
77126 public WrongMapIterator(BugReporter bugReporter) {
78127 this.bugAccumulator = new BugAccumulator(bugReporter);
79128 }
80129
81130 @Override
131 public void visitClassContext(ClassContext classContext) {
132 if(hasInterestingMethod(classContext.getJavaClass().getConstantPool(), methods)) {
133 super.visitClassContext(classContext);
134 }
135 }
136
137 @Override
82138 public void visit(Method obj) {
83 state = SAW_NOTHING;
84 loadedRegister = -1;
85 mapRegister = -1;
86 keySetRegister = -1;
87 iteratorRegister = -1;
88 keyRegister = -1;
139 reset();
140 }
141
142 private void reset() {
143 loadedVariable = NONE;
144 mapVariable = NONE;
145 mapAndKeyLoaded = false;
146 keySetRegister = NOT_FOUND;
147 iteratorRegister = NOT_FOUND;
148 keyRegister = NOT_FOUND;
89149 }
90150
91151 @Override
97157 /**
98158 * Determine from the class descriptor for a variable whether that variable
99159 * implements java.util.Map.
100 *
160 *
101161 * @param d
102162 * class descriptor for variable we want to check implements Map
103163 * @return true iff the descriptor corresponds to an implementor of Map
124184 return false;
125185 }
126186
187 private int handleStore(int storeRegister, int current) {
188 if(storeRegister == current) {
189 return NOT_FOUND;
190 }
191 if(current == IN_STACK) {
192 return storeRegister;
193 }
194 return current;
195 }
196
197 private void handleStore(int register) {
198 keySetRegister = handleStore(register, keySetRegister);
199 iteratorRegister = handleStore(register, iteratorRegister);
200 keyRegister = handleStore(register, keyRegister);
201 }
202
203 private void removedFromStack(boolean includeKey) {
204 if(keySetRegister == IN_STACK) {
205 keySetRegister = NOT_FOUND;
206 }
207 if(iteratorRegister == IN_STACK) {
208 iteratorRegister = NOT_FOUND;
209 }
210 if(keyRegister == IN_STACK && includeKey) {
211 keyRegister = NOT_FOUND;
212 }
213 }
214
127215 @Override
128216 public void sawOpcode(int seen) {
129 switch (state) {
130 case SAW_NOTHING:
131 loadedRegister = getLoadStoreRegister(seen, true);
132 if (loadedRegister >= 0)
133 state = SAW_MAP_LOAD1;
134 break;
135
136 case SAW_MAP_LOAD1:
137 // Doesn't check to see if the target object is a Map
138 if (((seen == INVOKEINTERFACE) || (seen == INVOKEVIRTUAL)) && ("keySet".equals(getNameConstantOperand()))
139 && ("()Ljava/util/Set;".equals(getSigConstantOperand()))
140 // Following check solves sourceforge bug 1830576
141 && implementsMap(getClassDescriptorOperand())) {
142 mapRegister = loadedRegister;
143 state = SAW_KEYSET;
144 } else {
145 state = SAW_NOTHING;
146 }
147 break;
148
149 case SAW_KEYSET:
150 keySetRegister = getLoadStoreRegister(seen, false);
151 if (keySetRegister >= 0)
152 state = SAW_KEYSET_STORE;
153 else if ((seen == INVOKEINTERFACE) && ("iterator".equals(getNameConstantOperand()))
154 && ("()Ljava/util/Iterator;".equals(getSigConstantOperand())))
155 state = SAW_ITERATOR;
156 else
157 state = SAW_NOTHING;
158 break;
159
160 case SAW_KEYSET_STORE:
161 if ((seen == INVOKEINTERFACE) && ("iterator".equals(getNameConstantOperand()))
162 && ("()Ljava/util/Iterator;".equals(getSigConstantOperand())))
163 state = SAW_ITERATOR;
164 else
165 state = NEED_KEYSET_LOAD;
166 break;
167
168 case NEED_KEYSET_LOAD:
169 loadedRegister = getLoadStoreRegister(seen, true);
170 if (loadedRegister == iteratorRegister)
171 state = SAW_ITERATOR;
172 break;
173
174 case SAW_ITERATOR:
175 iteratorRegister = getLoadStoreRegister(seen, false);
176 if (iteratorRegister >= 0)
177 state = SAW_ITERATOR_STORE;
178 else
179 state = SAW_NOTHING;
180 break;
181
182 case SAW_ITERATOR_STORE:
183 loadedRegister = getLoadStoreRegister(seen, true);
184 if (loadedRegister == iteratorRegister)
185 state = SAW_ITERATOR_LOAD;
186 break;
187
188 case SAW_ITERATOR_LOAD:
189 if ((seen == INVOKEINTERFACE) && ("next".equals(getNameConstantOperand()))
190 && ("()Ljava/lang/Object;".equals(getSigConstantOperand())))
191 state = SAW_NEXT;
192 else
193 state = SAW_ITERATOR_STORE;
194 break;
195
196 case SAW_NEXT:
197 if (seen == CHECKCAST)
198 state = SAW_CHECKCAST_ON_NEXT;
199 else {
200 keyRegister = getLoadStoreRegister(seen, false);
201 if (keyRegister >= 0)
202 state = SAW_KEY_STORE;
203 else
204 state = SAW_NOTHING;
205 }
206 break;
207
208 case SAW_CHECKCAST_ON_NEXT:
209 keyRegister = getLoadStoreRegister(seen, false);
210 if (keyRegister >= 0)
211 state = SAW_KEY_STORE;
212 break;
213
214 case SAW_KEY_STORE:
215 loadedRegister = getLoadStoreRegister(seen, true);
216 if (loadedRegister == mapRegister)
217 state = SAW_MAP_LOAD2;
218 break;
219
220 case SAW_MAP_LOAD2:
221 loadedRegister = getLoadStoreRegister(seen, true);
222 if (loadedRegister == keyRegister)
223 state = SAW_KEY_LOAD;
224 else
225 state = SAW_KEY_STORE;
226 break;
227
228 case SAW_KEY_LOAD:
229 if (((seen == INVOKEINTERFACE) || (seen == INVOKEVIRTUAL)) && ("get".equals(getNameConstantOperand()))
230 && ("(Ljava/lang/Object;)Ljava/lang/Object;".equals(getSigConstantOperand()))) {
231 MethodAnnotation ma = MethodAnnotation.fromVisitedMethod(this);
232 bugAccumulator.accumulateBug(new BugInstance(this, "WMI_WRONG_MAP_ITERATOR", NORMAL_PRIORITY).addClass(this)
233 .addMethod(ma), this);
234 state = SAW_NOTHING;
235 }
236 break;
237 }
238 }
239
240 private int getLoadStoreRegister(int seen, boolean doLoad) {
241 switch (seen) {
242 case ALOAD_0:
243 case ALOAD_1:
244 case ALOAD_2:
245 case ALOAD_3:
246 if (doLoad)
247 return seen - ALOAD_0;
248 break;
249
250 case ALOAD:
251 if (doLoad)
252 return getRegisterOperand();
253 break;
254
255 case ASTORE_0:
256 case ASTORE_1:
257 case ASTORE_2:
258 case ASTORE_3:
259 if (!doLoad)
260 return seen - ASTORE_0;
261 break;
262
263 case ASTORE:
264 if (!doLoad)
265 return getRegisterOperand();
266 break;
267 }
268
269 return -1;
217 boolean loadedPreserved = false;
218 if(isRegisterStore() && !isRegisterLoad()) {
219 handleStore(getRegisterOperand());
220 } else {
221 switch (seen) {
222 case INVOKEINTERFACE:
223 case INVOKEVIRTUAL:
224 if (!loadedVariable.none() &&
225 "keySet".equals(getNameConstantOperand()) && "()Ljava/util/Set;".equals(getSigConstantOperand())
226 // Following check solves sourceforge bug 1830576
227 && implementsMap(getClassDescriptorOperand())) {
228 mapVariable = loadedVariable;
229 removedFromStack(true);
230 keySetRegister = IN_STACK;
231 } else if ((keySetRegister == IN_STACK || loadedVariable.isRegister(keySetRegister))
232 && "iterator".equals(getNameConstantOperand()) && "()Ljava/util/Iterator;".equals(getSigConstantOperand())) {
233 removedFromStack(true);
234 iteratorRegister = IN_STACK;
235 } else if ((iteratorRegister == IN_STACK || loadedVariable.isRegister(iteratorRegister))
236 && "next".equals(getNameConstantOperand())
237 && "()Ljava/lang/Object;".equals(getSigConstantOperand())) {
238 removedFromStack(true);
239 keyRegister = IN_STACK;
240 } else if (mapAndKeyLoaded && "get".equals(getNameConstantOperand())
241 && "(Ljava/lang/Object;)Ljava/lang/Object;".equals(getSigConstantOperand())) {
242 MethodAnnotation ma = MethodAnnotation.fromVisitedMethod(this);
243 bugAccumulator.accumulateBug(mapVariable
244 .annotate(new BugInstance(this, "WMI_WRONG_MAP_ITERATOR", NORMAL_PRIORITY).addClass(this).addMethod(ma)),
245 this);
246 reset();
247 } else if(("intValue".equals(getNameConstantOperand()) && "java/lang/Integer".equals(getClassConstantOperand())) ||
248 ("longValue".equals(getNameConstantOperand()) && "java/lang/Long".equals(getClassConstantOperand())) ||
249 ("doubleValue".equals(getNameConstantOperand()) && "java/lang/Double".equals(getClassConstantOperand())) ||
250 ("floatValue".equals(getNameConstantOperand()) && "java/lang/Float".equals(getClassConstantOperand()))) {
251 removedFromStack(false);
252 } else {
253 removedFromStack(true);
254 }
255 break;
256 case INVOKESTATIC:
257 if ("valueOf".equals(getNameConstantOperand())
258 && ("java/lang/Integer".equals(getClassConstantOperand())
259 || "java/lang/Long".equals(getClassConstantOperand())
260 || "java/lang/Double".equals(getClassConstantOperand()) || "java/lang/Float"
261 .equals(getClassConstantOperand()))) {
262 loadedPreserved = true;
263 }
264 removedFromStack(true);
265 break;
266 case CHECKCAST:
267 removedFromStack(false);
268 break;
269 default:
270 removedFromStack(true);
271 }
272 }
273 if(!loadedPreserved) {
274 boolean mapLoaded = !loadedVariable.none() && loadedVariable.same(mapVariable);
275 loadedVariable = loadedVariable.seen(seen);
276 mapAndKeyLoaded = mapLoaded && loadedVariable.isRegister(keyRegister);
277 }
270278 }
271279 }
7070 try {
7171 if (seen == INVOKESPECIAL) {
7272 String newClsName = getClassConstantOperand();
73 if (rejectedXMLClasses.contains(newClsName))
73 if (rejectedXMLClasses.contains(newClsName)) {
7474 return;
75 }
7576 rejectedXMLClasses.add(newClsName);
7677
77 if (newClsName.startsWith("java/") || newClsName.startsWith("javax/"))
78 if (newClsName.startsWith("java/") || newClsName.startsWith("javax/")) {
7879 return;
80 }
7981
80 if (newClsName.endsWith("Adapter"))
82 if (newClsName.endsWith("Adapter")) {
8183 return;
84 }
8285
83 if (!getNameConstantOperand().equals("<init>"))
86 if (!"<init>".equals(getNameConstantOperand())) {
8487 return;
88 }
8589
8690 String invokerClsName = this.getClassName();
87 if (samePackageBase(invokerClsName, newClsName))
91 if (samePackageBase(invokerClsName, newClsName)) {
8892 return;
93 }
8994
9095 JavaClass newCls = Repository.lookupClass(getDottedClassConstantOperand());
9196
9297 JavaClass superCls = curClass.getSuperClass();
93 if (superCls.getClassName().equals(newClsName.replace('/', '.')))
98 if (superCls.getClassName().equals(newClsName.replace('/', '.'))) {
9499 return;
100 }
95101
96102 JavaClass[] infs = newCls.getAllInterfaces();
97103 for (JavaClass inf : infs) {
111117 String[] invokerParts = invokerClsName.split("/");
112118 String[] newClsParts = newClsName.split("/");
113119
114 if (newClsParts.length < 3)
120 if (newClsParts.length < 3) {
115121 return false;
116 if (invokerParts.length < 3)
122 }
123 if (invokerParts.length < 3) {
117124 return false;
125 }
118126
119 if (!invokerParts[0].equals(newClsParts[0]))
127 if (!invokerParts[0].equals(newClsParts[0])) {
120128 return false;
129 }
121130
122131 return invokerParts[1].equals(newClsParts[1]);
123132 }
3232 return anyMatches;
3333 }
3434
35 @Override
3536 public boolean match(BugInstance bugInstance) {
3637 Iterator<Matcher> i = childIterator();
3738 while (i.hasNext()) {
3839 Matcher child = i.next();
39 if (!child.match(bugInstance))
40 if (!child.match(bugInstance)) {
4041 return false;
42 }
4143 }
4244 anyMatches = true;
4345 return true;
4446
4547 }
4648
49 @Override
4750 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
4851 if (numberChildren() == 1) {
4952 // System.out.println("One child: " + this);
5154 return;
5255 }
5356 xmlOutput.startTag("And");
54 if (disabled)
57 if (disabled) {
5558 xmlOutput.addAttribute("disabled", "true");
59 }
5660 xmlOutput.stopTag(false);
5761 super.writeChildrenXML(xmlOutput);
5862 xmlOutput.closeTag("And");
6064
6165 @Override
6266 public String toString() {
63 if (numberChildren() == 1)
67 if (numberChildren() == 1) {
6468 return super.toString();
69 }
6570 return "And(" + super.toString() + ")";
6671 }
6772
6873 }
6974
70 // vim:ts=4
2626
2727 /**
2828 * Match bug instances having one of given codes or patterns.
29 *
29 *
3030 * @author rafal@caltha.pl
3131 */
3232 public class BugMatcher implements Matcher {
4040
4141 /**
4242 * Constructor.
43 *
43 *
4444 * @param codes
4545 * comma-separated list of bug codes
4646 * @param patterns
5454 this.categories = new StringSetMatch(categories);
5555 }
5656
57 @Override
5758 public boolean match(BugInstance bugInstance) {
5859 boolean result1 = codes.match(bugInstance.getAbbrev());
5960 boolean result2 = patterns.match(bugInstance.getType());
6061 boolean result3 = categories.match(bugInstance.getBugPattern().getCategory());
61 if (DEBUG)
62 if (DEBUG) {
6263 System.out.println("Matching " + bugInstance.getAbbrev() + "/" + bugInstance.getType() + "/"
6364 + bugInstance.getBugPattern().getCategory() + " with " + this + ", result = " + result1 + "/" + result2 + "/"
6465 + result3);
66 }
6567
6668 return result1 || result2 || result3;
6769 }
7375
7476 @Override
7577 public boolean equals(Object o) {
76 if (!(o instanceof BugMatcher))
78 if (!(o instanceof BugMatcher)) {
7779 return false;
80 }
7881 BugMatcher other = (BugMatcher) o;
7982 return codes.equals(other.codes) && patterns.equals(other.patterns) && categories.equals(other.categories);
8083 }
8184
85 @Override
8286 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
8387 xmlOutput.startTag("Bug");
84 if (disabled)
88 if (disabled) {
8589 xmlOutput.addAttribute("disabled", "true");
90 }
8691
8792 addAttribute(xmlOutput, "code", codes);
8893 addAttribute(xmlOutput, "pattern", patterns);
9297
9398 public void addAttribute(XMLOutput xmlOutput, String name, StringSetMatch matches) throws IOException {
9499 String value = matches.toString();
95 if (value.length() != 0)
100 if (value.length() != 0) {
96101 xmlOutput.addAttribute(name, value);
102 }
97103 }
98104
99105 @Override
100106 public String toString() {
101107 StringBuilder buf = new StringBuilder("Bug(");
102 if (!codes.isEmpty())
108 if (!codes.isEmpty()) {
103109 buf.append("code = \"").append(codes).append("\" ");
104 if (!patterns.isEmpty())
110 }
111 if (!patterns.isEmpty()) {
105112 buf.append("pattern = \"").append(patterns).append("\" ");
106 if (!categories.isEmpty())
113 }
114 if (!categories.isEmpty()) {
107115 buf.append("category = \"").append(categories).append("\" ");
116 }
108117 buf.setLength(buf.length() - 1);
109118 buf.append(")");
110119 return buf.toString();
2020
2121 import java.io.IOException;
2222
23 import edu.umd.cs.findbugs.BugAnnotation;
2324 import edu.umd.cs.findbugs.BugInstance;
2425 import edu.umd.cs.findbugs.ClassAnnotation;
2526 import edu.umd.cs.findbugs.SystemProperties;
2930 public class ClassMatcher implements Matcher {
3031 private static final boolean DEBUG = SystemProperties.getBoolean("filter.debug");
3132
32 private NameMatch className;
33 private final NameMatch className;
34
35 private final String role;
3336
3437 @Override
3538 public String toString() {
3740 }
3841
3942 public ClassMatcher(String className) {
40 this.className = new NameMatch(className);
43 this(className, null);
4144 }
4245
46 public ClassMatcher(String className, String role) {
47 this.className = new NameMatch(className);
48 this.role = role;
49 }
50
51 @Override
4352 public boolean match(BugInstance bugInstance) {
44 ClassAnnotation primaryClassAnnotation = bugInstance.getPrimaryClass();
45 String bugClassName = primaryClassAnnotation.getClassName();
53 ClassAnnotation classAnnotation = bugInstance.getPrimaryClass();
54 if (role != null && !"".equals(role)) {
55 for (BugAnnotation a : bugInstance.getAnnotations()) {
56 if (a instanceof ClassAnnotation && role.equals(a.getDescription())) {
57 classAnnotation = (ClassAnnotation) a;
58 break;
59 }
60 }
61 }
62 String bugClassName = classAnnotation.getClassName();
4663 boolean result = className.match(bugClassName);
47 if (DEBUG)
64 if (DEBUG) {
4865 System.out.println("Matching " + bugClassName + " with " + className + ", result = " + result);
66 }
4967 return result;
5068 }
5169
70 @Override
5271 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
5372 XMLAttributeList attributes = new XMLAttributeList().addAttribute("name", className.getSpec());
54 if (disabled)
73 if (disabled) {
5574 attributes.addAttribute("disabled", "true");
75 }
76 attributes.addOptionalAttribute("role", role);
5677 xmlOutput.openCloseTag("Class", attributes);
5778 }
5879 }
5980
60 // vim:ts=4
3232 @Override
3333 public int hashCode() {
3434 int result = this.getClass().hashCode();
35 for (Matcher m : children)
35 for (Matcher m : children) {
3636 result += m.hashCode();
37 }
3738 return result;
3839 }
3940
4041 @Override
4142 public boolean equals(Object o) {
42 if (o == null)
43 if (o == null) {
4344 return false;
44 if (o.getClass() != this.getClass())
45 }
46 if (o.getClass() != this.getClass()) {
4547 return false;
48 }
4649 CompoundMatcher m = (CompoundMatcher) o;
4750 return children.equals(m.children);
4851 }
7275 }
7376
7477 public void writeChildrenXML(XMLOutput xmlOutput) throws IOException {
75 for (Matcher m : children)
78 for (Matcher m : children) {
7679 m.writeXML(xmlOutput, false);
80 }
7781 }
7882
7983 @Override
8084 public String toString() {
81 if (children.isEmpty())
85 if (children.isEmpty()) {
8286 return "";
87 }
8388 StringBuilder buf = new StringBuilder();
84 for (Matcher m : children)
89 for (Matcher m : children) {
8590 buf.append(m).append(" ");
91 }
8692 buf.setLength(buf.length() - 1);
8793 return buf.toString();
8894 }
9298 }
9399 }
94100
95 // vim:ts=4
0 /*
1 * FindBugs - Find bugs in Java programs
2 * Copyright (C) 2005, University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 package edu.umd.cs.findbugs.filter;
19
20 import java.io.IOException;
21
22 import edu.umd.cs.findbugs.BugInstance;
23 import edu.umd.cs.findbugs.xml.XMLAttributeList;
24 import edu.umd.cs.findbugs.xml.XMLOutput;
25
26 /**
27 * Matcher to select BugInstances with a particular confidence.
28 *
29 * @author David Hovemeyer
30 */
31 public class ConfidenceMatcher implements Matcher {
32 private final int confidence;
33
34 @Override
35 public String toString() {
36 return "Confidence(confidence=" + confidence + ")";
37 }
38
39 /**
40 * Constructor.
41 *
42 * @param confidenceAsString
43 * the confidence, as a String
44 * @throws FilterException
45 */
46 public ConfidenceMatcher(String confidenceAsString) {
47 this.confidence = Integer.parseInt(confidenceAsString);
48 }
49
50 @Override
51 public int hashCode() {
52 return confidence;
53 }
54
55 @Override
56 public boolean equals(Object o) {
57 if (!(o instanceof ConfidenceMatcher)) {
58 return false;
59 }
60 ConfidenceMatcher other = (ConfidenceMatcher) o;
61 return confidence == other.confidence;
62 }
63
64 @Override
65 public boolean match(BugInstance bugInstance) {
66 return bugInstance.getPriority() == confidence;
67 }
68
69 @Override
70 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
71 XMLAttributeList attributes = new XMLAttributeList().addAttribute("value", Integer.toString(confidence));
72 if (disabled) {
73 attributes.addAttribute("disabled", "true");
74 }
75 xmlOutput.openCloseTag("Confidence", attributes);
76 }
77 }
2525
2626 /**
2727 * Match bug instances having one of given codes or patterns.
28 *
28 *
2929 * @author rafal@caltha.pl
3030 */
3131 public class DesignationMatcher implements Matcher {
32 private StringSetMatch designations;
32 private final StringSetMatch designations;
3333
3434 /**
3535 * Constructor.
36 *
36 *
3737 * @param designations
3838 * comma-separated list of designations
3939 */
4141 this.designations = new StringSetMatch(designations);
4242 }
4343
44 @Override
4445 public boolean match(BugInstance bugInstance) {
4546 return designations.match(bugInstance.getUserDesignationKey());
4647 }
5758
5859 @Override
5960 public boolean equals(Object o) {
60 if (!(o instanceof DesignationMatcher))
61 if (!(o instanceof DesignationMatcher)) {
6162 return false;
63 }
6264 DesignationMatcher other = (DesignationMatcher) o;
6365 return designations.equals(other.designations);
6466 }
6567
68 @Override
6669 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
6770 xmlOutput.startTag("Designation");
68 if (disabled)
71 if (disabled) {
6972 xmlOutput.addAttribute("disabled", "true");
73 }
7074 addAttribute(xmlOutput, "designation", designations);
7175 xmlOutput.stopTag(true);
7276 }
7377
7478 public void addAttribute(XMLOutput xmlOutput, String name, StringSetMatch matches) throws IOException {
7579 String value = matches.toString();
76 if (value.length() != 0)
80 if (value.length() != 0) {
7781 xmlOutput.addAttribute(name, value);
82 }
7883 }
7984
8085 }
3636 }
3737
3838 public FieldMatcher(String name, String type) {
39 super(name, SignatureUtil.createFieldSignature(type));
39 this(name, SignatureUtil.createFieldSignature(type), null);
40 }
41
42 public FieldMatcher(String name, String type, String role) {
43 super(name, SignatureUtil.createFieldSignature(type), role);
4044 }
4145
4246 @Override
4448 return "Method(" + super.toString() + ")";
4549 }
4650
51 @Override
4752 public boolean match(BugInstance bugInstance) {
4853 FieldAnnotation fieldAnnotation = null;
49 if (role == null || role.equals(""))
54 if (role == null || "".equals(role)) {
5055 fieldAnnotation = bugInstance.getPrimaryField();
51 else
52 for (BugAnnotation a : bugInstance.getAnnotations())
56 } else {
57 for (BugAnnotation a : bugInstance.getAnnotations()) {
5358 if (a instanceof FieldAnnotation && role.equals(a.getDescription())) {
5459 fieldAnnotation = (FieldAnnotation) a;
5560 break;
5661 }
62 }
63 }
5764 if (fieldAnnotation == null) {
5865 return false;
5966 }
6067 if (!name.match(fieldAnnotation.getFieldName())) {
6168 return false;
6269 }
63 if (signature != null && !signature.match(fieldAnnotation.getFieldSignature()))
70 if (signature != null && !signature.match(fieldAnnotation.getFieldSignature())) {
6471 return false;
72 }
6573 return true;
6674 }
6775
76 @Override
6877 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
6978 XMLAttributeList attributes = new XMLAttributeList().addAttribute("name", name.getSpec());
70 if (signature != null)
71 attributes.addOptionalAttribute(
72 "signature", signature.getSpec());
73 if (disabled)
79 if (signature != null) {
80 attributes.addOptionalAttribute("signature", signature.getSpec());
81 }
82 attributes.addOptionalAttribute("role", role);
83 if (disabled) {
7484 attributes.addAttribute("disabled", "true");
85 }
7586 xmlOutput.openCloseTag("Field", attributes);
7687 }
7788 }
1818
1919 package edu.umd.cs.findbugs.filter;
2020
21 import java.io.BufferedInputStream;
2221 import java.io.File;
2322 import java.io.FileInputStream;
2423 import java.io.IOException;
24 import java.io.InputStream;
2525 import java.io.OutputStream;
2626 import java.io.Reader;
2727 import java.util.IdentityHashMap;
2929
3030 import javax.annotation.WillClose;
3131
32 import org.dom4j.Attribute;
33 import org.dom4j.Document;
34 import org.dom4j.DocumentException;
35 import org.dom4j.Element;
36 import org.dom4j.io.SAXReader;
3732 import org.xml.sax.InputSource;
3833 import org.xml.sax.SAXException;
3934 import org.xml.sax.XMLReader;
4540 import edu.umd.cs.findbugs.util.Util;
4641 import edu.umd.cs.findbugs.xml.OutputStreamXMLOutput;
4742 import edu.umd.cs.findbugs.xml.XMLOutput;
48 import edu.umd.cs.findbugs.xml.XMLUtil;
4943
5044 /**
5145 * Filter to match a subset of BugInstances. The filter criteria are read from
5246 * an XML file.
53 *
47 *
5448 * @author David Hovemeyer
5549 */
5650
5751 public class Filter extends OrMatcher {
5852 private static final boolean DEBUG = SystemProperties.getBoolean("filter.debug");
5953
60 private IdentityHashMap<Matcher, Boolean> disabled = new IdentityHashMap<Matcher, Boolean>();
54 private final IdentityHashMap<Matcher, Boolean> disabled = new IdentityHashMap<Matcher, Boolean>();
6155
6256 /**
6357 * Constructor for empty filter
64 *
58 *
6559 */
6660 public Filter() {
6761
7771
7872 @Override
7973 public boolean equals(Object obj) {
80 if (this == obj)
74 if (this == obj) {
8175 return true;
82 if (!super.equals(obj))
76 }
77 if (!super.equals(obj)) {
8378 return false;
84 if (!(obj instanceof Filter))
79 }
80 if (!(obj instanceof Filter)) {
8581 return false;
82 }
8683 final Filter other = (Filter) obj;
8784 if (disabled == null) {
88 if (other.disabled != null)
85 if (other.disabled != null) {
8986 return false;
90 } else if (!disabled.equals(other.disabled))
87 }
88 } else if (!disabled.equals(other.disabled)) {
9189 return false;
90 }
9291 return true;
9392 }
9493
9796 }
9897
9998 public void setEnabled(Matcher m, boolean value) {
100 if (value)
99 if (value) {
101100 enable(m);
102 else
101 } else {
103102 disable(m);
103 }
104104 }
105105
106106 public void disable(Matcher m) {
121121
122122 /**
123123 * Constructor.
124 *
124 *
125125 * @param fileName
126126 * name of the filter file
127127 * @throws IOException
129129 public Filter(String fileName) throws IOException {
130130 try {
131131 parse(fileName);
132 if (false)
133 System.out.println("Parsed: " + this);
134132 } catch (SAXException e) {
135133 throw new IOException(e.getMessage());
136134 }
137135 }
138136
137 /**
138 * Constructor.
139 *
140 * @param stream
141 * content of the filter file
142 * @throws IOException
143 */
144 public Filter(InputStream stream) throws IOException {
145 try {
146 parse("", stream);
147 } catch (SAXException e) {
148 throw new IOException(e.getMessage());
149 }
150 }
151
139152 public boolean contains(Matcher child) {
140153 return children.contains(child);
141154 }
142155
143156 /**
144157 * Add if not present, but do not enable if already present and disabled
145 *
158 *
146159 * @param child
147160 */
148161 public void softAdd(Matcher child) {
172185 Iterator<Matcher> i = childIterator();
173186 while (i.hasNext()) {
174187 Matcher child = i.next();
175 if (isEnabled(child) && child.match(bugInstance))
188 if (isEnabled(child) && child.match(bugInstance)) {
176189 return true;
190 }
177191 }
178192 return false;
179193 }
180194
181195 /**
182196 * Parse and load the given filter file.
183 *
197 *
184198 * @param fileName
185199 * name of the filter file
186200 * @throws IOException
188202 * @throws FilterException
189203 */
190204 private void parse(String fileName) throws IOException, SAXException {
191
192 if (true) {
193 File file = new File(fileName);
194 SAXBugCollectionHandler handler = new SAXBugCollectionHandler(this, file);
205 FileInputStream fileInputStream = new FileInputStream(new File(fileName));
206 parse(fileName, fileInputStream);
207 }
208
209 /**
210 * Parse and load the given filter file.
211 *
212 * @param fileName
213 * name of the filter file
214 * @throws IOException
215 * @throws SAXException
216 * @throws FilterException
217 */
218 private void parse(String fileName, @WillClose InputStream stream) throws IOException, SAXException {
219 try {
220 SAXBugCollectionHandler handler = new SAXBugCollectionHandler(this, new File(fileName));
195221 XMLReader xr = XMLReaderFactory.createXMLReader();
196222 xr.setContentHandler(handler);
197223 xr.setErrorHandler(handler);
198 FileInputStream fileInputStream = new FileInputStream(file);
199 try {
200 Reader reader = Util.getReader(fileInputStream);
201 xr.parse(new InputSource(reader));
202 } finally {
203 Util.closeSilently(fileInputStream);
204 }
205 return;
206
207 }
208 Document filterDoc = null;
209
210 FileInputStream fileInputStream = new FileInputStream(fileName);
211
212 try {
213 SAXReader reader = new SAXReader();
214 filterDoc = reader.read(new BufferedInputStream(fileInputStream));
215 } catch (DocumentException e) {
216 throw new FilterException("Couldn't parse filter file " + fileName, e);
217 }
218
219 int count = 1;
220 // Iterate over Match elements
221 for (Object matchObj : XMLUtil.selectNodes(filterDoc, "/FindBugsFilter/Match")) {
222 Element matchNode = (Element) matchObj;
223 AndMatcher matchMatcher = new AndMatcher();
224
225 // Each match node may have either "class" or "classregex"
226 // attributes
227 Matcher classMatcher = null;
228 String classAttr = matchNode.valueOf("@class");
229 if (!classAttr.equals("")) {
230 classMatcher = new ClassMatcher(classAttr);
231 } else {
232 String classRegex = matchNode.valueOf("@classregex");
233 if (!classRegex.equals(""))
234 classMatcher = new ClassMatcher("~" + classRegex);
235 }
236 if (classMatcher != null)
237 matchMatcher.addChild(classMatcher);
238
239 if (DEBUG)
240 System.out.println("Match node");
241
242 // Iterate over child elements of Match node.
243 Iterator<?> j = matchNode.elementIterator();
244 while (j.hasNext()) {
245 Element child = (Element) j.next();
246 Matcher matcher = getMatcher(child);
247 matchMatcher.addChild(matcher);
248 }
249 if (matchMatcher.numberChildren() == 0)
250 throw new FilterException("Match element #" + count + " (starting at 1) is invalid in filter file " + fileName);
251 // Add the Match matcher to the overall Filter
252 this.addChild(matchMatcher);
253 count++;
254 }
255 if (this.numberChildren() == 0)
256 throw new FilterException("Could not find any /FindBugsFilter/Match nodes in filter file " + fileName);
257
258 }
259
260 /**
261 * Get a Matcher for given Element.
262 *
263 * @param element
264 * the Element
265 * @return a Matcher representing that element
266 * @throws FilterException
267 */
268 private static Matcher getMatcher(Element element) throws FilterException {
269 // These will be either BugCode, Priority, Class, Method, Field, or Or
270 // elements.
271 String name = element.getName();
272 if (name.equals("BugCode")) {
273 return new BugMatcher(element.valueOf("@name"), "", "");
274 } else if (name.equals("Local")) {
275 return new LocalMatcher(element.valueOf("@name"));
276 } else if (name.equals("BugPattern")) {
277 return new BugMatcher("", element.valueOf("@name"), "");
278 } else if (name.equals("Bug")) {
279 return new BugMatcher(element.valueOf("@code"), element.valueOf("@pattern"), element.valueOf("@category"));
280 } else if (name.equals("Priority") || name.equals("Confidence")) {
281 return new PriorityMatcher(element.valueOf("@value"));
282 } else if (name.equals("Rank")) {
283 return new RankMatcher(element.valueOf("@value"));
284 } else if (name.equals("Class")) {
285 Attribute nameAttr = element.attribute("name");
286
287 if (nameAttr == null)
288 throw new FilterException("Missing name attribute in Class element");
289
290 return new ClassMatcher(nameAttr.getValue());
291 } else if (name.equals("Package")) {
292 Attribute nameAttr = element.attribute("name");
293
294 if (nameAttr == null)
295 throw new FilterException("Missing name attribute in Package element");
296
297 String pName = nameAttr.getValue();
298 pName = pName.startsWith("~") ? pName : "~" + pName.replace(".", "\\.");
299 return new ClassMatcher(pName + "\\.[^.]+");
300 } else if (name.equals("Method")) {
301 Attribute nameAttr = element.attribute("name");
302 String nameValue;
303 Attribute paramsAttr = element.attribute("params");
304 Attribute returnsAttr = element.attribute("returns");
305 Attribute roleAttr = element.attribute("role");
306
307 if (nameAttr == null)
308 if (paramsAttr == null || returnsAttr == null)
309 throw new FilterException("Method element must have eiter name or params and returnss attributes");
310 else
311 nameValue = "~.*"; // any name
312 else
313 nameValue = nameAttr.getValue();
314
315 if ((paramsAttr != null || returnsAttr != null) && (paramsAttr == null || returnsAttr == null))
316 throw new FilterException("Method element must have both params and returns attributes if either is used");
317
318 if (paramsAttr == null)
319 if (roleAttr == null)
320 return new MethodMatcher(nameValue);
321 else
322 return new MethodMatcher(nameValue, roleAttr.getValue());
323 else if (roleAttr == null)
324 return new MethodMatcher(nameValue, paramsAttr.getValue(), returnsAttr.getValue());
325 else
326 return new MethodMatcher(nameValue, paramsAttr.getValue(), returnsAttr.getValue(), roleAttr.getValue());
327
328 } else if (name.equals("Field")) {
329 Attribute nameAttr = element.attribute("name");
330 String nameValue;
331 Attribute typeAttr = element.attribute("type");
332
333 if (nameAttr == null)
334 if (typeAttr == null)
335 throw new FilterException("Field element must have either name or type attribute");
336 else
337 nameValue = "~.*"; // any name
338 else
339 nameValue = nameAttr.getValue();
340
341 if (typeAttr == null)
342 return new FieldMatcher(nameValue);
343 else
344 return new FieldMatcher(nameValue, typeAttr.getValue());
345 } else if (name.equals("Or")) {
346 OrMatcher orMatcher = new OrMatcher();
347 Iterator<?> i = element.elementIterator();
348 while (i.hasNext()) {
349 orMatcher.addChild(getMatcher((Element) i.next()));
350 }
351 return orMatcher;
352 } else
353 throw new FilterException("Unknown element: " + name);
224 Reader reader = Util.getReader(stream);
225 xr.parse(new InputSource(reader));
226 } finally {
227 Util.closeSilently(stream);
228 }
354229 }
355230
356231 public static void main(String[] argv) {
411286
412287 }
413288
414 // vim:ts=4
4444 return "FirstVersion(version" + relOp + version + ")";
4545 }
4646
47 @Override
4748 public boolean match(BugInstance bugInstance) {
4849 return relOp.check(bugInstance.getFirstVersion(), version);
4950 }
5051
52 @Override
5153 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
5254 XMLAttributeList attributes = new XMLAttributeList().addAttribute("value", Long.toString(version)).addAttribute("relOp",
5355 relOp.getName());
54 if (disabled)
56 if (disabled) {
5557 attributes.addAttribute("disabled", "true");
58 }
5659 xmlOutput.openCloseTag("FirstVersion", attributes);
5760 }
5861 }
4242 super(version, relOp);
4343 }
4444
45 @Override
4546 public boolean match(BugInstance bugInstance) {
4647 return relOp.check(bugInstance.getLastVersion(), version);
4748 }
4849
50 @Override
4951 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
5052 XMLAttributeList attributes = new XMLAttributeList().addAttribute("value", Long.toString(version)).addAttribute("relOp",
5153 relOp.getName());
52 if (disabled)
54 if (disabled) {
5355 attributes.addAttribute("disabled", "true");
56 }
5457 xmlOutput.openCloseTag("LastVersion", attributes);
5558 }
5659
5760 @Override
5861 public String toString() {
59 if (version == -1 && relOp == RelationalOp.EQ)
62 if (version == -1 && relOp == RelationalOp.EQ) {
6063 return "ActiveBugs";
61 else if (version == -1 && relOp == RelationalOp.NEQ)
64 } else if (version == -1 && relOp == RelationalOp.NEQ) {
6265 return "DeadBugs";
66 }
6367 return "LastVersion(version " + relOp + version + ")";
6468 }
6569 }
2626 import edu.umd.cs.findbugs.xml.XMLOutput;
2727
2828 public class LocalMatcher implements Matcher {
29 private NameMatch name;
29 private final NameMatch name;
3030
3131 public LocalMatcher(String name) {
3232 this.name = new NameMatch(name);
4141 return "Local(name=" + name + ")";
4242 }
4343
44 @Override
4445 public boolean match(BugInstance bugInstance) {
4546 LocalVariableAnnotation localAnnotation = bugInstance.getPrimaryLocalVariableAnnotation();
4647 if (localAnnotation == null) {
5253 return true;
5354 }
5455
56 @Override
5557 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
5658 XMLAttributeList attributes = new XMLAttributeList().addAttribute("name", name.getSpec());
57 if (disabled)
59 if (disabled) {
5860 attributes.addAttribute("disabled", "true");
61 }
5962 xmlOutput.openCloseTag("Local", attributes);
6063 }
6164 }
2525
2626 /**
2727 * Match BugInstances for some feature.
28 *
28 *
2929 * @author David Hovemeyer
3030 */
3131 public interface Matcher {
3232 /**
3333 * Determine whether or not the given BugInstance has the feature this
3434 * Matcher tests for.
35 *
35 *
3636 * @param bugInstance
3737 * the BugInstance
3838 * @return true if the BugInstance matches, false if not
4242 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException;
4343 }
4444
45 // vim:ts=4
4646 public MemberMatcher(String name, String signature, String role) {
4747
4848 if (name == null) {
49 if (signature == null)
49 if (signature == null) {
5050 throw new FilterException(this.getClass().getName() + " must have eiter name or signature attributes");
51 else
51 }
52 else {
5253 name = "~.*"; // any name
54 }
5355 }
5456
5557 this.name = new NameMatch(name);
6769 buf.append("\"");
6870 }
6971 if (signature != null) {
70 if (buf.length() > 0)
72 if (buf.length() > 0) {
7173 buf.append(" ");
74 }
7275 buf.append("signature=\"");
7376 buf.append(signature);
7477 buf.append("\"");
8386
8487 @Override
8588 public boolean equals(Object o) {
86 if (o == null || this.getClass() != o.getClass())
89 if (o == null || this.getClass() != o.getClass()) {
8790 return false;
91 }
8892
8993 MemberMatcher other = (MemberMatcher) o;
9094 return name.equals(other.name) && Util.nullSafeEquals(signature, other.signature);
2323 import edu.umd.cs.findbugs.BugAnnotation;
2424 import edu.umd.cs.findbugs.BugInstance;
2525 import edu.umd.cs.findbugs.MethodAnnotation;
26 import edu.umd.cs.findbugs.ba.SignatureConverter;
2627 import edu.umd.cs.findbugs.xml.XMLAttributeList;
2728 import edu.umd.cs.findbugs.xml.XMLOutput;
2829
4445 super(name, SignatureUtil.createMethodSignature(params, returns), role);
4546 }
4647
48 @Override
4749 public boolean match(BugInstance bugInstance) {
4850
4951 MethodAnnotation methodAnnotation = null;
50 if (role == null || role.equals(""))
52 if (role == null || "".equals(role)) {
5153 methodAnnotation = bugInstance.getPrimaryMethod();
52 else
53 for (BugAnnotation a : bugInstance.getAnnotations())
54 } else {
55 for (BugAnnotation a : bugInstance.getAnnotations()) {
5456 if (a instanceof MethodAnnotation && role.equals(a.getDescription())) {
5557 methodAnnotation = (MethodAnnotation) a;
5658 break;
5759 }
58 if (methodAnnotation == null)
60 }
61 }
62 if (methodAnnotation == null) {
5963 return false;
60 if (!name.match(methodAnnotation.getMethodName()))
64 }
65 if (!name.match(methodAnnotation.getMethodName())) {
6166 return false;
62 if (signature != null && !signature.match(methodAnnotation.getMethodSignature()))
67 }
68 if (signature != null && !signature.match(methodAnnotation.getMethodSignature())) {
6369 return false;
70 }
6471 return true;
6572 }
6673
6976 return "Method(" + super.toString() + ")";
7077 }
7178
79 @Override
7280 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
7381 XMLAttributeList attributes = new XMLAttributeList().addAttribute("name", name.getSpec());
74 if (signature != null)
75 attributes
76 .addOptionalAttribute("signature", signature.getSpec()).addOptionalAttribute("role", role);
77 if (disabled)
82 if (signature != null && signature.getSpec() != null) {
83 StringBuilder paramsBuilder = new StringBuilder();
84 SignatureConverter converter = new SignatureConverter(signature.getSpec());
85 converter.skip();
86 while (converter.getFirst() != ')') {
87 if (paramsBuilder.length() > 1) {
88 paramsBuilder.append(", ");
89 }
90 paramsBuilder.append(converter.parseNext());
91 }
92 converter.skip();
93
94 String params = paramsBuilder.toString();
95 String returns = converter.parseNext();
96 attributes.addAttribute("params", params);
97 attributes.addAttribute("returns", returns);
98 }
99 attributes.addOptionalAttribute("role", role);
100 if (disabled) {
78101 attributes.addAttribute("disabled", "true");
102 }
79103 xmlOutput.openCloseTag("Method", attributes);
80104 }
81105 }
82106
83 // vim:ts=4
2626
2727 /**
2828 * Matches a String value against a predefined specification.
29 *
29 *
3030 * Matching can be done in three modes depending on ctor matchSpec argument.
31 *
31 *
3232 * If matchSpec is null, match will succeed for any value (including empty
3333 * String and null)
34 *
34 *
3535 * If matchSpec starts with ~ character it will be treated as
3636 * java.util.regex.Pattern, with the ~ character omited. The pattern will be
3737 * matched against whole value (ie Matcher.match(), not Matcher.find())
38 *
38 *
3939 * If matchSpec is a non-null String with any other initial charcter, exact
4040 * matching using String.equals(String) will be performed.
41 *
41 *
4242 * @author rafal@caltha.pl
4343 */
4444 public class NameMatch {
4545
4646 private @CheckForNull
47 final
4748 String spec;
4849
4950 private @CheckForNull
5455
5556 @Override
5657 public int hashCode() {
57 if (spec == null)
58 if (spec == null) {
5859 return 0;
60 }
5961 return spec.hashCode();
6062 }
6163
6264 public boolean isUniversal() {
63 if (spec == null)
65 if (spec == null) {
6466 return true;
65 return spec.equals("~.*");
67 }
68 return "~.*".equals(spec);
6669 }
6770
6871 @Override
6972 public boolean equals(Object o) {
70 if (!(o instanceof NameMatch))
73 if (!(o instanceof NameMatch)) {
7174 return false;
75 }
7276 return Util.nullSafeEquals(spec, ((NameMatch) o).spec);
7377 }
7478
7579 public String getValue() {
76 if (exact != null)
80 if (exact != null) {
7781 return exact;
78 if (pattern != null)
82 }
83 if (pattern != null) {
7984 return pattern.toString();
85 }
8086 return "~.*";
8187 }
8288
9298 }
9399
94100 public boolean match(String value) {
95 if (exact != null)
101 if (exact != null) {
96102 return exact.equals(value);
97 if (pattern != null)
103 }
104 if (pattern != null) {
98105 return pattern.matcher(value).matches();
106 }
99107 return true;
100108 }
101109
102110 @Override
103111 public String toString() {
104 if (exact != null)
112 if (exact != null) {
105113 return "exact(" + exact + ")";
106 if (pattern != null)
114 }
115 if (pattern != null) {
107116 return "regex(" + pattern.toString() + ")";
117 }
108118 return "any()";
109119 }
110120
2828
2929 public class NotMatcher extends CompoundMatcher {
3030
31 @Override
3132 public boolean match(BugInstance bugInstance) {
3233 if(!childIterator().hasNext() ) {
3334 return false;
3738 return ! invertedMatcher.match(bugInstance);
3839 }
3940
41 @Override
4042 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
4143 if(childIterator().hasNext()) {
4244 xmlOutput.startTag("Not");
43 if (disabled) xmlOutput.addAttribute("disabled","true");
45 if (disabled) {
46 xmlOutput.addAttribute("disabled","true");
47 }
4448 Matcher invertedMatcher = childIterator().next();
4549 xmlOutput.stopTag(false);
4650
4953 xmlOutput.closeTag("Not");
5054 }
5155 }
56
5257 @Override
5358 public String toString() {
5459 Matcher invertedMatcher = childIterator().hasNext() ? childIterator().next() : null;
6166 return 1;
6267 }
6368
64 @Override
65 public void addChild(Matcher child) {
66 super.addChild(child);
67 }
68
6969 public Matcher originalMatcher() {
7070 Iterator<Matcher> childMatchers = childIterator();
7171 if (childMatchers.hasNext()) {
2626
2727 public class OrMatcher extends CompoundMatcher {
2828
29 @Override
2930 public boolean match(BugInstance bugInstance) {
3031 Iterator<Matcher> i = childIterator();
3132 while (i.hasNext()) {
3233 Matcher child = i.next();
33 if (child.match(bugInstance))
34 if (child.match(bugInstance)) {
3435 return true;
36 }
3537 }
3638 return false;
3739 }
3840
41 @Override
3942 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
4043 if (numberChildren() == 1) {
4144 childIterator().next().writeXML(xmlOutput, false);
4245 return;
4346 }
4447 xmlOutput.startTag("Or");
45 if (disabled)
48 if (disabled) {
4649 xmlOutput.addAttribute("disabled", "true");
50 }
4751 xmlOutput.stopTag(false);
4852 writeChildrenXML(xmlOutput);
4953 xmlOutput.closeTag("Or");
5155
5256 @Override
5357 public String toString() {
54 if (numberChildren() == 1)
58 if (numberChildren() == 1) {
5559 return super.toString();
60 }
5661 return "Or(" + super.toString() + ")";
5762 }
5863
5964 }
6065
61 // vim:ts=4
2525
2626 /**
2727 * Matcher to select BugInstances with a particular priority.
28 *
28 *
2929 * @author David Hovemeyer
3030 */
3131 public class PriorityMatcher implements Matcher {
32 private int priority;
32 private final int priority;
3333
3434 @Override
3535 public String toString() {
3838
3939 /**
4040 * Constructor.
41 *
41 *
4242 * @param priorityAsString
4343 * the priority, as a String
4444 * @throws FilterException
5454
5555 @Override
5656 public boolean equals(Object o) {
57 if (!(o instanceof PriorityMatcher))
57 if (!(o instanceof PriorityMatcher)) {
5858 return false;
59 }
5960 PriorityMatcher other = (PriorityMatcher) o;
6061 return priority == other.priority;
6162 }
6263
64 @Override
6365 public boolean match(BugInstance bugInstance) {
6466 return bugInstance.getPriority() == priority;
6567 }
6668
69 @Override
6770 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
6871 XMLAttributeList attributes = new XMLAttributeList().addAttribute("value", Integer.toString(priority));
69 if (disabled)
72 if (disabled) {
7073 attributes.addAttribute("disabled", "true");
74 }
7175 xmlOutput.openCloseTag("Priority", attributes);
7276 }
7377 }
2626
2727 /**
2828 * Matcher to select BugInstances with a particular rank or higher.
29 *
29 *
3030 * @author William Pugh
3131 */
3232 public class RankMatcher implements Matcher {
33 private int rank;
33 private final int rank;
3434
3535 @Override
3636 public String toString() {
3939
4040 /**
4141 * Constructor.
42 *
42 *
4343 * @param rankAsString
4444 * the rank, as a String
4545 * @throws NumberFormatException
5656
5757 @Override
5858 public boolean equals(Object o) {
59 if (!(o instanceof RankMatcher))
59 if (!(o instanceof RankMatcher)) {
6060 return false;
61 }
6162 RankMatcher other = (RankMatcher) o;
6263 return rank == other.rank;
6364 }
6465
66 @Override
6567 public boolean match(BugInstance bugInstance) {
6668 return BugRanker.findRank(bugInstance) >= rank;
6769 }
6870
71 @Override
6972 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
7073 XMLAttributeList attributes = new XMLAttributeList().addAttribute("value", Integer.toString(rank));
71 if (disabled)
74 if (disabled) {
7275 attributes.addAttribute("disabled", "true");
76 }
7377 xmlOutput.openCloseTag("Rank", attributes);
7478 }
7579 }
4040
4141 public static RelationalOp byName(String s) {
4242 RelationalOp relationalOp = map.get(s);
43 if (relationalOp == null)
43 if (relationalOp == null) {
4444 throw new IllegalArgumentException("Could not find relOp named " + s + " in " + map.keySet());
45 }
4546 return relationalOp;
4647 }
4748
2727 public class SignatureUtil {
2828
2929 public static String createMethodSignature(String params, String returns) {
30 if (params == null && returns == null)
30 if (params == null && returns == null) {
3131 return null;
32
32 }
33
3334 String pString, rString;
34 if (params == null)
35 if (params == null) {
3536 pString = ".*";
36 else {
37 StringBuilder buf = new StringBuilder();
37 } else {
38 StringBuilder buf = new StringBuilder();
3839
39 StringTokenizer tok = new StringTokenizer(params, " \t\n\r\f,");
40 while (tok.hasMoreTokens()) {
41 String param = typeToSignature(tok.nextToken());
42 buf.append(param);
40 StringTokenizer tok = new StringTokenizer(params, " \t\n\r\f,");
41 while (tok.hasMoreTokens()) {
42 String param = typeToSignature(tok.nextToken());
43 buf.append(param);
44 }
45 pString = buf.toString();
4346 }
44 pString = buf.toString();
47 if (returns == null) {
48 rString = ".*";
49 } else {
50 rString = typeToSignature(returns);
4551 }
46 if (returns == null)
47 rString = ".*";
48 else
49 rString = typeToSignature(returns);
5052 if (params == null || returns == null) {
5153 String result = "~\\(" + pString + "\\)" + rString;
5254 assert Pattern.compile(result.substring(1)) != null;
5355 return result;
56 } else {
57 return "(" + pString + ")" + rString;
5458 }
55
56 else
57 return "(" + pString + ")" + rString;
58
59
5960 }
6061
6162 public static String createFieldSignature(String type) {
62 if (type == null)
63 if (type == null) {
6364 return null;
65 }
6466 return typeToSignature(type);
6567 }
6668
7375 }
7476
7577 private static String scalarTypeToSiganture(String type) {
76 if (type.equals("boolean"))
78 if ("boolean".equals(type)) {
7779 return "Z";
78 else if (type.equals("byte"))
80 } else if ("byte".equals(type)) {
7981 return "B";
80 else if (type.equals("char"))
82 } else if ("char".equals(type)) {
8183 return "C";
82 else if (type.equals("short"))
84 } else if ("short".equals(type)) {
8385 return "S";
84 else if (type.equals("int"))
86 } else if ("int".equals(type)) {
8587 return "I";
86 else if (type.equals("long"))
88 } else if ("long".equals(type)) {
8789 return "J";
88 else if (type.equals("float"))
90 } else if ("float".equals(type)) {
8991 return "F";
90 else if (type.equals("double"))
92 } else if ("double".equals(type)) {
9193 return "D";
92 else if (type.equals("void"))
94 } else if ("void".equals(type)) {
9395 return "V";
94 else
96 } else {
9597 return "L" + type.replace('.', '/') + ";";
98 }
9699 }
97100 }
0 /*
1 * FindBugs - Find bugs in Java programs
2 * Copyright (C) 2003-2005, University of Maryland
3 *
4 * Author: Andrey Loskutov
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 package edu.umd.cs.findbugs.filter;
22
23 import java.io.IOException;
24
25 import edu.umd.cs.findbugs.BugInstance;
26 import edu.umd.cs.findbugs.ClassAnnotation;
27 import edu.umd.cs.findbugs.SystemProperties;
28 import edu.umd.cs.findbugs.xml.XMLAttributeList;
29 import edu.umd.cs.findbugs.xml.XMLOutput;
30
31 public class SourceMatcher implements Matcher {
32 private static final boolean DEBUG = SystemProperties.getBoolean("filter.debug");
33
34 private final NameMatch fileName;
35
36 @Override
37 public String toString() {
38 return "Source(file=\"" + fileName.getValue() + "\")";
39 }
40
41 public SourceMatcher(String fileName) {
42 this.fileName = new NameMatch(fileName);
43 }
44
45 @Override
46 public boolean match(BugInstance bugInstance) {
47 ClassAnnotation primaryClassAnnotation = bugInstance.getPrimaryClass();
48 if(primaryClassAnnotation == null){
49 return false;
50 }
51 String bugFileName = primaryClassAnnotation.getSourceFileName();
52 if(bugFileName == null || bugFileName.isEmpty()){
53 return false;
54 }
55 boolean result = fileName.match(bugFileName);
56 if (DEBUG) {
57 System.out.println("Matching " + bugFileName + " with " + fileName + ", result = " + result);
58 }
59 return result;
60 }
61
62 @Override
63 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
64 XMLAttributeList attributes = new XMLAttributeList().addAttribute("name", fileName.getSpec());
65 if (disabled) {
66 attributes.addAttribute("disabled", "true");
67 }
68 xmlOutput.openCloseTag("Source", attributes);
69 }
70 }
71
2424
2525 /**
2626 * Matches a string against a set of predefined values.
27 *
27 *
2828 * Value set is defined using a String containing a comma separated value list.
2929 * Heading an trailing whitespace on the values is ignored in matching.
30 *
30 *
3131 * @author rak
3232 */
3333 public class StringSetMatch {
34 private Set<String> strings = new HashSet<String>();
34 private final Set<String> strings = new HashSet<String>();
3535
3636 @Override
3737 public int hashCode() {
4040
4141 @Override
4242 public boolean equals(Object o) {
43 if (!(o instanceof StringSetMatch))
43 if (!(o instanceof StringSetMatch)) {
4444 return false;
45 }
4546 return strings.equals(((StringSetMatch) o).strings);
4647 }
4748
4849 /**
4950 * Constructor.
50 *
51 *
5152 * @param strings
5253 * comma-separated list of Strings
5354 */
6667
6768 /**
6869 * Returns true if the given string is contained in the value set.
69 *
70 *
7071 * @param string
7172 * @return true if the given string is contained in the value set
7273 */
7677
7778 @Override
7879 public String toString() {
79 if (strings.isEmpty())
80 if (strings.isEmpty()) {
8081 return "";
82 }
8183 StringBuilder result = new StringBuilder();
82 for (String s : strings)
84 for (String s : strings) {
8385 result.append(s).append(",");
86 }
8487 return result.substring(0, result.length() - 1);
8588 }
8689 }
0 /*
1 * FindBugs - Find bugs in Java programs
2 * Copyright (C) 2003-2005, University of Maryland
3 *
4 * Author: Andrey Loskutov
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 package edu.umd.cs.findbugs.filter;
22
23 import java.io.IOException;
24
25 import edu.umd.cs.findbugs.BugAnnotation;
26 import edu.umd.cs.findbugs.BugInstance;
27 import edu.umd.cs.findbugs.TypeAnnotation;
28 import edu.umd.cs.findbugs.xml.XMLAttributeList;
29 import edu.umd.cs.findbugs.xml.XMLOutput;
30
31 public class TypeMatcher implements Matcher {
32
33 private final NameMatch descriptor;
34
35 private final String role;
36
37 private final String typeParameters;
38
39 @Override
40 public String toString() {
41 return "Type(descriptor=\"" + descriptor.getValue() + "\")";
42 }
43
44 public TypeMatcher(String descriptor, String role, String typeParameters) {
45 this.descriptor = new NameMatch(descriptor);
46 this.role = role;
47 this.typeParameters = typeParameters;
48 }
49
50 @Override
51 public boolean match(BugInstance bugInstance) {
52 TypeAnnotation typeAnnotation = bugInstance.getPrimaryType();
53 if (role != null && !"".equals(role)) {
54 for (BugAnnotation a : bugInstance.getAnnotations()) {
55 if (a instanceof TypeAnnotation && role.equals(a.getDescription())) {
56 typeAnnotation = (TypeAnnotation) a;
57 break;
58 }
59 }
60 }
61 if(typeAnnotation == null){
62 return false;
63 }
64 String typeDesctiptor = typeAnnotation.getTypeDescriptor();
65 if(!descriptor.match(typeDesctiptor)){
66 return false;
67 }
68 if (typeParameters != null && !typeParameters.equals(typeAnnotation.getTypeParameters())) {
69 return false;
70 }
71 return true;
72 }
73
74 @Override
75 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
76 XMLAttributeList attributes = new XMLAttributeList().addAttribute("descriptor", descriptor.getSpec());
77 if (disabled) {
78 attributes.addAttribute("disabled", "true");
79 }
80 attributes.addOptionalAttribute("typeParameters", typeParameters);
81 attributes.addOptionalAttribute("role", role);
82 xmlOutput.openCloseTag("Type", attributes);
83 }
84 }
85
3636
3737 @Override
3838 public boolean equals(Object o) {
39 if (o == null || this.getClass() != o.getClass())
39 if (o == null || this.getClass() != o.getClass()) {
4040 return false;
41 }
4142 VersionMatcher m = (VersionMatcher) o;
4243 return version == m.version && relOp.equals(m.relOp);
4344 }
4445
4546 public VersionMatcher(long version, @Nonnull RelationalOp relOp) {
46 if (relOp == null)
47 if (relOp == null) {
4748 throw new NullPointerException("relOp must be nonnull");
49 }
4850 this.version = version;
4951 this.relOp = relOp;
5052 }
3434 * <li>produces a topological sort of the vertices,
3535 * <em>if and only if the graph is acyclic</em>
3636 * </ul>
37 *
37 *
3838 * <p>
3939 * Concrete subclasses implement forward and reverse versions of depth first
4040 * search.
41 *
41 *
4242 * @author David Hovemeyer
4343 * @see Graph
4444 * @see DepthFirstSearch
4545 * @see ReverseDepthFirstSearch
4646 */
4747 public abstract class AbstractDepthFirstSearch<GraphType extends Graph<EdgeType, VertexType>, EdgeType extends GraphEdge<EdgeType, VertexType>, VertexType extends GraphVertex<VertexType>>
48 implements DFSEdgeTypes {
48 implements DFSEdgeTypes {
4949
5050 public final static boolean DEBUG = false;
5151
52 private GraphType graph;
53
54 private int[] colorList;
55
56 private int[] discoveryTimeList;
57
58 private int[] finishTimeList;
59
60 private int[] dfsEdgeTypeList;
52 private final GraphType graph;
53
54 private final int[] colorList;
55
56 private final int[] discoveryTimeList;
57
58 private final int[] finishTimeList;
59
60 private final int[] dfsEdgeTypeList;
6161
6262 private int timestamp;
6363
64 private LinkedList<VertexType> topologicalSortList;
64 private final LinkedList<VertexType> topologicalSortList;
6565
6666 private VertexChooser<VertexType> vertexChooser;
6767
8585
8686 /**
8787 * Constructor.
88 *
88 *
8989 * @param graph
9090 * the graph to be searched
9191 * @throws IllegalArgumentException
130130 * Choose the next search tree root. By default, this method just scans for
131131 * a WHITE vertex. Subclasses may override this method in order to choose
132132 * which vertices are used as search tree roots.
133 *
133 *
134134 * @return the next search tree root
135135 */
136136 protected VertexType getNextSearchTreeRoot() {
137137 // FIXME: slow linear search, should improve
138138 for (Iterator<VertexType> i = graph.vertexIterator(); i.hasNext();) {
139139 VertexType vertex = i.next();
140 if (visitMe(vertex))
140 if (visitMe(vertex)) {
141141 return vertex;
142 }
142143 }
143144 return null;
144145 }
148149
149150 for (Iterator<VertexType> i = graph.vertexIterator(); i.hasNext();) {
150151 VertexType v = i.next();
151 if (getColor(v) == WHITE)
152 if (getColor(v) == WHITE) {
152153 result.add(v);
154 }
153155 }
154156 return result;
155157 }
158160 * Specify a VertexChooser object to be used to selected which vertices are
159161 * visited by the search. This is useful if you only want to search a subset
160162 * of the vertices in the graph.
161 *
163 *
162164 * @param vertexChooser
163165 * the VertexChooser to use
164166 */
168170
169171 /**
170172 * Set a search tree callback.
171 *
173 *
172174 * @param searchTreeCallback
173175 * the search tree callback
174176 */
178180
179181 /**
180182 * Perform the depth first search.
181 *
183 *
182184 * @return this object
183185 */
184186 public AbstractDepthFirstSearch<GraphType, EdgeType, VertexType> search() {
192194 * Return whether or not the graph contains a cycle: i.e., whether it
193195 * contains any back edges. This should only be called after search() has
194196 * been called (since that method actually executes the search).
195 *
197 *
196198 * @return true if the graph contains a cycle, false otherwise
197199 */
198200 public boolean containsCycle() {
199201 for (Iterator<EdgeType> i = graph.edgeIterator(); i.hasNext();) {
200202 EdgeType edge = i.next();
201 if (getDFSEdgeType(edge) == BACK_EDGE)
203 if (getDFSEdgeType(edge) == BACK_EDGE) {
202204 return true;
205 }
203206 }
204207 return false;
205208 }
206209
207210 /**
208211 * Get the type of edge in the depth first search.
209 *
212 *
210213 * @param edge
211214 * the edge
212215 * @return the DFS type of edge: TREE_EDGE, FORWARD_EDGE, CROSS_EDGE, or
219222
220223 /**
221224 * Return the timestamp indicating when the given vertex was discovered.
222 *
225 *
223226 * @param vertex
224227 * the vertex
225228 */
230233 /**
231234 * Return the timestamp indicating when the given vertex was finished
232235 * (meaning that all of its descendents were visited recursively).
233 *
236 *
234237 * @param vertex
235238 * the vertex
236239 */
240243
241244 /**
242245 * Get array of finish times, indexed by vertex label.
243 *
246 *
244247 * @return the array of finish times
245248 */
246249 @SuppressFBWarnings("EI")
257260 }
258261
259262 private class Visit {
260 private VertexType vertex;
261
262 private Iterator<EdgeType> outgoingEdgeIterator;
263 private final VertexType vertex;
264
265 private final Iterator<EdgeType> outgoingEdgeIterator;
263266
264267 public Visit(VertexType vertex) {
265 if (vertex == null)
268 if (vertex == null) {
266269 throw new IllegalStateException();
270 }
267271 this.vertex = vertex;
268272 this.outgoingEdgeIterator = outgoingEdgeIterator(graph, vertex);
269273
288292 private void visitAll() {
289293 for (;;) {
290294 VertexType searchTreeRoot = getNextSearchTreeRoot();
291 if (searchTreeRoot == null)
295 if (searchTreeRoot == null) {
292296 // No more work to do
293297 break;
294
295 if (searchTreeCallback != null)
298 }
299
300 if (searchTreeCallback != null) {
296301 searchTreeCallback.startSearchTree(searchTreeRoot);
302 }
297303
298304 ArrayList<Visit> stack = new ArrayList<Visit>(graph.getNumVertexLabels());
299305 stack.add(new Visit(searchTreeRoot));
335341 case BLACK:
336342 dfsEdgeType = UNKNOWN_EDGE;
337343 break;// We can't distinguish between CROSS and FORWARD edges at
338 // this point
344 // this point
339345 default:
340346 assert false;
341347 }
344350 // If successor hasn't been visited yet, visit it
345351 if (visitMe(succ)) {
346352 // Add to search tree (if a search tree callback exists)
347 if (searchTreeCallback != null)
353 if (searchTreeCallback != null) {
348354 searchTreeCallback.addToSearchTree(getSource(edge), getTarget(edge));
355 }
349356
350357 // Add to visitation stack
351358 stack.add(new Visit(succ));
383390
384391 /**
385392 * Get the current color of given vertex.
386 *
393 *
387394 * @param vertex
388395 * the vertex
389396 * @return the color (WHITE, BLACK, or GRAY)
396403 * Predicate to determine which vertices should be visited as the search
397404 * progresses. Takes both vertex color and the vertex chooser (if any) into
398405 * account.
399 *
406 *
400407 * @param vertex
401408 * the vertex to possibly be visited
402409 * @return true if the vertex should be visited, false if not
418425 }
419426 }
420427
421 // vim:ts=4
2020
2121 /**
2222 * GraphEdge implementation for use with AbstractGraph.
23 *
23 *
2424 * @see GraphEdge
2525 * @see AbstractGraph
2626 * @see AbstractVertex
2727 * @author David Hovemeyer
2828 */
2929 public class AbstractEdge<ActualEdgeType extends AbstractEdge<ActualEdgeType, VertexType>, VertexType extends AbstractVertex<ActualEdgeType, VertexType>>
30 implements GraphEdge<ActualEdgeType, VertexType> {
30 implements GraphEdge<ActualEdgeType, VertexType> {
3131
32 private VertexType source;
32 private final VertexType source;
3333
34 private VertexType target;
34 private final VertexType target;
3535
3636 private int label;
3737
4141
4242 /**
4343 * Constructor.
44 *
44 *
4545 * @param source
4646 * the source vertex of the edge
4747 * @param target
5252 this.target = target;
5353 }
5454
55 @Override
5556 public VertexType getSource() {
5657 return source;
5758 }
5859
60 @Override
5961 public VertexType getTarget() {
6062 return target;
6163 }
6264
65 @Override
6366 public int getLabel() {
6467 return label;
6568 }
6669
70 @Override
6771 public void setLabel(int label) {
6872 this.label = label;
6973 }
7579
7680 @Override
7781 public boolean equals(Object o) {
78 if (!(o instanceof AbstractEdge))
82 if (!(o instanceof AbstractEdge)) {
7983 return false;
84 }
8085 AbstractEdge<?,?> other = (AbstractEdge<?,?>) o;
8186 return source.equals(other.source) && target.equals(other.target);
8287 }
8388
89 @Override
8490 public int compareTo(ActualEdgeType other) {
8591 int cmp = source.compareTo(other.getSource());
86 if (cmp != 0)
92 if (cmp != 0) {
8793 return cmp;
94 }
8895 return target.compareTo(other.getTarget());
8996 }
9097
106113
107114 }
108115
109 // vim:ts=4
2626 * A simple Graph implementation where the vertex objects store a list of
2727 * incoming and outgoing edges. The edge link fields are stored in the edge
2828 * objects, which means a fairly low space overhead.
29 *
29 *
3030 * <p>
3131 * The abstract allocateEdge() method must be implemented.
32 *
32 *
3333 * @see Graph
3434 * @see AbstractEdge
3535 * @see AbstractVertex
3636 * @author David Hovemeyer
3737 */
3838 public abstract class AbstractGraph<EdgeType extends AbstractEdge<EdgeType, VertexType>, VertexType extends AbstractVertex<EdgeType, VertexType>>
39 implements Graph<EdgeType, VertexType> {
39 implements Graph<EdgeType, VertexType> {
4040
4141 /*
4242 * ----------------------------------------------------------------------
4848 * Iterator over outgoing edges.
4949 */
5050 private static class OutgoingEdgeIterator<EdgeType extends AbstractEdge<EdgeType, VertexType>, VertexType extends AbstractVertex<EdgeType, VertexType>>
51 implements Iterator<EdgeType> {
51 implements Iterator<EdgeType> {
5252
5353 private EdgeType edge;
5454
5656 this.edge = source.getFirstOutgoingEdge();
5757 }
5858
59 @Override
5960 public boolean hasNext() {
6061 return edge != null;
6162 }
6263
64 @Override
6365 public EdgeType next() {
64 if (!hasNext())
66 if (!hasNext()) {
6567 throw new NoSuchElementException();
68 }
6669 EdgeType result = edge;
6770 edge = edge.getNextOutgoingEdge();
6871 return result;
6972 }
7073
74 @Override
7175 public void remove() {
7276 throw new UnsupportedOperationException();
7377 }
7781 * Iterator over incoming edges.
7882 */
7983 private static class IncomingEdgeIterator<EdgeType extends AbstractEdge<EdgeType, VertexType>, VertexType extends AbstractVertex<EdgeType, VertexType>>
80 implements Iterator<EdgeType> {
84 implements Iterator<EdgeType> {
8185
8286 private EdgeType edge;
8387
8589 this.edge = target.getFirstIncomingEdge();
8690 }
8791
92 @Override
8893 public boolean hasNext() {
8994 return edge != null;
9095 }
9196
97 @Override
9298 public EdgeType next() {
93 if (!hasNext())
99 if (!hasNext()) {
94100 throw new NoSuchElementException();
101 }
95102 EdgeType result = edge;
96103 edge = edge.getNextIncomingEdge();
97104 return result;
98105 }
99106
107 @Override
100108 public void remove() {
101109 throw new UnsupportedOperationException();
102110 }
108116 * ----------------------------------------------------------------------
109117 */
110118
111 private ArrayList<VertexType> vertexList;
112
113 private ArrayList<EdgeType> edgeList;
119 private final ArrayList<VertexType> vertexList;
120
121 private final ArrayList<EdgeType> edgeList;
114122
115123 private int maxVertexLabel;
116124
130138 this.maxEdgeLabel = 0;
131139 }
132140
141 @Override
133142 public int getNumEdges() {
134143 return edgeList.size();
135144 }
136145
146 @Override
137147 public int getNumVertices() {
138148 return vertexList.size();
139149 }
140150
151 @Override
141152 public Iterator<EdgeType> edgeIterator() {
142153 return edgeList.iterator();
143154 }
144155
156 @Override
145157 public Iterator<VertexType> vertexIterator() {
146158 return vertexList.iterator();
147159 }
150162 return vertexList;
151163 }
152164
165 @Override
153166 public void addVertex(VertexType v) {
154167 vertexList.add(v);
155168 v.setLabel(maxVertexLabel++);
156169 }
157170
171 @Override
158172 public boolean containsVertex(VertexType v) {
159173 for (VertexType existingVertex : vertexList) {
160 if (v == existingVertex)
174 if (v == existingVertex) {
161175 return true;
176 }
162177 }
163178 return false;
164179 }
165180
181 @Override
166182 public EdgeType createEdge(VertexType source, VertexType target) {
167183 EdgeType edge = allocateEdge(source, target);
168184 edgeList.add(edge);
172188 return edge;
173189 }
174190
191 @Override
175192 public EdgeType lookupEdge(VertexType source, VertexType target) {
176193 Iterator<EdgeType> i = outgoingEdgeIterator(source);
177194 while (i.hasNext()) {
178195 EdgeType edge = i.next();
179 if (edge.getTarget() == target)
196 if (edge.getTarget() == target) {
180197 return edge;
198 }
181199 }
182200 return null;
183201 }
184202
203 @Override
185204 public int getNumVertexLabels() {
186205 return maxVertexLabel;
187206 }
188207
208 @Override
189209 public void setNumVertexLabels(int numLabels) {
190210 this.maxVertexLabel = numLabels;
191211 }
192212
213 @Override
193214 public int getNumEdgeLabels() {
194215 return maxEdgeLabel;
195216 }
196217
218 @Override
197219 public void setNumEdgeLabels(int numLabels) {
198220 maxEdgeLabel = numLabels;
199221 }
200222
223 @Override
201224 public void removeEdge(EdgeType edge) {
202 if (!edgeList.remove(edge))
225 if (!edgeList.remove(edge)) {
203226 throw new IllegalArgumentException("removing nonexistent edge!");
227 }
204228 edge.getSource().removeOutgoingEdge(edge);
205229 edge.getTarget().removeIncomingEdge(edge);
206230 }
207231
232 @Override
208233 public void removeVertex(VertexType v) {
209 if (!vertexList.remove(v))
234 if (!vertexList.remove(v)) {
210235 throw new IllegalArgumentException("removing nonexistent vertex!");
211
212 for (Iterator<EdgeType> i = incomingEdgeIterator(v); i.hasNext();)
236 }
237
238 for (Iterator<EdgeType> i = incomingEdgeIterator(v); i.hasNext();) {
213239 removeEdge(i.next());
214
215 for (Iterator<EdgeType> i = outgoingEdgeIterator(v); i.hasNext();)
240 }
241
242 for (Iterator<EdgeType> i = outgoingEdgeIterator(v); i.hasNext();) {
216243 removeEdge(i.next());
217 }
218
244 }
245 }
246
247 @Override
219248 public Iterator<EdgeType> outgoingEdgeIterator(VertexType source) {
220249 return new OutgoingEdgeIterator<EdgeType, VertexType>(source);
221250 }
222251
252 @Override
223253 public Iterator<EdgeType> incomingEdgeIterator(VertexType target) {
224254 return new IncomingEdgeIterator<EdgeType, VertexType>(target);
225255 }
226256
257 @Override
227258 public int getNumIncomingEdges(VertexType vertex) {
228259 int count = 0;
229260 EdgeType e = vertex.firstIncomingEdge;
234265 return count;
235266 }
236267
268 @Override
237269 public int getNumOutgoingEdges(VertexType vertex) {
238270 int count = 0;
239271 EdgeType e = vertex.firstOutgoingEdge;
244276 return count;
245277 }
246278
279 @Override
247280 public Iterator<VertexType> successorIterator(final VertexType source) {
248281 return new Iterator<VertexType>() {
249 private Iterator<EdgeType> iter = outgoingEdgeIterator(source);
250
282 private final Iterator<EdgeType> iter = outgoingEdgeIterator(source);
283
284 @Override
251285 public boolean hasNext() {
252286 return iter.hasNext();
253287 }
254288
289 @Override
255290 public VertexType next() {
256291 return iter.next().getTarget();
257292 }
258293
294 @Override
259295 public void remove() {
260296 iter.remove();
261297 }
262298 };
263299 }
264300
301 @Override
265302 public Iterator<VertexType> predecessorIterator(final VertexType target) {
266303 return new Iterator<VertexType>() {
267 private Iterator<EdgeType> iter = incomingEdgeIterator(target);
268
304 private final Iterator<EdgeType> iter = incomingEdgeIterator(target);
305
306 @Override
269307 public boolean hasNext() {
270308 return iter.hasNext();
271309 }
272310
311 @Override
273312 public VertexType next() {
274313 return iter.next().getSource();
275314 }
276315
316 @Override
277317 public void remove() {
278318 iter.remove();
279319 }
290330
291331 }
292332
293 // vim:ts=4
2020
2121 /**
2222 * GraphVertex implementation for use with AbstractGraph.
23 *
23 *
2424 * @see GraphVertex
2525 * @see AbstractGraph
2626 * @see AbstractEdge
2727 * @author David Hovemeyer
2828 */
2929 public class AbstractVertex<EdgeType extends AbstractEdge<EdgeType, ActualVertexType>, ActualVertexType extends AbstractVertex<EdgeType, ActualVertexType>>
30 implements GraphVertex<ActualVertexType> {
30 implements GraphVertex<ActualVertexType> {
3131
3232 private int label;
3333
3535
3636 EdgeType firstOutgoingEdge, lastOutgoingEdge;
3737
38 @Override
3839 public int getLabel() {
3940 return label;
4041 }
4142
43 @Override
4244 public void setLabel(int label) {
4345 this.label = label;
4446 }
5759 return other.getLabel() == this.getLabel();
5860 }
5961
62 @Override
6063 public int compareTo(ActualVertexType other) {
6164 if (this.getLabel() < other.getLabel()) {
6265 return -1;
98101 while (cur != null) {
99102 EdgeType next = cur.getNextIncomingEdge();
100103 if (cur.equals(edge)) {
101 if (prev != null)
104 if (prev != null) {
102105 prev.setNextIncomingEdge(next);
103 else
106 } else {
104107 firstIncomingEdge = next;
108 }
105109 return;
106110 }
107111 prev = cur;
115119 while (cur != null) {
116120 EdgeType next = cur.getNextOutgoingEdge();
117121 if (cur.equals(edge)) {
118 if (prev != null)
122 if (prev != null) {
119123 prev.setNextOutgoingEdge(next);
120 else
124 } else {
121125 firstOutgoingEdge = next;
126 }
122127 return;
123128 }
124129 prev = cur;
129134
130135 }
131136
132 // vim:ts=4
2020
2121 /**
2222 * Edge types in a depth first search.
23 *
23 *
2424 * @see DepthFirstSearch
2525 */
2626 public interface DFSEdgeTypes {
5252 public static final int CROSS_EDGE = 3;
5353 }
5454
55 // vim:ts=4
2222
2323 /**
2424 * Perform a forward depth first search of a graph.
25 *
25 *
2626 * @author David Hovemeyer
2727 * @see Graph
2828 * @see AbstractDepthFirstSearch
2929 */
3030 public class DepthFirstSearch<GraphType extends Graph<EdgeType, VertexType>, EdgeType extends GraphEdge<EdgeType, VertexType>, VertexType extends GraphVertex<VertexType>>
31 extends AbstractDepthFirstSearch<GraphType, EdgeType, VertexType> {
31 extends AbstractDepthFirstSearch<GraphType, EdgeType, VertexType> {
3232
3333 /**
3434 * Constructor.
35 *
35 *
3636 * @param graph
3737 * the graph to perform a depth first search of
3838 */
5757
5858 }
5959
60 // vim:ts=4
5151 /**
5252 * Add given vertex to the graph. The vertex should not be part of any other
5353 * graph.
54 *
54 *
5555 * @param v
5656 * the vertex to add
5757 */
5959
6060 /**
6161 * Determine if the graph contains the given vertex.
62 *
62 *
6363 * @param v
6464 * the vertex
6565 * @return true if the vertex is part of the graph, false if not
6969 /**
7070 * Add a new edge to the graph. Duplicate edges (with same source and target
7171 * vertices) are allowed.
72 *
72 *
7373 * @param source
7474 * the source vertex
7575 * @param target
8181 /**
8282 * Look up an edge by source and target vertex. If multiple edges with same
8383 * source and target vertex exist, one is selected arbitrarily.
84 *
84 *
8585 * @param source
8686 * the source vertex
8787 * @param target
126126
127127 /**
128128 * Get an Iterator over outgoing edges from given vertex.
129 *
129 *
130130 * @param source
131131 * the source vertex
132132 * @return an Iterator over outgoing edges
135135
136136 /**
137137 * Get an Iterator over incoming edges to a given vertex.
138 *
138 *
139139 * @param target
140140 * the target vertex
141141 * @return an Iterator over incoming edges
144144
145145 /**
146146 * Get number of edges going into given vertex.
147 *
147 *
148148 * @param vertex
149149 * the vertex
150150 * @return number of edges going into the vertex
153153
154154 /**
155155 * Get number of edges going out of given vertex.
156 *
156 *
157157 * @param vertex
158158 * the vertex
159159 * @return number of edges going out of the vertex
163163 /**
164164 * Get an iterator over the successors of this vertex; i.e., the targets of
165165 * the vertex's outgoing edges.
166 *
166 *
167167 * @param source
168168 * the source vertex
169169 * @return an Iterator over the successors of the vertex
173173 /**
174174 * Get an iterator over the predecessors of this vertex; i.e., the sources
175175 * of the vertex's incoming edges.
176 *
176 *
177177 * @param target
178178 * the target vertex
179179 * @return an Iterator over the predecessors of the vertex
182182
183183 }
184184
185 // vim:ts=4
2424 * GraphEdge interface; represents an edge in a graph.
2525 */
2626 public interface GraphEdge<ActualEdgeType extends GraphEdge<ActualEdgeType, VertexType>, VertexType extends GraphVertex<VertexType>>
27 extends Comparable<ActualEdgeType> {
27 extends Comparable<ActualEdgeType> {
2828
2929 /**
3030 * Get the source vertex.
4848
4949 }
5050
51 // vim:ts=4
2626
2727 /**
2828 * Create a new empty graph (no vertices or edges).
29 *
29 *
3030 * @return the new graph
3131 */
3232 public GraphType createGraph();
3333
3434 /**
3535 * Make a copy of given vertex.
36 *
36 *
3737 * @param original
3838 * the vertex to copy
3939 * @return an exact duplicate of the vertex
4242
4343 /**
4444 * Copy auxiliary information from one edge to another.
45 *
45 *
4646 * @param source
4747 * the source edge
4848 * @param dest
5252
5353 }
5454
55 // vim:ts=4
3838
3939 /**
4040 * Merge the specified set of vertices into a single vertex.
41 *
41 *
4242 * @param vertexSet
4343 * the set of vertices to be merged
4444 * @param g
5353
5454 // Special case: if the vertex set contains a single vertex
5555 // or is empty, there is nothing to do
56 if (vertexSet.size() <= 1)
56 if (vertexSet.size() <= 1) {
5757 return;
58 }
5859
5960 // Get all vertices to which we have outgoing edges
6061 // or from which we have incoming edges, since they'll need
6263 TreeSet<EdgeType> edgeSet = new TreeSet<EdgeType>();
6364 for (Iterator<EdgeType> i = g.edgeIterator(); i.hasNext();) {
6465 EdgeType e = i.next();
65 if (vertexSet.contains(e.getSource()) || vertexSet.contains(e.getTarget()))
66 if (vertexSet.contains(e.getSource()) || vertexSet.contains(e.getTarget())) {
6667 edgeSet.add(e);
68 }
6769 }
6870
6971 // Combine all of the vertices into a single composite vertex
8284 // Don't create a self edge for the composite vertex
8385 // unless one of the vertices in the vertex set
8486 // had a self edge
85 if (source == compositeVertex && target == compositeVertex && e.getSource() != e.getTarget())
87 if (source == compositeVertex && target == compositeVertex && e.getSource() != e.getTarget()) {
8688 continue;
89 }
8790
8891 // Don't create duplicate edges.
89 if (g.lookupEdge(source, target) != null)
92 if (g.lookupEdge(source, target) != null) {
9093 continue;
94 }
9195
9296 EdgeType compositeEdge = g.createEdge(source, target);
9397 // FIXME: we really should have an EdgeCombinator here
105109
106110 }
107111
108 // vim:ts=4
2323 /**
2424 * Perform a reverse depth first search of a graph. (I.e., depth first search of
2525 * reversed graph.)
26 *
26 *
2727 * @author David Hovemeyer
2828 * @see Graph
2929 * @see AbstractDepthFirstSearch
3030 */
3131 public class ReverseDepthFirstSearch<GraphType extends Graph<EdgeType, VertexType>, EdgeType extends GraphEdge<EdgeType, VertexType>, VertexType extends GraphVertex<VertexType>>
32 extends AbstractDepthFirstSearch<GraphType, EdgeType, VertexType> {
32 extends AbstractDepthFirstSearch<GraphType, EdgeType, VertexType> {
3333
3434 /**
3535 * Constructor.
36 *
36 *
3737 * @param graph
3838 * the graph to perform a reverse depth first search of
3939 */
5858
5959 }
6060
61 // vim:ts=4
3030 */
3131 public class SearchTree<VertexType extends GraphVertex<VertexType>> {
3232
33 private VertexType m_vertex;
33 private final VertexType m_vertex;
3434
35 private ArrayList<SearchTree<VertexType>> m_childList;
35 private final ArrayList<SearchTree<VertexType>> m_childList;
3636
3737 /**
3838 * Create a new search tree.
8181
8282 }
8383
84 // vim:ts=4
2525 /**
2626 * A search tree callback implementation that builds a list of SearchTrees
2727 * recording a graph search.
28 *
28 *
2929 * @see SearchTreeCallback
3030 * @author David Hovemeyer
3131 */
3232 public class SearchTreeBuilder<VertexType extends GraphVertex<VertexType>> implements SearchTreeCallback<VertexType> {
3333
34 private HashMap<VertexType, SearchTree<VertexType>> searchTreeMap = new HashMap<VertexType, SearchTree<VertexType>>();
34 private final HashMap<VertexType, SearchTree<VertexType>> searchTreeMap = new HashMap<VertexType, SearchTree<VertexType>>();
3535
36 private LinkedList<SearchTree<VertexType>> searchTreeList = new LinkedList<SearchTree<VertexType>>();
36 private final LinkedList<SearchTree<VertexType>> searchTreeList = new LinkedList<SearchTree<VertexType>>();
3737
38 @Override
3839 public void startSearchTree(VertexType vertex) {
3940 searchTreeList.add(createSearchTree(vertex));
4041 }
4142
43 @Override
4244 public void addToSearchTree(VertexType parent, VertexType child) {
4345 SearchTree<VertexType> parentTree = searchTreeMap.get(parent);
44 if (parentTree == null)
46 if (parentTree == null) {
4547 throw new IllegalStateException();
48 }
4649 SearchTree<VertexType> childTree = createSearchTree(child);
4750 parentTree.addChild(childTree);
4851 }
6164 }
6265 }
6366
64 // vim:ts=4
2424 public interface SearchTreeCallback<VertexType extends GraphVertex<VertexType>> {
2525 /**
2626 * Start a search tree.
27 *
27 *
2828 * @param vertex
2929 * the root of the search tree
3030 */
3232
3333 /**
3434 * Add an edge to the current search tree.
35 *
35 *
3636 * @param parent
3737 * the parent vertex
3838 * @param child
4141 public void addToSearchTree(VertexType parent, VertexType child);
4242 }
4343
44 // vim:ts=4
3131 */
3232 public class StronglyConnectedComponents<GraphType extends Graph<EdgeType, VertexType>, EdgeType extends GraphEdge<EdgeType, VertexType>, VertexType extends GraphVertex<VertexType>> {
3333
34 private ArrayList<SearchTree<VertexType>> m_stronglyConnectedSearchTreeList;
34 private final ArrayList<SearchTree<VertexType>> m_stronglyConnectedSearchTreeList;
3535
3636 private VertexChooser<VertexType> m_vertexChooser;
3737
5454
5555 /**
5656 * Find the strongly connected components in given graph.
57 *
57 *
5858 * @param g
5959 * the graph
6060 * @param toolkit
6565
6666 // Perform the initial depth first search
6767 DepthFirstSearch<GraphType, EdgeType, VertexType> initialDFS = new DepthFirstSearch<GraphType, EdgeType, VertexType>(g);
68 if (m_vertexChooser != null)
68 if (m_vertexChooser != null) {
6969 initialDFS.setVertexChooser(m_vertexChooser);
70 }
7071 initialDFS.search();
7172
7273 // Create a transposed graph
9697 protected VertexType getNextSearchTreeRoot() {
9798 while (vertexIter.hasNext()) {
9899 VertexType vertex = vertexIter.next();
99 if (visitMe(vertex))
100 if (visitMe(vertex)) {
100101 return vertex;
102 }
101103 }
102104 return null;
103105 }
104106 };
105 if (m_vertexChooser != null)
107 if (m_vertexChooser != null) {
106108 transposeDFS.setVertexChooser(m_vertexChooser);
109 }
107110 transposeDFS.setSearchTreeCallback(searchTreeBuilder);
108111 transposeDFS.search();
109112
120123 /**
121124 * Make a copy of given search tree (in the transposed graph) using vertices
122125 * of the original graph.
123 *
126 *
124127 * @param tree
125128 * a search tree in the transposed graph
126129 * @param t
144147 /**
145148 * Returns an iterator over the search trees containing the vertices of each
146149 * strongly connected component.
147 *
150 *
148151 * @return an Iterator over a sequence of SearchTree objects
149152 */
150153 public Iterator<SearchTree<VertexType>> searchTreeIterator() {
156159 * components.
157160 */
158161 private class SCCSetIterator implements Iterator<Set<VertexType>> {
159 private Iterator<SearchTree<VertexType>> m_searchTreeIterator;
162 private final Iterator<SearchTree<VertexType>> m_searchTreeIterator;
160163
161164 public SCCSetIterator() {
162165 m_searchTreeIterator = searchTreeIterator();
163166 }
164167
168 @Override
165169 public boolean hasNext() {
166170 return m_searchTreeIterator.hasNext();
167171 }
168172
173 @Override
169174 public Set<VertexType> next() {
170175 SearchTree<VertexType> tree = m_searchTreeIterator.next();
171176 TreeSet<VertexType> set = new TreeSet<VertexType>();
173178 return set;
174179 }
175180
181 @Override
176182 public void remove() {
177183 throw new UnsupportedOperationException();
178184 }
181187 /**
182188 * Returns an iterator over the sets of vertices of each strongly connected
183189 * component.
184 *
190 *
185191 * @return an Iterator over a sequence of Set objects
186192 */
187193 public Iterator<Set<VertexType>> setIterator() {
190196
191197 }
192198
193 // vim:ts=4
2828 */
2929 public class Transpose<GraphType extends Graph<EdgeType, VertexType>, EdgeType extends GraphEdge<EdgeType, VertexType>, VertexType extends GraphVertex<VertexType>> {
3030
31 private IdentityHashMap<VertexType, VertexType> m_origToTransposeMap;
31 private final IdentityHashMap<VertexType, VertexType> m_origToTransposeMap;
3232
33 private IdentityHashMap<VertexType, VertexType> m_transposeToOrigMap;
33 private final IdentityHashMap<VertexType, VertexType> m_transposeToOrigMap;
3434
3535 /**
3636 * Constructor.
4343 /**
4444 * Transpose a graph. Note that the original graph is not modified; the new
4545 * graph and its vertices and edges are new objects.
46 *
46 *
4747 * @param orig
4848 * the graph to transpose
4949 * @param toolkit
9696 /**
9797 * Get the vertex in the transposed graph which corresponds to the given
9898 * vertex in the original graph.
99 *
99 *
100100 * @param v
101101 * the vertex in the original graph
102102 * @return the equivalent vertex in the transposed graph
108108 /**
109109 * Get the vertex in the original graph which corresponds to the given
110110 * vertex in the transposed graph.
111 *
111 *
112112 * @param v
113113 * the vertex in the transposed graph
114114 * @return the equivalent vertex in the original graph
119119
120120 }
121121
122 // vim:ts=4
2929 public interface VertexCombinator<VertexType extends GraphVertex<VertexType>> {
3030 /**
3131 * Combine given vertices into a single vertex.
32 *
32 *
3333 * @param vertexSet
3434 * set of vertices to be combined
3535 * @return the result of combining the vertices in the set
3737 public VertexType combineVertices(Set<VertexType> vertexSet);
3838 }
3939
40 // vim:ts=4
4343 */
4444 public static final int DESCENDING = 1;
4545
46 private int[] m_visitationTimeList;
46 private final int[] m_visitationTimeList;
4747
48 private int m_direction;
48 private final int m_direction;
4949
5050 /**
5151 * Constructor.
52 *
52 *
5353 * @param visitationTimeList
5454 * array of visitation times indexed by vertex label
5555 * @param direction
6060 m_visitationTimeList = visitationTimeList;
6161 m_direction = direction;
6262
63 if (direction != ASCENDING && direction != DESCENDING)
63 if (direction != ASCENDING && direction != DESCENDING) {
6464 throw new IllegalArgumentException();
65 }
6566 }
6667
68 @Override
6769 public int compare(VertexType v1, VertexType v2) {
6870 int f1 = m_visitationTimeList[v1.getLabel()];
6971 int f2 = m_visitationTimeList[v2.getLabel()];
7072
71 if (m_direction == ASCENDING)
73 if (m_direction == ASCENDING) {
7274 return f1 - f2;
73 else
75 } else {
7476 return f2 - f1;
77 }
7578 }
7679
7780 }
7881
79 // vim:ts=4
0 # Japanese translation provided by Shisei Hanai
1
2
3 menu.file_menu=\u30d5\u30a1\u30a4\u30eb(&F)
4 menu.new_item=\u65b0\u898f\u30d7\u30ed\u30b8\u30a7\u30af\u30c8(&N)...
5 menu.open_item=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u958b\u304f(&O)...
6 menu.recent_menu=\u6700\u8fd1\u4f7f\u7528\u3057\u305f\u30d7\u30ed\u30b8\u30a7\u30af\u30c8(&E)
7 menu.save_item=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u4fdd\u5b58(&S)
8 menu.saveas_item=\u540d\u524d\u3092\u4ed8\u3051\u3066\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u4fdd\u5b58(&A)...
9 menu.reload_item=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u518d\u8aad\u307f\u8fbc\u307f(&R)
10 menu.close_item=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u9589\u3058\u308b(&C)
11 menu.loadbugs_item=\u30d0\u30b0\u306e\u8aad\u307f\u8fbc\u307f(&L)...
12 menu.savebugs_item=\u30d0\u30b0\u306e\u4fdd\u5b58(&A)...
13 menu.exit_item=\u7d42\u4e86(&X)
14 menu.reconfig=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u518d\u69cb\u6210
15
16 menu.edit_menu=\u7de8\u96c6(&E)
17 menu.selectall_item=\u3059\u3079\u3066\u9078\u629e(&A)
18
19 menu.navigation=\u30ca\u30d3\u30b2\u30fc\u30b7\u30e7\u30f3(&N)
20 menu.designation=\u30d0\u30b0\u5206\u985e\u6307\u5b9a(&D)
21 menu.view_menu=\u8868\u793a(&V)
22 menu.console_item=\u30b3\u30f3\u30bd\u30fc\u30eb(&C)
23 menu.bugdetails_item=\u30d0\u30b0\u306e\u8a73\u7d30(&D)
24 menu.fulldescriptions_item=\u8a73\u7d30\u8aac\u660e(&F)
25 menu.viewprojectdetails_item=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u8a73\u7d30\u306e\u8868\u793a
26 menu.viewbugs_item=\u30d0\u30b0\u306e\u8868\u793a
27 menu.exit=\u7d42\u4e86
28 menu.filtersAndSupressions=\u30d5\u30a3\u30eb\u30bf\u30fc/\u6291\u6b62
29 menu.sortConfiguration=\u30bd\u30fc\u30c8\u8a2d\u5b9a...
30 menu.gotoLine=\u6307\u5b9a\u884c\u3078\u79fb\u52d5...
31 menu.changeDesignation=\u30d0\u30b0\u5206\u985e\u6307\u5b9a\u306e\u5909\u66f4
32 menu.filterBugsLikeThis=\u30d5\u30a3\u30eb\u30bf\u30fc\u3092\u8a2d\u5b9a...
33 menu.filterTheseBugs=\u30d5\u30a3\u30eb\u30bf\u30ea\u30f3\u30b0\u3059\u308b
34 menu.mergeAnalysis=\u30d0\u30b0\u3092\u30de\u30fc\u30b8...
35 menu.filterwarnings_menu=\u8b66\u544a\u30d5\u30a3\u30eb\u30bf\u30fc(&W)
36 menu.suppress=\u3053\u306e\u30d0\u30b0\u3092\u6291\u6b62
37 menu.expand=\u5c55\u958b\u3059\u308b
38 menu.collapse=\u6298\u308a\u305f\u305f\u3080
39 menu.up=\u4e0a\u3078
40 menu.down=\u4e0b\u3078
41
42 menu.filterwarnings_menu=\u8b66\u544a\u30d5\u30a3\u30eb\u30bf\u30fc
43 menu.exppriority_item=\u512a\u5148\u5ea6(\u5b9f\u9a13\u6bb5\u968e)
44 menu.lowpriority_item=\u512a\u5148\u5ea6(\u4f4e)
45 menu.mediumpriority_item=\u512a\u5148\u5ea6(\u4e2d)
46 menu.highpriority_item=\u512a\u5148\u5ea6(\u9ad8)
47
48 menu.preferences_menu=\u8a2d\u5b9a(&P)...
49 menu.settings_menu=\u8a2d\u5b9a(&S)
50 menu.configure_item=\u30c7\u30a3\u30c6\u30af\u30bf\u306e\u8a2d\u5b9a(&C)...
51 menu.effort_menu=Effort
52 menu.mineffort_item=\u6700\u5c0f
53 menu.normaleffort_item=\u6a19\u6e96
54 menu.maxeffort_item=\u6700\u5927
55
56 menu.help_menu=\u30d8\u30eb\u30d7(&H)
57 menu.about_item=FindBug\u306b\u3064\u3044\u3066(&A)
58
59 menu.empty_item=\u7a7a
60 menu.rerunAnalysis=\u5206\u6790\u306e\u518d\u5b9f\u884c
61
62 dlg.noproject_lbl=FindBugs - \u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u304c\u3042\u308a\u307e\u305b\u3093
63 dlg.new_item=\u65b0\u898f\u30d7\u30ed\u30b8\u30a7\u30af\u30c8
64 dlg.reconfig=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u518d\u69cb\u6210
65 dlg.unnamed_lbl=<<\u7121\u540d\u30d7\u30ed\u30b8\u30a7\u30af\u30c8>>
66 dlg.project_lbl=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8
67 dlg.jarfile_lbl=\u30a2\u30fc\u30ab\u30a4\u30d6\u307e\u305f\u306f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc
68 dlg.jarlist_lbl=\u30a2\u30fc\u30ab\u30a4\u30d6/\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc:
69 dlg.srcfile_lbl=\u30bd\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc:
70 dlg.srclist_lbl=\u30bd\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc:
71 dlg.classpathfile_lbl=\u30af\u30e9\u30b9\u30d1\u30b9\u30fb\u30a8\u30f3\u30c8\u30ea\u30fc:
72 dlg.classpathlist_lbl=\u30af\u30e9\u30b9\u30d1\u30b9\u30fb\u30a8\u30f3\u30c8\u30ea\u30fc:
73 dlg.add_btn=\u8ffd\u52a0...
74 dlg.remove_btn=\u524a\u9664
75 dlg.browse_btn=\u53c2\u7167
76 dlg.up_btn=\u4e0a\u3078
77 dlg.down_btn=\u4e0b\u3078
78
79 dlg.byclass_tab=\u30af\u30e9\u30b9\u5225
80 dlg.bypackage_tab=\u30d1\u30c3\u30b1\u30fc\u30b8\u5225
81 dlg.bybugtype_tab=\u30d0\u30b0\u30bf\u30a4\u30d7\u5225
82 dlg.bybugcategory_tab=\u30d0\u30b0\u30ab\u30c6\u30b4\u30ea\u30fc\u5225
83 dlg.summary_tab=\u30b5\u30de\u30ea\u30fc
84 dlg.details_tab=\u8a73\u7d30
85 dlg.sourcecode_tab=\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9
86 dlg.annotations_tab=\u6ce8\u91c8
87
88 dlg.analysiserror_lbl=\u5206\u6790\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f:
89 dlg.ok_btn=\u4e86\u89e3
90
91 dlg.stage_lbl=Stage:
92 dlg.count_lbl=Count:
93 dlg.progress_lbl=Progress:
94 dlg.cancel_btn=\u30ad\u30e3\u30f3\u30bb\u30eb
95
96 dlg.restoredefaults_btn=\u30c7\u30d5\u30a9\u30eb\u30c8\u306b\u623b\u3059
97
98 dlg.bugdetector_lbl=Bug Detector
99 dlg.speed_lbl=Speed
100 dlg.enabled_lbl=Enabled
101
102 dlg.about_tab=FindBug\u306b\u3064\u3044\u3066
103 dlg.license_tab=\u30e9\u30a4\u30bb\u30f3\u30b9
104 dlg.acknowledgements_tab=\u8b1d\u8f9e
105
106 dlg.findbugsprojects_lbl=FindBugs \u30d7\u30ed\u30b8\u30a7\u30af\u30c8 (*.fb)
107 dlg.jarsanddirectories_lbl=\u30a2\u30fc\u30ab\u30a4\u30d6\u30d5\u30a1\u30a4\u30eb\u304a\u3088\u3073\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc
108
109 dlg.runanalysis_ttl=\u5206\u6790\u306e\u5b9f\u884c
110 dlg.aboutfindbugs_ttl=FindBugs {0} \u306b\u3064\u3044\u3066
111 dlg.analysiserrors_ttl=\u5206\u6790\u30a8\u30e9\u30fc
112 dlg.configuredetectors_ttl=\u30c7\u30a3\u30c6\u30af\u30bf\u306e\u8a2d\u5b9a
113 dlg.xmlsavedbugs_lbl=\u30d0\u30b0\u4fdd\u5b58 XML \u30d5\u30a1\u30a4\u30eb
114 dlg.javaarchives_lbl=Java \u30a2\u30fc\u30ab\u30a4\u30d6 (*.class,*.jar,*.zip,*.ear,*.war,*.sar)
115 dlg.closeproject_lbl=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u9589\u3058\u308b
116 dlg.saveprojectas_ttl=\u540d\u524d\u3092\u4ed8\u3051\u3066\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u4fdd\u5b58
117 dlg.loadbugs_ttl=\u30d0\u30b0\u306e\u8aad\u307f\u8fbc\u307f
118 dlg.savebugs_ttl=\u30d0\u30b0\u306e\u4fdd\u5b58
119 dlg.rerunAnalysis=\u5206\u6790\u306e\u518d\u5b9f\u884c
120
121 msg.scanningarchives_txt=\u30a2\u30fc\u30ab\u30a4\u30d6\u3092\u30b9\u30ad\u30e3\u30f3\u4e2d
122 msg.analysingclasses_txt=\u30af\u30e9\u30b9\u3092\u5206\u6790\u4e2d
123 msg.finishedanalysis_txt=\u5206\u6790\u7d42\u4e86
124 msg.cancelanalysis_txt=\u5206\u6790\u3092\u30ad\u30e3\u30f3\u30bb\u30eb\u3057\u307e\u3059\u304b?
125 msg.analyze_txt=\u5206\u6790
126 msg.couldntload_txt={0} \u304c\u8aad\u307f\u8fbc\u3081\u307e\u305b\u3093
127 msg.failedtotransform_txt=\u30b5\u30de\u30ea\u30fc\u306e\u5909\u63db\u306b\u5931\u6557: {0}
128 msg.error_txt=\u30a8\u30e9\u30fc:
129 msg.warning_txt=\u8b66\u544a:
130 msg.exp_txt=\u5b9f\u9a13\u6bb5\u968e:
131 msg.errorformatting_txt=\u30d0\u30b0\u306b\u5bfe\u3059\u308b\u30e1\u30c3\u30bb\u30fc\u30b8\u306e\u30d5\u30a9\u30fc\u30de\u30c3\u30c8\u51e6\u7406\u3067\u30a8\u30e9\u30fc: {0}
132 msg.couldnotopenproject_txt=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u304c\u958b\u3051\u306a\u304b\u3063\u305f: {0}
133 msg.saveproject_txt=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306e\u4fdd\u5b58
134 msg.saveprojectas_txt=\u540d\u524d\u3092\u4ed8\u3051\u3066\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u4fdd\u5b58
135 msg.saveprojectquery_txt=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u4fdd\u5b58\u3057\u307e\u3059\u304b?
136 msg.projectnojars_txt=Project {0} \u306f\u30a2\u30fc\u30ab\u30a4\u30d6\u30d5\u30a1\u30a4\u30eb\u304c\u672a\u9078\u629e\u3067\u3059
137 msg.beginninganalysis_txt={0} \u306e\u5206\u6790\u3092\u958b\u59cb\u3057\u307e\u3059
138 msg.analysiscompleted_txt={0} \u306e\u5206\u6790\u304c\u5b8c\u4e86\u3057\u307e\u3057\u305f
139 msg.fatalanalysisexception_txt=\u5206\u6790\u306b\u304a\u3044\u3066\u81f4\u547d\u7684\u306a\u4f8b\u5916\u304c\u767a\u751f: {0}
140 msg.analysiscancelled_txt={0} \u306e\u5206\u6790\u306f\u30e6\u30fc\u30b6\u30fc\u306b\u3088\u308a\u30ad\u30e3\u30f3\u30bb\u30eb\u3055\u308c\u307e\u3057\u305f
141 msg_addsource_lbl=\u30bd\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u307e\u305f\u306f\u30a2\u30fc\u30ab\u30a4\u30d6\u3092\u8ffd\u52a0
142 msg.addarchiveordirectory_txt=\u30a2\u30fc\u30ab\u30a4\u30d6\u307e\u305f\u306f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u8ffd\u52a0
143 msg.newproject_txt=\u65b0\u898f\u30d7\u30ed\u30b8\u30a7\u30af\u30c8
144 msg.openproject_txt=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u958b\u304f...
145 msg.addRemoveFiles=\u30d5\u30a1\u30a4\u30eb\u306e\u8ffd\u52a0/\u524a\u9664...
146 msg.userelativepaths_txt=\u76f8\u5bfe\u30d1\u30b9\u3092\u4f7f\u7528
147 msg.bugpatternsreported_txt=\u5831\u544a\u3055\u308c\u305f\u30d0\u30b0\u30d1\u30bf\u30fc\u30f3
148
149 msg.nosource_txt=\u30bd\u30fc\u30b9\u304c\u4f7f\u7528\u3067\u304d\u307e\u305b\u3093
150 msg.loading_bugs_over_network_txt=\u30cd\u30c3\u30c8\u30ef\u30fc\u30af\u4e0a\u304b\u3089\u30d0\u30b0\u3092\u8aad\u307f\u8fbc\u3080...
151
152 # The following three properties are not needed any more.
153 msg.you_are_loading_txt=\u8aad\u307f\u8fbc\u307f\u4e2d\u3067\u3059
154 msg.you_are_closing_txt=You are closing
155 msg.without_saving_txt=without saving. \u4fdd\u5b58\u3057\u307e\u3059\u304b?
156 # instead, the following two are used
157 msg.you_are_loading_without_saving_txt=\u4fdd\u5b58\u305b\u305a\u306b\u8aad\u307f\u8fbc\u307f\u3092\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307e\u3059\u3002\u4fdd\u5b58\u3057\u307e\u3059\u304b?
158 msg.you_are_closing_without_saving_txt=\u4fdd\u5b58\u305b\u305a\u306b\u9589\u3058\u3088\u3046\u3068\u3057\u3066\u3044\u307e\u3059\u3002\u4fdd\u5b58\u3057\u307e\u3059\u304b?
159
160 msg.confirm_save_txt=\u4fdd\u5b58\u3057\u307e\u3059\u304b?
161 dlg.create_new_filter_ttl=\u30d5\u30a3\u30eb\u30bf\u30fc\u306e\u65b0\u898f\u4f5c\u6210
162 dlg.changing_text_lbl=\u3053\u306e\u30c6\u30ad\u30b9\u30c8\u30fb\u30dc\u30c3\u30af\u30b9\u3092\u5909\u66f4\u3059\u308b\u3068\u3001\u9078\u629e\u3055\u308c\u305f\u30d5\u30a9\u30eb\u30c0\u30fc\u304a\u3088\u3073\u30b5\u30d6\u30d5\u30a9\u30eb\u30c0\u30fc\u306b\u3042\u308b\u3059\u3079\u3066\u306e\u30d0\u30b0\u306e\u6ce8\u91c8\u304c\u4e0a\u66f8\u304d\u3055\u308c\u307e\u3059\u3002\u3088\u308d\u3057\u3044\u3067\u3059\u304b?
163 dlg.annotation_change_ttl=\u6ce8\u91c8\u306e\u5909\u66f4
164 dlg.yes_btn=\u306f\u3044
165 dlg.no_btn=\u3044\u3044\u3048
166 dlg.find_btn=\u691c\u7d22
167 dlg.find_next_btn=\u6b21\u3092\u691c\u7d22
168 dlg.find_prev_btn=\u524d\u3092\u691c\u7d22
169 dlg.save_dont_ask_btn=\u306f\u3044\u3002\u307e\u305f\u3001\u4eca\u5f8c\u3053\u306e\u78ba\u8a8d\u306f\u5fc5\u8981\u3042\u308a\u307e\u305b\u3093\u3002
170 dlg.proj_not_found=\u3053\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306f\u3082\u306f\u3084\u5b58\u5728\u3057\u307e\u305b\u3093\u3002
171 dlg.save_current_changes=\u73fe\u5728\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306f\u5909\u66f4\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u73fe\u5728\u306e\u5909\u66f4\u3092\u4fdd\u5b58\u3057\u307e\u3059\u304b?
172 dlg.save_changes=\u4fdd\u5b58\u3057\u307e\u3059\u304b?
173 dlg.go_to_line_lbl=\u6307\u5b9a\u884c\u3078\u79fb\u52d5
174 dlg.no_proj_save_lbl=\u4fdd\u5b58\u3059\u308b\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306f\u5b58\u5728\u3057\u307e\u305b\u3093
175 dlg.saveas_ttl=\u540d\u524d\u3092\u4ed8\u3051\u3066\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u3092\u4fdd\u5b58
176 dlg.proj_already_exists_lbl=\u3053\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u7f6e\u304d\u63db\u3048\u307e\u3059\u304b?
177 dlg.analysis_exists_lbl=\u3053\u306e\u30d0\u30b0\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u7f6e\u304d\u63db\u3048\u307e\u3059\u304b?
178 dlg.file_does_not_exist_lbl=\u30d5\u30a1\u30a4\u30eb\u304c\u5b58\u5728\u3057\u307e\u305b\u3093
179 dlg.not_dir_warning_lbl=\u8b66\u544a!\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3067\u306f\u3042\u308a\u307e\u305b\u3093
180 dlg.proj_not_dir_warning_lbl=\u8b66\u544a!\u3053\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306f\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3067\u306f\u3042\u308a\u307e\u305b\u3093
181 dlg.no_xml_data_lbl=\u3053\u306e\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u306f\u30d0\u30b0\u4fdd\u5b58XML\u3092\u542b\u3093\u3067\u3044\u307e\u305b\u3093\u3002\u5225\u306e\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc\u3092\u9078\u629e\u3057\u3066\u304f\u3060\u3055\u3044\u3002
182 dlg.not_xml_data_lbl=\u3053\u308c\u306f\u30d0\u30b0\u4fdd\u5b58XML\u3067\u306f\u3042\u308a\u307e\u305b\u3093\u3002
183 dlg.filter_settings_not_found_lbl=\u30d5\u30a3\u30eb\u30bf\u30fc\u8a2d\u5b9a\u304c\u307f\u3064\u304b\u308a\u307e\u305b\u3093\u3002\u30c7\u30d5\u30a9\u30eb\u30c8\u8a2d\u5b9a\u3092\u4f7f\u7528\u3057\u307e\u3059\u3002
184 dlg.saving_error_lbl=\u4fdd\u5b58\u4e2d\u306b\u30a8\u30e9\u30fc\u304c\u767a\u751f
185 dlg.warning_ttl=\u8b66\u544a!
186 dlg.finish_btn=\u7d42\u4e86
187 dlg.class_jars_dirs_lbl=\u5206\u6790\u3059\u308b\u30a2\u30fc\u30ab\u30a4\u30d6\u304a\u3088\u3073\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc
188 dlg.aux_class_lbl=\u88dc\u52a9\u30af\u30e9\u30b9\u30d1\u30b9(\u53c2\u7167\u3055\u308c\u308b\u304c\u5206\u6790\u306e\u5bfe\u8c61\u5916)
189 dlg.source_dirs_lbl=\u30bd\u30fc\u30b9\u30c7\u30a3\u30ec\u30af\u30c8\u30ea\u30fc:
190 dlg.invalid_txt= \u307f\u3064\u304b\u308a\u307e\u305b\u3093\u3002\u7d9a\u884c\u3057\u3066\u3044\u3044\u3067\u3059\u304b?
191 dlg.error_ttl=\u30d5\u30a1\u30a4\u30eb\u3092\u307f\u3064\u3051\u3089\u308c\u307e\u305b\u3093
192 dlg.project_settings_changed_lbl=\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u8a2d\u5b9a\u304c\u5909\u66f4\u3055\u308c\u3066\u3044\u307e\u3059\u3002\u5909\u66f4\u3055\u308c\u305f\u30d5\u30a1\u30a4\u30eb\u3067\u65b0\u305f\u306a\u5206\u6790\u3092\u5b9f\u884c\u3057\u307e\u3059\u304b?
193 dlg.redo_analysis_question_lbl=\u5206\u6790\u306e\u518d\u5b9f\u884c?
194 dlg.apply_btn=\u9069\u7528
195 dlg.unsuppress_btn=\u6291\u6b62\u89e3\u9664
196 dlg.remove_all_btn=\u3059\u3079\u3066\u524a\u9664
197 dlg.add_dot_btn=\u8ffd\u52a0...
198 dlg.fil_sup_ttl=\u30d5\u30a3\u30eb\u30bf\u30fc/\u6291\u6b62
199 dlg.choose_xmls_ttl=\u7d50\u5408\u3059\u308bXML\u3092\u3059\u3079\u3066\u9078\u629e
200 dlg.filter_bugs_lbl=\u6b21\u306e\u6761\u4ef6\u306e\u30d0\u30b0\u3092\u30d5\u30a3\u30eb\u30bf\u30fc\u3067\u9664\u53bb\u3059\u308b
201 dlg.is=\u304c
202 button.find=\u691c\u7d22
203 button.findNext=\u6b21\u3092\u691c\u7d22
204 button.findPrev=\u524d\u3092\u691c\u7d22
205 tree.bugs=\u30d0\u30b0
206 view.bugs=\u30d0\u30b0
207 view.bug_summary=\u30d0\u30b0\u30b5\u30de\u30ea\u30fc
208 view.comments=\u30b3\u30e1\u30f3\u30c8
209 view.source=\u30bd\u30fc\u30b9
210 mode.not_equal_to=\u7b49\u3057\u304f\u306a\u3044
211 mode.equal_to=\u7b49\u3057\u3044
212 mode.at_or_after=\u4ee5\u964d
213 mode.at_or_before=\u4ee5\u524d
214 menu.addRemoveFiles=\u30d5\u30a1\u30a4\u30eb\u306e\u8ffd\u52a0/\u524a\u9664...
215 menu.suppress_bug=\u3053\u306e\u30d0\u30b0\u3092\u6291\u6b62
216 menu.filter_bugs=\u30d5\u30a3\u30eb\u30bf\u30fc\u3092\u8a2d\u5b9a
217 menu.filter_these_bugs=\u30d5\u30a3\u30eb\u30bf\u30ea\u30f3\u30b0\u3059\u308b
218 menu.change_designation=\u30d0\u30b0\u5206\u985e\u6307\u5b9a\u306e\u5909\u66f4
219 menu.go_to_line=\u6307\u5b9a\u884c\u3078\u79fb\u52d5...
220 menu.about_fb=FindBugs \u306b\u3064\u3044\u3066...
221 menu.recent_projects_menu=\u6700\u8fd1\u4f7f\u7528\u3057\u305f\u30d7\u30ed\u30b8\u30a7\u30af\u30c8
222 tooltip.reorder_message=\u30c9\u30e9\u30c3\u30b0\u3059\u308b\u3068\u30d5\u30a9\u30eb\u30c0\u30fc\u306e\u30c4\u30ea\u30fc\u69cb\u6210\u3092\u518d\u914d\u7f6e\u3057\u3066\u30bd\u30fc\u30c8\u3057\u307e\u3059
223 tooltip.longer_description=\u691c\u51fa\u3055\u308c\u305f\u30d0\u30b0\u30d1\u30bf\u30fc\u30f3\u306e\u8a73\u7d30\u306a\u8aac\u660e \u3067\u3059
224 tooltip.click_to_go_to=\u30af\u30ea\u30c3\u30af\u3059\u308b\u3068\u79fb\u52d5\u3057\u307e\u3059\u3002\u79fb\u52d5\u5148
225 tooltip.enter_comments=\u3053\u306e\u30d0\u30b0\u306b\u5bfe\u3059\u308b\u30b3\u30e1\u30f3\u30c8\u3092\u5165\u529b\u3057\u3066\u304f\u3060\u3055\u3044
226 tooltip.reuse_comments=\u3053\u306e\u30d0\u30b0\u306b\u5bfe\u3059\u308b\u4ee5\u524d\u306e\u30b3\u30e1\u30f3\u30c8\u3092\u518d\u4f7f\u7528\u3057\u307e\u3059
227 tooltip.select_designation=\u3053\u306e\u30d0\u30b0\u306b\u5bfe\u3059\u308b\u30d0\u30b0\u5206\u985e\u6307\u5b9a\u3092\u9078\u629e\u3057\u307e\u3059
228 summary.null=null
229 summary.source_code=\u30bd\u30fc\u30b9\u30b3\u30fc\u30c9
230 summary_line=\u884c
231 summary_lines=\u884c
232 sort.priority_high=\u512a\u5148\u5ea6(\u9ad8)
233 sort.priority_normal=\u512a\u5148\u5ea6(\u4e2d)
234 sort.priority_low=\u512a\u5148\u5ea6(\u4f4e)
235 sort.priority_experimental=\u512a\u5148\u5ea6(\u5b9f\u9a13\u6bb5\u968e)
236 sort.priority_ignore=\u7121\u8996
237 sort.priority=\u512a\u5148\u5ea6
238 sort.class=\u30af\u30e9\u30b9
239 sort.package=\u30d1\u30c3\u30b1\u30fc\u30b8
240 sort.category=\u30ab\u30c6\u30b4\u30ea\u30fc
241 sort.designation=\u30d0\u30b0\u5206\u985e\u6307\u5b9a
242 sort.bug_kind=\u30d0\u30b0\u7a2e\u5225
243 sort.bug_pattern=\u30d0\u30b0\u30d1\u30bf\u30fc\u30f3
244 sort.divider=[\u4ed5\u5207\u308a]
245 sort.first_version=\u521d\u56de\u30d0\u30fc\u30b8\u30e7\u30f3
246 sort.last_version=\u6700\u7d42\u30d0\u30fc\u30b8\u30e7\u30f3
247 sort.first_version_not_defined=\u521d\u56de\u30d0\u30fc\u30b8\u30e7\u30f3\u304c\u672a\u5b9a\u7fa9
248 sort.last_version_not_defined=\u6700\u7d42\u30d0\u30fc\u30b8\u30e7\u30f3\u304c\u672a\u5b9a\u7fa9
249 txt.source_listing=<\u30bd\u30fc\u30b9\u30ea\u30b9\u30c8>
250 txt.source=\u30bd\u30fc\u30b9
251 txt.cut=\u5207\u308a\u53d6\u308a
252 txt.copy=\u30b3\u30d4\u30fc
253 txt.paste=\u8cbc\u308a\u4ed8\u3051
254 file.accepted_extensions=\u30af\u30e9\u30b9\u30a2\u30fc\u30ab\u30a4\u30d6\u30d5\u30a1\u30a4\u30eb (*.jar, *.war, *.ear, *.zip, *.sar)
255 pref.filters=\u30d5\u30a3\u30eb\u30bf\u30fc
256 pref.close=\u9589\u3058\u308b
257 pref.name=\u540d\u524d
258 pref.type=\u30bf\u30a4\u30d7
259 pref.description=\u8aac\u660e
260 pref.comments=\u30b3\u30e1\u30f3\u30c8
261 pref.suppressions=\u30d0\u30b0\u6291\u6b62
262 pref.suppressions_tab=\u6291\u6b62
263 progress.finishing_analysis=\u5206\u6790\u3092\u7d42\u4e86\u4e2d...
264 progress.analyzing_classes=\u30af\u30e9\u30b9\u3092\u5206\u6790\u4e2d...
265 progress.scanning_archives=\u30a2\u30fc\u30ab\u30a4\u30d6\u3092\u30b9\u30ad\u30e3\u30f3\u4e2d...
266 err.missing_pattern=\u30a8\u30e9\u30fc: \u30ad\u30fc\u306b\u5bfe\u3059\u308b\u30d0\u30b0\u30d1\u30bf\u30fc\u30f3\u304c\u6b20\u843d
267 err.missing_code=\u30a8\u30e9\u30fc: \u30ad\u30fc\u306b\u5bfe\u3059\u308b\u30d0\u30b0\u30b3\u30fc\u30c9\u304c\u6b20\u843d
268 statusbar.bug_hidden=\u96a0\u3055\u308c\u305f\u30d0\u30b0
269 statusbar.bugs_hidden=\u96a0\u3055\u308c\u305f\u30d0\u30b0
270
271 menu.addRemoveFiles=\u30d5\u30a1\u30a4\u30eb\u306e\u8ffd\u52a0/\u524a\u9664...
272 summary.line=\u884c
273 summary.lines=\u884c
274
275
276 # missing in findbugs_en.properties
277 menu.recent=\u6700\u8fd1\u4f7f\u3063\u305f\u30d5\u30a1\u30a4\u30eb
278 menu.importFilter_item=\u30d5\u30a3\u30eb\u30bf\u30fc\u3092\u30a4\u30f3\u30dd\u30fc\u30c8...
279 menu.exportFilter_item=\u30d5\u30a3\u30eb\u30bf\u30fc\u306e\u30a8\u30af\u30b9\u30dd\u30fc\u30c8
280 pref.dead_bugs=Dead Bugs
281 msg.proj_not_found=\u3053\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306f\u3082\u306f\u3084\u5b58\u5728\u3057\u307e\u305b\u3093
282 dlg.no_filter=\u30d5\u30a3\u30eb\u30bf\u30fc\u304c\u3042\u308a\u307e\u305b\u3093
283 dlg.importFilter_ttl=\u30d5\u30a3\u30eb\u30bf\u30fc\u306e\u30a4\u30f3\u30dd\u30fc\u30c8\u304a\u3088\u3073\u30de\u30fc\u30b8
284 dlg.exportFilter_ttl=\u30d5\u30a3\u30eb\u30bf\u30fc\u306e\u30a8\u30af\u30b9\u30dd\u30fc\u30c8
285 dlg.file_exists_lbl=\u3053\u306e\u30d5\u30a1\u30a4\u30eb\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u7f6e\u304d\u63db\u3048\u307e\u3059\u304b?
286 FB\ Project\ File\ already\ exists=\u3053\u306e\u30d7\u30ed\u30b8\u30a7\u30af\u30c8\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u7f6e\u304d\u63db\u3048\u307e\u3059\u304b?
287 FB\ Analysis\ File\ already\ exists=\u3053\u306e\u30d0\u30b0\u306f\u3059\u3067\u306b\u5b58\u5728\u3057\u307e\u3059\u3002\u7f6e\u304d\u63db\u3048\u307e\u3059\u304b?
0 #Generated by ResourceBundle Editor (http://essiembre.github.io/eclipse-rbe/)
1 # Japanese translation provided by Shisei Hanai
2
3 button.find = \u691C\u7D22
4 button.findNext = \u6B21\u3092\u691C\u7D22
5 button.findPrev = \u524D\u3092\u691C\u7D22
6
7 dlg.about_tab = FindBug \u306B\u3064\u3044\u3066
8 dlg.aboutfindbugs_ttl = FindBugs {0} \u306B\u3064\u3044\u3066
9 dlg.acknowledgements_tab = \u8B1D\u8F9E
10 dlg.add_btn = \u8FFD\u52A0
11 dlg.add_dot_btn = \u8FFD\u52A0...
12 dlg.analysis_exists_lbl = \u3053\u306E\u5206\u6790\u306F\u3059\u3067\u306B\u5B58\u5728\u3057\u307E\u3059\u3002\u7F6E\u304D\u63DB\u3048\u307E\u3059\u304B?
13 dlg.analysiserror_lbl = \u5206\u6790\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u305F:
14 dlg.analysiserrors_ttl = \u5206\u6790\u30A8\u30E9\u30FC
15 dlg.analyze_btn = \u5206\u6790
16 dlg.annotation_change_ttl = \u3088\u308D\u3057\u3044\u3067\u3059\u304B?
17 dlg.annotations_tab = \u6CE8\u91C8
18 dlg.apply_btn = \u9069\u7528
19 dlg.aux_class_lbl = \u88DC\u52A9\u30AF\u30E9\u30B9\u30D1\u30B9 (\u30AA\u30D7\u30B7\u30E7\u30CA\u30EB; \u30AF\u30E9\u30B9\u30D1\u30B9\u5206\u6790\u3067\u53C2\u7167\u3055\u308C\u308B)
20 dlg.browse_btn = \u30D6\u30E9\u30A6\u30BA
21 dlg.bugdetector_lbl = \u30D0\u30B0\u30FB\u30C7\u30A3\u30C6\u30AF\u30BF\u30FC
22 dlg.bybugcategory_tab = \u30D0\u30B0\u30FB\u30AB\u30C6\u30B4\u30EA\u30FC\u5225
23 dlg.bybugtype_tab = \u30D0\u30B0\u30FB\u30BF\u30A4\u30D7\u5225
24 dlg.byclass_tab = \u30AF\u30E9\u30B9\u5225
25 dlg.bypackage_tab = \u30D1\u30C3\u30B1\u30FC\u30B8\u5225
26 dlg.cancel_btn = \u30AD\u30E3\u30F3\u30BB\u30EB
27 dlg.changing_text_lbl = \u3053\u306E\u30D5\u30A9\u30EB\u30C0\u30FC\u304A\u3088\u3073\u30B5\u30D6\u30D5\u30A9\u30EB\u30C0\u30FC\u306B\u3042\u308B\u3059\u3079\u3066\u306E\u30D0\u30B0\u306E\u30EC\u30D3\u30E5\u30FC\u304C\u4E0A\u66F8\u304D\u3055\u308C\u307E\u3059\u3002\u3088\u308D\u3057\u3044\u3067\u3059\u304B?
28 dlg.choose_xmls_ttl = \u7D50\u5408\u3059\u308B XML \u3092\u3059\u3079\u3066\u9078\u629E
29 dlg.class_jars_dirs_lbl = \u5206\u6790\u30AF\u30E9\u30B9\u30D1\u30B9 (\u30A2\u30FC\u30AB\u30A4\u30D6\u304A\u3088\u3073\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30FC)
30 dlg.classpathfile_lbl = \u30AF\u30E9\u30B9\u30D1\u30B9\u30FB\u30A8\u30F3\u30C8\u30EA\u30FC:
31 dlg.classpathlist_lbl = \u30AF\u30E9\u30B9\u30D1\u30B9\u30FB\u30A8\u30F3\u30C8\u30EA\u30FC:
32 dlg.closeproject_lbl = \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u9589\u3058\u308B
33 dlg.cloud.add_review = \u30EC\u30D3\u30E5\u30FC\u3092\u8FFD\u52A0...
34 dlg.cloud.add_review_multi = \u30D0\u30B0\u306B\u30EC\u30D3\u30E5\u30FC\u3092\u8FFD\u52A0...
35 dlg.cloud.ovwrt_review_multi = \u30EC\u30D3\u30E5\u30FC\u3092\u4E0A\u66F8\u304D\u3059\u308B...
36 dlg.configuredetectors_ttl = \u30C7\u30A3\u30C6\u30AF\u30BF\u30FC\u306E\u8A2D\u5B9A
37 dlg.count_lbl = \u30AB\u30A6\u30F3\u30C8:
38 dlg.create_new_filter_ttl = \u65B0\u898F\u30D5\u30A3\u30EB\u30BF\u30FC\u306E\u4F5C\u6210
39 dlg.details_tab = \u8A73\u7D30
40 dlg.dontsave_btn = \u4FDD\u5B58\u3057\u306A\u3044
41 dlg.down_btn = \u4E0B\u3078
42 dlg.enabled_lbl = \u4F7F\u7528\u53EF\u80FD
43 dlg.error_ttl = \u30D5\u30A1\u30A4\u30EB\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093
44 dlg.fil_sup_ttl = \u8A2D\u5B9A
45 dlg.file_does_not_exist_lbl = \u305D\u306E\u30D5\u30A1\u30A4\u30EB\u306F\u5B58\u5728\u3057\u307E\u305B\u3093
46 dlg.filter_bugs_lbl = \u3059\u3079\u3066\u306E\u30D0\u30B0\u3092\u30D5\u30A3\u30EB\u30BF\u30FC\u3059\u308B
47 dlg.filter_settings_not_found_lbl = \u30D5\u30A3\u30EB\u30BF\u30FC\u8A2D\u5B9A\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u30C7\u30D5\u30A9\u30EB\u30C8\u8A2D\u5B9A\u3092\u4F7F\u7528\u3057\u307E\u3059\u3002
48 dlg.find_btn = \u691C\u7D22
49 dlg.find_next_btn = \u6B21\u3092\u691C\u7D22
50 dlg.find_prev_btn = \u524D\u3092\u691C\u7D22
51 dlg.findbugsprojects_lbl = FindBugs \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8 (*.fb)
52 dlg.go_to_line_lbl = \u6307\u5B9A\u884C\u3078\u79FB\u52D5
53 dlg.invalid_txt = \u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002\u7D9A\u884C\u3057\u307E\u3059\u304B?
54 dlg.is = \u304C
55 dlg.jarfile_lbl = \u30A2\u30FC\u30AB\u30A4\u30D6\u307E\u305F\u306F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30FC
56 dlg.jarlist_lbl = \u30A2\u30FC\u30AB\u30A4\u30D6/\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30FC:
57 dlg.jarsanddirectories_lbl = \u30A2\u30FC\u30AB\u30A4\u30D6\u30FB\u30D5\u30A1\u30A4\u30EB\u304A\u3088\u3073\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30FC
58 dlg.javaarchives_lbl = Java \u30A2\u30FC\u30AB\u30A4\u30D6 (*.class,*.jar,*.zip,*.ear,*.war,*.sar)
59 dlg.license_tab = \u30E9\u30A4\u30BB\u30F3\u30B9
60 dlg.loadbugs_ttl = \u30D0\u30B0\u306E\u30ED\u30FC\u30C9...
61 dlg.new_item = \u65B0\u898F\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8
62 dlg.no_btn = \u3044\u3044\u3048
63 dlg.no_proj_save_lbl = \u4FDD\u5B58\u3059\u308B\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u304C\u3042\u308A\u307E\u305B\u3093
64 dlg.no_xml_data_lbl = \u3053\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30FC\u306F\u30D0\u30B0\u4FDD\u5B58 XML \u30C7\u30FC\u30BF\u3092\u542B\u3093\u3067\u3044\u307E\u305B\u3093\u3002\u5225\u306E\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30FC\u3092\u9078\u629E\u3057\u3066\u304F\u3060\u3055\u3044\u3002
65 dlg.noproject_lbl = FindBugs - \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u304C\u3042\u308A\u307E\u305B\u3093
66 dlg.not_dir_warning_lbl = \u8B66\u544A! \u30D5\u30A1\u30A4\u30EB\u306F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30FC\u3067\u306F\u3042\u308A\u307E\u305B\u3093
67 dlg.not_xml_data_lbl = \u3053\u308C\u306F\u30D0\u30B0\u4FDD\u5B58 XML \u30C7\u30FC\u30BF\u30FB\u30D5\u30A1\u30A4\u30EB\u3067\u306F\u3042\u308A\u307E\u305B\u3093\u3002
68 dlg.ok_btn = OK
69 dlg.progress_lbl = \u9032\u884C\u72B6\u6CC1:
70 dlg.proj_already_exists_lbl = \u3053\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306F\u3059\u3067\u306B\u5B58\u5728\u3057\u307E\u3059\u3002\u7F6E\u304D\u63DB\u3048\u307E\u3059\u304B?
71 dlg.proj_not_dir_warning_lbl = \u8B66\u544A! \u3053\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30FC\u3067\u306F\u3042\u308A\u307E\u305B\u3093
72 dlg.proj_not_found = \u3053\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306F\u3082\u3046\u3042\u308A\u307E\u305B\u3093\u3002
73 dlg.project_lbl = \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8
74 dlg.project_settings_changed_lbl = \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u8A2D\u5B9A\u304C\u5909\u66F4\u3055\u308C\u3066\u3044\u307E\u3059\u3002\u5909\u66F4\u3055\u308C\u305F\u30D5\u30A1\u30A4\u30EB\u3067\u65B0\u305F\u306A\u5206\u6790\u3092\u5B9F\u884C\u3057\u307E\u3059\u304B?
75 dlg.reconfig = \u518D\u69CB\u6210
76 dlg.redo_analysis_question_lbl = \u5206\u6790\u306E\u3084\u308A\u76F4\u3057?
77 dlg.remove_all_btn = \u3059\u3079\u3066\u9664\u53BB
78 dlg.remove_btn = \u9664\u53BB
79 dlg.rerunAnalysis = \u5206\u6790\u306E\u518D\u5B9F\u884C
80 dlg.restoredefaults_btn = \u30C7\u30D5\u30A9\u30EB\u30C8\u306E\u5FA9\u5143
81 dlg.runanalysis_ttl = \u5206\u6790\u306E\u5B9F\u884C
82 dlg.save_btn = \u4FDD\u5B58
83 dlg.save_changes = \u5909\u66F4\u3092\u4FDD\u5B58\u3057\u307E\u3059\u304B?
84 dlg.save_current_changes = \u73FE\u5728\u306E\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306F\u5909\u66F4\u3055\u308C\u3066\u3044\u307E\u3059\u3002\u73FE\u5728\u306E\u5909\u66F4\u3092\u4FDD\u5B58\u3057\u307E\u3059\u304B?
85 dlg.save_dont_ask_btn = \u5E38\u306B\u4FDD\u5B58
86 dlg.saveas_ttl = \u5225\u540D\u3067\u4FDD\u5B58
87 dlg.savebugs_ttl = \u30D0\u30B0\u306E\u4FDD\u5B58
88 dlg.saved_btn = \u4FDD\u5B58
89 dlg.saveprojectas_ttl = \u5225\u540D\u3067\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u4FDD\u5B58
90 dlg.saving_error_lbl = \u4FDD\u5B58\u4E2D\u306B\u30A8\u30E9\u30FC\u304C\u767A\u751F\u3057\u305F
91 dlg.source_dirs_lbl = \u30BD\u30FC\u30B9\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30FC (\u30AA\u30D7\u30B7\u30E7\u30CA\u30EB: \u767A\u898B\u3057\u305F\u30D0\u30B0\u3092\u30D6\u30E9\u30A6\u30BA\u3059\u308B\u3068\u304D\u306B\u4F7F\u308F\u308C\u308B)
92 dlg.sourcecode_tab = \u30BD\u30FC\u30B9\u30FB\u30B3\u30FC\u30C9
93 dlg.speed_lbl = \u901F\u5EA6
94 dlg.srcfile_lbl = \u30BD\u30FC\u30B9\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30FC:
95 dlg.srclist_lbl = \u30BD\u30FC\u30B9\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30FC:
96 dlg.stage_lbl = \u6BB5\u968E:
97 dlg.summary_tab = \u8981\u7D04
98 dlg.unnamed_lbl = <<\u7121\u540D\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8>>
99 dlg.unsuppress_btn = \u6291\u6B62\u89E3\u9664
100 dlg.up_btn = \u4E0A\u3078
101 dlg.warning_ttl = \u8B66\u544A!
102 dlg.xmlsavedbugs_lbl = \u30D0\u30B0\u4FDD\u5B58 XML \u30D5\u30A1\u30A4\u30EB
103 dlg.yes_btn = \u306F\u3044
104
105 err.missing_code = \u30A8\u30E9\u30FC: \u30AD\u30FC\u306B\u5BFE\u3059\u308B\u30D0\u30B0\u30FB\u30B3\u30FC\u30C9\u304C\u6B20\u843D
106 err.missing_pattern = \u30A8\u30E9\u30FC: \u30AD\u30FC\u306B\u5BFE\u3059\u308B\u30D0\u30B0\u30FB\u30D1\u30BF\u30FC\u30F3\u304C\u6B20\u843D
107
108 file.accepted_extensions = \u30AF\u30E9\u30B9\u30FB\u30A2\u30FC\u30AB\u30A4\u30D6\u30FB\u30D5\u30A1\u30A4\u30EB (*.jar, *.war, *.ear, *.zip, *.sar)
109
110 menu.about_fb = FindBugs \u306B\u3064\u3044\u3066...
111 menu.about_item = FindBug\u306B\u3064\u3044\u3066(&A)...
112 menu.addRemoveFiles = \u30D5\u30A1\u30A4\u30EB\u306E\u8FFD\u52A0/\u9664\u53BB...
113 menu.bugdetails_item = \u30D0\u30B0\u306E\u8A73\u7D30(&D)
114 menu.changeDesignation = \u6307\u5B9A\u306E\u5909\u66F4
115 menu.change_designation = \u30D0\u30B0\u540D\u79F0\u306E\u5909\u66F4
116 menu.check_for_updates = \u66F4\u65B0\u306E\u78BA\u8A8D(&U)...
117 menu.closeProject = \u9589\u3058\u308B(&C)
118 menu.close_item = \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u9589\u3058\u308B(&C)
119 menu.collapse = \u7E2E\u5C0F
120 menu.configure_item = \u30C7\u30A3\u30C6\u30AF\u30BF\u30FC\u306E\u69CB\u6210(&C)
121 menu.console_item = \u30B3\u30F3\u30BD\u30FC\u30EB(&C)
122 menu.designation = \u540D\u79F0(&D)
123 menu.down = \u4E0B\u3078
124 menu.edit_menu = \u7DE8\u96C6(&E)
125 menu.effort_menu = \u8A66\u884C
126 menu.empty_item = \u7A7A
127 menu.exit = \u7D42\u4E86
128 menu.exit_item = \u7D42\u4E86(&X)
129 menu.expand = \u5C55\u958B
130 menu.exppriority_item = \u512A\u5148\u5EA6 (\u5B9F\u9A13\u7684)
131 menu.file_menu = \u30D5\u30A1\u30A4\u30EB(&F)
132 menu.filterBugsLikeThis = \u3053\u306E\u3088\u3046\u306A\u30D0\u30B0\u3092\u30D5\u30A3\u30EB\u30BF\u30FC\u3059\u308B...
133 menu.filterTheseBugs = \u30D0\u30B0\u3092\u30D5\u30A3\u30EB\u30BF\u30FC\u3059\u308B
134 menu.filter_bugs = \u3053\u306E\u3088\u3046\u306A\u30D0\u30B0\u3092\u30D5\u30A3\u30EB\u30BF\u30FC\u3059\u308B
135 menu.filter_these_bugs = \u30D0\u30B0\u3092\u30D5\u30A3\u30EB\u30BF\u30FC\u3059\u308B
136 menu.filtersAndSupressions = \u30D5\u30A3\u30EB\u30BF\u30FC/\u6291\u6B62
137 menu.filterwarnings_menu = \u8B66\u544A\u3092\u30D5\u30A3\u30EB\u30BF\u30FC\u3059\u308B
138 menu.fulldescriptions_item = \u5B8C\u5168\u306A\u8AAC\u660E(&F)
139 menu.go_to_line = \u6307\u5B9A\u884C\u3078\u79FB\u52D5...
140 menu.gotoLine = \u6307\u5B9A\u884C\u3078\u79FB\u52D5...
141 menu.help_menu = \u30D8\u30EB\u30D7(&H)
142 menu.highpriority_item = \u512A\u5148\u5EA6 (\u9AD8)
143 menu.loadbugs_item = \u5206\u6790\u306E\u30ED\u30FC\u30C9(&L)...
144 menu.lowpriority_item = \u512A\u5148\u5EA6 (\u4F4E)
145 menu.maxeffort_item = \u6700\u5927
146 menu.mediumpriority_item = \u512A\u5148\u5EA6 (\u4E2D)
147 menu.mergeAnalysis = \u5206\u6790\u3092\u30DE\u30FC\u30B8...
148 menu.mineffort_item = \u6700\u5C0F
149 menu.navigation = \u30CA\u30D3\u30B2\u30FC\u30B7\u30E7\u30F3(&N)
150 menu.new_item = \u65B0\u898F\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8(&N)...
151 menu.normaleffort_item = \u6A19\u6E96
152 menu.open_item = \u958B\u304F(&O)...
153 menu.preferences_menu = \u8A2D\u5B9A(&P)...
154 menu.recent_menu = \u6700\u8FD1\u4F7F\u7528\u3057\u305F\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8(&E)
155 menu.recent_projects_menu = \u6700\u8FD1\u4F7F\u7528\u3057\u305F\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8
156 menu.reconfig = \u518D\u69CB\u6210...
157 menu.reload_item = \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E\u518D\u30ED\u30FC\u30C9(&R)
158 menu.rerunAnalysis = \u5206\u6790\u306E\u3084\u308A\u76F4\u3057
159 menu.save_item = \u4FDD\u5B58(&S)
160 menu.saveas_item = \u5225\u540D\u3067\u4FDD\u5B58(&A)...
161 menu.savebugs_item = \u5206\u6790\u306E\u4FDD\u5B58(&A)...
162 menu.selectall_item = \u3059\u3079\u3066\u9078\u629E(&A)
163 menu.settings_menu = \u8A2D\u5B9A(&S)
164 menu.sortConfiguration = \u30D0\u30B0\u3092\u30B0\u30EB\u30FC\u30D7\u5316...
165 menu.suppress = \u3053\u306E\u30D0\u30B0\u3092\u6291\u6B62
166 menu.suppress_bug = \u3053\u306E\u30D0\u30B0\u3092\u6291\u6B62
167 menu.up = \u4E0A\u3078
168 menu.view_menu = \u8868\u793A(&V)
169 menu.viewbugs_item = \u30D0\u30B0\u306E\u8868\u793A
170 menu.viewprojectdetails_item = \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u8A73\u7D30\u306E\u8868\u793A
171
172 mode.at_or_after = \u4EE5\u964D
173 mode.at_or_before = \u4EE5\u524D
174 mode.equal_to = \u7B49\u3057\u3044
175 mode.not_equal_to = \u7B49\u3057\u304F\u306A\u3044
176
177 msg.addRemoveFiles = \u30D5\u30A1\u30A4\u30EB\u306E\u8FFD\u52A0/\u524A\u9664...
178 msg.addarchiveordirectory_txt = \u30A2\u30FC\u30AB\u30A4\u30D6\u307E\u305F\u306F\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30FC\u3092\u8FFD\u52A0
179 msg.analysingclasses_txt = \u30AF\u30E9\u30B9\u3092\u5206\u6790\u4E2D
180 msg.analysiscancelled_txt = {0} \u306E\u5206\u6790\u306F\u30E6\u30FC\u30B6\u30FC\u306B\u3088\u308A\u30AD\u30E3\u30F3\u30BB\u30EB\u3055\u308C\u307E\u3057\u305F
181 msg.analysiscompleted_txt = {0} \u306E\u5206\u6790\u304C\u5B8C\u4E86\u3057\u307E\u3057\u305F
182 msg.analyze_txt = \u5206\u6790
183 msg.beginninganalysis_txt = {0} \u306E\u5206\u6790\u3092\u958B\u59CB\u3057\u307E\u3059
184 msg.bugpatternsreported_txt = \u5831\u544A\u3055\u308C\u305F\u30D0\u30B0\u30FB\u30D1\u30BF\u30FC\u30F3
185 msg.cancelanalysis_txt = \u5206\u6790\u3092\u30AD\u30E3\u30F3\u30BB\u30EB\u3057\u307E\u3059\u304B?
186 msg.confirm_save_txt = \u4FDD\u5B58\u3057\u307E\u3059\u304B?
187 msg.couldnotopenproject_txt = \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u304C\u958B\u3051\u306A\u304B\u3063\u305F: {0}
188 msg.couldntload_txt = {0} \u3092\u30ED\u30FC\u30C9\u3067\u304D\u307E\u305B\u3093
189 msg.error_txt = \u30A8\u30E9\u30FC:
190 msg.errorformatting_txt = \u30D0\u30B0\u306B\u5BFE\u3059\u308B\u30E1\u30C3\u30BB\u30FC\u30B8\u306E\u30D5\u30A9\u30FC\u30DE\u30C3\u30C8\u51E6\u7406\u3067\u30A8\u30E9\u30FC: {0}
191 msg.exp_txt = \u5B9F\u9A13\u6BB5\u968E:
192 msg.failedtotransform_txt = \u8981\u7D04\u306E\u5909\u63DB\u306B\u5931\u6557: {0}
193 msg.fatalanalysisexception_txt = \u81F4\u547D\u7684\u306A\u5206\u6790\u4F8B\u5916: {0}
194 msg.finishedanalysis_txt = \u5206\u6790\u7D42\u4E86
195 msg.loading_bugs_over_network_txt = \u30CD\u30C3\u30C8\u30EF\u30FC\u30AF\u4E0A\u304B\u3089\u30D0\u30B0\u3092\u30ED\u30FC\u30C9\u3059\u308B...
196 msg.newproject_txt = \u65B0\u898F\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8
197 msg.nosource_txt = \u5229\u7528\u3067\u304D\u306A\u3044\u30BD\u30FC\u30B9
198 msg.openproject_txt = \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u958B\u304F...
199 msg.projectnojars_txt = \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8 {0} \u306F\u3001\u9078\u629E\u3055\u308C\u305F\u30AF\u30E9\u30B9\u30FB\u30A2\u30FC\u30AB\u30A4\u30D6\u30FB\u30D5\u30A1\u30A4\u30EB\u304C\u3042\u308A\u307E\u305B\u3093
200 msg.saveproject_txt = \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u306E\u4FDD\u5B58
201 msg.saveprojectas_txt = \u5225\u540D\u3067\u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u4FDD\u5B58
202 msg.saveprojectquery_txt = \u30D7\u30ED\u30B8\u30A7\u30AF\u30C8\u3092\u4FDD\u5B58\u3057\u307E\u3059\u304B?
203 msg.scanningarchives_txt = \u30A2\u30FC\u30AB\u30A4\u30D6\u3092\u30B9\u30AD\u30E3\u30F3\u4E2D
204 msg.userelativepaths_txt = \u76F8\u5BFE\u30D1\u30B9\u3092\u4F7F\u7528
205 msg.warning_txt = \u8B66\u544A:
206 msg.without_saving_txt = \u4FDD\u5B58\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002\u4FDD\u5B58\u3057\u307E\u3059\u304B?
207 msg.you_are_closing_txt = \u9589\u3058\u3066\u3044\u307E\u3059
208 msg.you_are_closing_without_saving_txt = \u4FDD\u5B58\u305B\u305A\u306B\u9589\u3058\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059\u3002\u4FDD\u5B58\u3057\u307E\u3059\u304B?
209 # The following three properties are not needed any more.
210 msg.you_are_loading_txt = \u30ED\u30FC\u30C9\u4E2D\u3067\u3059
211 # instead, the following two are used
212 msg.you_are_loading_without_saving_txt = \u4FDD\u5B58\u305B\u305A\u306B\u30ED\u30FC\u30C9\u3057\u3088\u3046\u3068\u3057\u3066\u3044\u307E\u3059\u3002\u4FDD\u5B58\u3057\u307E\u3059\u304B?
213
214 msg_addsource_lbl = \u30BD\u30FC\u30B9\u30FB\u30C7\u30A3\u30EC\u30AF\u30C8\u30EA\u30FC\u307E\u305F\u306F\u30A2\u30FC\u30AB\u30A4\u30D6\u3092\u8FFD\u52A0
215
216 pref.close = \u9589\u3058\u308B
217 pref.comments = \u30EC\u30D3\u30E5\u30FC
218 pref.description = \u8AAC\u660E
219 pref.filters = \u30D5\u30A3\u30EB\u30BF\u30FC
220 pref.name = \u540D\u524D
221 pref.suppressions = \u30D0\u30B0\u6291\u6B62
222 pref.suppressions_tab = \u6291\u6B62
223 pref.type = \u30BF\u30A4\u30D7
224
225 progress.analyzing_classes = \u30AF\u30E9\u30B9\u3092\u5206\u6790\u4E2D...
226 progress.finishing_analysis = \u5206\u6790\u3092\u7D42\u4E86\u4E2D...
227 progress.scanning_archives = \u30A2\u30FC\u30AB\u30A4\u30D6\u3092\u30B9\u30AD\u30E3\u30F3\u4E2D...
228
229 sort.bug_kind = \u30D0\u30B0\u7A2E\u985E
230 sort.bug_pattern = \u30D0\u30B0\u30FB\u30D1\u30BF\u30FC\u30F3
231 sort.category = \u30AB\u30C6\u30B4\u30EA\u30FC
232 sort.class = \u30AF\u30E9\u30B9
233 sort.designation = \u540D\u79F0
234 sort.divider = [\u4ED5\u5207\u308A]
235 sort.first_version = \u521D\u56DE\u30D0\u30FC\u30B8\u30E7\u30F3
236 sort.first_version_not_defined = \u521D\u56DE\u30D0\u30FC\u30B8\u30E7\u30F3\u304C\u672A\u5B9A\u7FA9
237 sort.last_version = \u6700\u7D42\u30D0\u30FC\u30B8\u30E7\u30F3
238 sort.last_version_not_defined = \u6700\u7D42\u30D0\u30FC\u30B8\u30E7\u30F3\u304C\u672A\u5B9A\u7FA9
239 sort.package = \u30D1\u30C3\u30B1\u30FC\u30B8
240 sort.priority = \u4FE1\u983C\u6027
241 sort.priority_experimental = \u512A\u5148\u5EA6 (\u5B9F\u9A13\u7684)
242 sort.priority_high = \u512A\u5148\u5EA6 (\u9AD8)
243 sort.priority_ignore = \u7121\u8996
244 sort.priority_low = \u512A\u5148\u5EA6 (\u4F4E)
245 sort.priority_normal = \u512A\u5148\u5EA6 (\u4E2D)
246
247 statusbar.bug_hidden = \u96A0\u3055\u308C\u305F\u30D0\u30B0 (\u30D3\u30E5\u30FC\u30FB\u30E1\u30CB\u30E5\u30FC\u53C2\u7167)
248 statusbar.bugs_hidden = \u96A0\u3055\u308C\u305F\u30D0\u30B0 (\u30D3\u30E5\u30FC\u30FB\u30E1\u30CB\u30E5\u30FC\u53C2\u7167)
249
250 summary.line = \u884C
251 summary.lines = \u884C
252 summary.null = null
253 summary.source_code = \u30BD\u30FC\u30B9\u30FB\u30B3\u30FC\u30C9\u3002
254
255 summary_line = \u884C
256
257 summary_lines = \u884C
258
259 tooltip.click_to_go_to = \u30AF\u30EA\u30C3\u30AF\u3057\u3066\u79FB\u52D5
260 tooltip.enter_comments = \u3053\u306E\u30D0\u30B0\u306B\u3064\u3044\u3066\u306E\u30B3\u30E1\u30F3\u30C8\u3092\u5165\u529B\u3057\u3066\u304F\u3060\u3055\u3044
261 tooltip.reorder_message = \u30C9\u30E9\u30C3\u30B0\u3059\u308B\u3068\u30D5\u30A9\u30EB\u30C0\u30FC\u30FB\u30C4\u30EA\u30FC\u3068\u30BD\u30FC\u30C8\u9806\u3092\u4E26\u3079\u66FF\u3048\u307E\u3059
262
263 tree.bugs = \u30D0\u30B0
264
265 txt.copy = \u30B3\u30D4\u30FC
266 txt.cut = \u5207\u308A\u53D6\u308A
267 txt.paste = \u8CBC\u308A\u4ED8\u3051
268
269 view.bug_summary = \u30D0\u30B0\u8981\u7D04
270 view.bugs = \u30D0\u30B0
271 view.comments = \u30EC\u30D3\u30E5\u30FC
272 view.source = \u30BD\u30FC\u30B9
0 package edu.umd.cs.findbugs.gui2;
1
2 import java.awt.Dimension;
3 import java.awt.event.ActionEvent;
4 import java.awt.event.ActionListener;
5 import java.util.List;
6
7 import javax.swing.JButton;
8 import javax.swing.JPopupMenu;
9 import javax.swing.JRadioButtonMenuItem;
10
11 import org.apache.commons.lang.StringUtils;
12
13 import edu.umd.cs.findbugs.cloud.CloudPlugin;
14
15 public class CloudCommentsPaneSwing extends CloudCommentsPane {
16
17
18 @Override
19 public Dimension getPreferredSize() {
20 return super.getMinimumSize();
21
22 }
23
24 @Override
25 protected void setupLinksOrButtons() {
26 signInOutLink = new JButton("Sign in");
27 ((JButton)signInOutLink).addActionListener(new ActionListener() {
28 public void actionPerformed(ActionEvent e) {
29 signInOrOutClicked();
30 }
31 });
32 cancelLink = new JButton("Cancel");
33 ((JButton) cancelLink).addActionListener(new ActionListener() {
34 public void actionPerformed(ActionEvent e) {
35 cancelClicked();
36 }
37 });
38 }
39
40 @Override
41 protected boolean isDisabled(CloudPlugin plugin) {
42 return false;
43 }
44
45 @Override
46 protected void showCloudChooser(List<CloudPlugin> plugins, List<String> descriptions) {
47 JPopupMenu popup = new JPopupMenu();
48 for (int i = 0; i < plugins.size(); i++) {
49 final CloudPlugin plugin = plugins.get(i);
50 String id = _bugCollection.getCloud().getPlugin().getId();
51 String thisid = plugin.getId();
52 boolean selected = id.equals(thisid);
53 JRadioButtonMenuItem item = new JRadioButtonMenuItem(descriptions.get(i), selected);
54 item.setToolTipText(plugin.getDetails());
55 item.addActionListener(new ActionListener() {
56 public void actionPerformed(ActionEvent e) {
57 changeCloud(plugin.getId());
58 }
59 });
60 popup.add(item);
61 }
62 popup.show(signInOutLink, 0, signInOutLink.getHeight() + 5);
63 }
64
65 @Override
66 protected void setSignInOutText(String buttonText) {
67 ((JButton) signInOutLink).setText(StringUtils.capitalize(buttonText));
68 }
69
70 }
0 package edu.umd.cs.findbugs.gui2;
1
2 import java.awt.Dimension;
3 import java.awt.event.ActionEvent;
4 import java.awt.event.ActionListener;
5 import java.util.List;
6
7 import javax.swing.JButton;
8 import javax.swing.JPopupMenu;
9 import javax.swing.JRadioButtonMenuItem;
10
11 import org.apache.commons.lang.StringUtils;
12
13 import edu.umd.cs.findbugs.cloud.CloudPlugin;
14
15 public class CloudCommentsPaneSwing extends CloudCommentsPane {
16
17
18 @Override
19 public Dimension getPreferredSize() {
20 return super.getMinimumSize();
21
22 }
23
24 @Override
25 protected void setupLinksOrButtons() {
26 signInOutLink = new JButton("Sign in");
27 ((JButton)signInOutLink).addActionListener(new ActionListener() {
28 @Override
29 public void actionPerformed(ActionEvent e) {
30 signInOrOutClicked();
31 }
32 });
33 cancelLink = new JButton("Cancel");
34 ((JButton) cancelLink).addActionListener(new ActionListener() {
35 @Override
36 public void actionPerformed(ActionEvent e) {
37 cancelClicked();
38 }
39 });
40 }
41
42 @Override
43 protected boolean isDisabled(CloudPlugin plugin) {
44 return false;
45 }
46
47 @Override
48 protected void showCloudChooser(List<CloudPlugin> plugins, List<String> descriptions) {
49 JPopupMenu popup = new JPopupMenu();
50 for (int i = 0; i < plugins.size(); i++) {
51 final CloudPlugin plugin = plugins.get(i);
52 String id = _bugCollection.getCloud().getPlugin().getId();
53 String thisid = plugin.getId();
54 boolean selected = id.equals(thisid);
55 JRadioButtonMenuItem item = new JRadioButtonMenuItem(descriptions.get(i), selected);
56 item.setToolTipText(plugin.getDetails());
57 item.addActionListener(new ActionListener() {
58 @Override
59 public void actionPerformed(ActionEvent e) {
60 changeCloud(plugin.getId());
61 }
62 });
63 popup.add(item);
64 }
65 popup.show(signInOutLink, 0, signInOutLink.getHeight() + 5);
66 }
67
68 @Override
69 protected void setSignInOutText(String buttonText) {
70 ((JButton) signInOutLink).setText(StringUtils.capitalize(buttonText));
71 }
72
73 }
2828 /**
2929 * Denotes a class name or package name where the . character is used to
3030 * separate package/class name components.
31 *
31 *
3232 * @author pugh
3333 */
3434 @Documented
5252
5353 final static Pattern pattern = Pattern.compile(slashedClassName);
5454
55 @Override
5556 @Nonnull
5657 public When forConstantValue(@Nonnull SlashedClassName annotation, Object value) {
57 if (!(value instanceof String))
58 if (!(value instanceof String)) {
5859 return When.UNKNOWN;
60 }
5961
60 if (pattern.matcher((String) value).matches())
62 if (pattern.matcher((String) value).matches()) {
6163 return When.ALWAYS;
64 }
6265
63 if (value.equals(NOT_AVAILABLE))
66 if (value.equals(NOT_AVAILABLE)) {
6467 return When.MAYBE;
68 }
6569
6670 return When.NEVER;
6771
7979 static byte[] copyOf(byte[] original, int newLength) {
8080 byte[] copy = new byte[newLength];
8181 System.arraycopy(original, 0, copy, 0,
82 Math.min(original.length, newLength));
82 Math.min(original.length, newLength));
8383 return copy;
8484 }
8585
8686
8787 public static byte[] readAll(@WillClose InputStream in, int size) throws IOException {
8888 try {
89 if (size == 0)
89 if (size == 0) {
9090 throw new IllegalArgumentException();
91 }
9192 byte[] result = new byte[size];
9293 int pos = 0;
9394 while (true) {
9697 pos += sz;
9798 }
9899
99 if (pos < size)
100 if (pos < size) {
100101 return copyOf(result, pos);
102 }
101103 int nextByte = in.read();
102 if (nextByte == -1)
104 if (nextByte == -1) {
103105 return result;
106 }
104107 size = size * 2 + 500;
105108 result = copyOf(result, size);
106109 result[pos++] = (byte) nextByte;
127130
128131 public static long copy(@WillNotClose InputStream in, @WillNotClose OutputStream out, long maxBytes)
129132
130 throws IOException {
133 throws IOException {
131134 long total = 0;
132135
133136 int sz = 0;
145148
146149 public static long copy(Reader in, Writer out, long maxChars)
147150
148 throws IOException {
151 throws IOException {
149152 long total = 0;
150153
151154 int sz;
164167 * Close given InputStream, ignoring any resulting exception.
165168 *
166169 */
167 public static void close(@CheckForNull Closeable c) {
170 public static void close(@CheckForNull Closeable c) {
168171 if (c == null) {
169172 return;
170173 }
228231 * if in.skip throws an IOException
229232 */
230233 public static void skipFully(InputStream in, long bytes) throws IOException {
231 if (bytes < 0)
234 if (bytes < 0) {
232235 throw new IllegalArgumentException("Can't skip " + bytes + " bytes");
236 }
233237 long remaining = bytes;
234238 while (remaining > 0) {
235239 long skipped = in.skip(remaining);
236 if (skipped <= 0)
240 if (skipped <= 0) {
237241 throw new EOFException("Reached EOF while trying to skip a total of " + bytes);
242 }
238243 remaining -= skipped;
239244 }
240245
241246 }
242247
243248 public static boolean verifyURL(URL u) {
244 if (u == null)
249 if (u == null) {
245250 return false;
251 }
246252
247253 InputStream i = null;
248254
2323 import java.io.InputStream;
2424
2525 public class SlowInputStream extends FilterInputStream {
26 private long started = System.currentTimeMillis();
26 private final long started = System.currentTimeMillis();
2727
2828 private long length = 0;
2929
30 private int bytesPerSecond;
30 private final int bytesPerSecond;
3131
3232 public SlowInputStream(InputStream in, int baudRate) {
3333 super(in);
4848 @Override
4949 public int read() throws IOException {
5050 int b = in.read();
51 if (b >= 0)
51 if (b >= 0) {
5252 length++;
53 }
5354 delay();
5455 return b;
5556 }
6162
6263 @Override
6364 public int read(byte[] b, int off, int len) throws IOException {
64 if (len > bytesPerSecond / 10)
65 if (len > bytesPerSecond / 10) {
6566 len = bytesPerSecond / 10;
67 }
6668 int tmp = in.read(b, off, len);
6769 if (tmp >= 0) {
6870 length += tmp;
7476 @Override
7577 public long skip(long n) throws IOException {
7678 n = in.skip(n);
77 if (n >= 0)
79 if (n >= 0) {
7880 length += n;
81 }
7982 delay();
8083 return n;
8184 }
2222 * @author David Hovemeyer
2323 */
2424 public class JAIFEnumConstant {
25 private String name;
25 private final String name;
2626
2727 public JAIFEnumConstant(String name) {
2828 this.name = name;
3737
3838 /*
3939 * (non-Javadoc)
40 *
40 *
4141 * @see java.lang.Object#toString()
4242 */
4343 @Override
2020
2121 /**
2222 * Callbacks for parsing an extenal annotation file.
23 *
23 *
2424 * @author David Hovemeyer
2525 * @see <a
2626 * href="http://groups.csail.mit.edu/pag/jsr308/annotation-file-utilities/">Annotation
3030
3131 /**
3232 * Called to indicate the start of a package definition.
33 *
33 *
3434 * @param pkgName
3535 * package name
3636 */
3838
3939 /**
4040 * Called to indicate the end of a package definition.
41 *
41 *
4242 * @param pkgName
4343 */
4444 void endPackageDefinition(String pkgName);
4545
4646 /**
4747 * Called to indicate the start of an annotation.
48 *
48 *
4949 * @param annotationName
5050 * annotation name
5151 */
5353
5454 /**
5555 * Called to indicate the end of an annotation.
56 *
56 *
5757 * @param annotationName
5858 * annotation name
5959 */
6161
6262 /**
6363 * Called to visit an annotation field.
64 *
64 *
6565 * @param fieldName
6666 * annotation field name
6767 * @param constant
7272
7373 /**
7474 * Called to indicate the start of an annotation definition.
75 *
75 *
7676 * @param annotationName
7777 * name of the annotation
7878 * @param retention
8282
8383 /**
8484 * Called to indicate the end of an annotation definition.
85 *
85 *
8686 * @param annotationName
8787 * name of the annotation
8888 */
9090
9191 /**
9292 * Called to visit an annotation field definition.
93 *
93 *
9494 * @param type
9595 * type of the annotation field (in JVM signature format)
9696 * @param fieldName
2626
2727 /**
2828 * Parse an external annotation file.
29 *
29 *
3030 * @author David Hovemeyer
3131 * @see <a
3232 * href="http://groups.csail.mit.edu/pag/jsr308/annotation-file-utilities/">Annotation
3333 * File Utilities/</a>
3434 */
3535 public class JAIFParser {
36 private JAIFScanner scanner;
37
38 private JAIFEvents callback;
36 private final JAIFScanner scanner;
37
38 private final JAIFEvents callback;
3939
4040 public JAIFParser(Reader reader, JAIFEvents callback) {
4141 this.scanner = new JAIFScanner(reader);
127127 private String readType() throws IOException, JAIFSyntaxException {
128128 StringBuilder buf = new StringBuilder();
129129
130 JAIFToken t = expect(JAIFTokenKind.IDENTIFIER_OR_KEYWORD);
131
132 if (t.lexeme.equals("enum")) {
133
134 }
130 /*JAIFToken t = */expect(JAIFTokenKind.IDENTIFIER_OR_KEYWORD);
131
132 // if (t.lexeme.equals("enum")) {
133 //
134 // }
135135
136136 return buf.toString();
137137 }
172172
173173 while (!scanner.atEOF()) {
174174 t = scanner.peekToken();
175 if (t.lexeme.equals("package")) {
175 if ("package".equals(t.lexeme)) {
176176 break;
177177 }
178178
321321 private void parseAnnotationDefinitionOrClassDefinition() throws IOException, JAIFSyntaxException {
322322 JAIFToken t = scanner.peekToken();
323323
324 if (t.lexeme.equals("annotation")) {
324 if ("annotation".equals(t.lexeme)) {
325325 parseAnnotationDefinition();
326 } else if (t.lexeme.equals("class")) {
326 } else if ("class".equals(t.lexeme)) {
327327 parseClassDefinition();
328328 } else {
329329 throw new JAIFSyntaxException(this, "Unexpected token " + t + " (expected `annotation' or `class')");
336336 String retention = null;
337337
338338 JAIFToken t = scanner.peekToken();
339 if (t.lexeme.equals("visible") || t.lexeme.equals("invisible") || t.lexeme.equals("source")) {
339 if ("visible".equals(t.lexeme) || "invisible".equals(t.lexeme) || "source".equals(t.lexeme)) {
340340 retention = t.lexeme;
341341 scanner.nextToken();
342342 }
372372 }
373373
374374 JAIFEvents callback = new JAIFEvents() {
375 @Override
375376 public void annotationField(String fieldName, Object constant) {
376377 System.out.println(" " + fieldName + "=" + constant);
377378 }
378379
380 @Override
379381 public void endAnnotation(String annotationName) {
380382 }
381383
384 @Override
382385 public void endPackageDefinition(String pkgName) {
383386 }
384387
388 @Override
385389 public void startAnnotation(String annotationName) {
386390 System.out.println(" annotation " + annotationName);
387391 }
388392
393 @Override
389394 public void startPackageDefinition(String pkgName) {
390395 System.out.println("package " + pkgName);
391396 }
392397
398 @Override
393399 public void startAnnotationDefinition(String annotationName, String retention) {
394400 System.out.println(" annotation " + annotationName + " " + retention);
395401 }
396402
403 @Override
397404 public void endAnnotationDefinition(String annotationName) {
398405 }
399406
407 @Override
400408 public void annotationFieldDefinition(String type, String fieldName) {
401409 System.out.println(" " + type + " " + fieldName);
402410 }
2626
2727 /**
2828 * Lexical scanner for external annotation files.
29 *
29 *
3030 * @author David Hovemeyer
3131 * @see <a
3232 * href="http://groups.csail.mit.edu/pag/jsr308/annotation-file-utilities/">Annotation
3535 public class JAIFScanner {
3636
3737 static class TokenPattern {
38 private Pattern pattern;
39
40 private JAIFTokenKind kind;
38 private final Pattern pattern;
39
40 private final JAIFTokenKind kind;
4141
4242 public TokenPattern(String regex, JAIFTokenKind kind) {
4343 this.pattern = Pattern.compile("^" + regex);
8989 private static final String INT_TYPE_SUFFIX_OPT = "[Ll]?";
9090
9191 private static final String INPUT_CHAR = "[^\\\\\\\"]";// anything other
92 // than backslash or
93 // double-quote
94 // character
92 // than backslash or
93 // double-quote
94 // character
9595
9696 private static final String OCT_ESCAPE = "([0-7]|[0-3]?[0-7][0-7])";
9797
100100 private static final String STRING_CHARS_OPT = "(" + INPUT_CHAR + "|" + ESCAPE_SEQ + ")*";
101101
102102 private static final TokenPattern[] TOKEN_PATTERNS = {
103 // Misc. syntax
104 new TokenPattern(":", JAIFTokenKind.COLON),
105 new TokenPattern("\\(", JAIFTokenKind.LPAREN),
106 new TokenPattern("\\)", JAIFTokenKind.RPAREN),
107 new TokenPattern(",", JAIFTokenKind.COMMA),
108 new TokenPattern("=", JAIFTokenKind.EQUALS),
109
110 // Identifiers and keywords
111 new TokenPattern(ID_START + "(" + ID_REST + ")*", JAIFTokenKind.IDENTIFIER_OR_KEYWORD),
112
113 // FP literals
114 new TokenPattern(DIGITS + DOT + DIGITS_OPT + EXP_PART_OPT + FLOAT_TYPE_SUFFIX_OPT,
115 JAIFTokenKind.FLOATING_POINT_LITERAL),
116 new TokenPattern(DOT + DIGITS + EXP_PART_OPT + FLOAT_TYPE_SUFFIX_OPT, JAIFTokenKind.FLOATING_POINT_LITERAL),
117 new TokenPattern(DIGITS + EXP_PART + FLOAT_TYPE_SUFFIX_OPT, JAIFTokenKind.FLOATING_POINT_LITERAL),
118 new TokenPattern(DIGITS + EXP_PART_OPT + FLOAT_TYPE_SUFFIX, JAIFTokenKind.FLOATING_POINT_LITERAL),
119
120 // This must come after the FP literal patterns
121 new TokenPattern(DOT, JAIFTokenKind.DOT),
122
123 // Integer literals
124 new TokenPattern("0" + OCTAL_DIGITS + INT_TYPE_SUFFIX_OPT, JAIFTokenKind.OCTAL_LITERAL),
125 new TokenPattern(HEX_SIGNIFIER + HEX_DIGITS + INT_TYPE_SUFFIX_OPT, JAIFTokenKind.HEX_LITERAL),
126 new TokenPattern(DIGITS + INT_TYPE_SUFFIX_OPT, JAIFTokenKind.DECIMAL_LITERAL),
127
128 // String literals
129 new TokenPattern("\"" + STRING_CHARS_OPT + "\"", JAIFTokenKind.STRING_LITERAL), };
130
131 private BufferedReader reader;
103 // Misc. syntax
104 new TokenPattern(":", JAIFTokenKind.COLON),
105 new TokenPattern("\\(", JAIFTokenKind.LPAREN),
106 new TokenPattern("\\)", JAIFTokenKind.RPAREN),
107 new TokenPattern(",", JAIFTokenKind.COMMA),
108 new TokenPattern("=", JAIFTokenKind.EQUALS),
109
110 // Identifiers and keywords
111 new TokenPattern(ID_START + "(" + ID_REST + ")*", JAIFTokenKind.IDENTIFIER_OR_KEYWORD),
112
113 // FP literals
114 new TokenPattern(DIGITS + DOT + DIGITS_OPT + EXP_PART_OPT + FLOAT_TYPE_SUFFIX_OPT,
115 JAIFTokenKind.FLOATING_POINT_LITERAL),
116 new TokenPattern(DOT + DIGITS + EXP_PART_OPT + FLOAT_TYPE_SUFFIX_OPT, JAIFTokenKind.FLOATING_POINT_LITERAL),
117 new TokenPattern(DIGITS + EXP_PART + FLOAT_TYPE_SUFFIX_OPT, JAIFTokenKind.FLOATING_POINT_LITERAL),
118 new TokenPattern(DIGITS + EXP_PART_OPT + FLOAT_TYPE_SUFFIX, JAIFTokenKind.FLOATING_POINT_LITERAL),
119
120 // This must come after the FP literal patterns
121 new TokenPattern(DOT, JAIFTokenKind.DOT),
122
123 // Integer literals
124 new TokenPattern("0" + OCTAL_DIGITS + INT_TYPE_SUFFIX_OPT, JAIFTokenKind.OCTAL_LITERAL),
125 new TokenPattern(HEX_SIGNIFIER + HEX_DIGITS + INT_TYPE_SUFFIX_OPT, JAIFTokenKind.HEX_LITERAL),
126 new TokenPattern(DIGITS + INT_TYPE_SUFFIX_OPT, JAIFTokenKind.DECIMAL_LITERAL),
127
128 // String literals
129 new TokenPattern("\"" + STRING_CHARS_OPT + "\"", JAIFTokenKind.STRING_LITERAL), };
130
131 private final BufferedReader reader;
132132
133133 private JAIFToken next;
134134
201201 // System.out.println("Consumed " + wsCount +
202202 // " characters of horizontal whitespace");
203203
204 if (lineBuf.equals("")) {
204 if ("".equals(lineBuf)) {
205205 // Reached end of line.
206206 next = new JAIFToken(JAIFTokenKind.NEWLINE, "\n", lineNum);
207207 lineBuf = null;
2020
2121 /**
2222 * One lexical token of an external annotations file.
23 *
23 *
2424 * @author David Hovemeyer
2525 * @see <a href="http://pag.csail.mit.edu/jsr308/annotation-file-utilities/">
2626 * http://pag.csail.mit.edu/jsr308/annotation-file-utilities/</a>
3030
3131 String lexeme;
3232
33 int lineNum;
33 // int lineNum;
3434
3535 public JAIFToken(JAIFTokenKind kind, String lexeme, int lineNum) {
3636 this.kind = kind;
3737 this.lexeme = lexeme;
38 this.lineNum = lineNum;
38 // this.lineNum = lineNum;
3939 // System.out.println("token: " + this);
4040 }
4141
4545
4646 /*
4747 * (non-Javadoc)
48 *
48 *
4949 * @see java.lang.Object#toString()
5050 */
5151 @Override
5252 public String toString() {
53 if (lexeme.equals("\n")) {
53 if ("\n".equals(lexeme)) {
5454 return "<newline>";
5555 } else {
5656 return lexeme;
2121 public enum JAIFTokenKind {
2222 NEWLINE("<newline>"), COLON(":"), DOT("."), IDENTIFIER_OR_KEYWORD("<identifier or keyword>"), LPAREN("("), RPAREN(")"), COMMA(
2323 ","), EQUALS("="), OCTAL_LITERAL("<octal literal>"), DECIMAL_LITERAL("<decimal literal>"), HEX_LITERAL(
24 "<hex literal>"), FLOATING_POINT_LITERAL("<floating point literal>"), STRING_LITERAL("<string literal>");
24 "<hex literal>"), FLOATING_POINT_LITERAL("<floating point literal>"), STRING_LITERAL("<string literal>");
2525
2626 private String stringRep;
2727
3131
3232 /*
3333 * (non-Javadoc)
34 *
34 *
3535 * @see java.lang.Enum#toString()
3636 */
3737 @Override
3333 */
3434 public class ConsoleLogger implements Logger {
3535
36 private LogSync logSync;
36 private final LogSync logSync;
3737
3838 /**
3939 * Creates a new instance of ConsoleLogger
4242 this.logSync = logSync;
4343 }
4444
45 @Override
4546 public void logMessage(int severity, String message) {
4647 // If this is an error, pass it to the GUI
47 if (severity == ERROR)
48 if (severity == ERROR) {
4849 logSync.error(message);
50 }
4951
5052 // Format a message for the console window
5153 Date date = new Date();
5355 buf.append('[');
5456 buf.append(date.toString());
5557 buf.append("] ");
56 if (severity == ERROR)
58 if (severity == ERROR) {
5759 buf.append(L10N.getLocalString("msg.error_txt", "ERROR: "));
58 else if (severity == WARNING)
60 } else if (severity == WARNING) {
5961 buf.append(L10N.getLocalString("msg.warning_txt", "WARNING: "));
62 }
6063 buf.append(message);
6164 logSync.writeToLog(buf.toString());
6265 }
2626 import java.util.Stack;
2727 import java.util.TreeSet;
2828 import java.util.concurrent.ConcurrentHashMap;
29 import java.util.concurrent.ConcurrentMap;
2930 import java.util.concurrent.TimeUnit;
3031 import java.util.concurrent.atomic.AtomicInteger;
3132 import java.util.concurrent.atomic.AtomicLong;
4748 public Profiler() {
4849 startTimes = new Stack<Clock>();
4950 profile = new ConcurrentHashMap<Class<?>, Profile>();
50 if (REPORT)
51 if (REPORT) {
5152 System.err.println("Profiling activated");
53 }
5254 }
5355
5456 public static interface Filter {
6264 this.minNanoSeconds = minNanoSeconds;
6365 }
6466
67 @Override
6568 public boolean accepts(Profile p) {
6669 long time = p.totalTime.get();
6770 if (time < minNanoSeconds) {
7881 this.minNanoSeconds = minNanoSeconds;
7982 }
8083
84 @Override
8185 public boolean accepts(Profile p) {
8286 int totalCalls = p.totalCalls.get();
8387 long time = p.totalTime.get();
9599 this.minCalls = minCalls;
96100 }
97101
102 @Override
98103 public boolean accepts(Profile p) {
99104 int totalCalls = p.totalCalls.get();
100105 if (totalCalls < minCalls) {
116121 final AtomicLong totalSquareMicroseconds = new AtomicLong();
117122
118123 private final String className;
119
124
120125 Object maxContext;
121126
122127 /**
133138 long oldMax = maxTime.get();
134139 if (nanoTime > oldMax) {
135140 maxTime.compareAndSet(oldMax, nanoTime);
136 if (MAX_CONTEXT)
141 if (MAX_CONTEXT) {
137142 maxContext = context;
143 }
138144 }
139145 long microseconds = TimeUnit.MICROSECONDS.convert(nanoTime, TimeUnit.NANOSECONDS);
140146 totalSquareMicroseconds.addAndGet(microseconds * microseconds);
149155 * @throws IOException
150156 */
151157
158 @Override
152159 public void writeXML(XMLOutput xmlOutput) throws IOException {
153160 long time = totalTime.get();
154161 int callCount = totalCalls.get();
169176 xmlOutput.addAttribute("invocations", String.valueOf(callCount));
170177 xmlOutput.addAttribute("avgMicrosecondsPerInvocation", String.valueOf(averageTimeMicros));
171178 xmlOutput.addAttribute("maxMicrosecondsPerInvocation", String.valueOf(maxTimeMicros));
172 if (maxContext != null)
173 xmlOutput.addAttribute("maxContext", String.valueOf(maxContext));
179 if (maxContext != null) {
180 xmlOutput.addAttribute("maxContext", String.valueOf(maxContext));
181 }
174182 xmlOutput.addAttribute("standardDeviationMircosecondsPerInvocation", String.valueOf(timeStandardDeviation));
175183 xmlOutput.stopTag(true);
176184 }
201209
202210 final Stack<Clock> startTimes;
203211
204 final ConcurrentHashMap<Class<?>, Profile> profile;
205
212 final ConcurrentMap<Class<?>, Profile> profile;
213
206214 final Stack<Object> context = new Stack<Object>();
207
215
208216 public void startContext(Object context) {
209217 this.context.push(context);
210218 }
211
219
212220 public void endContext(Object context) {
213221 Object o = this.context.pop();
214222 assert o == context;
215223 }
216224
217225 private Object getContext() {
218 if (context.size() == 0)
226 if (context.size() == 0) {
219227 return "";
228 }
220229 try {
221230 return context.peek();
222231 } catch (EmptyStackException e) {
273282 this.profiler = p;
274283 }
275284
285 @Override
276286 public int compare(Class<?> c1, Class<?> c2) {
277287 try {
278288 return c1.getSimpleName().compareTo(c2.getSimpleName());
280290 AnalysisContext.logError("Error comparing " + c1 + " and " + c2, e);
281291 int i1 = System.identityHashCode(c1);
282292 int i2 = System.identityHashCode(c2);
283 if (i1 < i2)
293 if (i1 < i2) {
284294 return -1;
285 if (i1 > i2)
295 }
296 if (i1 > i2) {
286297 return 1;
298 }
287299 return 0;
288300 }
289301 }
413425 AnalysisContext.logError("Unexpected null profile for " + c.getName(), new NullPointerException());
414426 result = new Profile(c.getName());
415427 Profile tmp = profile.putIfAbsent(c, result);
416 if (tmp != null)
428 if (tmp != null) {
417429 return tmp;
430 }
418431 }
419432 return result;
420433 }
426439 * edu.umd.cs.findbugs.xml.XMLWriteable#writeXML(edu.umd.cs.findbugs.xml
427440 * .XMLOutput)
428441 */
442 @Override
429443 public void writeXML(XMLOutput xmlOutput) throws IOException {
430444 xmlOutput.startTag("FindBugsProfile");
431445 xmlOutput.stopTag(false);
432446 TreeSet<Class<?>> treeSet = new TreeSet<Class<?>>(new TotalTimeComparator(this));
433447 treeSet.addAll(profile.keySet());
434448 long totalTime = 0;
435 for (Profile p : profile.values())
449 for (Profile p : profile.values()) {
436450 totalTime += p.totalTime.get();
451 }
437452
438453 long accumulatedTime = 0;
439454
440455 for (Class<?> c : treeSet) {
441456 Profile p = getProfile(c);
442 if (p == null)
457 if (p == null) {
443458 continue;
459 }
444460 p.writeXML(xmlOutput);
445461 accumulatedTime += p.totalTime.get();
446 if (accumulatedTime > 3 * totalTime / 4)
462 if (accumulatedTime > 3 * totalTime / 4) {
447463 break;
464 }
448465 }
449466 xmlOutput.closeTag("FindBugsProfile");
450467 }
2020
2121 import java.lang.reflect.Method;
2222
23 import edu.umd.cs.findbugs.SystemProperties;
24 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
25
2326 /**
2427 * @author pugh
2528 */
2629 public class YourKitController {
2730
31 private static final boolean ENABLED = SystemProperties.getBoolean("findbugs.yourkit.enabled");
32
2833 Object controller;
2934
3035 Method advanceGeneration, captureMemorySnapshot, getStatus;
31
32 public static final long ALLOCATION_RECORDING = 2L;
36
37 public static final long ALLOCATION_RECORDING = 2L;
3338
3439 public YourKitController() {
40 if(!ENABLED){
41 return;
42 }
3543 try {
3644 Class<?> c = Class.forName("com.yourkit.api.Controller");
3745 controller = c.newInstance();
4856 }
4957
5058 public void advanceGeneration(String name) {
51 if (controller == null)
59 if (controller == null) {
5260 return;
61 }
5362 try {
5463 advanceGeneration.invoke(controller, name);
55 } catch (Exception e) {
64 } catch (Throwable e) {
5665 assert true;
5766 }
5867 }
59
68
6069 public long getStatus() {
61 if (getStatus == null)
70 if (getStatus == null) {
6271 return 0;
72 }
6373 try {
6474 return (Long) getStatus.invoke(controller);
6575 } catch (RuntimeException e) {
6979 }
7080 }
7181
82 @SuppressFBWarnings("DM_GC")
7283 public void captureMemorySnapshot() {
73 if (controller == null)
84 if (controller == null) {
7485 return;
86 }
7587 try {
7688 System.gc();
7789 captureMemorySnapshot.invoke(controller);
7890 } catch (RuntimeException e) {
7991 throw e;
80 } catch (Exception e) {
92 } catch (Throwable e) {
8193 assert true;
8294 }
8395 }
4141 /**
4242 * Features of a class which may be used to identify it if it is renamed or
4343 * modified.
44 *
44 *
4545 * @author David Hovemeyer
4646 */
4747 public class ClassFeatureSet implements XMLWriteable {
5757
5858 private boolean isInterface;
5959
60 private Set<String> featureSet;
60 private final Set<String> featureSet;
6161
6262 /**
6363 * Constructor. Creates an empty feature set.
7373
7474 /**
7575 * Initialize from given JavaClass.
76 *
76 *
7777 * @param javaClass
7878 * the JavaClass
7979 * @return this object
112112 /**
113113 * Determine if given method overrides a superclass or superinterface
114114 * method.
115 *
115 *
116116 * @param javaClass
117117 * class defining the method
118118 * @param method
121121 * false if not
122122 */
123123 private boolean overridesSuperclassMethod(JavaClass javaClass, Method method) {
124 if (method.isStatic())
124 if (method.isStatic()) {
125125 return false;
126 }
126127
127128 try {
128129 JavaClass[] superclassList = javaClass.getSuperClasses();
129130 if (superclassList != null) {
130131 JavaClassAndMethod match = Hierarchy.findMethod(superclassList, method.getName(), method.getSignature(),
131132 Hierarchy.INSTANCE_METHOD);
132 if (match != null)
133 if (match != null) {
133134 return true;
135 }
134136 }
135137
136138 JavaClass[] interfaceList = javaClass.getAllInterfaces();
137139 if (interfaceList != null) {
138140 JavaClassAndMethod match = Hierarchy.findMethod(interfaceList, method.getName(), method.getSignature(),
139141 Hierarchy.INSTANCE_METHOD);
140 if (match != null)
142 if (match != null) {
141143 return true;
144 }
142145 }
143146
144147 return false;
149152
150153 /**
151154 * Figure out if a class member (field or method) is synthetic.
152 *
155 *
153156 * @param member
154157 * a field or method
155158 * @return true if the member is synthetic
156159 */
157160 private boolean isSynthetic(FieldOrMethod member) {
158 if (BCELUtil.isSynthetic(member)) // this never works, but worth a try
161 if (BCELUtil.isSynthetic(member)) {
159162 return true;
163 }
160164
161165 String name = member.getName();
162166
163 if (name.startsWith("class$"))
167 if (name.startsWith("class$")) {
164168 return true;
165
166 if (name.startsWith("access$"))
169 }
170
171 if (name.startsWith("access$")) {
167172 return true;
173 }
168174
169175 return false;
170176 }
217223
218224 /**
219225 * Transform a class name by stripping its package name.
220 *
226 *
221227 * @param className
222228 * a class name
223229 * @return the transformed class name
236242 /**
237243 * Return true if classes in the given package is unlikely to be renamed:
238244 * e.g., because they are part of a public API.
239 *
245 *
240246 * @param pkg
241247 * the package name
242248 * @return true if classes in the package is unlikely to be renamed
248254 /**
249255 * Transform a method signature to allow it to be compared even if any of
250256 * its parameter types are moved to another package.
251 *
257 *
252258 * @param signature
253259 * a method signature
254260 * @return the transformed signature
273279 /**
274280 * Transform a field or method parameter signature to allow it to be
275281 * compared even if it is moved to another package.
276 *
282 *
277283 * @param signature
278284 * the signature
279285 * @return the transformed signature
316322
317323 public static double similarity(ClassFeatureSet a, ClassFeatureSet b) {
318324 // Some features must match exactly
319 if (a.isInterface() != b.isInterface())
325 if (a.isInterface() != b.isInterface()) {
320326 return 0.0;
321
322 if (a.getNumFeatures() < MIN_FEATURES || b.getNumFeatures() < MIN_FEATURES)
327 }
328
329 if (a.getNumFeatures() < MIN_FEATURES || b.getNumFeatures() < MIN_FEATURES) {
323330 return a.getClassName().equals(b.getClassName()) ? EXACT_CLASS_NAME_MATCH : 0.0;
331 }
324332
325333 int numMatch = 0;
326334 int max = Math.max(a.getNumFeatures(), b.getNumFeatures());
362370
363371 /*
364372 * (non-Javadoc)
365 *
373 *
366374 * @see
367375 * edu.umd.cs.findbugs.xml.XMLWriteable#writeXML(edu.umd.cs.findbugs.xml
368376 * .XMLOutput)
369377 */
378 @Override
370379 public void writeXML(XMLOutput xmlOutput) throws IOException {
371380 xmlOutput.openTag(ELEMENT_NAME, new XMLAttributeList().addAttribute("class", className));
372381 for (Iterator<String> i = featureIterator(); i.hasNext();) {
2020
2121 /**
2222 * Rewrite class names. This is needed for comparing warnings across versions.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public interface ClassNameRewriter {
2727 /**
2828 * Rewrite a class name.
29 *
29 *
3030 * @param className
3131 * a class name
3232 * @return the rewritten class name
2626
2727 /**
2828 * Utility methods for using a ClassNameRewriter.
29 *
29 *
3030 * @author David Hovemeyer
3131 */
3232 public abstract class ClassNameRewriterUtil {
3333
3434 /**
3535 * Rewrite a method signature.
36 *
36 *
3737 * @param classNameRewriter
3838 * a ClassNameRewriter
3939 * @param methodSignature
6262
6363 /**
6464 * Rewrite a signature.
65 *
65 *
6666 * @param classNameRewriter
6767 * a ClassNameRewriter
6868 * @param signature
8484 /**
8585 * Rewrite a MethodAnnotation to update the class name, and any class names
8686 * mentioned in the method signature.
87 *
87 *
8888 * @param classNameRewriter
8989 * a ClassNameRewriter
9090 * @param annotation
104104 /**
105105 * Rewrite a FieldAnnotation to update the class name and field signature,
106106 * if needed.
107 *
107 *
108108 * @param classNameRewriter
109109 * a ClassNameRewriter
110110 * @param annotation
2222
2323 /**
2424 * ClassNameRewriter that leaves classe names unchanged.
25 *
25 *
2626 * @author David Hovemeyer
2727 */
2828 public class IdentityClassNameRewriter implements ClassNameRewriter, Serializable {
3333
3434 /**
3535 * Get the single instance.
36 *
36 *
3737 * @return the instance
3838 */
3939 public static IdentityClassNameRewriter instance() {
4343 /**
4444 * @see edu.umd.cs.findbugs.model.ClassNameRewriter#rewriteClassName(java.lang.String)
4545 */
46 @Override
4647 public String rewriteClassName(String className) {
47 if (className.indexOf('+') >= 0)
48 if (className.indexOf('+') >= 0) {
4849 className = className.replace('+', '$');
50 }
4951 return className;
5052 }
5153
3434 /**
3535 * Build a map of added class names to removed class names. Serves as a
3636 * ClassNameRewriter that can match up renamed classes in two BugCollections.
37 *
37 *
3838 * @author David Hovemeyer
3939 */
4040 public class MovedClassMap implements ClassNameRewriter {
4141
4242 private static final boolean DEBUG = SystemProperties.getBoolean("movedClasses.debug");
4343
44 private BugCollection before;
44 private final BugCollection before;
4545
46 private BugCollection after;
46 private final BugCollection after;
4747
48 private Map<String, String> rewriteMap;
48 private final Map<String, String> rewriteMap;
4949
5050 public MovedClassMap(BugCollection before, BugCollection after) {
5151 this.before = before;
7575 String shortAddedName = getShortClassName(fullAddedName);
7676 String fullRemovedName = removedShortNameToFullNameMap.get(shortAddedName);
7777 if (fullRemovedName != null) {
78 if (DEBUG)
78 if (DEBUG) {
7979 System.err.println(fullAddedName + " --> " + fullRemovedName);
80 }
8081 rewriteMap.put(fullAddedName, fullRemovedName);
8182 }
8283
8990 return rewriteMap.isEmpty();
9091 }
9192
93 @Override
9294 public String rewriteClassName(String className) {
9395 String rewrittenClassName = rewriteMap.get(className);
9496 if (rewrittenClassName != null) {
99101
100102 /**
101103 * Find set of classes referenced in given BugCollection.
102 *
104 *
103105 * @param bugCollection
104106 * @return set of classes referenced in the BugCollection
105107 */
110112 BugInstance warning = i.next();
111113 for (Iterator<BugAnnotation> j = warning.annotationIterator(); j.hasNext();) {
112114 BugAnnotation annotation = j.next();
113 if (!(annotation instanceof ClassAnnotation))
115 if (!(annotation instanceof ClassAnnotation)) {
114116 continue;
117 }
115118 classSet.add(((ClassAnnotation) annotation).getClassName());
116119 }
117120 }
121124
122125 /**
123126 * Build a map of short class names (without package) to full class names.
124 *
127 *
125128 * @param classSet
126129 * set of fully-qualified class names
127130 * @return map of short class names to fully-qualified class names
137140
138141 /**
139142 * Get a short class name (no package part).
140 *
143 *
141144 * @param className
142145 * a class name
143146 * @return short class name
3434 /**
3535 * Repopulate a BugCollection with class features from the classes in a
3636 * specified jar file.
37 *
37 *
3838 * @author David Hovemeyer
3939 */
4040 public class RegenerateClassFeatures {
41 private BugCollection bugCollection;
41 private final BugCollection bugCollection;
4242
43 private String jarFile;
43 private final String jarFile;
4444
4545 public RegenerateClassFeatures(BugCollection bugCollection, String jarFile) {
4646 this.bugCollection = bugCollection;
5050 public RegenerateClassFeatures execute() throws IOException {
5151 bugCollection.clearClassFeatures();
5252
53 ZipFile zipFile = new ZipFile(jarFile);
5453
5554 ArrayList<JavaClass> classList = new ArrayList<JavaClass>();
5655
57 // Add all classes to repository (for hierarchy queries)
58 Enumeration<? extends ZipEntry> entries = zipFile.entries();
59 while (entries.hasMoreElements()) {
60 ZipEntry entry = entries.nextElement();
56 try (ZipFile zipFile = new ZipFile(jarFile)){
6157
62 if (!entry.getName().endsWith(".class"))
63 continue;
58 // Add all classes to repository (for hierarchy queries)
59 Enumeration<? extends ZipEntry> entries = zipFile.entries();
60 while (entries.hasMoreElements()) {
61 ZipEntry entry = entries.nextElement();
6462
65 ClassParser parser = new ClassParser(zipFile.getInputStream(entry), entry.getName());
66 JavaClass javaClass = parser.parse();
63 if (!entry.getName().endsWith(".class")) {
64 continue;
65 }
6766
68 Repository.addClass(javaClass);
69 classList.add(javaClass);
67 ClassParser parser = new ClassParser(zipFile.getInputStream(entry), entry.getName());
68 JavaClass javaClass = parser.parse();
69
70 Repository.addClass(javaClass);
71 classList.add(javaClass);
72 }
7073 }
71 zipFile.close();
7274
7375 for (JavaClass javaClass : classList) {
7476 ClassFeatureSet classFeatureSet = new ClassFeatureSet().initialize(javaClass);
2626 * @author David Hovemeyer
2727 */
2828 public class SimilarClassFinder {
29 private List<SimilarClassSet> similarClassSetList;
29 private final List<SimilarClassSet> similarClassSetList;
3030
3131 public SimilarClassFinder() {
3232 this.similarClassSetList = new LinkedList<SimilarClassSet>();
2323
2424 /**
2525 * A set of classes considered similar because of their features.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class SimilarClassSet {
30 private List<ClassFeatureSet> memberList;
30 private final List<ClassFeatureSet> memberList;
3131
3232 public SimilarClassSet() {
3333 this.memberList = new LinkedList<ClassFeatureSet>();
3535
3636 public boolean shouldContain(ClassFeatureSet candidate) {
3737 for (ClassFeatureSet member : memberList) {
38 if (candidate.similarTo(member))
38 if (candidate.similarTo(member)) {
3939 return true;
40 }
4041 }
4142 return false;
4243 }
4647 }
4748
4849 public String getRepresentativeClassName() {
49 if (memberList.isEmpty())
50 if (memberList.isEmpty()) {
5051 throw new IllegalStateException();
52 }
5153 return memberList.get(0).getClassName();
5254 }
5355
3232 /**
3333 * An analysis pass in the overall ExecutionPlan. This is a list of Detectors to
3434 * be applied to analyzed classes.
35 *
35 *
3636 * @see ExecutionPlan
3737 * @author David Hovemeyer
3838 */
3939 public class AnalysisPass {
40 private LinkedList<DetectorFactory> orderedFactoryList;
40 private final LinkedList<DetectorFactory> orderedFactoryList;
4141
42 private HashSet<DetectorFactory> memberSet;
42 private final HashSet<DetectorFactory> memberSet;
4343
4444 // private Detector2[] detectorList;
4545
4646 /**
4747 * Constructor.
48 *
48 *
4949 * Creates an empty analysis pass.
5050 */
5151 public AnalysisPass() {
5656 /**
5757 * Make given DetectorFactory a member of this pass. Does not position the
5858 * factory within the overall list of detectors.
59 *
59 *
6060 * @param factory
6161 * a DetectorFactory
6262 */
6767 /**
6868 * Append the given DetectorFactory to the end of the ordered detector list.
6969 * The factory must be a member of the pass.
70 *
70 *
7171 * @param factory
7272 * a DetectorFactory
7373 */
7474 public void append(DetectorFactory factory) {
75 if (!memberSet.contains(factory))
75 if (!memberSet.contains(factory)) {
7676 throw new IllegalArgumentException("Detector " + factory.getFullName() + " appended to pass it doesn't belong to");
77 }
7778 this.orderedFactoryList.addLast(factory);
7879 }
7980
8081 /**
8182 * Get the members of this pass.
82 *
83 *
8384 * @return members of this pass
8485 */
8586 public Collection<DetectorFactory> getMembers() {
106107
107108 /**
108109 * Return whether or not this pass contains the given DetectorFactory.
109 *
110 *
110111 * @param factory
111112 * the DetectorFactory
112113 * @return true if this pass contains the DetectorFactory, false if not
118119 /**
119120 * Instantiate all of the Detector2s in this pass and return them in a
120121 * (correctly-ordered) array.
121 *
122 *
122123 * @param bugReporter
123124 * the BugReporter
124125 * @return array of Detector2s
137138 * BCEL-only Detector interface. Detectors that do not support this
138139 * interface will not be created. Therefore, new code should use the
139140 * instantiateDetector2sInPass() method, which can support all detectors.
140 *
141 *
141142 * @param bugReporter
142143 * the BugReporter
143144 * @return array of Detectors
167168 }
168169 }
169170
170 // vim:ts=4
2424 /**
2525 * DetectorFactorySelector implementation that chooses detectors based on an
2626 * implemented interface or extended superclass.
27 *
27 *
2828 * @author David Hovemeyer
2929 */
3030 public class ByInterfaceDetectorFactorySelector implements DetectorFactorySelector {
31 private Plugin plugin;
31 private final Plugin plugin;
3232
33 private Class<?> detectorInterface;
33 private final Class<?> detectorInterface;
3434
3535 public ByInterfaceDetectorFactorySelector(Plugin plugin, Class<?> detectorInterface) {
3636 this.plugin = plugin;
3737 this.detectorInterface = detectorInterface;
3838 }
3939
40 @Override
4041 public boolean selectFactory(DetectorFactory factory) {
41 if (plugin != null && factory.getPlugin() != plugin)
42 if (plugin != null && factory.getPlugin() != plugin) {
4243 return false;
44 }
4345 return factory.isDetectorClassSubtypeOf(detectorInterface);
4446 }
4547 }
2323 /**
2424 * Edge in a ConstraintGraph. Edges flow from earlier detectors to later
2525 * detectors.
26 *
26 *
2727 * @see ConstraintGraph
2828 * @see DetectorNode
2929 * @see ExecutionPlan
3434
3535 /**
3636 * Constructor.
37 *
37 *
3838 * @param source
3939 * the source vertex (earlier Detector)
4040 * @param target
4646
4747 /**
4848 * Set the DetectorOrderingConstraint that created this edge.
49 *
49 *
5050 * @param constraint
5151 * the DetectorOrderingConstraint that created this edge
5252 */
5959 * constraint having a single detector as its source (ealier detector). Such
6060 * constraints automatically enable the source (earlier) detector if the
6161 * target (later) detector is enabled.
62 *
62 *
6363 * @return true if this edge has a single detector as its source (earlier
6464 * detector)
6565 */
6868 }
6969 }
7070
71 // vim:ts=4
2424 * Graph of Detector ordering constraints. It may represent ordering constraints
2525 * between analysis passes, or ordering constraints within a single pass. Edges
2626 * flow from earlier detectors to later detectors.
27 *
27 *
2828 * @see DetectorNode
2929 * @see ConstraintEdge
3030 * @see ExecutionPlan
4242 }
4343 }
4444
45 // vim:ts=4
2323 /**
2424 * Select one or more DetectorFactories as part of satisfying a Detector
2525 * ordering constraint.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public interface DetectorFactorySelector {
3030 /**
3131 * Is given DetectorFactory selected (as part of an ordering constraint)?
32 *
32 *
3333 * @param factory
3434 * a DetectorFactory
3535 * @return true if the factory is selected, false otherwise
2424 /**
2525 * Node in a ConstraintGraph. It represents a single Detector which must be
2626 * ordered before or after some other Detector(s).
27 *
27 *
2828 * @see ConstraintGraph
2929 * @see ConstraintEdge
3030 * @see ExecutionPlan
3131 * @author David Hovemeyer
3232 */
3333 public class DetectorNode extends AbstractVertex<ConstraintEdge, DetectorNode> {
34 private DetectorFactory factory;
34 private final DetectorFactory factory;
3535
3636 /**
3737 * Constructor.
38 *
38 *
3939 * @param factory
4040 * the DetectorFactory for the Detector this node represents
4141 */
5959 }
6060 }
6161
62 // vim:ts=4
2121 /**
2222 * An ordering constraint which must be taken into account when Detectors are
2323 * run.
24 *
24 *
2525 * @see edu.umd.cs.findbugs.Plugin
2626 * @see edu.umd.cs.findbugs.plan.ExecutionPlan
2727 * @author David Hovemeyer
2828 */
2929 public class DetectorOrderingConstraint {
30 private DetectorFactorySelector earlier;
30 private final DetectorFactorySelector earlier;
3131
32 private DetectorFactorySelector later;
32 private final DetectorFactorySelector later;
3333
3434 private boolean singleSource;
3535
5353 * ordering constraint having a single detector as its source (ealier
5454 * detector). Such constraints automatically enable the source (earlier)
5555 * detector if the target (later) detector is enabled.
56 *
56 *
5757 * @return true if this edge has a single detector as its source (earlier
5858 * detector)
5959 */
7575 }
7676 }
7777
78 // vim:ts=4
4444 * A plan for executing Detectors on an application. Automatically assigns
4545 * Detectors to passes and orders Detectors within each pass based on ordering
4646 * constraints specified in the plugin descriptor(s).
47 *
47 *
4848 * @author David Hovemeyer
4949 */
5050 public class ExecutionPlan {
7171 public ExecutionPlan() {
7272 this.pluginList = new LinkedList<Plugin>();
7373 this.factoryChooser = new DetectorFactoryChooser() {
74 @Override
7475 public boolean choose(DetectorFactory factory) {
7576 return true;
7677 }
7778
79 @Override
7880 public void enable(DetectorFactory factory) {
7981 // OK...
8082 }
231233 }
232234 appendDetectorsToPass(unassignedSet, lastPass);
233235 }
234 if (DEBUG)
236 if (DEBUG) {
235237 print();
238 }
236239 }
237240
238241 /**
244247
245248 /**
246249 * Get the number of passes in the execution plan.
247 *
250 *
248251 * @return the number of passes in the execution plan
249252 */
250253 public int getNumPasses() {
263266 * correct ordering of the detectors (which may mean either passes or an
264267 * ordering within a single pass, depending on whether the constraints are
265268 * inter-pass or intra-pass).
266 *
269 *
267270 * @param nodeMap
268271 * map to be populated with detector class names to constraint
269272 * graph nodes for those detectors
358361 while (incomingEdgeIterator.hasNext()) {
359362 ConstraintEdge edge = incomingEdgeIterator.next();
360363 System.out.println(" requires " + edge.getSource().getFactory().getShortName());
361
364
362365 }
363366 }
364367
483486 }
484487
485488 private void appendDetectorsToPass(Collection<DetectorFactory> detectorSet, AnalysisPass pass)
486 {
489 {
487490 DetectorFactory[] unassignedList = detectorSet.toArray(new DetectorFactory[detectorSet.size()]);
488491 Arrays.sort(unassignedList, new Comparator<DetectorFactory>() {
492 @Override
489493 public int compare(DetectorFactory a, DetectorFactory b) {
490494 // Sort first by plugin id...
491495 int cmp = a.getPlugin().getPluginId().compareTo(b.getPlugin().getPluginId());
542546 }
543547 }
544548
545 // vim:ts=4
2323 /**
2424 * An exception indicating that the detector ordering constraints specified by a
2525 * plugin are invalid.
26 *
26 *
2727 * @see DetectorOrderingConstraint
2828 * @see ExecutionPlan
2929 * @author David Hovemeyer
3636
3737 /**
3838 * Constructor.
39 *
39 *
4040 * @param msg
4141 * the message describing the exception
4242 */
4545 }
4646 }
4747
48 // vim:ts=4
2323
2424 /**
2525 * Select all detector factories for reporting detectors.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class ReportingDetectorFactorySelector implements DetectorFactorySelector {
30 private Plugin plugin;
30 private final Plugin plugin;
3131
3232 /**
3333 * Constructor.
34 *
34 *
3535 * @param plugin
3636 * Plugin containing detector factories to be selected; if null,
3737 * factories from any Plugin may be selected
4242
4343 /*
4444 * (non-Javadoc)
45 *
45 *
4646 * @see
4747 * edu.umd.cs.findbugs.plan.DetectorFactorySelector#selectFactory(edu.umd
4848 * .cs.findbugs.DetectorFactory)
4949 */
50 @Override
5051 public boolean selectFactory(DetectorFactory factory) {
5152 return (plugin == null || plugin == factory.getPlugin()) && factory.isReportingDetector();
5253 }
2323
2424 /**
2525 * Select a detector factory for a specific detector class.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class SingleDetectorFactorySelector implements DetectorFactorySelector {
30 private Plugin plugin;
30 private final Plugin plugin;
3131
32 private String className;
32 private final String className;
3333
3434 public SingleDetectorFactorySelector(Plugin plugin, String className) {
3535 this.plugin = plugin;
3636 this.className = className;
3737 }
3838
39 @Override
3940 public boolean selectFactory(DetectorFactory factory) {
4041 return plugin == factory.getPlugin()
4142 && (factory.getFullName().equals(className) || factory.getShortName().equals(className));
2020
2121 /**
2222 * Abstract base class for implementing warning properties.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public abstract class AbstractWarningProperty implements WarningProperty {
27 private String shortName;
27 private final String shortName;
2828
29 private PriorityAdjustment priorityAdjustment;
29 private final PriorityAdjustment priorityAdjustment;
3030
3131 /**
3232 * Constructor.
33 *
33 *
3434 * @param shortName
3535 * the short name of the property; will be qualified with the
3636 * full name of the warning property class
4444
4545 /*
4646 * (non-Javadoc)
47 *
47 *
4848 * @see edu.umd.cs.findbugs.props.WarningProperty#getPriorityAdjustment()
4949 */
50 @Override
5051 public PriorityAdjustment getPriorityAdjustment() {
5152 return priorityAdjustment;
5253 }
5354
5455 /*
5556 * (non-Javadoc)
56 *
57 *
5758 * @see edu.umd.cs.findbugs.props.WarningProperty#getName()
5859 */
60 @Override
5961 public String getName() {
6062 return this.getClass().getName() + "." + shortName;
6163 }
2222 * any warning to provide information which might be useful in determining
2323 * whether or not the bug is a false positive, and/or the severity of the
2424 * warning.
25 *
25 *
2626 * @author David Hovemeyer
2727 */
2828 public class GeneralWarningProperty extends AbstractWarningProperty {
2020 /**
2121 * Enum representing how a particular warning property is expected to affect its
2222 * likelihood of being serious, benign, or a false positive.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public class PriorityAdjustment {
27 private String value;
27 private final String value;
2828
2929 private PriorityAdjustment(String value) {
3030 this.value = value;
1919
2020 /**
2121 * Interface that all warning property enumerations are expected to implement.
22 *
22 *
2323 * @author David Hovemeyer
2424 */
2525 public interface WarningProperty {
2727 * Get the priority adjustment: i.e., the effect that this warning property
2828 * is expected to have on the likelihood that the warning is real, benign,
2929 * or a false positive.
30 *
30 *
3131 * @return the priority adjustment
3232 */
3333 public abstract PriorityAdjustment getPriorityAdjustment();
3535 /**
3636 * Get the fully qualified name of the property. Should be full class name,
3737 * ".", followed by the descriptive name of the property.
38 *
38 *
3939 * @return fully qualified name of the property
4040 */
4141 public abstract String getName();
190190 } else if (adj == PriorityAdjustment.FALSE_POSITIVE) {
191191 falsePositive = true;
192192 atMostLow = true;
193 } else if (adj == PriorityAdjustment.A_LITTLE_BIT_LOWER_PRIORITY)
193 } else if (adj == PriorityAdjustment.A_LITTLE_BIT_LOWER_PRIORITY) {
194194 aLittleBitLower++;
195 else if (adj == PriorityAdjustment.A_LITTLE_BIT_HIGHER_PRIORITY)
195 } else if (adj == PriorityAdjustment.A_LITTLE_BIT_HIGHER_PRIORITY) {
196196 aLittleBitLower--;
197 else if (adj == PriorityAdjustment.RAISE_PRIORITY)
197 } else if (adj == PriorityAdjustment.RAISE_PRIORITY) {
198198 --priority;
199 else if (adj == PriorityAdjustment.RAISE_PRIORITY_TO_AT_LEAST_NORMAL) {
199 } else if (adj == PriorityAdjustment.RAISE_PRIORITY_TO_AT_LEAST_NORMAL) {
200200 --priority;
201201 atLeastMedium = true;
202202 } else if (adj == PriorityAdjustment.LOWER_PRIORITY_TO_AT_MOST_NORMAL) {
214214 atMostMedium = true;
215215 } else if (adj == PriorityAdjustment.NO_ADJUSTMENT) {
216216 assert true; // do nothing
217 } else
217 } else {
218218 throw new IllegalStateException("Unknown priority " + adj);
219
220 }
221
222 if (peggedHigh && !falsePositive)
219 }
220
221 }
222
223 if (peggedHigh && !falsePositive) {
223224 return Priorities.HIGH_PRIORITY;
224 if (aLittleBitLower >= 3 || priority == 1 && aLittleBitLower == 2)
225 }
226 if (aLittleBitLower >= 3 || priority == 1 && aLittleBitLower == 2) {
225227 priority++;
226 else if (aLittleBitLower <= -2)
228 } else if (aLittleBitLower <= -2) {
227229 priority--;
228 if (atMostMedium)
230 }
231 if (atMostMedium) {
229232 priority = Math.max(Priorities.NORMAL_PRIORITY, priority);
230
231 if (falsePositive && !atLeastMedium)
233 }
234
235 if (falsePositive && !atLeastMedium) {
232236 return Priorities.EXP_PRIORITY + 1;
233 else if (atMostLow)
237 } else if (atMostLow) {
234238 return Math.min(Math.max(Priorities.LOW_PRIORITY, priority), Priorities.EXP_PRIORITY);
235 if (atLeastMedium && priority > Priorities.NORMAL_PRIORITY)
239 }
240 if (atLeastMedium && priority > Priorities.NORMAL_PRIORITY) {
236241 priority = Priorities.NORMAL_PRIORITY;
237
238 if (priority < Priorities.HIGH_PRIORITY)
242 }
243
244 if (priority < Priorities.HIGH_PRIORITY) {
239245 priority = Priorities.HIGH_PRIORITY;
240 else if (priority > Priorities.EXP_PRIORITY)
246 } else if (priority > Priorities.EXP_PRIORITY) {
241247 priority = Priorities.EXP_PRIORITY;
248 }
242249 }
243250
244251 return priority;
269276 for (Map.Entry<T, Object> entry : map.entrySet()) {
270277 WarningProperty prop = entry.getKey();
271278 Object attribute = entry.getValue();
272 if (attribute == null)
279 if (attribute == null) {
273280 attribute = "";
281 }
274282 bugInstance.setProperty(prop.getName(), attribute.toString());
275283 }
276284 }
3939
4040 /**
4141 * Utility methods for creating general warning properties.
42 *
42 *
4343 * @author David Hovemeyer
4444 */
4545 public abstract class WarningPropertyUtil {
6060 * Get a Location matching the given PC value. Because of JSR subroutines,
6161 * there may be multiple Locations referring to the given instruction. This
6262 * method simply returns one of them arbitrarily.
63 *
63 *
6464 * @param classContext
6565 * the ClassContext containing the method
6666 * @param method
7575 CFG cfg = classContext.getCFG(method);
7676 for (Iterator<Location> i = cfg.locationIterator(); i.hasNext();) {
7777 Location location = i.next();
78 if (location.getHandle().getPosition() == pc)
78 if (location.getHandle().getPosition() == pc) {
7979 return location;
80 }
8081 }
8182 return null;
8283 }
8485 /**
8586 * Add a RECEIVER_OBJECT_TYPE warning property for a particular location in
8687 * a method to given warning property set.
87 *
88 *
8889 * @param propertySet
8990 * the property set
9091 * @param classContext
99100 try {
100101 Instruction ins = location.getHandle().getInstruction();
101102
102 if (!receiverObjectInstructionSet.get(ins.getOpcode()))
103 if (!receiverObjectInstructionSet.get(ins.getOpcode())) {
103104 return;
105 }
104106
105107 TypeDataflow typeDataflow = classContext.getTypeDataflow(method);
106108 TypeFrame frame = typeDataflow.getFactAtLocation(location);
120122 /**
121123 * Add CALLED_METHOD_<i>n</i> warning properties based on methods which have
122124 * been called and returned normally at given Location.
123 *
125 *
124126 * @param propertySet
125127 * the WarningPropertySet
126128 * @param classContext
135137 try {
136138 CallListDataflow dataflow = classContext.getCallListDataflow(method);
137139 CallList callList = dataflow.getFactAtLocation(location);
138 if (!callList.isValid())
140 if (!callList.isValid()) {
139141 return;
142 }
140143 int count = 0;
141144 for (Iterator<Call> i = callList.callIterator(); count < 4 && i.hasNext(); ++count) {
142145 Call call = i.next();
169172 /**
170173 * Add all relevant general warning properties to the given property set for
171174 * the given Location.
172 *
175 *
173176 * @param propertySet
174177 * the WarningPropertySet
175178 * @param classContext
188191 /**
189192 * Add all relevant general warning properties to the given property set for
190193 * the given Location.
191 *
194 *
192195 * @param propertySet
193196 * the WarningPropertySet
194197 * @param classContext
3838 import edu.umd.cs.findbugs.Plugin;
3939 import edu.umd.cs.findbugs.SystemProperties;
4040 import edu.umd.cs.findbugs.Version;
41 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
4142 import edu.umd.cs.findbugs.util.MultiMap;
4243 import edu.umd.cs.findbugs.util.Util;
4344 import edu.umd.cs.findbugs.xml.OutputStreamXMLOutput;
8687 startUpdateCheckThread(uri, pluginsByUrl.get(uri), latch);
8788 }
8889 }
89
90
9091 waitForCompletion(latch, force);
9192 }
9293
93 /**
94 * @param force
95 * @return
96 */
9794 public @CheckForNull URI getRedirectURL(final boolean force) {
9895 String redirect = dfc.getGlobalOption(KEY_REDIRECT_ALL_UPDATE_CHECKS);
9996 String sysprop = System.getProperty("findbugs.redirectUpdateChecks");
100 if (sysprop != null)
97 if (sysprop != null) {
10198 redirect = sysprop;
99 }
102100 Plugin setter = dfc.getGlobalOptionSetter(KEY_REDIRECT_ALL_UPDATE_CHECKS);
103101 URI redirectUri = null;
104102 String pluginName = setter == null ? "<unknown plugin>" : setter.getShortDescription();
105 if (redirect != null && !redirect.trim().equals("")) {
103 if (redirect != null && !"".equals(redirect.trim())) {
106104 try {
107105 redirectUri = new URI(redirect);
108106 logError(Level.INFO, "Redirecting all plugin update checks to " + redirectUri + " (" + pluginName + ")");
109
107
110108 } catch (URISyntaxException e) {
111109 String error = "Invalid update check redirect URI in " + pluginName + ": " + redirect;
112110 logError(Level.SEVERE, error);
119117
120118 private long dontWarnAgainUntil() {
121119 Preferences prefs = Preferences.userNodeForPackage(UpdateChecker.class);
122
120
123121 String oldSeen = prefs.get("last-plugin-update-seen", "");
124 if (oldSeen == null || oldSeen.equals(""))
122 if (oldSeen == null || "".equals(oldSeen)) {
125123 return 0;
124 }
126125 try {
127 return Long.parseLong(oldSeen) + DONT_REMIND_WINDOW;
126 return Long.parseLong(oldSeen) + DONT_REMIND_WINDOW;
128127 } catch (Exception e) {
129128 return 0;
130129 }
134133 long now = System.currentTimeMillis();
135134 Preferences prefs = Preferences.userNodeForPackage(UpdateChecker.class);
136135 String oldHash = prefs.get("last-plugin-update-hash", "");
137
136
138137 String newHash = Integer.toString(buildPluginUpdateHash(updates));
139138 if (oldHash.equals(newHash) && dontWarnAgainUntil() > now) {
140139 LOGGER.fine("Skipping update dialog because these updates have been seen before");
152151 }
153152 return builder.hashCode();
154153 }
155
154
156155 private void waitForCompletion(final CountDownLatch latch, final boolean force) {
157156 Util.runInDameonThread(new Runnable() {
157 @Override
158158 public void run() {
159 if (DEBUG)
159 if (DEBUG) {
160160 System.out.println("Checking for version updates");
161 }
161162 try {
162163 if (! latch.await(15, TimeUnit.SECONDS)) {
163164 logError(Level.INFO, "Update check timed out");
200201 final String entryPoint = getEntryPoint();
201202 if ((entryPoint.contains("edu.umd.cs.findbugs.FindBugsTestCase")
202203 || entryPoint.contains("edu.umd.cs.findbugs.cloud.appEngine.AbstractWebCloudTest"))
203 && (url.getScheme().equals("http") || url.getScheme().equals("https"))) {
204 && ("http".equals(url.getScheme()) || "https".equals(url.getScheme()))) {
204205 LOGGER.fine("Skipping update check because we're running in FindBugsTestCase and using "
205206 + url.getScheme());
206207 return;
207208 }
208209 Runnable r = new Runnable() {
210 @Override
209211 public void run() {
210212 try {
211213 actuallyCheckforUpdates(url, plugins, entryPoint);
212214 } catch (Exception e) {
213 if (e instanceof IllegalStateException && e.getMessage().contains("Shutdown in progress"))
215 if (e instanceof IllegalStateException && e.getMessage().contains("Shutdown in progress")) {
214216 return;
217 }
215218 logError(e, "Error doing update check at " + url);
216219 } finally {
217220 latch.countDown();
218221 }
219222 }
220223 };
221 if (DEBUG)
224 if (DEBUG) {
222225 r.run();
223 else
226 } else {
224227 Util.runInDameonThread(r, "Check for updates");
228 }
225229 }
226230
227231 static final boolean DEBUG = SystemProperties.getBoolean("findbugs.updatecheck.debug");
228232 /** protected for testing */
229233 protected void actuallyCheckforUpdates(URI url, Collection<Plugin> plugins, String entryPoint) throws IOException {
230234 LOGGER.fine("Checking for updates at " + url + " for " + getPluginNames(plugins));
231 if (DEBUG)
235 if (DEBUG) {
232236 System.out.println(url);
237 }
233238 HttpURLConnection conn = (HttpURLConnection) url.toURL().openConnection();
234239 conn.setDoInput(true);
235240 conn.setDoOutput(true);
246251 if (responseCode != 200) {
247252 logError(SystemProperties.ASSERTIONS_ENABLED ? Level.WARNING : Level.FINE,
248253 "Error checking for updates at " + url + ": "
249 + responseCode + " - " + conn.getResponseMessage());
254 + responseCode + " - " + conn.getResponseMessage());
250255 } else {
251256 parseUpdateXml(url, plugins, conn.getInputStream());
252257 }
254259 }
255260
256261 /** protected for testing */
262 @SuppressFBWarnings("OBL_UNSATISFIED_OBLIGATION")
257263 protected final void writeXml(OutputStream out, Collection<Plugin> plugins, String entryPoint,
258264 boolean finish) throws IOException {
259265 OutputStreamXMLOutput xmlOutput = new OutputStreamXMLOutput(out);
263269 xmlOutput.startTag("findbugs-invocation");
264270 xmlOutput.addAttribute("version", Version.RELEASE);
265271 String applicationName = Version.getApplicationName();
266 if (applicationName == null || applicationName.equals("")) {
272 if (applicationName == null || "".equals(applicationName)) {
267273 int lastDot = entryPoint.lastIndexOf('.');
268 if (lastDot == -1)
274 if (lastDot == -1) {
269275 applicationName = entryPoint;
270 else
276 } else {
271277 applicationName = entryPoint.substring(lastDot + 1);
278 }
272279 }
273280 xmlOutput.addAttribute("app-name", applicationName);
274281 String applicationVersion = Version.getApplicationVersion();
275 if (applicationVersion == null)
282 if (applicationVersion == null) {
276283 applicationVersion = "";
284 }
277285 xmlOutput.addAttribute("app-version", applicationVersion);
278286 xmlOutput.addAttribute("entry-point", entryPoint);
279287 xmlOutput.addAttribute("os", SystemProperties.getProperty("os.name", ""));
289297 xmlOutput.addAttribute("name", plugin.getShortDescription());
290298 xmlOutput.addAttribute("version", plugin.getVersion());
291299 Date date = plugin.getReleaseDate();
292 if (date != null)
300 if (date != null) {
293301 xmlOutput.addAttribute("release-date", Long.toString(date.getTime()));
302 }
294303 xmlOutput.stopTag(true);
295304 }
296305
297306 xmlOutput.closeTag("findbugs-invocation");
298307 xmlOutput.flush();
299308 } finally {
300 if (finish)
309 if (finish) {
301310 xmlOutput.finish();
311 }
302312 }
303313 }
304314
305315 // package-private for testing
306316 @SuppressWarnings({ "unchecked" })
307317 void parseUpdateXml(URI url, Collection<Plugin> plugins, @WillClose
308 InputStream inputStream) {
318 InputStream inputStream) {
309319 try {
310320 Document doc = new SAXReader().read(inputStream);
311321 if (DEBUG) {
317327 }
318328 List<Element> pluginEls = XMLUtil.selectNodes(doc, "fb-plugin-updates/plugin");
319329 Map<String, Plugin> map = new HashMap<String, Plugin>();
320 for (Plugin p : plugins)
330 for (Plugin p : plugins) {
321331 map.put(p.getPluginId(), p);
332 }
322333 for (Element pluginEl : pluginEls) {
323334 String id = pluginEl.attributeValue("id");
324335 Plugin plugin = map.get(id);
344355 private void checkPluginRelease(Plugin plugin, Element maxEl) {
345356 @CheckForNull Date updateDate = parseReleaseDate(maxEl);
346357 @CheckForNull Date installedDate = plugin.getReleaseDate();
347 if (updateDate != null && installedDate != null && updateDate.before(installedDate))
358 if (updateDate != null && installedDate != null && updateDate.before(installedDate)) {
348359 return;
360 }
349361 String version = maxEl.attributeValue("version");
350 if (version.equals(plugin.getVersion()))
362 if (version.equals(plugin.getVersion())) {
351363 return;
364 }
352365
353366 String url = maxEl.attributeValue("url");
354367 String message = maxEl.element("message").getTextTrim();
369382 private @CheckForNull Date parseReleaseDate(Element releaseEl) {
370383 SimpleDateFormat format = new SimpleDateFormat(PLUGIN_RELEASE_DATE_FMT);
371384 String dateStr = releaseEl.attributeValue("date");
372 if (dateStr == null)
385 if (dateStr == null) {
373386 return null;
387 }
374388 try {
375389 return format.parse(dateStr);
376 } catch (Exception e) {
377 throw new IllegalArgumentException("Error parsing " + dateStr, e);
390 } catch (java.text.ParseException e) {
391 throw new IllegalArgumentException("Error parsing " + dateStr + " using " + PLUGIN_RELEASE_DATE_FMT, e);
378392 }
379393 }
380394
398412 }
399413 return lastFbClass;
400414 }
401
415
402416 /** Should only be used once */
403417 private static Random random = new Random();
404418
420434 String ver = SystemProperties.getProperty("java.version", "");
421435 Matcher m = Pattern.compile("^\\d+\\.\\d+").matcher(ver);
422436 if (m.find()) {
423 return m.group();
437 return m.group();
424438 }
425439 return "";
426440 }
459473 public @Nonnull String getMessage() {
460474 return message;
461475 }
462
476
463477 @Override
464478 public String toString() {
465479 SimpleDateFormat format = new SimpleDateFormat(PLUGIN_RELEASE_DATE_FMT);
466480 StringBuilder buf = new StringBuilder();
467481 String name = getPlugin().isCorePlugin() ? "FindBugs" : "FindBugs plugin " + getPlugin().getShortDescription();
468482 buf.append( name + " " + getVersion() );
469 if (date == null)
483 if (date == null) {
470484 buf.append(" has been released");
471 else
485 } else {
472486 buf.append(" was released " + format.format(date));
487 }
473488 buf.append(
474489 " (you have " + getPlugin().getVersion()
475490 + ")");
477492
478493 buf.append(" " + message.replaceAll("\n", "\n "));
479494
480 if (url != null)
495 if (url != null) {
481496 buf.append("\nVisit " + url + " for details.");
497 }
482498 return buf.toString();
483499 }
484500 }
485
501
486502 public static void main(String args[]) throws Exception {
487503 FindBugs.setNoAnalysis();
488504 DetectorFactoryCollection dfc = DetectorFactoryCollection.instance();
489505 UpdateChecker checker = dfc.getUpdateChecker();
490 if (checker.updateChecksGloballyDisabled())
506 if (checker.updateChecksGloballyDisabled()) {
491507 System.out.println("Update checkes are globally disabled");
508 }
492509 URI redirect = checker.getRedirectURL(false);
493 if (redirect != null)
510 if (redirect != null) {
494511 System.out.println("All update checks redirected to " + redirect);
512 }
495513 checker.writeXml(System.out, dfc.plugins(), "UpdateChecker", true);
496
497
514
515
498516 }
499517 }
2222
2323 /**
2424 * A TypeMatcher that matches all types.
25 *
25 *
2626 * @author David Hovemeyer
2727 */
2828 public class AnyTypeMatcher implements TypeMatcher {
2929
30 @Override
3031 public boolean matches(Type t) {
3132 return true;
3233 }
5858 return ARCHIVE_EXTENSION_SET.contains(extension);
5959 }
6060
61 /**
62 * @param fileName
63 * @return
64 */
6561 private static String getExtension(String fileName) {
6662 int lastDot = fileName.lastIndexOf('.');
6763 if (lastDot < 0) {
7066 String extension = fileName.substring(lastDot).toLowerCase(Locale.ENGLISH);
7167 return extension;
7268 }
73
69
7470 public static boolean isLibraryFileName(String fileName) {
7571 String extension = getExtension(fileName);
76 return extension.equals(".jar");
72 return ".jar".equals(extension);
7773 }
7874 }
2525
2626 /**
2727 * Simple implementation of a Bag
28 *
28 *
2929 * @author pugh
3030 */
3131 public class Bag<E> {
4141
4242 public boolean add(E e) {
4343 Integer v = map.get(e);
44 if (v == null)
44 if (v == null) {
4545 map.put(e, 1);
46 else
46 } else {
4747 map.put(e, v + 1);
48 }
4849 return true;
4950 }
5051
5152 public boolean add(E e, int count) {
5253 Integer v = map.get(e);
53 if (v == null)
54 if (v == null) {
5455 map.put(e, count);
55 else
56 } else {
5657 map.put(e, v + count);
58 }
5759 return true;
5860 }
5961
6769
6870 public int getCount(E e) {
6971 Integer v = map.get(e);
70 if (v == null)
72 if (v == null) {
7173 return 0;
72 else
74 } else {
7375 return v;
76 }
7477 }
7578
7679 }
2121 import javax.annotation.CheckForNull;
2222 import javax.annotation.meta.When;
2323
24 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
2425 import edu.umd.cs.findbugs.classfile.DescriptorFactory;
2526 import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
2627 import edu.umd.cs.findbugs.internalAnnotations.SlashedClassName;
3334 public abstract class ClassName {
3435
3536 public static boolean isMathClass(@SlashedClassName String className) {
36 return className.equals("java/lang/Math") || className.equals("java/lang/StrictMath");
37 return "java/lang/Math".equals(className) || "java/lang/StrictMath".equals(className);
3738 }
3839
3940 public static @DottedClassName String assertIsDotted(@DottedClassName String className) {
4546 return className;
4647 }
4748 public static String toSignature(@SlashedClassName String className) {
48 if (className.length() == 0)
49 if (className.length() == 0) {
4950 throw new IllegalArgumentException("classname can't be empty");
50 if (className.charAt(0) == '[' || className.endsWith(";"))
51 }
52 if (className.charAt(0) == '[' || className.endsWith(";")) {
5153 return className;
54 }
5255 return "L" + className + ";";
5356 }
5457
5558 public static @CheckForNull String getPrimitiveType(@SlashedClassName String cls) {
56 if (!cls.startsWith("java/lang/")) return null;
59 if (!cls.startsWith("java/lang/")) {
60 return null;
61 }
5762 cls = cls.substring(10);
58 if (cls.equals("Integer")) return "I";
59 if (cls.equals("Float")) return "F";
60 if (cls.equals("Double")) return "D";
61 if (cls.equals("Long")) return "J";
62 if (cls.equals("Byte")) return "B";
63 if (cls.equals("Character")) return "C";
64 if (cls.equals("Short")) return "S";
65 if (cls.equals("Boolean")) return "Z";
63 if ("Integer".equals(cls)) {
64 return "I";
65 }
66 if ("Float".equals(cls)) {
67 return "F";
68 }
69 if ("Double".equals(cls)) {
70 return "D";
71 }
72 if ("Long".equals(cls)) {
73 return "J";
74 }
75 if ("Byte".equals(cls)) {
76 return "B";
77 }
78 if ("Character".equals(cls)) {
79 return "C";
80 }
81 if ("Short".equals(cls)) {
82 return "S";
83 }
84 if ("Boolean".equals(cls)) {
85 return "Z";
86 }
6687 return null;
6788 }
68
89
6990 /**
7091 * Converts from signature to slashed class name
7192 * (e.g., from Ljava/lang/String; to java/lang/String).
81102 return signature.substring(1, signature.length() - 1);
82103 }
83104
84
85105 /**
86106 * Convert class name to slashed format. If the class name is already in
87107 * slashed format, it is returned unmodified.
90110 * a class name
91111 * @return the same class name in slashed format
92112 */
93 public static @SlashedClassName
94 String toSlashedClassName(@SlashedClassName(when = When.UNKNOWN) String className) {
113 @SlashedClassName
114 @SuppressFBWarnings("TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_ALWAYS_SINK")
115 public static String toSlashedClassName(@SlashedClassName(when = When.UNKNOWN) String className) {
95116 if (className.indexOf('.') >= 0) {
96117 return DescriptorFactory.canonicalizeString(className.replace('.', '/'));
97118 }
106127 * a class name
107128 * @return the same class name in dotted format
108129 */
109 public static @DottedClassName
110 String toDottedClassName(@SlashedClassName(when = When.UNKNOWN) String className) {
130 @DottedClassName
131 @SuppressFBWarnings("TQ_EXPLICIT_UNKNOWN_SOURCE_VALUE_REACHES_NEVER_SINK")
132 public static String toDottedClassName(@SlashedClassName(when = When.UNKNOWN) String className) {
111133 if (className.indexOf('/') >= 0) {
112134 return DescriptorFactory.canonicalizeString(className.replace('/', '.'));
113135 }
125147 public static @DottedClassName
126148 String extractPackageName(@DottedClassName String className) {
127149 int i = className.lastIndexOf('.');
128 if (i < 0)
150 if (i < 0) {
129151 return "";
152 }
130153 return className.substring(0, i);
131154 }
132155
133156 public static String extractSimpleName(@DottedClassName String className) {
134157 int i = className.lastIndexOf('.');
135 if (i > 0)
158 if (i > 0) {
136159 className = className.substring(i + 1);
137 className = className.replace('$', '.');
160 }
161 // to be consistent with the Class.getSimpleName(),
162 // simple class name does not! contain the enclosing class name
163 i = className.lastIndexOf('$');
164 if (i > 0) {
165 className = className.substring(i + 1);
166 }
167 // can be empty!
138168 return className;
139169 }
140170
180210 public static @SlashedClassName
181211 String extractClassName(String originalName) {
182212 String name = originalName;
183 if (name.charAt(0) != '[' && name.charAt(name.length() - 1) != ';')
213 if (name.charAt(0) != '[' && name.charAt(name.length() - 1) != ';') {
184214 return name;
185 while (name.charAt(0) == '[')
215 }
216 while (name.charAt(0) == '[') {
186217 name = name.substring(1);
187 if (name.charAt(0) == 'L' && name.charAt(name.length() - 1) == ';')
218 }
219 if (name.charAt(0) == 'L' && name.charAt(name.length() - 1) == ';') {
188220 name = name.substring(1, name.length() - 1);
189 if (name.charAt(0) == '[')
221 }
222 if (name.charAt(0) == '[') {
190223 throw new IllegalArgumentException("Bad class name: " + originalName);
224 }
191225 return name;
192226 }
193227
196230 int prefixLength = 0;
197231 while (dotsSeen < count) {
198232 int p = packageName.indexOf('.', prefixLength);
199 if (p < 0)
233 if (p < 0) {
200234 return packageName;
235 }
201236 prefixLength = p + 1;
202237 dotsSeen++;
203238 }
204 if (prefixLength == 0)
239 if (prefixLength == 0) {
205240 return "";
241 }
206242 return packageName.substring(0, prefixLength - 1);
207243 }
208244
209245 public static boolean matchedPrefixes(String[] classSearchStrings, @DottedClassName String className) {
210246 String[] pp = classSearchStrings;
211 if (pp == null || pp.length == 0)
247 if (pp == null || pp.length == 0) {
212248 return true;
213
214 for (String p : pp)
215 if (p.length() > 0 && className.indexOf(p) >= 0)
249 }
250
251 for (String p : pp) {
252 if (p.length() > 0 && className.indexOf(p) >= 0) {
216253 return true;
254 }
255 }
217256
218257 return false;
219258
2727
2828 /**
2929 * Some utility methods for working with the Java class path.
30 *
30 *
3131 * @author David Hovemeyer
3232 */
3333 public class ClassPathUtil {
3434 /**
3535 * Try to find a codebase with the given name in the given class path
3636 * string.
37 *
37 *
3838 * @param codeBaseName
3939 * name of a codebase (e.g., "findbugs.jar")
4040 * @param classPath
6262 /**
6363 * Try to find a codebase matching the given pattern in the given class path
6464 * string.
65 *
65 *
6666 * @param codeBaseNamePattern
6767 * pattern describing a codebase (e.g., compiled from the regex
6868 * "findbugs\\.jar$")
2525 /**
2626 * A StringMatcher that checks to see if a candidate string (assumed to be a
2727 * camel-case word), when broken into components, contains a given word.
28 *
28 *
2929 * @author David Hovemeyer
3030 */
3131 public class ContainsCamelCaseWordStringMatcher implements StringMatcher {
32 private String expected;
32 private final String expected;
3333
3434 /**
3535 * Constructor. This StringMatcher will match any string which, when broken
3636 * into camel-case identifier components, has a component which matches the
3737 * (lower-cased) expected string value.
38 *
38 *
3939 * @param expected
4040 * the expected string value
4141 */
4343 this.expected = expected.toLowerCase(Locale.ENGLISH);
4444 }
4545
46 @Override
4647 public boolean matches(String s) {
4748 SplitCamelCaseIdentifier splitter = new SplitCamelCaseIdentifier(s);
4849 Collection<String> components = splitter.split();
3131
3232 public V get(K1 k1, K2 k2) {
3333 Map<K2, V> m = map.get(k1);
34 if (m == null)
34 if (m == null) {
3535 return null;
36 }
3637 return m.get(k2);
3738 }
3839
3940 public boolean containsKey(K1 k1, K2 k2) {
4041 Map<K2, V> m = map.get(k1);
41 if (m == null)
42 if (m == null) {
4243 return false;
44 }
4345 return m.containsKey(k2);
4446 }
4547
4648 public Map<K2, V> get(K1 k1) {
4749 Map<K2, V> m = map.get(k1);
48 if (m == null)
50 if (m == null) {
4951 return Collections.<K2, V> emptyMap();
52 }
5053 return m;
5154 }
5255
6063 m = Collections.singletonMap(k2, v);
6164 map.put(k1, m);
6265 return null;
63 } else if (m instanceof HashMap)
66 } else if (m instanceof HashMap) {
6467 return m.put(k2, v);
65 else {
68 } else {
6669 m = Util.makeSmallHashMap(m);
6770 map.put(k1, m);
6871 return m.put(k2, v);
2828 private static final int INSERT_OR_DELETE_COST = 2;
2929
3030 private static int minimum(int a, int b, int c) {
31 if (a > b)
31 if (a > b) {
3232 return Math.min(b, c);
33 }
3334 return Math.min(a, c);
3435 }
3536
3637 private static int distance(char a, char b) {
37 if (a == b)
38 if (a == b) {
3839 return 0;
39 if (Character.toLowerCase(a) == Character.toLowerCase(b))
40 }
41 if (Character.toLowerCase(a) == Character.toLowerCase(b)) {
4042 return 1;
43 }
4144 return 2;
4245 }
4346
5154 int n1 = str1.length();
5255 int n2 = str2.length();
5356 int diff = Math.abs(n1 - n2);
54 if (diff > 6)
57 if (diff > 6) {
5558 return INSERT_OR_DELETE_COST * Math.max(n1, n2);
59 }
5660 return editDistance1(str1, str2);
5761
5862 }
6771 distance[i] = new int[n2 + 1];
6872 distance[i][0] = INSERT_OR_DELETE_COST * i;
6973 }
70 for (int j = 1; j <= n2; j++)
74 for (int j = 1; j <= n2; j++) {
7175 distance[0][j] = INSERT_OR_DELETE_COST * j;
76 }
7277
73 for (int i = 1; i <= n1; i++)
74 for (int j = 1; j <= n2; j++)
78 for (int i = 1; i <= n1; i++) {
79 for (int j = 1; j <= n2; j++) {
7580 distance[i][j] = minimum(distance[i - 1][j] + INSERT_OR_DELETE_COST, distance[i][j - 1] + INSERT_OR_DELETE_COST,
7681 distance[i - 1][j - 1] + distance(str1.charAt(i - 1), str2.charAt(j - 1)));
82 }
83 }
7784
7885 return distance[n1][n2];
7986 }
8592 int[] distance = new int[n2 + 1];
8693 int[] oldDistance = new int[n2 + 1];
8794
88 for (int j = 1; j <= n2; j++)
95 for (int j = 1; j <= n2; j++) {
8996 oldDistance[j] = INSERT_OR_DELETE_COST * j;
97 }
9098
9199 for (int i = 1; i <= n1; i++) {
92100 distance[0] = INSERT_OR_DELETE_COST * i;
93 for (int j = 1; j <= n2; j++)
101 for (int j = 1; j <= n2; j++) {
94102 distance[j] = minimum(oldDistance[j] + INSERT_OR_DELETE_COST, distance[j - 1] + INSERT_OR_DELETE_COST,
95103 oldDistance[j - 1] + distance(str1.charAt(i - 1), str2.charAt(j - 1)));
104 }
96105 int[] tmp = oldDistance;
97106 oldDistance = distance;
98107 distance = tmp;
2020
2121 /**
2222 * Exact String-matching predicate.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public class ExactStringMatcher implements StringMatcher {
2828
2929 /**
3030 * Constructor.
31 *
31 *
3232 * @param expected
3333 * the expected string value
3434 */
3636 this.expected = expected;
3737 }
3838
39 @Override
3940 public boolean matches(String s) {
4041 return this.expected.equals(s);
4142 }
2525 import java.util.Map.Entry;
2626 import java.util.TreeSet;
2727
28 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
29
2830 /**
2931 * @author pwilliam
3032 */
4951
5052 public void add(K k, double val) {
5153 Double v = map.get(k);
52 if (v == null)
54 if (v == null) {
5355 map.put(k, val);
54 else
56 } else {
5557 map.put(k, v + val);
58 }
5659 }
5760
5861 public double getValue(K k) {
5962 Double v = map.get(k);
60 if (v == null)
63 if (v == null) {
6164 return 0;
65 }
6266 return v;
6367 }
6468
6569 public void turnTotalIntoAverage(Multiset<K> counts) {
6670 for (Map.Entry<K, Double> e : map.entrySet()) {
6771 int count = counts.getCount(e.getKey());
68 if (count == 0)
72 if (count == 0) {
6973 e.setValue(Double.NaN);
70 else
74 } else {
7175 e.setValue(e.getValue() / count);
76 }
7277
7378 }
7479 }
7782 return map.entrySet();
7883 }
7984
85 @SuppressFBWarnings("DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS")
8086 public Iterable<Map.Entry<K, Double>> entriesInDecreasingOrder() {
8187 TreeSet<Map.Entry<K, Double>> result = new TreeSet<Map.Entry<K, Double>>(new DecreasingOrderEntryComparator<K>());
8288 result.addAll(map.entrySet());
83 if (result.size() != map.size())
84 throw new IllegalStateException("Map " + map.getClass().getSimpleName()
89 if (result.size() != map.size()) {
90 throw new IllegalStateException("Map " + map.getClass().getSimpleName()
8591 + " reuses Map.Entry objects; entrySet can't be passed to addAll");
92 }
8693 return result;
8794 }
88
89
95
96 @SuppressFBWarnings("DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS")
9097 public Iterable<Map.Entry<K, Double>> entriesInIncreasingOrder() {
9198 TreeSet<Map.Entry<K, Double>> result = new TreeSet<Map.Entry<K, Double>>(new DecreasingOrderEntryComparator<K>());
9299 result.addAll(map.entrySet());
93 if (result.size() != map.size())
94 throw new IllegalStateException("Map " + map.getClass().getSimpleName()
100 if (result.size() != map.size()) {
101 throw new IllegalStateException("Map " + map.getClass().getSimpleName()
95102 + " reuses Map.Entry objects; entrySet can't be passed to addAll");
103 }
96104 return result;
97105 }
98106
99
107
100108 private static <E> int compareValues(Entry<E, Double> o1, Entry<E, Double> o2) {
101109 double c1 = o1.getValue();
102110 double c2 = o2.getValue();
103 if (c1 < c2)
111 if (c1 < c2) {
104112 return 1;
105 if (c1 > c2)
113 }
114 if (c1 > c2) {
106115 return -1;
116 }
107117 return System.identityHashCode(o1.getKey()) - System.identityHashCode(o2.getKey());
108118 }
109119
110120 static class DecreasingOrderEntryComparator<E> implements Comparator<Map.Entry<E, Double>>, Serializable {
121 @Override
111122 public int compare(Entry<E, Double> o1, Entry<E, Double> o2) {
112123 return compareValues(o1, o2);
113124 }
114125 }
115126
116127 static class IncreasingOrderEntryComparator<E> implements Comparator<Map.Entry<E, Double>>, Serializable {
128 @Override
117129 public int compare(Entry<E, Double> o1, Entry<E, Double> o2) {
118130 return -compareValues(o1, o2);
119131 }
00 /*
11 * FindBugs - Find Bugs in Java programs
22 * Copyright (C) 2003-2008 University of Maryland
3 *
3 *
44 * This library is free software; you can redistribute it and/or
55 * modify it under the terms of the GNU Lesser General Public
66 * License as published by the Free Software Foundation; either
77 * version 2.1 of the License, or (at your option) any later version.
8 *
8 *
99 * This library is distributed in the hope that it will be useful,
1010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1111 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1212 * Lesser General Public License for more details.
13 *
13 *
1414 * You should have received a copy of the GNU Lesser General Public
1515 * License along with this library; if not, write to the Free Software
1616 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
3434
3535 volatile boolean canceled;
3636
37 @Override
3738 public synchronized boolean cancel(boolean arg0) {
38 if (latch.getCount() == 0)
39 if (latch.getCount() == 0) {
3940 return false;
41 }
4042 canceled = true;
4143 latch.countDown();
4244 return true;
4345 }
4446
45 /*
46 * (non-Javadoc)
47 *
48 * @see java.util.concurrent.Future#get()
49 */
50 public V get() throws InterruptedException {
47 @Override
48 public synchronized V get() throws InterruptedException {
5149 latch.await();
52 if (canceled)
50 if (canceled) {
5351 throw new RuntimeException("Canceled");
52 }
5453 return value;
5554 }
5655
57 /*
58 * (non-Javadoc)
59 *
60 * @see java.util.concurrent.Future#get(long, java.util.concurrent.TimeUnit)
61 */
62 public V get(long arg0, TimeUnit arg1) throws InterruptedException, TimeoutException {
63 if (!latch.await(arg0, arg1))
56 @Override
57 public synchronized V get(long arg0, TimeUnit arg1) throws InterruptedException, TimeoutException {
58 if (!latch.await(arg0, arg1)) {
6459 throw new TimeoutException();
65 if (canceled)
60 }
61 if (canceled) {
6662 throw new RuntimeException("Canceled");
63 }
6764 return value;
6865 }
6966
70 /*
71 * (non-Javadoc)
72 *
73 * @see java.util.concurrent.Future#get(long, java.util.concurrent.TimeUnit)
74 */
75 public V get(long arg0, TimeUnit arg1, V valueOnTimeout) throws InterruptedException {
76 if (!latch.await(arg0, arg1))
67 public synchronized V get(long arg0, TimeUnit arg1, V valueOnTimeout) throws InterruptedException {
68 if (!latch.await(arg0, arg1)) {
7769 return valueOnTimeout;
78
79 if (canceled)
70 }
71
72 if (canceled) {
8073 throw new RuntimeException("Canceled");
74 }
8175 return value;
8276 }
8377
84 /*
85 * (non-Javadoc)
86 *
87 * @see java.util.concurrent.Future#isCancelled()
88 */
78 @Override
8979 public boolean isCancelled() {
9080 return canceled;
9181 }
9282
93 /*
94 * (non-Javadoc)
95 *
96 * @see java.util.concurrent.Future#isDone()
97 */
83 @Override
9884 public boolean isDone() {
9985 return !canceled && latch.getCount() == 0;
10086 }
10187
10288 public synchronized void set(V value) {
103 if (canceled)
89 if (canceled) {
10490 throw new IllegalStateException("Already cancelled");
105 if (latch.getCount() == 0)
91 }
92 if (latch.getCount() == 0) {
10693 throw new IllegalStateException("Already set");
94 }
10795 this.value = value;
10896 latch.countDown();
10997 }
4040
4141 boolean startingParagraph = false;
4242
43 /**
44 * @param w
45 * @param doc
46 */
4743 public HTMLtoPlainTextWriter2(Writer w, HTMLDocument doc) {
4844 super(w, doc);
4945 setLineLength(80);
5450 protected void startTag(Element elem) throws IOException {
5551 String name = elem.getName();
5652 startingParagraph = true;
57 if (name.equals("ul")) {
53 if ("ul".equals(name)) {
5854 super.incrIndent();
5955 write(" ");
60 } else if (name.equals("pre")) {
56 } else if ("pre".equals(name)) {
6157 inPre = true;
62 } else if (name.equals("li")) {
58 } else if ("li".equals(name)) {
6359 super.incrIndent();
6460 write("* ");
65 } else if (name.equals("p")) {
61 } /*else if (name.equals("p")) {
6662
67 }
63 }*/
6864 }
6965
7066 @Override
7571 @Override
7672 protected void endTag(Element elem) throws IOException {
7773 String name = elem.getName();
78 if (name.equals("p")) {
74 if ("p".equals(name)) {
7975 writeLineSeparator();
8076 indent();
81 } else if (name.equals("pre")) {
77 } else if ("pre".equals(name)) {
8278 inPre = false;
83 } else if (name.equals("ul")) {
79 } else if ("ul".equals(name)) {
8480 super.decrIndent();
8581 writeLineSeparator();
8682 indent();
87 } else if (name.equals("li")) {
83 } else if ("li".equals(name)) {
8884 super.decrIndent();
8985 writeLineSeparator();
9086 indent();
10197
10298 @Override
10399 protected void emptyTag(Element elem) throws IOException, BadLocationException {
104 if (elem.getName().equals("content"))
100 if ("content".equals(elem.getName())) {
105101 super.emptyTag(elem);
102 }
106103 }
107104
108105 @Override
112109 contentStr = contentStr.replaceAll("\\s+", " ");
113110
114111 if (startingParagraph) {
115 while (contentStr.length() > 0 && contentStr.charAt(0) == ' ')
112 while (contentStr.length() > 0 && contentStr.charAt(0) == ' ') {
116113 contentStr = contentStr.substring(1);
114 }
117115 }
118116
119117 startingParagraph = false;
3939 Method jnlpGetCodeBaseMethod;
4040
4141 static final Object jnlpBasicService; // will not be null if
42 // jnlpShowMethod!=null
42 // jnlpShowMethod!=null
4343
4444 static {
4545 // attempt to set the JNLP BasicService object and its showDocument(URL)
7878 if (JavaWebStart.jnlpGetCodeBaseMethod != null) {
7979 try {
8080 URL base = (URL) JavaWebStart.jnlpGetCodeBaseMethod.invoke(JavaWebStart.jnlpBasicService);
81 if (base != null)
81 if (base != null) {
8282 return new URL(base, s);
83 }
8384 } catch (RuntimeException e) {
8485 assert true;
8586 } catch (Exception e) {
9091 }
9192
9293 static Boolean viaWebStart(URL url) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
93 if (JavaWebStart.jnlpShowDocumentMethod == null)
94 if (JavaWebStart.jnlpShowDocumentMethod == null) {
9495 throw new UnsupportedOperationException("Launch via web start not available");
96 }
9597 return (Boolean) JavaWebStart.jnlpShowDocumentMethod.invoke(JavaWebStart.jnlpBasicService, url);
9698 }
9799
98100 static boolean showViaWebStart(URL url) {
99 if (JavaWebStart.jnlpShowDocumentMethod != null)
101 if (JavaWebStart.jnlpShowDocumentMethod != null) {
100102 try {
101 if (LaunchBrowser.DEBUG)
103 if (LaunchBrowser.DEBUG) {
102104 JOptionPane.showMessageDialog(null, "Trying browse via webstart");
105 }
103106
104107 Boolean b = viaWebStart(url);
105108 boolean success = b != null && b.booleanValue();
106109
107 if (LaunchBrowser.DEBUG)
110 if (LaunchBrowser.DEBUG) {
108111 JOptionPane.showMessageDialog(null, " browse via webstart: " + success);
112 }
109113 return success;
110114
111115 } catch (InvocationTargetException ite) {
113117 } catch (IllegalAccessException iae) {
114118 assert true;
115119 }
120 }
116121 return false;
117122 }
118123
7474
7575 static boolean showDocumentViaDesktop(URL u) {
7676
77 if (desktopObject != null && desktopBrowseMethod != null)
77 if (desktopObject != null && desktopBrowseMethod != null) {
7878 try {
79 if (DEBUG)
79 if (DEBUG) {
8080 JOptionPane.showMessageDialog(null, "Trying desktop browse");
81 }
8182 viaDesktop(u.toURI());
82 if (DEBUG)
83 if (DEBUG) {
8384 JOptionPane.showMessageDialog(null, "desktop browse succeeded");
85 }
8486 return true;
8587 } catch (InvocationTargetException ite) {
8688 assert true;
9193 } catch (URISyntaxException e) {
9294 assert true;
9395 }
96 }
9497 return false;
9598 }
9699
97100 static void viaDesktop(URI u) throws IllegalAccessException, InvocationTargetException {
98 if (desktopBrowseMethod == null)
101 if (desktopBrowseMethod == null) {
99102 throw new UnsupportedOperationException("Launch via desktop not available");
103 }
100104 desktopBrowseMethod.invoke(desktopObject, u);
101105 }
102106
103107 static boolean showDocumentViaExec(URL url) {
104108 if (launchViaExec && !launchViaExecFailed) {
105 if (DEBUG)
109 if (DEBUG) {
106110 JOptionPane.showMessageDialog(null, "Trying exec browse");
111 }
107112
108113 try {
109114 Process p = launchViaExec(url);
112117 int exitValue = p.exitValue();
113118 if (exitValue != 0) {
114119 launchViaExecFailed = true;
115 if (DEBUG)
120 if (DEBUG) {
116121 JOptionPane.showMessageDialog(null, "exec browse launch failed with exit code " + exitValue);
122 }
117123 return false;
118124 }
119 if (DEBUG)
125 if (DEBUG) {
120126 JOptionPane.showMessageDialog(null, "exec browse succeeded");
127 }
121128 return true;
122129 } catch (IllegalThreadStateException e) {
123 if (DEBUG)
130 if (DEBUG) {
124131 JOptionPane.showMessageDialog(null, "exec browse succeeded but not done");
132 }
125133 return true;
126134 } catch (Exception e) {
127 if (DEBUG)
135 if (DEBUG) {
128136 JOptionPane.showMessageDialog(null, "exec browse failed" + e.getMessage());
137 }
129138 launchViaExecFailed = true;
130139 }
131140 }
142151 /**
143152 * attempt to show the given URL. will first attempt via the JNLP api, then
144153 * will try showViaExec().
145 *
154 *
146155 * @param url
147156 * the URL
148157 * @return true on success
149158 */
150159 public static boolean showDocument(URL url) {
151160
152 if (showDocumentViaDesktop(url))
161 if (showDocumentViaDesktop(url)) {
153162 return true;
154 if (showDocumentViaExec(url))
163 }
164 if (showDocumentViaExec(url)) {
155165 return true;
156 if (JavaWebStart.showViaWebStart(url))
166 }
167 if (JavaWebStart.showViaWebStart(url)) {
157168 return true;
169 }
158170
159171 return false;
160172
2727 * Provide a HashMap that can only grow to a specified maximum capacity, with
2828 * entries discarded using a LRU policy to keep the size of the HashMap within
2929 * that bound.
30 *
30 *
3131 * @author pugh
3232 */
3333 public class MapCache<K, V> extends LinkedHashMap<K, V> {
3737
3838 /**
3939 * Create a new MapCache
40 *
40 *
4141 * @param maxCapacity
4242 * - maximum number of entries in the map
4343 */
5050 @Override
5151 protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
5252 boolean result = size() > maxCapacity;
53 if (false && result && eldest.getKey() instanceof JavaClass)
53 if (false && result && eldest.getKey() instanceof JavaClass) {
5454 System.out.println("Dropping " + ((JavaClass) eldest.getKey()).getClassName());
55 }
5556 return result;
5657 }
5758
3131 @Override
3232 protected V mergeValues(V oldValue, V newValue) {
3333
34 if (oldValue.compareTo(newValue) > 0)
34 if (oldValue.compareTo(newValue) > 0) {
3535 return newValue;
36 }
3637 return oldValue;
3738 }
3839 }
4243 @Override
4344 protected V mergeValues(V oldValue, V newValue) {
4445
45 if (oldValue.compareTo(newValue) < 0)
46 if (oldValue.compareTo(newValue) < 0) {
4647 return newValue;
48 }
4749 return oldValue;
4850 }
4951 }
6769 return v;
6870 }
6971 V result = mergeValues(currentValue, v);
70 if (currentValue != result)
72 if (currentValue != result) {
7173 map.put(k, v);
74 }
7275
7376 return result;
7477 }
7070 Collection<V> s = map.get(k);
7171 if (s != null) {
7272 s.remove(v);
73 if (s.isEmpty())
73 if (s.isEmpty()) {
7474 map.remove(k);
75 }
7576 }
7677 }
7778
8182
8283 public Collection<V> get(K k) {
8384 Collection<V> s = map.get(k);
84 if (s != null)
85 if (s != null) {
8586 return s;
87 }
8688 return Collections.<V> emptySet();
8789 }
8890
2525 import java.util.Map.Entry;
2626 import java.util.Set;
2727 import java.util.TreeSet;
28
29 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
2830
2931 /**
3032 * @author pwilliam
6264
6365 public boolean remove(K k) {
6466 Integer v = map.get(k);
65 if (v == null || v.intValue() == 0)
67 if (v == null || v.intValue() == 0) {
6668 return false;
69 }
6770 if (v.intValue() == 1) {
6871 map.remove(k);
6972 return true;
7477
7578 public void add(K k, int val) {
7679 Integer v = map.get(k);
77 if (v == null)
80 if (v == null) {
7881 map.put(k, val);
79 else
82 } else {
8083 map.put(k, v + val);
84 }
8185 }
8286
8387 public void addAll(Iterable<K> c) {
84 for (K k : c)
88 for (K k : c) {
8589 add(k);
90 }
8691 }
8792
8893 public int getCount(K k) {
8994 Integer v = map.get(k);
90 if (v == null)
95 if (v == null) {
9196 return 0;
97 }
9298 return v;
9399 }
94100
100106 return map.keySet();
101107 }
102108
109 @SuppressFBWarnings("DMI_ENTRY_SETS_MAY_REUSE_ENTRY_OBJECTS")
103110 public Iterable<Map.Entry<K, Integer>> entriesInDecreasingFrequency() {
104111 TreeSet<Map.Entry<K, Integer>> result = new TreeSet<Map.Entry<K, Integer>>(new EntryComparator<K>());
105112 result.addAll(map.entrySet());
106 if (result.size() != map.size())
107 throw new IllegalStateException("Map " + map.getClass().getSimpleName()
113 if (result.size() != map.size()) {
114 throw new IllegalStateException("Map " + map.getClass().getSimpleName()
108115 + " reuses Map.Entry objects; entrySet can't be passed to addAll");
116 }
109117 return result;
110118 }
111119
112120 static class EntryComparator<E> implements Comparator<Map.Entry<E, Integer>>, Serializable {
113121
122 @Override
114123 public int compare(Entry<E, Integer> o1, Entry<E, Integer> o2) {
115124 int c1 = o1.getValue();
116125 int c2 = o2.getValue();
117 if (c1 < c2)
126 if (c1 < c2) {
118127 return 1;
119 if (c1 > c2)
128 }
129 if (c1 > c2) {
120130 return -1;
131 }
121132 return System.identityHashCode(o1.getKey()) - System.identityHashCode(o2.getKey());
122133 }
123134
2323
2424 /**
2525 * An Iterator that returns no elements.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class NullIterator<E> implements Iterator<E> {
3030
3131 /*
3232 * (non-Javadoc)
33 *
33 *
3434 * @see java.util.Iterator#hasNext()
3535 */
36 @Override
3637 public boolean hasNext() {
3738 return false;
3839 }
3940
4041 /*
4142 * (non-Javadoc)
42 *
43 *
4344 * @see java.util.Iterator#next()
4445 */
46 @Override
4547 public E next() {
4648 throw new NoSuchElementException();
4749 }
4850
4951 /*
5052 * (non-Javadoc)
51 *
53 *
5254 * @see java.util.Iterator#remove()
5355 */
56 @Override
5457 public void remove() {
5558 throw new UnsupportedOperationException();
5659 }
3535 @Override
3636 public void run() {
3737 System.out.println("Profile for map cache " + ProfilingMapCache.this.name);
38 for (int i = 0; i < count.length; i++)
38 for (int i = 0; i < count.length; i++) {
3939 System.out.printf("%4d %5d%n", i, count[i]);
40 }
4041 }
4142 });
4243 }
6263
6364 public String getStatistics() {
6465 StringBuilder b = new StringBuilder();
65 for (int c : count)
66 for (int c : count) {
6667 b.append(c).append(" ");
68 }
6769 return b.toString();
6870 }
6971 }
2323
2424 /**
2525 * StringMatcher that matches based on a regular expression.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class RegexStringMatcher implements StringMatcher {
3030
31 private Pattern pattern;
31 private final Pattern pattern;
3232
3333 /**
3434 * Constructor.
35 *
35 *
3636 * @param patStr
3737 * a String defining the regular expression pattern to match
3838 */
4040 pattern = Pattern.compile(patStr);
4141 }
4242
43 @Override
4344 public boolean matches(String s) {
4445 Matcher m = pattern.matcher(s);
4546 return m.matches();
2424
2525 /**
2626 * Split a camel case identifier into individual words.
27 *
27 *
2828 * @author David Hovemeyer
2929 */
3030 public class SplitCamelCaseIdentifier {
31 private String ident;
31 private final String ident;
3232
3333 /**
3434 * Constructor.
35 *
35 *
3636 * @param ident
3737 * the identifier to split into words
3838 */
4242
4343 /**
4444 * Split the identifier into words.
45 *
45 *
4646 * @return Collection of words in the identifier
4747 */
4848 public Collection<String> split() {
6969 while (i < s.length()) {
7070 char c = s.charAt(i);
7171 if (Character.isUpperCase(c)) {
72 if (camelWord)
72 if (camelWord) {
7373 break;
74 }
7475 } else if (!camelWord) {
7576 break;
7677 }
2020
2121 /**
2222 * A boolean predicate for matching String values.
23 *
23 *
2424 * @author David Hovemeyer
2525 */
2626 public interface StringMatcher {
2727 /**
2828 * Return whether or not the given String matches.
29 *
29 *
3030 * @param s
3131 * a String
3232 * @return true if the String matches, false if it does not match
2727
2828 /**
2929 * A class for static String utility methods.
30 *
30 *
3131 * @author Brian Cole
3232 */
3333 public class Strings {
3535 /**
3636 * This is intended to be semantically equivalent to
3737 * <code>source.replace(find, repl)</code> but also compatible with JDK 1.4.
38 *
38 *
3939 * @param source
4040 * The String on which to operate
4141 * @param find
6060 * also compatible with JDK 1.4. This concatenates the results of calling
6161 * String.valueOf() on each element of the array, so this won't work well
6262 * for multi-dimensional arrays.
63 *
63 *
6464 * @see java.lang.String#valueOf(Object)
6565 * @see java.util.Arrays#toString(Object[])
6666 * @see java.util.Arrays#deepToString(Object[])
7272
7373 /**
7474 * Trim trailing comma from given string.
75 *
75 *
7676 * @param s
7777 * a string
7878 * @return the same string with trailing comma trimmed (if any)
9393 private static final int xmlAllowedLowCharacterBound = 0x20;
9494
9595 private static boolean isInvalidXMLCharacter(int c) {
96 if (c < xmlAllowedLowCharacterBound && c >= 0x0 &&
97 // low-value characters allowed by XML 1.0 spec
98 c != 0x9 && c != 0xA && c != 0xD)
96 if ((c < xmlAllowedLowCharacterBound && c >= 0x0 &&
97 // low-value characters allowed by XML 1.0 spec
98 // '\uFFFE' (&#65534;) cannot be deserialized by SAX reader.
99 c != 0x9 && c != 0xA && c != 0xD) || c == 0xFFFE) {
99100 return true;
101 }
100102 return false;
101103 }
102104
110112 * Initializes the map of characters to be escaped and their corresponding
111113 * escape sequences. This method will be invoked automatically the first
112114 * time a string is escaped/unescaped.
113 *
115 *
114116 * @see <a href="http://www.w3.org/TR/REC-xml/#charsets">Extensible Markup
115117 * Language (XML) 1.0 (Fifth Edition)</a>
116118 */
117119 public static void initializeEscapeMap() {
118 if (xmlLowValueEscapeStringsInitialized == true)
120 if (xmlLowValueEscapeStringsInitialized == true) {
119121 return;
122 }
120123
121124 /*
122125 * synchronize the lazy initialization so things don't break if FindBugs
124127 * warning about the thread safety of this operation)
125128 */
126129 synchronized (escapeInitLockObject) {
127 if (xmlLowValueEscapeStringsInitialized == true)
130 if (xmlLowValueEscapeStringsInitialized == true) {
128131 return;
132 }
129133
130134 for (int i = 0; i < xmlAllowedLowCharacterBound; i++) {
131135 if (isInvalidXMLCharacter(i)) {
145149 * org.apache.commons.lang.StringEscapeUtils.escapeXml by escaping
146150 * low-valued unprintable characters, which are not permitted by the W3C XML
147151 * 1.0 specification.
148 *
152 *
149153 * @param s
150154 * a string
151155 * @return the same string with characters not permitted by the XML
159163 public static String escapeXml(String s) {
160164 initializeEscapeMap();
161165
162 if (s == null || s.length() == 0)
166 if (s == null || s.length() == 0) {
163167 return s;
168 }
164169
165170 char[] sChars = s.toCharArray();
166171 StringBuilder sb = new StringBuilder();
170175 // append intermediate string to string builder
171176 sb.append(sChars, lastReplacement, i - lastReplacement);
172177 // substitute control character with escape sequence
173 sb.append(xmlLowValueEscapeStrings[sChars[i]]);
178 sb.append(sChars[i] == 0xFFFE ? "\\ufffe" : xmlLowValueEscapeStrings[sChars[i]]);
174179 // advance last pointer past this character
175180 lastReplacement = i + 1;
176181 }
177182 }
178 if (lastReplacement < sChars.length)
183 if (lastReplacement < sChars.length) {
179184 sb.append(sChars, lastReplacement, sChars.length - lastReplacement);
185 }
180186
181187 return StringEscapeUtils.escapeXml(sb.toString());
182188 }
194200 * invoked automatically the first time a string is unescaped.
195201 */
196202 public static boolean initializeUnescapePattern() {
197 if (paternIsInitialized == true)
203 if (paternIsInitialized == true) {
198204 return true;
205 }
199206
200207 synchronized (unescapeInitLockObject) {
201 if (paternIsInitialized == true)
208 if (paternIsInitialized == true) {
202209 return true;
210 }
203211
204212 try {
205213 unescapePattern = Pattern.compile(unicodeUnescapeMatchExpression);
223231 * org.apache.commons.lang.StringEscapeUtils.unescapeXml by unescaping
224232 * low-valued unprintable characters, which are not permitted by the W3C XML
225233 * 1.0 specification.
226 *
234 *
227235 * @param s
228236 * a string
229237 * @return the same string with XML entities/escape sequences unescaped
241249 * we can't escape the string if the pattern doesn't compile! (but that
242250 * should never happen since the pattern is static)
243251 */
244 if (!initializeUnescapePattern())
252 if (!initializeUnescapePattern()) {
245253 return s;
246
247 if (s == null || s.length() == 0)
254 }
255
256 if (s == null || s.length() == 0) {
248257 return s;
258 }
249259
250260 /*
251261 * skip this expensive check entirely if there are no substrings
3030 /**
3131 * Type matcher that determines if a candidate Type is a subtype of a given
3232 * Type.
33 *
33 *
3434 * @author David Hovemeyer
3535 */
3636 public class SubtypeTypeMatcher implements TypeMatcher {
37 private ReferenceType supertype;
37 private final ReferenceType supertype;
3838
3939 /**
4040 * Constructor.
41 *
41 *
4242 * @param supertype
4343 * a ReferenceType: this TypeMatcher will test whether or not
4444 * candidate Types are subtypes of this Type
4949
5050 /**
5151 * Constructor.
52 *
52 *
5353 * @param classDescriptor
5454 * a ClassDescriptor naming a class: this TypeMatcher will test
5555 * whether or not candidate Types are subtypes of the class
5858 this(BCELUtil.getObjectTypeInstance(classDescriptor.toDottedClassName()));
5959 }
6060
61 @Override
6162 public boolean matches(Type t) {
6263 if (!(t instanceof ReferenceType)) {
6364 return false;
5050 public class TestDesktopIntegration extends JPanel {
5151
5252 private static String[] propertyNames = { "java.version", "java.vendor", "java.vendor.url", "java.home",
53 "java.vm.specification.version", "java.vm.specification.vendor", "java.vm.specification.name", "java.vm.version",
54 "java.vm.vendor", "java.vm.name", "java.specification.version", "java.specification.vendor",
55 "java.specification.name", "java.class.version", "java.class.path", "java.library.path", "java.io.tmpdir",
56 "java.compiler", "java.ext.dirs", "os.name", "os.arch", "os.version", "file.separator", "path.separator",
57 "line.separator", "user.name", "user.home", "user.dir" };
53 "java.vm.specification.version", "java.vm.specification.vendor", "java.vm.specification.name", "java.vm.version",
54 "java.vm.vendor", "java.vm.name", "java.specification.version", "java.specification.vendor",
55 "java.specification.name", "java.class.version", "java.class.path", "java.library.path", "java.io.tmpdir",
56 "java.compiler", "java.ext.dirs", "os.name", "os.arch", "os.version", "file.separator", "path.separator",
57 "line.separator", "user.name", "user.home", "user.dir" };
5858
5959 public static void main(String args[]) throws Exception {
6060 String u = SystemProperties.getProperty("findbugs.browserTestURL", "http://findbugs.sourceforge.net/");
6161 url = new URL(u);
6262
6363 SwingUtilities.invokeLater(new Runnable() {
64 @Override
6465 public void run() {
6566 createAndShowGUI();
6667 }
9495
9596 /*
9697 * (non-Javadoc)
97 *
98 *
9899 * @see java.io.Writer#flush()
99100 */
100101 @Override
104105
105106 /*
106107 * (non-Javadoc)
107 *
108 *
108109 * @see java.io.Writer#write(char[], int, int)
109110 */
110111 @Override
131132 console.setEditable(false);
132133 console.setLineWrap(true);
133134 add(scrollPane);
134 } else
135 } else {
135136 add(new JLabel("These buttons should view " + url), BorderLayout.NORTH);
137 }
136138 if (LaunchBrowser.desktopFeasible()) {
137139 JButton desktop = new JButton("Use java.awt.Desktop");
138140 desktop.addActionListener(new ActionListener() {
139141
142 @Override
140143 public void actionPerformed(ActionEvent e) {
141144 try {
142145
160163 if (LaunchBrowser.webstartFeasible()) {
161164 JButton jnlp = new JButton("Use jnlp");
162165 jnlp.addActionListener(new ActionListener() {
166 @Override
163167 public void actionPerformed(ActionEvent e) {
164168 try {
165169
182186 top.add(exec);
183187 if (LaunchBrowser.launchViaExec) {
184188 exec.addActionListener(new ActionListener() {
189 @Override
185190 public void actionPerformed(ActionEvent e) {
186191 try {
187192 writer.println("Launch via exec " + LaunchBrowser.execCommand);
209214 top.add(chooseFile);
210215 chooseFile.addActionListener(new ActionListener() {
211216
217 @Override
212218 public void actionPerformed(ActionEvent e) {
213219 final JFileChooser fc = new JFileChooser();
214220 int retvel = fc.showOpenDialog(TestDesktopIntegration.this);
235241 writer.println("System properties:");
236242 TreeSet<String> props = new TreeSet<String>();
237243 for (Object o : System.getProperties().keySet()) {
238 if (o instanceof String)
244 if (o instanceof String) {
239245 props.add((String) o);
246 }
240247 }
241248 props.addAll(Arrays.asList(propertyNames));
242249
5858 this.base = base;
5959 }
6060
61 @Override
6162 public Collection<E> getOutEdges(E e) {
6263 Collection<E> result = map.get(e);
6364 if (result == null) {
8182 }
8283
8384 public static <E> void countBadEdges(List<E> elements, OutEdges<E> outEdges) {
84 if (!DEBUG)
85 if (!DEBUG) {
8586 return;
87 }
8688 HashSet<E> seen = new HashSet<E>();
8789 HashSet<E> all = new HashSet<E>(elements);
8890 int result = 0;
8991 int total = 0;
9092 for (E e : elements) {
91 for (E e2 : outEdges.getOutEdges(e))
93 for (E e2 : outEdges.getOutEdges(e)) {
9294 if (e != e2 && all.contains(e2) && !outEdges.getOutEdges(e2).contains(e)) {
9395 total++;
94 if (!seen.contains(e2))
96 if (!seen.contains(e2)) {
9597 result++;
96 }
98 }
99 }
100 }
97101 seen.add(e);
98102 }
99103 System.out.println(" bad edges are " + result + "/" + total);
119123
120124 Set<E> consider = new HashSet<E>();
121125
126 @Override
122127 public List<E> compute() {
123 for (E e : consider)
128 for (E e : consider) {
124129 visit(e);
130 }
125131 return result;
126132 }
127133
128134 void visit(E e) {
129 if (!consider.contains(e))
135 if (!consider.contains(e)) {
130136 return;
131 if (!visited.add(e))
137 }
138 if (!visited.add(e)) {
132139 return;
133 for (E e2 : outEdges.getOutEdges(e))
140 }
141 for (E e2 : outEdges.getOutEdges(e)) {
134142 visit(e2);
143 }
135144
136145 result.add(e);
137146 }
139148
140149 static class Worker2<E> implements SortAlgorithm<E> {
141150 Worker2(Collection<E> consider, OutEdges<E> outEdges) {
142 if (outEdges == null)
151 if (outEdges == null) {
143152 throw new IllegalArgumentException("outEdges must not be null");
153 }
144154 this.consider = new LinkedHashSet<E>(consider);
145155 this.outEdges = outEdges;
146156
166176 oEdges.removeAll(e);
167177 }
168178
179 @Override
169180 public List<E> compute() {
170181 ArrayList<E> doFirst = new ArrayList<E>(consider.size());
171182 ArrayList<E> doLast = new ArrayList<E>(consider.size());
174185 iEdges = new MultiMap<E, E>(LinkedList.class);
175186 oEdges = new MultiMap<E, E>(LinkedList.class);
176187
177 for (E e : consider)
178 for (E e2 : outEdges.getOutEdges(e))
188 for (E e : consider) {
189 for (E e2 : outEdges.getOutEdges(e)) {
179190 if (e != e2 && consider.contains(e2)) {
180191 iEdges.add(e2, e);
181192 oEdges.add(e, e2);
182193 }
194 }
195 }
183196 for (E e : consider) {
184197 HashSet<E> both = new HashSet<E>(iEdges.get(e));
185198 both.retainAll(oEdges.get(e));
197210 if (oEdges.get(e).isEmpty()) {
198211 doFirst.add(e);
199212 removeVertex(e);
200 if (DEBUG)
213 if (DEBUG) {
201214 System.out.println("do " + e + " first");
215 }
202216 i.remove();
203217 foundSomething = true;
204218 } else if (iEdges.get(e).isEmpty()) {
205219 doLast.add(e);
206220 removeVertex(e);
207 if (DEBUG)
221 if (DEBUG) {
208222 System.out.println("do " + e + " last");
223 }
209224 i.remove();
210225 foundSomething = true;
211226 } else {
249264 int myScore = score(e);
250265 if (outEdges instanceof OutEdges2) {
251266 int score2 = ((OutEdges2<E>) outEdges).score(e);
252 if (score2 > 1)
267 if (score2 > 1) {
253268 score2 += 11;
269 }
254270 myScore = 5 * myScore + score2;
255271 }
256272 return myScore;
262278 */
263279 private int score(E e) {
264280 int myScore = 0;
265 for (E e2 : oEdges.get(e))
266 if (iEdges.get(e2).size() == 1)
281 for (E e2 : oEdges.get(e)) {
282 if (iEdges.get(e2).size() == 1) {
267283 myScore -= 2;
268 else
284 } else {
269285 myScore -= 1;
270 for (E e2 : iEdges.get(e))
271 if (oEdges.get(e2).size() == 1)
286 }
287 }
288 for (E e2 : iEdges.get(e)) {
289 if (oEdges.get(e2).size() == 1) {
272290 myScore += 2;
273 else
291 } else {
274292 myScore += 1;
293 }
294 }
275295 return myScore;
276296 }
277297
2929
3030 public V get(K1 k1, K2 k2, K3 k3) {
3131 DualKeyHashMap<K2, K3, V> m = map.get(k1);
32 if (m == null)
32 if (m == null) {
3333 return null;
34 }
3435 return m.get(k2, k3);
3536 }
3637
2222
2323 /**
2424 * Predicate for matching types.
25 *
25 *
2626 * @author David Hovemeyer
2727 */
2828 public interface TypeMatcher {
2929 /**
3030 * Determine whether given type matches this predicate.
31 *
31 *
3232 * @param t
3333 * a Type
3434 * @return true if the Type matches, false otherwise
5959 import javax.annotation.WillNotClose;
6060
6161 import edu.umd.cs.findbugs.SystemProperties;
62 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
6263 import edu.umd.cs.findbugs.charsets.UTF8;
6364
6465 /**
6566 * @author William Pugh
6667 */
6768 public class Util {
68
69
6970 public static Thread startDameonThread(Thread t) {
7071 t.setDaemon(true);
7172 t.start();
7273 return t;
7374 }
74
75
7576 public static Thread runInDameonThread(Runnable r, String name) {
7677 Thread t = new Thread(r, name);
7778 return startDameonThread(t);
78
79
7980 }
8081 public static Thread runInDameonThread(Runnable r) {
8182 Thread t = new Thread(r);
8283 return startDameonThread(t);
83
84 }
85
84
85 }
86
8687 public static int sign(int x) {
87 if (x < 0)
88 if (x < 0) {
8889 return -1;
89 if (x > 0)
90 }
91 if (x > 0) {
9092 return 1;
93 }
9194 return 0;
9295 }
9396 /**
9497 * return sign of x - y
95 *
96 * @param x
97 * @param y
98 * @return
9998 */
10099 public static int compare(int x, int y) {
101 if (x > y)
100 if (x > y) {
102101 return 1;
103 if (x < y)
102 }
103 if (x < y) {
104104 return -1;
105 }
105106 return 0;
106107 }
107108
108109 /**
109110 * return sign of x - y
110 *
111 * @param x
112 * @param y
113 * @return
114111 */
115112 public static int compare(long x, long y) {
116 if (x > y)
113 if (x > y) {
117114 return 1;
118 if (x < y)
115 }
116 if (x < y) {
119117 return -1;
118 }
120119 return 0;
121120 }
122121
123122 public static Iterable<Integer> setBitIteratable(final BitSet b) {
124123 return new Iterable<Integer>() {
124 @Override
125125 public Iterator<Integer> iterator() {
126126 return setBitIterator(b);
127127 }
132132 return new Iterator<Integer>() {
133133 int nextBit = b.nextSetBit(0);
134134
135 @Override
135136 public boolean hasNext() {
136137 return nextBit >= 0;
137138 }
138139
140 @Override
139141 public Integer next() {
140142 int result = nextBit;
141143 nextBit = b.nextSetBit(nextBit + 1);
142144 return result;
143145 }
144146
147 @Override
145148 public void remove() {
146149 throw new UnsupportedOperationException();
147150 }
150153
151154 public static String repeat(String s, int number) {
152155 StringBuilder b = new StringBuilder(s.length() * number);
153 for (int i = 0; i < number; i++)
156 for (int i = 0; i < number; i++) {
154157 b.append(s);
158 }
155159 return b.toString();
156160 }
157161
158162 static Collection<Runnable> runAtShutdown;
159163
160164 public static String getNetworkErrorMessage(Throwable e) {
161 if (e.getClass().getSimpleName().equals("InvalidProtocolBufferException")) {
162 return "Your Internet provider may require you to log in via your web browser.";
165 if ("InvalidProtocolBufferException".equals(e.getClass().getSimpleName())) {
166 return "Your Internet provider may require you to log in via your web browser.";
163167 }
164168 if (e instanceof UnknownHostException) {
165169 return "You may not be connected to the Internet.";
166 } else
170 } else {
167171 return e.getClass().getSimpleName() + ": " + e.getMessage();
172 }
168173 }
169174
170175 static class ShutdownLogging {
194199 }
195200
196201 public static <T> Set<T> emptyOrNonnullSingleton(T t) {
197 if (t == null)
202 if (t == null) {
198203 return Collections.<T> emptySet();
204 }
199205 return Collections.<T> singleton(t);
200206 }
201207
202208 public static <K, V> Map<K, V> immutableMap(Map<K, V> map) {
203 if (map.size() == 0)
209 if (map.size() == 0) {
204210 return Collections.<K, V> emptyMap();
211 }
205212 return Collections.<K, V> unmodifiableMap(map);
206213 }
207214
208215 public static int nullSafeHashcode(@CheckForNull Object o) {
209 if (o == null)
216 if (o == null) {
210217 return 0;
218 }
211219 return o.hashCode();
212220 }
213221
214222 public static <T> boolean nullSafeEquals(@CheckForNull T o1, @CheckForNull T o2) {
215 if (o1 == o2)
223 if (o1 == o2) {
216224 return true;
217 if (o1 == null || o2 == null)
225 }
226 if (o1 == null || o2 == null) {
218227 return false;
228 }
219229 return o1.equals(o2);
220230 }
221231
222232 public static <T extends Comparable<? super T>> int nullSafeCompareTo(@CheckForNull T o1, @CheckForNull T o2) {
223 if (o1 == o2)
233 if (o1 == o2) {
224234 return 0;
225 if (o1 == null)
235 }
236 if (o1 == null) {
226237 return -1;
227 if (o2 == null)
238 }
239 if (o2 == null) {
228240 return 1;
241 }
229242 return o1.compareTo(o2);
230243 }
231244
251264
252265 public static void closeSilently(@WillClose Connection c) {
253266 try {
254 if (c != null)
267 if (c != null) {
255268 c.close();
269 }
256270 } catch (SQLException e) {
257271 assert true;
258272 }
260274
261275 public static void closeSilently(@WillClose PreparedStatement c) {
262276 try {
263 if (c != null)
277 if (c != null) {
264278 c.close();
279 }
265280 } catch (SQLException e) {
266281 assert true;
267282 }
269284
270285 public static void closeSilently(@WillClose ResultSet c) {
271286 try {
272 if (c != null)
287 if (c != null) {
273288 c.close();
289 }
274290 } catch (SQLException e) {
275291 assert true;
276292 }
278294
279295 public static void closeSilently(@WillClose InputStream in) {
280296 try {
281 if (in != null)
297 if (in != null) {
282298 in.close();
299 }
283300 } catch (IOException e) {
284301 assert true;
285302 }
287304
288305 public static void closeSilently(@WillClose Reader in) {
289306 try {
290 if (in != null)
307 if (in != null) {
291308 in.close();
309 }
292310 } catch (IOException e) {
293311 assert true;
294312 }
296314
297315 public static void closeSilently(@WillClose OutputStream out) {
298316 try {
299 if (out != null)
317 if (out != null) {
300318 out.close();
319 }
301320 } catch (IOException e) {
302321 assert true;
303322 }
305324
306325 public static void closeSilently(@WillClose Closeable out) {
307326 try {
308 if (out != null)
327 if (out != null) {
309328 out.close();
329 }
310330 } catch (IOException e) {
311331 assert true;
312332 }
313333 }
314
334
315335 public static void closeSilently(@WillClose ZipFile zip) {
316336 try {
317 if (zip != null)
337 if (zip != null) {
318338 zip.close();
339 }
319340 } catch (IOException e) {
320341 assert true;
321342 }
323344
324345 static final Pattern tag = Pattern.compile("^\\s*<(\\w+)");
325346
347 @SuppressFBWarnings("OS_OPEN_STREAM")
326348 public static String getXMLType(@WillNotClose InputStream in) throws IOException {
327 if (!in.markSupported())
349 if (!in.markSupported()) {
328350 throw new IllegalArgumentException("Input stream does not support mark");
351 }
329352
330353 in.mark(5000);
331354 BufferedReader r = null;
336359 int count = 0;
337360 while (count < 4) {
338361 s = r.readLine();
339 if (s == null)
362 if (s == null) {
340363 break;
364 }
341365 Matcher m = tag.matcher(s);
342 if (m.find())
366 if (m.find()) {
343367 return m.group(1);
368 }
344369 }
345370 throw new IOException("Didn't find xml tag");
346371 } finally {
356381 }
357382 private static String getFileExtension(String name) {
358383 int lastDot = name.lastIndexOf('.');
359 if (lastDot == -1)
384 if (lastDot == -1) {
360385 return "";
386 }
361387 return name.substring(lastDot + 1);
362388 }
363389
367393 public static String getFileExtensionIgnoringGz(File f) {
368394
369395 String name = f.getName().toLowerCase();
370 if (name.endsWith(".gz"))
396 if (name.endsWith(".gz")) {
371397 name = name.substring(0,name.length()-3);
372 return getFileExtension(name);
398 }
399 return getFileExtension(name);
373400 }
374401
375402 public static void throwIOException(String msg, Throwable cause) throws IOException {
385412 */
386413 public static <E> E first(Iterable<E> i) {
387414 Iterator<E> iterator = i.iterator();
388 if (!iterator.hasNext())
415 if (!iterator.hasNext()) {
389416 throw new IllegalArgumentException("iterator has no elements");
417 }
390418 return iterator.next();
391419 }
392420
393421 public static String commonPrefix(String s1, String s2) {
394 if (s1 == null)
422 if (s1 == null) {
395423 return s2;
396 if (s2 == null)
424 }
425 if (s2 == null) {
397426 return s1;
427 }
398428 int minLength = Math.min(s1.length(), s2.length());
399 for (int i = 0; i < minLength; i++)
400 if (s1.charAt(i) != s2.charAt(i))
429 for (int i = 0; i < minLength; i++) {
430 if (s1.charAt(i) != s2.charAt(i)) {
401431 return s1.substring(0, i);
402 if (s1.length() == minLength)
432 }
433 }
434 if (s1.length() == minLength) {
403435 return s1;
436 }
404437 assert s2.length() == minLength;
405438 return s2;
406439
522555 }
523556
524557 public static <K> Set<K> addTo(Set<K> s, K k) {
525 if (s.isEmpty())
558 if (s.isEmpty()) {
526559 return Collections.singleton(k);
527 if (s.contains(k))
560 }
561 if (s.contains(k)) {
528562 return s;
563 }
529564 if (s instanceof HashSet) {
530565 s.add(k);
531566 return s;
537572 }
538573
539574 public static <K> List<K> addTo(List<K> s, K k) {
540 if (s.isEmpty())
575 if (s.isEmpty()) {
541576 return Collections.singletonList(k);
542 if (!(s instanceof ArrayList))
577 }
578 if (!(s instanceof ArrayList)) {
543579 s = makeSmallArrayList(s);
580 }
544581 s.add(k);
545582 return s;
546583 }
66 public class WriteOnceProperties extends Properties {
77
88 private static final long serialVersionUID = 1L;
9 private final Map<String, PropertyReadAt> propertReadAt = new HashMap<String, PropertyReadAt>();
910
1011 static class PropertyReadAt extends Exception {
1112 private static final long serialVersionUID = 1L;
1213 }
13
14 private final Map<String, PropertyReadAt> propertReadAt = new HashMap<String, PropertyReadAt>();
1514
1615 private WriteOnceProperties(Properties initialValue) {
1716 super.putAll(initialValue);
1817 }
1918
2019 @Override
21 public boolean equals(Object o) {
20 public final synchronized int hashCode() {
21 return super.hashCode();
22 }
23
24 /* overridden to avoid EQ_DOESNT_OVERRIDE_EQUALS */
25 @Override
26 public final synchronized boolean equals(Object o) {
2227 return super.equals(o);
2328 }
2429
2530 @Override
26 public int hashCode() {
27 return super.hashCode();
28 }
29
30 @Override
31 public String getProperty(String key) {
31 public synchronized String getProperty(String key) {
3232 String result = super.getProperty(key);
33 if (result != null && result.length() > 0 && !propertReadAt.containsKey(key))
33 if (result != null && result.length() > 0 && !propertReadAt.containsKey(key)) {
3434 propertReadAt.put(key, new PropertyReadAt());
35 }
3536 return result;
3637 }
3738
3839 @Override
39 public String getProperty(String key, String defaultValue) {
40 public synchronized String getProperty(String key, String defaultValue) {
4041 String result = super.getProperty(key, defaultValue);
41 if (result != null && result.length() > 0 && !propertReadAt.containsKey(key))
42 if (result != null && result.length() > 0 && !propertReadAt.containsKey(key)) {
4243 propertReadAt.put(key, new PropertyReadAt());
44 }
4345 return result;
4446 }
4547
4648 @Override
47 public Object setProperty(String key, String value) {
49 public synchronized Object setProperty(String key, String value) {
4850 if (propertReadAt.containsKey(key) && !value.equals(super.getProperty(key))) {
4951 IllegalStateException e = new IllegalStateException("Changing property '" + key + "' to '" + value
5052 + "' after it has already been read as '" + super.getProperty(key) + "'");
5153 e.initCause(propertReadAt.get(key));
52
5354 throw e;
5455 }
5556 return super.setProperty(key, value);
5758
5859 public static void makeSystemPropertiesWriteOnce() {
5960 Properties properties = System.getProperties();
60 if (properties instanceof WriteOnceProperties)
61 if (properties instanceof WriteOnceProperties) {
6162 return;
63 }
6264 System.setProperties(new WriteOnceProperties(properties));
6365 }
6466
7577
7678 }
7779
78 /**
79 *
80 */
8180 private static void dumpProperties() {
8281
8382 Properties properties = System.getProperties();
1717 */
1818 package edu.umd.cs.findbugs.visitclass;
1919
20 import java.io.DataInputStream;
21 import java.io.IOException;
2220 import java.util.HashMap;
2321 import java.util.Map;
22
23 import javax.annotation.CheckForNull;
2424
2525 import org.apache.bcel.classfile.AnnotationEntry;
2626 import org.apache.bcel.classfile.Annotations;
2727 import org.apache.bcel.classfile.ArrayElementValue;
28 import org.apache.bcel.classfile.Constant;
29 import org.apache.bcel.classfile.ConstantDouble;
30 import org.apache.bcel.classfile.ConstantFloat;
31 import org.apache.bcel.classfile.ConstantInteger;
32 import org.apache.bcel.classfile.ConstantLong;
33 import org.apache.bcel.classfile.ConstantUtf8;
3428 import org.apache.bcel.classfile.ElementValue;
3529 import org.apache.bcel.classfile.ElementValuePair;
3630 import org.apache.bcel.classfile.ParameterAnnotationEntry;
3832 import org.apache.bcel.classfile.SimpleElementValue;
3933
4034 import edu.umd.cs.findbugs.SystemProperties;
35 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
4136 import edu.umd.cs.findbugs.internalAnnotations.DottedClassName;
4237 import edu.umd.cs.findbugs.util.ClassName;
4338
4439 /**
4540 * Subclass of PreorderVisitor that visits annotations on classes, fields,
4641 * methods, and method parameters.
47 *
42 *
4843 * @author William Pugh
4944 */
5045 public class AnnotationVisitor extends PreorderVisitor {
5146
52
53 private static final String RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = "RuntimeInvisibleParameterAnnotations";
54
55
56 private static final String RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations";
57
58
59 private static final String RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations";
60
61 private static final String RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations";
62
6347 static final boolean DEBUG = SystemProperties.getBoolean("annotation.visitor");
6448
6549 /**
6650 * Visit annotation on a class, field or method
67 *
51 *
6852 * @param annotationClass
6953 * class of annotation
7054 * @param map
8670 try {
8771 ElementValue ev = map.get(parameter);
8872
89 if (ev instanceof SimpleElementValue)
73 if (ev instanceof SimpleElementValue) {
9074 return ((SimpleElementValue) ev).getValueString();
75 }
9176 return null;
9277 } catch (Exception e) {
9378 return null;
9580 }
9681 }
9782
83 @CheckForNull
84 @SuppressFBWarnings("PZLA_PREFER_ZERO_LENGTH_ARRAYS")
9885 protected static String[] getAnnotationParameterAsStringArray(Map<String, ElementValue> map, String parameter) {
9986 try {
10087 ElementValue e = map.get(parameter);
10188 ArrayElementValue a = (ArrayElementValue) e;
10289 int size = a.getElementValuesArraySize();
10390 String[] result = new String[size];
104 for (int i = 0; i < size; i++)
105 result[i] = ((SimpleElementValue) a.getElementValuesArray()[i]).getValueString();
91 ElementValue[] elementValuesArray = a.getElementValuesArray();
92 for (int i = 0; i < size; i++) {
93 result[i] = ((SimpleElementValue) elementValuesArray[i]).getValueString();
94 }
10695 return result;
10796 } catch (Exception e) {
10897 return null;
109
11098 }
11199 }
112100
113101 /**
114102 * Visit annotation on a method parameter
115 *
103 *
116104 * @param p
117105 * parameter number, starting at zero ("this" parameter is not
118106 * counted)
131119 public void visitSyntheticParameterAnnotation(int p, boolean runtimeVisible) {
132120 }
133121
134
122 /*
123
124 private static final String RUNTIME_INVISIBLE_PARAMETER_ANNOTATIONS = "RuntimeInvisibleParameterAnnotations";
125 private static final String RUNTIME_INVISIBLE_ANNOTATIONS = "RuntimeInvisibleAnnotations";
126 private static final String RUNTIME_VISIBLE_ANNOTATIONS = "RuntimeVisibleAnnotations";
127 private static final String RUNTIME_VISIBLE_PARAMETER_ANNOTATIONS = "RuntimeVisibleParameterAnnotations";
128
135129 private Map<String, Object> readAnnotationValues(DataInputStream bytes, int numPairs) throws IOException {
136130 Map<String, Object> values = new HashMap<String, Object>();
137131 for (int j = 0; j < numPairs; j++) {
138132 int memberNameIndex = bytes.readUnsignedShort();
139133 String memberName = ((ConstantUtf8) getConstantPool().getConstant(memberNameIndex)).getBytes();
140 if (DEBUG)
134 if (DEBUG) {
141135 System.out.println("memberName: " + memberName);
136 }
142137 Object value = readAnnotationValue(bytes);
143 if (DEBUG)
138 if (DEBUG) {
144139 System.out.println(memberName + ":" + value);
140 }
145141 values.put(memberName, value);
146142 }
147143 return values;
148144 }
145
149146
150147 private @DottedClassName
151148 String getAnnotationName(DataInputStream bytes) throws IOException {
152149 int annotationNameIndex = bytes.readUnsignedShort();
153150 String annotationName = ((ConstantUtf8) getConstantPool().getConstant(annotationNameIndex)).getBytes().replace('/', '.');
154151 annotationName = annotationName.substring(1, annotationName.length() - 1);
155 if (DEBUG)
152 if (DEBUG) {
156153 System.out.println("Annotation name: " + annotationName);
154 }
157155 return annotationName;
158156 }
157
159158
160159 private Object readAnnotationValue(DataInputStream bytes) throws IOException {
161160 try {
162161 char tag = (char) bytes.readUnsignedByte();
163 if (DEBUG)
162 if (DEBUG) {
164163 System.out.println("tag: " + tag);
164 }
165165 switch (tag) {
166166 case '[': {
167167 int sz = bytes.readUnsignedShort();
168 if (DEBUG)
168 if (DEBUG) {
169169 System.out.println("Array of " + sz + " entries");
170 }
170171 Object[] result = new Object[sz];
171 for (int i = 0; i < sz; i++)
172 for (int i = 0; i < sz; i++) {
172173 result[i] = readAnnotationValue(bytes);
174 }
173175 return result;
174176 }
175177 case 'B':
205207 return ((ConstantUtf8) c).getBytes();
206208 case 'c':
207209 String cName = ((ConstantUtf8) c).getBytes().replace('/', '.');
208 if (cName.startsWith("L") && cName.endsWith(";"))
210 if (cName.startsWith("L") && cName.endsWith(";")) {
209211 cName = cName.substring(1, cName.length() - 1);
210 if (DEBUG)
212 }
213 if (DEBUG) {
211214 System.out.println("cName: " + cName);
215 }
212216 return cName;
213217 default:
214 if (DEBUG)
218 if (DEBUG) {
215219 System.out.println("Impossible");
220 }
216221 throw new IllegalStateException("Impossible");
217222 }
218223 case '@':
221226 int cp1 = bytes.readUnsignedShort();
222227 ConstantUtf8 c1 = (ConstantUtf8) getConstantPool().getConstant(cp1);
223228 String cName = c1.getBytes().replace('/', '.');
224 if (cName.startsWith("L") && cName.endsWith(";"))
229 if (cName.startsWith("L") && cName.endsWith(";")) {
225230 cName = cName.substring(1, cName.length() - 1);
231 }
226232 int cp2 = bytes.readUnsignedShort();
227233 ConstantUtf8 c2 = (ConstantUtf8) getConstantPool().getConstant(cp2);
228234 String result = cName + "." + c2.getBytes();
230236 return result;
231237 }
232238 default:
233 if (DEBUG)
239 if (DEBUG) {
234240 System.out.println("Unexpected tag of " + tag);
241 }
235242 throw new IllegalArgumentException("Unexpected tag of " + tag);
236243 }
237244 } catch (RuntimeException e) {
242249 throw e;
243250 }
244251 }
252 */
245253
246254 @Override
247255 public void visitParameterAnnotation(ParameterAnnotations arg0) {
257265 boolean runtimeVisible = ae.isRuntimeVisible();
258266
259267 String name = ClassName.fromFieldSignature(ae.getAnnotationType());
260 if (name == null)
268 if (name == null) {
261269 continue;
270 }
262271 name = ClassName.toDottedClassName(name);
263272 Map<String, ElementValue> map = new HashMap<String, ElementValue>();
264273 for (ElementValuePair ev : ae.getElementValuePairs()) {
272281
273282 /*
274283 * (non-Javadoc)
275 *
284 *
276285 * @see
277286 * org.apache.bcel.classfile.Visitor#visitAnnotation(org.apache.bcel.classfile
278287 * .Annotations)
282291 for (AnnotationEntry ae : arg0.getAnnotationEntries()) {
283292 boolean runtimeVisible = ae.isRuntimeVisible();
284293 String name = ClassName.fromFieldSignature(ae.getAnnotationType());
285 if (name == null)
294 if (name == null) {
286295 continue;
296 }
287297 name = ClassName.toDottedClassName(name);
288298 Map<String, ElementValue> map = new HashMap<String, ElementValue>();
289299 for (ElementValuePair ev : ae.getElementValuePairs()) {
5959
6060 /**
6161 * Fixedup of from org.apache.bcel.classfile.Visitor
62 *
62 *
6363 * @author <A HREF="http://www.cs.umd.edu/~pugh">William Pugh</A>
6464 * @version 980818
6565 */
212212 }
213213
214214 // Attributes
215 @Override
215216 public void visitCode(Code obj) {
216217 visit(obj);
217218 }
218219
220 @Override
219221 public void visitCodeException(CodeException obj) {
220222 visit(obj);
221223 }
222224
223225 // Constants
226 @Override
224227 public void visitConstantClass(ConstantClass obj) {
225228 visit(obj);
226229 }
227230
231 @Override
228232 public void visitConstantDouble(ConstantDouble obj) {
229233 visit(obj);
230234 }
231235
236 @Override
232237 public void visitConstantFieldref(ConstantFieldref obj) {
233238 visit(obj);
234239 }
235240
241 @Override
236242 public void visitConstantFloat(ConstantFloat obj) {
237243 visit(obj);
238244 }
239245
246 @Override
240247 public void visitConstantInteger(ConstantInteger obj) {
241248 visit(obj);
242249 }
243250
251 @Override
244252 public void visitConstantInterfaceMethodref(ConstantInterfaceMethodref obj) {
245253 visit(obj);
246254 }
247255
256 @Override
248257 public void visitConstantLong(ConstantLong obj) {
249258 visit(obj);
250259 }
251260
261 @Override
252262 public void visitConstantMethodref(ConstantMethodref obj) {
253263 visit(obj);
254264 }
255265
266 @Override
256267 public void visitConstantNameAndType(ConstantNameAndType obj) {
257268 visit(obj);
258269 }
259270
271 @Override
260272 public void visitConstantPool(ConstantPool obj) {
261273 visit(obj);
262274 }
263275
276 @Override
264277 public void visitConstantString(ConstantString obj) {
265278 visit(obj);
266279 }
267280
281 @Override
268282 public void visitConstantUtf8(ConstantUtf8 obj) {
269283 visit(obj);
270284 }
271285
286 @Override
272287 public void visitConstantValue(ConstantValue obj) {
273288 visit(obj);
274289 }
275290
291 @Override
276292 public void visitDeprecated(org.apache.bcel.classfile.Deprecated obj) {
277293 visit(obj);
278294 }
279295
296 @Override
280297 public void visitExceptionTable(ExceptionTable obj) {
281298 visit(obj);
282299 }
283300
301 @Override
284302 public void visitField(Field obj) {
285303 visit(obj);
286304 }
287305
288306 // Extra classes (i.e. leaves in this context)
307 @Override
289308 public void visitInnerClass(InnerClass obj) {
290309 visit(obj);
291310 }
292311
312 @Override
293313 public void visitInnerClasses(InnerClasses obj) {
294314 visit(obj);
295315 }
296316
297317 // General classes
318 @Override
298319 public void visitJavaClass(JavaClass obj) {
299320 visit(obj);
300321 }
301322
323 @Override
302324 public void visitLineNumber(LineNumber obj) {
303325 visit(obj);
304326 }
305327
328 @Override
306329 public void visitLineNumberTable(LineNumberTable obj) {
307330 visit(obj);
308331 }
309332
333 @Override
310334 public void visitLocalVariable(LocalVariable obj) {
311335 visit(obj);
312336 }
313337
338 @Override
314339 public void visitLocalVariableTable(LocalVariableTable obj) {
315340 visit(obj);
316341 }
317342
343 @Override
318344 public void visitLocalVariableTypeTable(LocalVariableTypeTable obj) {
319345 visit(obj);
320346 }
321347
348 @Override
322349 public void visitMethod(Method obj) {
323350 visit(obj);
324351 }
325352
353 @Override
326354 public void visitSignature(Signature obj) {
327355 visit(obj);
328356 }
329357
358 @Override
330359 public void visitSourceFile(SourceFile obj) {
331360 visit(obj);
332361 }
333362
363 @Override
334364 public void visitSynthetic(Synthetic obj) {
335365 visit(obj);
336366 }
337367
368 @Override
338369 public void visitUnknown(Unknown obj) {
339370 visit(obj);
340371 }
341372
373 @Override
342374 public void visitStackMapEntry(StackMapEntry obj) {
343375 visit(obj);
344376 }
345377
378 @Override
346379 public void visitStackMap(StackMap obj) {
347380 visit(obj);
348381 }
3535 import org.apache.bcel.classfile.ConstantFloat;
3636 import org.apache.bcel.classfile.ConstantInteger;
3737 import org.apache.bcel.classfile.ConstantInterfaceMethodref;
38 import org.apache.bcel.classfile.ConstantInvokeDynamic;
3839 import org.apache.bcel.classfile.ConstantLong;
3940 import org.apache.bcel.classfile.ConstantMethodref;
4041 import org.apache.bcel.classfile.ConstantNameAndType;
152153 * Meaning of bytecode operands
153154 */
154155 static final byte[][] MEANING_OF_OPERANDS = {
155 // 0 1 2 3 4 5 6 7 8 9
156 {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, { M_INT }, { M_INT }, { M_CP }, { M_CP }, { M_CP },
157 { M_R }, { M_R }, { M_R }, { M_R }, { M_R }, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
158 {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, { M_R }, { M_R }, { M_R }, { M_R }, { M_R }, {}, {}, {}, {}, {}, {}, {}, {},
159 {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
160 {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
161 {},
162 {},
163 {},
164 {},
165 {},
166 // 130 1 2 3 4 5 6 7 8 9
167 {}, {}, { M_R, M_INT }, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, { M_BR },
168 { M_BR }, { M_BR }, { M_BR }, { M_BR }, { M_BR }, { M_BR }, { M_BR }, { M_BR }, { M_BR }, { M_BR }, { M_BR },
169 { M_BR }, { M_BR }, { M_BR },
170 { M_BR },
171 { M_R },
172 // 170 1 2 3 4 5 6 7 8 9
173 {}, {}, {}, {}, {}, {}, {}, {}, { M_CP }, { M_CP }, { M_CP }, { M_CP }, { M_CP }, { M_CP }, { M_CP },
174 { M_CP, M_PAD, M_PAD }, {}, { M_CP }, { M_UINT },
175 { M_CP },
176 // 190 1 2 3 4 5 6 7 8 9
177 {}, {}, { M_CP }, { M_CP }, {}, {}, { M_PAD }, { M_CP, M_UINT }, { M_BR }, { M_BR }, { M_BR }, { M_BR }, {}, {}, {},
178 {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
179 {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} };
156 // 0 1 2 3 4 5 6 7 8 9
157 {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, { M_INT }, { M_INT }, { M_CP }, { M_CP }, { M_CP },
158 { M_R }, { M_R }, { M_R }, { M_R }, { M_R }, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
159 {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, { M_R }, { M_R }, { M_R }, { M_R }, { M_R }, {}, {}, {}, {}, {}, {}, {}, {},
160 {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
161 {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
162 {},
163 {},
164 {},
165 {},
166 {},
167 // 130 1 2 3 4 5 6 7 8 9
168 {}, {}, { M_R, M_INT }, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, { M_BR },
169 { M_BR }, { M_BR }, { M_BR }, { M_BR }, { M_BR }, { M_BR }, { M_BR }, { M_BR }, { M_BR }, { M_BR }, { M_BR },
170 { M_BR }, { M_BR }, { M_BR },
171 { M_BR },
172 { M_R },
173 // 170 1 2 3 4 5 6 7 8 9
174 {}, {}, {}, {}, {}, {}, {}, {}, { M_CP }, { M_CP }, { M_CP }, { M_CP }, { M_CP }, { M_CP }, { M_CP },
175 { M_CP, M_PAD, M_PAD }, { M_CP, M_PAD, M_PAD }, { M_CP }, { M_UINT },
176 { M_CP },
177 // 190 1 2 3 4 5 6 7 8 9
178 {}, {}, { M_CP }, { M_CP }, {}, {}, { M_PAD }, { M_CP, M_UINT }, { M_BR }, { M_BR }, { M_BR }, { M_BR }, {}, {}, {},
179 {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
180 {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {} };
180181
181182 protected byte[] codeBytes;
182183
185186 // Accessors
186187
187188 public ClassDescriptor getClassDescriptorOperand() {
188 if (referencedClass == null)
189 if (referencedClass == null) {
189190 throw new IllegalStateException("getClassDescriptorOperand called but value not available");
191 }
190192 return referencedClass;
191193 }
192194
198200 public boolean isMethodCall() {
199201 switch(opcode) {
200202 default: return false;
201 case INVOKEDYNAMIC:
202203 case INVOKEINTERFACE:
203204 case INVOKESPECIAL:
204205 case INVOKEVIRTUAL:
207208
208209 }
209210 }
211
212 @SuppressFBWarnings("ES_COMPARING_STRINGS_WITH_EQ")
210213 public MethodDescriptor getMethodDescriptorOperand() {
211 if (nameConstantOperand == NOT_AVAILABLE)
214 if (nameConstantOperand == NOT_AVAILABLE || classConstantOperand == NOT_AVAILABLE) {
212215 throw new IllegalStateException("getMethodDescriptorOperand called but value not available");
216 }
213217
214218 if (referencedMethod == null) {
215219 referencedMethod = DescriptorFactory.instance().getMethodDescriptor(classConstantOperand, nameConstantOperand,
219223 }
220224
221225 public @CheckForNull
226 @SuppressFBWarnings("ES_COMPARING_STRINGS_WITH_EQ")
222227 XMethod getXMethodOperand() {
228 if (nameConstantOperand == NOT_AVAILABLE || classConstantOperand == NOT_AVAILABLE) {
229 throw new IllegalStateException("getXMethodOperand called but value not available");
230 }
231
223232 if (getReferencedXClass() != null && referencedXMethod == null) {
224233 referencedXMethod = Hierarchy2.findInvocationLeastUpperBound(getReferencedXClass(), nameConstantOperand,
225234 sigConstantOperand, opcode == INVOKESTATIC, opcode == INVOKEINTERFACE);
228237 return referencedXMethod;
229238 }
230239
240 @SuppressFBWarnings("ES_COMPARING_STRINGS_WITH_EQ")
231241 public FieldDescriptor getFieldDescriptorOperand() {
232 if (nameConstantOperand == NOT_AVAILABLE)
242 if (nameConstantOperand == NOT_AVAILABLE) {
233243 throw new IllegalStateException("getFieldDescriptorOperand called but value not available");
244 }
234245
235246 if (referencedField == null) {
236247 referencedField = DescriptorFactory.instance().getFieldDescriptor(classConstantOperand, nameConstantOperand,
241252
242253 public @CheckForNull
243254 XField getXFieldOperand() {
244 if (getReferencedXClass() != null && referencedXField == null)
255 if (getReferencedXClass() != null && referencedXField == null) {
245256 referencedXField = getReferencedXClass().findField(nameConstantOperand, sigConstantOperand,
246257 opcode == GETSTATIC || opcode == PUTSTATIC);
258 }
247259
248260 return referencedXField;
249261 }
252264 * If the current opcode has a class operand, get the associated class
253265 * constant, dot-formatted
254266 */
267 @SuppressFBWarnings("ES_COMPARING_STRINGS_WITH_EQ")
255268 public String getDottedClassConstantOperand() {
256269 if (dottedClassConstantOperand != null) {
257270 assert dottedClassConstantOperand != NOT_AVAILABLE;
258271 return dottedClassConstantOperand;
259272 }
260 if (classConstantOperand == NOT_AVAILABLE)
273 if (classConstantOperand == NOT_AVAILABLE) {
261274 throw new IllegalStateException("getDottedClassConstantOperand called but value not available");
275 }
262276 dottedClassConstantOperand = ClassName.toDottedClassName(classConstantOperand);
263277 return dottedClassConstantOperand;
264278 }
268282 * representation
269283 */
270284 @Deprecated
285 @SuppressFBWarnings("ES_COMPARING_STRINGS_WITH_EQ")
271286 public String getRefConstantOperand() {
272 if (refConstantOperand == NOT_AVAILABLE)
287 if (refConstantOperand == NOT_AVAILABLE) {
273288 throw new IllegalStateException("getRefConstantOperand called but value not available");
289 }
274290 if (refConstantOperand == null) {
275291 String dottedClassConstantOperand = getDottedClassConstantOperand();
276292 StringBuilder ref = new StringBuilder(dottedClassConstantOperand.length() + nameConstantOperand.length()
277293 + sigConstantOperand.length() + 5);
278294 ref.append(dottedClassConstantOperand).append(".").append(nameConstantOperand).append(" : ")
279 .append(replaceSlashesWithDots(sigConstantOperand));
295 .append(replaceSlashesWithDots(sigConstantOperand));
280296 refConstantOperand = ref.toString();
281297 }
282298 return refConstantOperand;
283299 }
284300
285301 /** If the current opcode has a reference constant operand, get its name */
302 @SuppressFBWarnings("ES_COMPARING_STRINGS_WITH_EQ")
286303 public String getNameConstantOperand() {
287 if (nameConstantOperand == NOT_AVAILABLE)
304 if (nameConstantOperand == NOT_AVAILABLE) {
288305 throw new IllegalStateException("getNameConstantOperand called but value not available");
306 }
289307 return nameConstantOperand;
290308 }
291309
293311 * If the current opcode has a reference constant operand, get its
294312 * signature, slash-formatted
295313 */
314 @SuppressFBWarnings("ES_COMPARING_STRINGS_WITH_EQ")
296315 public String getSigConstantOperand() {
297 if (sigConstantOperand == NOT_AVAILABLE)
316 if (sigConstantOperand == NOT_AVAILABLE) {
298317 throw new IllegalStateException("getSigConstantOperand called but value not available");
318 }
299319 return sigConstantOperand;
300320 }
301321
304324 * slash-formatted.
305325 */
306326 public @SlashedClassName
327 @SuppressFBWarnings("ES_COMPARING_STRINGS_WITH_EQ")
307328 String getClassConstantOperand() {
308 if (classConstantOperand == NOT_AVAILABLE)
329 if (classConstantOperand == NOT_AVAILABLE) {
309330 throw new IllegalStateException("getClassConstantOperand called but value not available");
331 }
310332 return classConstantOperand;
311333 }
312334
313335 /** If the current opcode has a string constant operand, get its name */
336 @SuppressFBWarnings("ES_COMPARING_STRINGS_WITH_EQ")
314337 public String getStringConstantOperand() {
315 if (stringConstantOperand == NOT_AVAILABLE)
338 if (stringConstantOperand == NOT_AVAILABLE) {
316339 throw new IllegalStateException("getStringConstantOperand called but value not available");
340 }
317341 return stringConstantOperand;
318342 }
319343
320344 public Constant getConstantRefOperand() {
321 if (constantRefOperand == null)
345 if (constantRefOperand == null) {
322346 throw new IllegalStateException("getConstantRefOperand called but value not available");
347 }
323348 return constantRefOperand;
324349 }
325350
332357 }
333358
334359 public int getRegisterOperand() {
335 if (registerOperand == -1)
360 if (registerOperand == -1) {
336361 throw new IllegalStateException("getRegisterOperand called but value not available");
362 }
337363 return registerOperand;
338364 }
339365
346372 return longConstant;
347373 }
348374 public int getBranchOffset() {
349 if (branchOffset == INVALID_OFFSET)
375 if (branchOffset == INVALID_OFFSET) {
350376 throw new IllegalStateException("getBranchOffset called but value not available");
377 }
351378 return branchOffset;
352379 }
353380
354381 public int getBranchTarget() {
355 if (branchTarget == INVALID_OFFSET)
382 if (branchTarget == INVALID_OFFSET) {
356383 throw new IllegalStateException("getBranchTarget called but value not available");
384 }
357385 return branchTarget;
358386 }
359387
360388 public int getBranchFallThrough() {
361 if (branchFallThrough == INVALID_OFFSET)
389 if (branchFallThrough == INVALID_OFFSET) {
362390 throw new IllegalStateException("getBranchFallThrough called but value not available");
391 }
363392 return branchFallThrough;
364393 }
365394
366395 public int getDefaultSwitchOffset() {
367 if (defaultSwitchOffset == INVALID_OFFSET)
396 if (defaultSwitchOffset == INVALID_OFFSET) {
368397 throw new IllegalStateException("getDefaultSwitchOffset called but value not available");
398 }
369399 return defaultSwitchOffset;
370400 }
371401
384414 * 0 for current opcode, 1 for one before that, etc.
385415 */
386416 public int getPrevOpcode(int offset) {
387 if (offset < 0)
417 if (offset < 0) {
388418 throw new IllegalArgumentException("offset (" + offset + ") must be nonnegative");
389 if (offset >= prevOpcode.length || offset > sizePrevOpcodeBuffer)
419 }
420 if (offset >= prevOpcode.length || offset > sizePrevOpcodeBuffer) {
390421 return NOP;
422 }
391423 int pos = currentPosInPrevOpcodeBuffer - offset;
392 if (pos < 0)
424 if (pos < 0) {
393425 pos += prevOpcode.length;
426 }
394427 return prevOpcode[pos];
395428 }
396429
423456
424457 @SuppressFBWarnings("EI")
425458 public int[] getSwitchOffsets() {
426 if (switchOffsets == null)
459 if (switchOffsets == null) {
427460 throw new IllegalStateException("getSwitchOffsets called but value not available");
461 }
428462 return switchOffsets;
429463 }
430464
431465 @SuppressFBWarnings("EI")
432466 public int[] getSwitchLabels() {
433 if (switchLabels == null)
467 if (switchLabels == null) {
434468 throw new IllegalStateException("getSwitchLabels called but value not available");
469 }
435470 return switchLabels;
436471 }
437472
458493 // Sort by offset
459494 for (int j = 0; j < npairs; j++) {
460495 int min = j;
461 for (int k = j + 1; k < npairs; k++)
462 if (switchOffsets[min] > switchOffsets[k])
496 for (int k = j + 1; k < npairs; k++) {
497 if (switchOffsets[min] > switchOffsets[k]) {
463498 min = k;
499 }
500 }
464501 if (min > j) {
465502 int tmp = switchOffsets[min];
466503 switchOffsets[min] = switchOffsets[j];
485522 }
486523
487524 public boolean atCatchBlock() {
488 for (CodeException e : getCode().getExceptionTable())
489 if (e.getHandlerPC() == getPC())
525 for (CodeException e : getCode().getExceptionTable()) {
526 if (e.getHandlerPC() == getPC()) {
490527 return true;
528 }
529 }
491530 return false;
492531 }
493532
494533 @Override
495534 public void visit(Code obj) {
535 // if (getXMethod().usesInvokeDynamic()) {
536 // AnalysisContext.currentAnalysisContext().analysisSkippedDueToInvokeDynamic(getXMethod());
537 // return;
538 // }
496539 sizePrevOpcodeBuffer = 0;
497540 currentPosInPrevOpcodeBuffer = prevOpcode.length - 1;
498541
512555
513556 sizePrevOpcodeBuffer++;
514557 currentPosInPrevOpcodeBuffer++;
515 if (currentPosInPrevOpcodeBuffer >= prevOpcode.length)
558 if (currentPosInPrevOpcodeBuffer >= prevOpcode.length) {
516559 currentPosInPrevOpcodeBuffer = 0;
560 }
517561 prevOpcode[currentPosInPrevOpcodeBuffer] = opcode;
518562 i++;
519563 // System.out.println(OPCODE_NAMES[opCode]);
522566
523567 if (opcode == LOOKUPSWITCH) {
524568 int pad = 4 - (i & 3);
525 if (pad == 4)
569 if (pad == 4) {
526570 pad = 0;
571 }
527572 int count = pad;
528 while (count > 0)
573 while (count > 0) {
529574 count -= byteStream.skipBytes(count);
575 }
530576 i += pad;
531577 defaultSwitchOffset = byteStream.readInt();
532578 branchOffset = defaultSwitchOffset;
544590 sortByOffset(switchOffsets, switchLabels);
545591 } else if (opcode == TABLESWITCH) {
546592 int pad = 4 - (i & 3);
547 if (pad == 4)
593 if (pad == 4) {
548594 pad = 0;
595 }
549596 int count = pad;
550 while (count > 0)
597 while (count > 0) {
551598 count -= byteStream.skipBytes(count);
599 }
552600 i += pad;
553601 defaultSwitchOffset = byteStream.readInt();
554602 branchOffset = defaultSwitchOffset;
593641 i += 2;
594642 break;
595643 default:
596 throw new IllegalStateException("bad wide bytecode: " + OPCODE_NAMES[opcode]);
644 throw new IllegalStateException(String.format("bad wide bytecode %d: %s" , opcode, OPCODE_NAMES[opcode]));
597645 }
598 } else
599 throw new IllegalStateException("bad unpredicatable bytecode: " + OPCODE_NAMES[opcode]);
646 } else {
647 throw new IllegalStateException(String.format("bad unpredicatable bytecode %d: %s" , opcode, OPCODE_NAMES[opcode]));
648 }
600649 } else {
601 if (byteStreamArgCount < 0)
602 throw new IllegalStateException("bad length for bytecode: " + OPCODE_NAMES[opcode]);
650 if (byteStreamArgCount < 0) {
651 throw new IllegalStateException(String.format("bad length for bytecode %d: %s" , opcode, OPCODE_NAMES[opcode]));
652 }
603653 for (int k = 0; k < TYPE_OF_OPERANDS[opcode].length; k++) {
604654
605655 int v;
608658 boolean unsigned = (m == M_CP || m == M_R || m == M_UINT);
609659 switch (t) {
610660 case T_BYTE:
611 if (unsigned)
661 if (unsigned) {
612662 v = byteStream.readUnsignedByte();
613 else
663 } else {
614664 v = byteStream.readByte();
665 }
615666 /*
616667 * System.out.print("Read byte " + v);
617668 * System.out.println(" with meaning" + m);
619670 i++;
620671 break;
621672 case T_SHORT:
622 if (unsigned)
673 if (unsigned) {
623674 v = byteStream.readUnsignedShort();
624 else
675 } else {
625676 v = byteStream.readShort();
677 }
626678 i += 2;
627679 break;
628680 case T_INT:
645697 classConstantOperand = getStringFromIndex(clazz.getNameIndex());
646698 referencedClass = DescriptorFactory.createClassDescriptor(classConstantOperand);
647699
648 } else if (constantRefOperand instanceof ConstantInteger)
700 } else if (constantRefOperand instanceof ConstantInteger) {
649701 intConstant = ((ConstantInteger) constantRefOperand).getBytes();
650 else if (constantRefOperand instanceof ConstantLong)
702 } else if (constantRefOperand instanceof ConstantLong) {
651703 longConstant = ((ConstantLong) constantRefOperand).getBytes();
652 else if (constantRefOperand instanceof ConstantFloat)
704 } else if (constantRefOperand instanceof ConstantFloat) {
653705 floatConstant = ((ConstantFloat) constantRefOperand).getBytes();
654 else if (constantRefOperand instanceof ConstantDouble)
706 } else if (constantRefOperand instanceof ConstantDouble) {
655707 doubleConstant = ((ConstantDouble) constantRefOperand).getBytes();
656 else if (constantRefOperand instanceof ConstantString) {
708 } else if (constantRefOperand instanceof ConstantString) {
657709 int s = ((ConstantString) constantRefOperand).getStringIndex();
658710
659711 stringConstantOperand = getStringFromIndex(s);
668720 nameConstantOperand = getStringFromIndex(sig.getNameIndex());
669721 sigConstantOperand = getStringFromIndex(sig.getSignatureIndex());
670722 refConstantOperand = null;
723 } else if (constantRefOperand instanceof ConstantInvokeDynamic) {
724 ConstantInvokeDynamic id = (ConstantInvokeDynamic) constantRefOperand;
725 ConstantNameAndType sig = (ConstantNameAndType) getConstantPool().getConstant(
726 id.getNameAndTypeIndex());
727 nameConstantOperand = getStringFromIndex(sig.getNameIndex());
728 sigConstantOperand = getStringFromIndex(sig.getSignatureIndex());
729
730
671731 }
672732 break;
673733 case M_R:
783843 case DSTORE:
784844 isRegisterStore = true;
785845 break;
786
846 default:
847 break;
787848 }
788849
789850 switch (opcode) {
812873 case PUTFIELD:
813874 refFieldIsStatic = false;
814875 break;
876 default:
877 break;
815878 }
816879
817880 nextPC = i;
818 if (beforeOpcode(opcode))
881 if (beforeOpcode(opcode)) {
819882 sawOpcode(opcode);
883 }
820884 afterOpcode(opcode);
821885
822886 if (opcode == TABLESWITCH) {
823887 sawInt(switchLow);
824888 sawInt(switchHigh);
825 int prevOffset = i - PC;
889 // int prevOffset = i - PC;
826890 for (int o = 0; o <= switchHigh - switchLow; o++) {
827891 sawBranchTo(switchOffsets[o] + PC);
828 prevOffset = switchOffsets[o];
892 // prevOffset = switchOffsets[o];
829893 }
830894 sawBranchTo(defaultSwitchOffset + PC);
831895 } else if (opcode == LOOKUPSWITCH) {
832896 sawInt(switchOffsets.length);
833 int prevOffset = i - PC;
897 // int prevOffset = i - PC;
834898 for (int o = 0; o < switchOffsets.length; o++) {
835899 sawBranchTo(switchOffsets[o] + PC);
836 prevOffset = switchOffsets[o];
900 // prevOffset = switchOffsets[o];
837901 sawInt(switchLabels[o]);
838902 }
839903 sawBranchTo(defaultSwitchOffset + PC);
840 } else
904 } else {
841905 for (int k = 0; k < TYPE_OF_OPERANDS[opcode].length; k++) {
842906 int m = MEANING_OF_OPERANDS[opcode][k];
843907 switch (m) {
845909 sawBranchTo(branchOffset + PC);
846910 break;
847911 case M_CP:
848 if (constantRefOperand instanceof ConstantInteger)
912 if (constantRefOperand instanceof ConstantInteger) {
849913 sawInt(intConstant);
850 else if (constantRefOperand instanceof ConstantLong)
914 } else if (constantRefOperand instanceof ConstantLong) {
851915 sawLong(longConstant);
852 else if (constantRefOperand instanceof ConstantFloat)
916 } else if (constantRefOperand instanceof ConstantFloat) {
853917 sawFloat(floatConstant);
854 else if (constantRefOperand instanceof ConstantDouble)
918 } else if (constantRefOperand instanceof ConstantDouble) {
855919 sawDouble(doubleConstant);
856 else if (constantRefOperand instanceof ConstantString)
920 } else if (constantRefOperand instanceof ConstantString) {
857921 sawString(stringConstantOperand);
858 else if (constantRefOperand instanceof ConstantFieldref)
922 } else if (constantRefOperand instanceof ConstantFieldref) {
859923 sawField();
860 else if (constantRefOperand instanceof ConstantMethodref)
924 } else if (constantRefOperand instanceof ConstantMethodref) {
861925 sawMethod();
862 else if (constantRefOperand instanceof ConstantInterfaceMethodref)
926 } else if (constantRefOperand instanceof ConstantInterfaceMethodref) {
863927 sawIMethod();
864 else if (constantRefOperand instanceof ConstantClass)
928 } else if (constantRefOperand instanceof ConstantClass) {
865929 sawClass();
930 }
866931 break;
867932 case M_R:
868933 sawRegister(registerOperand);
870935 case M_INT:
871936 sawInt(intConstant);
872937 break;
938 default:
939 break;
873940 }
874941 }
942 }
875943 }
876944 } catch (IOException e) {
877945 AnalysisContext.logError("Error while dismantling bytecode", e);
9371005
9381006 public void printOpCode(int seen) {
9391007 System.out.print(" " + this.getClass().getSimpleName() + ": [" + formatter.format(getPC()) + "] " + OPCODE_NAMES[seen]);
940 if ((seen == INVOKEVIRTUAL) || (seen == INVOKESPECIAL) || (seen == INVOKEINTERFACE) || (seen == INVOKESTATIC))
1008 if ((seen == INVOKEVIRTUAL) || (seen == INVOKESPECIAL) || (seen == INVOKEINTERFACE) || (seen == INVOKESTATIC)) {
9411009 System.out.print(" " + getClassConstantOperand() + "." + getNameConstantOperand() + " " + getSigConstantOperand());
942 else if (seen == LDC || seen == LDC_W || seen == LDC2_W) {
1010 } else if (seen == LDC || seen == LDC_W || seen == LDC2_W) {
9431011 Constant c = getConstantRefOperand();
944 if (c instanceof ConstantString)
1012 if (c instanceof ConstantString) {
9451013 System.out.print(" \"" + getStringConstantOperand() + "\"");
946 else if (c instanceof ConstantClass)
1014 } else if (c instanceof ConstantClass) {
9471015 System.out.print(" " + getClassConstantOperand());
948 else
1016 } else {
9491017 System.out.print(" " + c);
950 } else if ((seen == ALOAD) || (seen == ASTORE))
1018 }
1019 } else if ((seen == ALOAD) || (seen == ASTORE)) {
9511020 System.out.print(" " + getRegisterOperand());
952 else if ((seen == GOTO) || (seen == GOTO_W) || (seen == IF_ACMPEQ) || (seen == IF_ACMPNE) || (seen == IF_ICMPEQ)
1021 } else if ((seen == GOTO) || (seen == GOTO_W) || (seen == IF_ACMPEQ) || (seen == IF_ACMPNE) || (seen == IF_ICMPEQ)
9531022 || (seen == IF_ICMPGE) || (seen == IF_ICMPGT) || (seen == IF_ICMPLE) || (seen == IF_ICMPLT)
9541023 || (seen == IF_ICMPNE) || (seen == IFEQ) || (seen == IFGE) || (seen == IFGT) || (seen == IFLE) || (seen == IFLT)
955 || (seen == IFNE) || (seen == IFNONNULL) || (seen == IFNULL))
1024 || (seen == IFNE) || (seen == IFNONNULL) || (seen == IFNULL)) {
9561025 System.out.print(" " + getBranchTarget());
957 else if ((seen == NEW) || (seen == INSTANCEOF))
1026 } else if ((seen == NEW) || (seen == INSTANCEOF)) {
9581027 System.out.print(" " + getClassConstantOperand());
959 else if ((seen == TABLESWITCH) || (seen == LOOKUPSWITCH)) {
1028 } else if ((seen == TABLESWITCH) || (seen == LOOKUPSWITCH)) {
9601029 System.out.print(" [");
9611030 int switchPC = getPC();
9621031 int[] offsets = getSwitchOffsets();
9801049 return codeBytes[nextPC] & 0xff;
9811050 }
9821051
1052 public int getNextCodeByte(int offset) {
1053 return codeBytes[nextPC + offset] & 0xff;
1054 }
9831055 public boolean isReturn(int opcode) {
9841056 switch (opcode) {
9851057 case IRETURN:
9931065 return false;
9941066 }
9951067 }
1068 public boolean isShift(int opcode) {
1069 switch (opcode) {
1070 case IUSHR:
1071 case ISHR:
1072 case ISHL:
1073 case LUSHR:
1074 case LSHR:
1075 case LSHL:
1076 return true;
1077 default:
1078 return false;
1079 }
1080 }
9961081
9971082 public static boolean areOppositeBranches(int opcode1, int opcode2) {
998 if (!isBranch(opcode1))
1083 if (!isBranch(opcode1)) {
9991084 throw new IllegalArgumentException(OPCODE_NAMES[opcode1] + " isn't a branch");
1000 if (!isBranch(opcode2))
1085 }
1086 if (!isBranch(opcode2)) {
10011087 throw new IllegalArgumentException(OPCODE_NAMES[opcode2] + " isn't a branch");
1088 }
10021089 switch (opcode1) {
10031090 case IF_ACMPEQ:
10041091 case IF_ACMPNE:
10751162 * @return Returns the referencedXClass.
10761163 */
10771164 private XClass getReferencedXClass() {
1078 if (referencedXClass == null && referencedClass != null)
1165 if (referencedXClass == null && referencedClass != null) {
10791166 try {
10801167 referencedXClass = Global.getAnalysisCache().getClassAnalysis(XClass.class, referencedClass);
10811168 } catch (CheckedAnalysisException e) {
10821169 assert true;
10831170 }
1171 }
10841172 return referencedXClass;
10851173 }
10861174 }
3131 public class LVTHelper {
3232 /**
3333 * returns the local variable at an index int the scope of PC
34 *
34 *
3535 * @param lvt
3636 * the local variable table
3737 * @param index
4646 for (int i = 0; i < length; i++) {
4747 if (lvs[i].getIndex() == index) {
4848 int startPC = lvs[i].getStartPC();
49 if ((pc >= startPC) && (pc < (startPC + lvs[i].getLength())))
49 if ((pc >= startPC) && (pc < (startPC + lvs[i].getLength()))) {
5050 return lvs[i];
51 }
5152 }
5253 }
5354
1919 package edu.umd.cs.findbugs.visitclass;
2020
2121 import java.util.Arrays;
22 import java.util.Collection;
2223 import java.util.HashSet;
2324 import java.util.Set;
2425 import java.util.regex.Pattern;
3031 import org.apache.bcel.classfile.Code;
3132 import org.apache.bcel.classfile.CodeException;
3233 import org.apache.bcel.classfile.Constant;
34 import org.apache.bcel.classfile.ConstantCP;
3335 import org.apache.bcel.classfile.ConstantClass;
36 import org.apache.bcel.classfile.ConstantInterfaceMethodref;
37 import org.apache.bcel.classfile.ConstantMethodref;
38 import org.apache.bcel.classfile.ConstantNameAndType;
3439 import org.apache.bcel.classfile.ConstantPool;
3540 import org.apache.bcel.classfile.ConstantUtf8;
3641 import org.apache.bcel.classfile.EnclosingMethod;
5762 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
5863 import edu.umd.cs.findbugs.classfile.DescriptorFactory;
5964 import edu.umd.cs.findbugs.classfile.FieldDescriptor;
65 import edu.umd.cs.findbugs.classfile.FieldOrMethodDescriptor;
6066 import edu.umd.cs.findbugs.classfile.Global;
6167 import edu.umd.cs.findbugs.classfile.IAnalysisCache;
6268 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
150156 * @return current code attribute
151157 */
152158 public Code getCode() {
153 if (code == null)
159 if (code == null) {
154160 throw new IllegalStateException("Not visiting Code");
161 }
155162 return code;
156163 }
157164
161168
162169 public Set<String> getSurroundingCaughtExceptions(int pc, int maxTryBlockSize) {
163170 HashSet<String> result = new HashSet<String>();
164 if (code == null)
171 if (code == null) {
165172 throw new IllegalStateException("Not visiting Code");
173 }
166174 int size = maxTryBlockSize;
167 if (code.getExceptionTable() == null)
175 if (code.getExceptionTable() == null) {
168176 return result;
177 }
169178 for (CodeException catchBlock : code.getExceptionTable()) {
170179 int startPC = catchBlock.getStartPC();
171180 int endPC = catchBlock.getEndPC();
176185 size = thisSize;
177186 Constant kind = constantPool.getConstant(catchBlock.getCatchType());
178187 result.add("C" + catchBlock.getCatchType());
179 } else if (size == thisSize)
188 } else if (size == thisSize) {
180189 result.add("C" + catchBlock.getCatchType());
190 }
181191 }
182192 }
183193 return result;
200210 * @return number of lines of code in try block
201211 */
202212 public int getSizeOfSurroundingTryBlock(String vmNameOfExceptionClass, int pc) {
203 if (code == null)
213 if (code == null) {
204214 throw new IllegalStateException("Not visiting Code");
215 }
205216 return Util.getSizeOfSurroundingTryBlock(constantPool, code, vmNameOfExceptionClass, pc);
206217 }
207218
210221 }
211222
212223 public CodeException getSurroundingTryBlock(String vmNameOfExceptionClass, int pc) {
213 if (code == null)
224 if (code == null) {
214225 throw new IllegalStateException("Not visiting Code");
226 }
215227 return Util.getSurroundingTryBlock(constantPool, code, vmNameOfExceptionClass, pc);
216228 }
217229
221233 code = obj;
222234 super.visitCode(obj);
223235 CodeException[] exceptions = obj.getExceptionTable();
224 for (CodeException exception : exceptions)
236 for (CodeException exception : exceptions) {
225237 exception.accept(this);
238 }
226239 Attribute[] attributes = obj.getAttributes();
227 for (Attribute attribute : attributes)
240 for (Attribute attribute : attributes) {
228241 attribute.accept(this);
242 }
229243 visitAfter(obj);
230244 code = null;
231245 }
247261 for (int i = 1; i < constant_pool.length; i++) {
248262 constant_pool[i].accept(this);
249263 byte tag = constant_pool[i].getTag();
250 if ((tag == CONSTANT_Double) || (tag == CONSTANT_Long))
264 if ((tag == CONSTANT_Double) || (tag == CONSTANT_Long)) {
251265 i++;
266 }
252267 }
253268 }
254269
255270 private void doVisitField(Field field) {
256 if (visitingField)
271 if (visitingField) {
257272 throw new IllegalStateException("visitField called when already visiting a field");
273 }
258274 visitingField = true;
259275 this.field = field;
260276 try {
264280 fieldIsStatic = field.isStatic();
265281 field.accept(this);
266282 Attribute[] attributes = field.getAttributes();
267 for (Attribute attribute : attributes)
283 for (Attribute attribute : attributes) {
268284 attribute.accept(this);
285 }
269286 } finally {
270287 visitingField = false;
271288 this.field = null;
274291 }
275292
276293 public void doVisitMethod(Method method) {
277 if (visitingMethod)
294 if (visitingMethod) {
278295 throw new IllegalStateException("doVisitMethod called when already visiting a method");
296 }
279297 visitingMethod = true;
280298 try {
281299 this.method = method;
284302 assert thisMethodInfo != null : "Can't get method info for " + getFullyQualifiedMethodName();
285303 this.method.accept(this);
286304 Attribute[] attributes = method.getAttributes();
287 for (Attribute attribute : attributes)
305 for (Attribute attribute : attributes) {
288306 attribute.accept(this);
307 }
289308 } finally {
290309 visitingMethod = false;
291310 this.method = null;
294313 }
295314
296315 public boolean amVisitingMainMethod() {
297 if (!visitingMethod)
316 if (!visitingMethod) {
298317 throw new IllegalStateException("Not visiting a method");
299 if (!method.isStatic())
318 }
319 if (!method.isStatic()) {
300320 return false;
301 if (!getMethodName().equals("main"))
321 }
322 if (!"main".equals(getMethodName())) {
302323 return false;
303 if (!getMethodSig().equals("([Ljava/lang/String;)V"))
324 }
325 if (!"([Ljava/lang/String;)V".equals(getMethodSig())) {
304326 return false;
327 }
305328 return true;
306329
307330 }
311334 public void visitInnerClasses(InnerClasses obj) {
312335 super.visitInnerClasses(obj);
313336 InnerClass[] inner_classes = obj.getInnerClasses();
314 for (InnerClass inner_class : inner_classes)
337 for (InnerClass inner_class : inner_classes) {
315338 inner_class.accept(this);
339 }
316340 }
317341
318342 public void visitAfter(JavaClass obj) {
325349 boolean visitMethodsInCallOrder;
326350
327351
328 protected boolean isVisitMethodsInCallOrder() {
352 protected boolean isVisitMethodsInCallOrder() {
329353 return visitMethodsInCallOrder;
330354 }
331355
344368 constantPool.accept(this);
345369 Field[] fields = obj.getFields();
346370 Attribute[] attributes = obj.getAttributes();
347 for (Field field : fields)
371 for (Field field : fields) {
348372 doVisitField(field);
373 }
349374 boolean didInCallOrder = false;
350375
351376 if (visitMethodsInCallOrder) {
356381
357382 ClassContext classContext = analysisCache.getClassAnalysis(ClassContext.class, c);
358383 didInCallOrder = true;
359 for (Method m : classContext.getMethodsInCallOrder())
384 for (Method m : classContext.getMethodsInCallOrder()) {
360385 doVisitMethod(m);
386 }
361387
362388 } catch (CheckedAnalysisException e) {
363 AnalysisContext.logError("Error trying to visit methods in order", e);
389 AnalysisContext.logError("Error trying to visit methods in order", e);
364390 }
365391 }
366 if (!didInCallOrder)
367 for (Method m : getMethodVisitOrder(obj))
392 if (!didInCallOrder) {
393 for (Method m : getMethodVisitOrder(obj)) {
368394 doVisitMethod(m);
369 for (Attribute attribute : attributes)
395 }
396 }
397 for (Attribute attribute : attributes) {
370398 attribute.accept(this);
399 }
371400 visitAfter(obj);
372401 }
373402 }
384413 superclassName = dottedSuperclassName.replace('.', '/');
385414
386415 ClassDescriptor cDesc = DescriptorFactory.createClassDescriptor(className);
387 if (!FindBugs.isNoAnalysis())
388 try {
389 thisClassInfo = (ClassInfo) Global.getAnalysisCache().getClassAnalysis(XClass.class, cDesc);
390 } catch (CheckedAnalysisException e) {
391 throw new AssertionError("Can't find ClassInfo for " + cDesc);
416 if (!FindBugs.isNoAnalysis()) {
417 try {
418 thisClassInfo = (ClassInfo) Global.getAnalysisCache().getClassAnalysis(XClass.class, cDesc);
419 } catch (CheckedAnalysisException e) {
420 throw new AssertionError("Can't find ClassInfo for " + cDesc);
421 }
392422 }
393423
394424 super.visitJavaClass(obj);
398428 public void visitLineNumberTable(LineNumberTable obj) {
399429 super.visitLineNumberTable(obj);
400430 LineNumber[] line_number_table = obj.getLineNumberTable();
401 for (LineNumber aLine_number_table : line_number_table)
431 for (LineNumber aLine_number_table : line_number_table) {
402432 aLine_number_table.accept(this);
433 }
403434 }
404435
405436 @Override
406437 public void visitLocalVariableTable(LocalVariableTable obj) {
407438 super.visitLocalVariableTable(obj);
408439 LocalVariable[] local_variable_table = obj.getLocalVariableTable();
409 for (LocalVariable aLocal_variable_table : local_variable_table)
440 for (LocalVariable aLocal_variable_table : local_variable_table) {
410441 aLocal_variable_table.accept(this);
442 }
411443 }
412444
413445 // Accessors
414446
415447 public XClass getXClass() {
416 if (thisClassInfo == null)
448 if (thisClassInfo == null) {
417449 throw new AssertionError("XClass information not set");
450 }
418451 return thisClassInfo;
419452 }
420453
496529
497530 /** If currently visiting a method, get the method's fully qualified name */
498531 public String getFullyQualifiedMethodName() {
499 if (!visitingMethod)
532 if (!visitingMethod) {
500533 throw new IllegalStateException("getFullyQualifiedMethodName called while not visiting method");
534 }
501535 if (fullyQualifiedMethodName == null) {
502 getDottedSuperclassName();
503536 getMethodName();
504537 getDottedMethodSig();
505538 StringBuilder ref = new StringBuilder(5 + dottedClassName.length() + methodName.length() + dottedMethodSig.length());
526559
527560 /** If currently visiting a field, get the field's Field object */
528561 public Field getField() {
529 if (!visitingField)
562 if (!visitingField) {
530563 throw new IllegalStateException("getField called while not visiting field");
564 }
531565 return field;
532566 }
533567
534568 /** If currently visiting a method, get the method's Method object */
535569 public Method getMethod() {
536 if (!visitingMethod)
570 if (!visitingMethod) {
537571 throw new IllegalStateException("getMethod called while not visiting method");
572 }
538573 return method;
539574 }
540575
541576 /** If currently visiting a method, get the method's name */
542577 public String getMethodName() {
543 if (!visitingMethod)
578 if (!visitingMethod) {
544579 throw new IllegalStateException("getMethodName called while not visiting method");
545 if (methodName == null)
580 }
581 if (methodName == null) {
546582 methodName = getStringFromIndex(method.getNameIndex());
583 }
547584
548585 return methodName;
549586 }
560597 case ')':
561598 return count;
562599 case '[':
563 if (!inArray)
600 if (!inArray) {
564601 count++;
602 }
565603 inArray = true;
566604 break;
567605 case 'L':
568 if (!inArray)
606 if (!inArray) {
569607 count++;
570 while (signature.charAt(pos) != ';')
608 }
609 while (signature.charAt(pos) != ';') {
571610 pos++;
611 }
572612 pos++;
573613 inArray = false;
574614 break;
575615 default:
576 if (!inArray)
616 if (!inArray) {
577617 count++;
618 }
578619 inArray = false;
579620 break;
580621 }
582623
583624 }
584625
626 /**
627 * Returns true if given constant pool probably has a reference to any of supplied methods
628 * Useful to exclude from analysis uninteresting classes
629 * @param cp constant pool
630 * @param methods methods collection
631 * @return true if method is found
632 */
633 public static boolean hasInterestingMethod(ConstantPool cp, Collection<MethodDescriptor> methods) {
634 for(Constant c : cp.getConstantPool()) {
635 if(c instanceof ConstantMethodref || c instanceof ConstantInterfaceMethodref) {
636 ConstantCP desc = (ConstantCP)c;
637 ConstantNameAndType nameAndType = (ConstantNameAndType) cp.getConstant(desc.getNameAndTypeIndex());
638 String className = cp.getConstantString(desc.getClassIndex(), CONSTANT_Class);
639 String name = ((ConstantUtf8)cp.getConstant(nameAndType.getNameIndex())).getBytes();
640 String signature = ((ConstantUtf8)cp.getConstant(nameAndType.getSignatureIndex())).getBytes();
641 // We don't know whether method is static thus cannot use equals
642 int hash = FieldOrMethodDescriptor.getNameSigHashCode(name, signature);
643 for(MethodDescriptor method : methods) {
644 if (method.getNameSigHashCode() == hash
645 && (method.getSlashedClassName().isEmpty() || method.getSlashedClassName().equals(className))
646 && method.getName().equals(name) && method.getSignature().equals(signature)) {
647 return true;
648 }
649 }
650 }
651 }
652 return false;
653 }
654
655 public static boolean hasInterestingClass(ConstantPool cp, Collection<String> classes) {
656 for(Constant c : cp.getConstantPool()) {
657 if(c instanceof ConstantClass) {
658 String className = ((ConstantUtf8)cp.getConstant(((ConstantClass)c).getNameIndex())).getBytes();
659 if(classes.contains(className)) {
660 return true;
661 }
662 }
663 }
664 return false;
665 }
666
585667 public int getNumberMethodArguments() {
586668 return getNumberArguments(getMethodSig());
587669 }
591673 * signature
592674 */
593675 public String getMethodSig() {
594 if (!visitingMethod)
676 if (!visitingMethod) {
595677 throw new IllegalStateException("getMethodSig called while not visiting method");
596 if (methodSig == null)
678 }
679 if (methodSig == null) {
597680 methodSig = getStringFromIndex(method.getSignatureIndex());
681 }
598682 return methodSig;
599683 }
600684
601685 /** If currently visiting a method, get the method's dotted method signature */
602686 public String getDottedMethodSig() {
603 if (!visitingMethod)
687 if (!visitingMethod) {
604688 throw new IllegalStateException("getDottedMethodSig called while not visiting method");
605 if (dottedMethodSig == null)
689 }
690 if (dottedMethodSig == null) {
606691 dottedMethodSig = getMethodSig().replace('/', '.');
692 }
607693 return dottedMethodSig;
608694 }
609695
610696 /** If currently visiting a field, get the field's name */
611697 public String getFieldName() {
612 if (!visitingField)
698 if (!visitingField) {
613699 throw new IllegalStateException("getFieldName called while not visiting field");
614 if (fieldName == null)
700 }
701 if (fieldName == null) {
615702 fieldName = getStringFromIndex(field.getNameIndex());
703 }
616704
617705 return fieldName;
618706 }
619707
620708 /** If currently visiting a field, get the field's slash-formatted signature */
621709 public String getFieldSig() {
622 if (!visitingField)
710 if (!visitingField) {
623711 throw new IllegalStateException("getFieldSig called while not visiting field");
624 if (fieldSig == null)
712 }
713 if (fieldSig == null) {
625714 fieldSig = getStringFromIndex(field.getSignatureIndex());
715 }
626716 return fieldSig;
627717 }
628718
629719 /** If currently visiting a field, return whether or not the field is static */
630720 public boolean getFieldIsStatic() {
631 if (!visitingField)
721 if (!visitingField) {
632722 throw new IllegalStateException("getFieldIsStatic called while not visiting field");
723 }
633724 return fieldIsStatic;
634725 }
635726
636727 /** If currently visiting a field, get the field's fully qualified name */
637728 public String getFullyQualifiedFieldName() {
638 if (!visitingField)
729 if (!visitingField) {
639730 throw new IllegalStateException("getFullyQualifiedFieldName called while not visiting field");
640 if (fullyQualifiedFieldName == null)
731 }
732 if (fullyQualifiedFieldName == null) {
641733 fullyQualifiedFieldName = getDottedClassName() + "." + getFieldName() + " : " + getFieldSig();
734 }
642735 return fullyQualifiedFieldName;
643736 }
644737
645738 /** If currently visiting a field, get the field's dot-formatted signature */
646739 @Deprecated
647740 public String getDottedFieldSig() {
648 if (!visitingField)
741 if (!visitingField) {
649742 throw new IllegalStateException("getDottedFieldSig called while not visiting field");
650 if (dottedFieldSig == null)
743 }
744 if (dottedFieldSig == null) {
651745 dottedFieldSig = fieldSig.replace('/', '.');
746 }
652747 return dottedFieldSig;
653748 }
654749
655750 @Override
656751 public String toString() {
657 if (visitingMethod)
752 if (visitingMethod) {
658753 return this.getClass().getSimpleName() + " analyzing " + getClassName() + "." + getMethodName() + getMethodSig();
659 else if (visitingField)
754 } else if (visitingField) {
660755 return this.getClass().getSimpleName() + " analyzing " + getClassName() + "." + getFieldName();
756 }
661757 return this.getClass().getSimpleName() + " analyzing " + getClassName();
662758 }
663759
668764 * org.apache.bcel.classfile.Visitor#visitAnnotation(org.apache.bcel.classfile
669765 * .Annotations)
670766 */
767 @Override
671768 public void visitAnnotation(Annotations arg0) {
672769 // TODO Auto-generated method stub
673770
680777 * org.apache.bcel.classfile.Visitor#visitAnnotationDefault(org.apache.bcel
681778 * .classfile.AnnotationDefault)
682779 */
780 @Override
683781 public void visitAnnotationDefault(AnnotationDefault arg0) {
684782 // TODO Auto-generated method stub
685783
692790 * org.apache.bcel.classfile.Visitor#visitAnnotationEntry(org.apache.bcel
693791 * .classfile.AnnotationEntry)
694792 */
793 @Override
695794 public void visitAnnotationEntry(AnnotationEntry arg0) {
696795 // TODO Auto-generated method stub
697796
704803 * org.apache.bcel.classfile.Visitor#visitEnclosingMethod(org.apache.bcel
705804 * .classfile.EnclosingMethod)
706805 */
806 @Override
707807 public void visitEnclosingMethod(EnclosingMethod arg0) {
708808 // TODO Auto-generated method stub
709809
716816 * org.apache.bcel.classfile.Visitor#visitParameterAnnotation(org.apache
717817 * .bcel.classfile.ParameterAnnotations)
718818 */
819 @Override
719820 public void visitParameterAnnotation(ParameterAnnotations arg0) {
720821 }
721822
726827 * org.apache.bcel.classfile.Visitor#visitStackMapTable(org.apache.bcel.
727828 * classfile.StackMapTable)
728829 */
830 @Override
729831 public void visitStackMapTable(StackMapTable arg0) {
730832 // TODO Auto-generated method stub
731833
738840 * org.apache.bcel.classfile.Visitor#visitStackMapTableEntry(org.apache.
739841 * bcel.classfile.StackMapTableEntry)
740842 */
843 @Override
741844 public void visitStackMapTableEntry(StackMapTableEntry arg0) {
742845 // TODO Auto-generated method stub
743846
4242 static final class ZipEntryComparator implements Comparator<ZipEntry>, Serializable {
4343 private static final long serialVersionUID = 1L;
4444
45 @Override
4546 public int compare(ZipEntry e1, ZipEntry e2) {
4647 String s1 = e1.getName();
4748 int pos1 = s1.lastIndexOf('/');
4849 String p1 = "-";
49 if (pos1 >= 0)
50 if (pos1 >= 0) {
5051 p1 = s1.substring(0, pos1);
52 }
5153
5254 String s2 = e2.getName();
5355 int pos2 = s2.lastIndexOf('/');
5456 String p2 = "-";
55 if (pos2 >= 0)
57 if (pos2 >= 0) {
5658 p2 = s2.substring(0, pos2);
59 }
5760 int r = p1.compareTo(p2);
58 if (r != 0)
61 if (r != 0) {
5962 return r;
63 }
6064 return s1.compareTo(s2);
6165 }
6266 }
6872 public static void main(String argv[]) throws IOException {
6973 String[] file_name = new String[argv.length];
7074 int files = 0;
71 ClassParser parser = null;
7275 String zip_file = null;
7376
7477 /*
7679 */
7780 for (int i = 0; i < argv.length; i++) {
7881 if (argv[i].charAt(0) == '-') { // command line switch
79 if (argv[i].equals("-constants"))
82 if ("-constants".equals(argv[i])) {
8083 constants = true;
81 else if (argv[i].equals("-code"))
84 } else if ("-code".equals(argv[i])) {
8285 code = true;
83 else if (argv[i].equals("-super"))
86 } else if ("-super".equals(argv[i])) {
8487 superClasses = true;
85 else if (argv[i].equals("-zip"))
88 } else if ("-zip".equals(argv[i])) {
8689 zip_file = argv[++i];
87 } else if (argv[i].endsWith(".zip") || argv[i].endsWith(".jar"))
90 }
91 } else if (argv[i].endsWith(".zip") || argv[i].endsWith(".jar")) {
8892 zip_file = argv[i];
89 else { // add file name to list
93 } else { // add file name to list
9094 file_name[files++] = argv[i];
9195 }
9296 }
9397
94 if (!constants)
98 if (!constants) {
9599 code = true;
100 }
96101 if (files == 0 && zip_file == null) {
97102 System.err.println("list: No input files specified");
98103 } else if (zip_file != null) {
99 for (int i = 0; i < files; i++)
104 for (int i = 0; i < files; i++) {
100105 file_name[i] = file_name[i].replace('.', '/');
101 ZipFile z = new ZipFile(zip_file);
102 TreeSet<ZipEntry> zipEntries = new TreeSet<ZipEntry>(new ZipEntryComparator());
103 for (Enumeration<? extends ZipEntry> e = z.entries(); e.hasMoreElements();)
104 zipEntries.add(e.nextElement());
106 }
107 try(ZipFile z = new ZipFile(zip_file)){
108 TreeSet<ZipEntry> zipEntries = new TreeSet<ZipEntry>(new ZipEntryComparator());
109 for (Enumeration<? extends ZipEntry> e = z.entries(); e.hasMoreElements();) {
110 zipEntries.add(e.nextElement());
111 }
105112
106 for (ZipEntry ze : zipEntries) {
107 String name = ze.getName();
108 if (!name.endsWith(".class"))
109 continue;
110 checkMatch: if (files > 0) {
111 for (int i = 0; i < files; i++)
112 if (name.indexOf(file_name[i]) >= 0)
113 break checkMatch;
114 continue;
113 for (ZipEntry ze : zipEntries) {
114 String name = ze.getName();
115 if (!name.endsWith(".class")) {
116 continue;
117 }
118 checkMatch: if (files > 0) {
119 for (int i = 0; i < files; i++) {
120 if (name.indexOf(file_name[i]) >= 0) {
121 break checkMatch;
122 }
123 }
124 continue;
125 }
126 printClass(new ClassParser(z.getInputStream(ze), name));
127
115128 }
116 printClass(new ClassParser(z.getInputStream(ze), name));
117
118129 }
119 z.close();
120 } else
121 for (int i = 0; i < files; i++)
122 if (file_name[i].endsWith(".class"))
130 } else {
131 for (int i = 0; i < files; i++) {
132 if (file_name[i].endsWith(".class")) {
123133 printClass(new ClassParser(file_name[i]));
134 }
135 }
136 }
124137 }
125138
126139
141154 System.out.println();
142155 return;
143156 }
144 if (constants || code)
157 if (constants || code) {
145158 System.out.println(java_class); // Dump the contents
146 if (constants) // Dump the constant pool ?
159 }
160 if (constants) {
147161 System.out.println(java_class.getConstantPool());
162 }
148163
149 if (code) // Dump the method code ?
164 if (code) {
150165 printCode(java_class.getMethods());
166 }
151167 }
152168
153169 /**
157173 for (Method m : methods) {
158174 System.out.println(m);
159175 Code code = m.getCode();
160 if (code != null)
176 if (code != null) {
161177 System.out.println(code);
178 }
162179
163180 }
164181 }
5252
5353 @CheckForNull
5454 public static JavaClass getOuterClass(JavaClass obj) throws ClassNotFoundException {
55 for (Attribute a : obj.getAttributes())
55 for (Attribute a : obj.getAttributes()) {
5656 if (a instanceof InnerClasses) {
5757 for (InnerClass ic : ((InnerClasses) a).getInnerClasses()) {
5858 if (obj.getClassNameIndex() == ic.getInnerClassIndex()) {
6464 }
6565 }
6666 }
67 }
6768 return null;
6869 }
6970
7071 public static int getSizeOfSurroundingTryBlock(@CheckForNull Method method, Class<? extends Throwable> exceptionClass, int pc) {
71 if (method == null)
72 if (method == null) {
7273 return Integer.MAX_VALUE;
74 }
7375
7476 return getSizeOfSurroundingTryBlock(method, ClassName.toSlashedClassName(exceptionClass), pc);
7577 }
76 public static int getSizeOfSurroundingTryBlock(@CheckForNull Method method, String vmNameOfExceptionClass, int pc) {
77 if (method == null)
78 public static int getSizeOfSurroundingTryBlock(@CheckForNull Method method, @CheckForNull String vmNameOfExceptionClass, int pc) {
79 if (method == null) {
7880 return Integer.MAX_VALUE;
81 }
7982 return getSizeOfSurroundingTryBlock(method.getConstantPool(), method.getCode(), vmNameOfExceptionClass, pc);
8083 }
8184
8285 public static @CheckForNull
8386 CodeException getSurroundingTryBlock(ConstantPool constantPool, Code code, @CheckForNull String vmNameOfExceptionClass, int pc) {
8487 int size = Integer.MAX_VALUE;
85 if (code.getExceptionTable() == null)
88 if (code.getExceptionTable() == null) {
8689 return null;
90 }
8791 CodeException result = null;
8892 for (CodeException catchBlock : code.getExceptionTable()) {
8993 if (vmNameOfExceptionClass != null) {
9094 Constant catchType = constantPool.getConstant(catchBlock.getCatchType());
91 if (catchType == null || catchType instanceof ConstantClass
92 && !((ConstantClass) catchType).getBytes(constantPool).equals(vmNameOfExceptionClass))
95 if (catchType == null && !vmNameOfExceptionClass.isEmpty() || catchType instanceof ConstantClass
96 && !((ConstantClass) catchType).getBytes(constantPool).equals(vmNameOfExceptionClass)) {
9397 continue;
98 }
9499 }
95100 int startPC = catchBlock.getStartPC();
96101 int endPC = catchBlock.getEndPC();
110115 int size = Integer.MAX_VALUE;
111116 int tightStartPC = 0;
112117 int tightEndPC = Integer.MAX_VALUE;
113 if (code.getExceptionTable() == null)
118 if (code.getExceptionTable() == null) {
114119 return size;
120 }
115121 for (CodeException catchBlock : code.getExceptionTable()) {
116122 if (vmNameOfExceptionClass != null) {
117123 Constant catchType = constantPool.getConstant(catchBlock.getCatchType());
118 if (catchType == null) continue;
119 if (catchType instanceof ConstantClass) {
120 String name = ((ConstantClass) catchType).getBytes(constantPool);
121 if (!name.equals(vmNameOfExceptionClass))
122 continue;
124 if (catchType == null && !vmNameOfExceptionClass.isEmpty() || catchType instanceof ConstantClass
125 && !((ConstantClass) catchType).getBytes(constantPool).equals(vmNameOfExceptionClass)) {
126 continue;
123127 }
124128 }
125129 int startPC = catchBlock.getStartPC();
133137 }
134138 }
135139 }
136 if (size == Integer.MAX_VALUE)
140 if (size == Integer.MAX_VALUE) {
137141 return size;
142 }
138143
139144 // try to guestimate number of lines that correspond
140145 size = (size + 7) / 8;
141146 LineNumberTable lineNumberTable = code.getLineNumberTable();
142 if (lineNumberTable == null)
147 if (lineNumberTable == null) {
143148 return size;
149 }
144150
145151 int count = 0;
146152 for (LineNumber line : lineNumberTable.getLineNumberTable()) {
147 if (line.getStartPC() > tightEndPC)
153 if (line.getStartPC() > tightEndPC) {
148154 break;
149 if (line.getStartPC() >= tightStartPC)
155 }
156 if (line.getStartPC() >= tightStartPC) {
150157 count++;
158 }
151159 }
152160 return count;
153161
5555
5656 for (BugInstance b : origCollection) {
5757 SourceLineAnnotation s = b.getPrimarySourceLineAnnotation();
58 if (!s.isSourceFileKnown())
58 if (!s.isSourceFileKnown()) {
5959 continue;
60 if (!sourceFinder.hasSourceFile(s))
60 }
61 if (!sourceFinder.hasSourceFile(s)) {
6162 continue;
63 }
6264 SourceFile sourceFile = sourceFinder.findSourceFile(s);
6365 long when = sourceFile.getLastModified();
6466 if (when > 0) {
8082
8183 cloud.waitUntilIssueDataDownloaded();
8284
83 if (args.length > 1)
85 if (args.length > 1) {
8486 origCollection.writeXML(args[1]);
87 }
8588 cloud.waitUntilNewIssuesUploaded();
8689 cloud.shutdown();
8790
5252 }
5353
5454 String getKey(BugInstance b) {
55 if (false)
55 if (false) {
5656 return b.getType();
57 }
5758 String result = b.getCategoryAbbrev();
58 if (result.equals("C") || result.equals("N"))
59 if ("C".equals(result) || "N".equals(result)) {
5960 return result;
61 }
6062 return "O";
6163
6264 // return b.getPriorityAbbreviation() + "-" + b.getType();
6769
6870 int maxRemovedAtOnce() {
6971 int count = 0;
70 for (int c : lastCount.values())
71 if (count < c)
72 for (int c : lastCount.values()) {
73 if (count < c) {
7274 count = c;
75 }
76 }
7377 return count;
7478 }
7579
7680 Map<Long, Integer> lastCount = new HashMap<Long, Integer>();
7781
7882 void update(BugInstance bug) {
79 if (bug.isDead())
83 if (bug.isDead()) {
8084 fixed++;
81 else
85 } else {
8286 persist++;
87 }
8388 final long lastVersion = bug.getLastVersion();
8489 if (lastVersion != -1) {
8590 Integer v = lastCount.get(lastVersion);
86 if (v == null)
91 if (v == null) {
8792 lastCount.put(lastVersion, 0);
88 else
93 } else {
8994 lastCount.put(lastVersion, v + 1);
95 }
9096 }
9197 }
9298 }
110116
111117 String key = getKey(bugInstance);
112118 Data d = data.get(key);
113 if (d == null)
119 if (d == null) {
114120 data.put(key, d = new Data());
121 }
115122 d.update(bugInstance);
116123 all.update(bugInstance);
117124
127134 System.out.printf("%3d #age %s%n", lifespan, key);
128135 System.out.printf("%3d %3d #spread %s%n", first, last, key);
129136 diedAfter[lifespan]++;
130 for (int t = 1; t < lifespan; t++)
137 for (int t = 1; t < lifespan; t++) {
131138 aliveAt[t]++;
139 }
132140 } else if (first != 0) {
133141 int lifespan = (int) (bugCollection.getSequenceNumber() - first + 1);
134 for (int t = 1; t < lifespan; t++)
142 for (int t = 1; t < lifespan; t++) {
135143 aliveAt[t]++;
144 }
136145 }
137146 }
138147 return this;
140149
141150 public void dump(PrintStream out) {
142151 for (int t = 1; t < aliveAt.length; t++) {
143 if (aliveAt[t] != 0)
152 if (aliveAt[t] != 0) {
144153 System.out.printf("%3d%% %4d %5d %3d #decay%n", diedAfter[t] * 100 / aliveAt[t], diedAfter[t], aliveAt[t], t);
154 }
145155 }
146156 System.out.printf("%7s %3s %5s %5s %5s %s%n", "chi", "%", "const", "fix", "max", "kind");
147157 double fixRate;
148 if (this.fixRate == -1)
158 if (this.fixRate == -1) {
149159 fixRate = ((double) all.fixed) / (all.fixed + all.persist);
150 else
160 } else {
151161 fixRate = this.fixRate / 100.0;
162 }
152163 double highFixRate = fixRate + 0.05;
153164 double lowFixRate = fixRate - 0.05;
154165 for (Map.Entry<String, Data> e : data.entrySet()) {
155166 Data d = e.getValue();
156167 int total = d.persist + d.fixed;
157 if (total < 2)
168 if (total < 2) {
158169 continue;
170 }
159171
160172 double rawFixRate = ((double) d.fixed) / total;
161173
165177 } else {
166178 double baseFixRate;
167179
168 if (rawFixRate < lowFixRate)
180 if (rawFixRate < lowFixRate) {
169181 baseFixRate = lowFixRate;
170 else
182 } else {
171183 baseFixRate = highFixRate;
184 }
172185 double expectedFixed = baseFixRate * total;
173186 double expectedPersist = (1 - baseFixRate) * total;
174187 chiValue = (d.fixed - expectedFixed) * (d.fixed - expectedFixed) / expectedFixed + (d.persist - expectedPersist)
175188 * (d.persist - expectedPersist) / expectedPersist;
176 if (rawFixRate < lowFixRate)
189 if (rawFixRate < lowFixRate) {
177190 chiValue = -chiValue;
191 }
178192 }
179193
180194 System.out.printf("%7d %3d %5d %5d %5d %s%n", (int) chiValue, d.fixed * 100 / total, d.persist, d.fixed,
196210
197211 @Override
198212 public void handleOptionWithArgument(String option, String argument) {
199 if (option.equals("-fixRate"))
213 if ("-fixRate".equals(option)) {
200214 fixRate = Integer.parseInt(argument);
201 else
215 } else {
202216 throw new IllegalArgumentException("unknown option: " + option);
217 }
203218 }
204219 }
205220
212227 .parse(args, 0, 2, "Usage: " + Churn.class.getName() + " [options] [<xml results> [<history]] ");
213228
214229 SortedBugCollection bugCollection = new SortedBugCollection();
215 if (argCount < args.length)
230 if (argCount < args.length) {
216231 bugCollection.readXML(args[argCount++]);
217 else
232 } else {
218233 bugCollection.readXML(System.in);
234 }
219235 churn.setBugCollection(bugCollection);
220236 churn.execute();
221237 PrintStream out = System.out;
3030 /**
3131 * Java main application to compute update a historical bug collection with
3232 * results from another build/analysis.
33 *
33 *
3434 * @author William Pugh
3535 */
3636
5151 }
5252
5353 BugCollection bugs = new SortedBugCollection();
54 if (args.length == 0)
54 if (args.length == 0) {
5555 bugs.readXML(System.in);
56 else
56 } else {
5757 bugs.readXML(args[0]);
58 }
5859 bugs.getCloud().waitUntilIssueDataDownloaded();
5960 PrintWriter out = UTF8.printWriter(System.out);
6061 bugs.getCloud().printCloudSummary(out, bugs, new String[0]);
8686 */
8787 @Override
8888 protected void handleOptionWithArgument(String option, String argument) throws IOException {
89 if (option.equals("-cloud")) {
89 if ("-cloud".equals(option)) {
9090 options.cloudId = argument;
91 } else if (option.equals("-recent")) {
91 } else if ("-recent".equals(option)) {
9292 options.ageInHours = Integer.parseInt(argument);
93 } else if (option.equals("-cloudSummary")) {
93 } else if ("-cloudSummary".equals(option)) {
9494 options.cloudSummary = argument;
95 } else
95 } else {
9696 throw new IllegalArgumentException("Unknown option : " + option);
97 }
9798 }
9899 }
99100
105106 int argCount = commandLine.parse(argv, 0, 1, "Usage: " + CloudSyncAndReport.class.getName()
106107 + " [options] [<results1> <results2> ... <resultsn>] ");
107108
108 if (argCount < argv.length)
109 if (argCount < argv.length) {
109110 options.analysisFile = argv[argCount];
111 }
110112
111113 CloudSyncAndReport csr = new CloudSyncAndReport(options);
112114 csr.load();
132134 }
133135
134136 public void load() throws IOException, DocumentException {
135 if (options.analysisFile == null)
137 if (options.analysisFile == null) {
136138 bugCollection.readXML(UTF8.bufferedReader(System.in));
137 else
139 } else {
138140 bugCollection.readXML(options.analysisFile);
141 }
139142 if (options.cloudId != null && !options.cloudId.equals(bugCollection.getProject().getCloudId())) {
140143 bugCollection.getProject().setCloudId(options.cloudId);
141144 bugCollection.reinitializeCloud();
192195 cs.printf("%6s %6s %s%n", "recent", "total", "Rank category");
193196 for (Entry<BugRankCategory, Stats> e : stats.entrySet()) {
194197 Stats s = e.getValue();
195 if (s.total > 0)
198 if (s.total > 0) {
196199 cs.printf("%6d %6d %s%n", s.recent, s.total, e.getKey());
200 }
197201 }
198202 cs.println();
199203 cloud.printCloudSummary(cs, bugs, null);
3737 import edu.umd.cs.findbugs.Project;
3838 import edu.umd.cs.findbugs.SortedBugCollection;
3939 import edu.umd.cs.findbugs.SourceLineAnnotation;
40 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
4041 import edu.umd.cs.findbugs.ba.SourceFile;
4142 import edu.umd.cs.findbugs.ba.SourceFinder;
4243
4344 /**
4445 * Java main application to compute update a historical bug collection with
4546 * results from another build/analysis.
46 *
47 *
4748 * @author William Pugh
4849 */
4950
5152 enum SrcKind {
5253 DIR, ZIP, Z0P_GZ;
5354 static SrcKind get(File f) {
54 if (f.exists() && f.isDirectory() && f.canWrite())
55 if (f.exists() && f.isDirectory() && f.canWrite()) {
5556 return DIR;
57 }
5658 if (!f.exists()) {
57 if (f.getName().endsWith(".zip"))
59 if (f.getName().endsWith(".zip")) {
5860 return ZIP;
59 if (f.getName().endsWith(".z0p.gz"))
61 }
62 if (f.getName().endsWith(".z0p.gz")) {
6063 return Z0P_GZ;
64 }
6165 }
6266 throw new IllegalArgumentException("Invalid src destination: " + f);
6367 }
6973 */
7074 private static final String USAGE = "Usage: <cmd> " + " <bugs.xml> <destinationSrc>";
7175
72
76
7377 public static void main(String[] args) throws Exception {
7478 FindBugs.setNoAnalysis();
7579 DetectorFactoryCollection.instance();
116120
117121 }
118122
123 @SuppressFBWarnings("RV_RETURN_VALUE_IGNORED_BAD_PRACTICE")
119124 private void copySourceFile(String fullName, SourceFile sourceFile) {
120125 InputStream in = null;
121126 OutputStream out = null;
123128 long lastModified = sourceFile.getLastModified();
124129 in = sourceFile.getInputStream();
125130 out = getOutputStream(fullName, 0);
126 if (out == null)
131 if (out == null) {
127132 return;
133 }
128134 while (true) {
129135 int sz = in.read(buf);
130 if (sz < 0)
136 if (sz < 0) {
131137 break;
138 }
132139 out.write(buf, 0, sz);
133140 }
134 if (dstFile != null)
141 if (dstFile != null) {
135142 dstFile.setLastModified(lastModified);
143 }
136144 System.out.println("Copied " + fullName);
137145 copyCount++;
138146 } catch (IOException e) {
148156
149157 private void copySourceForAnnotation(BugAnnotation ann) {
150158 SourceLineAnnotation sourceAnnotation;
151 if (ann instanceof BugAnnotationWithSourceLines)
159 if (ann instanceof BugAnnotationWithSourceLines) {
152160 sourceAnnotation = ((BugAnnotationWithSourceLines) ann).getSourceLines();
153 else if (ann instanceof SourceLineAnnotation)
161 } else if (ann instanceof SourceLineAnnotation) {
154162 sourceAnnotation = (SourceLineAnnotation) ann;
155 else
156 return;
157 if (sourceAnnotation == null)
158 return;
159 if (sourceAnnotation.isUnknown())
160 return;
163 } else {
164 return;
165 }
166 if (sourceAnnotation == null) {
167 return;
168 }
169 if (sourceAnnotation.isUnknown()) {
170 return;
171 }
161172
162173 String fullName = SourceFinder.getPlatformName(sourceAnnotation);
163174
166177 sourceFile = sourceFinder.findSourceFile(sourceAnnotation);
167178 } catch (IOException e) {
168179
169 if (couldNotFind.add(fullName))
180 if (couldNotFind.add(fullName)) {
170181 System.out.println("Did not find " + fullName);
182 }
171183 return;
172184 }
173185
183195 copySourceForAnnotation(i.next());
184196 }
185197 }
186 if (zOut != null)
198 if (zOut != null) {
187199 zOut.close();
200 }
188201
189202 System.out.printf("All done. %d files not found, %d files copied%n", couldNotFind.size(), copyCount);
190203 }
204217
205218 if (!parent.mkdirs() && !parent.isDirectory()) {
206219 String path = parent.getPath();
207 if (couldNotCreate.add(path))
220 if (couldNotCreate.add(path)) {
208221 System.out.println("Can't create directory for " + path);
222 }
209223 return null;
210224 }
211225
218232 }
219233
220234 }
221
235
222236 public static void close(InputStream in) {
223237 try {
224 if (in != null)
238 if (in != null) {
225239 in.close();
240 }
226241 } catch (IOException e) {
227242 }
228243
230245
231246 public static void close(OutputStream out) {
232247 try {
233 if (out instanceof ZipOutputStream)
248 if (out instanceof ZipOutputStream) {
234249 ((ZipOutputStream) out).closeEntry();
235 else if (out != null)
250 } else if (out != null) {
236251 out.close();
252 }
237253 } catch (IOException e) {
238254 }
239255
3232 /**
3333 * Java main application to compute update a historical bug collection with
3434 * results from another build/analysis.
35 *
35 *
3636 * @author William Pugh
3737 */
3838
5353
5454 int prefixLength = Integer.parseInt(args[0]);
5555 BugCollection origCollection = new SortedBugCollection();
56 if (args.length == 1)
56 if (args.length == 1) {
5757 origCollection.readXML(System.in);
58 else
58 } else {
5959 origCollection.readXML(args[1]);
60 }
6061 Map<String, Integer> map = new TreeMap<String, Integer>();
6162 Map<String, Integer> ncss = new TreeMap<String, Integer>();
6263
6364 for (BugInstance b : origCollection.getCollection()) {
6465 String prefix = ClassName.extractPackagePrefix(b.getPrimaryClass().getPackageName(), prefixLength);
6566 Integer v = map.get(prefix);
66 if (v == null)
67 if (v == null) {
6768 map.put(prefix, 1);
68 else
69 } else {
6970 map.put(prefix, v + 1);
71 }
7072 }
7173 for (PackageStats ps : origCollection.getProjectStats().getPackageStats()) {
7274 String prefix = ClassName.extractPackagePrefix(ps.getPackageName(), prefixLength);
7375
7476 Integer v = ncss.get(prefix);
75 if (v == null)
77 if (v == null) {
7678 ncss.put(prefix, ps.size());
77 else
79 } else {
7880 ncss.put(prefix, v + ps.size());
81 }
7982
8083 }
8184 for (Map.Entry<String, Integer> e : map.entrySet()) {
8285 String prefix = e.getKey();
8386 int warnings = e.getValue();
84 if (warnings == 0)
87 if (warnings == 0) {
8588 continue;
89 }
8690 Integer v = ncss.get(prefix);
87 if (v == null || v.intValue() == 0)
91 if (v == null || v.intValue() == 0) {
8892 v = 1;
93 }
8994
9095 int density = warnings * 1000000 / v;
91 if (warnings < 3 || v < 2000)
96 if (warnings < 3 || v < 2000) {
9297 System.out.printf("%4s %4d %4d %s%n", " ", warnings, v / 1000, prefix);
93 else
98 } else {
9499 System.out.printf("%4d %4d %4d %s%n", density, warnings, v / 1000, prefix);
100 }
95101 }
96102
97103 }
5555 List<String> lst = new LinkedList<String>();
5656 while (true) {
5757 String s = in.readLine();
58 if (s == null)
58 if (s == null) {
5959 return lst;
60 }
6061 lst.add(s);
6162 }
6263 }
7576 addOption("-prefix", "class name prefix", "prefix of class names that should be analyzed e.g., edu.umd.cs.)");
7677 }
7778
78 /*
79 * (non-Javadoc)
80 *
81 * @see
82 * edu.umd.cs.findbugs.config.CommandLine#handleOption(java.lang.String,
83 * java.lang.String)
84 */
8579 @Override
8680 protected void handleOption(String option, String optionExtraPart) throws IOException {
8781 throw new IllegalArgumentException("Unknown option : " + option);
8882 }
8983
90 /*
91 * (non-Javadoc)
92 *
93 * @see
94 * edu.umd.cs.findbugs.config.CommandLine#handleOptionWithArgument(java
95 * .lang.String, java.lang.String)
96 */
9784 @Override
9885 protected void handleOptionWithArgument(String option, String argument) throws IOException {
99 if (option.equals("-prefix"))
86 if ("-prefix".equals(option)) {
10087 prefix = argument;
101 else if (option.equals("-inputFileList"))
88 } else if ("-inputFileList".equals(option)) {
10289 inputFileList = argument;
103 else if (option.equals("-maxAge"))
90 } else if ("-maxAge".equals(option)) {
10491 maxAge = System.currentTimeMillis() - (24 * 60 * 60 * 1000L) * Integer.parseInt(argument);
105 else
92 } else {
10693 throw new IllegalArgumentException("Unknown option : " + option);
94 }
10795 }
10896
10997 }
114102 int argCount = commandLine.parse(args, 0, Integer.MAX_VALUE, "Usage: " + CountClassVersions.class.getName()
115103 + " [options] [<jarFile>+] ");
116104
117 int analysisClassCount = 0;
118105 List<String> fileList;
119106
120 if (commandLine.inputFileList != null)
107 if (commandLine.inputFileList != null) {
121108 fileList = readFrom(UTF8.fileReader(commandLine.inputFileList));
122 else if (argCount == args.length)
109 } else if (argCount == args.length) {
123110 fileList = readFromStandardInput();
124 else
111 } else {
125112 fileList = Arrays.asList(args).subList(argCount, args.length - 1);
113 }
126114 byte buffer[] = new byte[8192];
127115 MessageDigest digest = Util.getMD5Digest();
128116 DualKeyHashMap<String, String, String> map = new DualKeyHashMap<String, String, String>();
134122 continue;
135123 }
136124 System.err.println("Opening " + f);
137 ZipFile zipInputFile;
138 try {
139 zipInputFile = new ZipFile(f);
125
126 try (ZipFile zipInputFile = new ZipFile(f)){
127 for (Enumeration<? extends ZipEntry> e = zipInputFile.entries(); e.hasMoreElements();) {
128 ZipEntry ze = e.nextElement();
129
130 if (ze == null) {
131 break;
132 }
133 if (ze.isDirectory()) {
134 continue;
135 }
136
137 String name = ze.getName();
138 if (!name.endsWith(".class")) {
139 continue;
140 }
141 if (!name.replace('/', '.').startsWith(commandLine.prefix)) {
142 continue;
143 }
144
145 InputStream zipIn = zipInputFile.getInputStream(ze);
146
147 while (true) {
148 int bytesRead = zipIn.read(buffer);
149 if (bytesRead < 0) {
150 break;
151 }
152 digest.update(buffer, 0, bytesRead);
153
154 }
155 String hash = new BigInteger(1, digest.digest()).toString(16);
156 map.put(name, hash, fInName);
157 }
140158 } catch (IOException e) {
141159 e.printStackTrace();
142160 continue;
143161 }
144
145 for (Enumeration<? extends ZipEntry> e = zipInputFile.entries(); e.hasMoreElements();) {
146 ZipEntry ze = e.nextElement();
147
148 if (ze == null)
149 break;
150 if (ze.isDirectory())
151 continue;
152
153 String name = ze.getName();
154 if (!name.endsWith(".class"))
155 continue;
156 if (!name.replace('/', '.').startsWith(commandLine.prefix))
157 continue;
158
159 InputStream zipIn = zipInputFile.getInputStream(ze);
160
161 while (true) {
162 int bytesRead = zipIn.read(buffer);
163 if (bytesRead < 0)
164 break;
165 digest.update(buffer, 0, bytesRead);
166
167 }
168 String hash = new BigInteger(1, digest.digest()).toString(16);
169 map.put(name, hash, fInName);
170 }
171 zipInputFile.close();
172162 }
173163 for (String s : map.keySet()) {
174164 Map<String, String> values = map.get(s);
2626 /**
2727 * Java main application to compute defect density for a bug collection (stored
2828 * as an XML collection)
29 *
29 *
3030 * @author William Pugh
3131 */
3232 public class DefectDensity {
4040 }
4141
4242 public static double density(int bugs, int ncss) {
43 if (ncss == 0)
43 if (ncss == 0) {
4444 return Double.NaN;
45 }
4546 long bugsPer10KNCSS = Math.round(10000.0 * bugs / ncss);
4647 return bugsPer10KNCSS / 10.0;
4748 }
5556 FindBugs.setNoAnalysis();
5657 BugCollection origCollection = new SortedBugCollection();
5758 int argCount = 0;
58 if (argCount == args.length)
59 if (argCount == args.length) {
5960 origCollection.readXML(System.in);
60 else
61 } else {
6162 origCollection.readXML(args[argCount]);
63 }
6264 ProjectStats stats = origCollection.getProjectStats();
6365 printRow("kind", "name", "density/KNCSS", "bugs", "NCSS");
6466 double projectDensity = density(stats.getTotalBugs(), stats.getCodeSize());
6567 printRow("project", origCollection.getCurrentAppVersion().getReleaseName(), projectDensity, stats.getTotalBugs(),
6668 stats.getCodeSize());
67 for (PackageStats p : stats.getPackageStats())
69 for (PackageStats p : stats.getPackageStats()) {
6870 if (p.getTotalBugs() > 4) {
6971
7072 double packageDensity = density(p.getTotalBugs(), p.size());
71 if (Double.isNaN(packageDensity) || packageDensity < projectDensity)
73 if (Double.isNaN(packageDensity) || packageDensity < projectDensity) {
7274 continue;
75 }
7376 printRow("package", p.getPackageName(), packageDensity, p.getTotalBugs(), p.size());
74 for (ClassStats c : p.getSortedClassStats())
77 for (ClassStats c : p.getSortedClassStats()) {
7578 if (c.getTotalBugs() > 4) {
7679 double density = density(c.getTotalBugs(), c.size());
77 if (Double.isNaN(density) || density < packageDensity)
80 if (Double.isNaN(density) || density < packageDensity) {
7881 continue;
82 }
7983 printRow("class", c.getName(), density, c.getTotalBugs(), c.size());
8084 }
85 }
8186 }
87 }
8288
8389 }
8490
00 /*
11 * FindBugs - Find Bugs in Java programs
22 * Copyright (C) 2003-2008 University of Maryland
3 *
3 *
44 * This library is free software; you can redistribute it and/or
55 * modify it under the terms of the GNU Lesser General Public
66 * License as published by the Free Software Foundation; either
77 * version 2.1 of the License, or (at your option) any later version.
8 *
8 *
99 * This library is distributed in the hope that it will be useful,
1010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1111 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1212 * Lesser General Public License for more details.
13 *
13 *
1414 * You should have received a copy of the GNU Lesser General Public
1515 * License along with this library; if not, write to the Free Software
1616 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
3737 } else {
3838 cmd = args[0];
3939 a = new String[args.length - 1];
40 for (int i = 1; i < args.length; i++)
40 for (int i = 1; i < args.length; i++) {
4141 a[i - 1] = args[i];
42 }
4243 }
4344
4445 DetectorFactoryCollection.instance();
5253 }
5354 return;
5455 }
55
56
5657 }
57
58
5859 throw new IllegalArgumentException("Unable to find FindBugs main for " + cmd);
5960 }
6061
3838 * For each source file that has reported bugs, compute a hash of all the issues
3939 * reported for that file. These hashes use line numbers, so a change that only
4040 * changes the line number of an issue will cause the hash to be different.
41 *
41 *
4242 * @author William Pugh
4343 */
4444 public class FileBugHash {
5353
5454 FileBugHash(BugCollection bugs) {
5555
56 for (PackageStats pStat : bugs.getProjectStats().getPackageStats())
56 for (PackageStats pStat : bugs.getProjectStats().getPackageStats()) {
5757 for (ClassStats cStat : pStat.getSortedClassStats()) {
5858 String path = cStat.getName();
59 if (path.indexOf('.') == -1)
59 if (path.indexOf('.') == -1) {
6060 path = cStat.getSourceFile();
61 else
61 } else {
6262 path = path.substring(0, path.lastIndexOf('.') + 1).replace('.', '/') + cStat.getSourceFile();
63 }
6364 counts.put(path, 0);
6465 Integer size = sizes.get(path);
65 if (size == null)
66 if (size == null) {
6667 size = 0;
68 }
6769 sizes.put(path, size + cStat.size());
6870 }
71 }
6972 for (BugInstance bug : bugs.getCollection()) {
7073 SourceLineAnnotation source = bug.getPrimarySourceLineAnnotation();
7174
7275 String packagePath = source.getPackageName().replace('.', '/');
7376 String key;
74 if (packagePath.length() == 0)
77 if (packagePath.length() == 0) {
7578 key = source.getSourceFile();
76 else
79 } else {
7780 key = packagePath + "/" + source.getSourceFile();
81 }
7882 StringBuilder buf = hashes.get(key);
7983 if (buf == null) {
8084 buf = new StringBuilder();
8185 hashes.put(key, buf);
8286 }
8387 buf.append(bug.getInstanceKey()).append("-").append(source.getStartLine()).append(".")
84 .append(source.getStartBytecode()).append(" ");
88 .append(source.getStartBytecode()).append(" ");
8589 Integer count = counts.get(key);
86 if (count == null)
90 if (count == null) {
8791 counts.put(key, 1);
88 else
92 } else {
8993 counts.put(key, 1 + count);
94 }
9095 }
9196 }
9297
97102 public @CheckForNull
98103 String getHash(String sourceFile) {
99104 StringBuilder rawHash = hashes.get(sourceFile);
100 if (rawHash == null || digest == null)
105 if (rawHash == null || digest == null) {
101106 return null;
107 }
102108 byte[] data = digest.digest(UTF8.getBytes(rawHash.toString()));
103109 String tmp = new BigInteger(1, data).toString(16);
104 if (tmp.length() < 32)
110 if (tmp.length() < 32) {
105111 tmp = "000000000000000000000000000000000".substring(0, 32 - tmp.length()) + tmp;
112 }
106113 return tmp;
107114 }
108115
109116 public int getBugCount(String sourceFile) {
110117 Integer count = counts.get(sourceFile);
111 if (count == null)
118 if (count == null) {
112119 return 0;
120 }
113121 return count;
114122 }
115123
116124 public int getSize(String sourceFile) {
117125 Integer size = sizes.get(sourceFile);
118 if (size == null)
126 if (size == null) {
119127 return 0;
128 }
120129 return size;
121130 }
122131
128137 }
129138 BugCollection origCollection = new SortedBugCollection();
130139 int argCount = 0;
131 if (argCount == args.length)
140 if (argCount == args.length) {
132141 origCollection.readXML(System.in);
133 else
142 } else {
134143 origCollection.readXML(args[argCount]);
144 }
135145 FileBugHash result = compute(origCollection);
136146 for (String sourceFile : result.getSourceFiles()) {
137147 System.out.println(result.getHash(sourceFile) + "\t" + sourceFile);
139149
140150 }
141151
142 /**
143 * @param origCollection
144 * @return
145 */
146152 public static FileBugHash compute(BugCollection origCollection) {
147153 return new FileBugHash(origCollection);
148154 }
130130 public boolean purgeHistorySpecified = false;
131131
132132 public boolean purgeHistory = false;
133
133
134134 public boolean activeSpecified = false;
135135
136136 public boolean active = false;
204204 Set<String> designationKey = new HashSet<String>();
205205
206206 Set<String> categoryKey = new HashSet<String>();
207
207
208208 SortedSet<BugInstance> uniqueSloppy;
209209
210210 int priority = 3;
257257
258258 addOption("-class", "pattern", "allow only bugs whose primary class name matches this pattern");
259259 addOption("-calls", "pattern", "allow only bugs that involve a call to a method that matches this pattern (matches with method class or name)");
260
260
261261 addOption("-bugPattern", "pattern", "allow only bugs whose type matches this pattern");
262262 addOption("-category", "category", "allow only warnings with a category that starts with this string");
263263 addOption("-designation", "designation",
264264 "allow only warnings with this designation (e.g., -designation SHOULD_FIX,MUST_FIX)");
265 addSwitch("-dontUpdateStats",
265 addSwitch("-dontUpdateStats",
266266 "used when withSource is specified to only update bugs, not the class and package stats");
267267 addOption("-hashes", "hash file", "only bugs with instance hashes contained in the hash file");
268268
269269 }
270270
271271 public static long getVersionNum(BugCollection collection, String val,
272 boolean roundToLaterVersion) {
273 if (val == null)
272 boolean roundToLaterVersion) {
273 if (val == null) {
274274 return -1;
275 }
275276 Map<String, AppVersion> versions = new HashMap<String, AppVersion>();
276277 SortedMap<Long, AppVersion> timeStamps = new TreeMap<Long, AppVersion>();
277278
286287 timeStamps.put(v.getTimestamp(), v);
287288
288289 return getVersionNum(versions, timeStamps, val,
289 roundToLaterVersion, v.getSequenceNumber());
290 roundToLaterVersion, v.getSequenceNumber());
290291 }
291292
292293 public static long getVersionNum(Map<String, AppVersion> versions, SortedMap<Long, AppVersion> timeStamps, String val,
293294 boolean roundToLaterVersion, long currentSeqNum) {
294 if (val == null)
295 if (val == null) {
295296 return -1;
297 }
296298 long numVersions = currentSeqNum + 1;
297299
298 if (val.equals("last") || val.equals("lastVersion"))
300 if ("last".equals(val) || "lastVersion".equals(val)) {
299301 return numVersions - 1;
302 }
300303
301304 AppVersion v = versions.get(val);
302 if (v != null)
305 if (v != null) {
303306 return v.getSequenceNumber();
307 }
304308 try {
305309 long time = 0;
306 if (val.endsWith("daysAgo"))
310 if (val.endsWith("daysAgo")) {
307311 time = System.currentTimeMillis() - MILLISECONDS_PER_DAY
308312 * Integer.parseInt(val.substring(0, val.length() - 7));
309 else
313 } else {
310314 time = Date.parse(val);
315 }
311316 return getAppropriateSeq(timeStamps, time, roundToLaterVersion);
312317 } catch (Exception e) {
313318 try {
330335 static private long getAppropriateSeq(SortedMap<Long, AppVersion> timeStamps, long when, boolean roundToLaterVersion) {
331336 if (roundToLaterVersion) {
332337 SortedMap<Long, AppVersion> geq = timeStamps.tailMap(when);
333 if (geq.isEmpty())
338 if (geq.isEmpty()) {
334339 return Long.MAX_VALUE;
340 }
335341 return geq.get(geq.firstKey()).getSequenceNumber();
336342 } else {
337343 SortedMap<Long, AppVersion> leq = timeStamps.headMap(when);
338 if (leq.isEmpty())
344 if (leq.isEmpty()) {
339345 return Long.MIN_VALUE;
346 }
340347 return leq.get(leq.lastKey()).getSequenceNumber();
341348 }
342349 }
360367 present = getVersionNum(collection, presentAsString, true);
361368 absent = getVersionNum(collection, absentAsString, true);
362369
363 if (sloppyUniqueSpecified)
370 if (sloppyUniqueSpecified) {
364371 uniqueSloppy = new TreeSet<BugInstance>(new SloppyBugComparator());
372 }
365373
366374 long fixed = getVersionNum(collection, fixedAsString, true);
367375 if (fixed >= 0)
376 {
368377 last = fixed - 1; // fixed means last on previous sequence (ok
369 // if -1)
378 // if -1)
379 }
370380 }
371381
372382 boolean accept(BugCollection collection, BugInstance bug) {
373383 boolean result = evaluate(collection, bug);
374 if (not)
384 if (not) {
375385 return !result;
386 }
376387 return result;
377388 }
378389
379390 boolean evaluate(BugCollection collection, BugInstance bug) {
380391
381 for (Matcher m : includeFilter)
382 if (!m.match(bug))
392 for (Matcher m : includeFilter) {
393 if (!m.match(bug)) {
383394 return false;
384 for (Matcher m : excludeFilter)
385 if (m.match(bug))
395 }
396 }
397 for (Matcher m : excludeFilter) {
398 if (m.match(bug)) {
386399 return false;
387 if (excludedInstanceHashes.contains(bug.getInstanceHash()))
388 return false;
389 if (annotation != null && bug.getAnnotationText().indexOf(annotation) == -1)
390 return false;
391 if (bug.getPriority() > priority)
392 return false;
393 if (firstAsString != null && bug.getFirstVersion() != first)
394 return false;
395 if (afterAsString != null && bug.getFirstVersion() <= after)
396 return false;
397 if (beforeAsString != null && bug.getFirstVersion() >= before)
398 return false;
399 if (hashesFromFile != null && !hashesFromFile.contains(bug.getInstanceHash()))
400 return false;
400 }
401 }
402 if (excludedInstanceHashes.contains(bug.getInstanceHash())) {
403 return false;
404 }
405 if (annotation != null && bug.getAnnotationText().indexOf(annotation) == -1) {
406 return false;
407 }
408 if (bug.getPriority() > priority) {
409 return false;
410 }
411 if (firstAsString != null && bug.getFirstVersion() != first) {
412 return false;
413 }
414 if (afterAsString != null && bug.getFirstVersion() <= after) {
415 return false;
416 }
417 if (beforeAsString != null && bug.getFirstVersion() >= before) {
418 return false;
419 }
420 if (hashesFromFile != null && !hashesFromFile.contains(bug.getInstanceHash())) {
421 return false;
422 }
401423 long lastSeen = bug.getLastVersion();
402 if (lastSeen < 0)
424 if (lastSeen < 0) {
403425 lastSeen = collection.getSequenceNumber();
426 }
404427 long thisDuration = lastSeen - bug.getFirstVersion();
405 if (duration > 0 && thisDuration > duration)
406 return false;
407 if ((lastAsString != null || fixedAsString != null) && (last < 0 || bug.getLastVersion() != last))
408 return false;
409 if (presentAsString != null && !bugLiveAt(bug, present))
410 return false;
411 if (absentAsString != null && bugLiveAt(bug, absent))
412 return false;
413
414 if (hasFieldSpecified && (hasField != (bug.getPrimaryField() != null)))
415 return false;
416 if (hasLocalSpecified && (hasLocal != (bug.getPrimaryLocalVariableAnnotation() != null)))
417 return false;
418
419 if (maxRank < Integer.MAX_VALUE && BugRanker.findRank(bug) > maxRank)
420 return false;
421
422 if (activeSpecified && active == bug.isDead())
423 return false;
424 if (removedByChangeSpecified && bug.isRemovedByChangeOfPersistingClass() != removedByChange)
425 return false;
426 if (introducedByChangeSpecified && bug.isIntroducedByChangeOfExistingClass() != introducedByChange)
427 return false;
428 if (newCodeSpecified && newCode != (!bug.isIntroducedByChangeOfExistingClass() && bug.getFirstVersion() != 0))
429 return false;
430 if (removedCodeSpecified && removedCode != (!bug.isRemovedByChangeOfPersistingClass() && bug.isDead()))
431 return false;
432
433 if (bugPattern != null && !bugPattern.matcher(bug.getType()).find())
434 return false;
435 if (classPattern != null && !classPattern.matcher(bug.getPrimaryClass().getClassName()).find())
436 return false;
428 if (duration > 0 && thisDuration > duration) {
429 return false;
430 }
431 if ((lastAsString != null || fixedAsString != null) && (last < 0 || bug.getLastVersion() != last)) {
432 return false;
433 }
434 if (presentAsString != null && !bugLiveAt(bug, present)) {
435 return false;
436 }
437 if (absentAsString != null && bugLiveAt(bug, absent)) {
438 return false;
439 }
440
441 if (hasFieldSpecified && (hasField != (bug.getPrimaryField() != null))) {
442 return false;
443 }
444 if (hasLocalSpecified && (hasLocal != (bug.getPrimaryLocalVariableAnnotation() != null))) {
445 return false;
446 }
447
448 if (maxRank < Integer.MAX_VALUE && BugRanker.findRank(bug) > maxRank) {
449 return false;
450 }
451
452 if (activeSpecified && active == bug.isDead()) {
453 return false;
454 }
455 if (removedByChangeSpecified && bug.isRemovedByChangeOfPersistingClass() != removedByChange) {
456 return false;
457 }
458 if (introducedByChangeSpecified && bug.isIntroducedByChangeOfExistingClass() != introducedByChange) {
459 return false;
460 }
461 if (newCodeSpecified && newCode != (!bug.isIntroducedByChangeOfExistingClass() && bug.getFirstVersion() != 0)) {
462 return false;
463 }
464 if (removedCodeSpecified && removedCode != (!bug.isRemovedByChangeOfPersistingClass() && bug.isDead())) {
465 return false;
466 }
467
468 if (bugPattern != null && !bugPattern.matcher(bug.getType()).find()) {
469 return false;
470 }
471 if (classPattern != null && !classPattern.matcher(bug.getPrimaryClass().getClassName()).find()) {
472 return false;
473 }
437474 if (callsPattern != null) {
438475 MethodAnnotation m = bug.getAnnotationWithRole(MethodAnnotation.class, MethodAnnotation.METHOD_CALLED);
439 if (m == null)
476 if (m == null) {
440477 return false;
441 if (!callsPattern.matcher(m.getClassName()).find() && !callsPattern.matcher(m.getMethodName()).find())
478 }
479 if (!callsPattern.matcher(m.getClassName()).find() && !callsPattern.matcher(m.getMethodName()).find()) {
442480 return false;
443 }
444
445 if (maybeMutatedAsString != null && !(atMutationPoint(bug) && mutationPoints.contains(getBugLocation(bug))))
446 return false;
481 }
482 }
483
484 if (maybeMutatedAsString != null && !(atMutationPoint(bug) && mutationPoints.contains(getBugLocation(bug)))) {
485 return false;
486 }
447487
448488 BugPattern thisBugPattern = bug.getBugPattern();
449 if (!categoryKey.isEmpty() && thisBugPattern != null && !categoryKey.contains(thisBugPattern.getCategory()))
450 return false;
451 if (!designationKey.isEmpty() && !designationKey.contains(bug.getUserDesignationKey()))
452 return false;
489 if (!categoryKey.isEmpty() && thisBugPattern != null && !categoryKey.contains(thisBugPattern.getCategory())) {
490 return false;
491 }
492 if (!designationKey.isEmpty() && !designationKey.contains(bug.getUserDesignationKey())) {
493 return false;
494 }
453495
454496 if (hashChangedSpecified) {
455 if (bug.isInstanceHashConsistent() == hashChanged)
497 if (bug.isInstanceHashConsistent() == hashChanged) {
456498 return false;
457 }
458 if (applySuppressionSpecified && applySuppression && suppressionFilter.match(bug))
459 return false;
499 }
500 }
501 if (applySuppressionSpecified && applySuppression && suppressionFilter.match(bug)) {
502 return false;
503 }
460504 SourceLineAnnotation primarySourceLineAnnotation = bug.getPrimarySourceLineAnnotation();
461505
462506 if (knownSourceSpecified) {
463 if (primarySourceLineAnnotation.isUnknown() == knownSource)
507 if (primarySourceLineAnnotation.isUnknown() == knownSource) {
464508 return false;
509 }
465510 }
466511 if (withSourceSpecified) {
467 if (sourceSearcher.findSource(primarySourceLineAnnotation) != withSource)
512 if (sourceSearcher.findSource(primarySourceLineAnnotation) != withSource) {
468513 return false;
514 }
469515 }
470516
471517 Cloud cloud = collection.getCloud();
472518 if (maxAgeSpecified) {
473519 long firstSeen = cloud.getFirstSeen(bug);
474 if (!cloud.isInCloud(bug))
520 if (!cloud.isInCloud(bug)) {
475521 return false;
476 if (firstSeen < minFirstSeen)
522 }
523 if (firstSeen < minFirstSeen) {
477524 return false;
478 }
479
480 if (notAProblemSpecified && notAProblem != (cloud.getConsensusDesignation(bug).score() < 0))
481 return false;
482 if (shouldFixSpecified && shouldFix != (cloud.getConsensusDesignation(bug).score() > 0))
483 return false;
484
525 }
526 }
527
528 if (notAProblemSpecified && notAProblem != (cloud.getConsensusDesignation(bug).score() < 0)) {
529 return false;
530 }
531 if (shouldFixSpecified && shouldFix != (cloud.getConsensusDesignation(bug).score() > 0)) {
532 return false;
533 }
534
485535 if (sloppyUniqueSpecified) {
486536 boolean unique = uniqueSloppy.add(bug);
487 if (unique != sloppyUnique)
537 if (unique != sloppyUnique) {
488538 return false;
539 }
489540 }
490541
491542 return true;
519570 }
520571
521572 private boolean bugLiveAt(BugInstance bug, long now) {
522 if (now < bug.getFirstVersion())
523 return false;
524 if (bug.isDead() && bug.getLastVersion() < now)
525 return false;
573 if (now < bug.getFirstVersion()) {
574 return false;
575 }
576 if (bug.isDead() && bug.getLastVersion() < now) {
577 return false;
578 }
526579 return true;
527580 }
528581
529582 @Override
530583 protected void handleOption(String option, String optionExtraPart) throws IOException {
531584 option = option.substring(1);
532 if (optionExtraPart.length() == 0)
585 if (optionExtraPart.length() == 0) {
533586 setField(option, true);
534 else
587 } else {
535588 setField(option, Boolean.parseBoolean(optionExtraPart));
589 }
536590 setField(option + "Specified", true);
537591 }
538592
551605 @Override
552606 protected void handleOptionWithArgument(String option, String argument) throws IOException {
553607
554 if (option.equals("-priority") || option.equals("-confidence")) {
608 if ("-priority".equals(option) || "-confidence".equals(option)) {
555609 priority = parsePriority(argument);
556610 }
557611
558 else if (option.equals("-maxRank"))
612 else if ("-maxRank".equals(option)) {
559613 maxRank = Integer.parseInt(argument);
560
561 else if (option.equals("-first"))
614 } else if ("-first".equals(option)) {
562615 firstAsString = argument;
563 else if (option.equals("-maybeMutated"))
616 } else if ("-maybeMutated".equals(option)) {
564617 maybeMutatedAsString = argument;
565 else if (option.equals("-last"))
618 } else if ("-last".equals(option)) {
566619 lastAsString = argument;
567 else if (option.equals("-trimToVersion"))
620 } else if ("-trimToVersion".equals(option)) {
568621 trimToVersionAsString = argument;
569 else if (option.equals("-maxDuration"))
622 } else if ("-maxDuration".equals(option)) {
570623 duration = Integer.parseInt(argument);
571 else if (option.equals("-fixed"))
624 } else if ("-fixed".equals(option)) {
572625 fixedAsString = argument;
573 else if (option.equals("-after"))
626 } else if ("-after".equals(option)) {
574627 afterAsString = argument;
575 else if (option.equals("-before"))
628 } else if ("-before".equals(option)) {
576629 beforeAsString = argument;
577 else if (option.equals("-present"))
630 } else if ("-present".equals(option)) {
578631 presentAsString = argument;
579 else if (option.equals("-absent"))
632 } else if ("-absent".equals(option)) {
580633 absentAsString = argument;
581
582 else if (option.equals("-category"))
634 } else if ("-category".equals(option)) {
583635 addCategoryKey(argument);
584 else if (option.equals("-designation"))
636 } else if ("-designation".equals(option)) {
585637 addDesignationKey(argument);
586 else if (option.equals("-class"))
638 } else if ("-class".equals(option)) {
587639 classPattern = Pattern.compile(argument.replace(',', '|'));
588 else if (option.equals("-calls"))
640 } else if ("-calls".equals(option)) {
589641 callsPattern = Pattern.compile(argument.replace(',', '|'));
590 else if (option.equals("-bugPattern"))
642 } else if ("-bugPattern".equals(option)) {
591643 bugPattern = Pattern.compile(argument);
592 else if (option.equals("-annotation"))
644 } else if ("-annotation".equals(option)) {
593645 annotation = argument;
594 else if (option.equals("-excludeBugs")) {
646 } else if ("-excludeBugs".equals(option)) {
595647 try {
596648 ExcludingHashesBugReporter.addToExcludedInstanceHashes(excludedInstanceHashes, argument);
597649 } catch (DocumentException e) {
598650 throw new IllegalArgumentException("Error processing include file: " + argument, e);
599651 }
600 } else if (option.equals("-include")) {
652 } else if ("-include".equals(option)) {
601653 try {
602654 includeFilter.add(new edu.umd.cs.findbugs.filter.Filter(argument));
603655 } catch (FilterException e) {
604656 throw new IllegalArgumentException("Error processing include file: " + argument, e);
605657 }
606 } else if (option.equals("-exclude")) {
658 } else if ("-exclude".equals(option)) {
607659 try {
608660 excludeFilter.add(new edu.umd.cs.findbugs.filter.Filter(argument));
609661 } catch (FilterException e) {
610662 throw new IllegalArgumentException("Error processing include file: " + argument, e);
611663 }
612 } else if (option.equals("-maxAge")) {
664 } else if ("-maxAge".equals(option)) {
613665 maxAge = Integer.parseInt(argument);
614666 maxAgeSpecified = true;
615 } else if (option.equals("-hashes")) {
667 } else if ("-hashes".equals(option)) {
616668 hashesFromFile = new HashSet<String>();
617669 BufferedReader in = null;
618670 try {
619671 in = new BufferedReader(UTF8.fileReader(argument));
620672 while (true) {
621673 String h = in.readLine();
622 if (h == null)
674 if (h == null) {
623675 break;
676 }
624677 hashesFromFile.add(h);
625678 }
626679 } catch (IOException e) {
628681 } finally {
629682 Util.closeSilently(in);
630683 }
631 } else
684 } else {
632685 throw new IllegalArgumentException("can't handle command line argument of " + option);
686 }
633687
634688 }
635689
644698 if (maybeMutatedAsString != null) {
645699 HashSet<String> addedIssues = new HashSet<String>();
646700 HashSet<String> removedIssues = new HashSet<String>();
647 for (BugInstance b : origCollection)
648 if (b.getFirstVersion() == maybeMutated)
701 for (BugInstance b : origCollection) {
702 if (b.getFirstVersion() == maybeMutated) {
649703 addedIssues.add(getBugLocation(b));
650 else if (b.getLastVersion() == maybeMutated - 1)
704 } else if (b.getLastVersion() == maybeMutated - 1) {
651705 removedIssues.add(getBugLocation(b));
706 }
707 }
652708 addedIssues.remove(null);
653709 addedIssues.retainAll(removedIssues);
654710 mutationPoints = addedIssues;
672728 String point;
673729 MethodAnnotation m = b.getPrimaryMethod();
674730 FieldAnnotation f = b.getPrimaryField();
675 if (m != null)
731 if (m != null) {
676732 point = m.toString();
677 else if (f != null)
733 } else if (f != null) {
678734 point = f.toString();
679 else
735 } else {
680736 point = null;
737 }
681738 return point;
682739 }
683740
685742
686743 public static int parsePriority(String argument) {
687744 int i = " HMLE".indexOf(argument);
688 if (i == -1)
745 if (i == -1) {
689746 i = " 1234".indexOf(argument);
690 if (i == -1)
747 }
748 if (i == -1) {
691749 throw new IllegalArgumentException("Bad priority: " + argument);
750 }
692751 return i;
693752 }
694753
695754 static SourceSearcher sourceSearcher;
696755
697
756
698757 public static void main(String[] args) throws Exception {
699758 FindBugs.setNoAnalysis();
700759 DetectorFactoryCollection.instance();
705764 + " [options] [<orig results> [<new results]] ");
706765 SortedBugCollection origCollection = new SortedBugCollection();
707766
708 if (argCount == args.length)
767 if (argCount == args.length) {
709768 origCollection.readXML(System.in);
710 else
769 } else {
711770 origCollection.readXML(args[argCount++]);
771 }
712772 boolean verbose = argCount < args.length;
713773 SortedBugCollection resultCollection = origCollection.createEmptyCollectionWithMetadata();
714774 Project project = resultCollection.getProject();
715775 int passed = 0;
716776 int dropped = 0;
717777 resultCollection.setWithMessages(commandLine.withMessages);
718 if (commandLine.hashChangedSpecified)
778 if (commandLine.hashChangedSpecified) {
719779 origCollection.computeBugHashes();
780 }
720781 commandLine.adjustFilter(project, resultCollection);
721782 ProjectStats projectStats = resultCollection.getProjectStats();
722783 projectStats.clearBugCounts();
761822 + "; ignoring filtering options that require cloud access");
762823
763824 } else if (!cloud.waitUntilIssueDataDownloaded(20, TimeUnit.SECONDS)) {
764 if (verbose)
825 if (verbose) {
765826 System.out.println("Waiting for cloud information required for filtering");
766 if (!cloud.waitUntilIssueDataDownloaded(60, TimeUnit.SECONDS))
827 }
828 if (!cloud.waitUntilIssueDataDownloaded(60, TimeUnit.SECONDS)) {
767829 disconnect(verbose, commandLine, resultCollection,
768830 "Unable to connect to cloud; ignoring filtering options that require cloud access");
831 }
769832 }
770833 }
771834
772835 commandLine.getReady(origCollection);
773836
774 for (BugInstance bug : origCollection.getCollection())
837 for (BugInstance bug : origCollection.getCollection()) {
775838 if (commandLine.accept(origCollection, bug)) {
776839 if (trimToVersion >= 0) {
777840 if (bug.getFirstVersion() > trimToVersion) {
784847 }
785848 resultCollection.add(bug, false);
786849 passed++;
787 } else
850 } else {
788851 dropped++;
852 }
853 }
789854
790855 if (commandLine.purgeHistorySpecified && commandLine.purgeHistory) {
791856 resultCollection.clearAppVersions();
792857 for (BugInstance bug : resultCollection.getCollection()) {
793858 bug.clearHistory();
794859 }
795
796
797 }
798 if (verbose)
860
861
862 }
863 if (verbose) {
799864 System.out.println(passed + " warnings passed through, " + dropped + " warnings dropped");
865 }
800866 if (commandLine.withSourceSpecified && commandLine.withSource && !commandLine.dontUpdateStats
801867 && projectStats.hasClassStats()) {
802868 for (PackageStats stats : projectStats.getPackageStats()) {
804870 while (i.hasNext()) {
805871 String className = i.next().getName();
806872 if (sourceSearcher.sourceNotFound.contains(className) || !sourceSearcher.sourceFound.contains(className)
807 && !sourceSearcher.findSource(SourceLineAnnotation.createReallyUnknown(className)))
873 && !sourceSearcher.findSource(SourceLineAnnotation.createReallyUnknown(className))) {
808874 i.remove();
875 }
809876 }
810877 }
811878
824891
825892
826893 private static void disconnect(boolean verbose, final FilterCommandLine commandLine, SortedBugCollection resultCollection,
827 String msg) {
828 if (verbose)
894 String msg) {
895 if (verbose) {
829896 System.out.println(msg);
897 }
830898 resultCollection.addError(msg);
831899 commandLine.maxAgeSpecified = commandLine.notAProblemSpecified = commandLine.shouldFixSpecified = false;
832900 }
833901
834902 }
835903
836 // vim:ts=4
5555
5656 @Override
5757 public void handleOption(String option, String optionalExtraPart) {
58 if (option.equals("-formatDates"))
58 if ("-formatDates".equals(option)) {
5959 formatDates = true;
60 else
60 } else {
6161 throw new IllegalArgumentException("unknown option: " + option);
62 }
6263 }
6364
6465 @Override
7576 int argCount = commandLine.parse(args, 0, Integer.MAX_VALUE, USAGE);
7677
7778 PrintWriter out = UTF8.printWriter(System.out, true);
78 if (argCount == args.length)
79 if (argCount == args.length) {
7980 listVersion(out, null, commandLine.formatDates);
80 else {
81 } else {
8182 out.println("version\ttime\tclasses\tNCSS\terrors\ttotal\thigh\tmedium\tlow\tfile");
8283 while (argCount < args.length) {
8384 String fileName = args[argCount++];
8889 }
8990
9091 private static void listVersion(PrintWriter out, @CheckForNull String fileName, boolean formatDates) throws IOException,
91 DocumentException {
92 DocumentException {
9293 SortedBugCollection origCollection;
9394 origCollection = new SortedBugCollection();
9495
95 if (fileName == null)
96 if (fileName == null) {
9697 origCollection.readXML(System.in);
97 else
98 } else {
9899 origCollection.readXML(fileName);
100 }
99101 AppVersion appVersion = origCollection.getCurrentAppVersion();
100102 ProjectStats stats = origCollection.getProjectStats();
101103 out.print(appVersion.getReleaseName());
102104 out.print('\t');
103 if (formatDates)
105 if (formatDates) {
104106 out.print("\"" + new Date(appVersion.getTimestamp()) + "\"");
105 else
107 } else {
106108 out.print(appVersion.getTimestamp());
109 }
107110 out.print('\t');
108111
109112 out.print(appVersion.getNumClasses());
2424
2525 /**
2626 * List the analysis errors in a bug collection
27 *
27 *
2828 * @author Bill Pugh
2929 */
3030 public class ListErrors {
3838 bugCollection.readXML(args[0]);
3939 for (AnalysisError e : bugCollection.getErrors()) {
4040 String msg = e.getExceptionMessage();
41 if (msg != null && msg.trim().length() > 0)
41 if (msg != null && msg.trim().length() > 0) {
4242 System.out.println(e.getMessage() + " : " + msg);
43
44 else
43 } else {
4544 System.out.println(e.getMessage());
45 }
4646
4747 }
4848
101101 */
102102 @Override
103103 protected void handleOption(String option, String optionExtraPart) throws IOException {
104 if (option.equals("-gui"))
104 if ("-gui".equals(option)) {
105105 options.alwaysShowGui = true;
106 else
106 } else {
107107 throw new IllegalArgumentException("Unknown option : " + option);
108 }
108109 }
109110
110111 /*
116117 */
117118 @Override
118119 protected void handleOptionWithArgument(String option, String argument) throws IOException {
119 if (option.equals("-workingDir"))
120 if ("-workingDir".equals(option)) {
120121 options.workingDirList = Arrays.asList(argument.split(","));
121 else if (option.equals("-srcDir"))
122 } else if ("-srcDir".equals(option)) {
122123 options.srcDirList = Arrays.asList(argument.split(","));
123 else if (option.equals("-maxRank"))
124 } else if ("-maxRank".equals(option)) {
124125 options.maxRank = Integer.parseInt(argument);
125 else if (option.equals("-maxAge"))
126 } else if ("-maxAge".equals(option)) {
126127 options.maxAge = Integer.parseInt(argument);
127 else if (option.equals("-cloud"))
128 } else if ("-cloud".equals(option)) {
128129 options.cloudId = argument;
129 else if (option.equals("-baseline"))
130 } else if ("-baseline".equals(option)) {
130131 try {
131132 options.baselineDate = new SimpleDateFormat("MM/dd/yyyy", Locale.ENGLISH).parse(argument);
132133 } catch (ParseException e) {
133134 System.err.println("Date " + argument + " not in MM/dd/yyyy format (e.g., 05/13/2009)");
134135 }
135 else
136 } else {
136137 throw new IllegalArgumentException("Unknown option : " + option);
138 }
137139 }
138140
139141 }
170172 int argCount = commandLine.parse(argv, 1, Integer.MAX_VALUE, "Usage: " + MergeSummarizeAndView.class.getName()
171173 + " [options] [<results1> <results2> ... <resultsn>] ");
172174
173 for (int i = argCount; i < argv.length; i++)
175 for (int i = argCount; i < argv.length; i++) {
174176 options.analysisFiles.add(argv[i]);
177 }
175178 MergeSummarizeAndView msv = new MergeSummarizeAndView(options);
176179 boolean isCloudManagedByGui = false;
177180 try {
317320 long old = System.currentTimeMillis() - options.maxAge * (24 * 3600 * 1000L);
318321 if (options.baselineDate != null) {
319322 long old2 = options.baselineDate.getTime();
320 if (old2 > old)
323 if (old2 > old) {
321324 old = old2;
325 }
322326 }
323327
324328 scaryBugs = results.createEmptyCollectionWithMetadata();
325 for (BugInstance warning : results.getCollection())
329 for (BugInstance warning : results.getCollection()) {
326330 if (!project.getSuppressionFilter().match(warning)) {
327331 int rank = BugRanker.findRank(warning);
328 if (rank > BugRanker.VISIBLE_RANK_MAX)
332 if (rank > BugRanker.VISIBLE_RANK_MAX) {
329333 continue;
334 }
330335 if (cloud.getConsensusDesignation(warning).score() < 0) {
331336 harmless++;
332337 continue;
335340 long firstSeen = cloud.getFirstSeen(warning);
336341 boolean isOld = FindBugs.validTimestamp(firstSeen) && firstSeen < old;
337342 boolean highRank = rank > options.maxRank;
338 if (highRank)
343 if (highRank) {
339344 numLowConfidence++;
340 else if (isOld)
345 } else if (isOld) {
341346 tooOld++;
342 else
347 } else {
343348 scaryBugs.add(warning);
344 }
349 }
350 }
351 }
345352 }
346353
347354 private boolean report() {
364371 if (hasScaryBugs) {
365372 System.out.println();
366373 System.out.print("plus ");
367 if (numLowConfidence > 0)
374 if (numLowConfidence > 0) {
368375 System.out.printf("%d less scary recent issues", numLowConfidence);
369 if (numLowConfidence > 0 && tooOld > 0)
376 }
377 if (numLowConfidence > 0 && tooOld > 0) {
370378 System.out.printf(" and ");
371 if (tooOld > 0)
379 }
380 if (tooOld > 0) {
372381 System.out.printf("%d older issues", tooOld);
382 }
373383 System.out.println();
374384 }
375385 }
409419
410420 }
411421
412 // vim:ts=3
3939 * Mine historical information from a BugCollection. The BugCollection should be
4040 * built using UpdateBugCollection to record the history of analyzing all
4141 * versions over time.
42 *
42 *
4343 * @author David Hovemeyer
4444 * @author William Pugh
4545 */
4646 public class MineBugHistory {
4747 /**
48 *
48 *
4949 */
5050 private static final int WIDTH = 12;
5151
8585
8686 void increment(int key) {
8787 tuple[key]++;
88 if (key == ADDED || key == RETAINED || key == NEWCODE)
88 if (key == ADDED || key == RETAINED || key == NEWCODE) {
8989 tuple[ACTIVE_NOW]++;
90 }
9091 }
9192
9293 int get(int key) {
162163 BugInstance bugInstance = j.next();
163164
164165 for (int i = 0; i <= maxSequence; ++i) {
165 if (bugInstance.getFirstVersion() > i)
166 if (bugInstance.getFirstVersion() > i) {
166167 continue;
168 }
167169 boolean activePrevious = bugInstance.getFirstVersion() < i
168170 && (!bugInstance.isDead() || bugInstance.getLastVersion() >= i - 1);
169171 boolean activeCurrent = !bugInstance.isDead() || bugInstance.getLastVersion() >= i;
170172
171173 int key = getKey(activePrevious, activeCurrent);
172 if (key == REMOVED && !bugInstance.isRemovedByChangeOfPersistingClass())
174 if (key == REMOVED && !bugInstance.isRemovedByChangeOfPersistingClass()) {
173175 key = REMOVEDCODE;
174 else if (key == ADDED && !bugInstance.isIntroducedByChangeOfExistingClass())
176 } else if (key == ADDED && !bugInstance.isIntroducedByChangeOfExistingClass()) {
175177 key = NEWCODE;
178 }
176179 versionList[i].increment(key);
177180 }
178181 }
181184 }
182185
183186 public void dump(PrintStream out) {
184 if (xml)
187 if (xml) {
185188 dumpXml(out);
186 else if (noTabs)
189 } else if (noTabs) {
187190 dumpNoTabs(out);
188 else if (summary)
191 } else if (summary) {
189192 dumpSummary(out);
190 else
193 } else {
191194 dumpOriginal(out);
195 }
192196 }
193197
194198 public void dumpSummary(PrintStream out) {
208212 b.append('-');
209213 b.append(removed);
210214 }
211 if (added == 0 && removed == 0)
215 if (added == 0 && removed == 0) {
212216 b.append('0');
217 }
213218
214219 int paddingNeeded = WIDTH - b.length() % WIDTH;
215 if (paddingNeeded > 0)
220 if (paddingNeeded > 0) {
216221 b.append(" ".substring(0, paddingNeeded));
222 }
217223 }
218224 int errors = bugCollection.getErrors().size();
219 if (errors > 0)
225 if (errors > 0) {
220226 b.append(" ").append(errors).append(" errors");
227 }
221228
222229 out.println(b.toString());
223230 }
232239 out.print('\t');
233240 out.print(appVersion != null ? appVersion.getReleaseName() : "");
234241 out.print('\t');
235 if (formatDates)
242 if (formatDates) {
236243 out.print("\"" + (appVersion != null ? dateFormat.format(new Date(appVersion.getTimestamp())) : "") + "\"");
237 else
244 } else {
238245 out.print(appVersion != null ? appVersion.getTimestamp() / 1000 : 0L);
246 }
239247 out.print('\t');
240248 if (appVersion != null) {
241249 out.print(appVersion.getNumClasses());
242250 out.print('\t');
243251 out.print(appVersion.getCodeSize());
244252
245 } else
253 } else {
246254 out.print("\t0\t0");
255 }
247256
248257 for (int j = 0; j < TUPLE_SIZE; ++j) {
249258 out.print('\t');
255264
256265 /** emit <code>width</code> space characters to <code>out</code> */
257266 private static void pad(int width, PrintStream out) {
258 while (width-- > 0)
267 while (width-- > 0) {
259268 out.print(' ');
269 }
260270 }
261271
262272 /**
263273 * equivalent to out.print(obj) except it may be padded on the left or right
264 *
274 *
265275 * @param width
266276 * padding will occur if the stringified oxj is shorter than this
267277 * @param alignRight
274284 private static void print(int width, boolean alignRight, PrintStream out, Object obj) {
275285 String s = String.valueOf(obj);
276286 int padLen = width - s.length();
277 if (alignRight)
287 if (alignRight) {
278288 pad(padLen, out);
289 }
279290 out.print(s); // doesn't truncate if (s.length() > width)
280 if (!alignRight)
291 if (!alignRight) {
281292 pad(padLen, out);
293 }
282294 }
283295
284296 /**
286298 * with a fixed-width font) by padding with spaces instead of using tabs.
287299 * Also, timestamps are formatted more tersely (-formatDates option). The
288300 * bad news is that it requires a minimum of 112 columns.
289 *
301 *
290302 * @see #dumpOriginal(PrintStream)
291303 */
292304 public void dumpNoTabs(PrintStream out) {
322334 out.print(' ');
323335
324336 long ts = (appVersion != null ? appVersion.getTimestamp() : 0L);
325 if (formatDates)
337 if (formatDates) {
326338 print(12, false, out, dateFormat.format(ts));
327 else
339 } else {
328340 print(10, false, out, ts / 1000);
341 }
329342 out.print(' ');
330343
331344 print(7, true, out, appVersion != null ? appVersion.getNumClasses() : 0);
357370 out.print(appVersion != null ? appVersion.getReleaseName() : "");
358371 out.print("\" ");
359372 out.print("time=\"");
360 if (formatDates)
373 if (formatDates) {
361374 out.print((appVersion != null ? new Date(appVersion.getTimestamp()).toString() : ""));
362 else
375 } else {
363376 out.print(appVersion != null ? appVersion.getTimestamp() : 0L);
377 }
364378 out.print("\"");
365379 out.println(">");
366380
391405 /**
392406 * Get key used to classify the presence and/or abscence of a BugInstance in
393407 * successive versions in the history.
394 *
408 *
395409 * @param activePrevious
396410 * true if the bug was active in the previous version, false if
397411 * not
400414 * @return the key: one of ADDED, RETAINED, REMOVED, and DEAD
401415 */
402416 private int getKey(boolean activePrevious, boolean activeCurrent) {
403 if (activePrevious)
417 if (activePrevious) {
404418 return activeCurrent ? RETAINED : REMOVED;
405 else
419 } else {
406420 // !activePrevious
407421 return activeCurrent ? ADDED : DEAD;
422 }
408423 }
409424
410425 class MineBugHistoryCommandLine extends CommandLine {
418433
419434 @Override
420435 public void handleOption(String option, String optionalExtraPart) {
421 if (option.equals("-formatDates"))
436 if ("-formatDates".equals(option)) {
422437 setFormatDates(true);
423 else if (option.equals("-noTabs"))
438 } else if ("-noTabs".equals(option)) {
424439 setNoTabs();
425 else if (option.equals("-xml"))
440 } else if ("-xml".equals(option)) {
426441 setXml();
427 else if (option.equals("-summary"))
442 } else if ("-summary".equals(option)) {
428443 setSummary();
429 else
444 } else {
430445 throw new IllegalArgumentException("unknown option: " + option);
446 }
431447 }
432448
433449 @Override
447463 + " [options] [<xml results> [<history]] ");
448464
449465 SortedBugCollection bugCollection = new SortedBugCollection();
450 if (argCount < args.length)
466 if (argCount < args.length) {
451467 bugCollection.readXML(args[argCount++]);
452 else
468 } else {
453469 bugCollection.readXML(System.in);
470 }
454471 mineBugHistory.setBugCollection(bugCollection);
455472
456473 mineBugHistory.execute();
4545 public ObfuscateBugs execute() {
4646 ProjectPackagePrefixes foo = new ProjectPackagePrefixes();
4747
48 for (BugInstance b : bugCollection.getCollection())
48 for (BugInstance b : bugCollection.getCollection()) {
4949 foo.countBug(b);
50 }
5051 foo.report();
5152
5253 return this;
6162
6263 /*
6364 * (non-Javadoc)
64 *
65 *
6566 * @see
6667 * edu.umd.cs.findbugs.config.CommandLine#handleOptionWithArgument(java
6768 * .lang.String, java.lang.String)
8081 int argCount = commandLine.parse(args, 0, 2, "Usage: " + ObfuscateBugs.class.getName() + " [options] [<xml results>] ");
8182
8283 SortedBugCollection bugCollection = new SortedBugCollection();
83 if (argCount < args.length)
84 if (argCount < args.length) {
8485 bugCollection.readXML(args[argCount++]);
85 else
86 } else {
8687 bugCollection.readXML(System.in);
88 }
8789
8890 SortedBugCollection results = bugCollection.createEmptyCollectionWithMetadata();
8991 Project project = results.getProject();
2323
2424 /**
2525 * Print the AppVersion information from a BugCollection.
26 *
26 *
2727 * @author David Hovemeyer
2828 */
2929 public class PrintAppVersion {
3535 * Mine historical information from a BugCollection. The BugCollection should be
3636 * built using UpdateBugCollection to record the history of analyzing all
3737 * versions over time.
38 *
38 *
3939 * @author David Hovemeyer
4040 * @author William Pugh
4141 */
5656 public RebornIssues execute() {
5757
5858 Map<String, List<BugInstance>> map = new HashMap<String, List<BugInstance>>();
59 for (BugInstance b : bugCollection.getCollection())
59 for (BugInstance b : bugCollection.getCollection()) {
6060 if (b.getFirstVersion() != 0 || b.getLastVersion() != -1) {
6161 List<BugInstance> lst = map.get(b.getInstanceHash());
6262 if (lst == null) {
6565 }
6666 lst.add(b);
6767 }
68 }
6869 for (List<BugInstance> lst : map.values()) {
6970 if (lst.size() > 1) {
7071 TreeSet<Long> removalTimes = new TreeSet<Long>();
7374 String bugPattern = "XXX";
7475 for (BugInstance b : lst) {
7576 bugPattern = b.getBugPattern().getType();
76 if (b.getFirstVersion() > 0)
77 if (b.getFirstVersion() > 0) {
7778 additionTimes.add(b.getFirstVersion());
78 if (b.getLastVersion() != -1)
79 }
80 if (b.getLastVersion() != -1) {
7981 removalTimes.add(b.getLastVersion());
82 }
8083 }
8184 Iterator<Long> aI = additionTimes.iterator();
82 if (!aI.hasNext())
85 if (!aI.hasNext()) {
8386 continue;
87 }
8488 long a = aI.next();
8589 loop: for (Long removed : removalTimes) {
8690 while (a <= removed) {
87 if (!aI.hasNext())
91 if (!aI.hasNext()) {
8892 break loop;
93 }
8994 a = aI.next();
9095 }
9196 System.out.printf("%5d %5d %s%n", removed, a, bugPattern);
119124 + " [options] [<xml results> [<history]] ");
120125
121126 SortedBugCollection bugCollection = new SortedBugCollection();
122 if (argCount < args.length)
127 if (argCount < args.length) {
123128 bugCollection.readXML(args[argCount++]);
124 else
129 } else {
125130 bugCollection.readXML(System.in);
131 }
126132 reborn.setBugCollection(bugCollection);
127133 reborn.execute();
128134
2626 public class RecursiveSearchForJavaFiles {
2727
2828 public static void main(String args[]) {
29 for (File f : search(new File(args[0])))
29 for (File f : search(new File(args[0]))) {
3030 System.out.println(f.getPath());
31 }
3132 }
3233
3334 public static Set<File> search(File root) {
3940 while (!worklist.isEmpty()) {
4041 File next = worklist.removeFirst();
4142 File[] files = next.listFiles();
42 if (files != null)
43 if (files != null) {
4344 for (File f : files) {
44 if (f.getName().endsWith(".java"))
45 if (f.getName().endsWith(".java")) {
4546 result.add(f);
46 else if (f.isDirectory() && directories.add(f)) {
47 } else if (f.isDirectory() && directories.add(f)) {
4748 worklist.add(f);
4849 }
4950
5051 }
52 }
5153
5254 }
5355 return result;
6868 PatternMatcher(String arg) {
6969 String[] p = arg.split(",");
7070 this.pattern = new Pattern[p.length];
71 for (int i = 0; i < p.length; i++)
71 for (int i = 0; i < p.length; i++) {
7272 pattern[i] = Pattern.compile(p[i]);
73 }
7374 }
7475
7576 public boolean matches(String arg) {
76 for (Pattern p : pattern)
77 if (p.matcher(arg).find())
77 for (Pattern p : pattern) {
78 if (p.matcher(arg).find()) {
7879 return true;
80 }
81 }
7982
8083 return false;
8184 }
9396 }
9497
9598 public boolean matches(String arg) {
96 for (String p : prefixes)
97 if (arg.startsWith(p))
99 for (String p : prefixes) {
100 if (arg.startsWith(p)) {
98101 return true;
102 }
103 }
99104
100105 return false;
101106 }
102107
103108 public boolean matchesEverything() {
104 for (String p : prefixes)
105 if (p.length() == 0)
109 for (String p : prefixes) {
110 if (p.length() == 0) {
106111 return true;
112 }
113 }
107114 return false;
108115 }
109116 }
135142 addOption("-maxClasses", "num", "maximum number of classes per analysis*.jar file");
136143 addOption("-outputDir", "dir", "directory for the generated jar files");
137144 addSwitch("-ignoreTimestamps", "ignore timestamps on zip entries; use first version found");
138
145
139146 addOption("-prefix", "class name prefix",
140147 "comma separated list of class name prefixes that should be analyzed (e.g., edu.umd.cs.)");
141148 addOption("-exclude", "class name prefix",
156163 */
157164 @Override
158165 protected void handleOption(String option, String optionExtraPart) throws IOException {
159 if (option.equals("-analyzeOnly")) {
166 if ("-analyzeOnly".equals(option)) {
160167 onlyAnalyze = true;
161 } else if (option.equals("-ignoreTimestamps")) {
168 } else if ("-ignoreTimestamps".equals(option)) {
162169 ignoreTimestamps = true;
163 }else
170 } else {
164171 throw new IllegalArgumentException("Unknown option : " + option);
172 }
165173 }
166174
167175 /*
173181 */
174182 @Override
175183 protected void handleOptionWithArgument(String option, String argument) throws IOException {
176 if (option.equals("-prefix"))
184 if ("-prefix".equals(option)) {
177185 prefix = new PrefixMatcher(argument);
178 else if (option.equals("-exclude"))
186 } else if ("-exclude".equals(option)) {
179187 exclude = new PrefixMatcher(argument);
180 else if (option.equals("-inputFileList"))
188 } else if ("-inputFileList".equals(option)) {
181189 inputFileList = argument;
182 else if (option.equals("-auxFileList"))
190 } else if ("-auxFileList".equals(option)) {
183191 auxFileList = argument;
184 else if (option.equals("-maxClasses"))
192 } else if ("-maxClasses".equals(option)) {
185193 maxClasses = Integer.parseInt(argument);
186 else if (option.equals("-maxAge"))
194 } else if ("-maxAge".equals(option)) {
187195 maxAge = System.currentTimeMillis() - (24 * 60 * 60 * 1000L) * Integer.parseInt(argument);
188 else if (option.equals("-outputDir"))
196 } else if ("-outputDir".equals(option)) {
189197 outputDir = new File(argument);
190 else if (option.equals("-excludePattern"))
198 } else if ("-excludePattern".equals(option)) {
191199 excludePatterns = new PatternMatcher(argument);
192 else
200 } else {
193201 throw new IllegalArgumentException("Unknown option : " + option);
202 }
194203 }
195204
196205 boolean skip(ZipEntry ze) {
211220 }
212221
213222 public static void readFromStandardInput(Collection<String> result) throws IOException {
214 readFrom(result, UserTextFile.bufferedReader(System.in));
223 readFrom(result, UserTextFile.bufferedReader(System.in));
215224 }
216225
217226 SortedMap<String, ZipOutputStream> analysisOutputFiles = new TreeMap<String, ZipOutputStream>();
219228 public @Nonnull
220229 ZipOutputStream getZipOutputFile(String path) {
221230 ZipOutputStream result = analysisOutputFiles.get(path);
222 if (result != null)
231 if (result != null) {
223232 return result;
233 }
224234 SortedMap<String, ZipOutputStream> head = analysisOutputFiles.headMap(path);
225235 String matchingPath = head.lastKey();
226236 result = analysisOutputFiles.get(matchingPath);
227 if (result == null)
237 if (result == null) {
228238 throw new IllegalArgumentException("No zip output file for " + path);
239 }
229240 return result;
230241 }
231242
248259
249260 String getNextAuxilaryFileOutput() {
250261 String result;
251 if (auxilaryCount == 1)
262 if (auxilaryCount == 1) {
252263 result = "auxilary.jar";
253 else
264 } else {
254265 result = "auxilary" + (auxilaryCount) + ".jar";
266 }
255267 auxilaryCount++;
256268 System.out.println("Starting " + result);
257269 return result;
259271
260272 String getNextAnalyzeFileOutput() {
261273 String result;
262 if (analysisCount == 1)
274 if (analysisCount == 1) {
263275 result = "analyze.jar";
264 else
276 } else {
265277 result = "analyze" + (analysisCount) + ".jar";
278 }
266279 analysisCount++;
267280 System.out.println("Starting " + result);
268281 return result;
295308 final byte buffer[] = new byte[8192];
296309
297310 private boolean exclude(String dottedName) {
298 if (!Character.isJavaIdentifierStart(dottedName.charAt(0)))
299 return true;
311 if (!Character.isJavaIdentifierStart(dottedName.charAt(0))) {
312 return true;
313 }
300314 if (commandLine.excludePatterns != null && commandLine.excludePatterns.matches(dottedName)
301315 || commandLine.exclude.matches(dottedName)) {
302316 excluded.add(dottedName);
310324
311325 ArrayList<String> fileList = new ArrayList<String>();
312326
313 if (commandLine.inputFileList != null)
327 if (commandLine.inputFileList != null) {
314328 readFrom(fileList, UTF8.fileReader(commandLine.inputFileList));
315 else if (argCount == args.length)
316 readFromStandardInput(fileList);
317 else
329 } else if (argCount == args.length) {
330 readFromStandardInput(fileList);
331 } else {
318332 fileList.addAll(Arrays.asList(args).subList(argCount, args.length));
333 }
319334 ArrayList<String> auxFileList = new ArrayList<String>();
320335 if (commandLine.auxFileList != null) {
321336 readFrom(auxFileList, UTF8.fileReader(commandLine.auxFileList));
330345 System.err.println("Skipping " + fInName + ", too old (" + new Date(f.lastModified()) + ")");
331346 continue;
332347 }
333
348
334349 int oldSize = copied.size();
335350 classFileFound = false;
336351 if (processZipEntries(f, new ZipElementHandler() {
337352 boolean checked = false;
338353
354 @Override
339355 public void handle(ZipFile file, ZipEntry ze) throws IOException {
340 if (commandLine.skip(ze))
341 return;
356 if (commandLine.skip(ze)) {
357 return;
358 }
342359 String name = ze.getName();
343360
344361 String dottedName = name.replace('/', '.');
345 if (exclude(dottedName))
346 return;
362 if (exclude(dottedName)) {
363 return;
364 }
347365
348366 if (!checked) {
349 checked = true;
367 checked = true;
350368 if (embeddedNameMismatch(file, ze)) {
351369 System.out.println("Class name mismatch for " + name + " in " + file.getName());
352370 throw new ClassFileNameMismatch();
353371 }
354372 }
355 if (!commandLine.prefix.matches(dottedName))
356 return;
373 if (!commandLine.prefix.matches(dottedName)) {
374 return;
375 }
357376 classFileFound = true;
358377 long timestamp = ze.getTime();
359378 Long oldTimestamp = copied.get(name);
374393 * @return
375394 */
376395
377 }) && oldSize < copied.size())
396 }) && oldSize < copied.size()) {
378397 inputZipFiles.add(f);
379 else if (classFileFound)
398 } else if (classFileFound) {
380399 System.err.println("Skipping " + fInName + ", no new classes found");
381 else
400 } else {
382401 System.err.println("Skipping " + fInName + ", no classes found");
402 }
383403 }
384404 for (String fInName : auxFileList) {
385405 final File f = new File(fInName);
390410 int oldSize = copied.size();
391411 classFileFound = false;
392412 if (processZipEntries(f, new ZipElementHandler() {
413 @Override
393414 public void handle(ZipFile file, ZipEntry ze) {
394 if (commandLine.skip(ze))
395 return;
415 if (commandLine.skip(ze)) {
416 return;
417 }
396418
397419 String name = ze.getName();
398420 String dottedName = name.replace('/', '.');
409431 }
410432 }
411433 }
412 }) && oldSize < copied.size())
434 }) && oldSize < copied.size()) {
413435 auxZipFiles.add(f);
414 else if (classFileFound)
436 } else if (classFileFound) {
415437 System.err.println("Skipping aux file " + fInName + ", no new classes found");
416 else
438 } else {
417439 System.err.println("Skipping aux file" + fInName + ", no classes found");
440 }
418441 }
419442
420443 System.out.printf(" # Zip/jar files: %2d%n", inputZipFiles.size());
421444 System.out.printf("# aux Zip/jar files: %2d%n", auxZipFiles.size());
422445 System.out.printf("Unique class files: %6d%n", copied.size());
423 if (numFilesToAnalyze != copied.size())
446 if (numFilesToAnalyze != copied.size()) {
424447 System.out.printf(" files to analyze: %6d%n", numFilesToAnalyze);
425
426 if (!excluded.isEmpty())
448 }
449
450 if (!excluded.isEmpty()) {
427451 System.out.printf(" excluded files: %6d%n", excluded.size());
428
429 if (commandLine.onlyAnalyze)
452 }
453
454 if (commandLine.onlyAnalyze) {
430455 return;
431
432 if (numFilesToAnalyze < copied.size() || numFilesToAnalyze > commandLine.maxClasses)
456 }
457
458 if (numFilesToAnalyze < copied.size() || numFilesToAnalyze > commandLine.maxClasses) {
433459 auxilaryOut = createZipFile(getNextAuxilaryFileOutput());
460 }
434461
435462 int count = Integer.MAX_VALUE;
436463 String oldBaseClass = "x x";
441468 int firstDollar = path.indexOf('$', lastSlash);
442469 String baseClass = firstDollar < 0 ? path : path.substring(0, firstDollar - 1);
443470 boolean switchOutput;
444 if (count > commandLine.maxClasses)
471 if (count > commandLine.maxClasses) {
445472 switchOutput = true;
446 else if (count + 50 > commandLine.maxClasses && !baseClass.equals(oldBaseClass))
473 } else if (count + 50 > commandLine.maxClasses && !baseClass.equals(oldBaseClass)) {
447474 switchOutput = true;
448 else if (count + 250 > commandLine.maxClasses && !packageName.equals(oldPackage))
475 } else if (count + 250 > commandLine.maxClasses && !packageName.equals(oldPackage)) {
449476 switchOutput = true;
450 else
477 } else {
451478 switchOutput = false;
479 }
452480
453481 if (switchOutput) {
454482 // advance
467495 final File ff = f;
468496 processZipEntries(f, new ZipElementHandler() {
469497
498 @Override
470499 public void handle(ZipFile zipInputFile, ZipEntry ze) throws IOException {
471 if (commandLine.skip(ze))
472 return;
473
474
500 if (commandLine.skip(ze)) {
501 return;
502 }
503
504
475505 String name = ze.getName();
476506 String dottedName = name.replace('/', '.');
477 if (exclude(dottedName))
478 return;
507 if (exclude(dottedName)) {
508 return;
509 }
479510 if (!ff.equals(copyFrom.get(name))) {
480511 return;
481512 }
482 if (name.contains("DefaultProblem.class"))
513 if (name.contains("DefaultProblem.class")) {
483514 System.out.printf("%40s %40s%n", name, ff);
484
515 }
516
485517 boolean writeToAnalyzeOut = false;
486518 boolean writeToAuxilaryOut = false;
487519 if (commandLine.prefix.matches(dottedName)) {
488520 writeToAnalyzeOut = true;
489 if (numFilesToAnalyze > commandLine.maxClasses)
521 if (numFilesToAnalyze > commandLine.maxClasses) {
490522 writeToAuxilaryOut = true;
491 } else
523 }
524 } else {
492525 writeToAuxilaryOut = auxilaryOut != null;
526 }
493527 ZipOutputStream out = null;
494528 if (writeToAnalyzeOut) {
495529 out = getZipOutputFile(name);
516550 System.err.println("Opening aux file " + f);
517551 processZipEntries(f, new ZipElementHandler() {
518552
553 @Override
519554 public void handle(ZipFile zipInputFile, ZipEntry ze) throws IOException {
520 if (commandLine.skip(ze))
521 return;
555 if (commandLine.skip(ze)) {
556 return;
557 }
522558
523559 String name = ze.getName();
524560 String dottedName = name.replace('/', '.');
525561
526 if (exclude(dottedName))
527 return;
562 if (exclude(dottedName)) {
563 return;
564 }
528565 if (!ff.equals(copyFrom.get(name))) {
529566 return;
530567 }
542579 });
543580 }
544581
545 if (auxilaryOut != null)
582 if (auxilaryOut != null) {
546583 auxilaryOut.close();
547 for (ZipOutputStream out : analysisOutputFiles.values())
584 }
585 for (ZipOutputStream out : analysisOutputFiles.values()) {
548586 out.close();
587 }
549588
550589 System.out.println("All done");
551590 }
552591
553 /**
554 * @param fileName
555 * @return
556 * @throws FileNotFoundException
557 */
558592 private ZipOutputStream createZipFile(String fileName) throws FileNotFoundException {
559593 File newFile = new File(commandLine.outputDir, fileName);
560594 return new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(newFile)));
572606 System.out.println(name);
573607 System.out.println(" " + className);
574608 }
575 if (computedFileName.equals(name))
609 if (computedFileName.equals(name)) {
576610 return false;
611 }
577612 System.out.println("In " + name + " found " + className);
578613 return true;
579614 }
584619
585620 while (true) {
586621 int bytesRead = zipIn.read(buffer);
587 if (bytesRead < 0)
622 if (bytesRead < 0) {
588623 break;
589 if (writeToAnalyzeOut)
624 }
625 if (writeToAnalyzeOut) {
590626 analyzeOut1.write(buffer, 0, bytesRead);
591 if (writeToAuxilaryOut)
627 }
628 if (writeToAuxilaryOut) {
592629 auxilaryOut1.write(buffer, 0, bytesRead);
593 }
594 if (writeToAnalyzeOut)
630 }
631 }
632 if (writeToAnalyzeOut) {
595633 analyzeOut1.closeEntry();
596 if (writeToAuxilaryOut)
634 }
635 if (writeToAuxilaryOut) {
597636 auxilaryOut1.closeEntry();
637 }
598638 zipIn.close();
599639 }
600640
625665 zipInputFile = new ZipFile(f);
626666 for (Enumeration<? extends ZipEntry> e = zipInputFile.entries(); e.hasMoreElements();) {
627667 ZipEntry ze = e.nextElement();
628 if (!ze.isDirectory() && ze.getName().endsWith(".class") && ze.getSize() != 0)
668 if (!ze.isDirectory() && ze.getName().endsWith(".class") && ze.getSize() != 0) {
629669 handler.handle(zipInputFile, ze);
670 }
630671
631672 }
632673 zipInputFile.close();
641682 }
642683
643684
644 /**
645 * @param ze
646 * @return
647 */
648685 public ZipEntry newZipEntry(ZipEntry ze) {
649686 ZipEntry ze2 = new ZipEntry(ze.getName());
650687 ze2.setComment(ze.getComment());
108108
109109 @Override
110110 protected void handleOption(String option, String optionExtraPart) throws IOException {
111 if (option.equals("-withMessages"))
111 if ("-withMessages".equals(option)) {
112112 withMessages = true;
113 else if (option.equals("-resetSource"))
113 } else if ("-resetSource".equals(option)) {
114114 resetSource = true;
115 else if (option.equals("-resetProject"))
115 } else if ("-resetProject".equals(option)) {
116116 resetProject = true;
117 else if (option.equals("-purgeStats"))
117 } else if ("-purgeStats".equals(option)) {
118118 purgeStats = true;
119 else if (option.equals("-purgeDesignations"))
119 } else if ("-purgeDesignations".equals(option)) {
120120 purgeDesignations = true;
121 else if (option.equals("-purgeClassStats"))
121 } else if ("-purgeClassStats".equals(option)) {
122122 purgeClassStats = true;
123 else if (option.equals("-purgeMissingClasses"))
123 } else if ("-purgeMissingClasses".equals(option)) {
124124 purgeMissingClasses = true;
125 else
125 } else {
126126 throw new IllegalArgumentException("no option " + option);
127 }
127128
128129 }
129130
130131 @Override
131132 protected void handleOptionWithArgument(String option, String argument) throws IOException {
132 if (option.equals("-name"))
133 if ("-name".equals(option)) {
133134 revisionName = argument;
134 else if (option.equals("-cloud"))
135 } else if ("-cloud".equals(option)) {
135136 cloudId = argument;
136 else if (option.equals("-cloudProperty")) {
137 } else if ("-cloudProperty".equals(option)) {
137138 int e = argument.indexOf('=');
138 if (e == -1)
139 if (e == -1) {
139140 throw new IllegalArgumentException("Bad cloud property: " + argument);
141 }
140142 String key = argument.substring(0, e);
141143 String value = argument.substring(e + 1);
142144 cloudProperties.put(key, value);
143145
144 } else if (option.equals("-projectName"))
146 } else if ("-projectName".equals(option)) {
145147 projectName = argument;
146 else if (option.equals("-suppress"))
148 } else if ("-suppress".equals(option)) {
147149 exclusionFilterFile = argument;
148 else if (option.equals("-timestamp"))
150 } else if ("-timestamp".equals(option)) {
149151 revisionTimestamp = Date.parse(argument);
150
151 else if (option.equals("-source"))
152 } else if ("-source".equals(option)) {
152153 sourcePaths.add(argument);
153 else if (option.equals("-lastVersion")) {
154 } else if ("-lastVersion".equals(option)) {
154155 lastVersion = argument;
155 } else if (option.equals("-findSource"))
156 } else if ("-findSource".equals(option)) {
156157 searchSourcePaths.add(argument);
157 else
158 } else {
158159 throw new IllegalArgumentException("Can't handle option " + option);
160 }
159161
160162 }
161163
169171
170172 SortedBugCollection origCollection = new SortedBugCollection();
171173
172 if (argCount < args.length)
174 if (argCount < args.length) {
173175 origCollection.readXML(args[argCount++]);
174 else
176 } else {
175177 origCollection.readXML(System.in);
178 }
176179 Project project = origCollection.getProject();
177180
178 if (commandLine.revisionName != null)
181 if (commandLine.revisionName != null) {
179182 origCollection.setReleaseName(commandLine.revisionName);
180 if (commandLine.projectName != null)
183 }
184 if (commandLine.projectName != null) {
181185 origCollection.getProject().setProjectName(commandLine.projectName);
182 if (commandLine.revisionTimestamp != 0)
186 }
187 if (commandLine.revisionTimestamp != 0) {
183188 origCollection.setTimestamp(commandLine.revisionTimestamp);
189 }
184190 origCollection.setWithMessages(commandLine.withMessages);
185191
186 if (commandLine.purgeDesignations)
192 if (commandLine.purgeDesignations) {
187193 for (BugInstance b : origCollection) {
188194 b.setUserDesignation(null);
189195 }
196 }
190197 if (commandLine.exclusionFilterFile != null) {
191198 project.setSuppressionFilter(Filter.parseFilter(commandLine.exclusionFilterFile));
192199 }
204211 project.getCloudProperties().setProperty(e.getKey(), e.getValue());
205212 reinitializeCloud = true;
206213 }
207
208 if (commandLine.resetSource)
214
215 if (commandLine.resetSource) {
209216 project.getSourceDirList().clear();
210 for (String source : commandLine.sourcePaths)
217 }
218 for (String source : commandLine.sourcePaths) {
211219 project.addSourceDir(source);
212 if (commandLine.purgeStats)
220 }
221 if (commandLine.purgeStats) {
213222 origCollection.getProjectStats().getPackageStats().clear();
214 if (commandLine.purgeClassStats)
223 }
224 if (commandLine.purgeClassStats) {
215225 for (PackageStats ps : origCollection.getProjectStats().getPackageStats()) {
216226 ps.getClassStats().clear();
217227 }
218 if (commandLine.purgeMissingClasses)
228 }
229 if (commandLine.purgeMissingClasses) {
219230 origCollection.clearMissingClasses();
231 }
220232 if (commandLine.lastVersion != null) {
221233 long last = edu.umd.cs.findbugs.workflow.Filter.FilterCommandLine.getVersionNum(origCollection,
222234 commandLine.lastVersion, true);
247259 }
248260 }
249261 Set<String> foundPaths = new HashSet<String>();
250 for (String f : commandLine.searchSourcePaths)
262 for (String f : commandLine.searchSourcePaths) {
251263 for (File javaFile : RecursiveSearchForJavaFiles.search(new File(f))) {
252264 Set<String> matchingMissingClasses = missingFiles.get(javaFile.getName());
253265 if (matchingMissingClasses == null) {
254266 // System.out.println("Nothing for " + javaFile);
255 } else
267 } else {
256268 for (String sourcePath : matchingMissingClasses) {
257269 String path = javaFile.getAbsolutePath();
258270 if (path.endsWith(sourcePath)) {
261273
262274 }
263275 }
264
265 }
276 }
277
278 }
279 }
266280
267281 Set<String> toRemove = new HashSet<String>();
268 for (String p1 : foundPaths)
269 for (String p2 : foundPaths)
282 for (String p1 : foundPaths) {
283 for (String p2 : foundPaths) {
270284 if (!p1.equals(p2) && p1.startsWith(p2)) {
271285 toRemove.add(p1);
272286 break;
273287 }
288 }
289 }
274290 foundPaths.removeAll(toRemove);
275291
276292 for (String dir : foundPaths) {
277293 project.addSourceDir(dir);
278 if (argCount < args.length)
294 if (argCount < args.length) {
279295 System.out.println("Found " + dir);
280 }
281
282 }
283
296 }
297 }
298
299 }
300
284301 if (reinitializeCloud)
302 {
285303 origCollection.clearCloud();
286 // OK, now we know all the missing source files
287 // we also know all the .java files in the directories we were pointed
288 // to
289
290 if (argCount < args.length)
304 // OK, now we know all the missing source files
305 // we also know all the .java files in the directories we were pointed
306 // to
307 }
308
309 if (argCount < args.length) {
291310 origCollection.writeXML(args[argCount++]);
292 else
311 } else {
293312 origCollection.writeXML(System.out);
313 }
294314
295315 }
296316
4141 }
4242
4343 public boolean findSource(SourceLineAnnotation srcLine) {
44 if (srcLine == null)
44 if (srcLine == null) {
4545 return false;
46 }
4647 String cName = srcLine.getClassName();
47 if (sourceFound.contains(cName))
48 if (sourceFound.contains(cName)) {
4849 return true;
49 if (sourceNotFound.contains(cName))
50 }
51 if (sourceNotFound.contains(cName)) {
5052 return false;
53 }
5154
5255 boolean result = sourceFinder.hasSourceFile(srcLine);
5356 return result;
5457 }
5558
5659 public boolean findSource0(SourceLineAnnotation srcLine) {
57 if (srcLine == null)
60 if (srcLine == null) {
5861 return false;
62 }
5963 String cName = srcLine.getClassName();
60 if (sourceFound.contains(cName))
64 if (sourceFound.contains(cName)) {
6165 return true;
62 if (sourceNotFound.contains(cName))
66 }
67 if (sourceNotFound.contains(cName)) {
6368 return false;
69 }
6470
6571 try {
6672 InputStream in = sourceFinder.openSource(srcLine);
4545 public TestingGround execute() {
4646 ProjectPackagePrefixes foo = new ProjectPackagePrefixes();
4747
48 for (BugInstance b : bugCollection.getCollection())
48 for (BugInstance b : bugCollection.getCollection()) {
4949 foo.countBug(b);
50 }
5051 foo.report();
5152
5253 return this;
7273 int argCount = commandLine.parse(args, 0, 2, "Usage: " + TestingGround.class.getName() + " [options] [<xml results>] ");
7374
7475 SortedBugCollection bugCollection = new SortedBugCollection();
75 if (argCount < args.length)
76 if (argCount < args.length) {
7677 bugCollection.readXML(args[argCount++]);
77 else
78 } else {
7879 bugCollection.readXML(System.in);
80 }
7981 ArrayList<Bag<String>> live = new ArrayList<Bag<String>>();
8082 ArrayList<Bag<String>> died = new ArrayList<Bag<String>>();
8183 Bag<String> allBugs = new Bag<String>();
8789 int first = (int) b.getFirstVersion();
8890 int buried = (int) b.getLastVersion() + 1;
8991 int finish = buried;
90 if (finish == 0)
92 if (finish == 0) {
9193 finish = (int) bugCollection.getSequenceNumber();
94 }
9295
9396 String bugPattern = b.getBugPattern().getType();
9497 allBugs.add(bugPattern);
9598
96 for (int i = first; i <= finish; i++)
99 for (int i = first; i <= finish; i++) {
97100 live.get(i).add(bugPattern);
98 if (buried > 0)
101 }
102 if (buried > 0) {
99103 died.get(buried).add(bugPattern);
104 }
100105 }
101106 for (int i = 0; i < bugCollection.getSequenceNumber(); i++) {
102107 for (Map.Entry<String, Integer> e : died.get(i).entrySet()) {
116121 if (buried > 0) {
117122 int buriedCount = died.get(buried).getCount(bugPattern);
118123 int total = live.get(buried).getCount(bugPattern);
119 if (buriedCount > 30 && buriedCount * 3 > total)
124 if (buriedCount > 30 && buriedCount * 3 > total) {
120125 continue;
126 }
121127 }
122128 int survied = live.get((int) bugCollection.getSequenceNumber()).getCount(bugPattern);
123 if (survied == 0 && allBugs.getCount(bugPattern) > 100)
129 if (survied == 0 && allBugs.getCount(bugPattern) > 100) {
124130 continue;
131 }
125132
126133 results.add(b, false);
127134 }
4848 }
4949 }
5050
51 /**
52 * @param packageName
53 * @return
54 */
5551 private static String superpackage(String packageName) {
5652 int i = packageName.lastIndexOf('.');
57 if (i == -1)
53 if (i == -1) {
5854 return "";
55 }
5956 String p = packageName.substring(0, i);
6057 return p;
6158 }
6966 if (buggyPackages.contains(superpackage) || interiorPackages.contains(superpackage) || superpackage.length() == 0) {
7067 goodCodeCount.add(packageName, classes);
7168 goodCodeSize.add(packageName, loc);
72 if (superpackage.length() > 0)
69 if (superpackage.length() > 0) {
7370 interiorPackages.add(superpackage);
71 }
7472
75 } else
73 } else {
7674 cleanCode(superpackage, loc, classes);
75 }
7776 }
7877
7978 public void generateTreeMap(BugCollection bugCollection) {
80 for (PackageStats p : bugCollection.getProjectStats().getPackageStats())
79 for (PackageStats p : bugCollection.getProjectStats().getPackageStats()) {
8180 if (p.getTotalBugs() > 0) {
8281 buggyPackages.add(p.getPackageName());
8382 addInteriorPackages(p.getPackageName());
8483
8584 }
85 }
8686
87 for (PackageStats p : bugCollection.getProjectStats().getPackageStats())
87 for (PackageStats p : bugCollection.getProjectStats().getPackageStats()) {
8888 if (p.getTotalBugs() == 0) {
8989 cleanCode(p.getPackageName(), p.size(), p.getClassStats().size());
9090 }
91 }
9192 System.out.println("LOC\tTypes\tH\tHM\tDensity");
9293 System.out.println("INTEGER\tINTEGER\tINTEGER\tINTEGER\tFLOAT");
93 for (PackageStats p : bugCollection.getProjectStats().getPackageStats())
94 for (PackageStats p : bugCollection.getProjectStats().getPackageStats()) {
9495 if (p.getTotalBugs() > 0) {
9596 int high = p.getBugsAtPriority(Priorities.HIGH_PRIORITY);
9697 int normal = p.getBugsAtPriority(Priorities.NORMAL_PRIORITY);
9798 System.out.printf("%d\t%d\t%d\t%d\t%g\t\t%s", p.size(), p.getClassStats().size(), high, high + normal,
9899 (high + normal) * 1000.0 / p.size(), p.getPackageName().substring(11).replace('.', '\t'));
99 if (isInteriorPackage(p.getPackageName()))
100 if (isInteriorPackage(p.getPackageName())) {
100101 System.out.print("\t*");
102 }
101103 System.out.println();
102104 }
105 }
103106 for (Map.Entry<String, Integer> e : goodCodeSize.entrySet()) {
104107 System.out.printf("%d\t%d\t%d\t%d\t%g\t\t%s%n", e.getValue(), goodCodeCount.getCount(e.getKey()), 0, 0, 0.0, e
105108 .getKey().substring(11).replace('.', '\t'));
114117
115118 SortedBugCollection bugCollection = new SortedBugCollection();
116119 int argCount = 0;
117 if (argCount < args.length)
120 if (argCount < args.length) {
118121 bugCollection.readXML(args[argCount++]);
119 else
122 } else {
120123 bugCollection.readXML(System.in);
124 }
121125
122126 new TreemapVisualization().generateTreeMap(bugCollection);
123127
5656 */
5757 @Override
5858 protected void handleOption(String option, String optionExtraPart) throws IOException {
59 if (option.equals("-withMessages"))
59 if ("-withMessages".equals(option)) {
6060 withMessages = true;
61 else
61 } else {
6262 throw new IllegalArgumentException("Unknown option : " + option);
63 }
6364 }
6465
6566 /*
7172 */
7273 @Override
7374 protected void handleOptionWithArgument(String option, String argument) throws IOException {
74 if (option.equals("-output"))
75 if ("-output".equals(option)) {
7576 outputFile = argument;
76 else
77 } else {
7778 throw new IllegalArgumentException("Unknown option : " + option);
79 }
7880 }
7981
8082 }
8183
8284 static {
8385 DetectorFactoryCollection.instance(); // as a side effect, loads
84 // detector plugins
86 // detector plugins
8587 }
8688
8789 static public SortedBugCollection union(SortedBugCollection origCollection, SortedBugCollection newCollection) {
9294 static public void merge(HashSet<String> hashes, SortedBugCollection into, SortedBugCollection from) {
9395
9496 for (BugInstance bugInstance : from.getCollection()) {
95 if (hashes == null || hashes.add(bugInstance.getInstanceHash()))
97 if (hashes == null || hashes.add(bugInstance.getInstanceHash())) {
9698 into.add(bugInstance);
99 }
97100 }
98101 ProjectStats stats = into.getProjectStats();
99102 ProjectStats stats2 = from.getProjectStats();
103106 Project project2 = from.getProject();
104107 project.add(project2);
105108
106 for(AnalysisError error : from.getErrors())
109 for(AnalysisError error : from.getErrors()) {
107110 into.addError(error);
111 }
108112
109113 return;
110114 }
125129 SortedBugCollection more = new SortedBugCollection();
126130
127131 more.readXML(argv[i]);
128 if (results == null)
132 if (results == null) {
129133 results = more.createEmptyCollectionWithMetadata();
134 }
130135
131136 merge(hashes, results, more);
132137
143148 return;
144149 }
145150 results.setWithMessages(commandLine.withMessages);
146 if (commandLine.outputFile == null)
151 if (commandLine.outputFile == null) {
147152 results.writeXML(System.out);
148 else
153 } else {
149154 results.writeXML(commandLine.outputFile);
155 }
150156 }
151157
152158 }
153159
154 // vim:ts=3
5959
6060 static final int maxResurrection = SystemProperties.getInt("findbugs.maxResurrection", 90);
6161
62 /**
63 *
64 */
6562 private static final String USAGE = "Usage: " + Update.class.getName() + " [options] data1File data2File data3File ... ";
6663
6764 private final Map<BugInstance, BugInstance> mapFromNewToOldBug = new IdentityHashMap<BugInstance, BugInstance>();
108105 addSwitch("-useAnalysisTimes", "use analysis timestamp rather than code timestamp in history");
109106 addSwitch("-withMessages", "Add bug description");
110107 addOption("-onlyMostRecent", "number", "only use the last # input files");
111
112108 }
113109
114110 @Override
115111 protected void handleOption(String option, String optionExtraPart) throws IOException {
116 if (option.equals("-overrideRevisionNames")) {
117 if (optionExtraPart.length() == 0)
112 if ("-overrideRevisionNames".equals(option)) {
113 if (optionExtraPart.length() == 0) {
118114 overrideRevisionNames = true;
119 else
115 } else {
120116 overrideRevisionNames = Boolean.parseBoolean(optionExtraPart);
121 } else if (option.equals("-noPackageMoves")) {
122 if (optionExtraPart.length() == 0)
117 }
118 } else if ("-noPackageMoves".equals(option)) {
119 if (optionExtraPart.length() == 0) {
123120 noPackageMoves = true;
124 else
121 } else {
125122 noPackageMoves = Boolean.parseBoolean(optionExtraPart);
126 } else if (option.equals("-noResurrections")) {
127 if (optionExtraPart.length() == 0)
123 }
124 } else if ("-noResurrections".equals(option)) {
125 if (optionExtraPart.length() == 0) {
128126 noResurrections = true;
129 else
127 } else {
130128 noResurrections = Boolean.parseBoolean(optionExtraPart);
131 } else if (option.equals("-preciseMatch")) {
129 }
130 } else if ("-preciseMatch".equals(option)) {
132131 preciseMatch = true;
133 } else if (option.equals("-sloppyMatch")) {
132 } else if ("-sloppyMatch".equals(option)) {
134133 sloppyMatch = true;
135 } else if (option.equals("-precisePriorityMatch")) {
134 } else if ("-precisePriorityMatch".equals(option)) {
136135 versionInsensitiveBugComparator.setComparePriorities(true);
137136 fuzzyBugPatternMatcher.setComparePriorities(true);
138137 precisePriorityMatch = true;
139 } else if (option.equals("-quiet"))
138 } else if ("-quiet".equals(option)) {
140139 verbose = false;
141 else if (option.equals("-useAnalysisTimes"))
140 } else if ("-useAnalysisTimes".equals(option)) {
142141 useAnalysisTimes = true;
143 else if (option.equals("-withMessages"))
142 } else if ("-withMessages".equals(option)) {
144143 withMessages = true;
145 else
144 } else {
146145 throw new IllegalArgumentException("no option " + option);
146 }
147147
148148 }
149149
150150 @Override
151151 protected void handleOptionWithArgument(String option, String argument) throws IOException {
152 if (option.equals("-output"))
152 if ("-output".equals(option)) {
153153 outputFilename = argument;
154 else if (option.equals("-maxRank")) {
154 } else if ("-maxRank".equals(option)) {
155155 maxRank = Integer.parseInt(argument);
156 } else if (option.equals("-onlyMostRecent")) {
156 } else if ("-onlyMostRecent".equals(option)) {
157157 mostRecent = Integer.parseInt(argument);
158 } else
158 } else {
159159 throw new IllegalArgumentException("Can't handle option " + option);
160 }
160161
161162 }
162163
186187 matchBugs(versionInsensitiveBugComparator, baselineCollection, bugCollection);
187188 for (Iterator<BugInstance> i = bugCollection.getCollection().iterator(); i.hasNext();) {
188189 BugInstance bug = i.next();
189 if (matchedOldBugs.containsKey(bug))
190 if (matchedOldBugs.containsKey(bug)) {
190191 i.remove();
192 }
191193 }
192194
193195 }
195197 public BugCollection mergeCollections(BugCollection origCollection, BugCollection newCollection, boolean copyDeadBugs,
196198 boolean incrementalAnalysis) {
197199
198 for (BugInstance b : newCollection)
199 if (b.isDead())
200 for (BugInstance b : newCollection) {
201 if (b.isDead()) {
200202 throw new IllegalArgumentException("Can't merge bug collections if the newer collection contains dead bugs: " + b);
203 }
204 }
201205
202206 mapFromNewToOldBug.clear();
203207
227231 long currentSequence = origCollection.getSequenceNumber() + 1;
228232 resultCollection.setSequenceNumber(currentSequence);
229233
230 int oldBugs = 0;
234 // int oldBugs = 0;
231235 matchBugs(origCollection, newCollection);
232236
233 if (sloppyMatch)
237 if (sloppyMatch) {
234238 matchBugs(new SloppyBugComparator(), origCollection, newCollection);
235
236 int newlyDeadBugs = 0;
237 int persistantBugs = 0;
238 int addedBugs = 0;
239 int addedInNewCode = 0;
240 int deadBugInDeadCode = 0;
239 }
240
241 // int newlyDeadBugs = 0;
242 // int persistantBugs = 0;
243 // int addedBugs = 0;
244 // int addedInNewCode = 0;
245 // int deadBugInDeadCode = 0;
241246
242247 HashSet<String> analyzedSourceFiles = sourceFilesInCollection(newCollection);
243248 // Copy unmatched bugs
244 if (copyDeadBugs || incrementalAnalysis)
245 for (BugInstance bug : origCollection.getCollection())
246 if (!matchedOldBugs.containsKey(bug))
249 if (copyDeadBugs || incrementalAnalysis) {
250 for (BugInstance bug : origCollection.getCollection()) {
251 if (!matchedOldBugs.containsKey(bug)) {
247252 if (bug.isDead()) {
248 oldBugs++;
253 // oldBugs++;
249254 BugInstance newBug = (BugInstance) bug.clone();
250255 resultCollection.add(newBug, false);
251256 } else {
252 newlyDeadBugs++;
257 // newlyDeadBugs++;
253258
254259 BugInstance newBug = (BugInstance) bug.clone();
255260
259264 boolean fixed = sourceFile != null && analyzedSourceFiles.contains(sourceFile)
260265 || newCollection.getProjectStats().getClassStats(className) != null;
261266 if (fixed) {
262 if (!copyDeadBugs)
267 if (!copyDeadBugs) {
263268 continue;
269 }
264270 newBug.setRemovedByChangeOfPersistingClass(true);
265271 newBug.setLastVersion(lastSequence);
266272 } else {
267 deadBugInDeadCode++;
268 if (!incrementalAnalysis)
273 // deadBugInDeadCode++;
274 if (!incrementalAnalysis) {
269275 newBug.setLastVersion(lastSequence);
276 }
270277 }
271278
272 if (newBug.isDead() && newBug.getFirstVersion() > newBug.getLastVersion())
279 if (newBug.isDead() && newBug.getFirstVersion() > newBug.getLastVersion()) {
273280 throw new IllegalStateException("Illegal Version range: " + newBug.getFirstVersion() + ".."
274281 + newBug.getLastVersion());
282 }
275283 resultCollection.add(newBug, false);
276284 }
285 }
286 }
287 }
277288 // Copy matched bugs
278289 for (BugInstance bug : newCollection.getCollection()) {
279290 BugInstance newBug = (BugInstance) bug.clone();
284295 // handle getAnnotationText()/setAnnotationText() and
285296 // designation key
286297 BugDesignation designation = newBug.getUserDesignation();
287 if (designation != null)
298 if (designation != null) {
288299 designation.merge(origWarning.getUserDesignation());
289 else
300 }
301 else {
290302 newBug.setUserDesignation(origWarning.getUserDesignation()); // clone??
291
292 persistantBugs++;
303 }
304
305 // persistantBugs++;
293306 } else {
294307 newBug.setFirstVersion(lastSequence + 1);
295 addedBugs++;
308 // addedBugs++;
296309
297310 ClassAnnotation classBugFoundIn = bug.getPrimaryClass();
298311
302315 // System.out.println("added bug to existing code " +
303316 // newBug.getUniqueId() + " : " + newBug.getAbbrev() + " in
304317 // " + classBugFoundIn);
305 } else
306 addedInNewCode++;
307 }
308 if (newBug.isDead())
318 } else {
319 // addedInNewCode++;
320 }
321 }
322 if (newBug.isDead()) {
309323 throw new IllegalStateException("Illegal Version range: " + newBug.getFirstVersion() + ".."
310324 + newBug.getLastVersion());
325 }
311326 int oldSize = resultCollection.getCollection().size();
312327 resultCollection.add(newBug, false);
313328 int newSize = resultCollection.getCollection().size();
315330 System.out.println("Failed to add bug" + newBug.getMessage());
316331 }
317332 }
333 /*
318334 if (false && verbose) {
319335 System.out.println(origCollection.getCollection().size() + " orig bugs, " + newCollection.getCollection().size()
320336 + " new bugs");
323339 + " in new code, " + (addedBugs - addedInNewCode) + " added");
324340 System.out.println(resultCollection.getCollection().size() + " resulting bugs");
325341 }
342 */
326343 return resultCollection;
327344
328345 }
334351 BugRanker.trimToMaxRank(newCollection, maxRank);
335352 if (sloppyMatch) {
336353 TreeSet<BugInstance> sloppyUnique = new TreeSet<BugInstance>(new SloppyBugComparator());
337 for(Iterator<BugInstance> i = newCollection.iterator(); i.hasNext(); )
338 if (!sloppyUnique.add(i.next()))
354 for(Iterator<BugInstance> i = newCollection.iterator(); i.hasNext(); ) {
355 if (!sloppyUnique.add(i.next())) {
339356 i.remove();
340 }
341 }
342
357 }
358 }
359 }
360 }
361
362 /*
343363 private static int size(BugCollection b) {
344364 int count = 0;
345365 for (Iterator<BugInstance> i = b.iterator(); i.hasNext();) {
348368 }
349369 return count;
350370 }
351
352 /**
353 * @param origCollection
354 * @param newCollection
355371 */
372
356373 private void matchBugs(BugCollection origCollection, BugCollection newCollection) {
357374 matchBugs(SortedBugCollection.BugInstanceComparator.instance, origCollection, newCollection);
358375
361378
362379 matchBugs(versionInsensitiveBugComparator, origCollection, newCollection);
363380 matchBugs(versionInsensitiveBugComparator, origCollection, newCollection, MatchOldBugs.IF_CLASS_NOT_SEEN_UNTIL_NOW);
364 if (doMatchFixedBugs)
381 if (doMatchFixedBugs) {
365382 matchBugs(versionInsensitiveBugComparator, origCollection, newCollection, MatchOldBugs.ALWAYS);
366
367 if (!preciseMatch)
383 }
384
385 if (!preciseMatch) {
368386 matchBugs(fuzzyBugPatternMatcher, origCollection, newCollection);
387 }
369388
370389 if (!noPackageMoves) {
371390 VersionInsensitiveBugComparator movedBugComparator = new VersionInsensitiveBugComparator();
379398 matchBugs(movedBugComparator, origCollection, newCollection);
380399 }
381400 }
382 if (false)
401 /*
402 if (false) {
383403 System.out.println("Matched old bugs: " + matchedOldBugs.size());
404 }
405 */
384406
385407 }
386408 }
403425 UpdateCommandLine commandLine = new UpdateCommandLine();
404426 int argCount = commandLine.parse(args, 1, Integer.MAX_VALUE, USAGE);
405427
406 if (commandLine.outputFilename == null)
428 if (commandLine.outputFilename == null) {
407429 verbose = false;
430 }
408431 if (mostRecent > 0) {
409432 argCount = Math.max(argCount, args.length - mostRecent);
410433 }
416439 commonPrefix = Math.min(commonPrefix, lengthCommonPrefix(firstPathParts, getFilePathParts(args[i])));
417440 }
418441
419 String origFilename = args[argCount++];
442 String origFilename = args[argCount++];
420443 BugCollection origCollection;
421444 origCollection = new SortedBugCollection();
422 if (verbose)
445 if (verbose) {
423446 System.out.println("Starting with " + origFilename);
424
425 while (true)
447 }
448
449 while (true) {
426450 try {
427451 while (true) {
428452 File f = new File(origFilename);
429 if (f.length() > 0)
453 if (f.length() > 0) {
430454 break;
431 if (verbose)
455 }
456 if (verbose) {
432457 System.out.println("Empty input file: " + f);
458 }
433459 origFilename = args[argCount++];
434460 }
435461 origCollection.readXML(origFilename);
441467 }
442468 origFilename = args[argCount++];
443469 }
470 }
444471
445472 if (commandLine.overrideRevisionNames || origCollection.getReleaseName() == null
446473 || origCollection.getReleaseName().length() == 0) {
457484 }
458485
459486 origCollection.setReleaseName(firstPathParts[commonPrefix]);
460 if (useAnalysisTimes)
487 if (useAnalysisTimes) {
461488 origCollection.setTimestamp(origCollection.getAnalysisTimestamp());
462 }
463
464 for (BugInstance bug : origCollection.getCollection())
465 if (bug.getLastVersion() >= 0 && bug.getFirstVersion() > bug.getLastVersion())
489 }
490 }
491
492 for (BugInstance bug : origCollection.getCollection()) {
493 if (bug.getLastVersion() >= 0 && bug.getFirstVersion() > bug.getLastVersion()) {
466494 throw new IllegalStateException("Illegal Version range: " + bug.getFirstVersion() + ".." + bug.getLastVersion());
495 }
496 }
467497
468498 discardUnwantedBugs(origCollection);
469499
472502 BugCollection newCollection = new SortedBugCollection();
473503
474504 String newFilename = args[argCount++];
475 if (verbose)
505 if (verbose) {
476506 System.out.println("Merging " + newFilename);
507 }
477508 try {
478509 File f = new File(newFilename);
479510 if (f.length() == 0) {
480 if (verbose)
511 if (verbose) {
481512 System.out.println("Empty input file: " + f);
513 }
482514 continue;
483515 }
484516 newCollection.readXML(newFilename);
485517
486518 if (commandLine.overrideRevisionNames || newCollection.getReleaseName() == null
487 || newCollection.getReleaseName().length() == 0)
519 || newCollection.getReleaseName().length() == 0) {
488520 newCollection.setReleaseName(getFilePathParts(newFilename)[commonPrefix]);
489 if (useAnalysisTimes)
521 }
522 if (useAnalysisTimes) {
490523 newCollection.setTimestamp(newCollection.getAnalysisTimestamp());
524 }
491525 discardUnwantedBugs(newCollection);
492526
493527 origCollection = mergeCollections(origCollection, newCollection, true, false);
494528 } catch (IOException e) {
495529 IOException e2 = new IOException("Error parsing " + newFilename);
496530 e2.initCause(e);
497 if (verbose)
531 if (verbose) {
498532 e2.printStackTrace();
533 }
499534 throw e2;
500535 } catch (DocumentException e) {
501536 DocumentException e2 = new DocumentException("Error parsing " + newFilename);
502537 e2.initCause(e);
503 if (verbose)
538 if (verbose) {
504539 e2.printStackTrace();
540 }
505541 throw e2;
506542 }
507543 }
508
509 if (false)
544 /*
545 if (false) {
510546 for (Iterator<BugInstance> i = origCollection.iterator(); i.hasNext();) {
511 if (!resurrected.contains(i.next().getInstanceKey()))
547 if (!resurrected.contains(i.next().getInstanceKey())) {
512548 i.remove();
513 }
549 }
550 }
551 }
552 */
514553 origCollection.setWithMessages(commandLine.withMessages);
515554 if (commandLine.outputFilename != null) {
516 if (verbose)
555 if (verbose) {
517556 System.out.println("Writing " + commandLine.outputFilename);
557 }
518558 origCollection.writeXML(commandLine.outputFilename);
519 } else
559 } else {
520560 origCollection.writeXML(System.out);
561 }
521562
522563 }
523564
524565 private static int lengthCommonPrefix(String[] string, String[] string2) {
525566 int maxLength = Math.min(string.length, string2.length);
526 for (int result = 0; result < maxLength; result++)
527 if (!string[result].equals(string2[result]))
567 for (int result = 0; result < maxLength; result++) {
568 if (!string[result].equals(string2[result])) {
528569 return result;
570 }
571 }
529572 return maxLength;
530573 }
531574
562605
563606 TreeMap<BugInstance, LinkedList<BugInstance>> set = new TreeMap<BugInstance, LinkedList<BugInstance>>(
564607 bugInstanceComparator);
565 int oldBugs = 0;
566 int newBugs = 0;
567 int matchedBugs = 0;
568 for (BugInstance bug : origCollection.getCollection())
608 // int oldBugs = 0;
609 // int newBugs = 0;
610 // int matchedBugs = 0;
611 for (BugInstance bug : origCollection.getCollection()) {
569612 if (!matchedOldBugs.containsKey(bug)) {
570613 if (matchOld.match(bug)) {
571 oldBugs++;
614 // oldBugs++;
572615 LinkedList<BugInstance> q = set.get(bug);
573616 if (q == null) {
574617 q = new LinkedList<BugInstance>();
578621 }
579622
580623 }
624 }
581625 long newVersion = origCollection.getCurrentAppVersion().getSequenceNumber() + 1;
582 for (BugInstance bug : newCollection.getCollection())
626 for (BugInstance bug : newCollection.getCollection()) {
583627 if (!mapFromNewToOldBug.containsKey(bug)) {
584 newBugs++;
628 // newBugs++;
585629 LinkedList<BugInstance> q = set.get(bug);
586 if (q == null)
630 if (q == null) {
587631 continue;
632 }
588633 for (Iterator<BugInstance> i = q.iterator(); i.hasNext();) {
589634 BugInstance matchedBug = i.next();
590635
591636 if (matchedBug.isDead()) {
592637 if (noResurrections || matchedBug.isRemovedByChangeOfPersistingClass()
593 && newVersion - matchedBug.getLastVersion() > maxResurrection)
638 && newVersion - matchedBug.getLastVersion() > maxResurrection) {
594639 continue;
640 }
595641 resurrected.add(bug.getInstanceKey());
596642 // System.out.println("in version " +
597643 // newCollection.getReleaseName());
599645 // bug.getMessageWithoutPrefix());
600646 }
601647
602 matchedBugs++;
648 // matchedBugs++;
603649 mapFromNewToOldBug.put(bug, matchedBug);
604650 matchedOldBugs.put(matchedBug, null);
605651 i.remove();
606 if (q.isEmpty())
652 if (q.isEmpty()) {
607653 set.remove(bug);
654 }
608655 break;
609656 }
610657 }
658 }
611659 }
612660
613661 }
2727
2828 /**
2929 * XMLOutput class to build all or part of a dom4j tree.
30 *
30 *
3131 * @see XMLOutput
3232 * @author David Hovemeyer
3333 */
3434 public class Dom4JXMLOutput implements XMLOutput {
35 private LinkedList<Branch> stack;
35 private final LinkedList<Branch> stack;
3636
3737 /**
3838 * Constructor.
39 *
39 *
4040 * @param topLevel
4141 * the Document or Element that is the root of the tree to be
4242 * built
4646 stack.addLast(topLevel);
4747 }
4848
49 @Override
4950 public void beginDocument() {
5051 }
5152
53 @Override
5254 public void openTag(String tagName) {
5355 Branch top = stack.getLast();
5456 Element element = top.addElement(tagName);
5557 stack.addLast(element);
5658 }
5759
60 @Override
5861 public void openTag(String tagName, XMLAttributeList attributeList) {
5962 Branch top = stack.getLast();
6063 Element element = top.addElement(tagName);
6669 }
6770 }
6871
72 @Override
6973 public void openCloseTag(String tagName) {
7074 openTag(tagName);
7175 closeTag(tagName);
7276 }
7377
78 @Override
7479 public void openCloseTag(String tagName, XMLAttributeList attributeList) {
7580 openTag(tagName, attributeList);
7681 closeTag(tagName);
7782 }
7883
84 @Override
7985 public void startTag(String tagName) {
8086 Branch top = stack.getLast();
8187 Element element = top.addElement(tagName);
8288 stack.addLast(element);
8389 }
8490
91 @Override
8592 public void addAttribute(String name, String value) {
8693 Element element = (Element) stack.getLast();
8794 element.addAttribute(name, value);
8895 }
8996
97 @Override
9098 public void stopTag(boolean close) {
9199 if (close) {
92100 closeTag(null);
93101 }
94102 }
95103
104 @Override
96105 public void closeTag(String tagName) {
97106 stack.removeLast();
98107 }
99108
109 @Override
100110 public void writeText(String text) {
101111 Element top = (Element) stack.getLast();
102112 top.addText(text);
103113 }
104114
115 @Override
105116 public void writeCDATA(String cdata) {
106117 Element top = (Element) stack.getLast();
107118 top.addCDATA(cdata);
110121 /**
111122 * Add a list of Strings to document as elements with given tag name to the
112123 * tree.
113 *
124 *
114125 * @param tagName
115126 * the tag name
116127 * @param listValues
126137
127138 /**
128139 * Add given object to the tree.
129 *
140 *
130141 * @param obj
131142 * the object
132143 */
140151
141152 /**
142153 * Add a Collection of XMLWriteable objects to the tree.
143 *
154 *
144155 * @param collection
145156 * Collection of XMLWriteable objects
146157 */
150161 }
151162 }
152163
164 @Override
153165 public void finish() {
154166 }
155167 }
156168
157 // vim:ts=4
2424
2525 /**
2626 * Map of metacharacters that need to be escaped, and what to replace them with.
27 *
27 *
2828 * @see QuoteMetaCharacters
2929 * @author David Hovemeyer
3030 */
3131 public class MetaCharacterMap {
32 private BitSet metaCharacterSet;
32 private final BitSet metaCharacterSet;
3333
34 private Map<String, String> replacementMap;
34 private final Map<String, String> replacementMap;
3535
3636 /**
3737 * Constructor. Creates an empty object.
4343
4444 /**
4545 * Add a metacharacter and its replacement.
46 *
46 *
4747 * @param meta
4848 * the metacharacter
4949 * @param replacement
6363
6464 /**
6565 * Get the replacement for a metacharacter.
66 *
66 *
6767 * @param c
6868 * a String containing the metacharacter
6969 */
7272 }
7373 }
7474
75 // vim:ts=4
2727 import javax.annotation.WillCloseWhenClosed;
2828
2929 import edu.umd.cs.findbugs.annotations.DischargesObligation;
30 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
3031 import edu.umd.cs.findbugs.util.Strings;
3132
3233 /**
3839 private static final String OPENING = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
3940
4041 private static String getStylesheetCode(String stylesheet) {
41 if (stylesheet == null)
42 if (stylesheet == null) {
4243 return "";
44 }
4345 return "<?xml-stylesheet type=\"text/xsl\" href=\"" + stylesheet + "\"?>\n";
4446 }
4547
46 private Writer out;
48 private final Writer out;
4749
4850 private int nestingLevel;
4951
5052 private boolean newLine;
5153
52 private String stylesheet;
54 private final String stylesheet;
5355
5456 /**
5557 * Constructor.
5759 * @param os
5860 * OutputStream to write XML output to
5961 */
62 @SuppressFBWarnings("OBL_UNSATISFIED_OBLIGATION")
6063 public OutputStreamXMLOutput(@WillCloseWhenClosed OutputStream os) {
6164 this(os, null);
6265 }
6366 /**
6467 * Constructor.
6568 *
66 * @param os
67 * OutputStream to write XML output to
68 */
69 * @param writer
70 * Writer to write XML output to
71 */
72 @SuppressFBWarnings("OBL_UNSATISFIED_OBLIGATION")
6973 public OutputStreamXMLOutput(@WillCloseWhenClosed Writer writer) {
7074 this(writer, null);
7175 }
8589 }
8690
8791 /*
88 * @param os
89 * Writer to write XML output to
90 * @param stylesheet
91 * name of stylesheet
92 */
93 public OutputStreamXMLOutput(@WillCloseWhenClosed Writer writer, String stylesheet) {
94 this.out = writer;
95 this.nestingLevel = 0;
96 this.newLine = true;
97 this.stylesheet = stylesheet;
98 }
92 * @param os
93 * Writer to write XML output to
94 * @param stylesheet
95 * name of stylesheet
96 */
97 public OutputStreamXMLOutput(@WillCloseWhenClosed Writer writer, String stylesheet) {
98 this.out = writer;
99 this.nestingLevel = 0;
100 this.newLine = true;
101 this.stylesheet = stylesheet;
102 }
103 @Override
99104 public void beginDocument() throws IOException {
100105 out.write(OPENING);
101106 out.write(getStylesheetCode(stylesheet));
103108 newLine = true;
104109 }
105110
111 @Override
106112 public void openTag(String tagName) throws IOException {
107113 emitTag(tagName, false);
108114 }
109115
116 @Override
110117 public void openTag(String tagName, XMLAttributeList attributeList) throws IOException {
111118 emitTag(tagName, attributeList.toString(), false);
112119 }
113120
121 @Override
114122 public void openCloseTag(String tagName) throws IOException {
115123 emitTag(tagName, true);
116124 }
117125
126 @Override
118127 public void openCloseTag(String tagName, XMLAttributeList attributeList) throws IOException {
119128 emitTag(tagName, attributeList.toString(), true);
120129 }
121130
131 @Override
122132 public void startTag(String tagName) throws IOException {
123133 indent();
124134 ++nestingLevel;
125135 out.write("<" + tagName);
126136 }
127137
138 @Override
128139 public void addAttribute(String name, String value) throws IOException {
129140 out.write(' ');
130141 out.write(name);
134145 out.write('"');
135146 }
136147
148 @Override
137149 public void stopTag(boolean close) throws IOException {
138150 if (close) {
139151 out.write("/>\n");
160172 stopTag(close);
161173 }
162174
175 @Override
163176 public void closeTag(String tagName) throws IOException {
164177 --nestingLevel;
165 if (newLine)
178 if (newLine) {
166179 indent();
180 }
167181 out.write("</" + tagName + ">\n");
168182 newLine = true;
169183 }
170184
185 @Override
171186 public void writeText(String text) throws IOException {
172187 out.write(Strings.escapeXml(text));
173188 }
174189
190 @Override
175191 public void writeCDATA(String cdata) throws IOException {
176192 // FIXME: We just trust fate that the characters being written
177193 // don't contain the string "]]>"
185201 public void flush() throws IOException {
186202 out.flush();
187203 }
204 @Override
188205 @DischargesObligation
189206 public void finish() throws IOException {
190207 out.close();
191208 }
192209
193210 private void indent() throws IOException {
194 if (!newLine)
211 if (!newLine) {
195212 out.write("\n");
213 }
196214 for (int i = 0; i < nestingLevel; ++i) {
197215 out.write(" ");
198216 }
199217 }
200218 }
201219
202 // vim:ts=4
2525
2626 /**
2727 * Quote metacharacters in a String.
28 *
28 *
2929 * @see MetaCharacterMap
3030 * @author David Hovemeyer
3131 */
3232 public abstract class QuoteMetaCharacters {
33 private String text;
33 private final String text;
3434
35 private MetaCharacterMap map;
35 private final MetaCharacterMap map;
3636
3737 /**
3838 * Constructor.
39 *
39 *
4040 * @param text
4141 * the text in which we want to quote metacharacters
4242 * @param map
4343 * the MetaCharacterMap
4444 */
4545 public QuoteMetaCharacters(@Nonnull String text, @Nonnull MetaCharacterMap map) {
46 if (text == null)
46 if (text == null) {
4747 throw new NullPointerException("text must be nonnull");
48 if (map == null)
48 }
49 if (map == null) {
4950 throw new NullPointerException("map must be nonnull");
51 }
5052 this.text = text;
5153 this.map = map;
5254 }
7274 /**
7375 * Downcall method to emit literal text, in which any occurrences of the
7476 * metacharacters are quoted.
75 *
77 *
7678 * @param s
7779 * the literal text to emit
7880 */
8183 private int findNextMeta(String s, int start) {
8284 for (int i = start; i < s.length(); ++i) {
8385 char c = s.charAt(i);
84 if (map.isMeta(c))
86 if (map.isMeta(c)) {
8587 return i;
88 }
8689 }
8790 return -1;
8891 }
8992 }
9093
91 // vim:ts=4
2929
3030 /**
3131 * Helper class to format attributes in an XML tag.
32 *
32 *
3333 * @author David Hovemeyer
3434 */
3535 public class XMLAttributeList {
3636 public static class NameValuePair {
37 private String name;
37 private final String name;
3838
39 private String value;
39 private final String value;
4040
4141 public NameValuePair(String name, String value) {
4242 this.name = name;
5353 }
5454
5555 // Fields
56 private List<NameValuePair> nameValuePairList;
56 private final List<NameValuePair> nameValuePairList;
5757
5858 /**
5959 * Constructor. Creates an empty object.
6464
6565 /**
6666 * Add a single attribute name and value.
67 *
67 *
6868 * @param name
6969 * the attribute name
7070 * @param value
7272 * @return this object (so calls to addAttribute() can be chained)
7373 */
7474 public XMLAttributeList addAttribute(@Nonnull String name, @Nonnull String value) {
75 if (name == null)
75 if (name == null) {
7676 throw new NullPointerException("name must be nonnull");
77 if (value == null)
77 }
78 if (value == null) {
7879 throw new NullPointerException("value must be nonnull");
80 }
7981 nameValuePairList.add(new NameValuePair(name, value));
8082 return this;
8183 }
8284
8385 /**
8486 * Add a single attribute name and value.
85 *
87 *
8688 * @param name
8789 * the attribute name
8890 * @param value
9092 * @return this object (so calls to addAttribute() can be chained)
9193 */
9294 public XMLAttributeList addOptionalAttribute(@Nonnull String name, @CheckForNull String value) {
93 if (value == null)
95 if (value == null) {
9496 return this;
97 }
9598 return addAttribute(name, value);
9699 }
97100
122125
123126 /**
124127 * Return a properly quoted form for an attribute value.
125 *
128 *
126129 * @param rawValue
127130 * the raw value of the attribute
128131 * @return a properly quoted representation of the value
132135 }
133136 }
134137
135 // vim:ts=4
2626 /**
2727 * Interface to generate an XML document in some form. E.g., writing it to a
2828 * stream, generating SAX events, etc.
29 *
29 *
3030 * @author David Hovemeyer
3131 */
3232 @CleanupObligation
3838
3939 /**
4040 * Open a tag with given name.
41 *
41 *
4242 * @param tagName
4343 * the tag name
4444 */
4646
4747 /**
4848 * Open a tag with given name and given attributes.
49 *
49 *
5050 * @param tagName
5151 * the tag name
5252 * @param attributeList
5757 /**
5858 * Start a tag, with the intention of adding attributes. Must be followed by
5959 * stopTag after zero or more addAttribute calls.
60 *
60 *
6161 * @param tagName
6262 * the tag name
6363 */
6565
6666 /**
6767 * Add an attribute to a started tag. Must follow a call to startTag.
68 *
68 *
6969 * @param name
7070 * the attribute name.
7171 * @param value
7575
7676 /**
7777 * End a started tag. Must follow a call to startTag.
78 *
78 *
7979 * @param close
8080 * true if the element has no content.
8181 */
8383
8484 /**
8585 * Open and close tag with given name.
86 *
86 *
8787 * @param tagName
8888 * the tag name
8989 */
9191
9292 /**
9393 * Open and close tag with given name and given attributes.
94 *
94 *
9595 * @param tagName
9696 * the tag name
9797 * @param attributeList
101101
102102 /**
103103 * Close tag with given name.
104 *
104 *
105105 * @param tagName
106106 * the tag name
107107 */
110110 /**
111111 * Write text to the XML document. XML metacharacters are automatically
112112 * escaped.
113 *
113 *
114114 * @param text
115115 * the text to write
116116 */
119119 /**
120120 * Write a CDATA section to the XML document. The characters are not escaped
121121 * in any way.
122 *
122 *
123123 * @param cdata
124124 * the character data to write
125125 */
135135 public void finish() throws IOException;
136136 }
137137
138 // vim:ts=4
2525
2626 /**
2727 * Utility routines for writing to XMLOutput.
28 *
28 *
2929 * @see XMLOutput
3030 * @author David Hovemeyer
3131 */
3232 public abstract class XMLOutputUtil {
3333 /**
3434 * Write a list of Strings to document as elements with given tag name.
35 *
35 *
3636 * @param xmlOutput
3737 * the XMLOutput object to write to
3838 * @param tagName
4646
4747 /**
4848 * Write a list of Strings to document as elements with given tag name.
49 *
49 *
5050 * @param xmlOutput
5151 * the XMLOutput object to write to
5252 * @param tagName
6565
6666 /**
6767 * Write a list of Strings to document as elements with given tag name.
68 *
68 *
6969 * @param xmlOutput
7070 * the XMLOutput object to write to
7171 * @param tagName
7474 * Collection of String values to write
7575 */
7676 public static void writeFileList(XMLOutput xmlOutput, String tagName, Iterable<File> listValues) throws IOException {
77 if (listValues != null)
77 if (listValues != null) {
7878 writeFileList(xmlOutput, tagName, listValues.iterator());
79 }
7980 }
8081
8182 /**
8283 * Write a list of Strings to document as elements with given tag name.
83 *
84 *
8485 * @param xmlOutput
8586 * the XMLOutput object to write to
8687 * @param tagName
9899
99100 /**
100101 * Write a Collection of XMLWriteable objects.
101 *
102 *
102103 * @param xmlOutput
103104 * the XMLOutput object to write to
104105 * @param collection
111112 }
112113 }
113114
114 // vim:ts=4
00 /*
11 * FindBugs - Find Bugs in Java programs
22 * Copyright (C) 2003-2008 University of Maryland
3 *
3 *
44 * This library is free software; you can redistribute it and/or
55 * modify it under the terms of the GNU Lesser General Public
66 * License as published by the Free Software Foundation; either
77 * version 2.1 of the License, or (at your option) any later version.
8 *
8 *
99 * This library is distributed in the hope that it will be useful,
1010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1111 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1212 * Lesser General Public License for more details.
13 *
13 *
1414 * You should have received a copy of the GNU Lesser General Public
1515 * License along with this library; if not, write to the Free Software
1616 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2727 */
2828 public class XMLUtil {
2929
30 /**
31 * @param node
32 * @param arg0
33 * @return
34 */
3530 @SuppressWarnings("unchecked")
3631 public static <T> List<T> selectNodes(Node node, String arg0) {
3732 return node.selectNodes(arg0);
2222
2323 /**
2424 * Interface indicating that an object can write itself to an XML document.
25 *
25 *
2626 * @see XMLOutput
2727 * @author David Hovemeyer
2828 */
2929 public interface XMLWriteable {
3030 /**
3131 * Write this object to given XMLOutput.
32 *
32 *
3333 * @param xmlOutput
3434 * the XMLOutput for the document
3535 */
3636 public void writeXML(XMLOutput xmlOutput) throws IOException;
3737 }
3838
39 // vim:ts=4
3131 /**
3232 * Find nodes in a dom4j tree that match a particular XPath expression. The
3333 * main() driver prints out information about matching nodes in an XML document.
34 *
34 *
3535 * <p>
3636 * For example, to find the list of non-disabled detectors in a FindBugs plugin
3737 * descriptor, you can use the expression <blockquote>
3838 * <code>/FindbugsPlugin/Detector[boolean(@disabled)=false()]/@class</code>
3939 * </blockquote>
40 *
40 *
4141 * @author David Hovemeyer
4242 */
4343 public abstract class XPathFind {
44 private Document document;
44 private final Document document;
4545
4646 public XPathFind(Document document) {
4747 this.document = document;
9393 }
9494 }
9595
96 // vim:ts=4
9595
9696 SortedBugCollection bc = new SortedBugCollection();
9797 bc.setWithMessages(false);
98
98
9999
100100 String output = writeXML(inst, bc);
101101 System.err.println(output);
154154 }
155155
156156 private void checkPropertyIterator(Iterator<BugProperty> iter, String[] names, String[] values) {
157 if (names.length != values.length)
157 if (names.length != values.length) {
158158 throw new IllegalArgumentException();
159 }
159160 for (int i = 0; i < names.length; ++i) {
160161 Assert.assertTrue(iter.hasNext());
161162 String name = names[i];
173174 private void removeThroughIterator(Iterator<BugProperty> iter, String name) {
174175 while (iter.hasNext()) {
175176 BugProperty prop = iter.next();
176 if (prop.getName().equals(name))
177 if (prop.getName().equals(name)) {
177178 iter.remove();
179 }
178180 }
179181 }
180182 }
2626
2727 import org.junit.After;
2828 import org.junit.Assert;
29 import org.junit.Assume;
2930 import org.junit.Before;
3031 import org.junit.Test;
3132
5758
5859 private File findbugsTestCases;
5960
60 public File getFindbugsTestCases() throws IOException {
61 if (findbugsTestCases != null)
61 /** detectors which are disabled by default but which must be used in test */
62 private final String[] enabledDetectors = {"CheckExpectedWarnings","InefficientMemberAccess","EmptyZipFileEntry"};
63
64 public File getFindbugsTestCases() {
65 if (findbugsTestCases != null) {
6266 return findbugsTestCases;
67 }
6368 File f = new File(SystemProperties.getProperty("findbugsTestCases.home", "../findbugsTestCases"));
64 if (f.exists() && f.isDirectory() && f.canRead()) {
65 findbugsTestCases = f;
66 return f;
67 }
68 throw new IOException("FindBugs test cases not available at " + f.getCanonicalPath());
69 }
70
71 public File getFindbugsTestCasesFile(String path) throws IOException {
69 Assume.assumeTrue(f.exists());
70 Assume.assumeTrue(f.isDirectory());
71 Assume.assumeTrue(f.canRead());
72
73 findbugsTestCases = f;
74 return f;
75 }
76
77 public File getFindbugsTestCasesFile(String path) {
7278 File f = new File(getFindbugsTestCases(), path);
73 if (f.exists() && f.canRead())
74 return f;
75 throw new IOException("FindBugs test cases file " + path + " not available at " + f.getCanonicalPath());
79 Assume.assumeTrue(f.exists());
80 Assume.assumeTrue(f.canRead());
81
82 return f;
7683 }
7784
7885 @Before
125132 }
126133 }
127134
128 if (!unexpectedBugs.isEmpty())
135 if (!unexpectedBugs.isEmpty()) {
129136 Assert.fail("Unexpected bugs (" + unexpectedBugs.size() + "):" + getBugsLocations(unexpectedBugs));
137 }
130138 }
131139
132140 /**
185193
186194 engine.setBugReporter(this.bugReporter);
187195 UserPreferences preferences = UserPreferences.createDefaultUserPreferences();
188 DetectorFactory checkExpectedWarnings = detectorFactoryCollection.getFactory("CheckExpectedWarnings");
189 preferences.enableDetector(checkExpectedWarnings, true);
196 for (String factory : enabledDetectors) {
197 DetectorFactory detFactory = detectorFactoryCollection.getFactory(factory);
198 preferences.enableDetector(detFactory, true);
199 }
190200 preferences.getFilterSettings().clearAllCategories();
191201 this.engine.setUserPreferences(preferences);
192202
200210
201211 project.addAuxClasspathEntry("lib/junit.jar");
202212 File lib = getFindbugsTestCasesFile("lib");
203 for(File f : lib.listFiles()) {
204 String path = f.getPath();
205 if (f.canRead() && path.endsWith(".jar")) {
213 for(File f : lib.listFiles()) {
214 String path = f.getPath();
215 if (f.canRead() && path.endsWith(".jar")) {
206216 project.addAuxClasspathEntry(path);
207217 }
208 }
218 }
209219
210220 }
211221 }
4141 * Data of an empty class in the default package called "Empty".
4242 */
4343 public static final byte[] EMPTY_CLASS_DATA = { (byte) 0xca, (byte) 0xfe, (byte) 0xba, (byte) 0xbe, (byte) 0x00, (byte) 0x00,
44 (byte) 0x00, (byte) 0x32, (byte) 0x00, (byte) 0x0d, (byte) 0x0a, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x0a,
45 (byte) 0x07, (byte) 0x00, (byte) 0x0b, (byte) 0x07, (byte) 0x00, (byte) 0x0c, (byte) 0x01, (byte) 0x00, (byte) 0x06,
46 (byte) 0x3c, (byte) 0x69, (byte) 0x6e, (byte) 0x69, (byte) 0x74, (byte) 0x3e, (byte) 0x01, (byte) 0x00, (byte) 0x03,
47 (byte) 0x28, (byte) 0x29, (byte) 0x56, (byte) 0x01, (byte) 0x00, (byte) 0x04, (byte) 0x43, (byte) 0x6f, (byte) 0x64,
48 (byte) 0x65, (byte) 0x01, (byte) 0x00, (byte) 0x0f, (byte) 0x4c, (byte) 0x69, (byte) 0x6e, (byte) 0x65, (byte) 0x4e,
49 (byte) 0x75, (byte) 0x6d, (byte) 0x62, (byte) 0x65, (byte) 0x72, (byte) 0x54, (byte) 0x61, (byte) 0x62, (byte) 0x6c,
50 (byte) 0x65, (byte) 0x01, (byte) 0x00, (byte) 0x0a, (byte) 0x53, (byte) 0x6f, (byte) 0x75, (byte) 0x72, (byte) 0x63,
51 (byte) 0x65, (byte) 0x46, (byte) 0x69, (byte) 0x6c, (byte) 0x65, (byte) 0x01, (byte) 0x00, (byte) 0x0a, (byte) 0x45,
52 (byte) 0x6d, (byte) 0x70, (byte) 0x74, (byte) 0x79, (byte) 0x2e, (byte) 0x6a, (byte) 0x61, (byte) 0x76, (byte) 0x61,
53 (byte) 0x0c, (byte) 0x00, (byte) 0x04, (byte) 0x00, (byte) 0x05, (byte) 0x01, (byte) 0x00, (byte) 0x05, (byte) 0x45,
54 (byte) 0x6d, (byte) 0x70, (byte) 0x74, (byte) 0x79, (byte) 0x01, (byte) 0x00, (byte) 0x10, (byte) 0x6a, (byte) 0x61,
55 (byte) 0x76, (byte) 0x61, (byte) 0x2f, (byte) 0x6c, (byte) 0x61, (byte) 0x6e, (byte) 0x67, (byte) 0x2f, (byte) 0x4f,
56 (byte) 0x62, (byte) 0x6a, (byte) 0x65, (byte) 0x63, (byte) 0x74, (byte) 0x00, (byte) 0x21, (byte) 0x00, (byte) 0x02,
57 (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00,
58 (byte) 0x01, (byte) 0x00, (byte) 0x04, (byte) 0x00, (byte) 0x05, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x06,
59 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x1d, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x00,
60 (byte) 0x00, (byte) 0x00, (byte) 0x05, (byte) 0x2a, (byte) 0xb7, (byte) 0x00, (byte) 0x01, (byte) 0xb1, (byte) 0x00,
61 (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x06,
62 (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x00,
63 (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x09, };
44 (byte) 0x00, (byte) 0x32, (byte) 0x00, (byte) 0x0d, (byte) 0x0a, (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x0a,
45 (byte) 0x07, (byte) 0x00, (byte) 0x0b, (byte) 0x07, (byte) 0x00, (byte) 0x0c, (byte) 0x01, (byte) 0x00, (byte) 0x06,
46 (byte) 0x3c, (byte) 0x69, (byte) 0x6e, (byte) 0x69, (byte) 0x74, (byte) 0x3e, (byte) 0x01, (byte) 0x00, (byte) 0x03,
47 (byte) 0x28, (byte) 0x29, (byte) 0x56, (byte) 0x01, (byte) 0x00, (byte) 0x04, (byte) 0x43, (byte) 0x6f, (byte) 0x64,
48 (byte) 0x65, (byte) 0x01, (byte) 0x00, (byte) 0x0f, (byte) 0x4c, (byte) 0x69, (byte) 0x6e, (byte) 0x65, (byte) 0x4e,
49 (byte) 0x75, (byte) 0x6d, (byte) 0x62, (byte) 0x65, (byte) 0x72, (byte) 0x54, (byte) 0x61, (byte) 0x62, (byte) 0x6c,
50 (byte) 0x65, (byte) 0x01, (byte) 0x00, (byte) 0x0a, (byte) 0x53, (byte) 0x6f, (byte) 0x75, (byte) 0x72, (byte) 0x63,
51 (byte) 0x65, (byte) 0x46, (byte) 0x69, (byte) 0x6c, (byte) 0x65, (byte) 0x01, (byte) 0x00, (byte) 0x0a, (byte) 0x45,
52 (byte) 0x6d, (byte) 0x70, (byte) 0x74, (byte) 0x79, (byte) 0x2e, (byte) 0x6a, (byte) 0x61, (byte) 0x76, (byte) 0x61,
53 (byte) 0x0c, (byte) 0x00, (byte) 0x04, (byte) 0x00, (byte) 0x05, (byte) 0x01, (byte) 0x00, (byte) 0x05, (byte) 0x45,
54 (byte) 0x6d, (byte) 0x70, (byte) 0x74, (byte) 0x79, (byte) 0x01, (byte) 0x00, (byte) 0x10, (byte) 0x6a, (byte) 0x61,
55 (byte) 0x76, (byte) 0x61, (byte) 0x2f, (byte) 0x6c, (byte) 0x61, (byte) 0x6e, (byte) 0x67, (byte) 0x2f, (byte) 0x4f,
56 (byte) 0x62, (byte) 0x6a, (byte) 0x65, (byte) 0x63, (byte) 0x74, (byte) 0x00, (byte) 0x21, (byte) 0x00, (byte) 0x02,
57 (byte) 0x00, (byte) 0x03, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00,
58 (byte) 0x01, (byte) 0x00, (byte) 0x04, (byte) 0x00, (byte) 0x05, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x06,
59 (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x1d, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x00,
60 (byte) 0x00, (byte) 0x00, (byte) 0x05, (byte) 0x2a, (byte) 0xb7, (byte) 0x00, (byte) 0x01, (byte) 0xb1, (byte) 0x00,
61 (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x07, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x06,
62 (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x01, (byte) 0x00, (byte) 0x01, (byte) 0x00,
63 (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x02, (byte) 0x00, (byte) 0x09, };
6464
6565 private static final class TestRunnerThread extends Thread {
6666 private final RunnableWithExceptions runnable;
159159 }
160160
161161 void deleteAndLog(File f) {
162 if (!f.delete())
162 if (!f.delete()) {
163163 System.err.println("Could not delete " + f);
164 }
164165 }
165166
166167 /**
00 /*
11 * FindBugs - Find Bugs in Java programs
22 * Copyright (C) 2003-2008 University of Maryland
3 *
3 *
44 * This library is free software; you can redistribute it and/or
55 * modify it under the terms of the GNU Lesser General Public
66 * License as published by the Free Software Foundation; either
77 * version 2.1 of the License, or (at your option) any later version.
8 *
8 *
99 * This library is distributed in the hope that it will be useful,
1010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1111 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1212 * Lesser General Public License for more details.
13 *
13 *
1414 * You should have received a copy of the GNU Lesser General Public
1515 * License along with this library; if not, write to the Free Software
1616 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
3232 assertEquals("0xffff", IntAnnotation.getShortInteger(0xffff));
3333 assertEquals("0x1ffff", IntAnnotation.getShortInteger(0x1ffff));
3434 assertEquals("0x1fffffff", IntAnnotation.getShortInteger(0x1fffffff));
35 assertEquals("0x7fffffff", IntAnnotation.getShortInteger(0x7fffffff));
35 assertEquals("0x7fffffff", IntAnnotation.getShortInteger(0x7fffffff));
3636 assertEquals("15", IntAnnotation.getShortInteger(15));
3737 assertEquals("255", IntAnnotation.getShortInteger(255));
3838 assertEquals("-1", IntAnnotation.getShortInteger(-1));
4040 assertEquals( "0xffffffff", IntAnnotation.getShortInteger(0xffffffffL));
4141 assertEquals("0x1ffffffff", IntAnnotation.getShortInteger(0x1ffffffffL));
4242 assertEquals("0xfffffffff", IntAnnotation.getShortInteger(0xfffffffffL));
43
43
4444 }
4545
4646 }
2424 /**
2525 * A special Detector2 class designed to run some JUnit test code. Only used by
2626 * FindBugsTestCase.
27 *
27 *
2828 * @author David Hovemeyer
2929 * @see FindBugsTestCase
3030 */
5555 }
5656
5757 public void finishTest() throws Exception {
58 if (throwable instanceof Exception)
58 if (throwable instanceof Exception) {
5959 throw (Exception) throwable;
60 if (throwable instanceof Error)
60 }
61 if (throwable instanceof Error) {
6162 throw (Error) throwable;
62 if (throwable != null)
63 }
64 if (throwable != null) {
6365 throw new Error(throwable);
66 }
6467
6568 }
6669
6770 /*
6871 * (non-Javadoc)
69 *
72 *
7073 * @see edu.umd.cs.findbugs.Detector2#finishPass()
7174 */
75 @Override
7276 public void finishPass() {
7377 }
7478
7579 /*
7680 * (non-Javadoc)
77 *
81 *
7882 * @see edu.umd.cs.findbugs.Detector2#getDetectorClassName()
7983 */
84 @Override
8085 public String getDetectorClassName() {
8186 return this.getClass().getName();
8287 }
8388
8489 /*
8590 * (non-Javadoc)
86 *
91 *
8792 * @see
8893 * edu.umd.cs.findbugs.Detector2#visitClass(edu.umd.cs.findbugs.classfile
8994 * .ClassDescriptor)
9095 */
96 @Override
9197 public void visitClass(ClassDescriptor classDescriptor) throws CheckedAnalysisException {
9298 // Only execute the test once
9399 if (testExecuted) {
2929 OpcodeStack.Item m2 = OpcodeStack.Item.merge(zeroItem, intItem);
3030 assertNull(m2.getConstant());
3131 }
32
32
3333 public void testMergeTypeOnly() {
3434 OpcodeStack.Item intOnly = OpcodeStack.Item.typeOnly("I");
3535 OpcodeStack.Item zeroItem = new OpcodeStack.Item("I", 0);
36
36
3737 OpcodeStack.Item m1 = OpcodeStack.Item.merge(intOnly, zeroItem);
3838 assertEquals(0,m1.getConstant());
3939 OpcodeStack.Item m2 = OpcodeStack.Item.merge(zeroItem, intOnly);
6969 + "</BugCollection>"));
7070 assertEquals(1, bc.getCollection().size());
7171 BugInstance bug = bc.getCollection().iterator().next();
72 assertEquals("MS_MUTABLE_ARRAY", bug.getBugPattern().getType());
72 assertEquals("MS_MUTABLE_ARRAY", bug.getBugPattern().getType());
7373 assertEquals("1acc5c5b9b7ab9efacede805afe1e53a", bug.getInstanceHash());
7474 assertEquals(16, bug.getBugRank());
7575 assertEquals("4/11/10 11:24 AM", BugInstance.firstSeenXMLFormat().format(bug.getXmlProps().getFirstSeen()));
4444
4545 /*
4646 * (non-Javadoc)
47 *
47 *
4848 * @see junit.framework.TestCase#setUp()
4949 */
5050
3030
3131 /**
3232 * Tests for Subtypes2.
33 *
33 *
3434 * @author Bill Pugh
3535 * @author David Hovemeyer
3636 */
9090
9191 /*
9292 * (non-Javadoc)
93 *
93 *
9494 * @see junit.framework.TestCase#setUp()
9595 */
9696 @Override
133133 executeFindBugsTest(new RunnableWithExceptions() {
134134 /*
135135 * (non-Javadoc)
136 *
137 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
138 */
136 *
137 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
138 */
139 @Override
139140 public void run() throws Throwable {
140141 Subtypes2 test = getSubtypes2();
141142
148149 executeFindBugsTest(new RunnableWithExceptions() {
149150 /*
150151 * (non-Javadoc)
151 *
152 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
153 */
152 *
153 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
154 */
155 @Override
154156 public void run() throws Throwable {
155157 Subtypes2 test = getSubtypes2();
156158
163165 executeFindBugsTest(new RunnableWithExceptions() {
164166 /*
165167 * (non-Javadoc)
166 *
167 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
168 */
168 *
169 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
170 */
171 @Override
169172 public void run() throws Throwable {
170173 Subtypes2 test = getSubtypes2();
171174
178181
179182 public void testInterfaceIsSubtypeOfObject() throws Throwable {
180183 executeFindBugsTest(new RunnableWithExceptions() {
184 @Override
181185 public void run() throws ClassNotFoundException {
182186 Subtypes2 test = getSubtypes2();
183187
190194 executeFindBugsTest(new RunnableWithExceptions() {
191195 /*
192196 * (non-Javadoc)
193 *
194 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
195 */
197 *
198 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
199 */
200 @Override
196201 public void run() throws Throwable {
197202 Subtypes2 test = getSubtypes2();
198203
206211 executeFindBugsTest(new RunnableWithExceptions() {
207212 /*
208213 * (non-Javadoc)
209 *
210 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
211 */
214 *
215 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
216 */
217 @Override
212218 public void run() throws Throwable {
213219 Subtypes2 test = getSubtypes2();
214220
221227 executeFindBugsTest(new RunnableWithExceptions() {
222228 /*
223229 * (non-Javadoc)
224 *
225 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
226 */
230 *
231 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
232 */
233 @Override
227234 public void run() throws Throwable {
228235 Subtypes2 test = getSubtypes2();
229236
236243 executeFindBugsTest(new RunnableWithExceptions() {
237244 /*
238245 * (non-Javadoc)
239 *
240 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
241 */
246 *
247 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
248 */
249 @Override
242250 public void run() throws Throwable {
243251 Subtypes2 test = getSubtypes2();
244252
253261 executeFindBugsTest(new RunnableWithExceptions() {
254262 /*
255263 * (non-Javadoc)
256 *
257 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
258 */
264 *
265 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
266 */
267 @Override
259268 public void run() throws Exception {
260269 Subtypes2 test = getSubtypes2();
261270
268277 executeFindBugsTest(new RunnableWithExceptions() {
269278 /*
270279 * (non-Javadoc)
271 *
272 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
273 */
280 *
281 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
282 */
283 @Override
274284 public void run() throws Exception {
275285 Subtypes2 test = getSubtypes2();
276286
285295 executeFindBugsTest(new RunnableWithExceptions() {
286296 /*
287297 * (non-Javadoc)
288 *
289 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
290 */
298 *
299 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
300 */
301 @Override
291302 public void run() throws Throwable {
292303 Subtypes2 test = getSubtypes2();
293304
306317 executeFindBugsTest(new RunnableWithExceptions() {
307318 /*
308319 * (non-Javadoc)
309 *
310 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
311 */
320 *
321 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
322 */
323 @Override
312324 public void run() throws Throwable {
313325 Subtypes2 test = getSubtypes2();
314326
325337 executeFindBugsTest(new RunnableWithExceptions() {
326338 /*
327339 * (non-Javadoc)
328 *
329 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
330 */
340 *
341 * @see edu.umd.cs.findbugs.RunnableWithExceptions#run()
342 */
343 @Override
331344 public void run() throws Throwable {
332345 Subtypes2 test = getSubtypes2();
333346
351364 public void testArrayFirstCommonSuperclassTricky() throws Exception {
352365 executeFindBugsTest(new RunnableWithExceptions() {
353366
367 @Override
354368 public void run() throws Throwable {
355369 Subtypes2 test = getSubtypes2();
356370
368382 public void testInterfaces() throws Exception {
369383 executeFindBugsTest(new RunnableWithExceptions() {
370384
385 @Override
371386 public void run() throws Throwable {
372387 Subtypes2 test = getSubtypes2();
373388 assertEquals(typeCollection, test.getFirstCommonSuperclass(typeCollection, typeHashSet));
6969 GenericUtilities.getType("[Ljava/util/Map<Ljava/lang/String;[Ljava/lang/String;>;");
7070 GenericUtilities.getType("Lcom/palantir/finance/commons/service/calculator/Call<-Ljava/util/List<!*>;+Ljava/util/List<Ljava/lang/String;>;>;");
7171 }
72
72
7373 }
6969 if (typeCategory == TypeCategory.PARAMETERIZED) {
7070 assertTrue(obj.hasParameters());
7171 assertTrue(obj.getNumParameters() == parameters.size());
72 for (int i = 0; i < obj.getNumParameters(); i++)
72 for (int i = 0; i < obj.getNumParameters(); i++) {
7373 compareTypes(obj.getParameterAt(i), parameters.get(i));
74 }
7475 assertNull(obj.getVariable());
7576 assertNull(obj.getExtension());
7677 } else if (typeCategory == TypeCategory.TYPE_VARIABLE) {
1818
1919 package edu.umd.cs.findbugs.ba.jsr305;
2020
21 import static org.junit.Assert.assertEquals;
22 import static org.junit.Assert.fail;
21 import static org.junit.Assert.*;
2322
2423 import java.io.File;
2524 import java.util.concurrent.atomic.AtomicBoolean;
4140 private static final SlashedClassName ANNOTATION = AnnotationTemplate.class.getAnnotation(SlashedClassName.class);
4241 static class BadValidator implements TypeQualifierValidator<SlashedClassName> {
4342
43 @Override
4444 public @Nonnull
4545 When forConstantValue(@Nonnull
46 SlashedClassName annotation, Object value) {
46 SlashedClassName annotation, Object value) {
4747 Thread t = new Thread() {
4848 @Override
4949 public void run() {
7272 t.start();
7373 t.join();
7474 assertEquals(true, b.get());
75 for (File f : File.listRoots())
75 for (File f : File.listRoots()) {
7676 f.listFiles();
77 }
7778 }
7879
7980 @SlashedClassName static class AnnotationTemplate {}
2424
2525 /**
2626 * Test cases for ReturnPathType class.
27 *
27 *
2828 * @author David Hovemeyer
2929 */
3030 public class ReturnPathTypeTest extends TestCase {
3636
3737 /*
3838 * (non-Javadoc)
39 *
39 *
4040 * @see junit.framework.TestCase#setUp()
4141 */
4242 @Override
2727
2828 @Test
2929 public void testMapSignaturePattern() {
30 // assertTrue(TypeFrameModelingVisitor.mapSignaturePattern.matcher("<") && !sourceSignature.contains("Map<TK;TV;>"))
30 // assertTrue(TypeFrameModelingVisitor.mapSignaturePattern.matcher("<") && !sourceSignature.contains("Map<TK;TV;>"))
3131 }
3232
3333 }
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2008 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.classfile;
20
21 import static org.junit.Assert.assertEquals;
22
23 import org.junit.Test;
24
25 /**
26 * @author andrei
27 */
28 public class TestClassDescriptor {
29
30 @Test
31 public void testSimpleName() {
32 ClassDescriptor p = DescriptorFactory.createClassDescriptor("com/bla/Parent");
33 assertEquals("com/bla/Parent", p.getClassName());
34 assertEquals("com.bla.Parent", p.getDottedClassName());
35 assertEquals("Lcom/bla/Parent;", p.getSignature());
36 assertEquals("com.bla", p.getPackageName());
37 assertEquals("Parent", p.getSimpleName());
38
39 ClassDescriptor c = DescriptorFactory.createClassDescriptor("com/bla/Parent$Child");
40 assertEquals("com/bla/Parent$Child", c.getClassName());
41 assertEquals("com.bla.Parent$Child", c.getDottedClassName());
42 assertEquals("Lcom/bla/Parent$Child;", c.getSignature());
43 assertEquals("com.bla", c.getPackageName());
44 assertEquals("Child", c.getSimpleName());
45
46 ClassDescriptor a = DescriptorFactory.createClassDescriptor("com/bla/Parent$Child$1");
47 assertEquals("com/bla/Parent$Child$1", a.getClassName());
48 assertEquals("com.bla.Parent$Child$1", a.getDottedClassName());
49 assertEquals("Lcom/bla/Parent$Child$1;", a.getSignature());
50 assertEquals("com.bla", a.getPackageName());
51 assertEquals("1", a.getSimpleName());
52 }
53
54 }
133133 assertEquals("line " + (i + 1), expectedLines[i].trim(), actualLines[i].trim());
134134 }
135135 int diff = actualLines.length - expectedLines.length;
136 if (diff < 0)
136 if (diff < 0) {
137137 fail((-diff) + " more lines than expected "
138138 + Arrays.asList(actualLines).subList(expectedLines.length, actualLines.length));
139 else if (diff > 0)
139 } else if (diff > 0) {
140140 fail((diff) + " missing lines " + Arrays.asList(expectedLines).subList(actualLines.length, expectedLines.length));
141 }
141142 }
142143
143144 public void testPrintSummaryOneBugNoEvals() {
162163 "Distribution of reviews", " num designation", " 2 I will fix", " 1 not a bug", "",
163164 "Distribution of number of reviews", " 3 with 1 review" },
164165
165 printSummary(bug1, bug2, bug3));
166 printSummary(bug1, bug2, bug3));
166167 }
167168
168169 public void testPrintSummaryWithMoreThan9Reviewers() {
181182 " 3 9 user9", " 4 8 user8", " 5 7 user7", " 6 6 user6", " 7 5 user5", " 8 4 user4",
182183 " 9 3 user3", " 11 1 user", "Total of 12 reviewers", "", "Distribution of reviews",
183184 " num designation", " 66 I will fix", " 1 not a bug", "", "Distribution of number of reviews",
184 " 67 with 1 review" },
185 " 67 with 1 review" },
185186
186187 printSummary(bugs.toArray(new BugInstance[0])));
187188 }
199200 " 1 mostly harmless", "", "Distribution of number of reviews", " 1 with 1 review",
200201 " 1 with 2 reviews", " 1 with 3 reviews" },
201202
202 printSummary(bugs.toArray(new BugInstance[0])));
203 printSummary(bugs.toArray(new BugInstance[0])));
203204 }
204205
205206 public void testVotingModePropertyNull() throws Exception {
271272 }
272273
273274 private void checkVotingMode(Mode expectedMode, String modeString) throws IOException {
274 if (modeString == null)
275 if (modeString == null) {
275276 plugin.getProperties().getProperties().remove("findbugs.cloud.votingmode");
276 else
277 } else {
277278 plugin.getProperties().setProperty("findbugs.cloud.votingmode", modeString);
279 }
278280 cloud = new MyAbstractCloud(plugin, bugCollection, new Properties());
279281 cloud.initialize();
280282 assertEquals(expectedMode, cloud.getMode());
282284
283285 private void initializeSourceLinks(String pattern, String format, String formatWithLine) throws IOException {
284286 plugin.getProperties().setProperty("findbugs.sourcelink.pattern", pattern);
285 if (format != null)
287 if (format != null) {
286288 plugin.getProperties().setProperty("findbugs.sourcelink.format", format);
287 if (formatWithLine != null)
289 }
290 if (formatWithLine != null) {
288291 plugin.getProperties().setProperty("findbugs.sourcelink.formatWithLine", formatWithLine);
292 }
289293 cloud = new MyAbstractCloud(plugin, bugCollection, new Properties());
290294 cloud.initialize();
291295 }
310314 StringBuilder builder = new StringBuilder();
311315 boolean first = true;
312316 for (String string : lines) {
313 if (!first)
317 if (!first) {
314318 builder.append("\n");
319 }
315320 builder.append(string);
316321 first = false;
317322 }
340345 super(plugin, bugs, properties);
341346 }
342347
348 @Override
343349 public void storeUserAnnotation(BugInstance bugInstance) {
344350 throw new UnsupportedOperationException();
345351 }
346352
353 @Override
347354 public String getUser() {
348355 return "user";
349356 }
353360 return SigninState.NO_SIGNIN_REQUIRED;
354361 }
355362
363 @Override
356364 public void setSaveSignInInformation(boolean save) {
357365 }
358366
367 @Override
359368 public boolean isSavingSignInInformationEnabled() {
360369 return false;
361370 }
362371
372 @Override
363373 public void signIn() {
364374 }
365375
376 @Override
366377 public void signOut() {
367378 }
368379
380 @Override
369381 public BugDesignation getPrimaryDesignation(BugInstance b) {
370382 throw new UnsupportedOperationException();
371383 }
375387 throw new UnsupportedOperationException();
376388 }
377389
390 @Override
378391 public void initiateCommunication() {
379392
380393 }
381394
395 @Override
382396 public void bugFiled(BugInstance b, Object bugLink) {
383397 throw new UnsupportedOperationException();
384398 }
385399
400 @Override
386401 public boolean availableForInitialization() {
387402 throw new UnsupportedOperationException();
388403 }
389404
405 @Override
390406 public void waitUntilIssueDataDownloaded() {
391407 }
408 @Override
392409 public boolean waitUntilNewIssuesUploaded(long timeout, TimeUnit unit) throws InterruptedException {
393410 return true;
394411 }
395412
413 @Override
396414 public void waitUntilNewIssuesUploaded() {
397415
398416 }
417 @Override
399418 public boolean waitUntilIssueDataDownloaded(long timeout, TimeUnit unit) throws InterruptedException {
400419 return true;
401420 }
402421
422 @Override
403423 public Collection<String> getProjects(String className) {
404424 return Collections.emptyList();
405425 }
406426
427 @Override
407428 public boolean isInCloud(BugInstance b) {
408429 return true;
409430 }
410431
432 @Override
411433 public boolean isOnlineCloud() {
412434 return false;
413435 }
423445
424446 /*
425447 * (non-Javadoc)
426 *
448 *
427449 * @see
428450 * edu.umd.cs.findbugs.cloud.Cloud#fileBug(edu.umd.cs.findbugs.BugInstance
429451 * , ProtoClasses.BugLinkType)
435457
436458 /*
437459 * (non-Javadoc)
438 *
460 *
439461 * @see
440462 * edu.umd.cs.findbugs.cloud.Cloud#getBugIsUnassigned(edu.umd.cs.findbugs
441463 * .BugInstance)
447469
448470 /*
449471 * (non-Javadoc)
450 *
472 *
451473 * @see
452474 * edu.umd.cs.findbugs.cloud.Cloud#getReviewers(edu.umd.cs.findbugs.
453475 * BugInstance)
459481
460482 /*
461483 * (non-Javadoc)
462 *
484 *
463485 * @see
464486 * edu.umd.cs.findbugs.cloud.Cloud#getWillNotBeFixed(edu.umd.cs.findbugs
465487 * .BugInstance)
471493
472494 /*
473495 * (non-Javadoc)
474 *
496 *
475497 * @see
476498 * edu.umd.cs.findbugs.cloud.AbstractCloud#getLatestDesignationFromEachUser
477499 * (edu.umd.cs.findbugs.BugInstance)
480502 protected Iterable<BugDesignation> getLatestDesignationFromEachUser(BugInstance b) {
481503 Map<String, BugDesignation> map = new HashMap<String, BugDesignation>();
482504 List<BugDesignation> list = designations.get(b);
483 if (list == null)
505 if (list == null) {
484506 return Collections.emptyList();
507 }
485508 for (BugDesignation bd : list) {
486509 BugDesignation old = map.get(bd.getUser());
487 if (old == null || old.getTimestamp() < bd.getTimestamp())
510 if (old == null || old.getTimestamp() < bd.getTimestamp()) {
488511 map.put(bd.getUser(), bd);
512 }
489513 }
490514 return map.values();
491515 }
492516
493517
494
518
495519 }
496520 }
6464
6565 public void testPlainCategories() {
6666 int count = 0;
67 for (String category : DetectorFactoryCollection.instance().getBugCategories())
67 for (String category : DetectorFactoryCollection.instance().getBugCategories()) {
6868 if (!category.equals("NOISE")) {
6969 Assert.assertTrue(plain.containsCategory(category));
7070 ++count;
7171 }
72 }
7273 Assert.assertEquals(count, plain.getActiveCategorySet().size());
7374 }
7475
7576 public void testAddCategory() {
7677 Assert.assertTrue(plain.containsCategory("FAKE_CATEGORY")); // unknown
77 // categories
78 // should be
79 // unhidden
80 // by
81 // default
78 // categories
79 // should be
80 // unhidden
81 // by
82 // default
8283 plain.addCategory("FAKE_CATEGORY");
8384 Assert.assertTrue(plain.containsCategory("FAKE_CATEGORY"));
8485 }
146147 }
147148 }
148149
149 // vim:ts=4
2020
2121 package edu.umd.cs.findbugs.filter;
2222
23 import static org.junit.Assert.assertFalse;
24 import static org.junit.Assert.assertSame;
25 import static org.junit.Assert.assertTrue;
23 import static org.junit.Assert.*;
2624 import static org.junit.matchers.JUnitMatchers.containsString;
2725
2826 import java.io.ByteArrayOutputStream;
7573 Matcher wrappedMatcher = new TestMatcher(true);
7674 NotMatcher notMatcher = new NotMatcher();
7775 notMatcher.addChild(wrappedMatcher);
78
76
7977 assertSame("Should return child matcher.", wrappedMatcher, notMatcher.originalMatcher());
8078 }
81
79
8280 @Test(expected=IllegalStateException.class)
8381 public void throwsExceptionWhenTryingToGetNonExistentChildMatcher() {
8482 new NotMatcher().originalMatcher();
8583 }
86
84
8785 private String writeXMLAndGetStringOutput(NotMatcher notMatcher) throws IOException {
8886 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
8987 XMLOutput xmlOutput = new OutputStreamXMLOutput(outputStream);
104102 this.alwaysMatches = alwaysMatches;
105103 }
106104
105 @Override
107106 public boolean match(BugInstance bugInstance) {
108107 return alwaysMatches;
109108 }
110109
110 @Override
111111 public void writeXML(XMLOutput xmlOutput, boolean disabled) throws IOException {
112112 xmlOutput.openTag("TestMatch");
113113 xmlOutput.closeTag("TestMatch");
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2008 University of Maryland
3 *
4 * Author: Andrey Loskutov
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 package edu.umd.cs.findbugs.filter;
22
23 import static org.junit.Assert.*;
24
25 import java.io.ByteArrayOutputStream;
26 import java.io.IOException;
27
28 import org.apache.tools.ant.filters.StringInputStream;
29 import org.junit.Before;
30 import org.junit.Test;
31
32 import edu.umd.cs.findbugs.BugInstance;
33 import edu.umd.cs.findbugs.ClassAnnotation;
34 import edu.umd.cs.findbugs.SourceLineAnnotation;
35 import edu.umd.cs.findbugs.xml.OutputStreamXMLOutput;
36 import edu.umd.cs.findbugs.xml.XMLOutput;
37
38 public class SourceMatcherTest {
39
40 private BugInstance bug;
41 private String fileName;
42
43 @Before
44 public void setup() {
45 bug = new BugInstance("UUF_UNUSED_FIELD", 0);
46 fileName = "bla.groovy";
47 }
48
49 @Test
50 public void writeXML() throws Exception {
51 SourceMatcher sm = new SourceMatcher(fileName);
52
53 String xmlOutput = writeXMLAndGetStringOutput(sm, false);
54 assertEquals("<Source name=\"" + fileName + "\"/>", xmlOutput);
55
56 sm = new SourceMatcher(fileName);
57 xmlOutput = writeXMLAndGetStringOutput(sm, true);
58 assertEquals("<Source name=\"" + fileName + "\" disabled=\"true\"/>", xmlOutput);
59 }
60
61 @Test
62 public void readXML() throws Exception {
63 SourceMatcher sm = new SourceMatcher(fileName);
64
65 String xml = writeXMLAndGetStringOutput(sm, false);
66 xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
67 + "\n<FindBugsFilter>"
68 + "\n<Match>"
69 + "\n" + xml
70 + "\n</Match>"
71 + "\n</FindBugsFilter>\n";
72
73 Filter filter = new Filter(new StringInputStream(xml));
74
75 assertFalse(filter.match(bug));
76
77 bug.addClass("bla", fileName);
78 assertTrue(filter.match(bug));
79 }
80
81
82 @Test
83 public void match() throws Exception {
84 SourceMatcher sm = new SourceMatcher(fileName);
85
86 // no source set: test incomplete data
87 assertFalse(sm.match(bug));
88
89 bug.addClass("bla", null);
90 assertFalse(sm.match(bug));
91
92 ClassAnnotation primaryClass = bug.getPrimaryClass();
93 primaryClass.setSourceLines(SourceLineAnnotation.createUnknown("bla", ""));
94 assertFalse(sm.match(bug));
95
96 // set right source file
97 primaryClass.setSourceLines(SourceLineAnnotation.createUnknown("bla", fileName));
98
99 // exact match
100 assertTrue(sm.match(bug));
101
102 // regexp first part
103 sm = new SourceMatcher("~bla.*");
104 assertTrue(sm.match(bug));
105
106 sm = new SourceMatcher("~blup.*");
107 assertFalse(sm.match(bug));
108
109 // regexp second part
110 sm = new SourceMatcher("~.*\\.groovy");
111 assertTrue(sm.match(bug));
112
113 sm = new SourceMatcher("~.*\\.java");
114 assertFalse(sm.match(bug));
115 }
116
117 private String writeXMLAndGetStringOutput(SourceMatcher matcher, boolean disabled) throws IOException {
118 ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
119 XMLOutput xmlOutput = new OutputStreamXMLOutput(outputStream);
120
121 matcher.writeXML(xmlOutput, disabled);
122 xmlOutput.finish();
123
124 return outputStream.toString().trim();
125 }
126
127
128 }
00 /*
11 * FindBugs - Find Bugs in Java programs
22 * Copyright (C) 2003-2008 University of Maryland
3 *
3 *
44 * This library is free software; you can redistribute it and/or
55 * modify it under the terms of the GNU Lesser General Public
66 * License as published by the Free Software Foundation; either
77 * version 2.1 of the License, or (at your option) any later version.
8 *
8 *
99 * This library is distributed in the hope that it will be useful,
1010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1111 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1212 * Lesser General Public License for more details.
13 *
13 *
1414 * You should have received a copy of the GNU Lesser General Public
1515 * License along with this library; if not, write to the Free Software
1616 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1919 package edu.umd.cs.findbugs.gui2;
2020
2121 import static java.util.Arrays.asList;
22 import static junit.framework.Assert.assertFalse;
23 import static junit.framework.Assert.assertSame;
24 import static junit.framework.Assert.assertTrue;
22 import static junit.framework.Assert.*;
2523
2624 import org.junit.Test;
2725
3331 * @author Graham Allan (grundlefleck@gmail.com)
3432 */
3533 public class FilterFactoryTest {
36
34
3735 @Test
3836 public void invertMatcherShouldNegateTheOriginalMatchingResult() {
3937 BugInstance bug = new BugInstance("UUF_UNUSED_FIELD", 0);
4038 Matcher originalMatcher = FilterFactory.makeMatcher(asList(Sortables.BUGCODE), bug);
41
39
4240 assertTrue("Original matcher should match bug.", originalMatcher.match(bug));
43
41
4442 Matcher notMatcher = FilterFactory.invertMatcher(originalMatcher);
4543 assertTrue("Should return an instance of NotMatcher.", notMatcher instanceof NotMatcher);
4644 assertFalse("Inverted matcher should now not match.", notMatcher.match(bug));
4745 }
48
46
4947 @Test
5048 public void shouldReturnTheOriginalMatcherWhenAskedToInvertANotMatcher() {
5149 BugInstance bug = new BugInstance("UUF_UNUSED_FIELD", 0);
5250 Matcher originalMatcher = FilterFactory.makeMatcher(asList(Sortables.BUGCODE), bug);
5351 Matcher notMatcher = FilterFactory.invertMatcher(originalMatcher);
5452 Matcher notNotMatcher = FilterFactory.invertMatcher(notMatcher);
55
53
5654 assertSame("Should return the originally wrapped matcher.", originalMatcher, notNotMatcher);
5755 assertTrue("Original matcher should now not match.", notNotMatcher.match(bug));
5856 }
0 package edu.umd.cs.findbugs.updates;
1
2 import java.io.ByteArrayInputStream;
3 import java.io.ByteArrayOutputStream;
4 import java.io.IOException;
5 import java.net.MalformedURLException;
6 import java.net.URI;
7 import java.net.URISyntaxException;
8 import java.net.URL;
9 import java.text.ParseException;
10 import java.text.SimpleDateFormat;
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.Collection;
14 import java.util.Date;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.Locale;
18 import java.util.Map;
19 import java.util.concurrent.CountDownLatch;
20 import java.util.logging.Level;
21 import java.util.regex.Pattern;
22
23 import junit.framework.TestCase;
24 import edu.umd.cs.findbugs.Plugin;
25 import edu.umd.cs.findbugs.PluginException;
26 import edu.umd.cs.findbugs.PluginLoader;
27 import edu.umd.cs.findbugs.Version;
28
29 public class UpdateCheckerTest extends TestCase {
30 private static final Date KEITHS_BIRTHDAY_2011;
31
32 static {
33 try {
34 KEITHS_BIRTHDAY_2011 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z", Locale.ENGLISH).parse("2011-03-20 02:00:00 EST");
35 } catch (ParseException e) {
36 throw new IllegalStateException(e);
37 }
38 }
39
40 private List<UpdateChecker.PluginUpdate> updateCollector;
41 private StringBuilder errors;
42 private String responseXml;
43 private CountDownLatch latch;
44 private UpdateChecker checker;
45 private Map<String, Collection<Plugin>> checked;
46 private Map<String, String> globalOptions;
47 private String uploadedXml;
48
49 @Override
50 protected void setUp() throws Exception {
51 updateCollector = new ArrayList<UpdateChecker.PluginUpdate>();
52 errors = new StringBuilder();
53 latch = new CountDownLatch(1);
54 checked = new HashMap<String, Collection<Plugin>>();
55 globalOptions = new HashMap<String, String>();
56 uploadedXml = null;
57 checker = new UpdateChecker(new TestingUpdateCheckCallback(latch)) {
58 @Override
59 protected void actuallyCheckforUpdates(URI url, Collection<Plugin> plugins, String entryPoint)
60 throws IOException {
61 String urlStr = url.toString();
62 assertFalse(checked.containsKey(urlStr));
63 checked.put(urlStr, plugins);
64 ByteArrayInputStream stream = new ByteArrayInputStream(responseXml.getBytes("UTF-8"));
65 ByteArrayOutputStream out = new ByteArrayOutputStream();
66 writeXml(out, plugins, "x.y.z", true);
67 uploadedXml = new String(out.toByteArray(), "UTF-8");
68 parseUpdateXml(url, plugins, stream);
69 }
70
71 @Override
72 protected void logError(Exception e, String msg) {
73 errors.append(msg).append("\n");
74 System.err.println(msg);
75 e.printStackTrace();
76 }
77
78 @Override
79 protected void logError(Level level, String msg) {
80 errors.append(msg).append("\n");
81 System.err.println(msg);
82 }
83 };
84 }
85
86 public void testSimplePluginUpdate() throws Exception {
87 // setup
88 setResponseXml("my.id", "09/01/2011 02:00 PM EST", "2.1");
89
90 // execute
91 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
92
93 // verify
94 assertEquals(1, updateCollector.size());
95 UpdateChecker.PluginUpdate update = updateCollector.get(0);
96 assertEquals("UPDATE ME", update.getMessage());
97 assertEquals("http://example.com/update", update.getUrl());
98 assertEquals("2.1", update.getVersion());
99 assertEquals("my.id", update.getPlugin().getPluginId());
100 }
101
102 public void testPluginSameVersionDifferentDate() throws Exception {
103 // setup
104 setResponseXml("my.id", "09/01/2011 02:00 PM EST", "2.0");
105
106 // execute
107 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
108
109 // verify
110 assertEquals(0, updateCollector.size());
111 }
112
113 public void testPluginSameVersionSameDate() throws Exception {
114 // setup
115 setResponseXml("my.id", "2011-03-20 02:00:00 EST", "2.0");
116
117 // execute
118 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
119
120 // verify
121 assertEquals(0, updateCollector.size());
122 }
123
124 public void testPluginDifferentVersionSameDate() throws Exception {
125 // setup
126 setResponseXml("my.id", "09/01/2011 02:00 PM EST", "2.0");
127
128 // execute
129 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
130
131 // verify
132 assertEquals(0, updateCollector.size());
133 }
134
135 public void testPluginNotPresent() throws Exception {
136 // setup
137 setResponseXml("SOME.OTHER.PLUGIN", "09/01/2011 02:00 PM EST", "2.0");
138
139 // execute
140 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
141
142 // verify
143 assertEquals(0, updateCollector.size());
144 }
145
146 public void testRedirectUpdateChecks() throws Exception {
147 // setup
148 setResponseXml("SOME.OTHER.PLUGIN", "09/01/2011 02:00 PM EST", "2.0");
149 globalOptions.put("redirectUpdateChecks", "http://redirect.com");
150
151 // execute
152 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
153
154 // verify
155 assertEquals(1, checked.size());
156 assertTrue(checked.containsKey("http://redirect.com"));
157 assertEquals(0, updateCollector.size());
158 }
159
160 public void testDisableUpdateChecks() throws Exception {
161 // setup
162 setResponseXml("my.id", "09/01/2011 02:00 PM EST", "2.1");
163 globalOptions.put("noUpdateChecks", "true");
164
165 // execute
166 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
167
168 // verify
169 assertEquals(0, checked.size());
170 assertEquals(0, updateCollector.size());
171 }
172
173 public void testDisableUpdateChecksFalse() throws Exception {
174 // setup
175 setResponseXml("my.id", "09/01/2011 02:00 PM EST", "2.1");
176 globalOptions.put("noUpdateChecks", "false");
177
178 // execute
179 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
180
181 // verify
182 assertEquals(1, checked.size());
183 assertEquals(1, updateCollector.size());
184 }
185
186 public void testDisableUpdateChecksInvalid() throws Exception {
187 // setup
188 setResponseXml("my.id", "09/01/2011 02:00 PM EST", "2.1");
189 globalOptions.put("noUpdateChecks", "BLAH");
190
191 // execute
192 try {
193 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
194 fail();
195 } catch (Throwable e) {
196 }
197
198 // verify
199 assertEquals(0, checked.size());
200 assertEquals(0, updateCollector.size());
201 }
202
203 public void testSubmittedXml() throws Exception {
204 // setup
205 setResponseXml("my.id", "09/01/2011 02:00 PM EST", "2.1");
206 Version.registerApplication("MyApp", "2.x");
207
208 // execute
209 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
210
211 // verify
212 String pattern = "<?xml version='1.0' encoding='UTF-8'?>\n" +
213 "\n" +
214 "<findbugs-invocation version='*' app-name='MyApp' app-version='2.x' " +
215 "entry-point='x.y.z' os='*' java-version='*' language='*' country='*' " +
216 "uuid='*'>\n" +
217 " <plugin id='my.id' name='My Plugin' version='2.0' release-date='1300604400000'/>\n" +
218 "</findbugs-invocation>\n";
219 String patternRE = convertGlobToRE(pattern);
220 assertTrue(uploadedXml + " did not match " + patternRE, uploadedXml.matches(patternRE));
221 }
222
223 // ================ end of tests =============
224
225 private void checkForUpdates(Plugin plugin) throws InterruptedException {
226 checker.checkForUpdates(Arrays.asList(plugin), true);
227 latch.await();
228 }
229
230 private String convertGlobToRE(String pattern) {
231 StringBuilder builder = new StringBuilder();
232 boolean first = true;
233 for (String blah : pattern.split("\\*")) {
234 if (first) first = false;
235 else builder.append(".*");
236 builder.append(Pattern.quote(blah.replaceAll("'", "\"")));
237 }
238 return builder.toString();
239 }
240
241 private void setResponseXml(String pluginid, String releaseDate, String v) {
242 responseXml =
243 "<fb-plugin-updates>" +
244 " <plugin id='" + pluginid + "'>" +
245 " <release date='" + releaseDate + "' version='" + v + "' url='http://example.com/update'>" +
246 " <message>UPDATE ME</message>" +
247 " </release>" +
248 " </plugin>" +
249 "</fb-plugin-updates>";
250 }
251
252 @SuppressWarnings({"deprecation"})
253 private Plugin createPlugin(String pluginId, Date releaseDate, String version) throws URISyntaxException, PluginException {
254 PluginLoader fakeLoader;
255 try {
256 fakeLoader = new PluginLoader(true, new URL("http://" + pluginId + ".findbugs.cs.umd.edu"));
257 } catch (MalformedURLException e) {
258 throw new RuntimeException(e);
259 }
260 Plugin plugin = new Plugin(pluginId, version, releaseDate, fakeLoader, true, false);
261 plugin.setShortDescription("My Plugin");
262 plugin.setUpdateUrl("http://example.com/update");
263 return plugin;
264 }
265
266 private class TestingUpdateCheckCallback implements UpdateCheckCallback {
267 private final CountDownLatch latch;
268
269 public TestingUpdateCheckCallback(CountDownLatch latch) {
270 this.latch = latch;
271 }
272
273 public void pluginUpdateCheckComplete(List<UpdateChecker.PluginUpdate> updates, boolean force) {
274 updateCollector.addAll(updates);
275 latch.countDown();
276 }
277
278 public String getGlobalOption(String key) {
279 return globalOptions.get(key);
280 }
281
282 public Plugin getGlobalOptionSetter(String key) {
283 return null;
284 }
285 }
286 }
0 package edu.umd.cs.findbugs.updates;
1
2 import java.io.ByteArrayInputStream;
3 import java.io.ByteArrayOutputStream;
4 import java.io.IOException;
5 import java.net.MalformedURLException;
6 import java.net.URI;
7 import java.net.URISyntaxException;
8 import java.net.URL;
9 import java.text.ParseException;
10 import java.text.SimpleDateFormat;
11 import java.util.ArrayList;
12 import java.util.Arrays;
13 import java.util.Collection;
14 import java.util.Date;
15 import java.util.HashMap;
16 import java.util.List;
17 import java.util.Locale;
18 import java.util.Map;
19 import java.util.concurrent.CountDownLatch;
20 import java.util.logging.Level;
21 import java.util.regex.Pattern;
22
23 import junit.framework.TestCase;
24 import edu.umd.cs.findbugs.Plugin;
25 import edu.umd.cs.findbugs.PluginException;
26 import edu.umd.cs.findbugs.PluginLoader;
27 import edu.umd.cs.findbugs.Version;
28
29 public class UpdateCheckerTest extends TestCase {
30 private static final Date KEITHS_BIRTHDAY_2011;
31
32 static {
33 try {
34 KEITHS_BIRTHDAY_2011 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss z", Locale.ENGLISH).parse("2011-03-20 02:00:00 EST");
35 } catch (ParseException e) {
36 throw new IllegalStateException(e);
37 }
38 }
39
40 private List<UpdateChecker.PluginUpdate> updateCollector;
41 private StringBuilder errors;
42 private String responseXml;
43 private CountDownLatch latch;
44 private UpdateChecker checker;
45 private Map<String, Collection<Plugin>> checked;
46 private Map<String, String> globalOptions;
47 private String uploadedXml;
48
49 @Override
50 protected void setUp() throws Exception {
51 updateCollector = new ArrayList<UpdateChecker.PluginUpdate>();
52 errors = new StringBuilder();
53 latch = new CountDownLatch(1);
54 checked = new HashMap<String, Collection<Plugin>>();
55 globalOptions = new HashMap<String, String>();
56 uploadedXml = null;
57 checker = new UpdateChecker(new TestingUpdateCheckCallback(latch)) {
58 @Override
59 protected void actuallyCheckforUpdates(URI url, Collection<Plugin> plugins, String entryPoint)
60 throws IOException {
61 String urlStr = url.toString();
62 assertFalse(checked.containsKey(urlStr));
63 checked.put(urlStr, plugins);
64 ByteArrayInputStream stream = new ByteArrayInputStream(responseXml.getBytes("UTF-8"));
65 ByteArrayOutputStream out = new ByteArrayOutputStream();
66 writeXml(out, plugins, "x.y.z", true);
67 uploadedXml = new String(out.toByteArray(), "UTF-8");
68 parseUpdateXml(url, plugins, stream);
69 }
70
71 @Override
72 protected void logError(Exception e, String msg) {
73 errors.append(msg).append("\n");
74 System.err.println(msg);
75 e.printStackTrace();
76 }
77
78 @Override
79 protected void logError(Level level, String msg) {
80 errors.append(msg).append("\n");
81 System.err.println(msg);
82 }
83 };
84 org.junit.Assume.assumeTrue(!checker.updateChecksGloballyDisabled());
85 }
86
87
88 public void testSimplePluginUpdate() throws Exception {
89 // setup
90 setResponseXml("my.id", "09/01/2011 02:00 PM EST", "2.1");
91
92 // execute
93 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
94
95 // verify
96 assertEquals(1, updateCollector.size());
97 UpdateChecker.PluginUpdate update = updateCollector.get(0);
98 assertEquals("UPDATE ME", update.getMessage());
99 assertEquals("http://example.com/update", update.getUrl());
100 assertEquals("2.1", update.getVersion());
101 assertEquals("my.id", update.getPlugin().getPluginId());
102 }
103
104 public void testPluginSameVersionDifferentDate() throws Exception {
105 // setup
106 setResponseXml("my.id", "09/01/2011 02:00 PM EST", "2.0");
107
108 // execute
109 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
110
111 // verify
112 assertEquals(0, updateCollector.size());
113 }
114
115 public void testPluginSameVersionSameDate() throws Exception {
116 // setup
117 setResponseXml("my.id", "03/20/2011 03:00 AM EDT", "2.0");
118
119 // execute
120 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
121
122 // verify
123 assertEquals(0, updateCollector.size());
124 }
125
126 public void testPluginDifferentVersionSameDate() throws Exception {
127 // setup
128 setResponseXml("my.id", "09/01/2011 02:00 PM EST", "2.0");
129
130 // execute
131 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
132
133 // verify
134 assertEquals(0, updateCollector.size());
135 }
136
137 public void testPluginNotPresent() throws Exception {
138 // setup
139 setResponseXml("SOME.OTHER.PLUGIN", "09/01/2011 02:00 PM EST", "2.0");
140
141 // execute
142 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
143
144 // verify
145 assertEquals(0, updateCollector.size());
146 }
147
148 public void testRedirectUpdateChecks() throws Exception {
149 // setup
150 setResponseXml("SOME.OTHER.PLUGIN", "09/01/2011 02:00 PM EST", "2.0");
151 globalOptions.put("redirectUpdateChecks", "http://redirect.com");
152
153 // execute
154 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
155
156 // verify
157 assertEquals(1, checked.size());
158 assertTrue(checked.containsKey("http://redirect.com"));
159 assertEquals(0, updateCollector.size());
160 }
161
162 public void testDisableUpdateChecks() throws Exception {
163 // setup
164 setResponseXml("my.id", "09/01/2011 02:00 PM EST", "2.1");
165 globalOptions.put("noUpdateChecks", "true");
166
167 // execute
168 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
169
170 // verify
171 assertEquals(0, checked.size());
172 assertEquals(0, updateCollector.size());
173 }
174
175 public void testDisableUpdateChecksFalse() throws Exception {
176 // setup
177 setResponseXml("my.id", "09/01/2011 02:00 PM EST", "2.1");
178 globalOptions.put("noUpdateChecks", "false");
179
180 // execute
181 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
182
183 // verify
184 assertEquals(1, checked.size());
185 assertEquals(1, updateCollector.size());
186 }
187
188 public void testDisableUpdateChecksInvalid() throws Exception {
189 // setup
190 setResponseXml("my.id", "09/01/2011 02:00 PM EST", "2.1");
191 globalOptions.put("noUpdateChecks", "BLAH");
192
193 // execute
194 try {
195 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
196 fail();
197 } catch (Throwable e) {
198 }
199
200 // verify
201 assertEquals(0, checked.size());
202 assertEquals(0, updateCollector.size());
203 }
204
205 public void testSubmittedXml() throws Exception {
206 // setup
207 setResponseXml("my.id", "09/01/2011 02:00 PM EST", "2.1");
208 Version.registerApplication("MyApp", "2.x");
209
210 // execute
211 checkForUpdates(createPlugin("my.id", KEITHS_BIRTHDAY_2011, "2.0"));
212
213 // verify
214 String pattern = "<?xml version='1.0' encoding='UTF-8'?>\n" +
215 "\n" +
216 "<findbugs-invocation version='*' app-name='MyApp' app-version='2.x' " +
217 "entry-point='x.y.z' os='*' java-version='*' language='*' country='*' " +
218 "uuid='*'>\n" +
219 " <plugin id='my.id' name='My Plugin' version='2.0' release-date='1300604400000'/>\n" +
220 "</findbugs-invocation>\n";
221 String patternRE = convertGlobToRE(pattern);
222 assertTrue(uploadedXml + " did not match " + patternRE, uploadedXml.matches(patternRE));
223 }
224
225 // ================ end of tests =============
226
227 private void checkForUpdates(Plugin plugin) throws InterruptedException {
228
229 checker.checkForUpdates(Arrays.asList(plugin), true);
230 latch.await();
231 }
232
233 private String convertGlobToRE(String pattern) {
234 StringBuilder builder = new StringBuilder();
235 boolean first = true;
236 for (String blah : pattern.split("\\*")) {
237 if (first) {
238 first = false;
239 } else {
240 builder.append(".*");
241 }
242 builder.append(Pattern.quote(blah.replaceAll("'", "\"")));
243 }
244 return builder.toString();
245 }
246
247 private void setResponseXml(String pluginid, String releaseDate, String v) {
248 responseXml =
249 "<fb-plugin-updates>" +
250 " <plugin id='" + pluginid + "'>" +
251 " <release date='" + releaseDate + "' version='" + v + "' url='http://example.com/update'>" +
252 " <message>UPDATE ME</message>" +
253 " </release>" +
254 " </plugin>" +
255 "</fb-plugin-updates>";
256 }
257
258 @SuppressWarnings({"deprecation"})
259 private Plugin createPlugin(String pluginId, Date releaseDate, String version) throws URISyntaxException, PluginException {
260 PluginLoader fakeLoader;
261 try {
262 fakeLoader = new PluginLoader(true, new URL("http://" + pluginId + ".findbugs.cs.umd.edu"));
263 } catch (MalformedURLException e) {
264 throw new RuntimeException(e);
265 }
266 Plugin plugin = new Plugin(pluginId, version, releaseDate, fakeLoader, true, false);
267 plugin.setShortDescription("My Plugin");
268 plugin.setUpdateUrl("http://example.com/update");
269 return plugin;
270 }
271
272 private class TestingUpdateCheckCallback implements UpdateCheckCallback {
273 private final CountDownLatch latch;
274
275 public TestingUpdateCheckCallback(CountDownLatch latch) {
276 this.latch = latch;
277 }
278
279 @Override
280 public void pluginUpdateCheckComplete(List<UpdateChecker.PluginUpdate> updates, boolean force) {
281 updateCollector.addAll(updates);
282 latch.countDown();
283 }
284
285 @Override
286 public String getGlobalOption(String key) {
287 return globalOptions.get(key);
288 }
289
290 @Override
291 public Plugin getGlobalOptionSetter(String key) {
292 return null;
293 }
294 }
295 }
4040 assertEquals("java/lang/Integer", ClassName.extractClassName("[[[Ljava/lang/Integer;"));
4141 assertEquals("java/lang/Integer", ClassName.extractClassName("java/lang/Integer"));
4242 }
43
43
4444 public void testGetPrimitiveType() {
4545 assertEquals("I", ClassName.getPrimitiveType("java/lang/Integer"));
4646 assertEquals("F", ClassName.getPrimitiveType("java/lang/Float"));
2323 public class StringsTest extends TestCase {
2424
2525 public static String[] escapedStrings = {
26 // mixed entities/unicode escape sequences
27 "a b c 1 2 3 &amp; &lt; &gt; &quot; &apos; \\u0005 \\u0013 &#955; \\\\u0007",
28 // *even* series of prefixed slashes + \\u -> do escape
29 "a b c \\\\\\u0005",
30 // *odd* series of prefixed slashes + \\u -> don't escape
31 "a b c \\\\\\\\u0005",
32 // mixed even/odd prefixed slashes
33 "a b c \\\\\\u0005 \\\\\\\\u0013",
34 // make sure slashes work on their own (no double un/escaping)
35 "\\\\\\",
36 // make sure that normal characters are handled correctly if they
37 // appear after escapes
38 "a b c 1 2 3 &amp; &lt; &gt; &quot; &apos; \\u0005 \\u0013 &#955; \\\\u0007 a b c 1 2 3",
39 // escaping a null string should be safe
40 null,
41 // an empty string should be safe too
42 "", };
26 // mixed entities/unicode escape sequences
27 "a b c 1 2 3 &amp; &lt; &gt; &quot; &apos; \\u0005 \\u0013 &#955; \\\\u0007",
28 // *even* series of prefixed slashes + \\u -> do escape
29 "a b c \\\\\\u0005",
30 // *odd* series of prefixed slashes + \\u -> don't escape
31 "a b c \\\\\\\\u0005",
32 // mixed even/odd prefixed slashes
33 "a b c \\\\\\u0005 \\\\\\\\u0013",
34 // make sure slashes work on their own (no double un/escaping)
35 "\\\\\\",
36 // make sure that normal characters are handled correctly if they
37 // appear after escapes
38 "a b c 1 2 3 &amp; &lt; &gt; &quot; &apos; \\u0005 \\u0013 &#955; \\\\u0007 a b c 1 2 3",
39 // escaping a null string should be safe
40 null,
41 // an empty string should be safe too
42 "", };
4343
4444 public static String[] unescapedStrings = { "a b c 1 2 3 & < > \" ' \u0005 \u0013 \u03BB \\\\u0007", "a b c \\\\\u0005",
45 "a b c \\\\\\\\u0005", "a b c \\\\\u0005 \\\\\\\\u0013", "\\\\\\",
46 "a b c 1 2 3 & < > \" ' \u0005 \u0013 \u03BB \\\\u0007 a b c 1 2 3", null, "", };
45 "a b c \\\\\\\\u0005", "a b c \\\\\u0005 \\\\\\\\u0013", "\\\\\\",
46 "a b c 1 2 3 & < > \" ' \u0005 \u0013 \u03BB \\\\u0007 a b c 1 2 3", null, "", };
4747
4848 public void testEscapeXml() {
4949 assert (escapedStrings.length == unescapedStrings.length);
2929 static String[] simpleTypes = "I J B C D F I S Z".split(" ");
3030
3131 public void testSimpleWithVoidReturnType() {
32 for (String s : simpleTypes)
32 for (String s : simpleTypes) {
3333 assertEquals(1, PreorderVisitor.getNumberArguments("(" + s + ")V"));
34 }
3435 }
3536
3637 public void testSimpleWithVoidIntegerType() {
37 for (String s : simpleTypes)
38 for (String s : simpleTypes) {
3839 assertEquals(1, PreorderVisitor.getNumberArguments("(" + s + ")I"));
40 }
3941 }
4042
4143 public void testArrays() {
+0
-105
src/obsolete/edu/umd/cs/findbugs/ba/npe2/AcmpCondition.java less more
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2006, University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.ba.npe2;
20
21 import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
22 import edu.umd.cs.findbugs.ba.Edge;
23 import edu.umd.cs.findbugs.ba.Location;
24 import edu.umd.cs.findbugs.ba.vna.ValueNumber;
25 import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;
26
27 /**
28 * @author David Hovemeyer
29 */
30 @Deprecated
31 public class AcmpCondition extends Condition {
32
33 public AcmpCondition(Location location) {
34 super(location);
35 }
36
37 /*
38 * (non-Javadoc)
39 *
40 * @see
41 * edu.umd.cs.findbugs.ba.npe2.Condition#getDecision(edu.umd.cs.findbugs
42 * .ba.Edge)
43 */
44 @Override
45 public Decision getDecision(Edge edge) {
46 // TODO Auto-generated method stub
47 return null;
48 }
49
50 /*
51 * (non-Javadoc)
52 *
53 * @see edu.umd.cs.findbugs.ba.npe2.Condition#getValueNumber()
54 */
55 @Override
56 public ValueNumber getValueNumber() {
57 // TODO Auto-generated method stub
58 return null;
59 }
60
61 /*
62 * (non-Javadoc)
63 *
64 * @see
65 * edu.umd.cs.findbugs.ba.npe2.Condition#refresh(edu.umd.cs.findbugs.ba.
66 * vna.ValueNumberFrame, edu.umd.cs.findbugs.ba.npe2.DefinitelyNullSet)
67 */
68 @Override
69 public void refresh(ValueNumberFrame vnaFrame, DefinitelyNullSet definitelyNullSet) throws DataflowAnalysisException {
70
71 // // Get top two stack values
72 // ValueNumber a = vnaFrame.getStackValue(0);
73 // ValueNumber b = vnaFrame.getStackValue(1);
74 //
75 // boolean acmpEqOpcode =
76 // getLocation().getHandle().getInstruction().getOpcode() ==
77 // Constants.IF_ACMPEQ;
78 //
79 // if (a.equals(b)
80 // || (definitelyNullSet.isValueNull(a) &&
81 // definitelyNullSet.isValueNull(b))) {
82 // // Definitely the same value.
83 // // We don't learn anything about the nullness,
84 // // but one of the edges of the branch is
85 // // infeasible.
86 //
87 // ifcmpDecision = new Decision(
88 // acmpEqOpcode,
89 // false,
90 // false
91 // );
92 //
93 // fallThroughDecision = new Decision(
94 // !acmpEqOpcode,
95 // false,
96 // false
97 // );
98 //
99 // return;
100 // }
101
102 }
103
104 }
+0
-47
src/obsolete/edu/umd/cs/findbugs/ba/npe2/Condition.java less more
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2006, University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.ba.npe2;
20
21 import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
22 import edu.umd.cs.findbugs.ba.Edge;
23 import edu.umd.cs.findbugs.ba.Location;
24 import edu.umd.cs.findbugs.ba.vna.ValueNumber;
25 import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;
26
27 /**
28 * @author David Hovemeyer
29 */
30 public abstract class Condition {
31 private Location location;
32
33 public Condition(Location location) {
34 this.location = location;
35 }
36
37 public Location getLocation() {
38 return location;
39 }
40
41 public abstract void refresh(ValueNumberFrame vnaFrame, DefinitelyNullSet definitelyNullSet) throws DataflowAnalysisException;
42
43 public abstract ValueNumber getValueNumber();
44
45 public abstract Decision getDecision(Edge edge);
46 }
+0
-42
src/obsolete/edu/umd/cs/findbugs/ba/npe2/Decision.java less more
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2006, University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.ba.npe2;
20
21 /**
22 * @author David Hovemeyer
23 */
24 public class Decision {
25 private boolean feasible;
26
27 private NullnessValue nullnessValue;
28
29 public Decision(boolean feasible, NullnessValue nullnessValue) {
30 this.feasible = feasible;
31 this.nullnessValue = nullnessValue;
32 }
33
34 public boolean isFeasible() {
35 return feasible;
36 }
37
38 public NullnessValue getNullnessValue() {
39 return nullnessValue;
40 }
41 }
+0
-201
src/obsolete/edu/umd/cs/findbugs/ba/npe2/DefinitelyNullSet.java less more
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2006, University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.ba.npe2;
20
21 import java.util.BitSet;
22
23 import edu.umd.cs.findbugs.ba.vna.ValueNumber;
24
25 /**
26 * Set of values that is definitely known to be null.
27 *
28 * @author David Hovemeyer
29 */
30 public class DefinitelyNullSet /* extends BitSet */{
31 private BitSet contents;
32
33 private int numValueNumbers;
34
35 public DefinitelyNullSet(int numValueNumbers) {
36 this.contents = new BitSet();
37 this.numValueNumbers = numValueNumbers;
38 }
39
40 public NullnessValue getNulllessValue(ValueNumber valueNumber) {
41 return getNullnessValue(valueNumber.getNumber());
42 }
43
44 private NullnessValue getNullnessValue(int vn) {
45 int flags = 0;
46
47 int start = getStartIndex(vn);
48 for (int i = 0; i < NullnessValue.FLAGS_MAX; i++) {
49 if (contents.get(start + i)) {
50 flags |= (1 << i);
51 }
52 }
53
54 return NullnessValue.fromFlags(flags);
55 }
56
57 public void setNullnessValue(ValueNumber valueNumber, NullnessValue nullnessValue) {
58 int flags = nullnessValue.getFlags();
59
60 int start = getStartIndex(valueNumber.getNumber());
61 for (int i = 0; i < NullnessValue.FLAGS_MAX; i++) {
62 contents.set(start + i, (flags & (1 << i)) != 0);
63 }
64 }
65
66 public void clear() {
67 contents.clear();
68 }
69
70 public void setTop() {
71 contents.clear();
72 contents.set(lastUsedBit());
73 }
74
75 public boolean isTop() {
76 return contents.get(lastUsedBit());
77 }
78
79 public void setBottom() {
80 contents.clear();
81 contents.set(lastUsedBit() + 1);
82 }
83
84 public boolean isBottom() {
85 return contents.get(lastUsedBit() + 1);
86 }
87
88 public boolean isValid() {
89 return !(isTop() || isBottom());
90 }
91
92 public void makeSameAs(DefinitelyNullSet other) {
93 contents.clear();
94 contents.or(other.contents);
95 }
96
97 public void mergeWith(DefinitelyNullSet other) {
98 if (this.isBottom() || other.isTop()) {
99 return;
100 }
101
102 if (this.isTop() || other.isBottom()) {
103 this.makeSameAs(other);
104 return;
105 }
106
107 // Result is intersection of sets
108 this.contents.and(other.contents);
109 }
110
111 public BitSet getAssignedNullLocationSet(ValueNumber vn) {
112 throw new UnsupportedOperationException();
113 }
114
115 public void addAssignedNullLocation(int valueNumber, int locationNumber) {
116 // Base class does not maintain this information.
117 }
118
119 public void clearAssignNullLocations(int valueNumber) {
120 // Base class does not maintain this information.
121 }
122
123 private int getStartIndex(int vn) {
124 return vn * (1 << NullnessValue.FLAGS_MAX);
125 }
126
127 private int lastUsedBit() {
128 return numValueNumbers * NullnessValue.FLAGS_MAX;
129 }
130
131 private int topBit() {
132 return lastUsedBit();
133 }
134
135 private int bottomBit() {
136 return lastUsedBit() + 1;
137 }
138
139 /*
140 * (non-Javadoc)
141 *
142 * @see java.lang.Object#hashCode()
143 */
144 @Override
145 public int hashCode() {
146 return contents.hashCode();
147 }
148
149 /*
150 * (non-Javadoc)
151 *
152 * @see java.lang.Object#equals(java.lang.Object)
153 */
154 @Override
155 public boolean equals(Object obj) {
156 if (obj == null || obj.getClass() != this.getClass()) {
157 return false;
158 }
159
160 DefinitelyNullSet other = (DefinitelyNullSet) obj;
161 return this.contents.equals(other.contents);
162 }
163
164 /*
165 * (non-Javadoc)
166 *
167 * @see java.lang.Object#toString()
168 */
169 @Override
170 public String toString() {
171 if (isTop()) {
172 return "[TOP]";
173 } else if (isBottom()) {
174 return "[BOTTOM]";
175 } else {
176 StringBuilder buf = new StringBuilder();
177 boolean first = true;
178
179 buf.append("{");
180
181 for (int i = 0; i < numValueNumbers; i++) {
182 NullnessValue val = getNullnessValue(i);
183 if (val.isDefinitelyNull() || val.isDefinitelyNotNull()) {
184 if (first) {
185 first = false;
186 } else {
187 buf.append(", ");
188 }
189 buf.append(i);
190 buf.append("->");
191 buf.append(val.toString());
192 }
193 }
194
195 buf.append("}");
196
197 return buf.toString();
198 }
199 }
200 }
+0
-346
src/obsolete/edu/umd/cs/findbugs/ba/npe2/DefinitelyNullSetAnalysis.java less more
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2006, University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.ba.npe2;
20
21 import java.util.BitSet;
22 import java.util.HashMap;
23 import java.util.Map;
24
25 import org.apache.bcel.Constants;
26 import org.apache.bcel.generic.InstructionHandle;
27
28 import edu.umd.cs.findbugs.ba.BasicBlock;
29 import edu.umd.cs.findbugs.ba.CompactLocationNumbering;
30 import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
31 import edu.umd.cs.findbugs.ba.DepthFirstSearch;
32 import edu.umd.cs.findbugs.ba.Edge;
33 import edu.umd.cs.findbugs.ba.ForwardDataflowAnalysis;
34 import edu.umd.cs.findbugs.ba.Location;
35 import edu.umd.cs.findbugs.ba.vna.ValueNumber;
36 import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow;
37 import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;
38
39 /**
40 * A simple null-pointer analysis that keeps track of which value numbers are
41 * definitely known to be null.
42 *
43 * @author David Hovemeyer
44 */
45 public class DefinitelyNullSetAnalysis extends ForwardDataflowAnalysis<DefinitelyNullSet> {
46 private ValueNumberDataflow vnaDataflow;
47
48 private Map<BasicBlock, Condition> conditionMap;
49
50 private static final BitSet IFNULL_OPCODE_SET = new BitSet();
51
52 private static final BitSet IFACMP_OPCODE_SET = new BitSet();
53
54 private static final BitSet REFCMP_OPCODE_SET = new BitSet();
55 static {
56 IFNULL_OPCODE_SET.set(Constants.IFNULL);
57 IFNULL_OPCODE_SET.set(Constants.IFNONNULL);
58
59 IFACMP_OPCODE_SET.set(Constants.IF_ACMPEQ);
60 IFACMP_OPCODE_SET.set(Constants.IF_ACMPNE);
61
62 REFCMP_OPCODE_SET.or(IFNULL_OPCODE_SET);
63 REFCMP_OPCODE_SET.or(IFACMP_OPCODE_SET);
64 }
65
66 /**
67 * Constructor.
68 *
69 * @param dfs
70 * DepthFirstSearch for the method
71 * @param vnaDataflow
72 * value number dataflow for the method
73 * @param compactLocationNumbering
74 * CompactLocationNumbering for the method
75 */
76
77 // TODO: compactLocationNumbering is ignored. Why?
78 public DefinitelyNullSetAnalysis(DepthFirstSearch dfs, ValueNumberDataflow vnaDataflow,
79 CompactLocationNumbering compactLocationNumbering) {
80 super(dfs);
81 this.vnaDataflow = vnaDataflow;
82 this.conditionMap = new HashMap<BasicBlock, Condition>();
83 }
84
85 /*
86 * (non-Javadoc)
87 *
88 * @see
89 * edu.umd.cs.findbugs.ba.AbstractDataflowAnalysis#isFactValid(java.lang
90 * .Object)
91 */
92 @Override
93 public boolean isFactValid(DefinitelyNullSet fact) {
94 return fact.isValid();
95 }
96
97 /*
98 * (non-Javadoc)
99 *
100 * @see
101 * edu.umd.cs.findbugs.ba.AbstractDataflowAnalysis#transferInstruction(org
102 * .apache.bcel.generic.InstructionHandle,
103 * edu.umd.cs.findbugs.ba.BasicBlock, java.lang.Object)
104 */
105 @Override
106 public void transferInstruction(InstructionHandle handle, BasicBlock basicBlock, DefinitelyNullSet fact)
107 throws DataflowAnalysisException {
108 Location location = new Location(handle, basicBlock);
109 ValueNumberFrame vnaFrame = vnaDataflow.getFactAfterLocation(location);
110
111 if (!vnaFrame.isValid()) {
112 fact.setTop();
113 return;
114 }
115
116 // ACONST_NULL obviously produces a value that is DEFINITELY NULL.
117 // LDC produces values that are NOT NULL.
118 // NEW produces values that are NOT NULL.
119
120 short opcode = handle.getInstruction().getOpcode();
121
122 if (opcode == Constants.ACONST_NULL) {
123 setTOS(vnaFrame, location, fact, NullnessValue.definitelyNullValue());
124 } else if (opcode == Constants.LDC || opcode == Constants.NEW) {
125 setTOS(vnaFrame, location, fact, NullnessValue.definitelyNotNullValue());
126 }
127
128 // TODO: for method invocations, check return annotation
129
130 // Refresh condition/decision information
131 if (handle == basicBlock.getLastInstruction() && REFCMP_OPCODE_SET.get(opcode)) {
132 Condition condition = getCondition(basicBlock);
133 if (condition != null) {
134 // System.out.println("handle: " + handle);
135 condition.refresh(vnaDataflow.getFactAtLocation(location), fact);
136 }
137 }
138 }
139
140 /**
141 * Get the ConditionDecision providing information about the branch at the
142 * end of the given basic block.
143 *
144 * @param basicBlock
145 * @return the ConditionDecision, or null if the basic block does not end in
146 * a reference comparison
147 */
148 private Condition getCondition(BasicBlock basicBlock) throws DataflowAnalysisException {
149 Condition condition = conditionMap.get(basicBlock);
150 if (condition == null) {
151 Location location = new Location(basicBlock.getLastInstruction(), basicBlock);
152 short opcode = basicBlock.getLastInstruction().getInstruction().getOpcode();
153 if (IFNULL_OPCODE_SET.get(opcode)) {
154 condition = new IfNullCondition(location);
155 } else if (IFACMP_OPCODE_SET.get(opcode)) {
156 // condition = new AcmpCondition(location);
157 return null;
158 } else {
159 return null;
160 }
161 conditionMap.put(basicBlock, condition);
162 }
163 return condition;
164 }
165
166 /*
167 * (non-Javadoc)
168 *
169 * @see
170 * edu.umd.cs.findbugs.ba.BasicAbstractDataflowAnalysis#edgeTransfer(edu
171 * .umd.cs.findbugs.ba.Edge, java.lang.Object)
172 */
173 @Override
174 public void edgeTransfer(Edge edge, DefinitelyNullSet fact) throws DataflowAnalysisException {
175 if (!fact.isValid()) {
176 return;
177 }
178
179 if (edge.getSource().isEmpty()) {
180 return;
181 }
182
183 Condition condition = getCondition(edge.getSource());
184 if (condition == null) {
185 return;
186 }
187
188 Decision decision = condition.getDecision(edge);
189 if (!decision.isFeasible()) {
190 // This edge is not feasible.
191 // Set fact to TOP to avoid polluting dataflow information
192 // at a future control merge.
193 fact.setTop();
194 return;
195 }
196
197 // System.out.println("Setting " + condition.getValueNumber() + " to " +
198 // decision.getNullnessValue() + " on edge " + edge);
199
200 changeNullnessOfValue(condition.getValueNumber(), condition.getLocation(), fact, decision.getNullnessValue());
201 }
202
203 /**
204 * Set the value on top of the stack as either null or unknown.
205 *
206 * @param vnaFrame
207 * ValueNumberFrame at Location
208 * @param location
209 * the Location
210 * @param fact
211 * DefinitelyNullSet to modify
212 * @param nullnessValue
213 * the nullness of the value number
214 * @throws DataflowAnalysisException
215 */
216 private void setTOS(ValueNumberFrame vnaFrame, Location location, DefinitelyNullSet fact, NullnessValue nullnessValue)
217 throws DataflowAnalysisException {
218 ValueNumber valueNumber = vnaFrame.getTopValue();
219 changeNullnessOfValue(valueNumber, location, fact, nullnessValue);
220 }
221
222 /**
223 * Change the nullness of a value number.
224 *
225 * @param valueNumber
226 * the ValueNumber
227 * @param location
228 * Location where information is gained
229 * @param fact
230 * the DefinitelyNullSet to modify
231 * @param nullnessValue
232 * the nullness of the value number
233 * @throws DataflowAnalysisException
234 */
235 private void changeNullnessOfValue(ValueNumber valueNumber, Location location, DefinitelyNullSet fact,
236 NullnessValue nullnessValue) throws DataflowAnalysisException {
237 // fact.setValue(valueNumber, isNull);
238 // if (isNull) {
239 // fact.addAssignedNullLocation(valueNumber.getNumber(),
240 // compactLocationNumbering.getNumber(location));
241 // } else {
242 // fact.clearAssignNullLocations(valueNumber.getNumber());
243 // }
244
245 fact.setNullnessValue(valueNumber, nullnessValue);
246
247 if (fact.getNulllessValue(valueNumber) != nullnessValue) {
248 throw new IllegalStateException();
249 }
250
251 // TODO: set location where value becomes null or non-null
252 }
253
254 /*
255 * (non-Javadoc)
256 *
257 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#copy(java.lang.Object,
258 * java.lang.Object)
259 */
260 public void copy(DefinitelyNullSet source, DefinitelyNullSet dest) {
261 dest.makeSameAs(source);
262 }
263
264 /*
265 * (non-Javadoc)
266 *
267 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#createFact()
268 */
269 public DefinitelyNullSet createFact() {
270 // TODO: optionally create one with null locations
271 return new DefinitelyNullSet(vnaDataflow.getAnalysis().getNumValuesAllocated());
272 }
273
274 /*
275 * (non-Javadoc)
276 *
277 * @see
278 * edu.umd.cs.findbugs.ba.DataflowAnalysis#initEntryFact(java.lang.Object)
279 */
280 public void initEntryFact(DefinitelyNullSet result) throws DataflowAnalysisException {
281 // TODO: parameter annotations?
282 result.clear();
283 }
284
285 /*
286 * (non-Javadoc)
287 *
288 * @see
289 * edu.umd.cs.findbugs.ba.DataflowAnalysis#makeFactTop(java.lang.Object)
290 */
291 public void makeFactTop(DefinitelyNullSet fact) {
292 fact.setTop();
293 }
294
295 public boolean isTop(DefinitelyNullSet fact) {
296 return fact.isTop();
297 }
298
299 /*
300 * (non-Javadoc)
301 *
302 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#meetInto(java.lang.Object,
303 * edu.umd.cs.findbugs.ba.Edge, java.lang.Object)
304 */
305 public void meetInto(DefinitelyNullSet fact, Edge edge, DefinitelyNullSet result) throws DataflowAnalysisException {
306 // TODO: use edge information (ifnull, ifnonnull, etc.)
307
308 result.mergeWith(fact);
309 }
310
311 /*
312 * (non-Javadoc)
313 *
314 * @see edu.umd.cs.findbugs.ba.DataflowAnalysis#same(java.lang.Object,
315 * java.lang.Object)
316 */
317 public boolean same(DefinitelyNullSet fact1, DefinitelyNullSet fact2) {
318 return fact1.equals(fact2);
319 }
320
321 // public static void main(String[] args) throws Exception {
322 // if (args.length != 1) {
323 // System.err.println("Usage: " + DefinitelyNullSetAnalysis.class.getName()
324 // + " <classfile>");
325 // System.exit(1);
326 // }
327 //
328 // DataflowTestDriver<DefinitelyNullSet, DefinitelyNullSetAnalysis> driver =
329 // new DataflowTestDriver<DefinitelyNullSet, DefinitelyNullSetAnalysis>() {
330 // /* (non-Javadoc)
331 // * @see
332 // edu.umd.cs.findbugs.ba.DataflowTestDriver#createDataflow(edu.umd.cs.findbugs.ba.ClassContext,
333 // org.apache.bcel.classfile.Method)
334 // */
335 // @Override
336 // public Dataflow<DefinitelyNullSet, DefinitelyNullSetAnalysis>
337 // createDataflow(ClassContext classContext, Method method) throws
338 // CFGBuilderException, DataflowAnalysisException {
339 // return classContext.getDefinitelyNullSetDataflow(method);
340 // }
341 // };
342 //
343 // driver.execute(args[0]);
344 // }
345 }
+0
-29
src/obsolete/edu/umd/cs/findbugs/ba/npe2/DefinitelyNullSetDataflow.java less more
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2006, University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.ba.npe2;
20
21 import edu.umd.cs.findbugs.ba.AbstractDataflow;
22 import edu.umd.cs.findbugs.ba.CFG;
23
24 public class DefinitelyNullSetDataflow extends AbstractDataflow<DefinitelyNullSet, DefinitelyNullSetAnalysis> {
25 public DefinitelyNullSetDataflow(CFG cfg, DefinitelyNullSetAnalysis analysis) {
26 super(cfg, analysis);
27 }
28 }
+0
-62
src/obsolete/edu/umd/cs/findbugs/ba/npe2/DefinitelyNullSetDataflowFactory.java less more
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2003-2007 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 package edu.umd.cs.findbugs.ba.npe2;
19
20 import edu.umd.cs.findbugs.ba.CFG;
21 import edu.umd.cs.findbugs.ba.CompactLocationNumbering;
22 import edu.umd.cs.findbugs.ba.DepthFirstSearch;
23 import edu.umd.cs.findbugs.ba.vna.ValueNumberDataflow;
24 import edu.umd.cs.findbugs.classfile.CheckedAnalysisException;
25 import edu.umd.cs.findbugs.classfile.IAnalysisCache;
26 import edu.umd.cs.findbugs.classfile.MethodDescriptor;
27 import edu.umd.cs.findbugs.classfile.engine.bcel.AnalysisFactory;
28
29 /**
30 * Analysis engine to produce DefinitelyNullSetDataflow objects for analyzed
31 * methods.
32 *
33 * @author David Hovemeyer
34 */
35 public class DefinitelyNullSetDataflowFactory extends AnalysisFactory<DefinitelyNullSetDataflow> {
36 public DefinitelyNullSetDataflowFactory() {
37 super("definitely null set dataflow", DefinitelyNullSetDataflow.class);
38 }
39
40 /*
41 * (non-Javadoc)
42 *
43 * @see
44 * edu.umd.cs.findbugs.classfile.IAnalysisEngine#analyze(edu.umd.cs.findbugs
45 * .classfile.IAnalysisCache, java.lang.Object)
46 */
47 public DefinitelyNullSetDataflow analyze(IAnalysisCache analysisCache, MethodDescriptor descriptor)
48 throws CheckedAnalysisException {
49 CFG cfg = getCFG(analysisCache, descriptor);
50 DepthFirstSearch dfs = getDepthFirstSearch(analysisCache, descriptor);
51 ValueNumberDataflow vnaDataflow = getValueNumberDataflow(analysisCache, descriptor);
52 CompactLocationNumbering compactLocationNumbering = getCompactLocationNumbering(analysisCache, descriptor);
53
54 DefinitelyNullSetAnalysis analysis = new DefinitelyNullSetAnalysis(dfs, vnaDataflow, compactLocationNumbering);
55 DefinitelyNullSetDataflow dataflow = new DefinitelyNullSetDataflow(cfg, analysis);
56
57 dataflow.execute();
58
59 return dataflow;
60 }
61 }
+0
-100
src/obsolete/edu/umd/cs/findbugs/ba/npe2/IfNullCondition.java less more
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2006, University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.ba.npe2;
20
21 import org.apache.bcel.Constants;
22
23 import edu.umd.cs.findbugs.ba.DataflowAnalysisException;
24 import edu.umd.cs.findbugs.ba.Edge;
25 import edu.umd.cs.findbugs.ba.EdgeTypes;
26 import edu.umd.cs.findbugs.ba.Location;
27 import edu.umd.cs.findbugs.ba.vna.ValueNumber;
28 import edu.umd.cs.findbugs.ba.vna.ValueNumberFrame;
29
30 /**
31 * @author David Hovemeyer
32 */
33 public class IfNullCondition extends Condition {
34 private ValueNumber valueNumber;
35
36 private Decision ifcmpDecision;
37
38 private Decision fallThroughDecision;
39
40 public IfNullCondition(Location location) {
41 super(location);
42 }
43
44 /*
45 * (non-Javadoc)
46 *
47 * @see
48 * edu.umd.cs.findbugs.ba.npe2.Condition#getDecision(edu.umd.cs.findbugs
49 * .ba.Edge)
50 */
51 @Override
52 public Decision getDecision(Edge edge) {
53 return edge.getType() == EdgeTypes.IFCMP_EDGE ? ifcmpDecision : fallThroughDecision;
54 }
55
56 /*
57 * (non-Javadoc)
58 *
59 * @see edu.umd.cs.findbugs.ba.npe2.Condition#getValueNumber()
60 */
61 @Override
62 public ValueNumber getValueNumber() {
63 return valueNumber;
64 }
65
66 /*
67 * (non-Javadoc)
68 *
69 * @see
70 * edu.umd.cs.findbugs.ba.npe2.Condition#refresh(edu.umd.cs.findbugs.ba.
71 * vna.ValueNumberFrame, edu.umd.cs.findbugs.ba.npe2.DefinitelyNullSet)
72 */
73 @Override
74 public void refresh(ValueNumberFrame vnaFrame, DefinitelyNullSet definitelyNullSet) throws DataflowAnalysisException {
75 valueNumber = vnaFrame.getTopValue();
76
77 NullnessValue nullnessValue = definitelyNullSet.getNulllessValue(valueNumber);
78 short opcode = getLocation().getHandle().getInstruction().getOpcode();
79
80 if (nullnessValue.isDefinitelyNull() || nullnessValue.isDefinitelyNotNull()) {
81 // Comparison is redundant.
82
83 boolean ifcmpFeasible = nullnessValue.isDefinitelyNull() == (opcode == Constants.IFNULL);
84 ifcmpDecision = new Decision(ifcmpFeasible, ifcmpFeasible ? nullnessValue.toCheckedValue() : null);
85
86 boolean fallThroughFeasible = nullnessValue.isDefinitelyNull() != (opcode == Constants.IFNONNULL);
87 fallThroughDecision = new Decision(fallThroughFeasible, fallThroughFeasible ? nullnessValue.toCheckedValue() : null);
88
89 return;
90 }
91
92 NullnessValue definitelyNull = NullnessValue.definitelyNullValue().toCheckedValue();
93 NullnessValue definitelyNotNull = NullnessValue.definitelyNotNullValue().toCheckedValue();
94
95 // Nullness is unknown, assume both branches are feasible.
96 ifcmpDecision = new Decision(true, (opcode == Constants.IFNULL) ? definitelyNull : definitelyNotNull);
97 fallThroughDecision = new Decision(true, (opcode == Constants.IFNULL) ? definitelyNotNull : definitelyNull);
98 }
99 }
+0
-144
src/obsolete/edu/umd/cs/findbugs/ba/npe2/NullnessValue.java less more
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2006, University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.ba.npe2;
20
21 /**
22 * Symbolic values representing the nullness of a runtime value.
23 *
24 * @author David Hovemeyer
25 */
26 public class NullnessValue {
27 static final int DEFINITELY_NULL = 0;
28
29 static final int DEFINITELY_NOT_NULL = 1;
30
31 static final int CHECKED = 2;
32
33 static final int NO_KABOOM = 3;
34
35 static final int FLAGS_MAX = 4;
36
37 private static final NullnessValue[] instanceList = new NullnessValue[1 << FLAGS_MAX];
38 static {
39 for (int i = 0; i < instanceList.length; i++) {
40 instanceList[i] = new NullnessValue(i);
41 }
42 }
43
44 private final int flags;
45
46 private NullnessValue(int flags) {
47 this.flags = flags;
48 }
49
50 int getFlags() {
51 return flags;
52 }
53
54 public boolean isDefinitelyNull() {
55 return isFlagSet(DEFINITELY_NULL);
56 }
57
58 public boolean isDefinitelyNotNull() {
59 return isFlagSet(DEFINITELY_NOT_NULL);
60 }
61
62 public boolean isChecked() {
63 return isFlagSet(CHECKED);
64 }
65
66 public boolean isNoKaboom() {
67 return isFlagSet(NO_KABOOM);
68 }
69
70 public NullnessValue toCheckedValue() {
71 return instanceList[flags | (1 << CHECKED)];
72 }
73
74 public NullnessValue toNoKaboomValue() {
75 return instanceList[flags | (1 << NO_KABOOM)];
76 }
77
78 // public NullnessValue toCheckedNullValue() {
79 // if (isDefinitelyNull() || isDefinitelyNotNull()) {
80 // throw new IllegalStateException();
81 // }
82 //
83 // return fromFlags(flags | DEFINITELY_NULL | CHECKED);
84 // }
85 //
86 // public NullnessValue toCheckedNotNullValue() {
87 // if (isDefinitelyNull() || isDefinitelyNotNull()) {
88 // throw new IllegalStateException();
89 // }
90 //
91 // return fromFlags(flags | DEFINITELY_NOT_NULL | CHECKED);
92 // }
93
94 private boolean isFlagSet(int flag) {
95 return (flags & (1 << flag)) != 0;
96 }
97
98 static NullnessValue fromFlags(int flags) {
99 return instanceList[flags];
100 }
101
102 public static NullnessValue definitelyNullValue() {
103 return fromFlags(1 << DEFINITELY_NULL);
104 }
105
106 public static NullnessValue definitelyNotNullValue() {
107 return fromFlags(1 << DEFINITELY_NOT_NULL);
108 }
109
110 public static NullnessValue unknownValue() {
111 return fromFlags(0);
112 }
113
114 /*
115 * (non-Javadoc)
116 *
117 * @see java.lang.Object#toString()
118 */
119 @Override
120 public String toString() {
121 String pfx = "";
122
123 if (isChecked()) {
124 pfx += "c";
125 }
126
127 if (isNoKaboom()) {
128 pfx += "k";
129 }
130
131 String val;
132
133 if (isDefinitelyNull()) {
134 val = "n";
135 } else if (isDefinitelyNotNull()) {
136 val = "N";
137 } else {
138 val = "-";
139 }
140
141 return pfx + val;
142 }
143 }
+0
-44
src/obsolete/edu/umd/cs/findbugs/ba/npe2/TestDefinitelyNullSet.java less more
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2006, University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.ba.npe2;
20
21 import junit.framework.Assert;
22 import junit.framework.TestCase;
23
24 /**
25 * @author David Hovemeyer
26 */
27 public class TestDefinitelyNullSet extends TestCase {
28 DefinitelyNullSet s;
29
30 /*
31 * (non-Javadoc)
32 *
33 * @see junit.framework.TestCase#setUp()
34 */
35 @Override
36 protected void setUp() throws Exception {
37 s = new DefinitelyNullSet(10);
38 }
39
40 public void testEmptyToString() {
41 Assert.assertEquals("{}", s.toString());
42 }
43 }
+0
-43
src/obsolete/edu/umd/cs/findbugs/ba/npe2/TestNullnessValue.java less more
0 /*
1 * FindBugs - Find Bugs in Java programs
2 * Copyright (C) 2006, University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.ba.npe2;
20
21 import junit.framework.Assert;
22 import junit.framework.TestCase;
23
24 /**
25 * @author David Hovemeyer
26 */
27 public class TestNullnessValue extends TestCase {
28 NullnessValue definitelyNull;
29
30 @Override
31 protected void setUp() {
32 definitelyNull = NullnessValue.definitelyNullValue();
33 }
34
35 public void testDefinitelyNullToString() {
36 Assert.assertEquals("n", definitelyNull.toString());
37 }
38
39 public void testDefinitelyNullToCheckedString() {
40 Assert.assertEquals("cn", definitelyNull.toCheckedValue().toString());
41 }
42 }
+0
-23
src/obsolete/edu/umd/cs/findbugs/ba/npe2/package.html less more
0 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
1 <html>
2 <head>
3 </head>
4
5 <body bgcolor="white">
6 <p>
7 This package is a rewrite of the null-pointer analysis to use
8 sets of value numbers rather than frames. The main advantage
9 of this approach is that it requires no special code to handle
10 the case where a value is stored in the heap and re-appears
11 in a subsequent field load.
12 </p>
13
14 <p>
15 This isn't ready for prime time, but may be in the future.
16 Or maybe we'll rewrite the entire dataflow analysis package
17 to use a different bytecode framework, and it will never
18 be used. Hard to say for sure.
19 </p>
20 </body>
21
22 </html>
+0
-115
src/obsolete/edu/umd/cs/findbugs/gui/AboutDialog.form less more
0 <?xml version="1.0" encoding="UTF-8" ?>
1
2 <Form version="1.0" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
3 <SyntheticProperties>
4 <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
5 </SyntheticProperties>
6 <Events>
7 <EventHandler event="windowClosing" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="closeDialog"/>
8 </Events>
9
10 <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
11 <SubComponents>
12 <Container class="javax.swing.JTabbedPane" name="aboutTabPane">
13 <Constraints>
14 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
15 <GridBagConstraints gridX="-1" gridY="-1" gridWidth="1" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="1.0"/>
16 </Constraint>
17 </Constraints>
18
19 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
20 <SubComponents>
21 <Container class="javax.swing.JScrollPane" name="aboutScrollPane">
22 <Constraints>
23 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
24 <JTabbedPaneConstraints tabName="About">
25 <Property name="tabTitle" type="java.lang.String" value="About"/>
26 </JTabbedPaneConstraints>
27 </Constraint>
28 </Constraints>
29
30 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
31 <SubComponents>
32 <Component class="javax.swing.JEditorPane" name="aboutEditorPane">
33 <Properties>
34 <Property name="editable" type="boolean" value="false"/>
35 </Properties>
36 <Events>
37 <EventHandler event="hyperlinkUpdate" listener="javax.swing.event.HyperlinkListener" parameters="javax.swing.event.HyperlinkEvent" handler="editorPaneHyperlinkUpdate"/>
38 </Events>
39 </Component>
40 </SubComponents>
41 </Container>
42 <Container class="javax.swing.JScrollPane" name="licenseScrollPane">
43 <Constraints>
44 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
45 <JTabbedPaneConstraints tabName="License">
46 <Property name="tabTitle" type="java.lang.String" value="License"/>
47 </JTabbedPaneConstraints>
48 </Constraint>
49 </Constraints>
50
51 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
52 <SubComponents>
53 <Component class="javax.swing.JEditorPane" name="licenseEditorPane">
54 <Properties>
55 <Property name="editable" type="boolean" value="false"/>
56 </Properties>
57 <Events>
58 <EventHandler event="hyperlinkUpdate" listener="javax.swing.event.HyperlinkListener" parameters="javax.swing.event.HyperlinkEvent" handler="editorPaneHyperlinkUpdate"/>
59 </Events>
60 </Component>
61 </SubComponents>
62 </Container>
63 <Container class="javax.swing.JScrollPane" name="acknowledgmentsScrollPane">
64 <Constraints>
65 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
66 <JTabbedPaneConstraints tabName="Acknowledgments">
67 <Property name="tabTitle" type="java.lang.String" value="Acknowledgments"/>
68 </JTabbedPaneConstraints>
69 </Constraint>
70 </Constraints>
71
72 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
73 <SubComponents>
74 <Component class="javax.swing.JEditorPane" name="acknowldgementsEditorPane">
75 <Properties>
76 <Property name="editable" type="boolean" value="false"/>
77 </Properties>
78 <Events>
79 <EventHandler event="hyperlinkUpdate" listener="javax.swing.event.HyperlinkListener" parameters="javax.swing.event.HyperlinkEvent" handler="editorPaneHyperlinkUpdate"/>
80 </Events>
81 </Component>
82 </SubComponents>
83 </Container>
84 </SubComponents>
85 </Container>
86 <Component class="javax.swing.JSeparator" name="jSeparator1">
87 <AuxValues>
88 <AuxValue name="JavaCodeGenerator_InitCodePre" type="java.lang.String" value="{&#xa; aboutTabPane.setTitleAt(0, L10N.getLocalString(&quot;dlg.about_tab&quot;, &quot;About&quot;));&#xa; aboutTabPane.setTitleAt(1, L10N.getLocalString(&quot;dlg.license_tab&quot;, &quot;License&quot;));&#xa; aboutTabPane.setTitleAt(2, L10N.getLocalString(&quot;dlg.acknowledgements_tab&quot;, &quot;Acknowledgements&quot;)); &#xa;}"/>
89 </AuxValues>
90 <Constraints>
91 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
92 <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
93 </Constraint>
94 </Constraints>
95 </Component>
96 <Component class="javax.swing.JButton" name="okButton">
97 <Properties>
98 <Property name="mnemonic" type="int" value="79"/>
99 <Property name="text" type="java.lang.String" value="OK"/>
100 </Properties>
101 <Events>
102 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="okButtonActionPerformed"/>
103 </Events>
104 <AuxValues>
105 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="okButton.setText(L10N.getLocalString(&quot;dlg.ok_btn&quot;, &quot;OK&quot;));"/>
106 </AuxValues>
107 <Constraints>
108 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
109 <GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="0" insetsBottom="3" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
110 </Constraint>
111 </Constraints>
112 </Component>
113 </SubComponents>
114 </Form>
+0
-255
src/obsolete/edu/umd/cs/findbugs/gui/AboutDialog.java less more
0 /*
1 * FindBugs - Find bugs in Java programs
2 * Copyright (C) 2003,2004 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 /*
20 * AboutDialog.java
21 *
22 * Created on April 6, 2003, 7:05 PM
23 */
24
25 package edu.umd.cs.findbugs.gui;
26
27 import java.io.BufferedReader;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.net.URL;
31 import java.text.MessageFormat;
32 import java.util.regex.Pattern;
33
34 import javax.swing.JFrame;
35 import javax.swing.event.HyperlinkEvent;
36
37 import edu.umd.cs.findbugs.L10N;
38 import edu.umd.cs.findbugs.Version;
39 import edu.umd.cs.findbugs.log.ConsoleLogger;
40 import edu.umd.cs.findbugs.log.Logger;
41 import edu.umd.cs.findbugs.util.LaunchBrowser;
42 import edu.umd.cs.findbugs.util.Util;
43
44 /**
45 * The Help:About dialog.
46 *
47 * @author David Hovemeyer
48 */
49 public class AboutDialog extends javax.swing.JDialog {
50 private static final long serialVersionUID = 3546076956480385584L;
51
52 //private JFrame parent;
53
54 /**
55 * Creates new form AboutDialog
56 */
57 public AboutDialog(JFrame parent, Logger l, boolean modal) {
58 super(parent, modal);
59 //this.parent = parent;
60
61 initComponents();
62
63 try {
64 aboutEditorPane.setPage(getClass().getClassLoader().getResource("edu/umd/cs/findbugs/gui/help/About.html"));
65 licenseEditorPane.setPage(getClass().getClassLoader().getResource("edu/umd/cs/findbugs/gui/help/License.html"));
66 acknowldgementsEditorPane.setPage(getClass().getClassLoader().getResource("edu/umd/cs/findbugs/gui/help/Acknowledgements.html"));
67 } catch (IOException e) {
68 l.logMessage(ConsoleLogger.ERROR, e.toString());
69 }
70
71 setTitle(MessageFormat.format(L10N.getLocalString("dlg.aboutfindbugs_ttl", "About FindBugs {0}"), new Object[]{Version.RELEASE}));
72 }
73
74 static Pattern pattern = Pattern.compile("@VERSION@");
75 /**
76 * Process an HTML page to replace certain substitution patterns.
77 * Right now, we just expand @VERSION@.
78 */
79 @edu.umd.cs.findbugs.annotations.SuppressWarnings("OS_OPEN_STREAM")
80 private void processPage(javax.swing.JEditorPane pane, String fileName) throws IOException {
81 InputStream in = null;
82 BufferedReader reader = null;
83 try {
84 StringBuilder buf = new StringBuilder();
85
86 // Open the file as a stream
87 in = getClass().getClassLoader().getResourceAsStream(fileName);
88 if (in == null)
89 throw new IOException(MessageFormat.format(L10N.getLocalString("msg.couldntload_txt", "Couldn't load {0}"), new Object[]{fileName}));
90 reader = new BufferedReader(Util.getReader(in));
91
92 // Replace instances of @VERSION@ with actual version number
93
94 String line;
95 while ((line = reader.readLine()) != null) {
96 line = pattern.matcher(line).replaceAll(Version.RELEASE);
97 buf.append(line);
98 buf.append('\n');
99 }
100
101 // Load the page into the editor pane
102 String text = buf.toString();
103 pane.setContentType("text/html");
104 pane.setText(text);
105 } finally {
106 try {
107 if (reader != null)
108 reader.close();
109 else if (in != null)
110 in.close();
111 } catch (IOException e) {
112 }
113 }
114 }
115
116 /**
117 * This method is called from within the constructor to
118 * initialize the form.
119 * WARNING: Do NOT modify this code. The content of this method is
120 * always regenerated by the Form Editor.
121 */
122 private void initComponents() {//GEN-BEGIN:initComponents
123 java.awt.GridBagConstraints gridBagConstraints;
124
125 aboutTabPane = new javax.swing.JTabbedPane();
126 aboutScrollPane = new javax.swing.JScrollPane();
127 aboutEditorPane = new javax.swing.JEditorPane();
128 licenseScrollPane = new javax.swing.JScrollPane();
129 licenseEditorPane = new javax.swing.JEditorPane();
130 acknowledgmentsScrollPane = new javax.swing.JScrollPane();
131 acknowldgementsEditorPane = new javax.swing.JEditorPane();
132 jSeparator1 = new javax.swing.JSeparator();
133 okButton = new javax.swing.JButton();
134
135 getContentPane().setLayout(new java.awt.GridBagLayout());
136
137 addWindowListener(new java.awt.event.WindowAdapter() {
138 @Override
139 public void windowClosing(java.awt.event.WindowEvent evt) {
140 closeDialog(evt);
141 }
142 });
143
144 aboutEditorPane.setEditable(false);
145 aboutEditorPane.addHyperlinkListener(new javax.swing.event.HyperlinkListener() {
146 public void hyperlinkUpdate(javax.swing.event.HyperlinkEvent evt) {
147 editorPaneHyperlinkUpdate(evt);
148 }
149 });
150
151 aboutScrollPane.setViewportView(aboutEditorPane);
152
153 aboutTabPane.addTab("About", aboutScrollPane);
154
155 licenseEditorPane.setEditable(false);
156 licenseEditorPane.addHyperlinkListener(new javax.swing.event.HyperlinkListener() {
157 public void hyperlinkUpdate(javax.swing.event.HyperlinkEvent evt) {
158 editorPaneHyperlinkUpdate(evt);
159 }
160 });
161
162 licenseScrollPane.setViewportView(licenseEditorPane);
163
164 aboutTabPane.addTab("License", licenseScrollPane);
165
166 acknowldgementsEditorPane.setEditable(false);
167 acknowldgementsEditorPane.addHyperlinkListener(new javax.swing.event.HyperlinkListener() {
168 public void hyperlinkUpdate(javax.swing.event.HyperlinkEvent evt) {
169 editorPaneHyperlinkUpdate(evt);
170 }
171 });
172
173 acknowledgmentsScrollPane.setViewportView(acknowldgementsEditorPane);
174
175 aboutTabPane.addTab("Acknowledgments", acknowledgmentsScrollPane);
176
177 gridBagConstraints = new java.awt.GridBagConstraints();
178 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
179 gridBagConstraints.weightx = 1.0;
180 gridBagConstraints.weighty = 1.0;
181 getContentPane().add(aboutTabPane, gridBagConstraints);
182
183 {
184 aboutTabPane.setTitleAt(0, L10N.getLocalString("dlg.about_tab", "About"));
185 aboutTabPane.setTitleAt(1, L10N.getLocalString("dlg.license_tab", "License"));
186 aboutTabPane.setTitleAt(2, L10N.getLocalString("dlg.acknowledgements_tab", "Acknowledgements"));
187 }
188 gridBagConstraints = new java.awt.GridBagConstraints();
189 gridBagConstraints.gridx = 0;
190 gridBagConstraints.gridy = 1;
191 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
192 getContentPane().add(jSeparator1, gridBagConstraints);
193
194 okButton.setMnemonic('O');
195 okButton.setText("OK");
196 okButton.setText(L10N.getLocalString("dlg.ok_btn", "OK"));
197 okButton.addActionListener(new java.awt.event.ActionListener() {
198 public void actionPerformed(java.awt.event.ActionEvent evt) {
199 okButtonActionPerformed(evt);
200 }
201 });
202
203 gridBagConstraints = new java.awt.GridBagConstraints();
204 gridBagConstraints.gridx = 0;
205 gridBagConstraints.gridy = 2;
206 gridBagConstraints.insets = new java.awt.Insets(3, 0, 3, 0);
207 getContentPane().add(okButton, gridBagConstraints);
208
209 pack();
210 }//GEN-END:initComponents
211
212 private void editorPaneHyperlinkUpdate(javax.swing.event.HyperlinkEvent evt) {//GEN-FIRST:event_editorPaneHyperlinkUpdate
213 try {
214 if (evt.getEventType().equals( HyperlinkEvent.EventType.ACTIVATED)) {
215 URL url = evt.getURL();
216 LaunchBrowser.showDocument(url);
217 }
218 }
219 catch (Exception e) {
220 }
221 }//GEN-LAST:event_editorPaneHyperlinkUpdate
222
223
224 private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
225 closeDialog();
226 }//GEN-LAST:event_okButtonActionPerformed
227
228 /**
229 * Closes the dialog
230 */
231 private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog
232 closeDialog();
233 }//GEN-LAST:event_closeDialog
234
235 private void closeDialog() {
236 setVisible(false);
237 dispose();
238 }
239
240 // Variables declaration - do not modify//GEN-BEGIN:variables
241 private javax.swing.JEditorPane aboutEditorPane;
242 private javax.swing.JScrollPane aboutScrollPane;
243 private javax.swing.JTabbedPane aboutTabPane;
244 private javax.swing.JEditorPane acknowldgementsEditorPane;
245 private javax.swing.JScrollPane acknowledgmentsScrollPane;
246 private javax.swing.JSeparator jSeparator1;
247 private javax.swing.JEditorPane licenseEditorPane;
248 private javax.swing.JScrollPane licenseScrollPane;
249 private javax.swing.JButton okButton;
250 // End of variables declaration//GEN-END:variables
251
252 }
253
254 // vim:ts=4
+0
-154
src/obsolete/edu/umd/cs/findbugs/gui/AnalysisErrorDialog.form less more
0 <?xml version="1.0" encoding="UTF-8" ?>
1
2 <Form version="1.0" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
3 <NonVisualComponents>
4 <Menu class="javax.swing.JMenuBar" name="analysisMenuBar">
5 <Properties>
6 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
7 <Font name="Dialog" size="12" style="0"/>
8 </Property>
9 </Properties>
10 <SubComponents>
11 <Menu class="javax.swing.JMenu" name="editMenu">
12 <Properties>
13 <Property name="text" type="java.lang.String" value="Edit"/>
14 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
15 <Font name="Dialog" size="12" style="0"/>
16 </Property>
17 </Properties>
18 <Events>
19 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="editMenuActionPerformed"/>
20 </Events>
21 <AuxValues>
22 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="L10N.localiseButton(editMenu, &quot;menu.edit_menu&quot;, &quot;&amp;Edit&quot;, true);"/>
23 </AuxValues>
24 <SubComponents>
25 <MenuItem class="javax.swing.JMenuItem" name="selectAllMenuItem">
26 <Properties>
27 <Property name="accelerator" type="javax.swing.KeyStroke" editor="org.netbeans.modules.form.editors.KeyStrokeEditor">
28 <KeyStroke key="Ctrl+A"/>
29 </Property>
30 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
31 <Font name="Dialog" size="12" style="0"/>
32 </Property>
33 <Property name="text" type="java.lang.String" value="Select All"/>
34 </Properties>
35 <Events>
36 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="selectAllItemActionListener"/>
37 </Events>
38 <AuxValues>
39 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="L10N.localiseButton(selectAllMenuItem, &quot;menu.selectall_item&quot;, &quot;Select &amp;All&quot;, true);"/>
40 </AuxValues>
41 </MenuItem>
42 <MenuItem class="javax.swing.JMenuItem" name="copyMenuItem">
43 <Properties>
44 <Property name="accelerator" type="javax.swing.KeyStroke" editor="org.netbeans.modules.form.editors.KeyStrokeEditor">
45 <KeyStroke key="Ctrl+C"/>
46 </Property>
47 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
48 <Font name="Dialog" size="12" style="0"/>
49 </Property>
50 <Property name="text" type="java.lang.String" value="Copy"/>
51 </Properties>
52 <Events>
53 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="copyMenuItemActionPerformed"/>
54 </Events>
55 <AuxValues>
56 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="L10N.localiseButton(copyMenuItem, &quot;menu.copy_item&quot;, &quot;Copy&quot;, true);"/>
57 </AuxValues>
58 </MenuItem>
59 </SubComponents>
60 </Menu>
61 </SubComponents>
62 </Menu>
63 </NonVisualComponents>
64 <Properties>
65 <Property name="title" type="java.lang.String" value="Analysis Errors" postCode="setTitle(L10N.getLocalString(&quot;dlg.analysiserrors_ttl&quot;, &quot;Analysis Errors&quot;));"/>
66 </Properties>
67 <SyntheticProperties>
68 <SyntheticProperty name="menuBar" type="java.lang.String" value="analysisMenuBar"/>
69 <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
70 </SyntheticProperties>
71 <Events>
72 <EventHandler event="windowClosing" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="closeDialog"/>
73 </Events>
74
75 <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
76 <SubComponents>
77 <Component class="javax.swing.JLabel" name="errorLabel">
78 <Properties>
79 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
80 <Font name="Dialog" size="12" style="0"/>
81 </Property>
82 <Property name="text" type="java.lang.String" value="Errors occured during the analysis:"/>
83 </Properties>
84 <AuxValues>
85 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="errorLabel.setText(L10N.getLocalString(&quot;dlg.analysiserror_lbl&quot;, &quot;Errors occurred during analysis:&quot;));"/>
86 </AuxValues>
87 <Constraints>
88 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
89 <GridBagConstraints gridX="0" gridY="0" gridWidth="3" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="6" insetsBottom="3" insetsRight="0" anchor="17" weightX="0.0" weightY="0.0"/>
90 </Constraint>
91 </Constraints>
92 </Component>
93 <Container class="javax.swing.JScrollPane" name="errorMessageScrollPane">
94 <Constraints>
95 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
96 <GridBagConstraints gridX="0" gridY="1" gridWidth="3" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="4" insetsLeft="6" insetsBottom="4" insetsRight="6" anchor="10" weightX="1.0" weightY="1.0"/>
97 </Constraint>
98 </Constraints>
99
100 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
101 <SubComponents>
102 <Component class="javax.swing.JTextPane" name="errorMessageTextArea">
103 <Properties>
104 <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
105 <Border info="org.netbeans.modules.form.compat2.border.BevelBorderInfo">
106 <BevelBorder bevelType="1"/>
107 </Border>
108 </Property>
109 <Property name="editable" type="boolean" value="false"/>
110 </Properties>
111 </Component>
112 </SubComponents>
113 </Container>
114 <Component class="javax.swing.JSeparator" name="jSeparator1">
115 <Constraints>
116 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
117 <GridBagConstraints gridX="0" gridY="2" gridWidth="3" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
118 </Constraint>
119 </Constraints>
120 </Component>
121 <Component class="javax.swing.JLabel" name="leftSpacer">
122 <Constraints>
123 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
124 <GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.5" weightY="0.0"/>
125 </Constraint>
126 </Constraints>
127 </Component>
128 <Component class="javax.swing.JLabel" name="rightSpacer">
129 <Constraints>
130 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
131 <GridBagConstraints gridX="2" gridY="3" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.5" weightY="0.0"/>
132 </Constraint>
133 </Constraints>
134 </Component>
135 <Component class="javax.swing.JButton" name="okButton">
136 <Properties>
137 <Property name="mnemonic" type="int" value="79"/>
138 <Property name="text" type="java.lang.String" value="OK"/>
139 </Properties>
140 <Events>
141 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="okButtonActionPerformed"/>
142 </Events>
143 <AuxValues>
144 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="okButton.setText(L10N.getLocalString(&quot;dlg.ok_btn&quot;, &quot;OK&quot;));"/>
145 </AuxValues>
146 <Constraints>
147 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
148 <GridBagConstraints gridX="1" gridY="3" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="0" insetsBottom="3" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
149 </Constraint>
150 </Constraints>
151 </Component>
152 </SubComponents>
153 </Form>
+0
-246
src/obsolete/edu/umd/cs/findbugs/gui/AnalysisErrorDialog.java less more
0 /*
1 * FindBugs - Find bugs in Java programs
2 * Copyright (C) 2003-2005, University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 /*
20 * AnalysisErrorDialog.java
21 *
22 * Created on June 5, 2003, 3:20 PM
23 */
24
25 package edu.umd.cs.findbugs.gui;
26
27 import edu.umd.cs.findbugs.BugReporter;
28 import edu.umd.cs.findbugs.L10N;
29
30 /**
31 * A dialog to report errors that occured during analysis.
32 *
33 * @author David Hovemeyer
34 */
35 public class AnalysisErrorDialog extends javax.swing.JDialog {
36
37 private BugReporter reporter;
38
39 /**
40 * Creates new form AnalysisErrorDialog
41 */
42 public AnalysisErrorDialog(java.awt.Frame parent, boolean modal, BugReporter reporter) {
43 super(parent, modal);
44 this.reporter = reporter;
45 initComponents();
46 }
47
48 public void generateContents() {
49 reporter.reportQueuedErrors();
50 }
51
52 /**
53 * This method is called from within the constructor to
54 * initialize the form.
55 * WARNING: Do NOT modify this code. The content of this method is
56 * always regenerated by the Form Editor.
57 */
58 private void initComponents() {//GEN-BEGIN:initComponents
59 java.awt.GridBagConstraints gridBagConstraints;
60
61 errorLabel = new javax.swing.JLabel();
62 errorMessageScrollPane = new javax.swing.JScrollPane();
63 errorMessageTextArea = new javax.swing.JTextPane();
64 jSeparator1 = new javax.swing.JSeparator();
65 leftSpacer = new javax.swing.JLabel();
66 rightSpacer = new javax.swing.JLabel();
67 okButton = new javax.swing.JButton();
68 analysisMenuBar = new javax.swing.JMenuBar();
69 editMenu = new javax.swing.JMenu();
70 selectAllMenuItem = new javax.swing.JMenuItem();
71 copyMenuItem = new javax.swing.JMenuItem();
72
73 getContentPane().setLayout(new java.awt.GridBagLayout());
74
75 setTitle("Analysis Errors");
76 setTitle(L10N.getLocalString("dlg.analysiserrors_ttl", "Analysis Errors"));
77 addWindowListener(new java.awt.event.WindowAdapter() {
78 @Override
79 public void windowClosing(java.awt.event.WindowEvent evt) {
80 closeDialog(evt);
81 }
82 });
83
84 errorLabel.setFont(new java.awt.Font("Dialog", 0, 12));
85 errorLabel.setText("Errors occured during the analysis:");
86 errorLabel.setText(L10N.getLocalString("dlg.analysiserror_lbl", "Errors occurred during analysis:"));
87 gridBagConstraints = new java.awt.GridBagConstraints();
88 gridBagConstraints.gridx = 0;
89 gridBagConstraints.gridy = 0;
90 gridBagConstraints.gridwidth = 3;
91 gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
92 gridBagConstraints.insets = new java.awt.Insets(6, 6, 3, 0);
93 getContentPane().add(errorLabel, gridBagConstraints);
94
95 errorMessageTextArea.setBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.LOWERED));
96 errorMessageTextArea.setEditable(false);
97 errorMessageScrollPane.setViewportView(errorMessageTextArea);
98
99 gridBagConstraints = new java.awt.GridBagConstraints();
100 gridBagConstraints.gridx = 0;
101 gridBagConstraints.gridy = 1;
102 gridBagConstraints.gridwidth = 3;
103 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
104 gridBagConstraints.weightx = 1.0;
105 gridBagConstraints.weighty = 1.0;
106 gridBagConstraints.insets = new java.awt.Insets(4, 6, 4, 6);
107 getContentPane().add(errorMessageScrollPane, gridBagConstraints);
108
109 gridBagConstraints = new java.awt.GridBagConstraints();
110 gridBagConstraints.gridx = 0;
111 gridBagConstraints.gridy = 2;
112 gridBagConstraints.gridwidth = 3;
113 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
114 getContentPane().add(jSeparator1, gridBagConstraints);
115
116 gridBagConstraints = new java.awt.GridBagConstraints();
117 gridBagConstraints.gridx = 0;
118 gridBagConstraints.gridy = 3;
119 gridBagConstraints.weightx = 0.5;
120 getContentPane().add(leftSpacer, gridBagConstraints);
121
122 gridBagConstraints = new java.awt.GridBagConstraints();
123 gridBagConstraints.gridx = 2;
124 gridBagConstraints.gridy = 3;
125 gridBagConstraints.weightx = 0.5;
126 getContentPane().add(rightSpacer, gridBagConstraints);
127
128 okButton.setMnemonic('O');
129 okButton.setText("OK");
130 okButton.setText(L10N.getLocalString("dlg.ok_btn", "OK"));
131 okButton.addActionListener(new java.awt.event.ActionListener() {
132 public void actionPerformed(java.awt.event.ActionEvent evt) {
133 okButtonActionPerformed(evt);
134 }
135 });
136
137 gridBagConstraints = new java.awt.GridBagConstraints();
138 gridBagConstraints.gridx = 1;
139 gridBagConstraints.gridy = 3;
140 gridBagConstraints.insets = new java.awt.Insets(3, 0, 3, 0);
141 getContentPane().add(okButton, gridBagConstraints);
142
143 analysisMenuBar.setFont(new java.awt.Font("Dialog", 0, 12));
144 editMenu.setText("Edit");
145 editMenu.setFont(new java.awt.Font("Dialog", 0, 12));
146 AnnotatedString.localiseButton(editMenu, "menu.edit_menu", "&Edit", true);
147 editMenu.addActionListener(new java.awt.event.ActionListener() {
148 public void actionPerformed(java.awt.event.ActionEvent evt) {
149 editMenuActionPerformed(evt);
150 }
151 });
152
153 selectAllMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_A, java.awt.event.InputEvent.CTRL_MASK));
154 selectAllMenuItem.setFont(new java.awt.Font("Dialog", 0, 12));
155 selectAllMenuItem.setText("Select All");
156 AnnotatedString.localiseButton(selectAllMenuItem, "menu.selectall_item", "Select &All", true);
157 selectAllMenuItem.addActionListener(new java.awt.event.ActionListener() {
158 public void actionPerformed(java.awt.event.ActionEvent evt) {
159 selectAllItemActionListener(evt);
160 }
161 });
162
163 editMenu.add(selectAllMenuItem);
164
165 copyMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_C, java.awt.event.InputEvent.CTRL_MASK));
166 copyMenuItem.setFont(new java.awt.Font("Dialog", 0, 12));
167 copyMenuItem.setText("Copy");
168 AnnotatedString.localiseButton(copyMenuItem, "menu.copy_item", "Copy", true);
169 copyMenuItem.addActionListener(new java.awt.event.ActionListener() {
170 public void actionPerformed(java.awt.event.ActionEvent evt) {
171 copyMenuItemActionPerformed(evt);
172 }
173 });
174
175 editMenu.add(copyMenuItem);
176
177 analysisMenuBar.add(editMenu);
178
179 setJMenuBar(analysisMenuBar);
180
181 pack();
182 }//GEN-END:initComponents
183
184 private void copyMenuItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_copyMenuItemActionPerformed
185 errorMessageTextArea.copy();
186 }//GEN-LAST:event_copyMenuItemActionPerformed
187
188 private void selectAllItemActionListener(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectAllItemActionListener
189 errorMessageTextArea.selectAll();
190 }//GEN-LAST:event_selectAllItemActionListener
191
192 private void editMenuActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_editMenuActionPerformed
193 // TODO add your handling code here:
194 }//GEN-LAST:event_editMenuActionPerformed
195
196 private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
197 closeDialog(null);
198 }//GEN-LAST:event_okButtonActionPerformed
199
200 /**
201 * Closes the dialog
202 */
203 private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog
204 setVisible(false);
205 dispose();
206 }//GEN-LAST:event_closeDialog
207
208 private StringBuilder buf = new StringBuilder();
209
210 public void addLine(String line) {
211 //System.out.println("Appending: " + line);
212 int start = 0;
213 int end = line.length() - 1;
214 while(line.substring(start, end).length() - 100 > 50) {
215 buf.append(line.substring(start, start + 99) + "-");
216 buf.append('\n');
217 start += 99;
218 }
219 buf.append(line.substring(start, end));
220 buf.append('\n');
221 }
222
223 public void clear() {
224 errorMessageTextArea.setText("");
225 }
226
227 public void finish() {
228 errorMessageTextArea.setText(buf.toString());
229 }
230
231 // Variables declaration - do not modify//GEN-BEGIN:variables
232 private javax.swing.JMenuBar analysisMenuBar;
233 private javax.swing.JMenuItem copyMenuItem;
234 private javax.swing.JMenu editMenu;
235 private javax.swing.JLabel errorLabel;
236 private javax.swing.JScrollPane errorMessageScrollPane;
237 private javax.swing.JTextPane errorMessageTextArea;
238 private javax.swing.JSeparator jSeparator1;
239 private javax.swing.JLabel leftSpacer;
240 private javax.swing.JButton okButton;
241 private javax.swing.JLabel rightSpacer;
242 private javax.swing.JMenuItem selectAllMenuItem;
243 // End of variables declaration//GEN-END:variables
244
245 }
+0
-220
src/obsolete/edu/umd/cs/findbugs/gui/AnalysisRun.java less more
0 /*
1 * FindBugs - Find bugs in Java programs
2 * Copyright (C) 2003-2005, University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 /*
20 * AnalysisRun.java
21 *
22 * Created on April 1, 2003, 2:24 PM
23 */
24
25 package edu.umd.cs.findbugs.gui;
26
27 import java.io.File;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.io.StringWriter;
31 import java.text.MessageFormat;
32 import java.util.HashMap;
33
34 import javax.swing.tree.DefaultTreeModel;
35
36 import org.dom4j.DocumentException;
37
38 import edu.umd.cs.findbugs.BugInstance;
39 import edu.umd.cs.findbugs.Detector;
40 import edu.umd.cs.findbugs.DetectorFactoryCollection;
41 import edu.umd.cs.findbugs.FindBugs2;
42 import edu.umd.cs.findbugs.FindBugsProgress;
43 import edu.umd.cs.findbugs.IFindBugsEngine;
44 import edu.umd.cs.findbugs.L10N;
45 import edu.umd.cs.findbugs.Project;
46 import edu.umd.cs.findbugs.ProjectStats;
47 import edu.umd.cs.findbugs.SystemProperties;
48 import edu.umd.cs.findbugs.config.UserPreferences;
49 import edu.umd.cs.findbugs.log.ConsoleLogger;
50 import edu.umd.cs.findbugs.log.Logger;
51
52
53
54 /**
55 * Representation of a run of the FindBugs analysis on a Project.
56 * This class has convenient methods which can be used to extract
57 * bug reports in various interesting ways.
58 *
59 * @author David Hovemeyer
60 */
61 public class AnalysisRun {
62 private FindBugsFrame frame;
63 private String summary;
64 private Logger logger;
65 private IFindBugsEngine findBugs;
66 private SwingGUIBugReporter reporter;
67 private HashMap<String, DefaultTreeModel> treeModelMap;
68
69 /**
70 * Creates a new instance of AnalysisRun.
71 */
72 public AnalysisRun(Project project, FindBugsFrame frame) {
73 this.frame = frame;
74 this.logger = frame.getLogger();
75 this.reporter = new SwingGUIBugReporter(this);
76 this.reporter.setPriorityThreshold(Detector.EXP_PRIORITY);
77
78 // Create IFindBugsEngine
79 FindBugs2 engine = new FindBugs2();
80 engine.setBugReporter(reporter);
81 engine.setProject(project);
82 engine.setDetectorFactoryCollection(DetectorFactoryCollection.instance());
83
84 this.findBugs = engine;
85 this.treeModelMap = new HashMap<String, DefaultTreeModel>();
86 }
87
88 /**
89 * Get the FindBugsFrame which created this analysis run.
90 *
91 * @return the FindBugsFrame
92 */
93 public FindBugsFrame getFrame() {
94 return frame;
95 }
96
97 /**
98 * Run the analysis.
99 * This should be done in a separate thread (not the GUI event thread).
100 * The progress callback can be used to update the user interface to
101 * reflect the progress of the analysis. The GUI may cancel the analysis
102 * by interrupting the analysis thread, in which case InterruptedException
103 * will be thrown by this method.
104 *
105 * @param progressCallback the progress callback
106 * @throws IOException if an I/O error occurs during the analysis
107 * @throws InterruptedException if the analysis thread is interrupted
108 */
109 public void execute(FindBugsProgress progressCallback) throws IOException, InterruptedException {
110 findBugs.setProgressCallback(progressCallback);
111
112 // Honor current UserPreferences
113 findBugs.setUserPreferences(UserPreferences.getUserPreferences());
114
115 // Set analysis feature settings
116 findBugs.setAnalysisFeatureSettings(frame.getSettingList());
117
118 // Run the analysis!
119 findBugs.execute();
120
121 if (!SystemProperties.getBoolean("findbugs.noSummary")) {
122 // Get the summary!
123 createSummary(reporter.getProjectStats());
124 }
125
126 }
127
128 private static final String MISSING_SUMMARY_MESSAGE =
129 "<html><head><title>Could not format summary</title></head>" +
130 "<body><h1>Could not format summary</h1>" +
131 "<p> Please report this failure to <a href=\"findbugs-discuss@cs.umd.edu\">" +
132 "findbugs-discuss@cs.umd.edu</a>.</body></html>";
133
134 private void createSummary(ProjectStats stats) throws IOException {
135 StringWriter html = new StringWriter();
136 try {
137 stats.transformSummaryToHTML(html);
138 summary = html.toString();
139 } catch (Exception e) {
140 logger.logMessage(ConsoleLogger.WARNING, MessageFormat.format(L10N.getLocalString("msg.failedtotransform_txt", "Failed to transform summary: {0}"), new Object[]{e.toString()}));
141 summary = MISSING_SUMMARY_MESSAGE;
142 }
143 }
144
145 private static final boolean CREATE_SUMMARY = !SystemProperties.getBoolean("findbugs.noSummary");
146
147 /**
148 * Load bugs from a file.
149 */
150 public void loadBugsFromFile(File file) throws IOException, org.dom4j.DocumentException {
151 reporter.getBugCollection().readXML(file);
152
153 // Update summary stats
154 summary = reporter.getBugCollection().getSummaryHTML();
155 }
156
157 /**
158 * Load bugs from an InputStream.
159 *
160 * @param in the InputStream
161 * @throws IOException
162 * @throws DocumentException
163 */
164 public void loadBugsFromInputStream(InputStream in) throws IOException, DocumentException {
165 reporter.getBugCollection().readXML(in);
166
167 // Update summary stats
168 summary = reporter.getBugCollection().getSummaryHTML();
169 }
170
171 /**
172 * Save bugs to a file.
173 */
174 public void saveBugsToFile(File file) throws IOException {
175 reporter.getBugCollection().writeXML(file);
176 }
177
178 /**
179 * Report any errors that may have occurred during analysis.
180 */
181 public void reportAnalysisErrors() {
182 if (reporter.errorsOccurred()) {
183 reporter.getErrorDialog().setSize(750, 520);
184 reporter.getErrorDialog().setLocationRelativeTo(null); // center the dialog
185 reporter.getErrorDialog().setVisible(true);
186 }
187 }
188
189 /**
190 * Return the collection of BugInstances.
191 */
192 public java.util.Collection<BugInstance> getBugInstances() {
193 return reporter.getBugCollection().getCollection();
194 }
195
196 /**
197 * Set the tree model to be used in the BugTree.
198 *
199 * @param groupByOrder the grouping order that the tree model will conform to
200 * @param treeModel the tree model
201 */
202 public void setTreeModel(String groupByOrder, DefaultTreeModel treeModel) {
203 treeModelMap.put(groupByOrder, treeModel);
204 }
205
206 /**
207 * Get the tree model to be used in the BugTree.
208 *
209 * @param groupByOrder the grouping order that the tree model conforms to
210 * @return the tree model
211 */
212 public DefaultTreeModel getTreeModel(String groupByOrder) {
213 return treeModelMap.get(groupByOrder);
214 }
215
216 public String getSummary() {
217 return summary;
218 }
219 }
+0
-144
src/obsolete/edu/umd/cs/findbugs/gui/BugCellRenderer.java less more
0 /*
1 * FindBugs - Find bugs in Java programs
2 * Copyright (C) 2003-2005, University of Maryland
3 * Copyright (C) 2004 Dave Brosius <dbrosius@users.sourceforge.net>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19 package edu.umd.cs.findbugs.gui;
20
21 import java.awt.Color;
22 import java.awt.Component;
23
24 import javax.swing.ImageIcon;
25 import javax.swing.JTree;
26 import javax.swing.tree.DefaultMutableTreeNode;
27 import javax.swing.tree.DefaultTreeCellRenderer;
28
29 import edu.umd.cs.findbugs.BugInstance;
30 import edu.umd.cs.findbugs.ClassAnnotation;
31 import edu.umd.cs.findbugs.Detector;
32 import edu.umd.cs.findbugs.FieldAnnotation;
33 import edu.umd.cs.findbugs.MethodAnnotation;
34 import edu.umd.cs.findbugs.SourceLineAnnotation;
35
36 /**
37 * Custom cell renderer for the bug tree.
38 * We use this to select the tree icons, and to set the
39 * text color based on the bug priority.
40 */
41 public class BugCellRenderer extends DefaultTreeCellRenderer {
42
43 private static final BugCellRenderer theInstance = new BugCellRenderer();
44
45 /**
46 * Get the single instance.
47 *
48 * @return the instance
49 */
50 public static BugCellRenderer instance() {
51 return theInstance;
52 }
53
54 private static final long serialVersionUID = 1L;
55 private ImageIcon bugGroupIcon;
56 private ImageIcon packageIcon;
57 private ImageIcon bugIcon;
58 private ImageIcon classIcon;
59 private ImageIcon methodIcon;
60 private ImageIcon fieldIcon;
61 private ImageIcon sourceFileIcon;
62 private Object value;
63
64 private BugCellRenderer() {
65 ClassLoader classLoader = this.getClass().getClassLoader();
66 bugGroupIcon = new ImageIcon(classLoader.getResource("edu/umd/cs/findbugs/gui/bug.png"));
67 packageIcon = new ImageIcon(classLoader.getResource("edu/umd/cs/findbugs/gui/package.png"));
68 bugIcon = new ImageIcon(classLoader.getResource("edu/umd/cs/findbugs/gui/bug2.png"));
69 classIcon = new ImageIcon(classLoader.getResource("edu/umd/cs/findbugs/gui/class.png"));
70 methodIcon = new ImageIcon(classLoader.getResource("edu/umd/cs/findbugs/gui/method.png"));
71 fieldIcon = new ImageIcon(classLoader.getResource("edu/umd/cs/findbugs/gui/field.png"));
72 sourceFileIcon = new ImageIcon(classLoader.getResource("edu/umd/cs/findbugs/gui/sourcefile.png"));
73 }
74
75 @Override
76 public Component getTreeCellRendererComponent(JTree tree, Object value, boolean sel,
77 boolean expanded, boolean leaf, int row, boolean hasFocus) {
78 DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
79 Object obj = node.getUserObject();
80
81 this.value = obj;
82
83 super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
84
85 // Set the icon, depending on what kind of node it is
86 if (obj instanceof BugInstance) {
87 setIcon(bugIcon);
88 } else if (obj instanceof ClassAnnotation) {
89 setIcon(classIcon);
90 } else if (obj instanceof MethodAnnotation) {
91 setIcon(methodIcon);
92 } else if (obj instanceof FieldAnnotation) {
93 setIcon(fieldIcon);
94 } else if (obj instanceof SourceLineAnnotation) {
95 setIcon(sourceFileIcon);
96 } else if (obj instanceof BugInstanceGroup) {
97 // This is a "group" node
98 BugInstanceGroup groupNode = (BugInstanceGroup) obj;
99 String groupType = groupNode.getGroupType();
100 if (groupType == FindBugsFrame.GROUP_BY_CLASS) {
101 setIcon(classIcon);
102 } else if (groupType == FindBugsFrame.GROUP_BY_PACKAGE) {
103 setIcon(packageIcon);
104 } else if (groupType == FindBugsFrame.GROUP_BY_BUG_TYPE) {
105 setIcon(bugGroupIcon);
106 } else if (groupType == FindBugsFrame.GROUP_BY_BUG_CATEGORY) {
107 setIcon(bugGroupIcon);
108 }
109 } else {
110 setIcon(null);
111 }
112
113 return this;
114 }
115
116 @Override
117 public Color getTextNonSelectionColor() {
118 return getCellTextColor();
119 }
120
121 private Color getCellTextColor() {
122 // Based on the priority, color-code the bug instance.
123 Color color = Color.BLACK;
124 if (value instanceof BugInstance) {
125 BugInstance bugInstance = (BugInstance) value;
126 switch (bugInstance.getPriority()) {
127 case Detector.EXP_PRIORITY:
128 color = FindBugsFrame.EXP_PRIORITY_COLOR;
129 break;
130 case Detector.LOW_PRIORITY:
131 color = FindBugsFrame.LOW_PRIORITY_COLOR;
132 break;
133 case Detector.NORMAL_PRIORITY:
134 color = FindBugsFrame.NORMAL_PRIORITY_COLOR;
135 break;
136 case Detector.HIGH_PRIORITY:
137 color = FindBugsFrame.HIGH_PRIORITY_COLOR;
138 break;
139 }
140 }
141 return color;
142 }
143 }
+0
-89
src/obsolete/edu/umd/cs/findbugs/gui/BugInstanceGroup.java less more
0 /*
1 * FindBugs - Find bugs in Java programs
2 * Copyright (C) 2003,2004 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 /*
20 * BugInstanceGroup.java
21 *
22 * Created on April 6, 2003, 11:31 AM
23 */
24
25 package edu.umd.cs.findbugs.gui;
26
27 /**
28 * A BugInstanceGroup represents a node in the bug tree which groups
29 * related bug instances. For example, it might group all of the bug instances
30 * for the same class.
31 *
32 * @author David Hovemeyer
33 */
34 public class BugInstanceGroup {
35
36 private String groupType;
37 private String groupName;
38 private int memberCount;
39
40 /**
41 * Creates a new instance of BugInstanceGroup.
42 *
43 * @param groupType string indicating why the bug instances in the group
44 * are related
45 * @param groupName name of the group (e.g., the class name if the group
46 * is all bug instances in the same class)
47 */
48 public BugInstanceGroup(String groupType, String groupName) {
49 this.groupType = groupType;
50 this.groupName = groupName;
51 this.memberCount = 0;
52 }
53
54 /**
55 * Get the group type.
56 */
57 public String getGroupType() {
58 return groupType;
59 }
60
61 /**
62 * Increment the member count (number of bug instances in this group).
63 */
64 public void incrementMemberCount() {
65 ++memberCount;
66 }
67
68 /**
69 * Get the member count (number of bug instances in this group).
70 */
71 public int getMemberCount() {
72 return memberCount;
73 }
74
75 /**
76 * Convert to string.
77 */
78 @Override
79 public String toString() {
80 StringBuilder buf = new StringBuilder();
81 buf.append(groupName);
82 buf.append(" (");
83 buf.append(memberCount);
84 buf.append(")");
85 return buf.toString();
86 }
87
88 }
+0
-138
src/obsolete/edu/umd/cs/findbugs/gui/ConfigureDetectorsDialog.form less more
0 <?xml version="1.0" encoding="UTF-8" ?>
1
2 <Form version="1.0" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
3 <Properties>
4 <Property name="title" type="java.lang.String" value="Configure Detectors"/>
5 </Properties>
6 <SyntheticProperties>
7 <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
8 </SyntheticProperties>
9 <Events>
10 <EventHandler event="windowClosing" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="closeDialog"/>
11 <EventHandler event="windowOpened" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="formWindowOpened"/>
12 </Events>
13
14 <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
15 <SubComponents>
16 <Container class="javax.swing.JScrollPane" name="detectorTableScrollPane">
17 <Properties>
18 <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
19 <Border info="org.netbeans.modules.form.compat2.border.BevelBorderInfo">
20 <BevelBorder bevelType="1"/>
21 </Border>
22 </Property>
23 </Properties>
24 <Constraints>
25 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
26 <GridBagConstraints gridX="-1" gridY="-1" gridWidth="4" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="6" insetsLeft="6" insetsBottom="2" insetsRight="6" anchor="10" weightX="1.0" weightY="0.8"/>
27 </Constraint>
28 </Constraints>
29
30 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
31 <SubComponents>
32 <Component class="javax.swing.JTable" name="detectorTable">
33 <Properties>
34 <Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor" postCode="populateTable();&#xa;detectorTable.getColumnModel().getColumn(ENABLED_COLUMN).setMaxWidth(60);&#xa;detectorTable.getColumnModel().getColumn(SPEED_COLUMN).setMaxWidth(60);&#xa;detectorTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);&#xa;">
35 <Table columnCount="3" rowCount="0">
36 <Column editable="false" title="Bug Detector" type="java.lang.String"/>
37 <Column editable="false" title="Speed" type="java.lang.String"/>
38 <Column editable="true" title="Enabled" type="java.lang.Boolean"/>
39 </Table>
40 </Property>
41 </Properties>
42 <AuxValues>
43 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="&#xa;{&#xa; DefaultTableModel m = (DefaultTableModel)detectorTable.getModel();&#xa; m.setColumnIdentifiers( new String[]&#xa; {&#xa; L10N.getLocalString(&quot;dlg.bugdetector_lbl&quot;, &quot;Bug Detector&quot;),&#xa; L10N.getLocalString(&quot;dlg.speed_lbl&quot;, &quot;Speed&quot;),&#xa; L10N.getLocalString(&quot;dlg.enabled_lbl&quot;, &quot;Enabled&quot;),&#xa; });&#xa;&#xa;&#xa; DefaultSortedTableModel sortedModel = new DefaultSortedTableModel(m, detectorTable.getTableHeader());&#xa; detectorTable.setModel(sortedModel);&#xa;} &#xa;"/>
44 </AuxValues>
45 </Component>
46 </SubComponents>
47 </Container>
48 <Container class="javax.swing.JScrollPane" name="detectorDescriptionScrollPane">
49 <Properties>
50 <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
51 <Border info="org.netbeans.modules.form.compat2.border.BevelBorderInfo">
52 <BevelBorder bevelType="1"/>
53 </Border>
54 </Property>
55 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
56 <Dimension value="[110, 120]"/>
57 </Property>
58 </Properties>
59 <Constraints>
60 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
61 <GridBagConstraints gridX="0" gridY="1" gridWidth="4" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="6" insetsBottom="2" insetsRight="6" anchor="10" weightX="0.0" weightY="0.3"/>
62 </Constraint>
63 </Constraints>
64
65 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
66 <SubComponents>
67 <Component class="javax.swing.JEditorPane" name="detectorDescription">
68 </Component>
69 </SubComponents>
70 </Container>
71 <Component class="javax.swing.JSeparator" name="jSeparator1">
72 <Constraints>
73 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
74 <GridBagConstraints gridX="0" gridY="2" gridWidth="4" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="0" insetsBottom="3" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
75 </Constraint>
76 </Constraints>
77 </Component>
78 <Component class="javax.swing.JButton" name="okButton">
79 <Properties>
80 <Property name="mnemonic" type="int" value="79"/>
81 <Property name="text" type="java.lang.String" value="OK"/>
82 </Properties>
83 <Events>
84 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="okButtonActionPerformed"/>
85 </Events>
86 <AuxValues>
87 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="okButton.setText(L10N.getLocalString(&quot;dlg.ok_btn&quot;,&quot;OK&quot;));"/>
88 </AuxValues>
89 <Constraints>
90 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
91 <GridBagConstraints gridX="2" gridY="3" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="4" insetsRight="2" anchor="10" weightX="0.0" weightY="0.0"/>
92 </Constraint>
93 </Constraints>
94 </Component>
95 <Component class="javax.swing.JButton" name="cancelButton">
96 <Properties>
97 <Property name="mnemonic" type="int" value="67"/>
98 <Property name="text" type="java.lang.String" value="Cancel"/>
99 </Properties>
100 <Events>
101 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/>
102 </Events>
103 <AuxValues>
104 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="cancelButton.setText(L10N.getLocalString(&quot;dlg.cancel_btn&quot;, &quot;Cancel&quot;));"/>
105 </AuxValues>
106 <Constraints>
107 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
108 <GridBagConstraints gridX="3" gridY="3" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="2" insetsBottom="4" insetsRight="6" anchor="10" weightX="0.0" weightY="0.0"/>
109 </Constraint>
110 </Constraints>
111 </Component>
112 <Component class="javax.swing.JLabel" name="spacer">
113 <Constraints>
114 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
115 <GridBagConstraints gridX="1" gridY="3" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="0.0"/>
116 </Constraint>
117 </Constraints>
118 </Component>
119 <Component class="javax.swing.JButton" name="restoreDefaultsButton">
120 <Properties>
121 <Property name="text" type="java.lang.String" value="Restore Defaults"/>
122 <Property name="horizontalAlignment" type="int" value="2"/>
123 </Properties>
124 <Events>
125 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="restoreDefaultsButtonActionPerformed"/>
126 </Events>
127 <AuxValues>
128 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="restoreDefaultsButton.setText(L10N.getLocalString(&quot;dlg.restoredefaults_btn&quot;, &quot;Restore Defaults&quot;));"/>
129 </AuxValues>
130 <Constraints>
131 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
132 <GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="6" insetsBottom="4" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
133 </Constraint>
134 </Constraints>
135 </Component>
136 </SubComponents>
137 </Form>
+0
-382
src/obsolete/edu/umd/cs/findbugs/gui/ConfigureDetectorsDialog.java less more
0 /*
1 * FindBugs - Find bugs in Java programs
2 * Copyright (C) 2003,2004 University of Maryland
3 * Copyright (C) 2004 Dave Brosius <dbrosius@users.sourceforge.net>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 /*
21 * ConfigureDetectorsDialog.java
22 *
23 * Created on June 3, 2003, 3:52 PM
24 */
25
26 package edu.umd.cs.findbugs.gui;
27
28 import java.util.ArrayList;
29 import java.util.Collection;
30 import java.util.Iterator;
31
32 import javax.swing.ListSelectionModel;
33 import javax.swing.event.ListSelectionEvent;
34 import javax.swing.event.ListSelectionListener;
35 import javax.swing.table.DefaultTableModel;
36 import javax.swing.table.TableModel;
37
38 import edu.umd.cs.findbugs.BugPattern;
39 import edu.umd.cs.findbugs.DetectorFactory;
40 import edu.umd.cs.findbugs.DetectorFactoryCollection;
41 import edu.umd.cs.findbugs.L10N;
42 import edu.umd.cs.findbugs.config.UserPreferences;
43
44 /**
45 * Configure Detectors by enabling/disabling them.
46 *
47 * @author David Hovemeyer
48 */
49 public class ConfigureDetectorsDialog extends javax.swing.JDialog {
50 private static final long serialVersionUID = 1L;
51
52 private static final int SPEED_COLUMN = 1;
53 private static final int ENABLED_COLUMN = 2;
54
55 /**
56 * Creates new form ConfigureDetectorsDialog
57 */
58 public ConfigureDetectorsDialog(java.awt.Frame parent, boolean modal) {
59 super(parent, modal);
60 initComponents();
61 postInitComponents();
62 }
63
64 /**
65 * This method is called from within the constructor to
66 * initialize the form.
67 * WARNING: Do NOT modify this code. The content of this method is
68 * always regenerated by the Form Editor.
69 */
70 private void initComponents() {//GEN-BEGIN:initComponents
71 java.awt.GridBagConstraints gridBagConstraints;
72
73 detectorTableScrollPane = new javax.swing.JScrollPane();
74 detectorTable = new javax.swing.JTable();
75 detectorDescriptionScrollPane = new javax.swing.JScrollPane();
76 detectorDescription = new javax.swing.JEditorPane();
77 jSeparator1 = new javax.swing.JSeparator();
78 okButton = new javax.swing.JButton();
79 cancelButton = new javax.swing.JButton();
80 spacer = new javax.swing.JLabel();
81 restoreDefaultsButton = new javax.swing.JButton();
82
83 getContentPane().setLayout(new java.awt.GridBagLayout());
84
85 setTitle("Configure Detectors");
86 addWindowListener(new java.awt.event.WindowAdapter() {
87 @Override
88 public void windowClosing(java.awt.event.WindowEvent evt) {
89 closeDialog(evt);
90 }
91 @Override
92 public void windowOpened(java.awt.event.WindowEvent evt) {
93 formWindowOpened(evt);
94 }
95 });
96
97 detectorTableScrollPane.setBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.LOWERED));
98 detectorTable.setModel(new javax.swing.table.DefaultTableModel(
99 new Object [][] {
100
101 },
102 new String [] {
103 "Bug Detector", "Speed", "Enabled"
104 }
105 ) {
106 private static final long serialVersionUID = 1L;
107 Class[] types = new Class [] {
108 java.lang.String.class, java.lang.String.class, java.lang.Boolean.class
109 };
110 boolean[] canEdit = new boolean [] {
111 false, false, true
112 };
113
114 @Override
115 public Class<?> getColumnClass(int columnIndex) {
116 return types [columnIndex];
117 }
118
119 @Override
120 public boolean isCellEditable(int rowIndex, int columnIndex) {
121 return canEdit [columnIndex];
122 }
123 });
124 populateTable();
125 detectorTable.getColumnModel().getColumn(ENABLED_COLUMN).setMaxWidth(60);
126 detectorTable.getColumnModel().getColumn(SPEED_COLUMN).setMaxWidth(60);
127 detectorTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
128
129 {
130 DefaultTableModel m = (DefaultTableModel)detectorTable.getModel();
131 m.setColumnIdentifiers( new String[]
132 {
133 L10N.getLocalString("dlg.bugdetector_lbl", "Bug Detector"),
134 L10N.getLocalString("dlg.speed_lbl", "Speed"),
135 L10N.getLocalString("dlg.enabled_lbl", "Enabled"),
136 });
137
138 DefaultSortedTableModel sortedModel = new DefaultSortedTableModel(m, detectorTable.getTableHeader());
139 detectorTable.setModel(sortedModel);
140 }
141
142 detectorTableScrollPane.setViewportView(detectorTable);
143
144 gridBagConstraints = new java.awt.GridBagConstraints();
145 gridBagConstraints.gridwidth = 4;
146 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
147 gridBagConstraints.weightx = 1.0;
148 gridBagConstraints.weighty = 0.8;
149 gridBagConstraints.insets = new java.awt.Insets(6, 6, 2, 6);
150 getContentPane().add(detectorTableScrollPane, gridBagConstraints);
151
152 detectorDescriptionScrollPane.setBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.LOWERED));
153 detectorDescriptionScrollPane.setPreferredSize(new java.awt.Dimension(110, 120));
154 detectorDescriptionScrollPane.setViewportView(detectorDescription);
155
156 gridBagConstraints = new java.awt.GridBagConstraints();
157 gridBagConstraints.gridx = 0;
158 gridBagConstraints.gridy = 1;
159 gridBagConstraints.gridwidth = 4;
160 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
161 gridBagConstraints.weighty = 0.3;
162 gridBagConstraints.insets = new java.awt.Insets(2, 6, 2, 6);
163 getContentPane().add(detectorDescriptionScrollPane, gridBagConstraints);
164
165 gridBagConstraints = new java.awt.GridBagConstraints();
166 gridBagConstraints.gridx = 0;
167 gridBagConstraints.gridy = 2;
168 gridBagConstraints.gridwidth = 4;
169 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
170 gridBagConstraints.insets = new java.awt.Insets(3, 0, 3, 0);
171 getContentPane().add(jSeparator1, gridBagConstraints);
172
173 okButton.setMnemonic('O');
174 okButton.setText("OK");
175 okButton.setText(L10N.getLocalString("dlg.ok_btn","OK"));
176 okButton.addActionListener(new java.awt.event.ActionListener() {
177 public void actionPerformed(java.awt.event.ActionEvent evt) {
178 okButtonActionPerformed(evt);
179 }
180 });
181
182 gridBagConstraints = new java.awt.GridBagConstraints();
183 gridBagConstraints.gridx = 2;
184 gridBagConstraints.gridy = 3;
185 gridBagConstraints.insets = new java.awt.Insets(0, 0, 4, 2);
186 getContentPane().add(okButton, gridBagConstraints);
187
188 cancelButton.setMnemonic('C');
189 cancelButton.setText("Cancel");
190 cancelButton.setText(L10N.getLocalString("dlg.cancel_btn", "Cancel"));
191 cancelButton.addActionListener(new java.awt.event.ActionListener() {
192 public void actionPerformed(java.awt.event.ActionEvent evt) {
193 cancelButtonActionPerformed(evt);
194 }
195 });
196
197 gridBagConstraints = new java.awt.GridBagConstraints();
198 gridBagConstraints.gridx = 3;
199 gridBagConstraints.gridy = 3;
200 gridBagConstraints.insets = new java.awt.Insets(0, 2, 4, 6);
201 getContentPane().add(cancelButton, gridBagConstraints);
202
203 gridBagConstraints = new java.awt.GridBagConstraints();
204 gridBagConstraints.gridx = 1;
205 gridBagConstraints.gridy = 3;
206 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
207 gridBagConstraints.weightx = 1.0;
208 getContentPane().add(spacer, gridBagConstraints);
209
210 restoreDefaultsButton.setText("Restore Defaults");
211 restoreDefaultsButton.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
212 restoreDefaultsButton.setText(L10N.getLocalString("dlg.restoredefaults_btn", "Restore Defaults"));
213 restoreDefaultsButton.addActionListener(new java.awt.event.ActionListener() {
214 public void actionPerformed(java.awt.event.ActionEvent evt) {
215 restoreDefaultsButtonActionPerformed(evt);
216 }
217 });
218
219 gridBagConstraints = new java.awt.GridBagConstraints();
220 gridBagConstraints.gridx = 0;
221 gridBagConstraints.gridy = 3;
222 gridBagConstraints.insets = new java.awt.Insets(0, 6, 4, 0);
223 getContentPane().add(restoreDefaultsButton, gridBagConstraints);
224
225 pack();
226 }//GEN-END:initComponents
227
228 private void formWindowOpened(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowOpened
229 setTitle(L10N.getLocalString("dlg.configuredetectors_ttl", "Configure Detectors"));
230 }//GEN-LAST:event_formWindowOpened
231
232 /**
233 * reverts the selected state of all the detectors to their defaults as specified in the findbugs.xml file
234 *
235 * @param evt the swing event corresponding to the mouse click of the Restore Defaults button
236 */
237 private void restoreDefaultsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_restoreDefaultsButtonActionPerformed
238 Iterator<DetectorFactory> i = DetectorFactoryCollection.instance().factoryIterator();
239 DefaultSortedTableModel sorter = (DefaultSortedTableModel) detectorTable.getModel();
240 TableModel model = sorter.getBaseTableModel();
241 int row = 0;
242 while (i.hasNext()) {
243 DetectorFactory factory = i.next();
244 if (factory.isHidden())
245 continue;
246 model.setValueAt(factory.isDefaultEnabled() ? Boolean.TRUE : Boolean.FALSE, row++, ENABLED_COLUMN);
247 }
248 }//GEN-LAST:event_restoreDefaultsButtonActionPerformed
249
250 private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
251 closeDialog();
252 }//GEN-LAST:event_cancelButtonActionPerformed
253
254 private void okButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_okButtonActionPerformed
255 // Update new enabled/disabled status for the Detectors
256 int num = factoryList.size();
257 DefaultSortedTableModel sorter = (DefaultSortedTableModel) detectorTable.getModel();
258 TableModel model = sorter.getBaseTableModel();
259 for (int i = 0; i < num; ++i) {
260 DetectorFactory factory = factoryList.get(i);
261 Boolean enabled = (Boolean) model.getValueAt(i, ENABLED_COLUMN);
262 UserPreferences.getUserPreferences().enableDetector(
263 factory, enabled.booleanValue());
264 }
265 closeDialog();
266 }//GEN-LAST:event_okButtonActionPerformed
267
268 /**
269 * Closes the dialog
270 */
271 private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog
272 setVisible(false);
273 dispose();
274 }//GEN-LAST:event_closeDialog
275
276 /**
277 * installs a list selection listener to populate the bottom details page based on selection changes in top grid.
278 * A conversion from the table sorter index to the base model index is done to get the correct details
279 */
280 private void postInitComponents() {
281 // Listen to detector table selections so we can (hopefully)
282 // display the description of the detector
283
284 ListSelectionModel rowSM = detectorTable.getSelectionModel();
285 rowSM.addListSelectionListener(new ListSelectionListener() {
286 public void valueChanged(ListSelectionEvent e) {
287 if (e.getValueIsAdjusting()) return;
288
289 ListSelectionModel lsm = (ListSelectionModel) e.getSource();
290 if (!lsm.isSelectionEmpty()) {
291 int selectedRow = lsm.getMinSelectionIndex();
292 DefaultSortedTableModel sorter = (DefaultSortedTableModel) detectorTable.getModel();
293 viewDetectorDetails(factoryList.get(sorter.getBaseModelIndex(selectedRow)));
294 }
295 }
296 });
297 }
298
299 /**
300 * populates the bottom detector details pane based on the detector selected
301 *
302 * @param factory the detector that is currently selected
303 */
304 private void viewDetectorDetails(DetectorFactory factory) {
305 String detailHTML = factory.getDetailHTML();
306 if (detailHTML == null) {
307 detectorDescription.setText("");
308 } else {
309 detectorDescription.setContentType("text/html");
310 detectorDescription.setText(detailHTML);
311 StringBuilder toolTip = new StringBuilder(100);
312 toolTip.append("<html><body><b>");
313 toolTip.append(factory.getFullName());
314 toolTip.append("</b><br><br><table border='1' width='100%'><tr><th>");
315 toolTip.append(L10N.getLocalString("msg.bugpatternsreported_txt", "Bug Patterns Reported"));
316 toolTip.append("</th></tr>");
317
318 Collection<BugPattern> patterns = factory.getReportedBugPatterns();
319 for (BugPattern pattern : patterns) {
320 toolTip.append("<tr><td align='center'>");
321 toolTip.append("[");
322 toolTip.append(pattern.getAbbrev());
323 toolTip.append("] ");
324 toolTip.append(pattern.getType());
325 toolTip.append("</td></tr>");
326 }
327 toolTip.append("</body></html>");
328 detectorDescription.setToolTipText(toolTip.toString());
329 }
330 }
331
332 /**
333 * populates the Detector JTable model with all available detectors
334 * Due to Netbeans form builder, populate table gets called before the tablesorter is installed,
335 * so it is correct for the model retrieved from the table to be assumed to be the base DefaultTableModel.
336 */
337 private void populateTable() {
338 Iterator<DetectorFactory> i = DetectorFactoryCollection.instance().factoryIterator();
339 while (i.hasNext()) {
340 DetectorFactory factory = i.next();
341 if (factory.isHidden())
342 continue;
343 DefaultTableModel model = (DefaultTableModel) detectorTable.getModel();
344 model.addRow(new Object[]{
345 factory.getShortName(),
346 factory.getSpeed(),
347 UserPreferences.getUserPreferences().isDetectorEnabled(factory)
348 ? Boolean.TRUE : Boolean.FALSE
349 });
350 factoryList.add(factory);
351 }
352 }
353
354 private void closeDialog() {
355 setVisible(false);
356 dispose();
357 }
358
359 /**
360 * @param args the command line arguments
361 */
362 public static void main(String args[]) {
363 new ConfigureDetectorsDialog(new javax.swing.JFrame(), true).setVisible(true);
364 }
365
366
367 // Variables declaration - do not modify//GEN-BEGIN:variables
368 private javax.swing.JButton cancelButton;
369 private javax.swing.JEditorPane detectorDescription;
370 private javax.swing.JScrollPane detectorDescriptionScrollPane;
371 private javax.swing.JTable detectorTable;
372 private javax.swing.JScrollPane detectorTableScrollPane;
373 private javax.swing.JSeparator jSeparator1;
374 private javax.swing.JButton okButton;
375 private javax.swing.JButton restoreDefaultsButton;
376 private javax.swing.JLabel spacer;
377 // End of variables declaration//GEN-END:variables
378
379 // My variables
380 private ArrayList<DetectorFactory> factoryList = new ArrayList<DetectorFactory>();
381 }
+0
-270
src/obsolete/edu/umd/cs/findbugs/gui/DefaultSortedTableModel.java less more
0 /*
1 * FindBugs - Find bugs in Java programs
2 * Copyright (C) 2004,2005 Dave Brosius
3 * Copyright (C) 2004,2005 University of Maryland
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20
21 package edu.umd.cs.findbugs.gui;
22
23 import java.awt.Component;
24 import java.awt.event.MouseAdapter;
25 import java.awt.event.MouseEvent;
26 import java.util.ArrayList;
27 import java.util.Collections;
28 import java.util.Comparator;
29 import java.util.List;
30
31 import javax.swing.ImageIcon;
32 import javax.swing.JLabel;
33 import javax.swing.JTable;
34 import javax.swing.SwingConstants;
35 import javax.swing.event.TableModelEvent;
36 import javax.swing.event.TableModelListener;
37 import javax.swing.table.AbstractTableModel;
38 import javax.swing.table.DefaultTableCellRenderer;
39 import javax.swing.table.JTableHeader;
40 import javax.swing.table.TableCellRenderer;
41 import javax.swing.table.TableModel;
42
43 /**
44 * A Table model that sits between the JTable and the real model.
45 * This model converts view row indexes, into sorted model row indexes.
46 */
47 public class DefaultSortedTableModel extends AbstractTableModel
48 {
49 /**
50 *
51 */
52 private static final long serialVersionUID = 1L;
53 public static final int SORT_NO_ORDER = 0;
54 public static final int SORT_ASCENDING_ORDER = 1;
55 public static final int SORT_DESCENDING_ORDER = 2;
56 public static final int NUM_SORT_DIREECTIONS = 3;
57
58 private AbstractTableModel baseModel;
59 private List<Integer> viewToModelMapping;
60 private int sortDirection = SORT_ASCENDING_ORDER;
61 private int sortColumn = 0;
62 private ImageIcon upIcon, downIcon;
63
64
65 public DefaultSortedTableModel( AbstractTableModel model, JTableHeader header ) {
66 baseModel = model;
67 model.addTableModelListener(new BaseTableModelListener());
68
69 final JTableHeader baseHeader = header;
70 baseHeader.addMouseListener(new HeaderListener());
71 final TableCellRenderer baseRenderer = baseHeader.getDefaultRenderer();
72 baseHeader.setDefaultRenderer( new DefaultTableCellRenderer() {
73 @Override
74 public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
75 JLabel label = (JLabel)baseRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
76 if (baseHeader.getTable().convertColumnIndexToModel(column) == sortColumn) {
77 if (sortDirection != SORT_NO_ORDER) {
78 label.setHorizontalTextPosition(SwingConstants.LEFT);
79 label.setIcon( sortDirection == SORT_ASCENDING_ORDER ? downIcon : upIcon );
80 } else {
81 label.setIcon(null);
82 }
83 } else {
84 label.setIcon(null);
85 }
86 return label;
87 }
88 });
89
90 setupMapping();
91 ClassLoader classLoader = this.getClass().getClassLoader();
92 upIcon = new ImageIcon(classLoader.getResource("edu/umd/cs/findbugs/gui/up.png"));
93 downIcon = new ImageIcon(classLoader.getResource("edu/umd/cs/findbugs/gui/down.png"));
94 }
95
96 // Base Model handling
97
98 public TableModel getBaseTableModel() {
99 return baseModel;
100 }
101
102 public int getBaseModelIndex( int viewIndex ) {
103 return viewToModelMapping.get(viewIndex).intValue();
104 }
105
106 // Event handling
107
108 @Override
109 public void fireTableCellUpdated( int row, int col ) {
110 if (baseModel != null)
111 setupMapping();
112 super.fireTableCellUpdated(row, col);
113 }
114
115 @Override
116 public void fireTableChanged( TableModelEvent e ) {
117 if (baseModel != null)
118 setupMapping();
119 super.fireTableChanged(e);
120 }
121
122 @Override
123 public void fireTableDataChanged() {
124 if (baseModel != null)
125 setupMapping();
126 super.fireTableDataChanged();
127 }
128
129 @Override
130 public void fireTableRowsDeleted( int first, int last ) {
131 if (baseModel != null)
132 setupMapping();
133 super.fireTableRowsDeleted(first,last);
134 }
135
136 @Override
137 public void fireTableRowsInserted( int first, int last ) {
138 if (baseModel != null)
139 setupMapping();
140 super.fireTableRowsInserted(first, last);
141 }
142
143 @Override
144 public void fireTableRowsUpdated( int first, int last ) {
145 if (baseModel != null)
146 setupMapping();
147 super.fireTableRowsUpdated(first, last);
148 }
149
150 @Override
151 public void fireTableStructureChanged() {
152 if (baseModel != null)
153 setupMapping();
154 super.fireTableStructureChanged();
155 }
156
157 // accessors
158
159 @Override
160 public int findColumn( String columnName ) {
161 if (baseModel == null)
162 return -1;
163
164 return baseModel.findColumn(columnName);
165 }
166
167 public int getColumnCount() {
168 if (baseModel == null)
169 return 0;
170
171 return baseModel.getColumnCount();
172 }
173
174 public int getRowCount() {
175 if (baseModel == null)
176 return 0;
177
178 return baseModel.getRowCount();
179 }
180
181 @Override
182 public Class<?> getColumnClass( int column ) {
183 if (baseModel == null)
184 return null;
185
186 return baseModel.getColumnClass(column);
187 }
188
189 @Override
190 public String getColumnName( int column ) {
191 if (baseModel == null)
192 return null;
193
194 return baseModel.getColumnName(column);
195 }
196
197 @Override
198 public boolean isCellEditable( int row, int col ) {
199 if (baseModel == null)
200 return false;
201
202 return baseModel.isCellEditable( row, col );
203 }
204
205 public Object getValueAt( int row, int col ) {
206 if (baseModel == null)
207 return null;
208
209 return baseModel.getValueAt(viewToModelMapping.get(row).intValue(), col);
210 }
211
212 @Override
213 public void setValueAt( Object value, int row, int col ) {
214 if (baseModel == null)
215 return;
216
217 baseModel.setValueAt( value, viewToModelMapping.get(row).intValue(), col );
218 fireTableDataChanged();
219 }
220
221 private void setupMapping() {
222 int numRows = baseModel.getRowCount();
223 viewToModelMapping = new ArrayList<Integer>(numRows);
224 for (int i = 0; i < numRows; i++)
225 viewToModelMapping.add(i);
226
227 Collections.sort( viewToModelMapping, new Comparator<Integer>() {
228 @SuppressWarnings("unchecked")
229 public int compare( Integer a, Integer b ) {
230 if ((sortDirection == SORT_NO_ORDER) || (sortColumn == -1))
231 return a.compareTo(b);
232
233 Comparable<Object> first = (Comparable<Object>)baseModel.getValueAt( a.intValue(), sortColumn );
234 Comparable<Object> second = (Comparable<Object>)baseModel.getValueAt( b.intValue(), sortColumn );
235
236 if (sortDirection == SORT_ASCENDING_ORDER)
237 return first.compareTo(second);
238 else
239 return second.compareTo(first);
240 }
241 });
242
243 }
244
245 private class BaseTableModelListener implements TableModelListener
246 {
247 public void tableChanged( TableModelEvent e ) {
248 DefaultSortedTableModel.this.fireTableChanged(e);
249 }
250 }
251
252 private class HeaderListener extends MouseAdapter
253 {
254 @Override
255 public void mouseClicked(MouseEvent e) {
256 JTableHeader header = (JTableHeader)e.getSource();
257 int column = header.columnAtPoint(e.getPoint());
258 column = header.getTable().convertColumnIndexToModel(column);
259 if (column != sortColumn) {
260 sortColumn = column;
261 sortDirection = SORT_ASCENDING_ORDER;
262 } else {
263 sortDirection = (sortDirection + 1) % NUM_SORT_DIREECTIONS;
264 }
265 super.mouseClicked(e);
266 DefaultSortedTableModel.this.fireTableDataChanged();
267 }
268 }
269 }
+0
-1575
src/obsolete/edu/umd/cs/findbugs/gui/FindBugsFrame.form less more
0 <?xml version="1.0" encoding="UTF-8" ?>
1
2 <Form version="1.0" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
3 <NonVisualComponents>
4 <Component class="javax.swing.ButtonGroup" name="priorityButtonGroup">
5 </Component>
6 <Component class="javax.swing.ButtonGroup" name="effortButtonGroup">
7 </Component>
8 <Menu class="javax.swing.JMenuBar" name="theMenuBar">
9 <Properties>
10 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
11 <Font name="Dialog" size="12" style="0"/>
12 </Property>
13 </Properties>
14 <SubComponents>
15 <Menu class="javax.swing.JMenu" name="fileMenu">
16 <Properties>
17 <Property name="text" type="java.lang.String" value="File"/>
18 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
19 <Font name="Dialog" size="12" style="0"/>
20 </Property>
21 </Properties>
22 <Events>
23 <EventHandler event="menuSelected" listener="javax.swing.event.MenuListener" parameters="javax.swing.event.MenuEvent" handler="fileMenuMenuSelected"/>
24 </Events>
25 <AuxValues>
26 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(fileMenu, &quot;menu.file_menu&quot;, &quot;&amp;File&quot;, true);"/>
27 </AuxValues>
28 <SubComponents>
29 <MenuItem class="javax.swing.JMenuItem" name="newProjectItem">
30 <Properties>
31 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
32 <Font name="Dialog" size="12" style="0"/>
33 </Property>
34 <Property name="text" type="java.lang.String" value="New Project"/>
35 </Properties>
36 <Events>
37 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="newProjectItemActionPerformed"/>
38 </Events>
39 <AuxValues>
40 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(newProjectItem, &quot;menu.new_item&quot;, &quot;&amp;New Project&quot;, true);"/>
41 </AuxValues>
42 </MenuItem>
43 <MenuItem class="javax.swing.JMenuItem" name="openProjectItem">
44 <Properties>
45 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
46 <Font name="Dialog" size="12" style="0"/>
47 </Property>
48 <Property name="text" type="java.lang.String" value="Open Project..."/>
49 </Properties>
50 <Events>
51 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="openProjectItemActionPerformed"/>
52 </Events>
53 <AuxValues>
54 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(openProjectItem, &quot;menu.open_item&quot;, &quot;&amp;Open Project...&quot;, true);"/>
55 </AuxValues>
56 </MenuItem>
57 <Menu class="javax.swing.JMenu" name="recentProjectsMenu">
58 <Properties>
59 <Property name="text" type="java.lang.String" value="Recent Projects"/>
60 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
61 <Font name="Dialog" size="12" style="0"/>
62 </Property>
63 </Properties>
64 <AuxValues>
65 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(recentProjectsMenu, &quot;menu.recent_menu&quot;, &quot;R&amp;ecent Projects&quot;, true);&#xa;rebuildRecentProjectsMenu();"/>
66 </AuxValues>
67 </Menu>
68 <MenuItem class="javax.swing.JSeparator" name="jSeparator9">
69 </MenuItem>
70 <MenuItem class="javax.swing.JMenuItem" name="closeProjectItem">
71 <Properties>
72 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
73 <Font name="Dialog" size="12" style="0"/>
74 </Property>
75 <Property name="text" type="java.lang.String" value="Close Project"/>
76 </Properties>
77 <Events>
78 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="closeProjectItemActionPerformed"/>
79 </Events>
80 <AuxValues>
81 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(closeProjectItem, &quot;menu.close_item&quot;, &quot;&amp;Close Project&quot;, true);"/>
82 </AuxValues>
83 </MenuItem>
84 <MenuItem class="javax.swing.JMenuItem" name="saveProjectItem">
85 <Properties>
86 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
87 <Font name="Dialog" size="12" style="0"/>
88 </Property>
89 <Property name="text" type="java.lang.String" value="Save Project"/>
90 </Properties>
91 <Events>
92 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="saveProjectItemActionPerformed"/>
93 </Events>
94 <AuxValues>
95 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(saveProjectItem, &quot;menu.save_item&quot;, &quot;&amp;Save Project&quot;, true);"/>
96 </AuxValues>
97 </MenuItem>
98 <MenuItem class="javax.swing.JMenuItem" name="saveProjectAsItem">
99 <Properties>
100 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
101 <Font name="Dialog" size="12" style="0"/>
102 </Property>
103 <Property name="text" type="java.lang.String" value="Save Project As..."/>
104 </Properties>
105 <Events>
106 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="saveProjectAsItemActionPerformed"/>
107 </Events>
108 <AuxValues>
109 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(saveProjectAsItem, &quot;menu.saveas_item&quot;, &quot;Save Project &amp;As...&quot;, true);"/>
110 </AuxValues>
111 </MenuItem>
112 <MenuItem class="javax.swing.JMenuItem" name="reloadProjectItem">
113 <Properties>
114 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
115 <Font name="Dialog" size="12" style="0"/>
116 </Property>
117 <Property name="text" type="java.lang.String" value="Reload Project"/>
118 </Properties>
119 <Events>
120 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="reloadProjectItemActionPerformed"/>
121 </Events>
122 <AuxValues>
123 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(reloadProjectItem, &quot;menu.reload_item&quot;, &quot;&amp;Reload Project&quot;, true);"/>
124 </AuxValues>
125 </MenuItem>
126 <MenuItem class="javax.swing.JSeparator" name="jSeparator3">
127 </MenuItem>
128 <MenuItem class="javax.swing.JMenuItem" name="loadBugsItem">
129 <Properties>
130 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
131 <Font name="Dialog" size="12" style="0"/>
132 </Property>
133 <Property name="text" type="java.lang.String" value="Load Bugs..."/>
134 </Properties>
135 <Events>
136 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="loadBugsItemActionPerformed"/>
137 </Events>
138 <AuxValues>
139 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(loadBugsItem, &quot;menu.loadbugs_item&quot;, &quot;&amp;Load Bugs...&quot;, true);"/>
140 </AuxValues>
141 </MenuItem>
142 <MenuItem class="javax.swing.JMenuItem" name="saveBugsItem">
143 <Properties>
144 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
145 <Font name="Dialog" size="12" style="0"/>
146 </Property>
147 <Property name="text" type="java.lang.String" value="Save Bugs"/>
148 </Properties>
149 <Events>
150 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="saveBugsItemActionPerformed"/>
151 </Events>
152 <AuxValues>
153 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(saveBugsItem, &quot;menu.savebugs_item&quot;, &quot;Save &amp;Bugs...&quot;, true);"/>
154 </AuxValues>
155 </MenuItem>
156 <MenuItem class="javax.swing.JSeparator" name="jSeparator6">
157 </MenuItem>
158 <MenuItem class="javax.swing.JMenuItem" name="exitItem">
159 <Properties>
160 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
161 <Font name="Dialog" size="12" style="0"/>
162 </Property>
163 <Property name="text" type="java.lang.String" value="Exit"/>
164 </Properties>
165 <Events>
166 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="exitItemActionPerformed"/>
167 </Events>
168 <AuxValues>
169 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(exitItem, &quot;menu.exit_item&quot;, &quot;E&amp;xit&quot;, true);"/>
170 </AuxValues>
171 </MenuItem>
172 </SubComponents>
173 </Menu>
174 <Menu class="javax.swing.JMenu" name="editMenu">
175 <Properties>
176 <Property name="text" type="java.lang.String" value="Edit"/>
177 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
178 <Font name="Dialog" size="12" style="0"/>
179 </Property>
180 <Property name="enabled" type="boolean" value="false"/>
181 </Properties>
182 <AuxValues>
183 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(editMenu, &quot;menu.edit_menu&quot;, &quot;&amp;Edit&quot;, true);"/>
184 </AuxValues>
185 <SubComponents>
186 <MenuItem class="javax.swing.JMenuItem" name="cutItem">
187 <Properties>
188 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
189 <Font name="Dialog" size="12" style="0"/>
190 </Property>
191 <Property name="text" type="java.lang.String" value="Cut"/>
192 </Properties>
193 <Events>
194 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cutActionPerformed"/>
195 </Events>
196 <AuxValues>
197 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(cutItem, &quot;menu.cut_item&quot;, &quot;Cut&quot;, true);"/>
198 </AuxValues>
199 </MenuItem>
200 <MenuItem class="javax.swing.JMenuItem" name="copyItem">
201 <Properties>
202 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
203 <Font name="Dialog" size="12" style="0"/>
204 </Property>
205 <Property name="text" type="java.lang.String" value="Copy"/>
206 </Properties>
207 <Events>
208 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="copyActionPerformed"/>
209 </Events>
210 <AuxValues>
211 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(copyItem, &quot;menu.copy_item&quot;, &quot;Copy&quot;, true);"/>
212 </AuxValues>
213 </MenuItem>
214 <MenuItem class="javax.swing.JMenuItem" name="pasteItem">
215 <Properties>
216 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
217 <Font name="Dialog" size="12" style="0"/>
218 </Property>
219 <Property name="text" type="java.lang.String" value="Paste"/>
220 </Properties>
221 <Events>
222 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="pasteActionPerformed"/>
223 </Events>
224 <AuxValues>
225 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(pasteItem, &quot;menu.paste_item&quot;, &quot;Paste&quot;, true);"/>
226 </AuxValues>
227 </MenuItem>
228 <MenuItem class="javax.swing.JSeparator" name="jSeparator10">
229 </MenuItem>
230 <MenuItem class="javax.swing.JMenuItem" name="selectAllItem">
231 <Properties>
232 <Property name="accelerator" type="javax.swing.KeyStroke" editor="org.netbeans.modules.form.editors.KeyStrokeEditor">
233 <KeyStroke key="Ctrl+A"/>
234 </Property>
235 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
236 <Font name="Dialog" size="12" style="0"/>
237 </Property>
238 <Property name="text" type="java.lang.String" value="Select All"/>
239 </Properties>
240 <Events>
241 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="selectAllActionPerformed"/>
242 </Events>
243 <AuxValues>
244 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(selectAllItem, &quot;menu.selectall_item&quot;, &quot;Select &amp;All&quot;, true);"/>
245 </AuxValues>
246 </MenuItem>
247 </SubComponents>
248 </Menu>
249 <Menu class="javax.swing.JMenu" name="viewMenu">
250 <Properties>
251 <Property name="text" type="java.lang.String" value="View"/>
252 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
253 <Font name="Dialog" size="12" style="0"/>
254 </Property>
255 </Properties>
256 <Events>
257 <EventHandler event="menuSelected" listener="javax.swing.event.MenuListener" parameters="javax.swing.event.MenuEvent" handler="viewMenuMenuSelected"/>
258 </Events>
259 <AuxValues>
260 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(viewMenu, &quot;menu.view_menu&quot;, &quot;&amp;View&quot;, true);&#xa;"/>
261 </AuxValues>
262 <SubComponents>
263 <MenuItem class="javax.swing.JCheckBoxMenuItem" name="viewConsoleItem">
264 <Properties>
265 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
266 <Font name="Dialog" size="12" style="0"/>
267 </Property>
268 <Property name="text" type="java.lang.String" value="Console"/>
269 </Properties>
270 <Events>
271 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="viewConsoleItemActionPerformed"/>
272 </Events>
273 <AuxValues>
274 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(viewConsoleItem, &quot;menu.console_item&quot;, &quot;&amp;Console&quot;, true);"/>
275 </AuxValues>
276 </MenuItem>
277 <MenuItem class="javax.swing.JCheckBoxMenuItem" name="viewBugDetailsItem">
278 <Properties>
279 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
280 <Font name="Dialog" size="12" style="0"/>
281 </Property>
282 <Property name="selected" type="boolean" value="true"/>
283 <Property name="text" type="java.lang.String" value="Bug Details"/>
284 </Properties>
285 <Events>
286 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="viewBugDetailsItemActionPerformed"/>
287 </Events>
288 <AuxValues>
289 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(viewBugDetailsItem, &quot;menu.bugdetails_item&quot;, &quot;Bug &amp;Details&quot;, true);"/>
290 </AuxValues>
291 </MenuItem>
292 <MenuItem class="javax.swing.JCheckBoxMenuItem" name="fullDescriptionsItem">
293 <Properties>
294 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
295 <Font name="Dialog" size="12" style="0"/>
296 </Property>
297 <Property name="selected" type="boolean" value="true"/>
298 <Property name="text" type="java.lang.String" value="Full Descriptions"/>
299 </Properties>
300 <Events>
301 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="fullDescriptionsItemActionPerformed"/>
302 </Events>
303 <AuxValues>
304 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(fullDescriptionsItem, &quot;menu.fulldescriptions_item&quot;, &quot;&amp;Full Descriptions&quot;, true);"/>
305 </AuxValues>
306 </MenuItem>
307 <MenuItem class="javax.swing.JSeparator" name="jSeparator7">
308 </MenuItem>
309 <Menu class="javax.swing.JMenu" name="filterWarningsMenu">
310 <Properties>
311 <Property name="text" type="java.lang.String" value="Filter Warnings"/>
312 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
313 <Font name="Dialog" size="12" style="0"/>
314 </Property>
315 </Properties>
316 <AuxValues>
317 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(filterWarningsMenu, &quot;menu.filterwarnings_menu&quot;, &quot;Filter &amp;Warnings&quot;, true);"/>
318 </AuxValues>
319 <SubComponents>
320 <MenuItem class="javax.swing.JRadioButtonMenuItem" name="expPriorityButton">
321 <Properties>
322 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
323 <Font name="Dialog" size="12" style="0"/>
324 </Property>
325 <Property name="text" type="java.lang.String" value="Experimental Priority"/>
326 <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
327 <ComponentRef name="priorityButtonGroup"/>
328 </Property>
329 </Properties>
330 <Events>
331 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="expPriorityButtonActionPerformed"/>
332 </Events>
333 <AuxValues>
334 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(expPriorityButton, &quot;menu.exppriority_item&quot;, &quot;&amp;Experimental Priority&quot;, true);&#xa;expPriorityButton.setSelected(getPriorityThreshold() == Detector.EXP_PRIORITY);"/>
335 </AuxValues>
336 </MenuItem>
337 <MenuItem class="javax.swing.JRadioButtonMenuItem" name="lowPriorityButton">
338 <Properties>
339 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
340 <Font name="Dialog" size="12" style="0"/>
341 </Property>
342 <Property name="text" type="java.lang.String" value="Low Priority"/>
343 <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
344 <ComponentRef name="priorityButtonGroup"/>
345 </Property>
346 </Properties>
347 <Events>
348 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="lowPriorityButtonActionPerformed"/>
349 </Events>
350 <AuxValues>
351 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(lowPriorityButton, &quot;menu.lowpriority_item&quot;, &quot;&amp;Low Priority&quot;, true);&#xa;lowPriorityButton.setSelected(getPriorityThreshold() == Detector.LOW_PRIORITY);"/>
352 </AuxValues>
353 </MenuItem>
354 <MenuItem class="javax.swing.JRadioButtonMenuItem" name="mediumPriorityButton">
355 <Properties>
356 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
357 <Font name="Dialog" size="12" style="0"/>
358 </Property>
359 <Property name="text" type="java.lang.String" value="Medium Priority"/>
360 <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
361 <ComponentRef name="priorityButtonGroup"/>
362 </Property>
363 </Properties>
364 <Events>
365 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="mediumPriorityButtonActionPerformed"/>
366 </Events>
367 <AuxValues>
368 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(mediumPriorityButton, &quot;menu.mediumpriority_item&quot;, &quot;&amp;Medium Priority&quot;, true);&#xa;mediumPriorityButton.setSelected(getPriorityThreshold() == Detector.NORMAL_PRIORITY);"/>
369 </AuxValues>
370 </MenuItem>
371 <MenuItem class="javax.swing.JRadioButtonMenuItem" name="highPriorityButton">
372 <Properties>
373 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
374 <Font name="Dialog" size="12" style="0"/>
375 </Property>
376 <Property name="text" type="java.lang.String" value="High Priority"/>
377 <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
378 <ComponentRef name="priorityButtonGroup"/>
379 </Property>
380 </Properties>
381 <Events>
382 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="highPriorityButtonActionPerformed"/>
383 </Events>
384 <AuxValues>
385 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(highPriorityButton, &quot;menu.highpriority_item&quot;, &quot;&amp;High Priority&quot;, true);&#xa;highPriorityButton.setSelected(getPriorityThreshold() == Detector.HIGH_PRIORITY);"/>
386 </AuxValues>
387 </MenuItem>
388 <MenuItem class="javax.swing.JSeparator" name="jSeparator11">
389 </MenuItem>
390 </SubComponents>
391 </Menu>
392 <MenuItem class="javax.swing.JSeparator" name="jSeparator8">
393 <AuxValues>
394 <AuxValue name="JavaCodeGenerator_InitCodePre" type="java.lang.String" value="ButtonGroup bg = new ButtonGroup();&#xa;bg.add(expPriorityButton);&#xa;bg.add(lowPriorityButton);&#xa;bg.add(mediumPriorityButton);&#xa;bg.add(highPriorityButton);&#xa;&#xa;"/>
395 </AuxValues>
396 </MenuItem>
397 <MenuItem class="javax.swing.JRadioButtonMenuItem" name="viewProjectItem">
398 <Properties>
399 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
400 <Font name="Dialog" size="12" style="0"/>
401 </Property>
402 <Property name="text" type="java.lang.String" value="View Project Details"/>
403 <Property name="enabled" type="boolean" value="false"/>
404 </Properties>
405 <Events>
406 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="viewProjectItemActionPerformed"/>
407 </Events>
408 <AuxValues>
409 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(viewProjectItem, &quot;menu.viewprojectdetails_item&quot;, &quot;View Project Details&quot;, true);"/>
410 </AuxValues>
411 </MenuItem>
412 <MenuItem class="javax.swing.JRadioButtonMenuItem" name="viewBugsItem">
413 <Properties>
414 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
415 <Font name="Dialog" size="12" style="0"/>
416 </Property>
417 <Property name="text" type="java.lang.String" value="View Bugs"/>
418 <Property name="enabled" type="boolean" value="false"/>
419 </Properties>
420 <Events>
421 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="viewBugsItemActionPerformed"/>
422 </Events>
423 <AuxValues>
424 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(viewBugsItem, &quot;menu.viewbugs_item&quot;, &quot;View Bugs&quot;, true);"/>
425 </AuxValues>
426 </MenuItem>
427 </SubComponents>
428 </Menu>
429 <Menu class="javax.swing.JMenu" name="settingsMenu">
430 <Properties>
431 <Property name="text" type="java.lang.String" value="Settings"/>
432 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
433 <Font name="Dialog" size="12" style="0"/>
434 </Property>
435 </Properties>
436 <Events>
437 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="settingsMenuActionPerformed"/>
438 </Events>
439 <AuxValues>
440 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(settingsMenu, &quot;menu.settings_menu&quot;, &quot;&amp;Settings&quot;, true);"/>
441 </AuxValues>
442 <SubComponents>
443 <MenuItem class="javax.swing.JMenuItem" name="configureDetectorsItem">
444 <Properties>
445 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
446 <Font name="Dialog" size="12" style="0"/>
447 </Property>
448 <Property name="text" type="java.lang.String" value="Configure Detectors..."/>
449 </Properties>
450 <Events>
451 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="configureDetectorsItemActionPerformed"/>
452 </Events>
453 <AuxValues>
454 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(configureDetectorsItem, &quot;menu.configure_item&quot;, &quot;&amp;Configure Detectors...&quot;, true);"/>
455 </AuxValues>
456 </MenuItem>
457 <Menu class="javax.swing.JMenu" name="effortMenu">
458 <Properties>
459 <Property name="text" type="java.lang.String" value="Effort"/>
460 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
461 <Font name="Dialog" size="12" style="0"/>
462 </Property>
463 </Properties>
464 <AuxValues>
465 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(effortMenu, &quot;menu.effort_menu&quot;, &quot;Effort&quot;, true);"/>
466 </AuxValues>
467 <SubComponents>
468 <MenuItem class="javax.swing.JCheckBoxMenuItem" name="minEffortItem">
469 <Properties>
470 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
471 <Font name="Dialog" size="12" style="0"/>
472 </Property>
473 <Property name="text" type="java.lang.String" value="Minimum"/>
474 <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
475 <ComponentRef name="effortButtonGroup"/>
476 </Property>
477 </Properties>
478 <Events>
479 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="minEffortItemActionPerformed"/>
480 </Events>
481 <AuxValues>
482 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(minEffortItem, &quot;menu.mineffort_item&quot;, &quot;&amp;Minimum&quot;, true);"/>
483 </AuxValues>
484 </MenuItem>
485 <MenuItem class="javax.swing.JCheckBoxMenuItem" name="normalEffortItem">
486 <Properties>
487 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
488 <Font name="Dialog" size="12" style="0"/>
489 </Property>
490 <Property name="selected" type="boolean" value="true"/>
491 <Property name="text" type="java.lang.String" value="Normal"/>
492 <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
493 <ComponentRef name="effortButtonGroup"/>
494 </Property>
495 </Properties>
496 <Events>
497 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="normalEffortItemActionPerformed"/>
498 </Events>
499 <AuxValues>
500 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(normalEffortItem, &quot;menu.normaleffort_item&quot;, &quot;&amp;Normal&quot;, true);"/>
501 </AuxValues>
502 </MenuItem>
503 <MenuItem class="javax.swing.JCheckBoxMenuItem" name="maxEffortItem">
504 <Properties>
505 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
506 <Font name="Dialog" size="12" style="0"/>
507 </Property>
508 <Property name="text" type="java.lang.String" value="Maximum"/>
509 <Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
510 <ComponentRef name="effortButtonGroup"/>
511 </Property>
512 </Properties>
513 <Events>
514 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="maxEffortItemActionPerformed"/>
515 </Events>
516 <AuxValues>
517 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(maxEffortItem, &quot;menu.maxeffort_item&quot;, &quot;&amp;Maximum&quot;, true);"/>
518 </AuxValues>
519 </MenuItem>
520 </SubComponents>
521 </Menu>
522 </SubComponents>
523 </Menu>
524 <Menu class="javax.swing.JMenu" name="helpMenu">
525 <Properties>
526 <Property name="text" type="java.lang.String" value="Help"/>
527 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
528 <Font name="Dialog" size="12" style="0"/>
529 </Property>
530 </Properties>
531 <AuxValues>
532 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(helpMenu, &quot;menu.help_menu&quot;, &quot;&amp;Help&quot;, true);"/>
533 </AuxValues>
534 <SubComponents>
535 <MenuItem class="javax.swing.JMenuItem" name="aboutItem">
536 <Properties>
537 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
538 <Font name="Dialog" size="12" style="0"/>
539 </Property>
540 <Property name="text" type="java.lang.String" value="About..."/>
541 </Properties>
542 <Events>
543 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="aboutItemActionPerformed"/>
544 </Events>
545 <AuxValues>
546 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="localiseButton(aboutItem, &quot;menu.about_item&quot;, &quot;&amp;About&quot;, true);"/>
547 </AuxValues>
548 </MenuItem>
549 </SubComponents>
550 </Menu>
551 </SubComponents>
552 </Menu>
553 </NonVisualComponents>
554 <SyntheticProperties>
555 <SyntheticProperty name="menuBar" type="java.lang.String" value="theMenuBar"/>
556 <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
557 </SyntheticProperties>
558 <Events>
559 <EventHandler event="windowClosing" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="exitForm"/>
560 <EventHandler event="windowOpened" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="formWindowOpened"/>
561 </Events>
562 <AuxValues>
563 <AuxValue name="designerSize" type="java.awt.Dimension" value="-84,-19,0,5,115,114,0,18,106,97,118,97,46,97,119,116,46,68,105,109,101,110,115,105,111,110,65,-114,-39,-41,-84,95,68,20,2,0,2,73,0,6,104,101,105,103,104,116,73,0,5,119,105,100,116,104,120,112,0,0,1,44,0,0,1,-58"/>
564 </AuxValues>
565
566 <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
567 <SubComponents>
568 <Container class="javax.swing.JSplitPane" name="consoleSplitter">
569 <Properties>
570 <Property name="orientation" type="int" value="0"/>
571 <Property name="resizeWeight" type="double" value="1.0"/>
572 <Property name="oneTouchExpandable" type="boolean" value="true"/>
573 </Properties>
574 <Events>
575 <EventHandler event="propertyChange" listener="java.beans.PropertyChangeListener" parameters="java.beans.PropertyChangeEvent" handler="consoleSplitterPropertyChange"/>
576 </Events>
577 <Constraints>
578 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
579 <GridBagConstraints gridX="0" gridY="0" gridWidth="2" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="1.0"/>
580 </Constraint>
581 </Constraints>
582
583 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
584 <SubComponents>
585 <Container class="javax.swing.JPanel" name="viewPanel">
586 <Constraints>
587 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
588 <JSplitPaneConstraints position="top"/>
589 </Constraint>
590 </Constraints>
591
592 <Layout class="org.netbeans.modules.form.compat2.layouts.DesignCardLayout"/>
593 <SubComponents>
594 <Container class="javax.swing.JPanel" name="emptyPanel">
595 <Constraints>
596 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
597 <CardConstraints cardName="EmptyPanel"/>
598 </Constraint>
599 </Constraints>
600
601 <Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
602 </Container>
603 <Container class="javax.swing.JPanel" name="reportPanel">
604 <Constraints>
605 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
606 <CardConstraints cardName="ReportPanel"/>
607 </Constraint>
608 </Constraints>
609
610 <Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
611 </Container>
612 <Container class="javax.swing.JPanel" name="editProjectPanel">
613 <Constraints>
614 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
615 <CardConstraints cardName="EditProjectPanel"/>
616 </Constraint>
617 </Constraints>
618
619 <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
620 <SubComponents>
621 <Component class="javax.swing.JLabel" name="jarFileLabel">
622 <Properties>
623 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
624 <Font name="Dialog" size="12" style="0"/>
625 </Property>
626 <Property name="text" type="java.lang.String" value="Archive or directory:"/>
627 </Properties>
628 <AuxValues>
629 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="jarFileLabel.setText(L10N.getLocalString(&quot;dlg.jarfile_lbl&quot;, &quot;Archive or Directory:&quot;));"/>
630 </AuxValues>
631 <Constraints>
632 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
633 <GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="3" anchor="13" weightX="0.0" weightY="0.0"/>
634 </Constraint>
635 </Constraints>
636 </Component>
637 <Component class="javax.swing.JTextField" name="jarNameTextField">
638 <Events>
639 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="jarNameTextFieldActionPerformed"/>
640 <EventHandler event="focusGained" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="focusGainedHandler"/>
641 </Events>
642 <Constraints>
643 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
644 <GridBagConstraints gridX="1" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="0.0"/>
645 </Constraint>
646 </Constraints>
647 </Component>
648 <Component class="javax.swing.JButton" name="addJarButton">
649 <Properties>
650 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
651 <Font name="Dialog" size="12" style="0"/>
652 </Property>
653 <Property name="text" type="java.lang.String" value="Add"/>
654 <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
655 <Dimension value="[90, 25]"/>
656 </Property>
657 <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
658 <Dimension value="[90, 25]"/>
659 </Property>
660 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
661 <Dimension value="[90, 25]"/>
662 </Property>
663 </Properties>
664 <Events>
665 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="addJarButtonActionPerformed"/>
666 </Events>
667 <AuxValues>
668 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="addJarButton.setText(L10N.getLocalString(&quot;dlg.add_btn&quot;, &quot;Add&quot;));"/>
669 </AuxValues>
670 <Constraints>
671 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
672 <GridBagConstraints gridX="3" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="3" anchor="17" weightX="0.0" weightY="0.0"/>
673 </Constraint>
674 </Constraints>
675 </Component>
676 <Component class="javax.swing.JLabel" name="jarFileListLabel">
677 <Properties>
678 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
679 <Font name="Dialog" size="12" style="0"/>
680 </Property>
681 <Property name="text" type="java.lang.String" value="Archives/directories:"/>
682 </Properties>
683 <AuxValues>
684 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="jarFileListLabel.setText(L10N.getLocalString(&quot;dlg.jarlist_lbl&quot;, &quot;Archives/Directories:&quot;));"/>
685 </AuxValues>
686 <Constraints>
687 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
688 <GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="3" anchor="13" weightX="0.0" weightY="0.0"/>
689 </Constraint>
690 </Constraints>
691 </Component>
692 <Component class="javax.swing.JLabel" name="sourceDirLabel">
693 <Properties>
694 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
695 <Font name="Dialog" size="12" style="0"/>
696 </Property>
697 <Property name="text" type="java.lang.String" value="Source directory:"/>
698 </Properties>
699 <AuxValues>
700 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="sourceDirLabel.setText(L10N.getLocalString(&quot;dlg.srcfile_lbl&quot;, &quot;Source directory:&quot;));"/>
701 </AuxValues>
702 <Constraints>
703 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
704 <GridBagConstraints gridX="0" gridY="8" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="3" anchor="13" weightX="0.0" weightY="0.0"/>
705 </Constraint>
706 </Constraints>
707 </Component>
708 <Component class="javax.swing.JTextField" name="srcDirTextField">
709 <Events>
710 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="srcDirTextFieldActionPerformed"/>
711 <EventHandler event="focusGained" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="focusGainedHandler"/>
712 </Events>
713 <Constraints>
714 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
715 <GridBagConstraints gridX="1" gridY="8" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="0.0"/>
716 </Constraint>
717 </Constraints>
718 </Component>
719 <Component class="javax.swing.JButton" name="addSourceDirButton">
720 <Properties>
721 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
722 <Font name="Dialog" size="12" style="0"/>
723 </Property>
724 <Property name="text" type="java.lang.String" value="Add"/>
725 <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
726 <Dimension value="[90, 25]"/>
727 </Property>
728 <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
729 <Dimension value="[90, 25]"/>
730 </Property>
731 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
732 <Dimension value="[90, 25]"/>
733 </Property>
734 </Properties>
735 <Events>
736 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="addSourceDirButtonActionPerformed"/>
737 </Events>
738 <AuxValues>
739 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="addSourceDirButton.setText(L10N.getLocalString(&quot;dlg.add_btn&quot;, &quot;Add&quot;));"/>
740 </AuxValues>
741 <Constraints>
742 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
743 <GridBagConstraints gridX="3" gridY="8" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="3" anchor="17" weightX="0.0" weightY="0.0"/>
744 </Constraint>
745 </Constraints>
746 </Component>
747 <Component class="javax.swing.JLabel" name="sourceDirListLabel">
748 <Properties>
749 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
750 <Font name="Dialog" size="12" style="0"/>
751 </Property>
752 <Property name="text" type="java.lang.String" value="Source directories:"/>
753 </Properties>
754 <AuxValues>
755 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="sourceDirListLabel.setText(L10N.getLocalString(&quot;dlg.srclist_lbl&quot;, &quot;Source directories:&quot;));"/>
756 </AuxValues>
757 <Constraints>
758 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
759 <GridBagConstraints gridX="0" gridY="9" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="3" anchor="13" weightX="0.0" weightY="0.0"/>
760 </Constraint>
761 </Constraints>
762 </Component>
763 <Component class="javax.swing.JButton" name="removeJarButton">
764 <Properties>
765 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
766 <Font name="Dialog" size="12" style="0"/>
767 </Property>
768 <Property name="text" type="java.lang.String" value="Remove"/>
769 <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
770 <Dimension value="[90, 25]"/>
771 </Property>
772 <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
773 <Dimension value="[90, 25]"/>
774 </Property>
775 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
776 <Dimension value="[90, 25]"/>
777 </Property>
778 </Properties>
779 <Events>
780 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="removeJarButtonActionPerformed"/>
781 </Events>
782 <AuxValues>
783 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="removeJarButton.setText(L10N.getLocalString(&quot;dlg.remove_btn&quot;, &quot;Remove&quot;));"/>
784 </AuxValues>
785 <Constraints>
786 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
787 <GridBagConstraints gridX="3" gridY="3" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="3" anchor="17" weightX="0.0" weightY="0.0"/>
788 </Constraint>
789 </Constraints>
790 </Component>
791 <Component class="javax.swing.JButton" name="removeSrcDirButton">
792 <Properties>
793 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
794 <Font name="Dialog" size="12" style="0"/>
795 </Property>
796 <Property name="text" type="java.lang.String" value="Remove"/>
797 <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
798 <Dimension value="[90, 25]"/>
799 </Property>
800 <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
801 <Dimension value="[90, 25]"/>
802 </Property>
803 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
804 <Dimension value="[90, 25]"/>
805 </Property>
806 </Properties>
807 <Events>
808 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="removeSrcDirButtonActionPerformed"/>
809 </Events>
810 <AuxValues>
811 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="removeSrcDirButton.setText(L10N.getLocalString(&quot;dlg.remove_btn&quot;, &quot;Remove&quot;));"/>
812 </AuxValues>
813 <Constraints>
814 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
815 <GridBagConstraints gridX="3" gridY="9" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="3" anchor="17" weightX="0.0" weightY="0.0"/>
816 </Constraint>
817 </Constraints>
818 </Component>
819 <Component class="javax.swing.JSeparator" name="jSeparator1">
820 <Constraints>
821 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
822 <GridBagConstraints gridX="0" gridY="1" gridWidth="4" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="3" insetsBottom="3" insetsRight="3" anchor="10" weightX="0.0" weightY="0.0"/>
823 </Constraint>
824 </Constraints>
825 </Component>
826 <Component class="javax.swing.JButton" name="browseJarButton">
827 <Properties>
828 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
829 <Font name="Dialog" size="12" style="0"/>
830 </Property>
831 <Property name="text" type="java.lang.String" value="Browse"/>
832 <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
833 <Dimension value="[90, 25]"/>
834 </Property>
835 <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
836 <Dimension value="[90, 25]"/>
837 </Property>
838 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
839 <Dimension value="[90, 25]"/>
840 </Property>
841 </Properties>
842 <Events>
843 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseJarButtonActionPerformed"/>
844 </Events>
845 <AuxValues>
846 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="browseJarButton.setText(L10N.getLocalString(&quot;dlg.browse_btn&quot;, &quot;Browse...&quot;));"/>
847 </AuxValues>
848 <Constraints>
849 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
850 <GridBagConstraints gridX="2" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="3" anchor="10" weightX="0.0" weightY="0.0"/>
851 </Constraint>
852 </Constraints>
853 </Component>
854 <Component class="javax.swing.JButton" name="browseSrcDirButton">
855 <Properties>
856 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
857 <Font name="Dialog" size="12" style="0"/>
858 </Property>
859 <Property name="text" type="java.lang.String" value="Browse"/>
860 <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
861 <Dimension value="[90, 25]"/>
862 </Property>
863 <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
864 <Dimension value="[90, 25]"/>
865 </Property>
866 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
867 <Dimension value="[90, 25]"/>
868 </Property>
869 </Properties>
870 <Events>
871 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseSrcDirButtonActionPerformed"/>
872 </Events>
873 <AuxValues>
874 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="browseSrcDirButton.setText(L10N.getLocalString(&quot;dlg.browse_btn&quot;, &quot;Browse...&quot;));"/>
875 </AuxValues>
876 <Constraints>
877 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
878 <GridBagConstraints gridX="2" gridY="8" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="3" anchor="10" weightX="0.0" weightY="0.0"/>
879 </Constraint>
880 </Constraints>
881 </Component>
882 <Component class="javax.swing.JLabel" name="editProjectLabel">
883 <Properties>
884 <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
885 <Color blue="cc" green="0" red="0" type="rgb"/>
886 </Property>
887 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
888 <Font name="Dialog" size="24" style="1"/>
889 </Property>
890 <Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
891 <Color blue="ff" green="ff" red="ff" type="rgb"/>
892 </Property>
893 <Property name="text" type="java.lang.String" value="Project"/>
894 <Property name="opaque" type="boolean" value="true"/>
895 </Properties>
896 <AuxValues>
897 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="editProjectLabel.setText(L10N.getLocalString(&quot;dlg.project_lbl&quot;, &quot;Project&quot;));"/>
898 </AuxValues>
899 <Constraints>
900 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
901 <GridBagConstraints gridX="0" gridY="0" gridWidth="4" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
902 </Constraint>
903 </Constraints>
904 </Component>
905 <Component class="javax.swing.JSeparator" name="jSeparator2">
906 <Constraints>
907 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
908 <GridBagConstraints gridX="0" gridY="7" gridWidth="4" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="3" insetsBottom="3" insetsRight="3" anchor="10" weightX="0.0" weightY="0.0"/>
909 </Constraint>
910 </Constraints>
911 </Component>
912 <Component class="javax.swing.JButton" name="findBugsButton">
913 <Properties>
914 <Property name="mnemonic" type="int" value="66"/>
915 <Property name="text" type="java.lang.String" value="Find Bugs!"/>
916 </Properties>
917 <Events>
918 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="findBugsButtonActionPerformed"/>
919 </Events>
920 <Constraints>
921 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
922 <GridBagConstraints gridX="0" gridY="21" gridWidth="4" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="3" insetsBottom="3" insetsRight="3" anchor="10" weightX="0.0" weightY="0.0"/>
923 </Constraint>
924 </Constraints>
925 </Component>
926 <Component class="javax.swing.JSeparator" name="jSeparator4">
927 <Constraints>
928 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
929 <GridBagConstraints gridX="0" gridY="14" gridWidth="4" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="3" insetsBottom="3" insetsRight="3" anchor="10" weightX="0.0" weightY="0.0"/>
930 </Constraint>
931 </Constraints>
932 </Component>
933 <Container class="javax.swing.JScrollPane" name="jarFileListScrollPane">
934 <Properties>
935 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
936 <Dimension value="[259, 1]"/>
937 </Property>
938 </Properties>
939 <Constraints>
940 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
941 <GridBagConstraints gridX="1" gridY="3" gridWidth="2" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="3" anchor="10" weightX="0.0" weightY="0.4"/>
942 </Constraint>
943 </Constraints>
944
945 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
946 <SubComponents>
947 <Component class="javax.swing.JList" name="jarFileList">
948 <Properties>
949 <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
950 <Border info="org.netbeans.modules.form.compat2.border.BevelBorderInfo">
951 <BevelBorder bevelType="1"/>
952 </Border>
953 </Property>
954 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
955 <Font name="Dialog" size="12" style="0"/>
956 </Property>
957 </Properties>
958 <Events>
959 <EventHandler event="focusGained" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="focusGainedHandler"/>
960 </Events>
961 <AuxValues>
962 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="disableEditKeyBindings(jarFileList);&#xa;&#xa;"/>
963 </AuxValues>
964 </Component>
965 </SubComponents>
966 </Container>
967 <Container class="javax.swing.JScrollPane" name="sourceDirListScrollPane">
968 <Properties>
969 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
970 <Dimension value="[259, 1]"/>
971 </Property>
972 </Properties>
973 <Constraints>
974 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
975 <GridBagConstraints gridX="1" gridY="9" gridWidth="2" gridHeight="3" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="3" anchor="10" weightX="0.0" weightY="0.1"/>
976 </Constraint>
977 </Constraints>
978
979 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
980 <SubComponents>
981 <Component class="javax.swing.JList" name="sourceDirList">
982 <Properties>
983 <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
984 <Border info="org.netbeans.modules.form.compat2.border.BevelBorderInfo">
985 <BevelBorder bevelType="1"/>
986 </Border>
987 </Property>
988 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
989 <Font name="Dialog" size="12" style="0"/>
990 </Property>
991 </Properties>
992 <Events>
993 <EventHandler event="focusGained" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="focusGainedHandler"/>
994 </Events>
995 <AuxValues>
996 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="disableEditKeyBindings(sourceDirList);"/>
997 </AuxValues>
998 </Component>
999 </SubComponents>
1000 </Container>
1001 <Component class="javax.swing.JLabel" name="classpathEntryLabel">
1002 <Properties>
1003 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
1004 <Font name="Dialog" size="12" style="0"/>
1005 </Property>
1006 <Property name="text" type="java.lang.String" value="Classpath entry:"/>
1007 </Properties>
1008 <AuxValues>
1009 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="classpathEntryLabel.setText(L10N.getLocalString(&quot;dlg.classpathfile_lbl&quot;, &quot;Classpath entry:&quot;));"/>
1010 </AuxValues>
1011 <Constraints>
1012 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
1013 <GridBagConstraints gridX="0" gridY="15" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="3" anchor="13" weightX="0.0" weightY="0.0"/>
1014 </Constraint>
1015 </Constraints>
1016 </Component>
1017 <Component class="javax.swing.JLabel" name="classpathEntryListLabel">
1018 <Properties>
1019 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
1020 <Font name="Dialog" size="12" style="0"/>
1021 </Property>
1022 <Property name="text" type="java.lang.String" value="Classpath entries:"/>
1023 </Properties>
1024 <AuxValues>
1025 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="classpathEntryListLabel.setText(L10N.getLocalString(&quot;dlg.classpathlist_lbl&quot;, &quot;Classpath entries:&quot;));"/>
1026 </AuxValues>
1027 <Constraints>
1028 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
1029 <GridBagConstraints gridX="0" gridY="16" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="3" anchor="13" weightX="0.0" weightY="0.0"/>
1030 </Constraint>
1031 </Constraints>
1032 </Component>
1033 <Component class="javax.swing.JTextField" name="classpathEntryTextField">
1034 <Events>
1035 <EventHandler event="focusGained" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="focusGainedHandler"/>
1036 </Events>
1037 <Constraints>
1038 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
1039 <GridBagConstraints gridX="1" gridY="15" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
1040 </Constraint>
1041 </Constraints>
1042 </Component>
1043 <Component class="javax.swing.JButton" name="browseClasspathEntryButton">
1044 <Properties>
1045 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
1046 <Font name="Dialog" size="12" style="0"/>
1047 </Property>
1048 <Property name="text" type="java.lang.String" value="Browse"/>
1049 <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1050 <Dimension value="[90, 25]"/>
1051 </Property>
1052 <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1053 <Dimension value="[90, 25]"/>
1054 </Property>
1055 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1056 <Dimension value="[90, 25]"/>
1057 </Property>
1058 </Properties>
1059 <Events>
1060 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="browseClasspathEntryButtonActionPerformed"/>
1061 </Events>
1062 <AuxValues>
1063 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="browseClasspathEntryButton.setText(L10N.getLocalString(&quot;dlg.browse_btn&quot;, &quot;Browse...&quot;));"/>
1064 </AuxValues>
1065 <Constraints>
1066 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
1067 <GridBagConstraints gridX="2" gridY="15" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
1068 </Constraint>
1069 </Constraints>
1070 </Component>
1071 <Component class="javax.swing.JButton" name="addClasspathEntryButton">
1072 <Properties>
1073 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
1074 <Font name="Dialog" size="12" style="0"/>
1075 </Property>
1076 <Property name="text" type="java.lang.String" value="Add"/>
1077 <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1078 <Dimension value="[90, 25]"/>
1079 </Property>
1080 <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1081 <Dimension value="[90, 25]"/>
1082 </Property>
1083 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1084 <Dimension value="[90, 25]"/>
1085 </Property>
1086 </Properties>
1087 <Events>
1088 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="addClasspathEntryButtonActionPerformed"/>
1089 </Events>
1090 <AuxValues>
1091 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="addClasspathEntryButton.setText(L10N.getLocalString(&quot;dlg.add_btn&quot;, &quot;Add&quot;));"/>
1092 </AuxValues>
1093 <Constraints>
1094 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
1095 <GridBagConstraints gridX="3" gridY="15" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="0" anchor="17" weightX="0.0" weightY="0.0"/>
1096 </Constraint>
1097 </Constraints>
1098 </Component>
1099 <Component class="javax.swing.JButton" name="removeClasspathEntryButton">
1100 <Properties>
1101 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
1102 <Font name="Dialog" size="12" style="0"/>
1103 </Property>
1104 <Property name="text" type="java.lang.String" value="Remove"/>
1105 <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1106 <Dimension value="[90, 25]"/>
1107 </Property>
1108 <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1109 <Dimension value="[90, 25]"/>
1110 </Property>
1111 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1112 <Dimension value="[90, 25]"/>
1113 </Property>
1114 </Properties>
1115 <Events>
1116 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="removeClasspathEntryButtonActionPerformed"/>
1117 </Events>
1118 <AuxValues>
1119 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="removeClasspathEntryButton.setText(L10N.getLocalString(&quot;dlg.remove_btn&quot;, &quot;Remove&quot;));"/>
1120 </AuxValues>
1121 <Constraints>
1122 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
1123 <GridBagConstraints gridX="3" gridY="16" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="0" anchor="17" weightX="0.0" weightY="0.0"/>
1124 </Constraint>
1125 </Constraints>
1126 </Component>
1127 <Container class="javax.swing.JScrollPane" name="classpathEntryListScrollPane">
1128 <Properties>
1129 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1130 <Dimension value="[259, 1]"/>
1131 </Property>
1132 </Properties>
1133 <Constraints>
1134 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
1135 <GridBagConstraints gridX="1" gridY="16" gridWidth="2" gridHeight="3" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="3" anchor="10" weightX="0.0" weightY="0.1"/>
1136 </Constraint>
1137 </Constraints>
1138
1139 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
1140 <SubComponents>
1141 <Component class="javax.swing.JList" name="classpathEntryList">
1142 <Properties>
1143 <Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
1144 <Border info="org.netbeans.modules.form.compat2.border.BevelBorderInfo">
1145 <BevelBorder bevelType="1"/>
1146 </Border>
1147 </Property>
1148 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
1149 <Font name="Dialog" size="12" style="0"/>
1150 </Property>
1151 </Properties>
1152 <Events>
1153 <EventHandler event="focusGained" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="focusGainedHandler"/>
1154 </Events>
1155 <AuxValues>
1156 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="disableEditKeyBindings(classpathEntryList);"/>
1157 </AuxValues>
1158 </Component>
1159 </SubComponents>
1160 </Container>
1161 <Component class="javax.swing.JSeparator" name="jSeparator5">
1162 <Constraints>
1163 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
1164 <GridBagConstraints gridX="0" gridY="20" gridWidth="4" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="0" insetsBottom="3" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
1165 </Constraint>
1166 </Constraints>
1167 </Component>
1168 <Component class="javax.swing.JButton" name="sourceUpButton">
1169 <Properties>
1170 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
1171 <Font name="Dialog" size="12" style="0"/>
1172 </Property>
1173 <Property name="text" type="java.lang.String" value="Up"/>
1174 <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1175 <Dimension value="[90, 25]"/>
1176 </Property>
1177 <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1178 <Dimension value="[90, 25]"/>
1179 </Property>
1180 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1181 <Dimension value="[90, 25]"/>
1182 </Property>
1183 </Properties>
1184 <Events>
1185 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="sourceUpButtonActionPerformed"/>
1186 </Events>
1187 <AuxValues>
1188 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="sourceUpButton.setText(L10N.getLocalString(&quot;dlg.up_btn&quot;, &quot;Up&quot;));"/>
1189 </AuxValues>
1190 <Constraints>
1191 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
1192 <GridBagConstraints gridX="3" gridY="10" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.2"/>
1193 </Constraint>
1194 </Constraints>
1195 </Component>
1196 <Component class="javax.swing.JButton" name="sourceDownButton">
1197 <Properties>
1198 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
1199 <Font name="Dialog" size="12" style="0"/>
1200 </Property>
1201 <Property name="text" type="java.lang.String" value="Down"/>
1202 <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1203 <Dimension value="[90, 25]"/>
1204 </Property>
1205 <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1206 <Dimension value="[90, 25]"/>
1207 </Property>
1208 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1209 <Dimension value="[90, 25]"/>
1210 </Property>
1211 </Properties>
1212 <Events>
1213 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="sourceDownButtonActionPerformed"/>
1214 </Events>
1215 <AuxValues>
1216 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="sourceDownButton.setText(L10N.getLocalString(&quot;dlg.down_btn&quot;, &quot;Down&quot;));"/>
1217 </AuxValues>
1218 <Constraints>
1219 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
1220 <GridBagConstraints gridX="-1" gridY="11" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
1221 </Constraint>
1222 </Constraints>
1223 </Component>
1224 <Component class="javax.swing.JButton" name="classpathUpButton">
1225 <Properties>
1226 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
1227 <Font name="Dialog" size="12" style="0"/>
1228 </Property>
1229 <Property name="text" type="java.lang.String" value="Up"/>
1230 <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1231 <Dimension value="[90, 25]"/>
1232 </Property>
1233 <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1234 <Dimension value="[90, 25]"/>
1235 </Property>
1236 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1237 <Dimension value="[90, 25]"/>
1238 </Property>
1239 </Properties>
1240 <Events>
1241 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="classpathUpButtonActionPerformed"/>
1242 </Events>
1243 <AuxValues>
1244 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="classpathUpButton.setText(L10N.getLocalString(&quot;dlg.up_btn&quot;, &quot;Up&quot;));"/>
1245 </AuxValues>
1246 <Constraints>
1247 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
1248 <GridBagConstraints gridX="3" gridY="17" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.2"/>
1249 </Constraint>
1250 </Constraints>
1251 </Component>
1252 <Component class="javax.swing.JButton" name="classpathDownButton">
1253 <Properties>
1254 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
1255 <Font name="Dialog" size="12" style="0"/>
1256 </Property>
1257 <Property name="text" type="java.lang.String" value="Down"/>
1258 <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1259 <Dimension value="[90, 25]"/>
1260 </Property>
1261 <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1262 <Dimension value="[90, 25]"/>
1263 </Property>
1264 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1265 <Dimension value="[90, 25]"/>
1266 </Property>
1267 </Properties>
1268 <Events>
1269 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="classpathDownButtonActionPerformed"/>
1270 </Events>
1271 <AuxValues>
1272 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="classpathDownButton.setText(L10N.getLocalString(&quot;dlg.down_btn&quot;, &quot;Down&quot;));"/>
1273 </AuxValues>
1274 <Constraints>
1275 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
1276 <GridBagConstraints gridX="3" gridY="18" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
1277 </Constraint>
1278 </Constraints>
1279 </Component>
1280 </SubComponents>
1281 </Container>
1282 <Container class="javax.swing.JPanel" name="bugTreePanel">
1283 <Constraints>
1284 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignCardLayout" value="org.netbeans.modules.form.compat2.layouts.DesignCardLayout$CardConstraintsDescription">
1285 <CardConstraints cardName="BugTree"/>
1286 </Constraint>
1287 </Constraints>
1288
1289 <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
1290 <SubComponents>
1291 <Container class="javax.swing.JSplitPane" name="bugTreeBugDetailsSplitter">
1292 <Properties>
1293 <Property name="orientation" type="int" value="0"/>
1294 <Property name="resizeWeight" type="double" value="1.0"/>
1295 <Property name="oneTouchExpandable" type="boolean" value="true"/>
1296 </Properties>
1297 <Events>
1298 <EventHandler event="propertyChange" listener="java.beans.PropertyChangeListener" parameters="java.beans.PropertyChangeEvent" handler="bugTreeBugDetailsSplitterPropertyChange"/>
1299 </Events>
1300 <Constraints>
1301 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
1302 <GridBagConstraints gridX="0" gridY="1" gridWidth="2" gridHeight="1" fill="1" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="1.0" weightY="1.0"/>
1303 </Constraint>
1304 </Constraints>
1305
1306 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout"/>
1307 <SubComponents>
1308 <Container class="javax.swing.JTabbedPane" name="groupByTabbedPane">
1309 <Constraints>
1310 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
1311 <JSplitPaneConstraints position="top"/>
1312 </Constraint>
1313 </Constraints>
1314
1315 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
1316 <SubComponents>
1317 <Container class="javax.swing.JScrollPane" name="byClassScrollPane">
1318 <Constraints>
1319 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
1320 <JTabbedPaneConstraints tabName="By Class">
1321 <Property name="tabTitle" type="java.lang.String" value="By Class"/>
1322 </JTabbedPaneConstraints>
1323 </Constraint>
1324 </Constraints>
1325
1326 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
1327 <SubComponents>
1328 <Component class="javax.swing.JTree" name="byClassBugTree">
1329 <Events>
1330 <EventHandler event="focusGained" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="focusGainedHandler"/>
1331 </Events>
1332 </Component>
1333 </SubComponents>
1334 </Container>
1335 <Container class="javax.swing.JScrollPane" name="byPackageScrollPane">
1336 <Constraints>
1337 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
1338 <JTabbedPaneConstraints tabName="By Package">
1339 <Property name="tabTitle" type="java.lang.String" value="By Package"/>
1340 </JTabbedPaneConstraints>
1341 </Constraint>
1342 </Constraints>
1343
1344 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
1345 <SubComponents>
1346 <Component class="javax.swing.JTree" name="byPackageBugTree">
1347 <Events>
1348 <EventHandler event="focusGained" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="focusGainedHandler"/>
1349 </Events>
1350 </Component>
1351 </SubComponents>
1352 </Container>
1353 <Container class="javax.swing.JScrollPane" name="byBugTypeScrollPane">
1354 <Constraints>
1355 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
1356 <JTabbedPaneConstraints tabName="By Bug Type">
1357 <Property name="tabTitle" type="java.lang.String" value="By Bug Type"/>
1358 </JTabbedPaneConstraints>
1359 </Constraint>
1360 </Constraints>
1361
1362 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
1363 <SubComponents>
1364 <Component class="javax.swing.JTree" name="byBugTypeBugTree">
1365 <Events>
1366 <EventHandler event="focusGained" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="focusGainedHandler"/>
1367 </Events>
1368 </Component>
1369 </SubComponents>
1370 </Container>
1371 <Container class="javax.swing.JScrollPane" name="byBugCategoryScrollPane">
1372 <Constraints>
1373 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
1374 <JTabbedPaneConstraints tabName="By Category Type">
1375 <Property name="tabTitle" type="java.lang.String" value="By Category Type"/>
1376 </JTabbedPaneConstraints>
1377 </Constraint>
1378 </Constraints>
1379
1380 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
1381 <SubComponents>
1382 <Component class="javax.swing.JTree" name="byBugCategoryBugTree">
1383 <Events>
1384 <EventHandler event="focusGained" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="focusGainedHandler"/>
1385 </Events>
1386 </Component>
1387 </SubComponents>
1388 </Container>
1389 <Container class="javax.swing.JScrollPane" name="bySummary">
1390 <Constraints>
1391 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
1392 <JTabbedPaneConstraints tabName="Summary">
1393 <Property name="tabTitle" type="java.lang.String" value="Summary"/>
1394 </JTabbedPaneConstraints>
1395 </Constraint>
1396 </Constraints>
1397
1398 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
1399 <SubComponents>
1400 <Component class="javax.swing.JEditorPane" name="bugSummaryEditorPane">
1401 <Events>
1402 <EventHandler event="focusGained" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="focusGainedHandler"/>
1403 </Events>
1404 </Component>
1405 </SubComponents>
1406 </Container>
1407 </SubComponents>
1408 </Container>
1409 <Container class="javax.swing.JTabbedPane" name="bugDetailsTabbedPane">
1410 <Constraints>
1411 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
1412 <JSplitPaneConstraints position="bottom"/>
1413 </Constraint>
1414 </Constraints>
1415
1416 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout"/>
1417 <SubComponents>
1418 <Container class="javax.swing.JScrollPane" name="bugDescriptionScrollPane">
1419 <Constraints>
1420 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
1421 <JTabbedPaneConstraints tabName="Details">
1422 <Property name="tabTitle" type="java.lang.String" value="Details"/>
1423 </JTabbedPaneConstraints>
1424 </Constraint>
1425 </Constraints>
1426
1427 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
1428 <SubComponents>
1429 <Component class="javax.swing.JEditorPane" name="bugDescriptionEditorPane">
1430 <Properties>
1431 <Property name="editable" type="boolean" value="false"/>
1432 </Properties>
1433 <Events>
1434 <EventHandler event="focusGained" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="focusGainedHandler"/>
1435 </Events>
1436 </Component>
1437 </SubComponents>
1438 </Container>
1439 <Container class="javax.swing.JScrollPane" name="sourceTextAreaScrollPane">
1440 <Properties>
1441 <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1442 <Dimension value="[22, 180]"/>
1443 </Property>
1444 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1445 <Dimension value="[0, 100]"/>
1446 </Property>
1447 </Properties>
1448 <Constraints>
1449 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
1450 <JTabbedPaneConstraints tabName="Source code">
1451 <Property name="tabTitle" type="java.lang.String" value="Source code"/>
1452 </JTabbedPaneConstraints>
1453 </Constraint>
1454 </Constraints>
1455
1456 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
1457 <SubComponents>
1458 <Component class="javax.swing.JTextArea" name="sourceTextArea">
1459 <Properties>
1460 <Property name="editable" type="boolean" value="false"/>
1461 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
1462 <Font name="Monospaced" size="12" style="0"/>
1463 </Property>
1464 <Property name="enabled" type="boolean" value="false"/>
1465 </Properties>
1466 <Events>
1467 <EventHandler event="focusGained" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="focusGainedHandler"/>
1468 </Events>
1469 </Component>
1470 </SubComponents>
1471 </Container>
1472 <Container class="javax.swing.JScrollPane" name="annotationTextAreaScrollPane">
1473 <Constraints>
1474 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
1475 <JTabbedPaneConstraints tabName="Annotations">
1476 <Property name="tabTitle" type="java.lang.String" value="Annotations"/>
1477 </JTabbedPaneConstraints>
1478 </Constraint>
1479 </Constraints>
1480
1481 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
1482 <SubComponents>
1483 <Component class="javax.swing.JTextArea" name="annotationTextArea">
1484 <Events>
1485 <EventHandler event="focusGained" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="focusGainedHandler"/>
1486 </Events>
1487 </Component>
1488 </SubComponents>
1489 </Container>
1490 </SubComponents>
1491 </Container>
1492 </SubComponents>
1493 </Container>
1494 </SubComponents>
1495 </Container>
1496 </SubComponents>
1497 </Container>
1498 <Container class="javax.swing.JScrollPane" name="consoleScrollPane">
1499 <Properties>
1500 <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1501 <Dimension value="[22, 100]"/>
1502 </Property>
1503 <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1504 <Dimension value="[0, 100]"/>
1505 </Property>
1506 </Properties>
1507 <AuxValues>
1508 <AuxValue name="JavaCodeGenerator_InitCodePre" type="java.lang.String" value="{ &#xa; equalizeControls( new JComponent[] &#xa; {&#xa; addJarButton,&#xa; addSourceDirButton,&#xa; addClasspathEntryButton,&#xa; removeJarButton,&#xa; removeSrcDirButton,&#xa; removeClasspathEntryButton,&#xa; browseJarButton,&#xa; browseSrcDirButton,&#xa; browseClasspathEntryButton,&#xa; sourceUpButton,&#xa; sourceDownButton,&#xa; classpathUpButton,&#xa; classpathDownButton&#xa; });&#xa; &#xa; groupByTabbedPane.setTitleAt(0, L10N.getLocalString( &quot;dlg.byclass_tab&quot;, &quot;By Class&quot;)); &#xa; groupByTabbedPane.setTitleAt(1, L10N.getLocalString( &quot;dlg.bypackage_tab&quot;, &quot;By Package&quot;)); &#xa; groupByTabbedPane.setTitleAt(2, L10N.getLocalString( &quot;dlg.bybugtype_tab&quot;, &quot;By Bug Type&quot;)); &#xa; groupByTabbedPane.setTitleAt(3, L10N.getLocalString( &quot;dlg.bybugcategory_tab&quot;, &quot;By Bug Category&quot;)); &#xa; groupByTabbedPane.setTitleAt(4, L10N.getLocalString( &quot;dlg.summary_tab&quot;, &quot;Summary&quot;)); &#xa; bugDetailsTabbedPane.setTitleAt(0, L10N.getLocalString( &quot;dlg.details_tab&quot;, &quot;Details&quot;)); &#xa; bugDetailsTabbedPane.setTitleAt(1, L10N.getLocalString( &quot;dlg.sourcecode_tab&quot;, &quot;Source Code&quot;)); &#xa; bugDetailsTabbedPane.setTitleAt(2, L10N.getLocalString( &quot;dlg.annotations_tab&quot;, &quot;Annotations&quot;)); &#xa;}"/>
1509 </AuxValues>
1510 <Constraints>
1511 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JSplitPaneSupportLayout$JSplitPaneConstraintsDescription">
1512 <JSplitPaneConstraints position="bottom"/>
1513 </Constraint>
1514 </Constraints>
1515
1516 <Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
1517 <SubComponents>
1518 <Component class="javax.swing.JTextArea" name="consoleMessageArea">
1519 <Properties>
1520 <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
1521 <Color blue="cc" green="cc" red="cc" type="rgb"/>
1522 </Property>
1523 <Property name="editable" type="boolean" value="false"/>
1524 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
1525 <Font name="Monospaced" size="12" style="0"/>
1526 </Property>
1527 <Property name="autoscrolls" type="boolean" value="false"/>
1528 <Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1529 <Dimension value="[0, 0]"/>
1530 </Property>
1531 </Properties>
1532 <Events>
1533 <EventHandler event="focusGained" listener="java.awt.event.FocusListener" parameters="java.awt.event.FocusEvent" handler="focusGainedHandler"/>
1534 </Events>
1535 </Component>
1536 </SubComponents>
1537 </Container>
1538 </SubComponents>
1539 </Container>
1540 <Component class="javax.swing.JLabel" name="urlLabel">
1541 <Properties>
1542 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
1543 <Font name="Dialog" size="12" style="0"/>
1544 </Property>
1545 <Property name="text" type="java.lang.String" value="FindBugs - http://findbugs.sourceforge.net/"/>
1546 </Properties>
1547 <Constraints>
1548 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
1549 <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="2" insetsBottom="2" insetsRight="0" anchor="17" weightX="0.0" weightY="0.0"/>
1550 </Constraint>
1551 </Constraints>
1552 </Component>
1553 <Container class="javax.swing.JPanel" name="jPanel1">
1554 <Constraints>
1555 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
1556 <GridBagConstraints gridX="1" gridY="1" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="2" insetsLeft="0" insetsBottom="2" insetsRight="2" anchor="14" weightX="0.0" weightY="0.0"/>
1557 </Constraint>
1558 </Constraints>
1559
1560 <Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/>
1561 <SubComponents>
1562 <Component class="javax.swing.JLabel" name="logoLabel">
1563 </Component>
1564 <Component class="javax.swing.JLabel" name="growBoxSpacer">
1565 <Properties>
1566 <Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
1567 <Dimension value="[16, 16]"/>
1568 </Property>
1569 </Properties>
1570 </Component>
1571 </SubComponents>
1572 </Container>
1573 </SubComponents>
1574 </Form>
+0
-3981
src/obsolete/edu/umd/cs/findbugs/gui/FindBugsFrame.java less more
0 /*
1 * FindBugs - Find bugs in Java programs
2 * Copyright (C) 2003-2005, University of Maryland
3 * Copyright (C) 2004 Dave Brosius <dbrosius@users.sourceforge.net>
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 /*
21 * FindBugsFrame.java
22 *
23 * Created on March 30, 2003, 12:05 PM
24 */
25
26 package edu.umd.cs.findbugs.gui;
27
28 import java.awt.BorderLayout;
29 import java.awt.CardLayout;
30 import java.awt.Color;
31 import java.awt.Component;
32 import java.awt.Cursor;
33 import java.awt.Dimension;
34 import java.awt.Event;
35 import java.awt.Font;
36 import java.awt.Graphics;
37 import java.awt.HeadlessException;
38 import java.awt.Point;
39 import java.awt.Rectangle;
40 import java.awt.Shape;
41 import java.awt.Toolkit;
42 import java.awt.datatransfer.Clipboard;
43 import java.awt.datatransfer.DataFlavor;
44 import java.awt.datatransfer.StringSelection;
45 import java.awt.datatransfer.Transferable;
46 import java.awt.event.ActionEvent;
47 import java.awt.event.ActionListener;
48 import java.awt.event.KeyEvent;
49 import java.io.BufferedReader;
50 import java.io.File;
51 import java.io.IOException;
52 import java.io.InputStream;
53 import java.io.InputStreamReader;
54 import java.io.Serializable;
55 import java.io.StringReader;
56 import java.lang.reflect.Method;
57 import java.net.URL;
58 import java.text.MessageFormat;
59 import java.util.Arrays;
60 import java.util.Collection;
61 import java.util.Comparator;
62 import java.util.HashSet;
63 import java.util.Iterator;
64 import java.util.List;
65 import java.util.TreeSet;
66
67 import javax.swing.AbstractButton;
68 import javax.swing.ButtonGroup;
69 import javax.swing.DefaultListModel;
70 import javax.swing.ImageIcon;
71 import javax.swing.JCheckBoxMenuItem;
72 import javax.swing.JComponent;
73 import javax.swing.JDialog;
74 import javax.swing.JFileChooser;
75 import javax.swing.JList;
76 import javax.swing.JMenuItem;
77 import javax.swing.JOptionPane;
78 import javax.swing.JRadioButton;
79 import javax.swing.JScrollPane;
80 import javax.swing.JSplitPane;
81 import javax.swing.JTextField;
82 import javax.swing.JTree;
83 import javax.swing.JViewport;
84 import javax.swing.KeyStroke;
85 import javax.swing.ListModel;
86 import javax.swing.SwingUtilities;
87 import javax.swing.UIManager;
88 import javax.swing.event.TreeSelectionEvent;
89 import javax.swing.event.TreeSelectionListener;
90 import javax.swing.filechooser.FileFilter;
91 import javax.swing.text.BadLocationException;
92 import javax.swing.text.DefaultHighlighter;
93 import javax.swing.text.Highlighter;
94 import javax.swing.text.JTextComponent;
95 import javax.swing.text.Position;
96 import javax.swing.text.View;
97 import javax.swing.tree.DefaultMutableTreeNode;
98 import javax.swing.tree.DefaultTreeModel;
99 import javax.swing.tree.TreePath;
100 import javax.swing.tree.TreeSelectionModel;
101
102 import edu.umd.cs.findbugs.cloud.SignInCancelledException;
103 import org.dom4j.DocumentException;
104
105 import edu.umd.cs.findbugs.BugAnnotation;
106 import edu.umd.cs.findbugs.BugAnnotationWithSourceLines;
107 import edu.umd.cs.findbugs.BugInstance;
108 import edu.umd.cs.findbugs.BugPattern;
109 import edu.umd.cs.findbugs.Detector;
110 import edu.umd.cs.findbugs.DetectorFactoryCollection;
111 import edu.umd.cs.findbugs.FindBugs;
112 import edu.umd.cs.findbugs.FindBugsCommandLine;
113 import edu.umd.cs.findbugs.I18N;
114 import edu.umd.cs.findbugs.L10N;
115 import edu.umd.cs.findbugs.Project;
116 import edu.umd.cs.findbugs.ShowHelp;
117 import edu.umd.cs.findbugs.SourceLineAnnotation;
118 import edu.umd.cs.findbugs.SystemProperties;
119 import edu.umd.cs.findbugs.annotations.SuppressWarnings;
120 import edu.umd.cs.findbugs.ba.SourceFile;
121 import edu.umd.cs.findbugs.ba.SourceFinder;
122 import edu.umd.cs.findbugs.config.AnalysisFeatureSetting;
123 import edu.umd.cs.findbugs.config.ProjectFilterSettings;
124 import edu.umd.cs.findbugs.config.UserPreferences;
125 import edu.umd.cs.findbugs.config.CommandLine.HelpRequestedException;
126 import edu.umd.cs.findbugs.log.ConsoleLogger;
127 import edu.umd.cs.findbugs.log.LogSync;
128 import edu.umd.cs.findbugs.log.Logger;
129
130 /**
131 * The main GUI frame for FindBugs.
132 *
133 * @author David Hovemeyer
134 */
135 public final class FindBugsFrame extends javax.swing.JFrame implements LogSync {
136 /**
137 *
138 */
139 private static final int fontSize = 12;
140 /**
141 *
142 */
143 private static final Font SOURCE_FONT = new java.awt.Font("Monospaced", 0, fontSize);
144 private static final Font JTREE_FONT = new java.awt.Font("SansSerif", 0, fontSize);
145
146 /**
147 *
148 */
149 private static final Font LABEL_FONT = new java.awt.Font("Dialog", 1, 2*fontSize);
150
151 /**
152 *
153 */
154 private static final Font BUTTON_FONT = new java.awt.Font("Dialog", 0, fontSize);
155
156 private static final long serialVersionUID = 1L;
157
158 /* ----------------------------------------------------------------------
159 * Helper classes
160 * ---------------------------------------------------------------------- */
161 static final Color HIGH_PRIORITY_COLOR = new Color(0xff0000);
162 static final Color NORMAL_PRIORITY_COLOR = new Color(0x9f0000);
163 static final Color LOW_PRIORITY_COLOR = Color.BLACK;
164 static final Color EXP_PRIORITY_COLOR = Color.BLACK;
165
166 /**
167 * Tree node type for BugInstances.
168 * We use this instead of plain DefaultMutableTreeNodes in order to
169 * get more control over the exact text that is shown in the tree.
170 */
171 private class BugTreeNode extends DefaultMutableTreeNode {
172 private static final long serialVersionUID = 1L;
173 private int count;
174
175 public BugTreeNode(BugInstance bugInstance) {
176 super(bugInstance);
177 count = -1;
178 }
179
180 public void setCount(int count) {
181 this.count = count;
182 }
183
184 @Override
185 public String toString() {
186 try {
187 BugInstance bugInstance = (BugInstance) getUserObject();
188 StringBuilder result = new StringBuilder();
189
190 if (count >= 0) {
191 result.append(count);
192 result.append(": ");
193 }
194
195 if (bugInstance.isExperimental())
196 result.append(L10N.getLocalString("msg.exp_txt", "EXP: "));
197
198 result.append(fullDescriptionsItem.isSelected() ? bugInstance.getMessage() : bugInstance.toString());
199
200 return result.toString();
201 } catch (Exception e) {
202 return MessageFormat.format(L10N.getLocalString("msg.errorformatting_txt", "Error formatting message for bug: "), new Object[]{e.toString()});
203 }
204 }
205 }
206
207 /**
208 * Compare BugInstance class names.
209 * This is useful for grouping bug instances by class.
210 * Note that all instances with the same class name will compare
211 * as equal.
212 */
213 private static class BugInstanceClassComparator implements Comparator<BugInstance> {
214 public int compare(BugInstance lhs, BugInstance rhs) {
215 return lhs.getPrimaryClass().compareTo(rhs.getPrimaryClass());
216 }
217 }
218
219 /**
220 * The instance of BugInstanceClassComparator.
221 */
222 private static final Comparator<BugInstance> bugInstanceClassComparator = new BugInstanceClassComparator();
223
224 /**
225 * Compare BugInstance package names.
226 * This is useful for grouping bug instances by package.
227 * Note that all instances with the same package name will compare
228 * as equal.
229 */
230 private static class BugInstancePackageComparator implements Comparator<BugInstance>, Serializable {
231 private static final long serialVersionUID = 1L;
232 public int compare(BugInstance lhs, BugInstance rhs) {
233 return lhs.getPrimaryClass().getPackageName().compareTo(rhs.getPrimaryClass().getPackageName());
234 }
235 }
236
237 /**
238 * The instance of BugInstancePackageComparator.
239 */
240 private static final Comparator<BugInstance> bugInstancePackageComparator = new BugInstancePackageComparator();
241
242 /**
243 * Compare BugInstance bug types.
244 * This is useful for grouping bug instances by bug type.
245 * Note that all instances with the same bug type will compare
246 * as equal.
247 */
248 private static class BugInstanceTypeComparator implements Comparator<BugInstance>, Serializable {
249 private static final long serialVersionUID = 1L;
250 public int compare(BugInstance lhs, BugInstance rhs) {
251 String lhsString = lhs.toString();
252 String rhsString = rhs.toString();
253 return lhsString.substring(0, lhsString.indexOf(':')).compareTo(rhsString.substring(0, rhsString.indexOf(':')));
254 }
255 }
256
257 /**
258 * The instance of BugInstanceTypeComparator.
259 */
260 private static final Comparator<BugInstance> bugInstanceTypeComparator = new BugInstanceTypeComparator();
261
262 /**
263 * Compare BugInstance bug categories.
264 * This is useful for grouping bug instances by bug category.
265 * Note that all instances with the same bug category will compare
266 * as equal.
267 */
268 private static class BugInstanceCategoryComparator implements Comparator<BugInstance>, Serializable {
269 private static final long serialVersionUID = 1L;
270
271 public int compare(BugInstance lhs, BugInstance rhs) {
272 return getCategory(lhs).compareTo(getCategory(rhs));
273 }
274
275 private String getCategory(BugInstance warning) {
276 BugPattern bugPattern = warning.getBugPattern();
277 if (bugPattern == null) {
278 if (FindBugs.DEBUG)
279 System.out.println("Unknown bug pattern for bug type: " + warning.getType());
280 return "";
281 } else {
282 return bugPattern.getCategory();
283 }
284 }
285 }
286
287 /**
288 * The instance of BugInstanceCategoryComparator.
289 */
290 private static final Comparator<BugInstance> bugInstanceCategoryComparator = new BugInstanceCategoryComparator();
291
292 /**
293 * Two-level comparison of bug instances by class name and
294 * BugInstance natural ordering.
295 */
296 private static class BugInstanceByClassComparator implements Comparator<BugInstance>, Serializable {
297 private static final long serialVersionUID = 1L;
298 public int compare(BugInstance a, BugInstance b) {
299 int cmp = bugInstanceClassComparator.compare(a, b);
300 if (cmp != 0)
301 return cmp;
302 return a.compareTo(b);
303 }
304 }
305
306 /**
307 * The instance of BugInstanceByClassComparator.
308 */
309 private static final Comparator<BugInstance> bugInstanceByClassComparator = new FindBugsFrame.BugInstanceByClassComparator();
310
311 /**
312 * Two-level comparison of bug instances by package and
313 * BugInstance natural ordering.
314 */
315 private static class BugInstanceByPackageComparator implements Comparator<BugInstance> {
316 public int compare(BugInstance a, BugInstance b) {
317 int cmp = bugInstancePackageComparator.compare(a, b);
318 if (cmp != 0)
319 return cmp;
320 return a.compareTo(b);
321 }
322 }
323
324 /**
325 * The instance of BugInstanceByPackageComparator.
326 */
327 private static final Comparator<BugInstance> bugInstanceByPackageComparator = new FindBugsFrame.BugInstanceByPackageComparator();
328
329 /**
330 * Two-level comparison of bug instances by bug type and
331 * BugInstance natural ordering.
332 */
333 private static class BugInstanceByTypeComparator implements Comparator<BugInstance>, Serializable {
334 private static final long serialVersionUID = 1L;
335 public int compare(BugInstance a, BugInstance b) {
336 int cmp = bugInstanceTypeComparator.compare(a, b);
337 if (cmp != 0)
338 return cmp;
339 return a.compareTo(b);
340 }
341 }
342
343 /**
344 * The instance of BugTypeByTypeComparator.
345 */
346 private static final Comparator<BugInstance> bugInstanceByTypeComparator = new FindBugsFrame.BugInstanceByTypeComparator();
347
348 /**
349 * Two-level comparison of bug instances by bug category and
350 * BugInstance natural ordering.
351 */
352 private static class BugInstanceByCategoryComparator implements Comparator<BugInstance>, Serializable {
353 private static final long serialVersionUID = 1L;
354 public int compare(BugInstance a, BugInstance b) {
355 int cmp = bugInstanceCategoryComparator.compare(a, b);
356 if (cmp != 0)
357 return cmp;
358 return a.compareTo(b);
359 }
360 }
361
362 /**
363 * The instance of BugTypeByCategoryComparator.
364 */
365 private static final Comparator<BugInstance> bugInstanceByCategoryComparator = new FindBugsFrame.BugInstanceByCategoryComparator();
366
367 /**
368 * Swing FileFilter class for file selection dialogs for FindBugs project files.
369 */
370 private static class ProjectFileFilter extends FileFilter {
371 @Override
372 public boolean accept(File file) {
373 return file.isDirectory() || file.getName().endsWith(".fb");
374 }
375
376 @Override
377 public String getDescription() {
378 return L10N.getLocalString("dlg.findbugsprojects_lbl", "FindBugs projects (*.fb)");
379 }
380 }
381
382 /**
383 * The instance of ProjectFileFilter.
384 */
385 private static final FileFilter projectFileFilter = new ProjectFileFilter();
386
387 /**
388 * Swing FileFilter for choosing an auxiliary classpath entry.
389 * Both Jar files and directories can be chosen.
390 */
391 private static class AuxClasspathEntryFileFilter extends FileFilter {
392 @Override
393 public boolean accept(File file) {
394 return file.isDirectory() || file.getName().endsWith(".jar");
395 }
396
397 @Override
398 public String getDescription() {
399 return L10N.getLocalString("dlg.jarsanddirectories_lbl", "Jar files and directories");
400 }
401 }
402
403 /**
404 * The instance of AuxClasspathEntryFileFilter.
405 */
406 private static final FileFilter auxClasspathEntryFileFilter = new AuxClasspathEntryFileFilter();
407
408 /**
409 * Swing FileFilter for choosing XML saved bug files.
410 */
411 private static class XMLFileFilter extends FileFilter {
412 @Override
413 public boolean accept(File file) {
414 return file.isDirectory() || file.getName().endsWith(".xml");
415 }
416
417 @Override
418 public String getDescription() {
419 return L10N.getLocalString("dlg.xmlsavedbugs_lbl", "XML saved bug files");
420 }
421 }
422
423 /**
424 * The instance of XMLFileFilter.
425 */
426 private static final FileFilter xmlFileFilter = new XMLFileFilter();
427
428 /**
429 * Set of archive file extensions.
430 */
431 private static final HashSet<String> archiveExtensionSet = new HashSet<String>();
432
433 static {
434 archiveExtensionSet.add(".jar");
435 archiveExtensionSet.add(".zip");
436 archiveExtensionSet.add(".ear");
437 archiveExtensionSet.add(".war");
438 archiveExtensionSet.add(".sar");
439 }
440
441 /**
442 * File filter for choosing archives and directories.
443 */
444 private static class ArchiveAndDirectoryFilter extends FileFilter {
445 @Override
446 public boolean accept(File file) {
447 if (file.isDirectory())
448 return true;
449
450 String fileName = file.getName();
451 int dot = fileName.lastIndexOf('.');
452 if (dot < 0)
453 return false;
454 String extension = fileName.substring(dot);
455 return archiveExtensionSet.contains(extension);
456 }
457
458 @Override
459 public String getDescription() {
460 return L10N.getLocalString("dlg.javaarchives_lbl", "Java archives (*.jar,*.zip,*.ear,*.war,*.sar)");
461 }
462 }
463
464 /**
465 * The instance of ArchiveAndDirectoryFilter.
466 */
467 private static final FileFilter archiveAndDirectoryFilter = new ArchiveAndDirectoryFilter();
468
469 /* ----------------------------------------------------------------------
470 * Constants
471 * ---------------------------------------------------------------------- */
472
473 static final String GROUP_BY_CLASS = "By class";
474 static final String GROUP_BY_PACKAGE = "By package";
475 static final String GROUP_BY_BUG_TYPE = "By bug type";
476 static final String GROUP_BY_BUG_CATEGORY="By bug category";
477 private static final String[] GROUP_BY_ORDER_LIST = {
478 GROUP_BY_CLASS, GROUP_BY_PACKAGE, GROUP_BY_BUG_TYPE, GROUP_BY_BUG_CATEGORY
479 };
480
481 /**
482 * A fudge value required in our hack to get the REAL maximum
483 * divider location for a JSplitPane. Experience suggests that
484 * the value "1" would work here, but setting it a little higher
485 * makes the code a bit more robust.
486 */
487 private static final int DIVIDER_FUDGE = 3;
488
489 private static final boolean BUG_COUNT = SystemProperties.getBoolean("findbugs.gui.bugCount");
490
491 /* ----------------------------------------------------------------------
492 * Member fields
493 * ---------------------------------------------------------------------- */
494 Component selectedComponent = null;
495
496 /* ----------------------------------------------------------------------
497 * Constructor
498 * ---------------------------------------------------------------------- */
499
500 /**
501 * Creates new form FindBugsFrame.
502 */
503 public FindBugsFrame() {
504
505 UserPreferences prefs = UserPreferences.getUserPreferences();
506 prefs.read();
507
508 String dirProp = SystemProperties.getProperty("user.dir");
509
510 if (dirProp != null) {
511 currentDirectory = new File(dirProp);
512 }
513
514 initComponents();
515 postInitComponents();
516 }
517
518 /* ----------------------------------------------------------------------
519 * Component initialization and event handlers
520 * ---------------------------------------------------------------------- */
521
522 /**
523 * This method is called from within the constructor to
524 * initialize the form.
525 * WARNING: Do NOT modify this code. The content of this method is
526 * always regenerated by the Form Editor.
527 */
528 private void initComponents() {//GEN-BEGIN:initComponents
529 java.awt.GridBagConstraints gridBagConstraints;
530
531 priorityButtonGroup = new javax.swing.ButtonGroup();
532 effortButtonGroup = new javax.swing.ButtonGroup();
533 //consoleSplitter = new javax.swing.JSplitPane();
534 viewPanel = new javax.swing.JPanel();
535 emptyPanel = new javax.swing.JPanel();
536 reportPanel = new javax.swing.JPanel();
537 editProjectPanel = new javax.swing.JPanel();
538 jarFileLabel = new javax.swing.JLabel();
539 jarNameTextField = new javax.swing.JTextField();
540 addJarButton = new javax.swing.JButton();
541 jarFileListLabel = new javax.swing.JLabel();
542 sourceDirLabel = new javax.swing.JLabel();
543 srcDirTextField = new javax.swing.JTextField();
544 addSourceDirButton = new javax.swing.JButton();
545 sourceDirListLabel = new javax.swing.JLabel();
546 removeJarButton = new javax.swing.JButton();
547 removeSrcDirButton = new javax.swing.JButton();
548 jSeparator1 = new javax.swing.JSeparator();
549 browseJarButton = new javax.swing.JButton();
550 browseSrcDirButton = new javax.swing.JButton();
551 editProjectLabel = new javax.swing.JLabel();
552 jSeparator2 = new javax.swing.JSeparator();
553 findBugsButton = new javax.swing.JButton();
554 jSeparator4 = new javax.swing.JSeparator();
555 jarFileListScrollPane = new javax.swing.JScrollPane();
556 jarFileList = new javax.swing.JList();
557 sourceDirListScrollPane = new javax.swing.JScrollPane();
558 sourceDirList = new javax.swing.JList();
559 classpathEntryLabel = new javax.swing.JLabel();
560 classpathEntryListLabel = new javax.swing.JLabel();
561 classpathEntryTextField = new javax.swing.JTextField();
562 browseClasspathEntryButton = new javax.swing.JButton();
563 addClasspathEntryButton = new javax.swing.JButton();
564 removeClasspathEntryButton = new javax.swing.JButton();
565 classpathEntryListScrollPane = new javax.swing.JScrollPane();
566 classpathEntryList = new javax.swing.JList();
567 jSeparator5 = new javax.swing.JSeparator();
568 sourceUpButton = new javax.swing.JButton();
569 sourceDownButton = new javax.swing.JButton();
570 classpathUpButton = new javax.swing.JButton();
571 classpathDownButton = new javax.swing.JButton();
572 bugTreePanel = new javax.swing.JPanel();
573 bugTreeBugDetailsSplitter = new javax.swing.JSplitPane();
574 groupByTabbedPane = new javax.swing.JTabbedPane();
575 byClassScrollPane = new javax.swing.JScrollPane();
576 byClassBugTree = new javax.swing.JTree();
577 byClassBugTree.setFont(JTREE_FONT);
578 byPackageScrollPane = new javax.swing.JScrollPane();
579 byPackageBugTree = new javax.swing.JTree();
580 byPackageBugTree.setFont(JTREE_FONT);
581 byBugTypeScrollPane = new javax.swing.JScrollPane();
582 byBugTypeBugTree = new javax.swing.JTree();
583 byBugTypeBugTree.setFont(JTREE_FONT);
584 byBugCategoryScrollPane = new javax.swing.JScrollPane();
585 byBugCategoryBugTree = new javax.swing.JTree();
586 byBugCategoryBugTree.setFont(JTREE_FONT);
587 bySummary = new javax.swing.JScrollPane();
588 bugSummaryEditorPane = new javax.swing.JEditorPane();
589 bugDetailsTabbedPane = new javax.swing.JTabbedPane();
590 bugDescriptionScrollPane = new javax.swing.JScrollPane();
591 bugDescriptionEditorPane = new javax.swing.JEditorPane();
592 sourceTextAreaScrollPane = new javax.swing.JScrollPane();
593 sourceTextArea = new javax.swing.JTextArea();
594 annotationTextAreaScrollPane = new javax.swing.JScrollPane();
595 annotationTextArea = new javax.swing.JTextArea();
596 urlLabel = new javax.swing.JLabel();
597 jPanel1 = new javax.swing.JPanel();
598 logoLabel = new javax.swing.JLabel();
599 growBoxSpacer = new javax.swing.JLabel();
600 theMenuBar = new javax.swing.JMenuBar();
601 fileMenu = new javax.swing.JMenu();
602 newProjectItem = new javax.swing.JMenuItem();
603 openProjectItem = new javax.swing.JMenuItem();
604 recentProjectsMenu = new javax.swing.JMenu();
605 jSeparator9 = new javax.swing.JSeparator();
606 closeProjectItem = new javax.swing.JMenuItem();
607 saveProjectItem = new javax.swing.JMenuItem();
608 saveProjectAsItem = new javax.swing.JMenuItem();
609 reloadProjectItem = new javax.swing.JMenuItem();
610 jSeparator3 = new javax.swing.JSeparator();
611 loadBugsItem = new javax.swing.JMenuItem();
612 saveBugsItem = new javax.swing.JMenuItem();
613 jSeparator6 = new javax.swing.JSeparator();
614 exitItem = new javax.swing.JMenuItem();
615 editMenu = new javax.swing.JMenu();
616 cutItem = new javax.swing.JMenuItem();
617 copyItem = new javax.swing.JMenuItem();
618 pasteItem = new javax.swing.JMenuItem();
619 jSeparator10 = new javax.swing.JSeparator();
620 selectAllItem = new javax.swing.JMenuItem();
621 viewMenu = new javax.swing.JMenu();
622 viewBugDetailsItem = new javax.swing.JCheckBoxMenuItem();
623 fullDescriptionsItem = new javax.swing.JCheckBoxMenuItem();
624 jSeparator7 = new javax.swing.JSeparator();
625 filterWarningsMenu = new javax.swing.JMenu();
626 expPriorityButton = new javax.swing.JRadioButtonMenuItem();
627 lowPriorityButton = new javax.swing.JRadioButtonMenuItem();
628 mediumPriorityButton = new javax.swing.JRadioButtonMenuItem();
629 highPriorityButton = new javax.swing.JRadioButtonMenuItem();
630 jSeparator11 = new javax.swing.JSeparator();
631 jSeparator8 = new javax.swing.JSeparator();
632 viewProjectItem = new javax.swing.JRadioButtonMenuItem();
633 viewBugsItem = new javax.swing.JRadioButtonMenuItem();
634 settingsMenu = new javax.swing.JMenu();
635 configureDetectorsItem = new javax.swing.JMenuItem();
636 effortMenu = new javax.swing.JMenu();
637 minEffortItem = new javax.swing.JCheckBoxMenuItem();
638 normalEffortItem = new javax.swing.JCheckBoxMenuItem();
639 maxEffortItem = new javax.swing.JCheckBoxMenuItem();
640 helpMenu = new javax.swing.JMenu();
641 aboutItem = new javax.swing.JMenuItem();
642
643 getContentPane().setLayout(new java.awt.GridBagLayout());
644
645 addWindowListener(new java.awt.event.WindowAdapter() {
646 @Override
647 public void windowClosing(java.awt.event.WindowEvent evt) {
648 exitForm(evt);
649 }
650 @Override
651 public void windowOpened(java.awt.event.WindowEvent evt) {
652 formWindowOpened(evt);
653 }
654 });
655
656 viewPanel.setLayout(new java.awt.CardLayout());
657
658 viewPanel.add(emptyPanel, "EmptyPanel");
659
660 viewPanel.add(reportPanel, "ReportPanel");
661
662 editProjectPanel.setLayout(new java.awt.GridBagLayout());
663
664 jarFileLabel.setFont(BUTTON_FONT);
665 jarFileLabel.setText("Archive or directory:");
666 jarFileLabel.setText(L10N.getLocalString("dlg.jarfile_lbl", "Archive or Directory:"));
667 gridBagConstraints = new java.awt.GridBagConstraints();
668 gridBagConstraints.gridx = 0;
669 gridBagConstraints.gridy = 2;
670 gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
671 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
672 editProjectPanel.add(jarFileLabel, gridBagConstraints);
673
674 jarNameTextField.addActionListener(new java.awt.event.ActionListener() {
675 public void actionPerformed(java.awt.event.ActionEvent evt) {
676 jarNameTextFieldActionPerformed(evt);
677 }
678 });
679 jarNameTextField.addFocusListener(new java.awt.event.FocusAdapter() {
680 @Override
681 public void focusGained(java.awt.event.FocusEvent evt) {
682 focusGainedHandler(evt);
683 }
684 });
685
686 gridBagConstraints = new java.awt.GridBagConstraints();
687 gridBagConstraints.gridx = 1;
688 gridBagConstraints.gridy = 2;
689 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
690 gridBagConstraints.weightx = 1.0;
691 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 0);
692 editProjectPanel.add(jarNameTextField, gridBagConstraints);
693
694 addJarButton.setFont(BUTTON_FONT);
695 addJarButton.setText("Add");
696 addJarButton.setMaximumSize(new java.awt.Dimension(90, 25));
697 addJarButton.setMinimumSize(new java.awt.Dimension(90, 25));
698 addJarButton.setPreferredSize(new java.awt.Dimension(90, 25));
699 addJarButton.setText(L10N.getLocalString("dlg.add_btn", "Add"));
700 addJarButton.addActionListener(new java.awt.event.ActionListener() {
701 public void actionPerformed(java.awt.event.ActionEvent evt) {
702 addJarButtonActionPerformed(evt);
703 }
704 });
705
706 gridBagConstraints = new java.awt.GridBagConstraints();
707 gridBagConstraints.gridx = 3;
708 gridBagConstraints.gridy = 2;
709 gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
710 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
711 editProjectPanel.add(addJarButton, gridBagConstraints);
712
713 jarFileListLabel.setFont(BUTTON_FONT);
714 jarFileListLabel.setText("Archives/directories:");
715 jarFileListLabel.setText(L10N.getLocalString("dlg.jarlist_lbl", "Archives/Directories:"));
716 gridBagConstraints = new java.awt.GridBagConstraints();
717 gridBagConstraints.gridx = 0;
718 gridBagConstraints.gridy = 3;
719 gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
720 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
721 editProjectPanel.add(jarFileListLabel, gridBagConstraints);
722
723 sourceDirLabel.setFont(BUTTON_FONT);
724 sourceDirLabel.setText("Source directory:");
725 sourceDirLabel.setText(L10N.getLocalString("dlg.srcfile_lbl", "Source directory:"));
726 gridBagConstraints = new java.awt.GridBagConstraints();
727 gridBagConstraints.gridx = 0;
728 gridBagConstraints.gridy = 8;
729 gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
730 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
731 editProjectPanel.add(sourceDirLabel, gridBagConstraints);
732
733 srcDirTextField.addActionListener(new java.awt.event.ActionListener() {
734 public void actionPerformed(java.awt.event.ActionEvent evt) {
735 srcDirTextFieldActionPerformed(evt);
736 }
737 });
738 srcDirTextField.addFocusListener(new java.awt.event.FocusAdapter() {
739 @Override
740 public void focusGained(java.awt.event.FocusEvent evt) {
741 focusGainedHandler(evt);
742 }
743 });
744
745 gridBagConstraints = new java.awt.GridBagConstraints();
746 gridBagConstraints.gridx = 1;
747 gridBagConstraints.gridy = 8;
748 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
749 gridBagConstraints.weightx = 1.0;
750 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 0);
751 editProjectPanel.add(srcDirTextField, gridBagConstraints);
752
753 addSourceDirButton.setFont(BUTTON_FONT);
754 addSourceDirButton.setText("Add");
755 addSourceDirButton.setMaximumSize(new java.awt.Dimension(90, 25));
756 addSourceDirButton.setMinimumSize(new java.awt.Dimension(90, 25));
757 addSourceDirButton.setPreferredSize(new java.awt.Dimension(90, 25));
758 addSourceDirButton.setText(L10N.getLocalString("dlg.add_btn", "Add"));
759 addSourceDirButton.addActionListener(new java.awt.event.ActionListener() {
760 public void actionPerformed(java.awt.event.ActionEvent evt) {
761 addSourceDirButtonActionPerformed(evt);
762 }
763 });
764
765 gridBagConstraints = new java.awt.GridBagConstraints();
766 gridBagConstraints.gridx = 3;
767 gridBagConstraints.gridy = 8;
768 gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
769 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
770 editProjectPanel.add(addSourceDirButton, gridBagConstraints);
771
772 sourceDirListLabel.setFont(BUTTON_FONT);
773 sourceDirListLabel.setText("Source directories:");
774 sourceDirListLabel.setText(L10N.getLocalString("dlg.srclist_lbl", "Source directories:"));
775 gridBagConstraints = new java.awt.GridBagConstraints();
776 gridBagConstraints.gridx = 0;
777 gridBagConstraints.gridy = 9;
778 gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
779 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
780 editProjectPanel.add(sourceDirListLabel, gridBagConstraints);
781
782 removeJarButton.setFont(BUTTON_FONT);
783 removeJarButton.setText("Remove");
784 removeJarButton.setMaximumSize(new java.awt.Dimension(90, 25));
785 removeJarButton.setMinimumSize(new java.awt.Dimension(90, 25));
786 removeJarButton.setPreferredSize(new java.awt.Dimension(90, 25));
787 removeJarButton.setText(L10N.getLocalString("dlg.remove_btn", "Remove"));
788 removeJarButton.addActionListener(new java.awt.event.ActionListener() {
789 public void actionPerformed(java.awt.event.ActionEvent evt) {
790 removeJarButtonActionPerformed(evt);
791 }
792 });
793
794 gridBagConstraints = new java.awt.GridBagConstraints();
795 gridBagConstraints.gridx = 3;
796 gridBagConstraints.gridy = 3;
797 gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
798 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
799 editProjectPanel.add(removeJarButton, gridBagConstraints);
800
801 removeSrcDirButton.setFont(BUTTON_FONT);
802 removeSrcDirButton.setText("Remove");
803 removeSrcDirButton.setMaximumSize(new java.awt.Dimension(90, 25));
804 removeSrcDirButton.setMinimumSize(new java.awt.Dimension(90, 25));
805 removeSrcDirButton.setPreferredSize(new java.awt.Dimension(90, 25));
806 removeSrcDirButton.setText(L10N.getLocalString("dlg.remove_btn", "Remove"));
807 removeSrcDirButton.addActionListener(new java.awt.event.ActionListener() {
808 public void actionPerformed(java.awt.event.ActionEvent evt) {
809 removeSrcDirButtonActionPerformed(evt);
810 }
811 });
812
813 gridBagConstraints = new java.awt.GridBagConstraints();
814 gridBagConstraints.gridx = 3;
815 gridBagConstraints.gridy = 9;
816 gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
817 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
818 editProjectPanel.add(removeSrcDirButton, gridBagConstraints);
819
820 gridBagConstraints = new java.awt.GridBagConstraints();
821 gridBagConstraints.gridx = 0;
822 gridBagConstraints.gridy = 1;
823 gridBagConstraints.gridwidth = 4;
824 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
825 gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
826 editProjectPanel.add(jSeparator1, gridBagConstraints);
827
828 browseJarButton.setFont(BUTTON_FONT);
829 browseJarButton.setText("Browse");
830 browseJarButton.setMaximumSize(new java.awt.Dimension(90, 25));
831 browseJarButton.setMinimumSize(new java.awt.Dimension(90, 25));
832 browseJarButton.setPreferredSize(new java.awt.Dimension(90, 25));
833 browseJarButton.setText(L10N.getLocalString("dlg.browse_btn", "Browse..."));
834 browseJarButton.addActionListener(new java.awt.event.ActionListener() {
835 public void actionPerformed(java.awt.event.ActionEvent evt) {
836 browseJarButtonActionPerformed(evt);
837 }
838 });
839
840 gridBagConstraints = new java.awt.GridBagConstraints();
841 gridBagConstraints.gridx = 2;
842 gridBagConstraints.gridy = 2;
843 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
844 editProjectPanel.add(browseJarButton, gridBagConstraints);
845
846 browseSrcDirButton.setFont(BUTTON_FONT);
847 browseSrcDirButton.setText("Browse");
848 browseSrcDirButton.setMaximumSize(new java.awt.Dimension(90, 25));
849 browseSrcDirButton.setMinimumSize(new java.awt.Dimension(90, 25));
850 browseSrcDirButton.setPreferredSize(new java.awt.Dimension(90, 25));
851 browseSrcDirButton.setText(L10N.getLocalString("dlg.browse_btn", "Browse..."));
852 browseSrcDirButton.addActionListener(new java.awt.event.ActionListener() {
853 public void actionPerformed(java.awt.event.ActionEvent evt) {
854 browseSrcDirButtonActionPerformed(evt);
855 }
856 });
857
858 gridBagConstraints = new java.awt.GridBagConstraints();
859 gridBagConstraints.gridx = 2;
860 gridBagConstraints.gridy = 8;
861 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
862 editProjectPanel.add(browseSrcDirButton, gridBagConstraints);
863
864 editProjectLabel.setBackground(new java.awt.Color(0, 0, 204));
865 editProjectLabel.setFont(LABEL_FONT);
866 editProjectLabel.setForeground(new java.awt.Color(255, 255, 255));
867 editProjectLabel.setText("Project");
868 editProjectLabel.setOpaque(true);
869 editProjectLabel.setText(L10N.getLocalString("dlg.project_lbl", "Project"));
870 gridBagConstraints = new java.awt.GridBagConstraints();
871 gridBagConstraints.gridx = 0;
872 gridBagConstraints.gridy = 0;
873 gridBagConstraints.gridwidth = 4;
874 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
875 editProjectPanel.add(editProjectLabel, gridBagConstraints);
876
877 gridBagConstraints = new java.awt.GridBagConstraints();
878 gridBagConstraints.gridx = 0;
879 gridBagConstraints.gridy = 7;
880 gridBagConstraints.gridwidth = 4;
881 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
882 gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
883 editProjectPanel.add(jSeparator2, gridBagConstraints);
884
885 findBugsButton.setMnemonic('B');
886 findBugsButton.setText("Find Bugs!");
887 findBugsButton.addActionListener(new java.awt.event.ActionListener() {
888 public void actionPerformed(java.awt.event.ActionEvent evt) {
889 findBugsButtonActionPerformed(evt);
890 }
891 });
892
893 gridBagConstraints = new java.awt.GridBagConstraints();
894 gridBagConstraints.gridx = 0;
895 gridBagConstraints.gridy = 21;
896 gridBagConstraints.gridwidth = 4;
897 gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
898 editProjectPanel.add(findBugsButton, gridBagConstraints);
899
900 gridBagConstraints = new java.awt.GridBagConstraints();
901 gridBagConstraints.gridx = 0;
902 gridBagConstraints.gridy = 14;
903 gridBagConstraints.gridwidth = 4;
904 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
905 gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
906 editProjectPanel.add(jSeparator4, gridBagConstraints);
907
908 jarFileListScrollPane.setPreferredSize(new java.awt.Dimension(259, 1));
909 jarFileList.setBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.LOWERED));
910 jarFileList.setFont(BUTTON_FONT);
911 disableEditKeyBindings(jarFileList);
912
913 jarFileList.addFocusListener(new java.awt.event.FocusAdapter() {
914 @Override
915 public void focusGained(java.awt.event.FocusEvent evt) {
916 focusGainedHandler(evt);
917 }
918 });
919
920 jarFileListScrollPane.setViewportView(jarFileList);
921
922 gridBagConstraints = new java.awt.GridBagConstraints();
923 gridBagConstraints.gridx = 1;
924 gridBagConstraints.gridy = 3;
925 gridBagConstraints.gridwidth = 2;
926 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
927 gridBagConstraints.weighty = 0.4;
928 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
929 editProjectPanel.add(jarFileListScrollPane, gridBagConstraints);
930
931 sourceDirListScrollPane.setPreferredSize(new java.awt.Dimension(259, 1));
932 sourceDirList.setBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.LOWERED));
933 sourceDirList.setFont(BUTTON_FONT);
934 disableEditKeyBindings(sourceDirList);
935 sourceDirList.addFocusListener(new java.awt.event.FocusAdapter() {
936 @Override
937 public void focusGained(java.awt.event.FocusEvent evt) {
938 focusGainedHandler(evt);
939 }
940 });
941
942 sourceDirListScrollPane.setViewportView(sourceDirList);
943
944 gridBagConstraints = new java.awt.GridBagConstraints();
945 gridBagConstraints.gridx = 1;
946 gridBagConstraints.gridy = 9;
947 gridBagConstraints.gridwidth = 2;
948 gridBagConstraints.gridheight = 3;
949 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
950 gridBagConstraints.weighty = 0.1;
951 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
952 editProjectPanel.add(sourceDirListScrollPane, gridBagConstraints);
953
954 classpathEntryLabel.setFont(BUTTON_FONT);
955 classpathEntryLabel.setText("Classpath entry:");
956 classpathEntryLabel.setText(L10N.getLocalString("dlg.classpathfile_lbl", "Classpath entry:"));
957 gridBagConstraints = new java.awt.GridBagConstraints();
958 gridBagConstraints.gridx = 0;
959 gridBagConstraints.gridy = 15;
960 gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
961 gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 3);
962 editProjectPanel.add(classpathEntryLabel, gridBagConstraints);
963
964 classpathEntryListLabel.setFont(BUTTON_FONT);
965 classpathEntryListLabel.setText("Classpath entries:");
966 classpathEntryListLabel.setText(L10N.getLocalString("dlg.classpathlist_lbl", "Classpath entries:"));
967 gridBagConstraints = new java.awt.GridBagConstraints();
968 gridBagConstraints.gridx = 0;
969 gridBagConstraints.gridy = 16;
970 gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
971 gridBagConstraints.insets = new java.awt.Insets(0, 0, 0, 3);
972 editProjectPanel.add(classpathEntryListLabel, gridBagConstraints);
973
974 classpathEntryTextField.addFocusListener(new java.awt.event.FocusAdapter() {
975 @Override
976 public void focusGained(java.awt.event.FocusEvent evt) {
977 focusGainedHandler(evt);
978 }
979 });
980
981 gridBagConstraints = new java.awt.GridBagConstraints();
982 gridBagConstraints.gridx = 1;
983 gridBagConstraints.gridy = 15;
984 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
985 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 0);
986 editProjectPanel.add(classpathEntryTextField, gridBagConstraints);
987
988 browseClasspathEntryButton.setFont(BUTTON_FONT);
989 browseClasspathEntryButton.setText("Browse");
990 browseClasspathEntryButton.setMaximumSize(new java.awt.Dimension(90, 25));
991 browseClasspathEntryButton.setMinimumSize(new java.awt.Dimension(90, 25));
992 browseClasspathEntryButton.setPreferredSize(new java.awt.Dimension(90, 25));
993 browseClasspathEntryButton.setText(L10N.getLocalString("dlg.browse_btn", "Browse..."));
994 browseClasspathEntryButton.addActionListener(new java.awt.event.ActionListener() {
995 public void actionPerformed(java.awt.event.ActionEvent evt) {
996 browseClasspathEntryButtonActionPerformed(evt);
997 }
998 });
999
1000 gridBagConstraints = new java.awt.GridBagConstraints();
1001 gridBagConstraints.gridx = 2;
1002 gridBagConstraints.gridy = 15;
1003 editProjectPanel.add(browseClasspathEntryButton, gridBagConstraints);
1004
1005 addClasspathEntryButton.setFont(BUTTON_FONT);
1006 addClasspathEntryButton.setText("Add");
1007 addClasspathEntryButton.setMaximumSize(new java.awt.Dimension(90, 25));
1008 addClasspathEntryButton.setMinimumSize(new java.awt.Dimension(90, 25));
1009 addClasspathEntryButton.setPreferredSize(new java.awt.Dimension(90, 25));
1010 addClasspathEntryButton.setText(L10N.getLocalString("dlg.add_btn", "Add"));
1011 addClasspathEntryButton.addActionListener(new java.awt.event.ActionListener() {
1012 public void actionPerformed(java.awt.event.ActionEvent evt) {
1013 addClasspathEntryButtonActionPerformed(evt);
1014 }
1015 });
1016
1017 gridBagConstraints = new java.awt.GridBagConstraints();
1018 gridBagConstraints.gridx = 3;
1019 gridBagConstraints.gridy = 15;
1020 gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
1021 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 0);
1022 editProjectPanel.add(addClasspathEntryButton, gridBagConstraints);
1023
1024 removeClasspathEntryButton.setFont(BUTTON_FONT);
1025 removeClasspathEntryButton.setText("Remove");
1026 removeClasspathEntryButton.setMaximumSize(new java.awt.Dimension(90, 25));
1027 removeClasspathEntryButton.setMinimumSize(new java.awt.Dimension(90, 25));
1028 removeClasspathEntryButton.setPreferredSize(new java.awt.Dimension(90, 25));
1029 removeClasspathEntryButton.setText(L10N.getLocalString("dlg.remove_btn", "Remove"));
1030 removeClasspathEntryButton.addActionListener(new java.awt.event.ActionListener() {
1031 public void actionPerformed(java.awt.event.ActionEvent evt) {
1032 removeClasspathEntryButtonActionPerformed(evt);
1033 }
1034 });
1035
1036 gridBagConstraints = new java.awt.GridBagConstraints();
1037 gridBagConstraints.gridx = 3;
1038 gridBagConstraints.gridy = 16;
1039 gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
1040 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 0);
1041 editProjectPanel.add(removeClasspathEntryButton, gridBagConstraints);
1042
1043 classpathEntryListScrollPane.setPreferredSize(new java.awt.Dimension(259, 1));
1044 classpathEntryList.setBorder(new javax.swing.border.BevelBorder(javax.swing.border.BevelBorder.LOWERED));
1045 classpathEntryList.setFont(BUTTON_FONT);
1046 disableEditKeyBindings(classpathEntryList);
1047 classpathEntryList.addFocusListener(new java.awt.event.FocusAdapter() {
1048 @Override
1049 public void focusGained(java.awt.event.FocusEvent evt) {
1050 focusGainedHandler(evt);
1051 }
1052 });
1053
1054 classpathEntryListScrollPane.setViewportView(classpathEntryList);
1055
1056 gridBagConstraints = new java.awt.GridBagConstraints();
1057 gridBagConstraints.gridx = 1;
1058 gridBagConstraints.gridy = 16;
1059 gridBagConstraints.gridwidth = 2;
1060 gridBagConstraints.gridheight = 3;
1061 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
1062 gridBagConstraints.weighty = 0.1;
1063 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
1064 editProjectPanel.add(classpathEntryListScrollPane, gridBagConstraints);
1065
1066 gridBagConstraints = new java.awt.GridBagConstraints();
1067 gridBagConstraints.gridx = 0;
1068 gridBagConstraints.gridy = 20;
1069 gridBagConstraints.gridwidth = 4;
1070 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
1071 gridBagConstraints.insets = new java.awt.Insets(3, 0, 3, 0);
1072 editProjectPanel.add(jSeparator5, gridBagConstraints);
1073
1074 sourceUpButton.setFont(BUTTON_FONT);
1075 sourceUpButton.setText("Up");
1076 sourceUpButton.setMaximumSize(new java.awt.Dimension(90, 25));
1077 sourceUpButton.setMinimumSize(new java.awt.Dimension(90, 25));
1078 sourceUpButton.setPreferredSize(new java.awt.Dimension(90, 25));
1079 sourceUpButton.setText(L10N.getLocalString("dlg.up_btn", "Up"));
1080 sourceUpButton.addActionListener(new java.awt.event.ActionListener() {
1081 public void actionPerformed(java.awt.event.ActionEvent evt) {
1082 sourceUpButtonActionPerformed(evt);
1083 }
1084 });
1085
1086 gridBagConstraints = new java.awt.GridBagConstraints();
1087 gridBagConstraints.gridx = 3;
1088 gridBagConstraints.gridy = 10;
1089 gridBagConstraints.weighty = 0.2;
1090 editProjectPanel.add(sourceUpButton, gridBagConstraints);
1091
1092 sourceDownButton.setFont(BUTTON_FONT);
1093 sourceDownButton.setText("Down");
1094 sourceDownButton.setMaximumSize(new java.awt.Dimension(90, 25));
1095 sourceDownButton.setMinimumSize(new java.awt.Dimension(90, 25));
1096 sourceDownButton.setPreferredSize(new java.awt.Dimension(90, 25));
1097 sourceDownButton.setText(L10N.getLocalString("dlg.down_btn", "Down"));
1098 sourceDownButton.addActionListener(new java.awt.event.ActionListener() {
1099 public void actionPerformed(java.awt.event.ActionEvent evt) {
1100 sourceDownButtonActionPerformed(evt);
1101 }
1102 });
1103
1104 gridBagConstraints = new java.awt.GridBagConstraints();
1105 gridBagConstraints.gridy = 11;
1106 editProjectPanel.add(sourceDownButton, gridBagConstraints);
1107
1108 classpathUpButton.setFont(BUTTON_FONT);
1109 classpathUpButton.setText("Up");
1110 classpathUpButton.setMaximumSize(new java.awt.Dimension(90, 25));
1111 classpathUpButton.setMinimumSize(new java.awt.Dimension(90, 25));
1112 classpathUpButton.setPreferredSize(new java.awt.Dimension(90, 25));
1113 classpathUpButton.setText(L10N.getLocalString("dlg.up_btn", "Up"));
1114 classpathUpButton.addActionListener(new java.awt.event.ActionListener() {
1115 public void actionPerformed(java.awt.event.ActionEvent evt) {
1116 classpathUpButtonActionPerformed(evt);
1117 }
1118 });
1119
1120 gridBagConstraints = new java.awt.GridBagConstraints();
1121 gridBagConstraints.gridx = 3;
1122 gridBagConstraints.gridy = 17;
1123 gridBagConstraints.weighty = 0.2;
1124 editProjectPanel.add(classpathUpButton, gridBagConstraints);
1125
1126 classpathDownButton.setFont(BUTTON_FONT);
1127 classpathDownButton.setText("Down");
1128 classpathDownButton.setMaximumSize(new java.awt.Dimension(90, 25));
1129 classpathDownButton.setMinimumSize(new java.awt.Dimension(90, 25));
1130 classpathDownButton.setPreferredSize(new java.awt.Dimension(90, 25));
1131 classpathDownButton.setText(L10N.getLocalString("dlg.down_btn", "Down"));
1132 classpathDownButton.addActionListener(new java.awt.event.ActionListener() {
1133 public void actionPerformed(java.awt.event.ActionEvent evt) {
1134 classpathDownButtonActionPerformed(evt);
1135 }
1136 });
1137
1138 gridBagConstraints = new java.awt.GridBagConstraints();
1139 gridBagConstraints.gridx = 3;
1140 gridBagConstraints.gridy = 18;
1141 editProjectPanel.add(classpathDownButton, gridBagConstraints);
1142
1143 viewPanel.add(editProjectPanel, "EditProjectPanel");
1144
1145 bugTreePanel.setLayout(new java.awt.GridBagLayout());
1146
1147 bugTreeBugDetailsSplitter.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
1148 bugTreeBugDetailsSplitter.setResizeWeight(1.0);
1149 bugTreeBugDetailsSplitter.setOneTouchExpandable(true);
1150 bugTreeBugDetailsSplitter.addPropertyChangeListener(new java.beans.PropertyChangeListener() {
1151 public void propertyChange(java.beans.PropertyChangeEvent evt) {
1152 bugTreeBugDetailsSplitterPropertyChange(evt);
1153 }
1154 });
1155
1156 byClassBugTree.addFocusListener(new java.awt.event.FocusAdapter() {
1157 @Override
1158 public void focusGained(java.awt.event.FocusEvent evt) {
1159 focusGainedHandler(evt);
1160 }
1161 });
1162
1163 byClassScrollPane.setViewportView(byClassBugTree);
1164
1165 groupByTabbedPane.addTab("By Class", byClassScrollPane);
1166
1167 byPackageBugTree.addFocusListener(new java.awt.event.FocusAdapter() {
1168 @Override
1169 public void focusGained(java.awt.event.FocusEvent evt) {
1170 focusGainedHandler(evt);
1171 }
1172 });
1173
1174 byPackageScrollPane.setViewportView(byPackageBugTree);
1175
1176 groupByTabbedPane.addTab("By Package", byPackageScrollPane);
1177
1178 byBugTypeBugTree.addFocusListener(new java.awt.event.FocusAdapter() {
1179 @Override
1180 public void focusGained(java.awt.event.FocusEvent evt) {
1181 focusGainedHandler(evt);
1182 }
1183 });
1184
1185 byBugTypeScrollPane.setViewportView(byBugTypeBugTree);
1186
1187 groupByTabbedPane.addTab("By Bug Type", byBugTypeScrollPane);
1188
1189 byBugCategoryBugTree.addFocusListener(new java.awt.event.FocusAdapter() {
1190 @Override
1191 public void focusGained(java.awt.event.FocusEvent evt) {
1192 focusGainedHandler(evt);
1193 }
1194 });
1195
1196 byBugCategoryScrollPane.setViewportView(byBugCategoryBugTree);
1197
1198 groupByTabbedPane.addTab("By Category Type", byBugCategoryScrollPane);
1199
1200 bugSummaryEditorPane.addFocusListener(new java.awt.event.FocusAdapter() {
1201 @Override
1202 public void focusGained(java.awt.event.FocusEvent evt) {
1203 focusGainedHandler(evt);
1204 }
1205 });
1206
1207 bySummary.setViewportView(bugSummaryEditorPane);
1208
1209 groupByTabbedPane.addTab("Summary", bySummary);
1210
1211 bugTreeBugDetailsSplitter.setTopComponent(groupByTabbedPane);
1212
1213 bugDescriptionEditorPane.setEditable(false);
1214 bugDescriptionEditorPane.addFocusListener(new java.awt.event.FocusAdapter() {
1215 @Override
1216 public void focusGained(java.awt.event.FocusEvent evt) {
1217 focusGainedHandler(evt);
1218 }
1219 });
1220
1221 bugDescriptionScrollPane.setViewportView(bugDescriptionEditorPane);
1222
1223 bugDetailsTabbedPane.addTab("Details", bugDescriptionScrollPane);
1224
1225 sourceTextAreaScrollPane.setMinimumSize(new java.awt.Dimension(22, 180));
1226 sourceTextAreaScrollPane.setPreferredSize(new java.awt.Dimension(0, 100));
1227 sourceTextArea.setEditable(false);
1228 sourceTextArea.setFont(SOURCE_FONT);
1229 sourceTextArea.setEnabled(false);
1230 sourceTextArea.addFocusListener(new java.awt.event.FocusAdapter() {
1231 @Override
1232 public void focusGained(java.awt.event.FocusEvent evt) {
1233 focusGainedHandler(evt);
1234 }
1235 });
1236
1237
1238 sourceTextAreaScrollPane.setViewportView(sourceTextArea);
1239 sourceLineNumberer = new LineNumberer(sourceTextArea);
1240 sourceLineNumberer.setBackground(Color.WHITE);
1241 sourceTextAreaScrollPane.setRowHeaderView(sourceLineNumberer);
1242
1243 bugDetailsTabbedPane.addTab("Source code", sourceTextAreaScrollPane);
1244
1245 annotationTextArea.addFocusListener(new java.awt.event.FocusAdapter() {
1246 @Override
1247 public void focusGained(java.awt.event.FocusEvent evt) {
1248 focusGainedHandler(evt);
1249 }
1250 });
1251
1252 annotationTextAreaScrollPane.setViewportView(annotationTextArea);
1253
1254 bugDetailsTabbedPane.addTab("Annotations", annotationTextAreaScrollPane);
1255
1256 bugTreeBugDetailsSplitter.setBottomComponent(bugDetailsTabbedPane);
1257
1258 gridBagConstraints = new java.awt.GridBagConstraints();
1259 gridBagConstraints.gridx = 0;
1260 gridBagConstraints.gridy = 1;
1261 gridBagConstraints.gridwidth = 2;
1262 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
1263 gridBagConstraints.weightx = 1.0;
1264 gridBagConstraints.weighty = 1.0;
1265 bugTreePanel.add(bugTreeBugDetailsSplitter, gridBagConstraints);
1266
1267 viewPanel.add(bugTreePanel, "BugTree");
1268
1269 {
1270 equalizeControls( new JComponent[]
1271 {
1272 addJarButton,
1273 addSourceDirButton,
1274 addClasspathEntryButton,
1275 removeJarButton,
1276 removeSrcDirButton,
1277 removeClasspathEntryButton,
1278 browseJarButton,
1279 browseSrcDirButton,
1280 browseClasspathEntryButton,
1281 sourceUpButton,
1282 sourceDownButton,
1283 classpathUpButton,
1284 classpathDownButton
1285 });
1286
1287 groupByTabbedPane.setTitleAt(0, L10N.getLocalString( "dlg.byclass_tab", "By Class"));
1288 groupByTabbedPane.setTitleAt(1, L10N.getLocalString( "dlg.bypackage_tab", "By Package"));
1289 groupByTabbedPane.setTitleAt(2, L10N.getLocalString( "dlg.bybugtype_tab", "By Bug Type"));
1290 groupByTabbedPane.setTitleAt(3, L10N.getLocalString( "dlg.bybugcategory_tab", "By Bug Category"));
1291 groupByTabbedPane.setTitleAt(4, L10N.getLocalString( "dlg.summary_tab", "Summary"));
1292 bugDetailsTabbedPane.setTitleAt(0, L10N.getLocalString( "dlg.details_tab", "Details"));
1293 bugDetailsTabbedPane.setTitleAt(1, L10N.getLocalString( "dlg.sourcecode_tab", "Source Code"));
1294 bugDetailsTabbedPane.setTitleAt(2, L10N.getLocalString( "dlg.annotations_tab", "Annotations"));
1295 }
1296
1297 gridBagConstraints = new java.awt.GridBagConstraints();
1298 gridBagConstraints.gridx = 0;
1299 gridBagConstraints.gridy = 0;
1300 gridBagConstraints.gridwidth = 2;
1301 gridBagConstraints.fill = java.awt.GridBagConstraints.BOTH;
1302 gridBagConstraints.weightx = 1.0;
1303 gridBagConstraints.weighty = 1.0;
1304 getContentPane().add(viewPanel, gridBagConstraints);
1305
1306 urlLabel.setFont(BUTTON_FONT);
1307 urlLabel.setText("FindBugs - http://findbugs.sourceforge.net/");
1308 gridBagConstraints = new java.awt.GridBagConstraints();
1309 gridBagConstraints.gridx = 0;
1310 gridBagConstraints.gridy = 1;
1311 gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
1312 gridBagConstraints.insets = new java.awt.Insets(2, 2, 2, 0);
1313 getContentPane().add(urlLabel, gridBagConstraints);
1314
1315 jPanel1.setLayout(new javax.swing.BoxLayout(jPanel1, javax.swing.BoxLayout.X_AXIS));
1316
1317 jPanel1.add(logoLabel);
1318
1319 growBoxSpacer.setMaximumSize(new java.awt.Dimension(16, 16));
1320 jPanel1.add(growBoxSpacer);
1321
1322 gridBagConstraints = new java.awt.GridBagConstraints();
1323 gridBagConstraints.gridx = 1;
1324 gridBagConstraints.gridy = 1;
1325 gridBagConstraints.anchor = java.awt.GridBagConstraints.SOUTHEAST;
1326 gridBagConstraints.insets = new java.awt.Insets(2, 0, 2, 2);
1327 getContentPane().add(jPanel1, gridBagConstraints);
1328
1329 theMenuBar.setFont(BUTTON_FONT);
1330 fileMenu.setText("File");
1331 fileMenu.setFont(BUTTON_FONT);
1332 localiseButton(fileMenu, "menu.file_menu", "&File", true);
1333 fileMenu.addMenuListener(new javax.swing.event.MenuListener() {
1334 public void menuCanceled(javax.swing.event.MenuEvent evt) {
1335 }
1336 public void menuDeselected(javax.swing.event.MenuEvent evt) {
1337 }
1338 public void menuSelected(javax.swing.event.MenuEvent evt) {
1339 fileMenuMenuSelected(evt);
1340 }
1341 });
1342
1343 newProjectItem.setFont(BUTTON_FONT);
1344 newProjectItem.setText("New Project");
1345 localiseButton(newProjectItem, "menu.new_item", "&New Project", true);
1346 newProjectItem.addActionListener(new java.awt.event.ActionListener() {
1347 public void actionPerformed(java.awt.event.ActionEvent evt) {
1348 newProjectItemActionPerformed(evt);
1349 }
1350 });
1351
1352 fileMenu.add(newProjectItem);
1353
1354 openProjectItem.setFont(BUTTON_FONT);
1355 openProjectItem.setText("Open Project...");
1356 localiseButton(openProjectItem, "menu.open_item", "&Open Project...", true);
1357 openProjectItem.addActionListener(new java.awt.event.ActionListener() {
1358 public void actionPerformed(java.awt.event.ActionEvent evt) {
1359 openProjectItemActionPerformed(evt);
1360 }
1361 });
1362
1363 fileMenu.add(openProjectItem);
1364
1365 recentProjectsMenu.setText("Recent Projects");
1366 recentProjectsMenu.setFont(BUTTON_FONT);
1367 localiseButton(recentProjectsMenu, "menu.recent_menu", "R&ecent Projects", true);
1368 rebuildRecentProjectsMenu();
1369 fileMenu.add(recentProjectsMenu);
1370
1371 fileMenu.add(jSeparator9);
1372
1373 closeProjectItem.setFont(BUTTON_FONT);
1374 closeProjectItem.setText("Close Project");
1375 localiseButton(closeProjectItem, "menu.close_item", "&Close Project", true);
1376 closeProjectItem.addActionListener(new java.awt.event.ActionListener() {
1377 public void actionPerformed(java.awt.event.ActionEvent evt) {
1378 closeProjectItemActionPerformed(evt);
1379 }
1380 });
1381
1382 fileMenu.add(closeProjectItem);
1383
1384 saveProjectItem.setFont(BUTTON_FONT);
1385 saveProjectItem.setText("Save Project");
1386 localiseButton(saveProjectItem, "menu.save_item", "&Save Project", true);
1387 saveProjectItem.addActionListener(new java.awt.event.ActionListener() {
1388 public void actionPerformed(java.awt.event.ActionEvent evt) {
1389 saveProjectItemActionPerformed(evt);
1390 }
1391 });
1392
1393 fileMenu.add(saveProjectItem);
1394
1395 saveProjectAsItem.setFont(BUTTON_FONT);
1396 saveProjectAsItem.setText("Save Project As...");
1397 localiseButton(saveProjectAsItem, "menu.saveas_item", "Save Project &As...", true);
1398 saveProjectAsItem.addActionListener(new java.awt.event.ActionListener() {
1399 public void actionPerformed(java.awt.event.ActionEvent evt) {
1400 saveProjectAsItemActionPerformed(evt);
1401 }
1402 });
1403
1404 fileMenu.add(saveProjectAsItem);
1405
1406 reloadProjectItem.setFont(BUTTON_FONT);
1407 reloadProjectItem.setText("Reload Project");
1408 localiseButton(reloadProjectItem, "menu.reload_item", "&Reload Project", true);
1409 reloadProjectItem.addActionListener(new java.awt.event.ActionListener() {
1410 public void actionPerformed(java.awt.event.ActionEvent evt) {
1411 reloadProjectItemActionPerformed(evt);
1412 }
1413 });
1414
1415 fileMenu.add(reloadProjectItem);
1416
1417 fileMenu.add(jSeparator3);
1418
1419 loadBugsItem.setFont(BUTTON_FONT);
1420 loadBugsItem.setText("Load Bugs...");
1421 localiseButton(loadBugsItem, "menu.loadbugs_item", "&Load Bugs...", true);
1422 loadBugsItem.addActionListener(new java.awt.event.ActionListener() {
1423 public void actionPerformed(java.awt.event.ActionEvent evt) {
1424 loadBugsItemActionPerformed(evt);
1425 }
1426 });
1427
1428 fileMenu.add(loadBugsItem);
1429
1430 saveBugsItem.setFont(BUTTON_FONT);
1431 saveBugsItem.setText("Save Bugs");
1432 localiseButton(saveBugsItem, "menu.savebugs_item", "Save &Bugs...", true);
1433 saveBugsItem.addActionListener(new java.awt.event.ActionListener() {
1434 public void actionPerformed(java.awt.event.ActionEvent evt) {
1435 saveBugsItemActionPerformed(evt);
1436 }
1437 });
1438
1439 fileMenu.add(saveBugsItem);
1440
1441 fileMenu.add(jSeparator6);
1442
1443 exitItem.setFont(BUTTON_FONT);
1444 exitItem.setText("Exit");
1445 localiseButton(exitItem, "menu.exit_item", "E&xit", true);
1446 exitItem.addActionListener(new java.awt.event.ActionListener() {
1447 public void actionPerformed(java.awt.event.ActionEvent evt) {
1448 exitItemActionPerformed(evt);
1449 }
1450 });
1451
1452 fileMenu.add(exitItem);
1453
1454 theMenuBar.add(fileMenu);
1455
1456 editMenu.setText("Edit");
1457 editMenu.setFont(BUTTON_FONT);
1458 editMenu.setEnabled(false);
1459 localiseButton(editMenu, "menu.edit_menu", "&Edit", true);
1460 cutItem.setFont(BUTTON_FONT);
1461 cutItem.setText("Cut");
1462 localiseButton(cutItem, "menu.cut_item", "Cut", true);
1463 cutItem.addActionListener(new java.awt.event.ActionListener() {
1464 public void actionPerformed(java.awt.event.ActionEvent evt) {
1465 cutActionPerformed(evt);
1466 }
1467 });
1468
1469 editMenu.add(cutItem);
1470
1471 copyItem.setFont(BUTTON_FONT);
1472 copyItem.setText("Copy");
1473 localiseButton(copyItem, "menu.copy_item", "Copy", true);
1474 copyItem.addActionListener(new java.awt.event.ActionListener() {
1475 public void actionPerformed(java.awt.event.ActionEvent evt) {
1476 copyActionPerformed(evt);
1477 }
1478 });
1479
1480 editMenu.add(copyItem);
1481
1482 pasteItem.setFont(BUTTON_FONT);
1483 pasteItem.setText("Paste");
1484 localiseButton(pasteItem, "menu.paste_item", "Paste", true);
1485 pasteItem.addActionListener(new java.awt.event.ActionListener() {
1486 public void actionPerformed(java.awt.event.ActionEvent evt) {
1487 pasteActionPerformed(evt);
1488 }
1489 });
1490
1491 editMenu.add(pasteItem);
1492
1493 editMenu.add(jSeparator10);
1494
1495 selectAllItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_A, java.awt.event.InputEvent.CTRL_MASK));
1496 selectAllItem.setFont(BUTTON_FONT);
1497 selectAllItem.setText("Select All");
1498 localiseButton(selectAllItem, "menu.selectall_item", "Select &All", true);
1499 selectAllItem.addActionListener(new java.awt.event.ActionListener() {
1500 public void actionPerformed(java.awt.event.ActionEvent evt) {
1501 selectAllActionPerformed(evt);
1502 }
1503 });
1504
1505 editMenu.add(selectAllItem);
1506
1507 theMenuBar.add(editMenu);
1508
1509 viewMenu.setText("View");
1510 viewMenu.setFont(BUTTON_FONT);
1511 localiseButton(viewMenu, "menu.view_menu", "&View", true);
1512
1513 viewMenu.addMenuListener(new javax.swing.event.MenuListener() {
1514 public void menuCanceled(javax.swing.event.MenuEvent evt) {
1515 }
1516 public void menuDeselected(javax.swing.event.MenuEvent evt) {
1517 }
1518 public void menuSelected(javax.swing.event.MenuEvent evt) {
1519 viewMenuMenuSelected(evt);
1520 }
1521 });
1522
1523 viewBugDetailsItem.setFont(BUTTON_FONT);
1524 viewBugDetailsItem.setSelected(true);
1525 viewBugDetailsItem.setText("Bug Details");
1526 localiseButton(viewBugDetailsItem, "menu.bugdetails_item", "Bug &Details", true);
1527 viewBugDetailsItem.addActionListener(new java.awt.event.ActionListener() {
1528 public void actionPerformed(java.awt.event.ActionEvent evt) {
1529 viewBugDetailsItemActionPerformed(evt);
1530 }
1531 });
1532
1533 viewMenu.add(viewBugDetailsItem);
1534
1535 fullDescriptionsItem.setFont(BUTTON_FONT);
1536 fullDescriptionsItem.setSelected(true);
1537 fullDescriptionsItem.setText("Full Descriptions");
1538 localiseButton(fullDescriptionsItem, "menu.fulldescriptions_item", "&Full Descriptions", true);
1539 fullDescriptionsItem.addActionListener(new java.awt.event.ActionListener() {
1540 public void actionPerformed(java.awt.event.ActionEvent evt) {
1541 fullDescriptionsItemActionPerformed(evt);
1542 }
1543 });
1544
1545 viewMenu.add(fullDescriptionsItem);
1546
1547 viewMenu.add(jSeparator7);
1548
1549 filterWarningsMenu.setText("Filter Warnings");
1550 filterWarningsMenu.setFont(BUTTON_FONT);
1551 localiseButton(filterWarningsMenu, "menu.filterwarnings_menu", "Filter &Warnings", true);
1552 expPriorityButton.setFont(BUTTON_FONT);
1553 expPriorityButton.setText("Experimental Priority");
1554 priorityButtonGroup.add(expPriorityButton);
1555 localiseButton(expPriorityButton, "menu.exppriority_item", "&Experimental Priority", true);
1556 expPriorityButton.setSelected(getPriorityThreshold() == Detector.EXP_PRIORITY);
1557 expPriorityButton.addActionListener(new java.awt.event.ActionListener() {
1558 public void actionPerformed(java.awt.event.ActionEvent evt) {
1559 expPriorityButtonActionPerformed(evt);
1560 }
1561 });
1562
1563 filterWarningsMenu.add(expPriorityButton);
1564
1565 lowPriorityButton.setFont(BUTTON_FONT);
1566 lowPriorityButton.setText("Low Priority");
1567 priorityButtonGroup.add(lowPriorityButton);
1568 localiseButton(lowPriorityButton, "menu.lowpriority_item", "&Low Priority", true);
1569 lowPriorityButton.setSelected(getPriorityThreshold() == Detector.LOW_PRIORITY);
1570 lowPriorityButton.addActionListener(new java.awt.event.ActionListener() {
1571 public void actionPerformed(java.awt.event.ActionEvent evt) {
1572 lowPriorityButtonActionPerformed(evt);
1573 }
1574 });
1575
1576 filterWarningsMenu.add(lowPriorityButton);
1577
1578 mediumPriorityButton.setFont(BUTTON_FONT);
1579 mediumPriorityButton.setText("Medium Priority");
1580 priorityButtonGroup.add(mediumPriorityButton);
1581 localiseButton(mediumPriorityButton, "menu.mediumpriority_item", "&Medium Priority", true);
1582 mediumPriorityButton.setSelected(getPriorityThreshold() == Detector.NORMAL_PRIORITY);
1583 mediumPriorityButton.addActionListener(new java.awt.event.ActionListener() {
1584 public void actionPerformed(java.awt.event.ActionEvent evt) {
1585 mediumPriorityButtonActionPerformed(evt);
1586 }
1587 });
1588
1589 filterWarningsMenu.add(mediumPriorityButton);
1590
1591 highPriorityButton.setFont(BUTTON_FONT);
1592 highPriorityButton.setText("High Priority");
1593 priorityButtonGroup.add(highPriorityButton);
1594 localiseButton(highPriorityButton, "menu.highpriority_item", "&High Priority", true);
1595 highPriorityButton.setSelected(getPriorityThreshold() == Detector.HIGH_PRIORITY);
1596 highPriorityButton.addActionListener(new java.awt.event.ActionListener() {
1597 public void actionPerformed(java.awt.event.ActionEvent evt) {
1598 highPriorityButtonActionPerformed(evt);
1599 }
1600 });
1601
1602 filterWarningsMenu.add(highPriorityButton);
1603
1604 filterWarningsMenu.add(jSeparator11);
1605
1606 viewMenu.add(filterWarningsMenu);
1607
1608 ButtonGroup bg = new ButtonGroup();
1609 bg.add(expPriorityButton);
1610 bg.add(lowPriorityButton);
1611 bg.add(mediumPriorityButton);
1612 bg.add(highPriorityButton);
1613
1614 viewMenu.add(jSeparator8);
1615
1616 viewProjectItem.setFont(BUTTON_FONT);
1617 viewProjectItem.setText("View Project Details");
1618 viewProjectItem.setEnabled(false);
1619 localiseButton(viewProjectItem, "menu.viewprojectdetails_item", "View Project Details", true);
1620 viewProjectItem.addActionListener(new java.awt.event.ActionListener() {
1621 public void actionPerformed(java.awt.event.ActionEvent evt) {
1622 viewProjectItemActionPerformed(evt);
1623 }
1624 });
1625
1626 viewMenu.add(viewProjectItem);
1627
1628 viewBugsItem.setFont(BUTTON_FONT);
1629 viewBugsItem.setText("View Bugs");
1630 viewBugsItem.setEnabled(false);
1631 localiseButton(viewBugsItem, "menu.viewbugs_item", "View Bugs", true);
1632 viewBugsItem.addActionListener(new java.awt.event.ActionListener() {
1633 public void actionPerformed(java.awt.event.ActionEvent evt) {
1634 viewBugsItemActionPerformed(evt);
1635 }
1636 });
1637
1638 viewMenu.add(viewBugsItem);
1639
1640 theMenuBar.add(viewMenu);
1641
1642 settingsMenu.setText("Settings");
1643 settingsMenu.setFont(BUTTON_FONT);
1644 localiseButton(settingsMenu, "menu.settings_menu", "&Settings", true);
1645 settingsMenu.addActionListener(new java.awt.event.ActionListener() {
1646 public void actionPerformed(java.awt.event.ActionEvent evt) {
1647 settingsMenuActionPerformed(evt);
1648 }
1649 });
1650
1651 configureDetectorsItem.setFont(BUTTON_FONT);
1652 configureDetectorsItem.setText("Configure Detectors...");
1653 localiseButton(configureDetectorsItem, "menu.configure_item", "&Configure Detectors...", true);
1654 configureDetectorsItem.addActionListener(new java.awt.event.ActionListener() {
1655 public void actionPerformed(java.awt.event.ActionEvent evt) {
1656 configureDetectorsItemActionPerformed(evt);
1657 }
1658 });
1659
1660 settingsMenu.add(configureDetectorsItem);
1661
1662 effortMenu.setText("Effort");
1663 effortMenu.setFont(BUTTON_FONT);
1664 localiseButton(effortMenu, "menu.effort_menu", "Effort", true);
1665 minEffortItem.setFont(BUTTON_FONT);
1666 minEffortItem.setText("Minimum");
1667 effortButtonGroup.add(minEffortItem);
1668 localiseButton(minEffortItem, "menu.mineffort_item", "&Minimum", true);
1669 minEffortItem.addActionListener(new java.awt.event.ActionListener() {
1670 public void actionPerformed(java.awt.event.ActionEvent evt) {
1671 minEffortItemActionPerformed(evt);
1672 }
1673 });
1674
1675 effortMenu.add(minEffortItem);
1676
1677 normalEffortItem.setFont(BUTTON_FONT);
1678 normalEffortItem.setSelected(true);
1679 normalEffortItem.setText("Normal");
1680 effortButtonGroup.add(normalEffortItem);
1681 localiseButton(normalEffortItem, "menu.normaleffort_item", "&Normal", true);
1682 normalEffortItem.addActionListener(new java.awt.event.ActionListener() {
1683 public void actionPerformed(java.awt.event.ActionEvent evt) {
1684 normalEffortItemActionPerformed(evt);
1685 }
1686 });
1687
1688 effortMenu.add(normalEffortItem);
1689
1690 maxEffortItem.setFont(BUTTON_FONT);
1691 maxEffortItem.setText("Maximum");
1692 effortButtonGroup.add(maxEffortItem);
1693 localiseButton(maxEffortItem, "menu.maxeffort_item", "&Maximum", true);
1694 maxEffortItem.addActionListener(new java.awt.event.ActionListener() {
1695 public void actionPerformed(java.awt.event.ActionEvent evt) {
1696 maxEffortItemActionPerformed(evt);
1697 }
1698 });
1699
1700 effortMenu.add(maxEffortItem);
1701
1702 settingsMenu.add(effortMenu);
1703
1704 theMenuBar.add(settingsMenu);
1705
1706 helpMenu.setText("Help");
1707 helpMenu.setFont(BUTTON_FONT);
1708 localiseButton(helpMenu, "menu.help_menu", "&Help", true);
1709 aboutItem.setFont(BUTTON_FONT);
1710 aboutItem.setText("About...");
1711 localiseButton(aboutItem, "menu.about_item", "&About", true);
1712 aboutItem.addActionListener(new java.awt.event.ActionListener() {
1713 public void actionPerformed(java.awt.event.ActionEvent evt) {
1714 aboutItemActionPerformed(evt);
1715 }
1716 });
1717
1718 helpMenu.add(aboutItem);
1719
1720 theMenuBar.add(helpMenu);
1721
1722 setJMenuBar(theMenuBar);
1723
1724 pack();
1725 }//GEN-END:initComponents
1726
1727 private void maxEffortItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_maxEffortItemActionPerformed
1728 settingList = FindBugs.MAX_EFFORT;
1729 }//GEN-LAST:event_maxEffortItemActionPerformed
1730
1731 private void normalEffortItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_normalEffortItemActionPerformed
1732 settingList = FindBugs.DEFAULT_EFFORT;
1733 }//GEN-LAST:event_normalEffortItemActionPerformed
1734
1735 private void minEffortItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_minEffortItemActionPerformed
1736 settingList = FindBugs.MIN_EFFORT;
1737 }//GEN-LAST:event_minEffortItemActionPerformed
1738
1739 private void settingsMenuActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_settingsMenuActionPerformed
1740 // TODO add your handling code here:
1741 }//GEN-LAST:event_settingsMenuActionPerformed
1742
1743 private void formWindowOpened(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowOpened
1744 if (SystemProperties.getBoolean("findbugs.noSummary")) {
1745 groupByTabbedPane.remove(bySummary);
1746 }
1747 }//GEN-LAST:event_formWindowOpened
1748
1749 private void selectAllActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_selectAllActionPerformed
1750 if (selectedComponent == null)
1751 return;
1752
1753 if (selectedComponent instanceof JTextComponent)
1754 ((JTextComponent)selectedComponent).selectAll();
1755 else if (selectedComponent instanceof JList) {
1756 JList list = (JList)selectedComponent;
1757 list.setSelectionInterval(0, list.getModel().getSize()-1);
1758 }
1759 }//GEN-LAST:event_selectAllActionPerformed
1760
1761 private void disableEditKeyBindings(JList list) {
1762 list.getInputMap().put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_X, java.awt.event.InputEvent.CTRL_MASK), "none");
1763 list.getInputMap().put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_C, java.awt.event.InputEvent.CTRL_MASK), "none");
1764 list.getInputMap().put(KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_V, java.awt.event.InputEvent.CTRL_MASK), "none");
1765 }
1766
1767 private String buildSelectPath(JList list) {
1768 StringBuilder path = new StringBuilder();
1769 int[] indices = list.getSelectedIndices();
1770 String separatorStr = SystemProperties.getProperty("path.separator");
1771 String sep = "";
1772 ListModel m = list.getModel();
1773 for (int indice : indices) {
1774 path.append(sep);
1775 sep = separatorStr;
1776 path.append(m.getElementAt(indice));
1777 }
1778 return path.toString();
1779 }
1780
1781 private void pasteActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_pasteActionPerformed
1782 if (selectedComponent == null)
1783 return;
1784
1785 if (selectedComponent instanceof JTextComponent)
1786 ((JTextComponent)selectedComponent).paste();
1787 else if (selectedComponent instanceof JList) {
1788 Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
1789 Transferable transfer = cb.getContents(this);
1790 if (transfer.isDataFlavorSupported(DataFlavor.stringFlavor)) {
1791 try {
1792 String path = (String)transfer.getTransferData(DataFlavor.stringFlavor);
1793
1794 if (selectedComponent == jarFileList) {
1795 jarNameTextField.setText(path);
1796 addJarButtonActionPerformed(evt);
1797 }
1798 else if (selectedComponent == sourceDirList) {
1799 srcDirTextField.setText(path);
1800 this.addSourceDirButtonActionPerformed(evt);
1801 }
1802 else if (selectedComponent == classpathEntryList) {
1803 classpathEntryTextField.setText(path);
1804 addClasspathEntryButtonActionPerformed(evt);
1805 }
1806 } catch (Exception e) {
1807 }
1808 }
1809 }
1810 }//GEN-LAST:event_pasteActionPerformed
1811
1812 private void copyActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_copyActionPerformed
1813 if (selectedComponent == null)
1814 return;
1815
1816 if (selectedComponent instanceof JTextComponent)
1817 ((JTextComponent)selectedComponent).copy();
1818 else if (selectedComponent instanceof JTree) {
1819 TreePath path = ((JTree)selectedComponent).getSelectionPath();
1820 StringSelection data = new StringSelection(path.getLastPathComponent().toString());
1821 Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
1822 cb.setContents(data, data);
1823 }
1824 else if (selectedComponent instanceof JList) {
1825 StringSelection path = new StringSelection(buildSelectPath((JList)selectedComponent));
1826 Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
1827 cb.setContents(path, path);
1828 }
1829 }//GEN-LAST:event_copyActionPerformed
1830
1831 private void cutActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cutActionPerformed
1832 if (selectedComponent == null)
1833 return;
1834
1835 if (selectedComponent instanceof JTextComponent)
1836 ((JTextComponent)selectedComponent).cut();
1837 else if (selectedComponent instanceof JList) {
1838 StringSelection path = new StringSelection(buildSelectPath((JList)selectedComponent));
1839 Clipboard cb = Toolkit.getDefaultToolkit().getSystemClipboard();
1840 cb.setContents(path, path);
1841 if (selectedComponent == jarFileList)
1842 removeJarButtonActionPerformed(evt);
1843 else if (selectedComponent == sourceDirList)
1844 removeSrcDirButtonActionPerformed(evt);
1845 else if (selectedComponent == classpathEntryList)
1846 removeClasspathEntryButtonActionPerformed(evt);
1847 }
1848 }//GEN-LAST:event_cutActionPerformed
1849
1850 private void focusGainedHandler(java.awt.event.FocusEvent evt) {//GEN-FIRST:event_focusGainedHandler
1851 Component old = evt.getOppositeComponent();
1852 if (old instanceof JList)
1853 ((JList) old).clearSelection();
1854 selectedComponent = evt.getComponent();
1855 ableEditMenu();
1856 }//GEN-LAST:event_focusGainedHandler
1857
1858 private void classpathUpButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_classpathUpButtonActionPerformed
1859 if (moveEntriesUp(classpathEntryList))
1860 resyncAuxClasspathEntries();
1861 }//GEN-LAST:event_classpathUpButtonActionPerformed
1862
1863 private void sourceDownButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sourceDownButtonActionPerformed
1864 if (moveEntriesDown(sourceDirList))
1865 resyncSourceEntries();
1866 }//GEN-LAST:event_sourceDownButtonActionPerformed
1867
1868 private void sourceUpButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_sourceUpButtonActionPerformed
1869 if (moveEntriesUp(sourceDirList))
1870 resyncSourceEntries();
1871 }//GEN-LAST:event_sourceUpButtonActionPerformed
1872
1873 private void classpathDownButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_classpathDownButtonActionPerformed
1874 if (moveEntriesDown(classpathEntryList))
1875 resyncAuxClasspathEntries();
1876 }//GEN-LAST:event_classpathDownButtonActionPerformed
1877
1878 private void viewBugsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_viewBugsItemActionPerformed
1879 setView("BugTree");
1880 }//GEN-LAST:event_viewBugsItemActionPerformed
1881
1882 private void viewProjectItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_viewProjectItemActionPerformed
1883 setView("EditProjectPanel");
1884 }//GEN-LAST:event_viewProjectItemActionPerformed
1885
1886 private void highPriorityButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_highPriorityButtonActionPerformed
1887 setPriorityThreshold(Detector.HIGH_PRIORITY);
1888 }//GEN-LAST:event_highPriorityButtonActionPerformed
1889
1890 private void mediumPriorityButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_mediumPriorityButtonActionPerformed
1891 setPriorityThreshold(Detector.NORMAL_PRIORITY);
1892 }//GEN-LAST:event_mediumPriorityButtonActionPerformed
1893
1894 private void lowPriorityButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_lowPriorityButtonActionPerformed
1895 setPriorityThreshold(Detector.LOW_PRIORITY);
1896 }//GEN-LAST:event_lowPriorityButtonActionPerformed
1897
1898 private void expPriorityButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_expPriorityButtonActionPerformed
1899 setPriorityThreshold(Detector.EXP_PRIORITY);
1900 }//GEN-LAST:event_expPriorityButtonActionPerformed
1901
1902 private void saveBugsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveBugsItemActionPerformed
1903
1904 try {
1905 if (currentAnalysisRun == null) {
1906 logger.logMessage(Logger.ERROR, "No bugs are loaded!");
1907 return;
1908 }
1909
1910 JFileChooser chooser = createFileChooser();
1911 chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
1912 chooser.setFileFilter(xmlFileFilter);
1913
1914 int result = chooseFile(chooser, L10N.getLocalString("dlg.savebugs_ttl", "Save Bugs"));
1915
1916 if (result != JFileChooser.CANCEL_OPTION) {
1917 // Make sure current annotation text is up to date with its
1918 // corresponding bug instance
1919 if (currentBugInstance != null)
1920 synchBugAnnotation(currentBugInstance);
1921
1922 // Save bugs to file
1923 File selectedFile = chooser.getSelectedFile();
1924 currentAnalysisRun.saveBugsToFile(selectedFile);
1925 }
1926 } catch (Exception e) {
1927 if (FindBugs.DEBUG) {
1928 e.printStackTrace();
1929 }
1930 logger.logMessage(Logger.ERROR, "Could not save bugs: " + e.toString());
1931 }
1932 }//GEN-LAST:event_saveBugsItemActionPerformed
1933
1934 private void loadBugsFromFile(File file) throws IOException, DocumentException {
1935 File selectedFile = file;
1936
1937 Project project = new Project();
1938 AnalysisRun analysisRun = new AnalysisRun(project, this);
1939
1940 analysisRun.loadBugsFromFile(selectedFile);
1941
1942 project.setProjectFileName(file.getName()); // otherwise frame will show "<<unnamed project>>"
1943 setProject(project);
1944 synchAnalysisRun(analysisRun);
1945 }
1946
1947 private void loadBugsFromURL(String urlspec) throws IOException, DocumentException {
1948 URL url = new URL(urlspec);
1949 InputStream in = url.openStream();
1950
1951 Project project = new Project();
1952 AnalysisRun analysisRun = new AnalysisRun(project, this);
1953
1954 analysisRun.loadBugsFromInputStream(in);
1955
1956 setProject(project);
1957 synchAnalysisRun(analysisRun);
1958 }
1959
1960 private void loadBugsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loadBugsItemActionPerformed
1961 // FIXME: offer to save current project and bugs
1962
1963 try {
1964
1965 JFileChooser chooser = createFileChooser();
1966 chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
1967 chooser.setFileFilter(xmlFileFilter);
1968
1969 int result = chooseFile(chooser, L10N.getLocalString("dlg.loadbugs_ttl", "Load Bugs..."));
1970
1971 if (result != JFileChooser.CANCEL_OPTION) {
1972 loadBugsFromFile(chooser.getSelectedFile());
1973 }
1974 } catch (Exception e) {
1975 if (FindBugs.DEBUG) {
1976 e.printStackTrace();
1977 }
1978 logger.logMessage(Logger.ERROR, "Could not load bugs: " + e.toString());
1979 }
1980
1981 }//GEN-LAST:event_loadBugsItemActionPerformed
1982
1983 private void configureDetectorsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_configureDetectorsItemActionPerformed
1984 ConfigureDetectorsDialog dialog = new ConfigureDetectorsDialog(this, true);
1985 dialog.setSize(700, 520);
1986 dialog.setLocationRelativeTo(null); // center the dialog
1987 dialog.setVisible(true);
1988 }//GEN-LAST:event_configureDetectorsItemActionPerformed
1989
1990 private void reloadProjectItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_reloadProjectItemActionPerformed
1991 Project current = getCurrentProject();
1992
1993 if (current == null)
1994 return;
1995
1996 try {
1997 String filename = current.getProjectFileName();
1998 Project project = new Project();
1999 project.read(filename);
2000 setProject(null);
2001 setProject(project);
2002 findBugsButtonActionPerformed(evt);
2003 } catch (IOException e) {
2004 logger.logMessage(Logger.ERROR, "Could not reload project: " + e.getMessage());
2005 }
2006
2007 }//GEN-LAST:event_reloadProjectItemActionPerformed
2008
2009 private void saveProjectAsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveProjectAsItemActionPerformed
2010 saveProject(getCurrentProject(), L10N.getLocalString("dlg.saveprojectas_ttl", "Save Project As..."), true);
2011 }//GEN-LAST:event_saveProjectAsItemActionPerformed
2012
2013 private void viewMenuMenuSelected(javax.swing.event.MenuEvent evt) {//GEN-FIRST:event_viewMenuMenuSelected
2014 // View bug details and full descriptions items,
2015 // are only enabled if there is a project open.
2016 boolean hasProject = getCurrentProject() != null;
2017 viewBugDetailsItem.setEnabled(hasProject);
2018 fullDescriptionsItem.setEnabled(hasProject);
2019 }//GEN-LAST:event_viewMenuMenuSelected
2020
2021 private void fileMenuMenuSelected(javax.swing.event.MenuEvent evt) {//GEN-FIRST:event_fileMenuMenuSelected
2022 // Save and close project items are only enabled if there is a project open.
2023 boolean hasProject = getCurrentProject() != null;
2024 saveProjectItem.setEnabled(hasProject);
2025 saveProjectAsItem.setEnabled(hasProject);
2026 reloadProjectItem.setEnabled(hasProject && !getCurrentProject().getProjectFileName().equals(Project.UNNAMED_PROJECT));
2027 closeProjectItem.setEnabled(hasProject);
2028
2029 // Save bugs is only enabled if there is a current analysis run
2030 boolean hasAnalysisRun = currentAnalysisRun != null;
2031 saveBugsItem.setEnabled(hasAnalysisRun);
2032
2033 }//GEN-LAST:event_fileMenuMenuSelected
2034
2035 private void closeProjectItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeProjectItemActionPerformed
2036 if (closeProjectHook(getCurrentProject(), L10N.getLocalString("dlg.closeproject_lbl", "Close Project"))) {
2037 setProject(null);
2038 }
2039 }//GEN-LAST:event_closeProjectItemActionPerformed
2040
2041 private void removeClasspathEntryButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeClasspathEntryButtonActionPerformed
2042 Project project = getCurrentProject();
2043 DefaultListModel listModel = (DefaultListModel) classpathEntryList.getModel();
2044
2045 int[] selIndices = classpathEntryList.getSelectedIndices();
2046 for (int i = selIndices.length - 1; i >= 0; i--) {
2047 int sel = selIndices[i];
2048 project.removeAuxClasspathEntry(sel);
2049 listModel.remove(sel);
2050 }
2051 }//GEN-LAST:event_removeClasspathEntryButtonActionPerformed
2052
2053 private void addClasspathEntryButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addClasspathEntryButtonActionPerformed
2054 addClasspathEntryToList();
2055 }//GEN-LAST:event_addClasspathEntryButtonActionPerformed
2056
2057 private void browseClasspathEntryButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseClasspathEntryButtonActionPerformed
2058 JFileChooser chooser = createFileChooser();
2059 chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
2060 chooser.setFileFilter(auxClasspathEntryFileFilter);
2061 chooser.setMultiSelectionEnabled(true);
2062
2063 int result = chooseFile(chooser, "Add Entry");
2064
2065 if (result != JFileChooser.CANCEL_OPTION) {
2066 File[] selectedFileList = chooser.getSelectedFiles();
2067 for (int i = 0; i < selectedFileList.length; ++i) {
2068 selectedFileList[i] = verifyFileSelection(selectedFileList[i]);
2069 String entry = selectedFileList[i].getPath();
2070 addClasspathEntryToProject(entry);
2071 }
2072 }
2073 }//GEN-LAST:event_browseClasspathEntryButtonActionPerformed
2074
2075 private void fullDescriptionsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_fullDescriptionsItemActionPerformed
2076 for (JTree bugTree : bugTreeList) {
2077 // Redisplay the displayed bug instance nodes
2078 DefaultTreeModel bugTreeModel = (DefaultTreeModel) bugTree.getModel();
2079 int numRows = bugTree.getRowCount();
2080
2081 for (int i = 0; i < numRows; ++i) {
2082 //System.out.println("Getting path for row " + i);
2083 TreePath path = bugTree.getPathForRow(i);
2084 if (path == null)
2085 continue;
2086 DefaultMutableTreeNode node = (DefaultMutableTreeNode) path.getLastPathComponent();
2087 if (node instanceof BugTreeNode)
2088 bugTreeModel.valueForPathChanged(path, node.getUserObject());
2089 }
2090 }
2091 }//GEN-LAST:event_fullDescriptionsItemActionPerformed
2092
2093 private void viewBugDetailsItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_viewBugDetailsItemActionPerformed
2094 String view = getView();
2095 if (view.equals("BugTree")) {
2096 checkBugDetailsVisibility();
2097 }
2098
2099 }//GEN-LAST:event_viewBugDetailsItemActionPerformed
2100
2101 private void bugTreeBugDetailsSplitterPropertyChange(java.beans.PropertyChangeEvent evt) {//GEN-FIRST:event_bugTreeBugDetailsSplitterPropertyChange
2102 // Here we want to
2103 // (1) Keep the View:Bug details checkbox item up to date, and
2104 // (2) keep the details window synchronized with the current bug instance
2105 String propertyName = evt.getPropertyName();
2106 if (propertyName.equals(JSplitPane.DIVIDER_LOCATION_PROPERTY)) {
2107 boolean isMaximized = isSplitterMaximized(bugTreeBugDetailsSplitter, evt);
2108 viewBugDetailsItem.setSelected(!isMaximized);
2109 if (!isMaximized) {
2110 // Details window is shown, so make sure it is populated
2111 // with bug detail information
2112 synchBugInstance();
2113 }
2114 }
2115 }//GEN-LAST:event_bugTreeBugDetailsSplitterPropertyChange
2116
2117 private void openProjectItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_openProjectItemActionPerformed
2118
2119 if (!closeProjectHook(getCurrentProject(), L10N.getLocalString("msg.openproject_txt", "Open Project")))
2120 return;
2121
2122 JFileChooser chooser = createFileChooser();
2123 chooser.setFileFilter(projectFileFilter);
2124 int result = chooseFileToOpen(chooser);
2125
2126 if (result == JFileChooser.CANCEL_OPTION)
2127 return;
2128 try {
2129 File file = chooser.getSelectedFile();
2130 Project project = new Project();
2131 project.read(file.getPath());
2132 setProject(project);
2133 UserPreferences.getUserPreferences().useProject(file.getPath());
2134 rebuildRecentProjectsMenu();
2135
2136 } catch (IOException e) {
2137 logger.logMessage(Logger.ERROR, MessageFormat.format( L10N.getLocalString("msg.couldnotopenproject_txt", "Could not open project: {0}"), new Object[]{e.getMessage()}));
2138 }
2139 }//GEN-LAST:event_openProjectItemActionPerformed
2140
2141 private void saveProjectItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveProjectItemActionPerformed
2142 saveProject(getCurrentProject(), L10N.getLocalString("msg.saveproject_txt", "Save Project"));
2143 }//GEN-LAST:event_saveProjectItemActionPerformed
2144
2145 private void aboutItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_aboutItemActionPerformed
2146 about();
2147 }//GEN-LAST:event_aboutItemActionPerformed
2148
2149 private void findBugsButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_findBugsButtonActionPerformed
2150 Project project = getCurrentProject();
2151
2152 if (project.getFileCount() == 0) {
2153 logger.logMessage(Logger.ERROR, MessageFormat.format(L10N.getLocalString("msg.projectnojars_txt", "Project {0} has no Jar files selected"), new Object[]{project}));
2154 return;
2155 }
2156
2157 bugDescriptionEditorPane.setText("");
2158 currentBugDetailsKey = null;
2159 sourceTextArea.setText("");
2160 AnalysisRun analysisRun = new AnalysisRun(project, this);
2161
2162 logger.logMessage(Logger.INFO, MessageFormat.format(L10N.getLocalString("msg.beginninganalysis_txt", "Beginning analysis of {0}"), new Object[]{project}));
2163
2164 // Run the analysis!
2165 RunAnalysisDialog dialog = new RunAnalysisDialog(this, analysisRun, analysisPriority);
2166 dialog.setSize(400, 300);
2167 dialog.setLocationRelativeTo(null); // center the dialog
2168 dialog.setVisible(true);
2169
2170 if (dialog.isCompleted()) {
2171 logger.logMessage(Logger.INFO, MessageFormat.format(L10N.getLocalString("msg.analysiscompleted_txt", "Analysis {0} completed"), new Object[]{project}));
2172
2173 // Report any errors that might have occurred during analysis
2174 analysisRun.reportAnalysisErrors();
2175
2176 // Now we have an analysis run to look at
2177 synchAnalysisRun(analysisRun);
2178 } else {
2179 if (dialog.exceptionOccurred()) {
2180 // The analysis was killed by an unexpected exception
2181 Exception e = dialog.getException();
2182 AnalysisErrorDialog err = new AnalysisErrorDialog(this, true, null);
2183 err.addLine(MessageFormat.format(L10N.getLocalString("msg.fatalanalysisexception_txt", "Fatal analysis exception: {0}"), new Object[]{e.toString()}));
2184 StackTraceElement[] callList = e.getStackTrace();
2185 for (StackTraceElement aCallList : callList)
2186 err.addLine("\t" + aCallList);
2187 err.finish();
2188 err.setSize(650, 500);
2189 err.setLocationRelativeTo(null); // center the dialog
2190 err.setVisible(true);
2191 } else {
2192 // Cancelled by user
2193 logger.logMessage(Logger.INFO, MessageFormat.format(L10N.getLocalString("msg.analysiscancelled_txt", "Analysis of {0} cancelled by user"), new Object[]{project}));
2194 }
2195 }
2196 }//GEN-LAST:event_findBugsButtonActionPerformed
2197
2198 private void browseSrcDirButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseSrcDirButtonActionPerformed
2199 JFileChooser chooser = createFileChooser();
2200 chooser.setFileFilter(archiveAndDirectoryFilter);
2201 chooser.setMultiSelectionEnabled(true);
2202 chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
2203
2204 int rc = chooseFile(chooser, L10N.getLocalString("msg_addsource_lbl", "Add source directory or archive"));
2205 if (rc == JFileChooser.APPROVE_OPTION) {
2206 File[] selectedFileList = chooser.getSelectedFiles();
2207 for (int i = 0; i < selectedFileList.length; ++i) {
2208 selectedFileList[i] = verifyFileSelection(selectedFileList[i]);
2209 String entry = selectedFileList[i].getPath();
2210 addSrcToProject(entry);
2211 }
2212 }
2213 }//GEN-LAST:event_browseSrcDirButtonActionPerformed
2214
2215 private void srcDirTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_srcDirTextFieldActionPerformed
2216 addSourceDirToList();
2217 }//GEN-LAST:event_srcDirTextFieldActionPerformed
2218
2219 private void jarNameTextFieldActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_jarNameTextFieldActionPerformed
2220 addJarToList();
2221 }//GEN-LAST:event_jarNameTextFieldActionPerformed
2222
2223 private void browseJarButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_browseJarButtonActionPerformed
2224 JFileChooser chooser = createFileChooser();
2225 chooser.setFileFilter(archiveAndDirectoryFilter);
2226 chooser.setMultiSelectionEnabled(true);
2227 chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
2228
2229 int rc = chooseFile(chooser, L10N.getLocalString("msg.addarchiveordirectory_txt", "Add archive or directory"));
2230 if (rc == JFileChooser.APPROVE_OPTION) {
2231 File[] selectedFileList = chooser.getSelectedFiles();
2232 for (int i = 0; i < selectedFileList.length; ++i) {
2233 selectedFileList[i] = verifyFileSelection(selectedFileList[i]);
2234 String entry = selectedFileList[i].getPath();
2235 addJarToProject(entry);
2236 }
2237 }
2238 }//GEN-LAST:event_browseJarButtonActionPerformed
2239
2240 private void newProjectItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_newProjectItemActionPerformed
2241
2242 if (!closeProjectHook(getCurrentProject(), L10N.getLocalString("msg.newproject_txt", "New Project")))
2243 return;
2244
2245 Project project = new Project();
2246 setProject(project);
2247 }//GEN-LAST:event_newProjectItemActionPerformed
2248
2249 private void exitItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_exitItemActionPerformed
2250 exitFindBugs();
2251 }//GEN-LAST:event_exitItemActionPerformed
2252
2253 private void removeSrcDirButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeSrcDirButtonActionPerformed
2254 Project project = getCurrentProject();
2255 DefaultListModel listModel = (DefaultListModel) sourceDirList.getModel();
2256
2257 int[] selIndices = sourceDirList.getSelectedIndices();
2258 for (int i = selIndices.length - 1; i >= 0; i--) {
2259 int sel = selIndices[i];
2260 project.removeSourceDir(sel);
2261 listModel.remove(sel);
2262 }
2263 }//GEN-LAST:event_removeSrcDirButtonActionPerformed
2264
2265 private void removeJarButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_removeJarButtonActionPerformed
2266 Project project = getCurrentProject();
2267 DefaultListModel listModel = (DefaultListModel) jarFileList.getModel();
2268
2269 int[] selIndices = jarFileList.getSelectedIndices();
2270 for (int i = selIndices.length - 1; i >= 0; i--) {
2271 int sel = selIndices[i];
2272 project.removeFile(sel);
2273 listModel.remove(sel);
2274 }
2275 }//GEN-LAST:event_removeJarButtonActionPerformed
2276
2277 private void addSourceDirButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addSourceDirButtonActionPerformed
2278 addSourceDirToList();
2279 }//GEN-LAST:event_addSourceDirButtonActionPerformed
2280
2281 private void addJarButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_addJarButtonActionPerformed
2282 addJarToList();
2283 }//GEN-LAST:event_addJarButtonActionPerformed
2284
2285 /**
2286 * Exit the Application
2287 */
2288 private void exitForm(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_exitForm
2289 exitFindBugs();
2290 }//GEN-LAST:event_exitForm
2291
2292 /**
2293 * This makes the set of controls passed in all the same size, equal to
2294 * the minimum needed of the largest control.
2295 */
2296 private void equalizeControls(JComponent[] components) {
2297 Dimension d;
2298
2299 int minX = 0, minY = 0;
2300 for (JComponent comp : components) {
2301 comp.setMaximumSize(null);
2302 comp.setMinimumSize(null);
2303 comp.setPreferredSize(null);
2304 d = comp.getPreferredSize();
2305 if (d.width > minX)
2306 minX = d.width;
2307 if (d.height > minY)
2308 minY = d.height;
2309 }
2310
2311 d = new Dimension(minX, minY);
2312 for (JComponent comp : components) {
2313 comp.setMinimumSize(d);
2314 comp.setMaximumSize(d);
2315 comp.setPreferredSize(d);
2316 }
2317 }
2318
2319 /**
2320 * This is called whenever the selection is changed in the bug tree.
2321 *
2322 * @param e the TreeSelectionEvent
2323 */
2324 private void bugTreeSelectionChanged(TreeSelectionEvent e) {
2325
2326 BugInstance selected = getCurrentBugInstance();
2327 if (selected != null) {
2328 synchBugInstance();
2329 }
2330 }
2331
2332 private void openRecentProjectItemActionPerformed(java.awt.event.ActionEvent evt) {
2333 if (!closeProjectHook(getCurrentProject(), L10N.getLocalString("msg.openproject_txt", "Open Project")))
2334 return;
2335
2336 JMenuItem recentProjectItem = (JMenuItem) evt.getSource();
2337 File file = new File(recentProjectItem.getText());
2338 try {
2339 System.setProperty("user.dir", file.getParent());
2340 Project project = new Project();
2341 project.read(file.getPath());
2342 setProject(project);
2343 UserPreferences.getUserPreferences().useProject(file.getPath());
2344 } catch (IOException e) {
2345 UserPreferences.getUserPreferences().removeProject(file.getPath());
2346 logger.logMessage(Logger.ERROR, MessageFormat.format(L10N.getLocalString("msg.couldnotopenproject_txt", "Could not open project: {0}"), new Object[]{e.getMessage()}));
2347 } finally {
2348 rebuildRecentProjectsMenu();
2349 }
2350 }
2351
2352 private boolean moveEntriesUp(JList entryList) {
2353 int[] selIndices = entryList.getSelectedIndices();
2354 if (selIndices.length == 0)
2355 return false;
2356
2357 boolean changed = false;
2358 int lastInsertPos = -1;
2359 DefaultListModel model = (DefaultListModel) entryList.getModel();
2360 for (int i = 0; i < selIndices.length; i++) {
2361 int sel = selIndices[i];
2362 if ((sel - 1) > lastInsertPos) {
2363 model.add(sel - 1, model.remove(sel));
2364 selIndices[i] = sel - 1;
2365 changed = true;
2366 }
2367 lastInsertPos = selIndices[i];
2368 }
2369
2370 entryList.setSelectedIndices(selIndices);
2371 return changed;
2372 }
2373
2374 private boolean moveEntriesDown(JList entryList) {
2375 int[] selIndices = entryList.getSelectedIndices();
2376 if (selIndices.length == 0)
2377 return false;
2378
2379 boolean changed = false;
2380 DefaultListModel model = (DefaultListModel) entryList.getModel();
2381 int lastInsertPos = model.getSize();
2382 for (int i = selIndices.length - 1; i >= 0; i--) {
2383 int sel = selIndices[i];
2384 if ((sel + 1) < lastInsertPos) {
2385 model.add(sel + 1, model.remove(sel));
2386 selIndices[i] = sel + 1;
2387 changed = true;
2388 }
2389 lastInsertPos = selIndices[i];
2390 }
2391
2392 entryList.setSelectedIndices(selIndices);
2393 return changed;
2394 }
2395
2396 private void resyncAuxClasspathEntries() {
2397 Project project = getCurrentProject();
2398 int numEntries = project.getNumAuxClasspathEntries();
2399 while (numEntries-- > 0)
2400 project.removeAuxClasspathEntry(0);
2401
2402 DefaultListModel model = (DefaultListModel) classpathEntryList.getModel();
2403 for (int i = 0; i < model.size(); i++)
2404 project.addAuxClasspathEntry((String) model.get(i));
2405 }
2406
2407 private void resyncSourceEntries() {
2408 Project project = getCurrentProject();
2409 int numEntries = project.getNumSourceDirs();
2410 while (numEntries-- > 0)
2411 project.removeSourceDir(0);
2412
2413 DefaultListModel model = (DefaultListModel) sourceDirList.getModel();
2414 for (int i = 0; i < model.size(); i++)
2415 project.addSourceDir((String) model.get(i));
2416 }
2417
2418 /**
2419 * Localise the given AbstractButton, setting the text and optionally mnemonic
2420 * Note that AbstractButton includes menus and menu items.
2421 * @param button The button to localise
2422 * @param key The key to look up in resource bundle
2423 * @param defaultString default String to use if key not found
2424 * @param setMnemonic whether or not to set the mnemonic. According to Sun's
2425 * guidelines, default/cancel buttons should not have mnemonics
2426 * but instead should use Return/Escape
2427 */
2428 private void localiseButton(AbstractButton button, String key, String defaultString,
2429 boolean setMnemonic) {
2430 AnnotatedString.localiseButton(button, key, defaultString, setMnemonic);
2431 }
2432
2433 /* ----------------------------------------------------------------------
2434 * Component initialization support
2435 * ---------------------------------------------------------------------- */
2436
2437 /**
2438 * This is called from the constructor to perform post-initialization
2439 * of the components in the form.
2440 */
2441 private void postInitComponents() {
2442 logger = new ConsoleLogger(this);
2443
2444 // Add menu items for bug categories to View->Filter Settings menu.
2445 // These are automatically localized assuming that a
2446 // BugCategoryDescriptions_<locale>.properties file exists
2447 // in edu.umd.cs.findbugs.
2448 Collection<String> bugCategoryCollection = edu.umd.cs.findbugs.I18N.instance().getBugCategories();
2449 this.bugCategoryCheckBoxList = new JCheckBoxMenuItem[bugCategoryCollection.size()];
2450 this.bugCategoryList = new String[bugCategoryCollection.size()];
2451 int count = 0;
2452 for (String bugCategory : bugCategoryCollection) {
2453 String bugCategoryDescription = I18N.instance().getBugCategoryDescription(bugCategory);
2454
2455 final JCheckBoxMenuItem item = new JCheckBoxMenuItem(bugCategoryDescription, true);
2456 item.setFont(BUTTON_FONT);
2457 item.setSelected(getFilterSettings().containsCategory(bugCategory));
2458 item.addActionListener(new ActionListener() {
2459 public void actionPerformed(ActionEvent evt) {
2460 toggleBugCategory(item);
2461 }
2462 });
2463
2464 filterWarningsMenu.add(item);
2465
2466 this.bugCategoryCheckBoxList[count] = item;
2467 this.bugCategoryList[count] = bugCategory;
2468
2469 ++count;
2470 }
2471
2472 viewPanelLayout = (CardLayout) viewPanel.getLayout();
2473
2474 // List of bug group tabs.
2475 // This must be in the same order as GROUP_BY_ORDER_LIST!
2476 bugTreeList = new JTree[]{byClassBugTree, byPackageBugTree, byBugTypeBugTree, byBugCategoryBugTree};
2477
2478 // Configure bug trees
2479 for (JTree bugTree : bugTreeList) {
2480 bugTree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
2481 bugTree.setCellRenderer(BugCellRenderer.instance());
2482 bugTree.setRootVisible(false);
2483 bugTree.setShowsRootHandles(true);
2484 bugTree.addTreeSelectionListener(new TreeSelectionListener() {
2485 public void valueChanged(TreeSelectionEvent e) {
2486 bugTreeSelectionChanged(e);
2487 }
2488 });
2489 }
2490
2491 jarFileList.setModel(new DefaultListModel());
2492 sourceDirList.setModel(new DefaultListModel());
2493 classpathEntryList.setModel(new DefaultListModel());
2494
2495 // We use a special highlight painter to ensure that the highlights cover
2496 // complete source lines, even though the source text doesn't
2497 // fill the lines completely.
2498 final Highlighter.HighlightPainter painter =
2499 new DefaultHighlighter.DefaultHighlightPainter(sourceTextArea.getSelectionColor()) {
2500 @Override
2501 public Shape paintLayer(Graphics g, int offs0, int offs1,
2502 Shape bounds, JTextComponent c, View view) {
2503 try {
2504 Shape extent = view.modelToView(offs0, Position.Bias.Forward, offs1, Position.Bias.Backward, bounds);
2505 Rectangle rect = extent.getBounds();
2506 rect.x = 0;
2507 rect.width = bounds.getBounds().width;
2508 g.setColor(getColor());
2509 g.fillRect(rect.x, rect.y, rect.width, rect.height);
2510 return rect;
2511 } catch (BadLocationException e) {
2512 return null;
2513 }
2514 }
2515 };
2516 Highlighter sourceHighlighter = new DefaultHighlighter() {
2517 @Override
2518 public Object addHighlight(int p0, int p1, Highlighter.HighlightPainter p)
2519 throws BadLocationException {
2520 return super.addHighlight(p0, p1, painter);
2521 }
2522 };
2523 sourceTextArea.setHighlighter(sourceHighlighter);
2524
2525 updateTitle(getCurrentProject());
2526
2527 // Load the icon for the UMD logo
2528 ClassLoader classLoader = this.getClass().getClassLoader();
2529 ImageIcon logoIcon = new ImageIcon(classLoader.getResource("edu/umd/cs/findbugs/gui/logo_umd.png"));
2530 logoLabel.setIcon(logoIcon);
2531
2532 // Set common Menu Accelerators
2533 final int MENU_MASK = getMenuMask();
2534 newProjectItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, MENU_MASK));
2535 openProjectItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, MENU_MASK));
2536 saveProjectItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, MENU_MASK));
2537 closeProjectItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_W, MENU_MASK));
2538 reloadProjectItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R, MENU_MASK));
2539
2540 cutItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, MENU_MASK));
2541 copyItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, MENU_MASK));
2542 pasteItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, MENU_MASK));
2543 selectAllItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A, MENU_MASK));
2544
2545 if (MAC_OS_X) {
2546 // Some more accelerators that use modifiers. Other platforms
2547 // tend not to use modifiers for menu accelerators
2548 saveProjectAsItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, MENU_MASK | Event.SHIFT_MASK));
2549 loadBugsItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_O, MENU_MASK | Event.ALT_MASK));
2550 saveBugsItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S, MENU_MASK | Event.ALT_MASK));
2551
2552 // Leave room for the growBox on Mac
2553 growBoxSpacer.setMinimumSize(new java.awt.Dimension(16,16));
2554
2555 // Remove Unnecessary/Redundant menu items.
2556 fileMenu.remove(exitItem);
2557 fileMenu.remove(jSeparator6);
2558 theMenuBar.remove(helpMenu);
2559
2560 // Set up listeners for Quit and About menu items using
2561 // Apple's EAWT API.
2562 // We use reflection here, so there is no posible chance that the
2563 // class loader will try to load OSXAdapter on a non Mac system
2564 try {
2565 Class<?> osxAdapter = Class.forName("edu.umd.cs.findbugs.gui.OSXAdapter");
2566 Class<?>[] defArgs = {FindBugsFrame.class};
2567 Method registerMethod = osxAdapter.getDeclaredMethod("registerMacOSXApplication", defArgs);
2568 if (registerMethod != null) {
2569 Object[] args = {this};
2570 registerMethod.invoke(osxAdapter, args);
2571 }
2572 } catch (NoClassDefFoundError e) {
2573 // This will be thrown first if the OSXAdapter is loaded on a system without the EAWT
2574 // because OSXAdapter extends ApplicationAdapter in its def
2575 System.err.println("This version of Mac OS X does not support the Apple EAWT. Application Menu handling has been disabled (" + e + ")");
2576 } catch (ClassNotFoundException e) {
2577 // This shouldn't be reached; if there's a problem with the OSXAdapter we should get the
2578 // above NoClassDefFoundError first.
2579 System.err.println("This version of Mac OS X does not support the Apple EAWT. Application Menu handling has been disabled (" + e + ")");
2580 } catch (Exception e) {
2581 System.err.println("Exception while loading the OSXAdapter: " + e);
2582 if (FindBugs.DEBUG) {
2583 e.printStackTrace();
2584 }
2585 }
2586 }
2587
2588 }
2589
2590 private void rebuildRecentProjectsMenu() {
2591 UserPreferences prefs = UserPreferences.getUserPreferences();
2592 final List<String> recentProjects = prefs.getRecentProjects();
2593 SwingUtilities.invokeLater(new Runnable() {
2594 public void run() {
2595 recentProjectsMenu.removeAll();
2596 java.awt.Font ft = BUTTON_FONT;
2597 if (recentProjects.size() == 0) {
2598 JMenuItem emptyItem = new JMenuItem(L10N.getLocalString("menu.empty_item", "Empty"));
2599 emptyItem.setFont(ft);
2600 emptyItem.setEnabled(false);
2601 recentProjectsMenu.add(emptyItem);
2602 } else {
2603 for (String recentProject : recentProjects) {
2604 JMenuItem projectItem = new JMenuItem(recentProject);
2605 projectItem.setFont(ft);
2606 projectItem.addActionListener(new ActionListener() {
2607 public void actionPerformed(ActionEvent evt) {
2608 openRecentProjectItemActionPerformed(evt);
2609 }
2610 });
2611 recentProjectsMenu.add(projectItem);
2612 }
2613 }
2614 }
2615 });
2616 }
2617
2618 /* ----------------------------------------------------------------------
2619 * Helpers for accessing and modifying UI components
2620 * ---------------------------------------------------------------------- */
2621
2622 /**
2623 * Based on the current tree selection path, get a user object
2624 * whose class is the same as the given class.
2625 *
2626 * @param tree the tree
2627 * @param c the class
2628 * @return an instance of the given kind of object which is in the
2629 * current selection, or null if there is no matching object
2630 */
2631 private static <E> E getTreeSelectionOf(JTree tree, Class<E> c) {
2632 TreePath selPath = tree.getSelectionPath();
2633
2634 // There may not be anything selected at the moment
2635 if (selPath == null)
2636 return null;
2637
2638 // Work backwards from end until we get to the kind of
2639 // object we're looking for.
2640 Object[] nodeList = selPath.getPath();
2641 for (int i = nodeList.length - 1; i >= 0; --i) {
2642 DefaultMutableTreeNode node = (DefaultMutableTreeNode) nodeList[i];
2643 Object nodeInfo = node.getUserObject();
2644 if (nodeInfo != null && nodeInfo.getClass() == c)
2645 return c.cast(nodeInfo);
2646 }
2647 return null;
2648 }
2649
2650 /**
2651 * Get the current project.
2652 */
2653 private Project getCurrentProject() {
2654 return currentProject;
2655 }
2656
2657 /**
2658 * Get the current analysis run.
2659 */
2660 private AnalysisRun getCurrentAnalysisRun() {
2661 return currentAnalysisRun;
2662 }
2663
2664 /**
2665 * Get the bug instance currently selected in the bug tree.
2666 */
2667 private BugInstance getCurrentBugInstance() {
2668 JTree bugTree = getCurrentBugTree();
2669 if (bugTree != null) {
2670 return getTreeSelectionOf(bugTree, BugInstance.class);
2671 }
2672 return null;
2673 }
2674
2675 /**
2676 * Return whether or not the given splitter is "maximized", meaning that
2677 * the top window of the split has been given all of the space.
2678 * Note that this window assumes that the split is vertical (meaning
2679 * that we have top and bottom components).
2680 *
2681 * @param splitter the JSplitPane
2682 * @param evt the event that is changing the splitter value
2683 */
2684 private boolean isSplitterMaximized(JSplitPane splitter, java.beans.PropertyChangeEvent evt) {
2685 Integer location = (Integer) evt.getNewValue();
2686
2687 int height = splitter.getHeight();
2688 int hopefullyMaxDivider = height - (splitter.getDividerSize() + DIVIDER_FUDGE);
2689 //System.out.println("Splitter: "+(splitter==consoleSplitter?"consoleSplitter":"bugTreeBugDetailsSplitter")+
2690 // ": height="+height+",location="+location+
2691 // ",hopefullyMax="+hopefullyMaxDivider);
2692 boolean isMaximized = location.intValue() >= hopefullyMaxDivider;
2693 return isMaximized;
2694 }
2695
2696 private void checkBugDetailsVisibility() {
2697 if (viewBugDetailsItem.isSelected()) {
2698 bugTreeBugDetailsSplitter.resetToPreferredSizes();
2699 } else {
2700 bugTreeBugDetailsSplitter.setDividerLocation(1.0);
2701 }
2702 //System.out.("New bug detail splitter location " + bugTreeBugDetailsSplitter.getDividerLocation());
2703 }
2704
2705 private JTree getCurrentBugTree() {
2706 JScrollPane selected = (JScrollPane) groupByTabbedPane.getSelectedComponent();
2707 Object view = selected.getViewport().getView();
2708 if (view instanceof JTree) {
2709 return (JTree) view;
2710 }
2711 return null;
2712 }
2713
2714 /* ----------------------------------------------------------------------
2715 * Synchronization of data model and UI
2716 * ---------------------------------------------------------------------- */
2717
2718 /**
2719 * Set the priority threshold for display of bugs in the bug tree.
2720 *
2721 * @param threshold the threshold
2722 */
2723 private void setPriorityThreshold(int threshold) {
2724 if (threshold != getFilterSettings().getMinPriorityAsInt()) {
2725 getFilterSettings().setMinPriority(ProjectFilterSettings.getIntPriorityAsString(threshold));
2726 if (currentAnalysisRun != null)
2727 synchAnalysisRun(currentAnalysisRun);
2728 }
2729 }
2730
2731 private void ableEditMenu() {
2732 String view = getView();
2733 if ((view != null) && view.equals("EditProjectPanel")) {
2734 if (selectedComponent != null) {
2735 boolean hasSelection = false;
2736 if (selectedComponent instanceof JList) {
2737 JList list = (JList)selectedComponent;
2738 hasSelection = list.getSelectedIndices().length > 0;
2739 } else if (selectedComponent instanceof JTextField) {
2740 JTextField tf = (JTextField)selectedComponent;
2741 hasSelection = ((tf.getSelectedText() != null) &&
2742 (tf.getSelectedText().length() > 0));
2743 }
2744
2745 cutItem.setEnabled(hasSelection);
2746 copyItem.setEnabled(hasSelection);
2747 selectAllItem.setEnabled(true);
2748 }
2749 // } else if (view.equals("BugTree")) {
2750 // } else if (view.equals("ReportPanel")) {
2751
2752 } else {
2753 cutItem.setEnabled(false);
2754 copyItem.setEnabled(true);
2755 pasteItem.setEnabled(false);
2756 selectAllItem.setEnabled(false);
2757 }
2758 }
2759
2760 private void setProject(Project project) {
2761 currentProject = project;
2762 if (project != null) {
2763 synchProject(project);
2764 setView("EditProjectPanel");
2765 editMenu.setEnabled(true);
2766 viewProjectItem.setEnabled(true);
2767 viewProjectItem.setSelected(true);
2768 viewBugsItem.setEnabled(false);
2769 viewBugsItem.setSelected(false);
2770 } else {
2771 editMenu.setEnabled(false);
2772 viewProjectItem.setEnabled(false);
2773 viewProjectItem.setSelected(false);
2774 viewBugsItem.setEnabled(false);
2775 viewBugsItem.setSelected(false);
2776 setView("EmptyPanel");
2777 }
2778 updateTitle(project);
2779 ableEditMenu();
2780 }
2781
2782 private void updateTitle(Project project) {
2783 if (project == null)
2784 this.setTitle(L10N.getLocalString("dlg.noproject_lbl", "FindBugs - no project"));
2785 else
2786 this.setTitle("FindBugs - " + project.toString());
2787 }
2788
2789 /**
2790 * Save given project.
2791 * If the project already has a valid filename, use that filename.
2792 * Otherwise, prompt for one.
2793 *
2794 * @param project the Project to save
2795 * @param dialogTitle the title for the save dialog (if needed)
2796 */
2797 private boolean saveProject(Project project, String dialogTitle) {
2798 return saveProject(project, dialogTitle, false);
2799 }
2800
2801 /**
2802 * Offer to save the current Project to a file.
2803 *
2804 * @param project the Project to save
2805 * @param dialogTitle the title for the save dialog (if needed)
2806 * @param chooseFilename if true, force a dialog to prompt the user
2807 * for a filename
2808 * @return true if the project is saved successfully, false if the user
2809 * cancels or an error occurs
2810 */
2811 private boolean saveProject(Project project, String dialogTitle, boolean chooseFilename) {
2812 boolean useRelativePaths;
2813 try {
2814 if (project == null)
2815 return true;
2816
2817 File file;
2818 String fileName = project.getProjectFileName();
2819
2820 if (!fileName.startsWith("<") && !chooseFilename) {
2821 file = new File(fileName);
2822 useRelativePaths = project.getOption( Project.RELATIVE_PATHS );
2823 } else {
2824 JRadioButton relativePaths = new JRadioButton(L10N.getLocalString("msg.userelativepaths_txt", "Use Relative Paths"));
2825 relativePaths.setSelected(project.getOption(Project.RELATIVE_PATHS));
2826 JFileChooser chooser = createFileChooser(relativePaths);
2827 chooser.setFileFilter(projectFileFilter);
2828 int result = chooseFile(chooser, dialogTitle);
2829 if (result == JFileChooser.CANCEL_OPTION)
2830 return false;
2831 file = chooser.getSelectedFile();
2832 fileName = Project.transformFilename(file.getPath());
2833 file = new File(fileName);
2834 useRelativePaths = relativePaths.isSelected();
2835 }
2836
2837 project.write(file.getPath(), useRelativePaths, file.getParent());
2838 logger.logMessage(Logger.INFO, "Project saved");
2839 project.setProjectFileName(file.getPath());
2840
2841 UserPreferences prefs = UserPreferences.getUserPreferences();
2842 prefs.useProject(file.getPath());
2843 prefs.read();
2844 rebuildRecentProjectsMenu();
2845
2846 updateTitle(project);
2847
2848 return true;
2849 } catch (IOException e) {
2850 logger.logMessage(Logger.ERROR, "Could not save project: " + e.toString());
2851 JOptionPane.showMessageDialog(this, "Error saving project: " + e.toString(),
2852 "Error", JOptionPane.ERROR_MESSAGE);
2853 return false;
2854 }
2855 }
2856
2857 /**
2858 * Hook to call before closing a project.
2859 *
2860 * @param project the project being closed
2861 * @param savePromptTitle title to use for the "Save project?" dialog
2862 * @return true if user has confirmed that the project should be closed,
2863 * false if the close is cancelled
2864 */
2865 private boolean closeProjectHook(Project project, String savePromptTitle) {
2866 if (project == null || !project.isModified())
2867 return true;
2868
2869 // Confirm that the project should be closed.
2870 int option = JOptionPane.showConfirmDialog(this, L10N.getLocalString("msg.saveprojectquery_txt", "Save Project?"), savePromptTitle,
2871 JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
2872
2873 if (option == JOptionPane.CANCEL_OPTION)
2874 return false;
2875 else if (option == JOptionPane.YES_OPTION) {
2876 boolean result = saveProject(project, "Save Project");
2877 if (result)
2878 JOptionPane.showMessageDialog(this, "Project was successfully saved.");
2879 return result;
2880 } else
2881 return true;
2882 }
2883
2884 /**
2885 * Synchronize the edit project dialog with given project.
2886 *
2887 * @param project the selected project
2888 */
2889 private void synchProject(Project project) {
2890 // Clear text fields
2891 jarNameTextField.setText("");
2892 srcDirTextField.setText("");
2893 classpathEntryTextField.setText("");
2894
2895 // Populate jar file, source directory, and aux classpath entry lists
2896
2897 DefaultListModel jarListModel = (DefaultListModel) jarFileList.getModel();
2898 jarListModel.clear();
2899 for (int i = 0; i < project.getFileCount(); ++i) {
2900 jarListModel.addElement(project.getFile(i));
2901 }
2902
2903 DefaultListModel srcDirListModel = (DefaultListModel) sourceDirList.getModel();
2904 srcDirListModel.clear();
2905 for (int i = 0; i < project.getNumSourceDirs(); ++i) {
2906 srcDirListModel.addElement(project.getSourceDir(i));
2907 }
2908
2909 DefaultListModel classpathEntryListModel = (DefaultListModel) classpathEntryList.getModel();
2910 classpathEntryListModel.clear();
2911 for (int i = 0; i < project.getNumAuxClasspathEntries(); ++i) {
2912 classpathEntryListModel.addElement(project.getAuxClasspathEntry(i));
2913 }
2914 }
2915
2916 /**
2917 * Synchronize the bug trees with the given analysisRun object.
2918 *
2919 * @param analysisRun the selected analysis run
2920 */
2921 private void synchAnalysisRun(AnalysisRun analysisRun) {
2922 // Create and populate tree models
2923 for (int i = 0; i < GROUP_BY_ORDER_LIST.length; ++i) {
2924 DefaultMutableTreeNode bugRootNode = new DefaultMutableTreeNode();
2925 DefaultTreeModel bugTreeModel = new DefaultTreeModel(bugRootNode);
2926
2927 String groupByOrder = GROUP_BY_ORDER_LIST[i];
2928 analysisRun.setTreeModel(groupByOrder, bugTreeModel);
2929 populateAnalysisRunTreeModel(analysisRun, groupByOrder);
2930 if (i < bugTreeList.length)
2931 bugTreeList[i].setModel(bugTreeModel);
2932 }
2933
2934 currentAnalysisRun = analysisRun;
2935
2936 //set the summary output
2937 setSummary(analysisRun.getSummary());
2938 setView("BugTree");
2939 }
2940
2941 private void setSummary(String summaryXML) {
2942 bugSummaryEditorPane.setContentType("text/html");
2943 /*
2944 bugSummaryEditorPane.setText(summaryXML);
2945 // : unfortunately, using setText() on the editor pane
2946 // results in the contents being scrolled to the bottom of the pane.
2947 // An immediate inline call to set the scroll position does nothing.
2948 // So, use invokeLater(), even though this results in flashing.
2949 // [What we really need is a way to set the text WITHOUT changing
2950 // the caret position. Need to investigate.]
2951 SwingUtilities.invokeLater(new Runnable() {
2952 public void run() {
2953 bySummary.getViewport().setViewPosition(new Point(0, 0));
2954 }
2955 });
2956 */
2957 StringReader reader = null;
2958 try {
2959 if (summaryXML != null) {
2960 reader = new StringReader(summaryXML); // no need for BufferedReader
2961 bugSummaryEditorPane.read(reader, "html summary");
2962 } else {
2963 bugSummaryEditorPane.setText("");
2964 }
2965 } catch (IOException e) {
2966 bugSummaryEditorPane.setText("Could not set summary: " + e.getMessage());
2967 logger.logMessage(Logger.WARNING, e.getMessage());
2968 } finally {
2969 if (reader != null)
2970 reader.close(); // polite, but doesn't do much in StringReader
2971 }
2972
2973 }
2974
2975 /**
2976 * Populate an analysis run's tree model for given sort order.
2977 */
2978 private void populateAnalysisRunTreeModel(AnalysisRun analysisRun, final String groupBy) {
2979 //System.out.println("Populating bug tree for order " + groupBy);
2980
2981 // Set busy cursor - this is potentially a time-consuming operation
2982 Cursor orig = this.getCursor();
2983 this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
2984
2985 final DefaultTreeModel bugTreeModel = analysisRun.getTreeModel(groupBy);
2986 final DefaultMutableTreeNode bugRootNode = (DefaultMutableTreeNode) bugTreeModel.getRoot();
2987
2988 // Delete all children from root node
2989 bugRootNode.removeAllChildren();
2990
2991 // Sort the instances (considering only those that meet the
2992 // priority threshold)
2993 TreeSet<BugInstance> sortedCollection = new TreeSet<BugInstance>(getBugInstanceComparator(groupBy));
2994 for (BugInstance bugInstance : analysisRun.getBugInstances()) {
2995 if (getFilterSettings().displayWarning(bugInstance))
2996 sortedCollection.add(bugInstance);
2997 }
2998
2999 // The grouper callback is what actually adds the group and bug
3000 // nodes to the tree.
3001 Grouper.Callback<BugInstance> callback = new Grouper.Callback<BugInstance>() {
3002 private BugInstanceGroup currentGroup;
3003 private DefaultMutableTreeNode currentGroupNode;
3004
3005 public void startGroup(BugInstance member) {
3006 String groupName;
3007 if (groupBy == GROUP_BY_CLASS)
3008 groupName = member.getPrimaryClass().getClassName();
3009 else if (groupBy == GROUP_BY_PACKAGE) {
3010 groupName = member.getPrimaryClass().getPackageName();
3011 if (groupName.equals(""))
3012 groupName = "Unnamed package";
3013 } else if (groupBy == GROUP_BY_BUG_TYPE) {
3014 String desc = member.toString();
3015 String shortBugType = desc.substring(0, desc.indexOf(':'));
3016 String bugTypeDescription = I18N.instance().getBugTypeDescription(shortBugType);
3017 groupName = shortBugType + ": " + bugTypeDescription;
3018 } else if (groupBy == GROUP_BY_BUG_CATEGORY) {
3019 BugPattern pattern = member.getBugPattern();
3020 if (pattern == null) {
3021 if (FindBugs.DEBUG)
3022 System.out.println("Unknown bug pattern " + member.getType());
3023 groupName = "Unknown category";
3024 } else {
3025 groupName = I18N.instance().getBugCategoryDescription(pattern.getCategory());
3026 }
3027 } else
3028 throw new IllegalStateException("Unknown sort order: " + groupBy);
3029 currentGroup = new BugInstanceGroup(groupBy, groupName);
3030 currentGroupNode = new DefaultMutableTreeNode(currentGroup);
3031 bugTreeModel.insertNodeInto(currentGroupNode, bugRootNode, bugRootNode.getChildCount());
3032
3033 insertIntoGroup(member);
3034 }
3035
3036 public void addToGroup(BugInstance member) {
3037 insertIntoGroup(member);
3038 }
3039
3040 private void insertIntoGroup(BugInstance member) {
3041 int count = currentGroup.getMemberCount();
3042 currentGroup.incrementMemberCount();
3043 BugTreeNode bugNode = new BugTreeNode(member);
3044 if (BUG_COUNT)
3045 bugNode.setCount(count);
3046 bugTreeModel.insertNodeInto(bugNode, currentGroupNode, currentGroupNode.getChildCount());
3047
3048 // Insert annotations
3049 Iterator<BugAnnotation> j = member.annotationIterator();
3050 while (j.hasNext()) {
3051 BugAnnotation annotation = j.next();
3052 DefaultMutableTreeNode annotationNode = new DefaultMutableTreeNode(annotation);
3053 bugTreeModel.insertNodeInto(annotationNode, bugNode, bugNode.getChildCount());
3054 }
3055
3056 }
3057 };
3058
3059 // Create the grouper, and execute it to populate the bug tree
3060 Grouper<BugInstance> grouper = new Grouper<BugInstance>(callback);
3061 Comparator<BugInstance> groupComparator = getGroupComparator(groupBy);
3062 grouper.group(sortedCollection, groupComparator);
3063
3064 // Let the tree know it needs to update itself
3065 bugTreeModel.nodeStructureChanged(bugRootNode);
3066
3067 // Now we're done
3068 this.setCursor(orig);
3069 }
3070
3071 /**
3072 * Get a BugInstance Comparator for given sort order.
3073 */
3074 private Comparator<BugInstance> getBugInstanceComparator(String sortOrder) {
3075 if (sortOrder.equals(GROUP_BY_CLASS))
3076 return bugInstanceByClassComparator;
3077 else if (sortOrder.equals(GROUP_BY_PACKAGE))
3078 return bugInstanceByPackageComparator;
3079 else if (sortOrder.equals(GROUP_BY_BUG_TYPE))
3080 return bugInstanceByTypeComparator;
3081 else if (sortOrder.equals(GROUP_BY_BUG_CATEGORY))
3082 return bugInstanceByCategoryComparator;
3083 else
3084 throw new IllegalArgumentException("Bad sort order: " + sortOrder);
3085 }
3086
3087 /**
3088 * Get a Grouper for a given sort order.
3089 */
3090 private Comparator<BugInstance> getGroupComparator(String groupBy) {
3091 if (groupBy.equals(GROUP_BY_CLASS)) {
3092 return bugInstanceClassComparator;
3093 } else if (groupBy.equals(GROUP_BY_PACKAGE)) {
3094 return bugInstancePackageComparator;
3095 } else if (groupBy.equals(GROUP_BY_BUG_TYPE)) {
3096 return bugInstanceTypeComparator;
3097 } else if (groupBy.equals(GROUP_BY_BUG_CATEGORY)) {
3098 return bugInstanceCategoryComparator;
3099 } else
3100 throw new IllegalArgumentException("Bad sort order: " + groupBy);
3101 }
3102
3103 /**
3104 * Set the view panel to display the named view.
3105 */
3106 private void setView(String viewName) {
3107 //System.out.println("Showing view " + viewName);
3108 viewPanelLayout.show(viewPanel, viewName);
3109 boolean viewingBugs = viewName.equals("BugTree");
3110 if (viewingBugs)
3111 checkBugDetailsVisibility();
3112
3113 viewProjectItem.setSelected(!viewingBugs);
3114 if (viewingBugs)
3115 viewBugsItem.setEnabled(true);
3116 viewBugsItem.setSelected(viewingBugs);
3117
3118 currentView = viewName;
3119 ableEditMenu();
3120 }
3121
3122 /**
3123 * Get which view is displayed currently.
3124 */
3125 private String getView() {
3126 return currentView;
3127 }
3128
3129 /**
3130 * Called to add the jar file in the jarNameTextField to the
3131 * Jar file list (and the project it represents).
3132 */
3133 private void addJarToList() {
3134 String dirs = jarNameTextField.getText();
3135 String[] jarDirs = parsePaths(dirs);
3136 for (String jarFile : jarDirs) {
3137 if (!jarFile.equals("")) {
3138 addJarToProject(jarFile);
3139 }
3140 }
3141 jarNameTextField.setText("");
3142 }
3143
3144 /**
3145 * Add a Src file to the current project.
3146 *
3147 * @param srcFile the jar file to add to the project
3148 */
3149 private void addSrcToProject(String srcFile) {
3150 Project project = getCurrentProject();
3151 if (project.addSourceDir(srcFile)) {
3152 DefaultListModel listModel = (DefaultListModel) sourceDirList.getModel();
3153 listModel.addElement(srcFile);
3154 }
3155 }
3156
3157 /**
3158 * Add a Jar file to the current project.
3159 *
3160 * @param jarFile the jar file to add to the project
3161 */
3162 private void addJarToProject(String jarFile) {
3163 Project project = getCurrentProject();
3164 if (project.addFile(jarFile)) {
3165 DefaultListModel listModel = (DefaultListModel) jarFileList.getModel();
3166 listModel.addElement(jarFile);
3167 }
3168 }
3169
3170 /**
3171 * Parses a classpath into it's sub paths
3172 *
3173 * @param path the classpath
3174 * @return an array of paths
3175 */
3176 private String[] parsePaths(String paths) {
3177 return paths.split(SystemProperties.getProperty("path.separator"));
3178 }
3179
3180 /**
3181 * Called to add the source directory in the sourceDirTextField
3182 * to the source directory list (and the project it represents).
3183 */
3184 private void addSourceDirToList() {
3185 String dirs = srcDirTextField.getText();
3186 String[] sourceDirs = parsePaths(dirs);
3187 for (String sourceDir : sourceDirs) {
3188 if (!sourceDir.equals("")) {
3189 Project project = getCurrentProject();
3190 if (project.addSourceDir(sourceDir)) {
3191 DefaultListModel listModel = (DefaultListModel) sourceDirList.getModel();
3192 listModel.addElement(sourceDir);
3193 }
3194 }
3195 }
3196 srcDirTextField.setText("");
3197 }
3198
3199 /**
3200 * Called to add the classpath entry in the classpathEntryTextField
3201 * to the classpath entry list (and the project it represents).
3202 */
3203 private void addClasspathEntryToList() {
3204 String dirs = classpathEntryTextField.getText();
3205 String[] classDirs = parsePaths(dirs);
3206 for (String classpathEntry : classDirs) {
3207 if (!classpathEntry.equals("")) {
3208 addClasspathEntryToProject(classpathEntry);
3209 }
3210 }
3211 classpathEntryTextField.setText("");
3212 }
3213
3214 /**
3215 * Add a classpath entry to the current project.
3216 *
3217 * @param classpathEntry the classpath entry to add
3218 */
3219 private void addClasspathEntryToProject(String classpathEntry) {
3220 Project project = getCurrentProject();
3221 if (project.addAuxClasspathEntry(classpathEntry)) {
3222 DefaultListModel listModel = (DefaultListModel) classpathEntryList.getModel();
3223 listModel.addElement(classpathEntry);
3224 }
3225 }
3226
3227 /**
3228 * Synchronize current bug instance with the bug detail
3229 * window (source view, details window, etc.)
3230 */
3231 private void synchBugInstance() {
3232 // Get current bug instance
3233 BugInstance selected = getCurrentBugInstance();
3234 if (selected == null)
3235 return;
3236
3237 // If the details window is minimized, then the user can't see
3238 // it and there is no point in updating it.
3239 if (!viewBugDetailsItem.isSelected())
3240 return;
3241
3242 // Get the current source line annotation.
3243 // If the current leaf selected is not a source line annotation,
3244 // or a method annotation containing a source line annotation.
3245 // use the default source line annotation from the current bug instance
3246 // (if any).
3247 JTree bugTree = getCurrentBugTree();
3248
3249 // if the summary window is shown then skip it
3250 if (bugTree == null) {
3251 return;
3252 }
3253 SourceLineAnnotation srcLine = null;
3254 TreePath selPath = bugTree.getSelectionPath();
3255 if (selPath != null) {
3256 Object leaf = ((DefaultMutableTreeNode) selPath.getLastPathComponent()).getUserObject();
3257 if (leaf instanceof SourceLineAnnotation)
3258 srcLine = (SourceLineAnnotation) leaf;
3259 else if (leaf instanceof BugAnnotationWithSourceLines)
3260 srcLine = ((BugAnnotationWithSourceLines) leaf).getSourceLines();
3261
3262 if (srcLine == null)
3263 srcLine = selected.getPrimarySourceLineAnnotation();
3264 }
3265
3266 // Show source code.
3267 if (srcLine == null || srcLine != currentSourceLineAnnotation) {
3268 Project project = getCurrentProject();
3269 AnalysisRun analysisRun = getCurrentAnalysisRun();
3270 if (project == null) throw new IllegalStateException("null project!");
3271 if (analysisRun == null) throw new IllegalStateException("null analysis run!");
3272 try {
3273 boolean success = viewSource(project, analysisRun, srcLine);
3274 sourceTextArea.setEnabled(success);
3275 if (!success)
3276 sourceTextArea.setText("No source line information for this bug");
3277 } catch (IOException e) {
3278 sourceTextArea.setText("Could not find source: " + e.getMessage());
3279 logger.logMessage(Logger.WARNING, e.getMessage());
3280 }
3281
3282 currentSourceLineAnnotation = srcLine;
3283 }
3284
3285 // Show bug info.
3286 showBugInfo(selected);
3287
3288 // Synch annotation text.
3289 synchBugAnnotation(selected);
3290
3291 // Now the bug details are up to date.
3292 currentBugInstance = selected;
3293 }
3294
3295 private static final int SELECTION_VOFFSET = 2;
3296
3297 /**
3298 * Update the source view window.
3299 *
3300 * @param project the project (containing the source directories to search)
3301 * @param analysisRun the analysis run (containing the mapping of classes to source files)
3302 * @param srcLine the source line annotation (specifying source file to load and
3303 * which lines to highlight)
3304 * @return true if the source was shown successfully, false otherwise
3305 */
3306 private boolean viewSource(Project project, AnalysisRun analysisRun, final SourceLineAnnotation srcLine)
3307 throws IOException {
3308 // Get rid of old source code text
3309 sourceTextArea.setText("");
3310
3311 // There is nothing to do without a source annotation
3312 // TODO: actually, might want to put a message in the source window
3313 // explaining that we don't have the source file, and that
3314 // they might want to recompile with debugging info turned on.
3315 if (srcLine == null)
3316 return false;
3317
3318 SourceFinder sourceFinder = project.getSourceFinder();
3319
3320 // Look up the source file for this class.
3321 String sourceFile;
3322 InputStream in;
3323 try {
3324 SourceFile source = sourceFinder.findSourceFile(srcLine);
3325 sourceFile = source.getFullFileName();
3326 in = source.getInputStream();
3327 } catch (IOException e) {
3328 logger.logMessage(Logger.WARNING, "No source file for class " + srcLine.getClassName());
3329 return false;
3330 }
3331
3332 BufferedReader reader = null;
3333
3334 try {
3335 reader = new BufferedReader(new InputStreamReader(in));
3336 sourceTextArea.read(reader, sourceFile); // 2nd arg is mostly ignored
3337 } finally {
3338 if (reader != null)
3339 reader.close();
3340 }
3341
3342 if (srcLine.isUnknown()) {
3343 // No line number information, so can't highlight anything
3344
3345 // There was code here to scroll to the top, but that isn't
3346 // needed because sourceTextArea.read() does that for us.
3347 return true;
3348 }
3349
3350 // Highlight the annotation.
3351 // There seems to be some bug in Swing that sometimes prevents this code
3352 // from working when executed immediately after populating the
3353 // text in the text area. My guess is that when a large amount of text
3354 // is added, Swing defers some UI update work until "later" that is needed
3355 // to compute the visibility of text in the text area.
3356 // So, post some code to do the update to the Swing event queue.
3357 // Not really an ideal solution, but it seems to work.
3358 // note: Could reimplement this to use sourceTextArea.scrollRectToVisible(),
3359 // but if it ain't broke...
3360 SwingUtilities.invokeLater(new Runnable() {
3361 public void run() {
3362 // Highlight the lines from the source annotation.
3363 // Note that the source lines start at 1, while the line numbers
3364 // in the text area start at 0.
3365 try {
3366 int startLine = srcLine.getStartLine() - 1;
3367 int endLine = srcLine.getEndLine();
3368
3369 // Compute number of rows visible.
3370 // What a colossal pain in the ass this is.
3371 // You'd think there would be a convenient method to
3372 // return this information, but no.
3373 JViewport viewport = sourceTextAreaScrollPane.getViewport();
3374 Rectangle viewportRect = viewport.getViewRect();
3375 int topRow = sourceTextArea.getLineOfOffset(sourceTextArea.viewToModel(viewportRect.getLocation()));
3376 int bottomRow = sourceTextArea.getLineOfOffset(sourceTextArea.viewToModel(new Point(viewportRect.x, (viewportRect.y + viewportRect.height) - 1)));
3377 int numRowsVisible = bottomRow - topRow;
3378
3379 // Scroll the window so the beginning of the
3380 // annotation text will be (approximately) centered.
3381 int viewLine = Math.max(startLine - (numRowsVisible > 0 ? numRowsVisible / 2 : 0), 0);
3382 int viewBegin = sourceTextArea.getLineStartOffset(viewLine);
3383 Rectangle viewRect = sourceTextArea.modelToView(viewBegin);
3384 viewport.setViewPosition(new Point(viewRect.x, viewRect.y));
3385
3386 // Select (and highlight) the annotation.
3387 int selBegin = sourceTextArea.getLineStartOffset(startLine);
3388 int selEnd = sourceTextArea.getLineStartOffset(endLine);
3389 sourceTextArea.select(selBegin, selEnd);
3390 sourceTextArea.getCaret().setSelectionVisible(true);
3391 } catch (javax.swing.text.BadLocationException e) {
3392 logger.logMessage(Logger.ERROR, e.toString());
3393 }
3394 }
3395 });
3396
3397 return true;
3398 }
3399
3400 /**
3401 * Show descriptive text about the type of bug
3402 *
3403 * @param bugInstance the bug instance
3404 */
3405 private void showBugInfo(BugInstance bugInstance) {
3406 // Are we already showing details for this kind of bug?
3407 String bugDetailsKey = bugInstance.getType();
3408 if (bugDetailsKey.equals(currentBugDetailsKey))
3409 return;
3410
3411 // Display the details
3412 String html = I18N.instance().getDetailHTML(bugDetailsKey);
3413 bugDescriptionEditorPane.setContentType("text/html");
3414 currentBugDetailsKey = bugDetailsKey;
3415 /*
3416 bugDescriptionEditorPane.setText(html);
3417
3418 // : unfortunately, using setText() on the editor pane
3419 // results in the contents being scrolled to the bottom of the pane.
3420 // An immediate inline call to set the scroll position does nothing.
3421 // So, use invokeLater(), even though this results in flashing.
3422 // [What we really need is a way to set the text WITHOUT changing
3423 // the caret position. Need to investigate.]
3424 SwingUtilities.invokeLater(new Runnable() {
3425 public void run() {
3426 bugDescriptionScrollPane.getViewport().setViewPosition(new Point(0, 0));
3427 }
3428 });
3429 */
3430 StringReader reader = new StringReader(html); // no need for BufferedReader
3431 try {
3432 bugDescriptionEditorPane.read(reader, "html bug description");
3433 } catch (IOException e) {
3434 bugDescriptionEditorPane.setText("Could not find bug description: " + e.getMessage());
3435 logger.logMessage(Logger.WARNING, e.getMessage());
3436 } finally {
3437 reader.close(); // polite, but doesn't do much in StringReader
3438 }
3439 }
3440
3441 /**
3442 * Synchronize the bug annotation text with the current bug instance,
3443 * and update the annotation text with the new bug instance.
3444 *
3445 * @param selected the new BugInstance
3446 */
3447 private void synchBugAnnotation(BugInstance selected) {
3448 if (currentBugInstance != null) {
3449 String text = annotationTextArea.getText();
3450 try {
3451 currentBugInstance.setAnnotationText(text, null);
3452 } catch (SignInCancelledException e) {
3453 logger.logMessage(Logger.WARNING, e.getMessage());
3454 }
3455 }
3456
3457 //annotationTextArea.setText(selected.getAnnotationText());
3458 String userAnnotation = selected.getAnnotationText();
3459 if (userAnnotation==null || userAnnotation.length()==0) {
3460 // this is the common case, so might as well optimize it
3461 annotationTextArea.setText("");
3462 return;
3463 }
3464 StringReader reader = new StringReader(userAnnotation); // no need for BufferedReader
3465 try {
3466 annotationTextArea.read(reader, "user annotation");
3467 } catch (IOException e) {
3468 annotationTextArea.setText("Could not find user annotation: " + e.getMessage());
3469 logger.logMessage(Logger.WARNING, e.getMessage());
3470 } finally {
3471 reader.close(); // polite, but doesn't do much in StringReader
3472 }
3473 }
3474
3475 /**
3476 * Toggle a bug category checkbox.
3477 * Changes are reflected in the displayed bug trees (if any)
3478 * and also in the user preferences.
3479 *
3480 * @param checkBox the bug category checkbox
3481 */
3482 private void toggleBugCategory(JCheckBoxMenuItem checkBox) {
3483 int index = 0;
3484
3485 while (index < bugCategoryCheckBoxList.length) {
3486 if (bugCategoryCheckBoxList[index] == checkBox)
3487 break;
3488 ++index;
3489 }
3490
3491 if (index == bugCategoryCheckBoxList.length) {
3492 error("Could not find bug category checkbox");
3493 return;
3494 }
3495
3496 boolean selected = checkBox.isSelected();
3497 String bugCategory = bugCategoryList[index];
3498
3499 if (selected) {
3500 getFilterSettings().addCategory(bugCategory);
3501 } else {
3502 getFilterSettings().removeCategory(bugCategory);
3503 }
3504
3505 if (currentAnalysisRun != null) {
3506 synchAnalysisRun(currentAnalysisRun);
3507 }
3508 }
3509
3510 /* ----------------------------------------------------------------------
3511 * Misc. helpers
3512 * ---------------------------------------------------------------------- */
3513
3514 /**
3515 * Show About
3516 */
3517 void about() {
3518 AboutDialog dialog = new AboutDialog(this, logger, true);
3519 dialog.setSize(600, 554);
3520 dialog.setLocationRelativeTo(null); // center the dialog
3521 dialog.setVisible(true);
3522 }
3523
3524 /**
3525 * Exit the application.
3526 */
3527 @SuppressWarnings("DM_EXIT")
3528 void exitFindBugs() {
3529 // TODO: offer to save work, etc.
3530 // UserPreferences.getUserPreferences().storeUserDetectorPreferences();
3531 UserPreferences.getUserPreferences().write();
3532 System.exit(0);
3533 }
3534
3535 /**
3536 * Create a file chooser dialog.
3537 * Ensures that the dialog will start in the current directory.
3538 *
3539 * @return the file chooser
3540 */
3541 private JFileChooser createFileChooser() {
3542 return new JFileChooser(currentDirectory);
3543 }
3544
3545 /**
3546 * Create a file chooser dialog.
3547 * Ensures that the dialog will start in the current directory.
3548 *
3549 * @param extraComp The extra component to append to the dialog
3550 * @return the file chooser
3551 */
3552 private JFileChooser createFileChooser(final JComponent extraComp) {
3553 return new JFileChooser(currentDirectory) {
3554 private static final long serialVersionUID = 1L;
3555
3556 @Override
3557 protected JDialog createDialog(Component parent) throws HeadlessException {
3558 JDialog dialog = super.createDialog(parent);
3559 dialog.getContentPane().add(extraComp, BorderLayout.SOUTH);
3560 dialog.setLocation(300, 200);
3561 dialog.setResizable(false);
3562 return dialog;
3563 }
3564 };
3565 }
3566
3567 /**
3568 * Run a file chooser dialog.
3569 * If a file is chosen, then the current directory is updated.
3570 *
3571 * @param dialog the file chooser dialog
3572 * @param dialogTitle the dialog title
3573 * @return the outcome
3574 */
3575 private int chooseFile(JFileChooser dialog, String dialogTitle) {
3576 int outcome = dialog.showDialog(this, dialogTitle);
3577 return updateCurrentDirectoryFromDialog(dialog, outcome);
3578 }
3579
3580 /**
3581 * Run a file chooser dialog to choose a file to open.
3582 * If a file is chosen, then the current directory is updated.
3583 *
3584 * @param dialog the file chooser dialog
3585 * @return the outcome
3586 */
3587 private int chooseFileToOpen(JFileChooser dialog) {
3588 int outcome = dialog.showOpenDialog(this);
3589 return updateCurrentDirectoryFromDialog(dialog, outcome);
3590 }
3591
3592 private int updateCurrentDirectoryFromDialog(JFileChooser dialog, int outcome) {
3593 if (outcome != JFileChooser.CANCEL_OPTION) {
3594 File selectedFile = dialog.getSelectedFile();
3595 currentDirectory = selectedFile.getParentFile();
3596 }
3597 return outcome;
3598 }
3599
3600 /**
3601 * Get the Logger.
3602 */
3603 public Logger getLogger() {
3604 return logger;
3605 }
3606
3607 /**
3608 * Show an error dialog.
3609 */
3610 public void error(String message) {
3611 JOptionPane.showMessageDialog(this, message, "Error", JOptionPane.ERROR_MESSAGE);
3612 }
3613
3614 /**
3615 * Write a message to the console window.
3616 *
3617 * @param message the message to write
3618 */
3619 public void writeToLog(String message) {
3620 // consoleMessageArea.append(message);
3621 // consoleMessageArea.append("\n");
3622 }
3623
3624 /**
3625 * Fix up the path that is received from JFileChooser, if necessary
3626 * Double clicking a directory causes a repeated name, for some reason
3627 * such as a:\b\c\c when a:\b\c was chosen
3628 */
3629 public File verifyFileSelection(File pickedFile) {
3630 if (pickedFile.exists())
3631 return pickedFile;
3632
3633 File parent = pickedFile.getParentFile();
3634 if ((parent != null) && parent.getName().equals(pickedFile.getName()))
3635 return parent;
3636
3637 //Something bad has happened
3638 return pickedFile;
3639 }
3640
3641 /**
3642 * Get the current ProjectFilterSettings.
3643 */
3644 public ProjectFilterSettings getFilterSettings() {
3645 return UserPreferences.getUserPreferences().getFilterSettings();
3646 }
3647
3648 /**
3649 * Get the current priority threshold.
3650 */
3651 public int getPriorityThreshold() {
3652 return getFilterSettings().getMinPriorityAsInt();
3653 }
3654
3655 /**
3656 * Get list of AnalysisFeatureSettings.
3657 *
3658 * @return list of AnalysisFeatureSettings
3659 */
3660 public AnalysisFeatureSetting[] getSettingList() {
3661 return settingList;
3662 }
3663
3664 /* ----------------------------------------------------------------------
3665 * main() method
3666 * ---------------------------------------------------------------------- */
3667
3668 private static class SwingCommandLine extends FindBugsCommandLine {
3669 public SwingCommandLine() {
3670 addSwitch("-debug", "enable debug output");
3671 addSwitchWithOptionalExtraPart("-look", "plastic|gtk|native", "set look and feel");
3672 addOption("-project", "project file", "load given project");
3673 addOption("-priority", "thread priority",
3674 "set analysis thread's priority level (default is " +
3675 (Thread.NORM_PRIORITY-1) + ")");
3676 addOption("-loadbugs", "bugs xml filename", "load given bugs xml file");
3677 }
3678
3679 String bugsFilename = "";
3680
3681 public String getBugsFilename() {
3682 return bugsFilename;
3683 }
3684
3685 // Thread priority for the analysis thread. The default is
3686 // just below the priority of the GUI
3687 private int priority = Thread.NORM_PRIORITY-1;
3688
3689 /**
3690 * Retrieve thread priority for the analysis thread.
3691 * @return thread priority for the analysis thread
3692 */
3693 public int getPriority() {
3694 return priority;
3695 }
3696
3697 @Override
3698 protected void handleOption(String option, String optionExtraPart) {
3699 if (option.equals("-debug")) {
3700 System.out.println("Setting findbugs.debug=true");
3701 System.setProperty("findbugs.debug", "true");
3702 } else if (option.equals("-look")) {
3703 String arg = optionExtraPart;
3704
3705 String theme = null;
3706 if (arg.equals("plastic")) {
3707 // You can get the Plastic look and feel from jgoodies.com:
3708 // http://www.jgoodies.com/downloads/libraries.html
3709 // Just put "plastic.jar" in the lib directory, right next
3710 // to the other jar files.
3711 theme = "com.jgoodies.plaf.plastic.PlasticXPLookAndFeel";
3712 } else if (arg.equals("gtk")) {
3713 theme = "com.sun.java.swing.plaf.gtk.GTKLookAndFeel";
3714 } else if (arg.equals("native")) {
3715 theme = UIManager.getSystemLookAndFeelClassName();
3716 } else {
3717 System.err.println("Style '" + arg + "' not supported");
3718 }
3719
3720 if (theme != null) {
3721 try {
3722 UIManager.setLookAndFeel(theme);
3723 } catch (Exception e) {
3724 System.err.println("Couldn't load " + arg +
3725 " look and feel: " + e.toString());
3726 }
3727 }
3728 } else {
3729 super.handleOption(option, optionExtraPart);
3730 }
3731 }
3732
3733
3734 @Override
3735 protected void handleOptionWithArgument(String option, String argument) throws IOException {
3736 if (option.equals("-loadbugs")) {
3737 bugsFilename = argument;
3738 } else if (option.equals("-priority")) {
3739 int num;
3740 try {
3741 num = Integer.parseInt(argument);
3742 } catch(NumberFormatException e) {
3743 num = Thread.NORM_PRIORITY-1;
3744 }
3745 priority = num;
3746 } else {
3747 super.handleOptionWithArgument(option, argument);
3748 }
3749 }
3750 }
3751
3752 /**
3753 * Invoke from the command line.
3754 *
3755 * @param args the command line arguments
3756 * @throws IOException
3757 */
3758 public static void main(String args[]) throws IOException {
3759 Project project = null;
3760
3761 SwingCommandLine commandLine = new SwingCommandLine();
3762 try {
3763 commandLine.parse(args);
3764 } catch (IllegalArgumentException e) {
3765 System.err.println("Error: " + e.getMessage());
3766 showSynopsis();
3767 ShowHelp.showGeneralOptions();
3768 showCommandLineOptions();
3769 System.exit(1);
3770 } catch (HelpRequestedException e) {
3771 showSynopsis();
3772 ShowHelp.showGeneralOptions();
3773 showCommandLineOptions();
3774 System.exit(1);
3775 }
3776
3777 if (commandLine.getProject().getFileCount() > 0) {
3778 project = commandLine.getProject();
3779 }
3780
3781 // Uncomment one of these to test I18N
3782 // Locale.setDefault( Locale.FRENCH );
3783 // Locale.setDefault( Locale.GERMAN );
3784 // Locale.setDefault( Locale.JAPANESE );
3785 // Locale.setDefault( new Locale( "et" ));
3786 // Locale.setDefault( new Locale( "fi" ));
3787 // Locale.setDefault( new Locale( "es" ));
3788 // Locale.setDefault( new Locale( "pl" ));
3789
3790 // Load plugins!
3791 DetectorFactoryCollection.instance();
3792
3793 FindBugsFrame frame = new FindBugsFrame();
3794
3795 if (project != null) {
3796 frame.setProject(project);
3797 } else if (commandLine.getBugsFilename().length() > 0) {
3798 try {
3799 File bugsFile = new File(commandLine.getBugsFilename());
3800 frame.loadBugsFromFile(bugsFile);
3801 } catch (Exception e) {
3802 System.err.println("Error: " + e.getMessage());
3803 }
3804 } else if (SystemProperties.getProperty("findbugs.loadBugsFromURL") != null) {
3805 // Allow JNLP launch to specify the URL of a report to load
3806 try {
3807 String urlspec = SystemProperties.getProperty("findbugs.loadBugsFromURL");
3808 frame.loadBugsFromURL(urlspec);
3809 } catch (Exception e) {
3810 System.err.println("Error: " + e.getMessage());
3811 }
3812 }
3813
3814 frame.setPriority(commandLine.getPriority());
3815
3816 if (commandLine.getSettingList() != null) {
3817 frame.settingList = commandLine.getSettingList();
3818 if (Arrays.equals(frame.settingList,FindBugs.MIN_EFFORT))
3819 frame.minEffortItem.setSelected(true);
3820 else if (Arrays.equals(frame.settingList, FindBugs.MAX_EFFORT))
3821 frame.maxEffortItem.setSelected(true);
3822 }
3823
3824 frame.setSize(800, 600);
3825 frame.setLocationRelativeTo(null); // center the frame
3826 frame.setVisible(true);
3827 }
3828
3829 private int analysisPriority = Thread.NORM_PRIORITY-1;
3830
3831 public void setPriority(int priority) {
3832 this.analysisPriority = priority;
3833 }
3834
3835 public static void showCommandLineOptions() {
3836 System.out.println("GUI options:");
3837 new SwingCommandLine().printUsage(System.out);
3838 }
3839
3840 public static void showSynopsis() {
3841 System.out.println("Usage: findbugs [general options] [gui options]");
3842 }
3843
3844 /* ----------------------------------------------------------------------
3845 * Instance variables
3846 * ---------------------------------------------------------------------- */
3847
3848 // Variables declaration - do not modify//GEN-BEGIN:variables
3849 private javax.swing.JMenuItem aboutItem;
3850 private javax.swing.JButton addClasspathEntryButton;
3851 private javax.swing.JButton addJarButton;
3852 private javax.swing.JButton addSourceDirButton;
3853 private javax.swing.JTextArea annotationTextArea;
3854 private javax.swing.JScrollPane annotationTextAreaScrollPane;
3855 private javax.swing.JButton browseClasspathEntryButton;
3856 private javax.swing.JButton browseJarButton;
3857 private javax.swing.JButton browseSrcDirButton;
3858 private javax.swing.JEditorPane bugDescriptionEditorPane;
3859 private javax.swing.JScrollPane bugDescriptionScrollPane;
3860 private javax.swing.JTabbedPane bugDetailsTabbedPane;
3861 private javax.swing.JEditorPane bugSummaryEditorPane;
3862 private javax.swing.JSplitPane bugTreeBugDetailsSplitter;
3863 private javax.swing.JPanel bugTreePanel;
3864 private javax.swing.JTree byBugCategoryBugTree;
3865 private javax.swing.JScrollPane byBugCategoryScrollPane;
3866 private javax.swing.JTree byBugTypeBugTree;
3867 private javax.swing.JScrollPane byBugTypeScrollPane;
3868 private javax.swing.JTree byClassBugTree;
3869 private javax.swing.JScrollPane byClassScrollPane;
3870 private javax.swing.JTree byPackageBugTree;
3871 private javax.swing.JScrollPane byPackageScrollPane;
3872 private javax.swing.JScrollPane bySummary;
3873 private javax.swing.JButton classpathDownButton;
3874 private javax.swing.JLabel classpathEntryLabel;
3875 private javax.swing.JList classpathEntryList;
3876 private javax.swing.JLabel classpathEntryListLabel;
3877 private javax.swing.JScrollPane classpathEntryListScrollPane;
3878 private javax.swing.JTextField classpathEntryTextField;
3879 private javax.swing.JButton classpathUpButton;
3880 private javax.swing.JMenuItem closeProjectItem;
3881 private javax.swing.JMenuItem configureDetectorsItem;
3882 private javax.swing.JMenuItem copyItem;
3883 private javax.swing.JMenuItem cutItem;
3884 private javax.swing.JMenu editMenu;
3885 private javax.swing.JLabel editProjectLabel;
3886 private javax.swing.JPanel editProjectPanel;
3887 private javax.swing.ButtonGroup effortButtonGroup;
3888 private javax.swing.JMenu effortMenu;
3889 private javax.swing.JPanel emptyPanel;
3890 private javax.swing.JMenuItem exitItem;
3891 private javax.swing.JRadioButtonMenuItem expPriorityButton;
3892 private javax.swing.JMenu fileMenu;
3893 private javax.swing.JMenu filterWarningsMenu;
3894 private javax.swing.JButton findBugsButton;
3895 private javax.swing.JCheckBoxMenuItem fullDescriptionsItem;
3896 private javax.swing.JTabbedPane groupByTabbedPane;
3897 private javax.swing.JLabel growBoxSpacer;
3898 private javax.swing.JMenu helpMenu;
3899 private javax.swing.JRadioButtonMenuItem highPriorityButton;
3900 private javax.swing.JPanel jPanel1;
3901 private javax.swing.JSeparator jSeparator1;
3902 private javax.swing.JSeparator jSeparator10;
3903 private javax.swing.JSeparator jSeparator11;
3904 private javax.swing.JSeparator jSeparator2;
3905 private javax.swing.JSeparator jSeparator3;
3906 private javax.swing.JSeparator jSeparator4;
3907 private javax.swing.JSeparator jSeparator5;
3908 private javax.swing.JSeparator jSeparator6;
3909 private javax.swing.JSeparator jSeparator7;
3910 private javax.swing.JSeparator jSeparator8;
3911 private javax.swing.JSeparator jSeparator9;
3912 private javax.swing.JLabel jarFileLabel;
3913 private javax.swing.JList jarFileList;
3914 private javax.swing.JLabel jarFileListLabel;
3915 private javax.swing.JScrollPane jarFileListScrollPane;
3916 private javax.swing.JTextField jarNameTextField;
3917 private javax.swing.JMenuItem loadBugsItem;
3918 private javax.swing.JLabel logoLabel;
3919 private javax.swing.JRadioButtonMenuItem lowPriorityButton;
3920 private javax.swing.JCheckBoxMenuItem maxEffortItem;
3921 private javax.swing.JRadioButtonMenuItem mediumPriorityButton;
3922 private javax.swing.JCheckBoxMenuItem minEffortItem;
3923 private javax.swing.JMenuItem newProjectItem;
3924 private javax.swing.JCheckBoxMenuItem normalEffortItem;
3925 private javax.swing.JMenuItem openProjectItem;
3926 private javax.swing.JMenuItem pasteItem;
3927 private javax.swing.ButtonGroup priorityButtonGroup;
3928 private javax.swing.JMenu recentProjectsMenu;
3929 private javax.swing.JMenuItem reloadProjectItem;
3930 private javax.swing.JButton removeClasspathEntryButton;
3931 private javax.swing.JButton removeJarButton;
3932 private javax.swing.JButton removeSrcDirButton;
3933 private javax.swing.JPanel reportPanel;
3934 private javax.swing.JMenuItem saveBugsItem;
3935 private javax.swing.JMenuItem saveProjectAsItem;
3936 private javax.swing.JMenuItem saveProjectItem;
3937 private javax.swing.JMenuItem selectAllItem;
3938 private javax.swing.JMenu settingsMenu;
3939 private javax.swing.JLabel sourceDirLabel;
3940 private javax.swing.JList sourceDirList;
3941 private javax.swing.JLabel sourceDirListLabel;
3942 private javax.swing.JScrollPane sourceDirListScrollPane;
3943 private javax.swing.JButton sourceDownButton;
3944 private javax.swing.JTextArea sourceTextArea;
3945 private LineNumberer sourceLineNumberer;
3946 private javax.swing.JScrollPane sourceTextAreaScrollPane;
3947 private javax.swing.JButton sourceUpButton;
3948 private javax.swing.JTextField srcDirTextField;
3949 private javax.swing.JMenuBar theMenuBar;
3950 private javax.swing.JLabel urlLabel;
3951 private javax.swing.JCheckBoxMenuItem viewBugDetailsItem;
3952 private javax.swing.JRadioButtonMenuItem viewBugsItem;
3953 private javax.swing.JMenu viewMenu;
3954 private javax.swing.JPanel viewPanel;
3955 private javax.swing.JRadioButtonMenuItem viewProjectItem;
3956 // End of variables declaration//GEN-END:variables
3957
3958 // My variable declarations
3959 private Logger logger;
3960 private CardLayout viewPanelLayout;
3961 private String currentView;
3962 private File currentDirectory;
3963 private Project currentProject;
3964 private JTree[] bugTreeList;
3965 private AnalysisRun currentAnalysisRun;
3966 private BugInstance currentBugInstance; // be lazy in switching bug instance details
3967 private SourceLineAnnotation currentSourceLineAnnotation; // as above
3968 private String currentBugDetailsKey;
3969 private JCheckBoxMenuItem[] bugCategoryCheckBoxList;
3970 private String[] bugCategoryList;
3971 private AnalysisFeatureSetting[] settingList = FindBugs.DEFAULT_EFFORT;
3972
3973 // My constant declarations
3974 private final static boolean MAC_OS_X = SystemProperties.getProperty("os.name").toLowerCase().startsWith("mac os x");
3975
3976 private static int getMenuMask() {
3977 return Toolkit.getDefaultToolkit().getMenuShortcutKeyMask();
3978 }
3979
3980 }
+0
-83
src/obsolete/edu/umd/cs/findbugs/gui/Grouper.java less more
0 /*
1 * FindBugs - Find bugs in Java programs
2 * Copyright (C) 2003,2004 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 /*
20 * Grouper.java
21 *
22 * Created on April 5, 2003, 3:46 PM
23 */
24
25 package edu.umd.cs.findbugs.gui;
26
27 import java.util.Collection;
28 import java.util.Comparator;
29 import java.util.Iterator;
30
31 /**
32 * Given a sorted Collection and a Comparator, produces groups of objects
33 * that compare as equal. If the Collection is not sorted, this
34 * class will not work correctly.
35 *
36 * @author David Hovemeyer
37 */
38 public class Grouper <ElementType> {
39
40 public interface Callback <ElementType2> {
41 public void startGroup(ElementType2 firstMember);
42
43 public void addToGroup(ElementType2 member);
44 }
45
46 private Callback<ElementType> callback;
47
48 /**
49 * Creates a new instance of Grouper.
50 *
51 * @param callback the callback which receives the groups and elements
52 */
53 public Grouper(Callback<ElementType> callback) {
54 this.callback = callback;
55 }
56
57 /**
58 * Group elements of given collection according to given
59 * compartor's test for equality. The groups are specified by
60 * calls to the Grouper's callback object.
61 *
62 * @param collection the collection
63 * @param comparator the comparator
64 */
65 public void group(Collection<ElementType> collection, Comparator<ElementType> comparator) {
66 Iterator<ElementType> i = collection.iterator();
67 ElementType last = null;
68 while (i.hasNext()) {
69 ElementType current = i.next();
70 if (last != null && comparator.compare(last, current) == 0) {
71 // Same group as before
72 callback.addToGroup(current);
73 } else {
74 // Start of a new group
75 callback.startGroup(current);
76 }
77
78 last = current;
79 }
80 }
81
82 }
+0
-90
src/obsolete/edu/umd/cs/findbugs/gui/LineNumberer.java less more
0 /*
1 * FindBugs - Find bugs in Java programs
2 * Copyright (C) 2005 Dave Brosius <dbrosius@users.sourceforge.net>
3 * Copyright (C) 2005 University of Maryland
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2.1 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19 package edu.umd.cs.findbugs.gui;
20
21 import java.awt.Color;
22 import java.awt.Dimension;
23 import java.awt.Font;
24 import java.awt.FontMetrics;
25 import java.awt.Graphics;
26 import java.awt.Rectangle;
27
28 import javax.swing.JComponent;
29 import javax.swing.JTextArea;
30
31 public class LineNumberer extends JComponent
32 {
33 public static final int PAD = 10;
34 public static final String PROTOTYPE = "00000";
35
36 private JTextArea textArea;
37 private FontMetrics fm;
38
39 public LineNumberer(JTextArea ta) {
40 setFont( ta.getFont() );
41 textArea = ta;
42 setForeground( Color.BLUE );
43
44 fm = this.getFontMetrics(ta.getFont());
45 setWidths();
46 }
47
48 @Override
49 public void setFont(Font font)
50 {
51 //ignore
52 }
53
54 private void setWidths() {
55 int width = fm.stringWidth( PROTOTYPE );
56 Dimension d = getPreferredSize();
57 d.setSize(PAD + width, Integer.MAX_VALUE);
58 setPreferredSize( d );
59 setSize( d );
60 }
61
62
63 @Override
64 public void paintComponent(Graphics g)
65 {
66 int lineHeight = fm.getHeight();
67 int startOffset = textArea.getInsets().top + fm.getAscent();
68
69 Rectangle clip = g.getClipBounds();
70
71 g.setColor( getBackground() );
72 g.fillRect(clip.x, clip.y, clip.width, clip.height);
73
74 g.setColor( getForeground() );
75 int beginLineNumber = (clip.y / lineHeight) + 1;
76 int endLineNumber = beginLineNumber + (clip.height / lineHeight);
77
78 int y = (clip.y / lineHeight) * lineHeight + startOffset;
79
80 for (int i = beginLineNumber; i <= endLineNumber; i++)
81 {
82 String ln = String.valueOf(i);
83 int width = fm.stringWidth( ln );
84 int rowWidth = getSize().width;
85 g.drawString(ln, rowWidth - width - PAD, y);
86 y += lineHeight;
87 }
88 }
89 }
+0
-108
src/obsolete/edu/umd/cs/findbugs/gui/OSXAdapter.java less more
0 package edu.umd.cs.findbugs.gui;
1
2 import com.apple.eawt.ApplicationAdapter;
3 import com.apple.eawt.ApplicationEvent;
4
5 /*
6 * Based on sample code from Apple.
7 *
8 * This is the only class that uses the Apple specific EAWT classes.
9 * This class should only ever be referenced via reflection after
10 * checking that we are running on Mac OS X.
11 */
12 public class OSXAdapter extends ApplicationAdapter {
13
14 // pseudo-singleton model; no point in making multiple instances
15 // of the EAWT application or our adapter
16 private static OSXAdapter theAdapter;
17 private static com.apple.eawt.Application theApplication;
18
19 // reference to the app where the existing quit, about, prefs code is
20 private FindBugsFrame mainApp;
21
22 private OSXAdapter (FindBugsFrame inApp) {
23 mainApp = inApp;
24 }
25
26 // implemented handler methods. These are basically hooks into
27 // existing functionality from the main app, as if it came
28 // over from another platform.
29
30 @Override
31 public void handleAbout(ApplicationEvent ae) {
32 if (mainApp != null) {
33 ae.setHandled(true);
34 // We need to invoke modal About Dialog asynchronously
35 // otherwise the Application queue is locked for the duration
36 // of the about Dialog, which results in a deadlock if a URL is
37 // selected, and we get a ReOpenApplication event when user
38 // switches back to Findbugs.
39 javax.swing.SwingUtilities.invokeLater(new Runnable() {
40 public void run() {
41 mainApp.about();
42 }
43 });
44 } else {
45 throw new IllegalStateException("handleAbout: " +
46 "MyApp instance detached from listener");
47 }
48 }
49
50 @Override
51 public void handlePreferences(ApplicationEvent ae) {
52 if (mainApp != null) {
53 // mainApp.preferences();
54 ae.setHandled(true);
55 } else {
56 throw new IllegalStateException("handlePreferences: MyApp instance " +
57 "detached from listener");
58 }
59 }
60
61 @Override
62 public void handleQuit(ApplicationEvent ae) {
63 if (mainApp != null) {
64
65 /*
66 * You MUST setHandled(false) if you want to
67 * delay or cancel the quit. This is important
68 * for cross-platform development -- have a
69 * universal quit routine that chooses whether
70 * or not to quit, so the functionality is
71 * identical on all platforms. This example
72 * simply cancels the AppleEvent-based quit and
73 * defers to that universal method.
74 */
75
76 ae.setHandled(false);
77 mainApp.exitFindBugs();
78 } else {
79 throw new IllegalStateException("handleQuit: MyApp instance detached " +
80 "from listener");
81 }
82 }
83
84
85 // The main entry-point for this functionality. This is the only method
86 // that needs to be called at runtime, and it can easily be done using
87 // reflection (see MyApp.java)
88 public static synchronized void registerMacOSXApplication(FindBugsFrame inApp) {
89 if (theApplication == null) {
90 theApplication = new com.apple.eawt.Application();
91 }
92
93 if (theAdapter == null) {
94 theAdapter = new OSXAdapter(inApp);
95 }
96 theApplication.addApplicationListener(theAdapter);
97 }
98
99 // Another static entry point for EAWT functionality. Enables the
100 // "Preferences..." menu item in the application menu.
101 public static synchronized void enablePrefs(boolean enabled) {
102 if (theApplication == null) {
103 theApplication = new com.apple.eawt.Application();
104 }
105 theApplication.setEnabledPreferencesMenu(enabled);
106 }
107 }
+0
-152
src/obsolete/edu/umd/cs/findbugs/gui/RunAnalysisDialog.form less more
0 <?xml version="1.0" encoding="UTF-8" ?>
1
2 <Form version="1.0" type="org.netbeans.modules.form.forminfo.JDialogFormInfo">
3 <Properties>
4 <Property name="title" type="java.lang.String" value="Run Analysis" postCode="this.setTitle(L10N.getLocalString(&quot;dlg.runanalysis_ttl&quot;, &quot;Run Analysis&quot;));"/>
5 </Properties>
6 <SyntheticProperties>
7 <SyntheticProperty name="formSizePolicy" type="int" value="1"/>
8 </SyntheticProperties>
9 <Events>
10 <EventHandler event="windowClosing" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="closeDialog"/>
11 <EventHandler event="windowOpened" listener="java.awt.event.WindowListener" parameters="java.awt.event.WindowEvent" handler="formWindowOpened"/>
12 </Events>
13
14 <Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout"/>
15 <SubComponents>
16 <Component class="javax.swing.JLabel" name="findBugsLabel">
17 <Properties>
18 <Property name="background" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
19 <Color blue="cc" green="0" red="0" type="rgb"/>
20 </Property>
21 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
22 <Font name="Dialog" size="24" style="1"/>
23 </Property>
24 <Property name="foreground" type="java.awt.Color" editor="org.netbeans.beaninfo.editors.ColorEditor">
25 <Color blue="ff" green="ff" red="ff" type="rgb"/>
26 </Property>
27 <Property name="text" type="java.lang.String" value="Find Bugs!"/>
28 <Property name="opaque" type="boolean" value="true"/>
29 </Properties>
30 <Constraints>
31 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
32 <GridBagConstraints gridX="-1" gridY="-1" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="3" insetsRight="0" anchor="11" weightX="1.0" weightY="0.0"/>
33 </Constraint>
34 </Constraints>
35 </Component>
36 <Component class="javax.swing.JLabel" name="countLabel">
37 <Properties>
38 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
39 <Font name="Dialog" size="12" style="0"/>
40 </Property>
41 <Property name="text" type="java.lang.String" value="Count:"/>
42 </Properties>
43 <AuxValues>
44 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="countLabel.setText(L10N.getLocalString(&quot;dlg.count_lbl&quot;, &quot;Count:&quot;));"/>
45 </AuxValues>
46 <Constraints>
47 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
48 <GridBagConstraints gridX="0" gridY="3" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="3" insetsBottom="3" insetsRight="3" anchor="13" weightX="0.0" weightY="0.0"/>
49 </Constraint>
50 </Constraints>
51 </Component>
52 <Component class="javax.swing.JLabel" name="progressLabel">
53 <Properties>
54 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
55 <Font name="Dialog" size="12" style="0"/>
56 </Property>
57 <Property name="text" type="java.lang.String" value="Progress:"/>
58 </Properties>
59 <AuxValues>
60 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="progressLabel.setText(L10N.getLocalString(&quot;dlg.progress_lbl&quot;, &quot;Progress:&quot;));"/>
61 </AuxValues>
62 <Constraints>
63 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
64 <GridBagConstraints gridX="0" gridY="5" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="3" insetsBottom="3" insetsRight="3" anchor="13" weightX="0.0" weightY="0.0"/>
65 </Constraint>
66 </Constraints>
67 </Component>
68 <Component class="javax.swing.JProgressBar" name="progressBar">
69 <Constraints>
70 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
71 <GridBagConstraints gridX="1" gridY="5" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="3" anchor="10" weightX="0.0" weightY="0.0"/>
72 </Constraint>
73 </Constraints>
74 </Component>
75 <Component class="javax.swing.JButton" name="cancelButton">
76 <Properties>
77 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
78 <Font name="Dialog" size="12" style="0"/>
79 </Property>
80 <Property name="text" type="java.lang.String" value="Cancel"/>
81 </Properties>
82 <Events>
83 <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cancelButtonActionPerformed"/>
84 </Events>
85 <AuxValues>
86 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="cancelButton.setText(L10N.getLocalString(&quot;dlg.cancel_btn&quot;, &quot;Cancel&quot;));"/>
87 </AuxValues>
88 <Constraints>
89 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
90 <GridBagConstraints gridX="0" gridY="8" gridWidth="2" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="0" insetsBottom="3" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
91 </Constraint>
92 </Constraints>
93 </Component>
94 <Component class="javax.swing.JSeparator" name="jSeparator1">
95 <Constraints>
96 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
97 <GridBagConstraints gridX="0" gridY="7" gridWidth="2" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
98 </Constraint>
99 </Constraints>
100 </Component>
101 <Component class="javax.swing.JLabel" name="stageLabel">
102 <Properties>
103 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
104 <Font name="Dialog" size="12" style="0"/>
105 </Property>
106 <Property name="text" type="java.lang.String" value="Stage:"/>
107 </Properties>
108 <AuxValues>
109 <AuxValue name="JavaCodeGenerator_InitCodePost" type="java.lang.String" value="stageLabel.setText(L10N.getLocalString(&quot;dlg.stage_lbl&quot;, &quot;Stage:&quot;));"/>
110 </AuxValues>
111 <Constraints>
112 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
113 <GridBagConstraints gridX="0" gridY="2" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="3" insetsLeft="3" insetsBottom="3" insetsRight="3" anchor="13" weightX="0.0" weightY="0.0"/>
114 </Constraint>
115 </Constraints>
116 </Component>
117 <Component class="javax.swing.JLabel" name="stageNameLabel">
118 <Properties>
119 <Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
120 <Font name="Dialog" size="12" style="0"/>
121 </Property>
122 </Properties>
123 <Constraints>
124 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
125 <GridBagConstraints gridX="1" gridY="2" gridWidth="1" gridHeight="1" fill="2" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.0"/>
126 </Constraint>
127 </Constraints>
128 </Component>
129 <Component class="javax.swing.JLabel" name="topVerticalFiller">
130 <Constraints>
131 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
132 <GridBagConstraints gridX="0" gridY="6" gridWidth="1" gridHeight="1" fill="3" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.5"/>
133 </Constraint>
134 </Constraints>
135 </Component>
136 <Component class="javax.swing.JLabel" name="bottomVerticalFiller">
137 <Constraints>
138 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
139 <GridBagConstraints gridX="0" gridY="1" gridWidth="1" gridHeight="1" fill="3" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="0" insetsBottom="0" insetsRight="0" anchor="10" weightX="0.0" weightY="0.5"/>
140 </Constraint>
141 </Constraints>
142 </Component>
143 <Component class="javax.swing.JLabel" name="countValueLabel">
144 <Constraints>
145 <Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout" value="org.netbeans.modules.form.compat2.layouts.DesignGridBagLayout$GridBagConstraintsDescription">
146 <GridBagConstraints gridX="1" gridY="3" gridWidth="1" gridHeight="1" fill="0" ipadX="0" ipadY="0" insetsTop="0" insetsLeft="3" insetsBottom="0" insetsRight="0" anchor="17" weightX="0.0" weightY="0.0"/>
147 </Constraint>
148 </Constraints>
149 </Component>
150 </SubComponents>
151 </Form>
+0
-386
src/obsolete/edu/umd/cs/findbugs/gui/RunAnalysisDialog.java less more
0 /*
1 * FindBugs - Find bugs in Java programs
2 * Copyright (C) 2003,2004 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 /*
20 * RunAnalysisDialog.java
21 *
22 * Created on April 1, 2003, 3:22 PM
23 */
24
25 package edu.umd.cs.findbugs.gui;
26
27 import java.awt.event.WindowEvent;
28
29 import javax.swing.JOptionPane;
30 import javax.swing.SwingUtilities;
31
32 import edu.umd.cs.findbugs.FindBugsProgress;
33 import edu.umd.cs.findbugs.L10N;
34
35 /**
36 * A modal dialog to run the actual FindBugs analysis on a project.
37 * The analysis is done in a separate thread, so that the GUI can
38 * still stay current while the analysis is running. We provide support
39 * for reporting the progress of the analysis, and for asynchronously
40 * cancelling the analysis before it completes.
41 *
42 * @author David Hovemeyer
43 */
44 public class RunAnalysisDialog extends javax.swing.JDialog {
45 private static final long serialVersionUID = 1L;
46
47 private class RunAnalysisProgress implements FindBugsProgress {
48 private int goal, count;
49
50 private synchronized int getGoal() {
51 return goal;
52 }
53
54 private synchronized int getCount() {
55 return count;
56 }
57
58 public void reportNumberOfArchives(final int numArchives) {
59 beginStage(L10N.getLocalString("msg.scanningarchives_txt", "Scanning archives"), numArchives);
60 }
61
62 public void finishArchive() {
63 step();
64 }
65
66 public void startAnalysis(int numClasses) {
67 beginStage(L10N.getLocalString("msg.analysingclasses_txt", "Analyzing classes"), numClasses);
68 }
69
70 public void finishClass() {
71 step();
72 }
73
74 public void finishPerClassAnalysis() {
75 SwingUtilities.invokeLater(new Runnable() {
76 public void run() {
77 stageNameLabel.setText(L10N.getLocalString("msg.finishedanalysis_txt", "Finishing analysis"));
78 }
79 });
80 }
81
82 private void beginStage(final String stageName, final int goal) {
83 synchronized (this) {
84 this.count = 0;
85 this.goal = goal;
86 }
87
88 SwingUtilities.invokeLater(new Runnable() {
89 public void run() {
90 int goal = getGoal();
91 stageNameLabel.setText(stageName);
92 countValueLabel.setText("0/" + goal);
93 progressBar.setMaximum(goal);
94 progressBar.setValue(0);
95 }
96 });
97 }
98
99 private void step() {
100 synchronized (this) {
101 count++;
102 }
103
104 SwingUtilities.invokeLater(new Runnable() {
105 public void run() {
106 int count = getCount();
107 int goal = getGoal();
108 countValueLabel.setText(count + "/" + goal);
109 progressBar.setValue(count);
110 }
111 });
112 }
113
114 public void predictPassCount(int[] classesPerPass) {
115 // noop
116 }
117
118 public void startArchive(String name) {
119 // noop
120 }
121 }
122
123 private final AnalysisRun analysisRun;
124 private Thread analysisThread;
125 private boolean completed;
126 private Exception fatalException;
127 private int analysisPriority;
128
129 /**
130 * Creates new form RunAnalysisDialog
131 */
132 public RunAnalysisDialog(java.awt.Frame parent, AnalysisRun analysisRun_, int analysisPriority) {
133 super(parent, true);
134 initComponents();
135 this.analysisRun = analysisRun_;
136 this.completed = false;
137 this.analysisPriority = analysisPriority;
138
139 // Create a progress callback to give the user feedback
140 // about how far along we are.
141 final FindBugsProgress progress = new RunAnalysisProgress();
142
143 // This is the thread that will actually run the analysis.
144 this.analysisThread = new Thread() {
145 @Override
146 public void run() {
147 try {
148 analysisRun.execute(progress);
149 setCompleted(true);
150 } catch (java.io.IOException e) {
151 setException(e);
152 } catch (InterruptedException e) {
153 // We don't need to do anything here.
154 // The completed flag is not set, so the frame
155 // will know that the analysis did not complete.
156 } catch (Exception e) {
157 setException(e);
158 }
159
160 // Send a message to the dialog that it should close
161 // That way, it goes away without any need for user intervention
162 SwingUtilities.invokeLater(new Runnable() {
163 public void run() {
164 closeDialog(new WindowEvent(RunAnalysisDialog.this, WindowEvent.WINDOW_CLOSING));
165 }
166 });
167 }
168 };
169 }
170
171 public synchronized void setCompleted(boolean completed) {
172 this.completed = completed;
173 }
174
175 /**
176 * The creator of the dialog may call this method to find out whether
177 * or not the analysis completed normally.
178 */
179 public synchronized boolean isCompleted() {
180 return completed;
181 }
182
183 public synchronized void setException(Exception e) {
184 fatalException = e;
185 }
186
187 /**
188 * Determine whether or not a fatal exception occurred
189 * during analysis.
190 */
191 public synchronized boolean exceptionOccurred() {
192 return fatalException != null;
193 }
194
195 /**
196 * Get the exception that abnormally terminated the analysis.
197 */
198 public synchronized Exception getException() {
199 return fatalException;
200 }
201
202 /**
203 * This method is called from within the constructor to
204 * initialize the form.
205 * WARNING: Do NOT modify this code. The content of this method is
206 * always regenerated by the Form Editor.
207 */
208 private void initComponents() {//GEN-BEGIN:initComponents
209 java.awt.GridBagConstraints gridBagConstraints;
210
211 findBugsLabel = new javax.swing.JLabel();
212 countLabel = new javax.swing.JLabel();
213 progressLabel = new javax.swing.JLabel();
214 progressBar = new javax.swing.JProgressBar();
215 cancelButton = new javax.swing.JButton();
216 jSeparator1 = new javax.swing.JSeparator();
217 stageLabel = new javax.swing.JLabel();
218 stageNameLabel = new javax.swing.JLabel();
219 topVerticalFiller = new javax.swing.JLabel();
220 bottomVerticalFiller = new javax.swing.JLabel();
221 countValueLabel = new javax.swing.JLabel();
222
223 getContentPane().setLayout(new java.awt.GridBagLayout());
224
225 setTitle("Run Analysis");
226 this.setTitle(L10N.getLocalString("dlg.runanalysis_ttl", "Run Analysis"));
227 addWindowListener(new java.awt.event.WindowAdapter() {
228 @Override
229 public void windowClosing(java.awt.event.WindowEvent evt) {
230 closeDialog(evt);
231 }
232 @Override
233 public void windowOpened(java.awt.event.WindowEvent evt) {
234 formWindowOpened(evt);
235 }
236 });
237
238 findBugsLabel.setBackground(new java.awt.Color(0, 0, 204));
239 findBugsLabel.setFont(new java.awt.Font("Dialog", 1, 24));
240 findBugsLabel.setForeground(new java.awt.Color(255, 255, 255));
241 findBugsLabel.setText("Find Bugs!");
242 findBugsLabel.setOpaque(true);
243 gridBagConstraints = new java.awt.GridBagConstraints();
244 gridBagConstraints.gridwidth = 2;
245 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
246 gridBagConstraints.anchor = java.awt.GridBagConstraints.NORTH;
247 gridBagConstraints.weightx = 1.0;
248 gridBagConstraints.insets = new java.awt.Insets(0, 0, 3, 0);
249 getContentPane().add(findBugsLabel, gridBagConstraints);
250
251 countLabel.setFont(new java.awt.Font("Dialog", 0, 12));
252 countLabel.setText("Count:");
253 countLabel.setText(L10N.getLocalString("dlg.count_lbl", "Count:"));
254 gridBagConstraints = new java.awt.GridBagConstraints();
255 gridBagConstraints.gridx = 0;
256 gridBagConstraints.gridy = 3;
257 gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
258 gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
259 getContentPane().add(countLabel, gridBagConstraints);
260
261 progressLabel.setFont(new java.awt.Font("Dialog", 0, 12));
262 progressLabel.setText("Progress:");
263 progressLabel.setText(L10N.getLocalString("dlg.progress_lbl", "Progress:"));
264 gridBagConstraints = new java.awt.GridBagConstraints();
265 gridBagConstraints.gridx = 0;
266 gridBagConstraints.gridy = 5;
267 gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
268 gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
269 getContentPane().add(progressLabel, gridBagConstraints);
270
271 gridBagConstraints = new java.awt.GridBagConstraints();
272 gridBagConstraints.gridx = 1;
273 gridBagConstraints.gridy = 5;
274 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
275 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 3);
276 getContentPane().add(progressBar, gridBagConstraints);
277
278 cancelButton.setFont(new java.awt.Font("Dialog", 0, 12));
279 cancelButton.setText("Cancel");
280 cancelButton.setText(L10N.getLocalString("dlg.cancel_btn", "Cancel"));
281 cancelButton.addActionListener(new java.awt.event.ActionListener() {
282 public void actionPerformed(java.awt.event.ActionEvent evt) {
283 cancelButtonActionPerformed(evt);
284 }
285 });
286
287 gridBagConstraints = new java.awt.GridBagConstraints();
288 gridBagConstraints.gridx = 0;
289 gridBagConstraints.gridy = 8;
290 gridBagConstraints.gridwidth = 2;
291 gridBagConstraints.insets = new java.awt.Insets(3, 0, 3, 0);
292 getContentPane().add(cancelButton, gridBagConstraints);
293
294 gridBagConstraints = new java.awt.GridBagConstraints();
295 gridBagConstraints.gridx = 0;
296 gridBagConstraints.gridy = 7;
297 gridBagConstraints.gridwidth = 2;
298 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
299 getContentPane().add(jSeparator1, gridBagConstraints);
300
301 stageLabel.setFont(new java.awt.Font("Dialog", 0, 12));
302 stageLabel.setText("Stage:");
303 stageLabel.setText(L10N.getLocalString("dlg.stage_lbl", "Stage:"));
304 gridBagConstraints = new java.awt.GridBagConstraints();
305 gridBagConstraints.gridx = 0;
306 gridBagConstraints.gridy = 2;
307 gridBagConstraints.anchor = java.awt.GridBagConstraints.EAST;
308 gridBagConstraints.insets = new java.awt.Insets(3, 3, 3, 3);
309 getContentPane().add(stageLabel, gridBagConstraints);
310
311 stageNameLabel.setFont(new java.awt.Font("Dialog", 0, 12));
312 gridBagConstraints = new java.awt.GridBagConstraints();
313 gridBagConstraints.gridx = 1;
314 gridBagConstraints.gridy = 2;
315 gridBagConstraints.fill = java.awt.GridBagConstraints.HORIZONTAL;
316 getContentPane().add(stageNameLabel, gridBagConstraints);
317
318 gridBagConstraints = new java.awt.GridBagConstraints();
319 gridBagConstraints.gridx = 0;
320 gridBagConstraints.gridy = 6;
321 gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
322 gridBagConstraints.weighty = 0.5;
323 getContentPane().add(topVerticalFiller, gridBagConstraints);
324
325 gridBagConstraints = new java.awt.GridBagConstraints();
326 gridBagConstraints.gridx = 0;
327 gridBagConstraints.gridy = 1;
328 gridBagConstraints.fill = java.awt.GridBagConstraints.VERTICAL;
329 gridBagConstraints.weighty = 0.5;
330 getContentPane().add(bottomVerticalFiller, gridBagConstraints);
331
332 gridBagConstraints = new java.awt.GridBagConstraints();
333 gridBagConstraints.gridx = 1;
334 gridBagConstraints.gridy = 3;
335 gridBagConstraints.anchor = java.awt.GridBagConstraints.WEST;
336 gridBagConstraints.insets = new java.awt.Insets(0, 3, 0, 0);
337 getContentPane().add(countValueLabel, gridBagConstraints);
338
339 pack();
340 }//GEN-END:initComponents
341
342 private void cancelButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cancelButtonActionPerformed
343 int option = JOptionPane.showConfirmDialog(this, L10N.getLocalString("msg.cancelanalysis_txt", "Cancel analysis?"), L10N.getLocalString("msg.analyze_txt", "Analysis"),
344 JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
345
346 if (option == JOptionPane.YES_OPTION) {
347 // All we need to do to cancel the analysis is to interrupt
348 // the analysis thread.
349 analysisThread.interrupt();
350 }
351 }//GEN-LAST:event_cancelButtonActionPerformed
352
353 private void formWindowOpened(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_formWindowOpened
354 // Here is where we actually kick off the analysis thread.
355
356 // Lower the priority of the analysis thread to leave more
357 // CPU for interactive tasks.
358 analysisThread.setPriority(analysisPriority);
359
360 analysisThread.start();
361 }//GEN-LAST:event_formWindowOpened
362
363 /**
364 * Closes the dialog
365 */
366 private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog
367 setVisible(false);
368 dispose();
369 }//GEN-LAST:event_closeDialog
370
371 // Variables declaration - do not modify//GEN-BEGIN:variables
372 private javax.swing.JLabel bottomVerticalFiller;
373 private javax.swing.JButton cancelButton;
374 private javax.swing.JLabel countLabel;
375 private javax.swing.JLabel countValueLabel;
376 private javax.swing.JLabel findBugsLabel;
377 private javax.swing.JSeparator jSeparator1;
378 private javax.swing.JProgressBar progressBar;
379 private javax.swing.JLabel progressLabel;
380 private javax.swing.JLabel stageLabel;
381 private javax.swing.JLabel stageNameLabel;
382 private javax.swing.JLabel topVerticalFiller;
383 // End of variables declaration//GEN-END:variables
384
385 }
+0
-107
src/obsolete/edu/umd/cs/findbugs/gui/SwingGUIBugReporter.java less more
0 /*
1 * FindBugs - Find bugs in Java programs
2 * Copyright (C) 2003-2005, University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18 package edu.umd.cs.findbugs.gui;
19
20 import edu.umd.cs.findbugs.SortedBugCollection;
21 import edu.umd.cs.findbugs.TextUIBugReporter;
22 import edu.umd.cs.findbugs.classfile.ClassDescriptor;
23 import edu.umd.cs.findbugs.log.ConsoleLogger;
24
25 /**
26 * BugReporter used by AnalysisRun.
27 */
28 public class SwingGUIBugReporter extends TextUIBugReporter {
29 private final AnalysisRun analysisRun;
30 private SortedBugCollection bugCollection;
31 private AnalysisErrorDialog errorDialog;
32 private int errorCount;
33
34 /**
35 * Constructor.
36 *
37 * @param analysisRun
38 */
39 public SwingGUIBugReporter(AnalysisRun analysisRun) {
40 this.analysisRun = analysisRun;
41 this.bugCollection = new SortedBugCollection(getProjectStats());
42 }
43
44 public SortedBugCollection getBugCollection() {
45 return bugCollection;
46 }
47
48 public boolean errorsOccurred() {
49 return errorCount > 0;
50 }
51
52 public AnalysisErrorDialog getErrorDialog() {
53 return errorDialog;
54 }
55
56 public void observeClass(ClassDescriptor classDescriptor) {
57 }
58
59 @Override
60 public void reportMissingClass(ClassNotFoundException ex) {
61 ++errorCount;
62 super.reportMissingClass(ex);
63 String message = getMissingClassName(ex);
64 bugCollection.addMissingClass(message);
65 }
66
67 @Override
68 public void logError(String message) {
69 ++errorCount;
70 analysisRun.getFrame().getLogger().logMessage(ConsoleLogger.WARNING, message);
71 super.logError(message);
72 bugCollection.addError(message);
73 }
74
75 public void finish() {
76 }
77
78 @Override
79 public void doReportBug(edu.umd.cs.findbugs.BugInstance bugInstance) {
80 checkBugInstance(bugInstance);
81 if (bugCollection.add(bugInstance))
82 notifyObservers(bugInstance);
83 }
84
85 private void createDialog() {
86 if (errorDialog == null) {
87 errorDialog = new AnalysisErrorDialog(analysisRun.getFrame(), true, this);
88 }
89 }
90
91
92 @Override
93 public void reportQueuedErrors() {
94 createDialog();
95 errorDialog.clear();
96 super.reportQueuedErrors();
97 errorDialog.finish();
98 }
99
100
101 @Override
102 protected void emitLine(String line) {
103 line = line.replaceAll("\t", " ");
104 errorDialog.addLine(line);
105 }
106 }
src/obsolete/edu/umd/cs/findbugs/gui/bug-logo.png less more
Binary diff not shown
src/obsolete/edu/umd/cs/findbugs/gui/bug.png less more
Binary diff not shown
src/obsolete/edu/umd/cs/findbugs/gui/bug2.png less more
Binary diff not shown
src/obsolete/edu/umd/cs/findbugs/gui/class.png less more
Binary diff not shown
src/obsolete/edu/umd/cs/findbugs/gui/down.png less more
Binary diff not shown
src/obsolete/edu/umd/cs/findbugs/gui/field.png less more
Binary diff not shown
src/obsolete/edu/umd/cs/findbugs/gui/informal.png less more
Binary diff not shown
src/obsolete/edu/umd/cs/findbugs/gui/logo_umd.png less more
Binary diff not shown
src/obsolete/edu/umd/cs/findbugs/gui/method.png less more
Binary diff not shown
src/obsolete/edu/umd/cs/findbugs/gui/package.png less more
Binary diff not shown
src/obsolete/edu/umd/cs/findbugs/gui/sourcefile.png less more
Binary diff not shown
src/obsolete/edu/umd/cs/findbugs/gui/up.png less more
Binary diff not shown
+0
-936
src/obsolete/edu/umd/cs/findbugs/ml/ConvertToARFF.java less more
0 /*
1 * Machine Learning support for FindBugs
2 * Copyright (C) 2004,2005 University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.ml;
20
21 import java.io.BufferedOutputStream;
22 import java.io.FileOutputStream;
23 import java.io.IOException;
24 import java.io.OutputStreamWriter;
25 import java.io.PrintStream;
26 import java.io.Writer;
27 import java.util.ArrayList;
28 import java.util.Collection;
29 import java.util.IdentityHashMap;
30 import java.util.Iterator;
31 import java.util.LinkedList;
32 import java.util.List;
33 import java.util.Random;
34 import java.util.Set;
35 import java.util.StringTokenizer;
36 import java.util.TreeSet;
37
38 import org.dom4j.Document;
39 import org.dom4j.Element;
40 import org.dom4j.Node;
41 import org.dom4j.io.SAXReader;
42
43 import edu.umd.cs.findbugs.config.CommandLine;
44
45 /**
46 * Convert a BugCollection into ARFF format. See Witten and Frank,
47 * <em>Data Mining</em>, ISBN 1-55860-552-5.
48 *
49 * @see edu.umd.cs.findbugs.BugCollection
50 * @see edu.umd.cs.findbugs.BugInstance
51 * @author David Hovemeyer
52 */
53 public class ConvertToARFF {
54 // ------------------------------------------------------------
55 // Helper classes
56 // ------------------------------------------------------------
57
58 private static class DataFile {
59 private Document document;
60
61 private String appName;
62
63 public DataFile(Document document, String appName) {
64 this.document = document;
65 this.appName = appName;
66 }
67
68 public Document getDocument() {
69 return document;
70 }
71
72 public String getAppName() {
73 return appName;
74 }
75 }
76
77 private static class MissingNodeException extends Exception {
78 private static final long serialVersionUID = -5042140832791541208L;
79
80 public MissingNodeException(String msg) {
81 super(msg);
82 }
83 }
84
85 public interface Attribute {
86 public String getName();
87
88 public void scan(Element element, String appName) throws MissingNodeException;
89
90 public String getRange();
91
92 public String getInstanceValue(Element element, String appName) throws MissingNodeException;
93 }
94
95 private abstract static class XPathAttribute implements Attribute {
96 private String name;
97
98 private String xpath;
99
100 public XPathAttribute(String name, String xpath) {
101 this.name = name;
102 this.xpath = xpath;
103 }
104
105 public String getName() {
106 return name;
107 }
108
109 public String getInstanceValue(Element element, String appName) throws MissingNodeException {
110 Object value = element.selectObject(xpath);
111 if (value == null)
112 throw new MissingNodeException("Could not get value from element (path=" + xpath + ")");
113 if (value instanceof List) {
114 List<?> list = (List<?>) value;
115 if (list.size() == 0)
116 throw new MissingNodeException("Could not get value from element (path=" + xpath + ")");
117 value = list.get(0);
118 }
119
120 if (value instanceof Node) {
121 Node node = (Node) value;
122 return node.getText();
123 } else if (value instanceof String) {
124 return (String) value;
125 } else if (value instanceof Number) {
126 String s = value.toString();
127 if (s.endsWith(".0"))
128 s = s.substring(0, s.length() - 2);
129 return s;
130 } else
131 throw new IllegalStateException("Unexpected object returned from xpath query: " + value);
132 }
133 }
134
135 public static class NominalAttribute extends XPathAttribute {
136 private Set<String> possibleValueSet;
137
138 public NominalAttribute(String name, String xpath) {
139 super(name, xpath);
140 this.possibleValueSet = new TreeSet<String>();
141 }
142
143 public void scan(Element element, String appName) {
144 try {
145 possibleValueSet.add(getInstanceValue(element, appName));
146 } catch (MissingNodeException ignore) {
147 // Ignore: we'll just use an n/a value for this instance
148 }
149 }
150
151 public String getRange() {
152 return collectionToRange(possibleValueSet);
153 }
154
155 @Override
156 public String getInstanceValue(Element element, String appName) throws MissingNodeException {
157 return "\"" + super.getInstanceValue(element, appName) + "\"";
158 }
159 }
160
161 public static class BooleanAttribute extends XPathAttribute {
162 public BooleanAttribute(String name, String xpath) {
163 super(name, xpath);
164 }
165
166 public void scan(Element element, String appName) throws MissingNodeException {
167 // Nothing to do.
168 }
169
170 public String getRange() {
171 return "{true, false}";
172 }
173
174 @Override
175 public String getInstanceValue(Element element, String appName) throws MissingNodeException {
176 try {
177 String value = super.getInstanceValue(element, appName);
178 return "\"" + Boolean.valueOf(value).toString() + "\"";
179 } catch (MissingNodeException e) {
180 return "\"false\"";
181 }
182 }
183 }
184
185 private static final int UNCLASSIFIED = 0;
186
187 private static final int BUG = 1;
188
189 private static final int NOT_BUG = 2;
190
191 private static final int HARMLESS = 4;
192
193 private static final int HARMLESS_BUG = HARMLESS | BUG;
194
195 public static abstract class AbstractClassificationAttribute implements Attribute {
196
197 /*
198 * (non-Javadoc)
199 *
200 * @see edu.umd.cs.findbugs.ml.ConvertToARFF.Attribute#getName()
201 */
202 public String getName() {
203 return "classification";
204 }
205
206 /*
207 * (non-Javadoc)
208 *
209 * @see
210 * edu.umd.cs.findbugs.ml.ConvertToARFF.Attribute#scan(org.dom4j.Element
211 * , java.lang.String)
212 */
213 public void scan(Element element, String appName) throws MissingNodeException {
214 }
215
216 /*
217 * (non-Javadoc)
218 *
219 * @see
220 * edu.umd.cs.findbugs.ml.ConvertToARFF.Attribute#getInstanceValue(org
221 * .dom4j.Element, java.lang.String)
222 */
223 public String getInstanceValue(Element element, String appName) throws MissingNodeException {
224 String annotationText = element.valueOf("./UserAnnotation[text()]");
225 // System.out.println("annotationText=" + annotationText);
226
227 int state = getBugClassification(annotationText);
228 return bugToString(state);
229 }
230
231 protected abstract String bugToString(int bugType) throws MissingNodeException;
232
233 }
234
235 public static class ClassificationAttribute extends AbstractClassificationAttribute {
236 public String getRange() {
237 return "{bug,not_bug,harmless_bug}";
238 }
239
240 @Override
241 protected String bugToString(int state) throws MissingNodeException {
242 if (state == NOT_BUG)
243 return "not_bug";
244 else if (state == BUG)
245 return "bug";
246 else if (state == HARMLESS_BUG)
247 return "harmless_bug";
248 else
249 throw new MissingNodeException("Unclassified warning");
250
251 }
252 }
253
254 public static class BinaryClassificationAttribute extends AbstractClassificationAttribute {
255 /*
256 * (non-Javadoc)
257 *
258 * @see edu.umd.cs.findbugs.ml.ConvertToARFF.Attribute#getRange()
259 */
260 public String getRange() {
261 return "{bug, not_bug}";
262 }
263
264 /*
265 * (non-Javadoc)
266 *
267 * @see
268 * edu.umd.cs.findbugs.ml.ConvertToARFF.AbstractClassificationAttribute
269 * #bugToString(int)
270 */
271 @Override
272 protected String bugToString(int state) throws MissingNodeException {
273 if (state == BUG)
274 return "bug";
275 else if (state == NOT_BUG || state == HARMLESS_BUG)
276 return "not_bug";
277 else
278 throw new MissingNodeException("unclassified warning");
279 }
280 }
281
282 public static class NumericAttribute extends XPathAttribute {
283 public NumericAttribute(String name, String xpath) {
284 super(name, xpath);
285 }
286
287 public void scan(Element element, String appName) throws MissingNodeException {
288 }
289
290 public String getRange() {
291 return "numeric";
292 }
293 }
294
295 public static class PriorityAttribute implements Attribute {
296 public String getName() {
297 return "priority";
298 }
299
300 public void scan(Element element, String appName) throws MissingNodeException {
301 }
302
303 public String getRange() {
304 return "{low,medium,high}";
305 }
306
307 public String getInstanceValue(Element element, String appName) throws MissingNodeException {
308 org.dom4j.Attribute attribute = element.attribute("priority");
309 if (attribute == null)
310 throw new MissingNodeException("Missing priority attribute");
311 String value = attribute.getValue();
312 try {
313 int prio = Integer.parseInt(value);
314 switch (prio) {
315 case 1:
316 return "high";
317 case 2:
318 return "medium";
319 case 3:
320 return "low";
321 default:
322 return "?";
323 }
324 } catch (NumberFormatException e) {
325 throw new MissingNodeException("Invalid priority value: " + value);
326 }
327 }
328 }
329
330 /**
331 * An attribute that just gives each instance a unique id. The application
332 * name is prepended, so each unique id really unique, even across
333 * applications. Obviously, this attribute shouldn't be used as input to a
334 * learning algorithm.
335 *
336 * <p>
337 * Uses the Element's uid attribute if it has one.
338 * </p>
339 */
340 public static class IdAttribute implements Attribute {
341 private TreeSet<String> possibleValueSet = new TreeSet<String>();
342
343 private boolean scanning = true;
344
345 private int count = 0;
346
347 public String getName() {
348 return "id";
349 }
350
351 public void scan(Element element, String appName) throws MissingNodeException {
352 possibleValueSet.add(instanceValue(element, appName));
353 }
354
355 public String getRange() {
356 return collectionToRange(possibleValueSet);
357 }
358
359 public String getInstanceValue(Element element, String appName) throws MissingNodeException {
360 if (scanning) {
361 count = 0;
362 scanning = false;
363 }
364 return instanceValue(element, appName);
365 }
366
367 private String instanceValue(Element element, String appName) {
368 String nextId;
369
370 org.dom4j.Attribute uidAttr = element.attribute("uid");
371 if (uidAttr != null) {
372 nextId = uidAttr.getValue();
373 } else {
374 nextId = String.valueOf(count++);
375 }
376
377 return "\"" + appName + "-" + nextId + "\"";
378 }
379 }
380
381 public static class IdStringAttribute implements Attribute {
382
383 /*
384 * (non-Javadoc)
385 *
386 * @see edu.umd.cs.findbugs.ml.ConvertToARFF.Attribute#getName()
387 */
388 public String getName() {
389 return "ids";
390 }
391
392 /*
393 * (non-Javadoc)
394 *
395 * @see
396 * edu.umd.cs.findbugs.ml.ConvertToARFF.Attribute#scan(org.dom4j.Element
397 * , java.lang.String)
398 */
399 public void scan(Element element, String appName) throws MissingNodeException {
400 }
401
402 /*
403 * (non-Javadoc)
404 *
405 * @see edu.umd.cs.findbugs.ml.ConvertToARFF.Attribute#getRange()
406 */
407 public String getRange() {
408 return "string";
409 }
410
411 int count = 0;
412
413 /*
414 * (non-Javadoc)
415 *
416 * @see
417 * edu.umd.cs.findbugs.ml.ConvertToARFF.Attribute#getInstanceValue(org
418 * .dom4j.Element, java.lang.String)
419 */
420 public String getInstanceValue(Element element, String appName) throws MissingNodeException {
421 String value;
422 org.dom4j.Attribute uidAttr = element.attribute("uid");
423 if (uidAttr == null) {
424 value = String.valueOf(count++);
425 } else {
426 value = uidAttr.getStringValue();
427 }
428
429 return "\"" + appName + "-" + value + "\"";
430 }
431
432 }
433
434 private static final String RANDOM_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
435
436 public static class RandomIdAttribute implements Attribute {
437
438 private Random rng = new Random();
439
440 private IdentityHashMap<Element, String> idMap = new IdentityHashMap<Element, String>();
441
442 /*
443 * (non-Javadoc)
444 *
445 * @see edu.umd.cs.findbugs.ml.ConvertToARFF.Attribute#getName()
446 */
447 public String getName() {
448 return "idr";
449 }
450
451 /*
452 * (non-Javadoc)
453 *
454 * @see
455 * edu.umd.cs.findbugs.ml.ConvertToARFF.Attribute#scan(org.dom4j.Element
456 * , java.lang.String)
457 */
458 public void scan(Element element, String appName) throws MissingNodeException {
459 idMap.put(element, generateId());
460 }
461
462 private String generateId() {
463 StringBuilder buf = new StringBuilder();
464
465 for (int i = 0; i < 20; ++i) {
466 char c = RANDOM_CHARS.charAt(rng.nextInt(RANDOM_CHARS.length()));
467 buf.append(c);
468 }
469
470 return buf.toString();
471 }
472
473 /*
474 * (non-Javadoc)
475 *
476 * @see edu.umd.cs.findbugs.ml.ConvertToARFF.Attribute#getRange()
477 */
478 public String getRange() {
479 TreeSet<String> range = new TreeSet<String>();
480 range.addAll(idMap.values());
481 if (range.size() != idMap.size())
482 throw new IllegalStateException("id collision!");
483 return collectionToRange(range);
484 }
485
486 /*
487 * (non-Javadoc)
488 *
489 * @see
490 * edu.umd.cs.findbugs.ml.ConvertToARFF.Attribute#getInstanceValue(org
491 * .dom4j.Element, java.lang.String)
492 */
493 public String getInstanceValue(Element element, String appName) throws MissingNodeException {
494 String id = idMap.get(element);
495 if (id == null)
496 throw new IllegalStateException("Element not scanned?");
497 return "\"" + id + "\"";
498 }
499
500 }
501
502 public static class AppNameAttribute implements Attribute {
503 private Set<String> appNameSet = new TreeSet<String>();
504
505 public String getName() {
506 return "appname";
507 }
508
509 public void scan(Element element, String appName) throws MissingNodeException {
510 appNameSet.add(appName);
511 }
512
513 public String getRange() {
514 return collectionToRange(appNameSet);
515 }
516
517 public String getInstanceValue(Element element, String appName) throws MissingNodeException {
518 return "\"" + appName + "\"";
519 }
520 }
521
522 public static String collectionToRange(Collection<String> collection) {
523 StringBuilder buf = new StringBuilder();
524 buf.append("{");
525 for (String aCollection : collection) {
526 if (buf.length() > 1)
527 buf.append(',');
528 buf.append(aCollection);
529 }
530 buf.append("}");
531
532 return buf.toString();
533 }
534
535 public interface AttributeCallback {
536 public void apply(Attribute attribute) throws MissingNodeException, IOException;
537 }
538
539 // ------------------------------------------------------------
540 // Constants
541 // ------------------------------------------------------------
542
543 private static final String DEFAULT_NODE_SELECTION_XPATH = "/BugCollection/BugInstance";
544
545 // ------------------------------------------------------------
546 // Fields
547 // ------------------------------------------------------------
548
549 private List<Attribute> attributeList;
550
551 private String nodeSelectionXpath;
552
553 private boolean dropUnclassifiedWarnings;
554
555 private String appName;
556
557 // ------------------------------------------------------------
558 // Public methods
559 // ------------------------------------------------------------
560
561 public ConvertToARFF() {
562 this.attributeList = new LinkedList<Attribute>();
563 this.nodeSelectionXpath = DEFAULT_NODE_SELECTION_XPATH;
564 this.dropUnclassifiedWarnings = false;
565 }
566
567 public void setAppName(String appName) {
568 this.appName = appName;
569 }
570
571 /**
572 * Set the xpath expression used to select BugInstance nodes.
573 *
574 * @param nodeSelectionXpath
575 * the node selection xpath expression
576 */
577 public void setNodeSelectionXpath(String nodeSelectionXpath) {
578 this.nodeSelectionXpath = nodeSelectionXpath;
579 }
580
581 public int getNumAttributes() {
582 return attributeList.size();
583 }
584
585 public void dropUnclassifiedWarnings() {
586 this.dropUnclassifiedWarnings = true;
587 }
588
589 public void addAttribute(Attribute attribute) {
590 attributeList.add(attribute);
591 }
592
593 public void addNominalAttribute(String name, String xpath) {
594 addAttribute(new NominalAttribute(name, xpath));
595 }
596
597 public void addBooleanAttribute(String name, String xpath) {
598 addAttribute(new BooleanAttribute(name, xpath));
599 }
600
601 public void addClassificationAttribute() {
602 addAttribute(new ClassificationAttribute());
603 }
604
605 public void addNumericAttribute(String name, String xpath) {
606 addAttribute(new NumericAttribute(name, xpath));
607 }
608
609 public void addPriorityAttribute() {
610 addAttribute(new PriorityAttribute());
611 }
612
613 public void addIdAttribute() {
614 addAttribute(new IdAttribute());
615 }
616
617 public void addAppNameAttribute() {
618 addAttribute(new AppNameAttribute());
619 }
620
621 /**
622 * Convert a single Document to ARFF format.
623 *
624 * @param relationName
625 * the relation name
626 * @param document
627 * the Document
628 * @param appName
629 * the application name
630 * @param out
631 * Writer to write the ARFF output to
632 */
633 public void convert(String relationName, Document document, String appName, final Writer out) throws IOException,
634 MissingNodeException {
635 scan(document, appName);
636 generateHeader(relationName, out);
637 generateInstances(document, appName, out);
638 }
639
640 /**
641 * Scan a Document to find out the ranges of attributes. All Documents must
642 * be scanned before generating the ARFF header and instances.
643 *
644 * @param document
645 * the Document
646 * @param appName
647 * the application name
648 */
649 public void scan(Document document, final String appName) throws MissingNodeException, IOException {
650 List<Element> bugInstanceList = getBugInstanceList(document);
651
652 for (final Element element : bugInstanceList) {
653 scanAttributeList(new AttributeCallback() {
654 public void apply(Attribute attribute) throws MissingNodeException {
655 attribute.scan(element, appName);
656 }
657 });
658 }
659 }
660
661 /**
662 * Generate ARFF header. Documents must have already been scanned.
663 *
664 * @param relationName
665 * the relation name
666 * @param out
667 * Writer to write the ARFF output to
668 */
669 public void generateHeader(String relationName, final Writer out) throws MissingNodeException, IOException {
670 out.write("@relation ");
671 out.write(relationName);
672 out.write("\n\n");
673
674 scanAttributeList(new AttributeCallback() {
675 public void apply(Attribute attribute) throws IOException {
676 out.write("@attribute ");
677 out.write(attribute.getName());
678 out.write(" ");
679 out.write(attribute.getRange());
680 out.write("\n");
681 }
682 });
683 out.write("\n");
684
685 out.write("@data\n");
686 }
687
688 /**
689 * Generate instances from given Document. Document should already have been
690 * scanned, and the ARFF header generated.
691 *
692 * @param document
693 * the Document
694 * @param appName
695 * the application name
696 * @param out
697 * Writer to write the ARFF output to
698 */
699 public void generateInstances(Document document, final String appName, final Writer out) throws MissingNodeException,
700 IOException {
701 List<Element> bugInstanceList = getBugInstanceList(document);
702
703 for (final Element element : bugInstanceList) {
704 scanAttributeList(new AttributeCallback() {
705 boolean first = true;
706
707 public void apply(Attribute attribute) throws IOException {
708 if (!first)
709 out.write(",");
710 first = false;
711 String value;
712 try {
713 value = attribute.getInstanceValue(element, appName);
714 } catch (MissingNodeException e) {
715 value = "?";
716 }
717 out.write(value);
718 }
719 });
720 out.write("\n");
721 }
722 }
723
724 /**
725 * Apply a callback to all Attributes.
726 *
727 * @param callback
728 * the callback
729 */
730 public void scanAttributeList(AttributeCallback callback) throws MissingNodeException, IOException {
731 for (Attribute attribute : attributeList) {
732 callback.apply(attribute);
733 }
734 }
735
736 // ------------------------------------------------------------
737 // Implementation
738 // ------------------------------------------------------------
739
740 private static int getBugClassification(String annotationText) {
741 StringTokenizer tok = new StringTokenizer(annotationText, " \t\r\n\f.,:;-");
742
743 int state = UNCLASSIFIED;
744
745 while (tok.hasMoreTokens()) {
746 String s = tok.nextToken();
747 if (s.equals("BUG"))
748 state |= BUG;
749 else if (s.equals("NOT_BUG"))
750 state |= NOT_BUG;
751 else if (s.equals("HARMLESS"))
752 state |= HARMLESS;
753 }
754
755 if ((state & NOT_BUG) != 0)
756 return NOT_BUG;
757 else if ((state & BUG) != 0)
758 return ((state & HARMLESS) != 0) ? HARMLESS_BUG : BUG;
759 else
760 return UNCLASSIFIED;
761 }
762
763 @SuppressWarnings("unchecked")
764 private List<Element> getBugInstanceList(Document document) {
765 List<Element> bugInstanceList = document.selectNodes(nodeSelectionXpath);
766 if (dropUnclassifiedWarnings) {
767 for (Iterator<Element> i = bugInstanceList.iterator(); i.hasNext();) {
768 Element element = i.next();
769 String annotationText = element.valueOf("./UserAnnotation[text()]");
770 int classification = getBugClassification(annotationText);
771 if (classification == UNCLASSIFIED)
772 i.remove();
773 }
774 }
775 return bugInstanceList;
776 }
777
778 private static class C2ACommandLine extends CommandLine {
779 private ConvertToARFF converter = new ConvertToARFF();
780
781 public C2ACommandLine() {
782 addOption("-select", "xpath expression", "select BugInstance elements");
783 addSwitch("-train", "drop unclassified warnings");
784 addSwitch("-id", "add unique id attribute (as nominal)");
785 addSwitch("-ids", "add unique id attribute (as string)");
786 addSwitch("-idr", "add random unique id attribtue (as nominal)");
787 addSwitch("-app", "add application name attribute");
788 addOption("-nominal", "attrName,xpath", "add a nominal attribute");
789 addOption("-boolean", "attrName,xpath", "add a boolean attribute");
790 addOption("-numeric", "attrName,xpath", "add a numeric attribute");
791 addSwitch("-classification", "add bug classification attribute");
792 addSwitch("-binclass", "add binary (bug/not_bug) classification attribute");
793 addSwitch("-priority", "add priority attribute");
794 addOption("-appname", "app name", "set application name of all tuples");
795 }
796
797 public ConvertToARFF getConverter() {
798 return converter;
799 }
800
801 @Override
802 protected void handleOption(String option, String optionExtraPart) throws IOException {
803 if (option.equals("-train")) {
804 converter.dropUnclassifiedWarnings();
805 } else if (option.equals("-id")) {
806 converter.addIdAttribute();
807 } else if (option.equals("-ids")) {
808 converter.addAttribute(new IdStringAttribute());
809 } else if (option.equals("-idr")) {
810 converter.addAttribute(new RandomIdAttribute());
811 } else if (option.equals("-app")) {
812 converter.addAppNameAttribute();
813 } else if (option.equals("-classification")) {
814 converter.addClassificationAttribute();
815 } else if (option.equals("-binclass")) {
816 converter.addAttribute(new BinaryClassificationAttribute());
817 } else if (option.equals("-priority")) {
818 converter.addPriorityAttribute();
819 }
820 }
821
822 private interface XPathAttributeCreator {
823 public Attribute create(String name, String xpath);
824 }
825
826 @Override
827 protected void handleOptionWithArgument(String option, String argument) throws IOException {
828
829 if (option.equals("-select")) {
830 converter.setNodeSelectionXpath(argument);
831 } else if (option.equals("-nominal")) {
832 addXPathAttribute(option, argument, new XPathAttributeCreator() {
833 public Attribute create(String name, String xpath) {
834 return new NominalAttribute(name, xpath);
835 }
836 });
837 } else if (option.equals("-boolean")) {
838 addXPathAttribute(option, argument, new XPathAttributeCreator() {
839 public Attribute create(String name, String xpath) {
840 return new BooleanAttribute(name, xpath);
841 }
842 });
843 } else if (option.equals("-numeric")) {
844 addXPathAttribute(option, argument, new XPathAttributeCreator() {
845 public Attribute create(String name, String xpath) {
846 return new NumericAttribute(name, xpath);
847 }
848 });
849 } else if (option.equals("-appname")) {
850 converter.setAppName(argument);
851 }
852 }
853
854 protected void addXPathAttribute(String option, String argument, XPathAttributeCreator creator) {
855 int comma = argument.indexOf(',');
856 if (comma < 0) {
857 throw new IllegalArgumentException("Missing comma separating attribute name and xpath in " + option + " option: "
858 + argument);
859 }
860 String attrName = argument.substring(0, comma);
861 String xpath = argument.substring(comma + 1);
862 converter.addAttribute(creator.create(attrName, xpath));
863 }
864
865 public void printUsage(PrintStream out) {
866 out.println("Usage: " + ConvertToARFF.class.getName()
867 + " [options] <relation name> <output file> <findbugs results> [<findbugs results>...]");
868 super.printUsage(out);
869 }
870 }
871
872 public String toAppName(String fileName) {
873 if (appName != null)
874 return appName;
875
876 // Remove file extension, if any
877 int lastDot = fileName.lastIndexOf('.');
878 if (lastDot >= 0)
879 fileName = fileName.substring(0, lastDot);
880 return fileName;
881 }
882
883 public static void main(String[] argv) throws Exception {
884 // Expand any option files
885 C2ACommandLine commandLine = new C2ACommandLine();
886 argv = commandLine.expandOptionFiles(argv, true, true);
887
888 // Parse command line arguments
889 int argCount = commandLine.parse(argv);
890 if (argCount > argv.length - 3) {
891 commandLine.printUsage(System.err);
892 System.exit(1);
893 }
894 String relationName = argv[argCount++];
895 String outputFileName = argv[argCount++];
896
897 // Create the converter
898 ConvertToARFF converter = commandLine.getConverter();
899 if (converter.getNumAttributes() == 0) {
900 throw new IllegalArgumentException("No attributes specified!");
901 }
902
903 // Open output file
904 Writer out = new OutputStreamWriter(new BufferedOutputStream(new FileOutputStream(outputFileName)));
905
906 // Read documents,
907 // scan documents to find ranges of attributes
908 List<DataFile> dataFileList = new ArrayList<DataFile>();
909 while (argCount < argv.length) {
910 String fileName = argv[argCount++];
911
912 // Read input file as dom4j tree
913 SAXReader reader = new SAXReader();
914 Document document = reader.read(fileName);
915
916 DataFile dataFile = new DataFile(document, converter.toAppName(fileName));
917 dataFileList.add(dataFile);
918
919 converter.scan(dataFile.getDocument(), dataFile.getAppName());
920 }
921
922 // Generate ARFF header
923 converter.generateHeader(relationName, out);
924
925 // Generate instances from each document
926 for (DataFile dataFile : dataFileList) {
927 converter.generateInstances(dataFile.getDocument(), dataFile.getAppName(), out);
928 }
929
930 out.close();
931 }
932
933 }
934
935 // vim:ts=4
+0
-128
src/obsolete/edu/umd/cs/findbugs/ml/GenerateUIDs.java less more
0 /*
1 * Machine Learning support for FindBugs
2 * Copyright (C) 2005, University of Maryland
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 */
18
19 package edu.umd.cs.findbugs.ml;
20
21 import java.io.BufferedInputStream;
22 import java.io.BufferedOutputStream;
23 import java.io.FileInputStream;
24 import java.io.FileOutputStream;
25 import java.io.IOException;
26 import java.io.InputStream;
27 import java.io.OutputStream;
28 import java.util.List;
29 import java.util.zip.GZIPInputStream;
30
31 import org.dom4j.Attribute;
32 import org.dom4j.Document;
33 import org.dom4j.DocumentException;
34 import org.dom4j.DocumentFactory;
35 import org.dom4j.Element;
36 import org.dom4j.io.OutputFormat;
37 import org.dom4j.io.XMLWriter;
38
39 import edu.umd.cs.findbugs.BugCollection;
40 import edu.umd.cs.findbugs.Project;
41 import edu.umd.cs.findbugs.SortedBugCollection;
42 import edu.umd.cs.findbugs.annotations.NonNull;
43 import edu.umd.cs.findbugs.xml.Dom4JXMLOutput;
44
45 /**
46 * Add uid attributes to BugInstances in a BugCollection. A uid is an integer
47 * that uniquely identifies a BugInstance in a BugCollection. Right now this is
48 * only used in machine learning experiments.
49 *
50 * @author David Hovemeyer
51 */
52 public class GenerateUIDs {
53 private BugCollection bugCollection;
54
55 @NonNull
56 private Project project;
57
58 private String inputFilename;
59
60 private String outputFilename;
61
62 public GenerateUIDs(String inputFilename, String outputFilename) {
63 this.bugCollection = new SortedBugCollection();
64 this.inputFilename = inputFilename;
65 this.outputFilename = outputFilename;
66 }
67
68 @SuppressWarnings("unchecked")
69 public void execute() throws IOException, DocumentException {
70 InputStream in = null;
71 try {
72 if (inputFilename.equals("-")) {
73 in = System.in;
74 } else {
75 in = new BufferedInputStream(new FileInputStream(inputFilename));
76 if (inputFilename.endsWith(".gz"))
77 in = new GZIPInputStream(in);
78 }
79
80 bugCollection.readXML(in);
81 in = null;
82 } finally {
83 if (in != null)
84 in.close();
85 }
86 Document document = DocumentFactory.getInstance().createDocument();
87 Dom4JXMLOutput xmlOutput = new Dom4JXMLOutput(document);
88 bugCollection.writeXML(xmlOutput);
89
90 int count = 0;
91
92 List<Element> bugInstanceList = document.selectNodes("/BugCollection/BugInstance");
93 for (Element element : bugInstanceList) {
94 Attribute uidAttr = element.attribute("uid");
95 if (uidAttr == null) {
96 element.addAttribute("uid", Integer.toString(count++));
97 }
98 }
99
100 OutputStream out;
101 if (outputFilename.equals("-")) {
102 out = System.out;
103 } else {
104 out = new BufferedOutputStream(new FileOutputStream(outputFilename));
105 }
106
107 XMLWriter xmlWriter = new XMLWriter(out, OutputFormat.createPrettyPrint());
108 try {
109 xmlWriter.write(document);
110 } finally {
111 xmlWriter.close();
112 }
113 }
114
115 public static void main(String[] args) throws IOException, DocumentException {
116 if (args.length != 2) {
117 System.err.println("Usage: " + GenerateUIDs.class.getName() + " <input file> <output file>");
118 System.exit(1);
119 }
120
121 String inputFilename = args[0];
122 String outputFilename = args[1];
123
124 GenerateUIDs generateUIDs = new GenerateUIDs(inputFilename, outputFilename);
125 generateUIDs.execute();
126 }
127 }
2828 /**
2929 * Verify that a set of jar files are compiled for Java 5.0, the release
3030 * standard for FindBugs
31 *
31 *
3232 */
3333 public class CheckClassfileVersion {
3434
4343 ArrayList<File> s = new ArrayList<File>(args.length);
4444 for (String f : args) {
4545 File file = new File(f);
46 if (!file.canRead())
46 if (!file.canRead()) {
4747 System.out.println("Can't read " + f);
48 }
4849 if (file.isDirectory()) {
49 for (File f2 : file.listFiles())
50 if (isJarFile(f2))
50 for (File f2 : file.listFiles()) {
51 if (isJarFile(f2)) {
5152 s.add(f2);
52 } else if (isJarFile(file))
53 }
54 }
55 } else if (isJarFile(file)) {
5356 s.add(file);
57 }
5458 }
5559
5660 for (File jarFile : s) {
5761 String jarFileName = jarFile.getName();
5862 System.out.println("Checking " + jarFileName);
59 JarFile z = new JarFile(jarFile);
60 for (Enumeration<JarEntry> e = z.entries(); e.hasMoreElements();) {
61 JarEntry ze = e.nextElement();
62 if (ze.isDirectory())
63 continue;
63 try(JarFile z = new JarFile(jarFile)){
64 for (Enumeration<JarEntry> e = z.entries(); e.hasMoreElements();) {
65 JarEntry ze = e.nextElement();
66 if (ze.isDirectory()) {
67 continue;
68 }
6469
65 String name = ze.getName();
66 boolean isClassFile = name.endsWith(".class");
67 if (!isClassFile)
68 continue;
70 String name = ze.getName();
71 boolean isClassFile = name.endsWith(".class");
72 if (!isClassFile) {
73 continue;
74 }
6975
70 DataInputStream zipIn = new DataInputStream(z.getInputStream(ze));
71 int magic = zipIn.readInt();
72 int minorVersion = zipIn.readUnsignedShort();
73 int majorVersion = zipIn.readUnsignedShort();
74 if (magic != 0xCAFEBABE) {
75 System.out.printf("bad magic %x: %s %s%n", magic, jarFileName, name);
76 fail = true;
77 } else if (minorVersion >= 60) {
78 System.out.printf("bad version %d:%s %s%n", minorVersion, jarFileName, name);
79 fail = true;
76 DataInputStream zipIn = new DataInputStream(z.getInputStream(ze));
77 int magic = zipIn.readInt();
78 int minorVersion = zipIn.readUnsignedShort();
79 // int majorVersion = zipIn.readUnsignedShort();
80 if (magic != 0xCAFEBABE) {
81 System.out.printf("bad magic %x: %s %s%n", magic, jarFileName, name);
82 fail = true;
83 } else if (minorVersion >= 60) {
84 System.out.printf("bad version %d:%s %s%n", minorVersion, jarFileName, name);
85 fail = true;
86 }
87 zipIn.close();
8088 }
81 zipIn.close();
8289 }
83 z.close();
8490 }
85 if (fail)
91 if (fail) {
8692 System.exit(1);
93 }
8794 }
8895
8996 }
00 /*
11 * FindBugs - Find Bugs in Java programs
22 * Copyright (C) 2003-2008 University of Maryland
3 *
3 *
44 * This library is free software; you can redistribute it and/or
55 * modify it under the terms of the GNU Lesser General Public
66 * License as published by the Free Software Foundation; either
77 * version 2.1 of the License, or (at your option) any later version.
8 *
8 *
99 * This library is distributed in the hope that it will be useful,
1010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1111 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1212 * Lesser General Public License for more details.
13 *
13 *
1414 * You should have received a copy of the GNU Lesser General Public
1515 * License along with this library; if not, write to the Free Software
1616 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
3838 * @author pugh
3939 */
4040 public class ComparePerfomance {
41
41
4242 final int num;
4343 final Map<String, int[]> performance = new TreeMap<String, int[]>();
44
44
4545 ComparePerfomance(String [] args) throws DocumentException, IOException {
4646 num = args.length;
4747 for(int i = 0; i < args.length; i++) {
4848 foo(new File(args[i]), i);
4949 }
50
50
5151 }
52
53
52
53
5454 public int[] getRecord(String className) {
5555 int [] result = performance.get(className);
56 if (result != null)
56 if (result != null) {
5757 return result;
58 }
5859 result = new int[num];
5960 performance.put(className, result);
6061 return result;
6162 }
63
6264 public void foo(File f, int i) throws DocumentException, IOException {
6365 Document doc;
6466 SAXReader reader = new SAXReader();
6567
6668 String fName = f.getName();
6769 InputStream in = new FileInputStream(f);
68 if (fName.endsWith(".gz"))
69 in = new GZIPInputStream(in);
70 doc = reader.read(in);
71 Node summary = doc.selectSingleNode("/BugCollection/FindBugsSummary");
72 double cpu_seconds = Double.parseDouble(summary.valueOf("@cpu_seconds"));
73 putStats("cpu_seconds", i, (int) (cpu_seconds * 1000));
74 double gc_seconds = Double.parseDouble(summary.valueOf("@gc_seconds"));
75 putStats("gc_seconds", i, (int) (gc_seconds * 1000));
76
77 List<Node> profileNodes = XMLUtil.selectNodes(doc, "/BugCollection/FindBugsSummary/FindBugsProfile/ClassProfile");
78 for(Node n : profileNodes) {
79 String name = n.valueOf("@name");
80 int totalMilliseconds = Integer.parseInt(n.valueOf("@totalMilliseconds"));
81 int invocations = Integer.parseInt(n.valueOf("@invocations"));
82 putStats(name, i, totalMilliseconds);
83 // System.out.printf("%6d %10d %s%n", invocations, totalMilliseconds, simpleName);
70 try {
71 if (fName.endsWith(".gz")) {
72 in = new GZIPInputStream(in);
73 }
74 doc = reader.read(in);
75 Node summary = doc.selectSingleNode("/BugCollection/FindBugsSummary");
76 double cpu_seconds = Double.parseDouble(summary.valueOf("@cpu_seconds"));
77 putStats("cpu_seconds", i, (int) (cpu_seconds * 1000));
78 double gc_seconds = Double.parseDouble(summary.valueOf("@gc_seconds"));
79 putStats("gc_seconds", i, (int) (gc_seconds * 1000));
80
81 List<Node> profileNodes = XMLUtil.selectNodes(doc, "/BugCollection/FindBugsSummary/FindBugsProfile/ClassProfile");
82 for(Node n : profileNodes) {
83 String name = n.valueOf("@name");
84 int totalMilliseconds = Integer.parseInt(n.valueOf("@totalMilliseconds"));
85 int invocations = Integer.parseInt(n.valueOf("@invocations"));
86 putStats(name, i, totalMilliseconds);
87 // System.out.printf("%6d %10d %s%n", invocations, totalMilliseconds, simpleName);
88 }
89 } finally {
90 in.close();
8491 }
85 in.close();
8692 }
8793
8894
95101 int [] stats = getRecord(name);
96102 stats[i] = totalMilliseconds;
97103 }
98
104
99105 public void print() {
100106 for(Map.Entry<String, int[]> e : performance.entrySet()) {
101107 String name = e.getKey();
102108 int lastDot = name.lastIndexOf('.');
103109 String simpleName = name.substring(lastDot+1);
104110 System.out.printf("%s,%s", name, simpleName);
105 for(int x : e.getValue())
111 for(int x : e.getValue()) {
106112 System.out.printf(",%d", x);
113 }
107114 System.out.println();
108115 }
109116 }
110
111
117
118
112119 public static void main(String args[]) throws Exception {
113120 ComparePerfomance p = new ComparePerfomance(args);
114121 p.print();
5353 Map<String, Integer> properties = new TreeMap<String, Integer>();
5454 Map<String, Integer> accessFlags = new TreeMap<String, Integer>();
5555
56 if (args.length == 0)
56 if (args.length == 0) {
5757 process(System.in, properties, accessFlags);
58 else
59 for (String f : args)
58 } else {
59 for (String f : args) {
6060 process(new FileInputStream(f), properties, accessFlags);
61 }
62 }
6163
6264 for (Entry<String, Integer> e : properties.entrySet()) {
6365 String key = e.getKey();
7375
7476 static Status getStatus(@DottedClassName String name) {
7577 if (name.startsWith("com.sun") || name.startsWith("com.oracle")
76 || name.startsWith("sun") || name.startsWith("netscape"))
78 || name.startsWith("sun") || name.startsWith("netscape")) {
7779 return Status.UNEXPOSED;
80 }
7881 Status result = classStatus.get(name);
79 if (result != null)
82 if (result != null) {
8083 return result;
84 }
8185
8286 try {
8387 Class<?> c = Class.forName(name, false, ClassLoader.getSystemClassLoader());
8488 int accessFlags = c.getModifiers();
85 if ((accessFlags & FLAGS) != 0)
89 if ((accessFlags & FLAGS) != 0) {
8690 result = Status.EXPOSED;
87 else {
91 } else {
8892 result = Status.UNEXPOSED;
8993 }
9094 } catch (Exception e) {
107111 try {
108112 while (true) {
109113 String s = in.readLine();
110 if (s == null)
114 if (s == null) {
111115 break;
116 }
112117 Matcher m = p.matcher(s);
113118 if (m.find()) {
114119 String key = m.group(1);
115120 String className = m.group(2);
116 if (getStatus(className) == Status.UNEXPOSED)
121 if (getStatus(className) == Status.UNEXPOSED) {
117122 continue;
123 }
118124 int accFlags = Integer.parseInt(m.group(3));
119125 int bits = Integer.parseInt(m.group(4));
120126 if ((accFlags & FLAGS) != 0) {
121127 accessFlags.put(key, accFlags);
122 if (properties.containsKey(key))
128 if (properties.containsKey(key)) {
123129 properties.put(key, bits | properties.get(key));
124 else
130 } else {
125131 properties.put(key, bits);
132 }
126133 }
127134 }
128135
4747 */
4848 public static void main(String[] args) throws IOException {
4949 InputStream inSource = System.in;
50 if (args.length > 0)
50 if (args.length > 0) {
5151 inSource = new FileInputStream(args[0]);
52 }
5253 process(inSource);
5354
5455 }
6465 in = new BufferedReader(Util.getReader(inSource));
6566
6667 Pattern p = Pattern.compile("^(([^,]+),.+),([0-9]+)\\|(.+)$");
67
68
6869 while (true) {
6970 String s = in.readLine();
70 if (s == null)
71 if (s == null) {
7172 break;
73 }
7274 Matcher m = p.matcher(s);
7375 if (m.find()) {
7476 String className = m.group(2);
75 if (FilterAndCombineBitfieldPropertyDatabase.getStatus(className) == Status.UNEXPOSED)
77 if (FilterAndCombineBitfieldPropertyDatabase.getStatus(className) == Status.UNEXPOSED) {
7678 continue;
79 }
7780 int accFlags = Integer.parseInt(m.group(3));
78
79 if ((accFlags & FLAGS) != 0)
81
82 if ((accFlags & FLAGS) != 0) {
8083 System.out.println(s);
84 }
8185 }
8286
8387 }
4141 static final String SPACES = " ";
4242
4343 static final boolean performUpdate = SystemProperties.getBoolean("fix.identation");
44
44
4545 public static void main(String args[]) throws Exception {
4646 File root = new File(args[0]);
47 if (!root.exists() || !root.canRead())
47 if (!root.exists() || !root.canRead()) {
4848 throw new IllegalArgumentException("Unable to read " +root);
49 }
4950 recursiveFix(root, true);
5051 System.out.printf("Updated %d/%d files%n", updated, examined);
5152 System.out.printf("%d nonblank lines%n", lines);
52 if (!performUpdate)
53 if (!performUpdate) {
5354 System.out.println("No update actually performed");
55 }
5456 }
5557
5658 static void recursiveFix(File root, boolean partial) throws IOException {
6264 while (!todo.isEmpty()) {
6365 File next = todo.remove().getAbsoluteFile();
6466 String nextPath = next.getAbsolutePath();
65 if (!nextPath.startsWith(rootPath))
67 if (!nextPath.startsWith(rootPath)) {
6668 continue;
69 }
6770
6871 if (next.isDirectory()) {
6972 File[] contents = next.listFiles();
70 if (contents != null)
71 for (File c : contents)
72 if (seen.add(c))
73 if (contents != null) {
74 for (File c : contents) {
75 if (seen.add(c)) {
7376 todo.add(c);
74 } else if (nextPath.endsWith(".java") || nextPath.endsWith(".xml"))
77 }
78 }
79 }
80 } else if (nextPath.endsWith(".java") || nextPath.endsWith(".xml")) {
7581 fix(next, partial);
82 }
7683 }
7784 }
7885
7986 static boolean TRIM_TRAILING_WS = false;
8087 static String fix(String s) {
81 if (s.length() == 0)
88 if (s.length() == 0) {
8289 return s;
83 if (TRIM_TRAILING_WS && s.trim().length() == 0)
90 }
91 if (TRIM_TRAILING_WS && s.trim().length() == 0) {
8492 return "";
93 }
8594 int pos = 0;
8695 int indentation = 0;
8796 int tabs = 0;
92101 } else if (c == '\t') {
93102 indentation += 4;
94103 tabs++;
95 } else
104 } else {
96105 break;
106 }
97107 }
98108
99109 if (TRIM_TRAILING_WS || tabs > 0) {
100 if (pos >= s.length())
110 if (pos >= s.length()) {
101111 return "";
112 }
102113 return SPACES.substring(0, indentation) + s.substring(pos).trim();
103114
104115 } else {
105 if (pos >= s.length())
116 if (pos >= s.length()) {
106117 return s;
118 }
107119 return SPACES.substring(0, indentation) + s.substring(pos);
108120 }
109
121
110122
111123 }
112124
123135 try {
124136 while (true) {
125137 String s = in.readLine();
126 if (s == null)
138 if (s == null) {
127139 break;
128 if (s.trim().length() > 0)
140 }
141 if (s.trim().length() > 0) {
129142 lines++;
143 }
130144 String s2 = fix(s);
131145 if (!s2.equals(s)) {
132146 consecutiveFixes++;
133147 if (consecutiveFixes > 3 && partial) {
134148 s2 = s;
135149 consecutiveFixes = 0;
136 } else
150 } else {
137151 anyChanges = true;
138 } else
152 }
153 } else {
139154 consecutiveFixes = 0;
155 }
140156 out.println(s2);
141157 }
142158 } finally {
143159 in.close();
144160 }
145 if (!anyChanges)
161 if (!anyChanges) {
146162 return;
163 }
147164 updated++;
148165 if (!performUpdate) {
149166 System.out.println("Would update " + fileToUpdate);
156173 try {
157174 while (true) {
158175 int sz = stringReader.read(buffer);
159 if (sz < 0)
176 if (sz < 0) {
160177 break;
178 }
161179 outFile.write(buffer, 0, sz);
162180 }
163181 } finally {
164182 outFile.close();
165183 }
166
184
167185 }
168186
169187 }
00 /*
11 * FindBugs - Find Bugs in Java programs
22 * Copyright (C) 2003-2008 University of Maryland
3 *
3 *
44 * This library is free software; you can redistribute it and/or
55 * modify it under the terms of the GNU Lesser General Public
66 * License as published by the Free Software Foundation; either
77 * version 2.1 of the License, or (at your option) any later version.
8 *
8 *
99 * This library is distributed in the hope that it will be useful,
1010 * but WITHOUT ANY WARRANTY; without even the implied warranty of
1111 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
1212 * Lesser General Public License for more details.
13 *
13 *
1414 * You should have received a copy of the GNU Lesser General Public
1515 * License along with this library; if not, write to the Free Software
1616 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2626 * @author pugh
2727 */
2828 public class GenerateUpdateXml {
29
29
3030 public static void main(String args[]) {
3131 FindBugs.setNoAnalysis();
3232 DetectorFactoryCollection dfc = DetectorFactoryCollection.instance();
3535 System.out.println(p.getReleaseDate());
3636 System.out.println(p.getVersion());
3737 System.out.println();
38
38
3939 }
40
40
4141 }
4242
4343 }
1919 package edu.umd.cs.findbugs.tools.html;
2020
2121 public class ColorAlternator {
22 private String[] colorList;
22 private final String[] colorList;
2323
2424 int index;
2525
3232 }
3333 }
3434
35 // vim:ts=3
4646 @Override
4747 protected void prologue() throws IOException {
4848 out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">" +
49 "\n<html><head><title>" + docTitle + "</title>");
49 "\n<html><head><META http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\">" +
50 "\n<title>" + docTitle + "</title>");
5051 header();
5152 out.println("</head><body>");
5253 beginBody();
7980
8081 public static void main(String[] args) throws Exception {
8182 String docTitle = "FindBugs Bug Descriptions";
82 if (args.length > 0)
83 if (args.length > 0) {
8384 docTitle = args[0];
85 }
8486 new PlainPrintBugDescriptions(docTitle, System.out).print();
8587 }
8688 }
8789
88 // vim:ts=3
3131 import edu.umd.cs.findbugs.I18N;
3232
3333 public class PrettyPrintBugDescriptions extends PlainPrintBugDescriptions {
34 private Set<BugPattern> bugPatternSet;
34 private final Set<BugPattern> bugPatternSet;
3535
3636 private String headerText;
3737
4646 private static final String[] TABLE_COLORS = new String[] { "#eeeeee", "#ffffff" };
4747
4848 private static class BugPatternComparator implements Comparator<BugPattern>, Serializable {
49 @Override
4950 public int compare(BugPattern a, BugPattern b) {
5051 int cmp = a.getCategory().compareTo(b.getCategory());
51 if (cmp != 0)
52 if (cmp != 0) {
5253 return cmp;
54 }
5355 cmp = a.getAbbrev().compareTo(b.getAbbrev());
54 if (cmp != 0)
56 if (cmp != 0) {
5557 return cmp;
58 }
5659 return a.getType().compareTo(b.getType());
5760 }
5861 }
160163 int argCount = 0;
161164 boolean unabridged = false;
162165
163 if (argCount < args.length && args[argCount].equals("-unabridged")) {
166 if (argCount < args.length && "-unabridged".equals(args[argCount])) {
164167 ++argCount;
165168 // Unabridged mode: emit all warnings reported by at least one
166169 // detector, even for disabled detectors.
190193 pp.setEndBodyText(args[argCount++]);
191194 }
192195
193 if (unabridged)
196 if (unabridged) {
194197 pp.unabridged = true;
198 }
195199
196200 pp.print();
197201 }
198202 }
199203
200 // vim:ts=3
3636 Collection<BugPattern> enabledPatternSet = new HashSet<BugPattern>();
3737 for (Iterator<DetectorFactory> i = factories.factoryIterator(); i.hasNext();) {
3838 DetectorFactory factory = i.next();
39 if (isEnabled(factory))
39 if (isEnabled(factory)) {
4040 enabledPatternSet.addAll(factory.getReportedBugPatterns());
41 }
4142 }
4243
4344 prologue();
4546 Iterator<BugPattern> i = DetectorFactoryCollection.instance().bugPatternIterator();
4647 while (i.hasNext()) {
4748 BugPattern bugPattern = i.next();
48 if (!enabledPatternSet.contains(bugPattern))
49 if (!enabledPatternSet.contains(bugPattern)) {
4950 continue;
51 }
5052 emit(bugPattern);
5153 }
5254
6466 protected abstract void epilogue() throws IOException;
6567 }
6668
67 // vim:ts=3
3636 * Run all of the JUnit tests in a jar file using the JUnit textui. There might
3737 * be a simple way of doing this directly with JUnit. However, I'm lazy and
3838 * impatient, and writing some code to do this was very simple.
39 *
39 *
4040 * @author David Hovemeyer
4141 */
4242 public class JUnitJarRunner {
43 private String jarFileName;
43 private final String jarFileName;
4444
4545 private String classpath;
4646
4747 /**
4848 * Constructor.
49 *
49 *
5050 * @param jarFileName
5151 * name of jar file to load tests from
5252 */
5757 /**
5858 * Set the classpath containing the code to be tested (if it is not already
5959 * on the system classpath).
60 *
60 *
6161 * @param classpath
6262 * the classpath
6363 */
6767
6868 /**
6969 * Build a TestSuite of all the tests contained in the jar file.
70 *
70 *
7171 * @return TestSuite for running all of the tests in the jar file
7272 */
7373 public TestSuite buildTestSuite() throws Exception {
8484
8585 ClassLoader cl = AccessController.doPrivileged(new PrivilegedExceptionAction<URLClassLoader>() {
8686
87 @Override
8788 public URLClassLoader run() throws Exception {
8889 return new URLClassLoader(urlList.toArray(new URL[urlList.size()]));
8990
9293
9394 Class<junit.framework.TestCase> testCaseClass = getTestCase(cl);
9495
95 JarFile jarFile = new JarFile(jarFileName);
96 Enumeration<JarEntry> e = jarFile.entries();
97 while (e.hasMoreElements()) {
98 JarEntry entry = e.nextElement();
99 String entryName = entry.getName();
100 if (entryName.endsWith(".class")) {
101 String className = entryName.substring(0, entryName.length() - ".class".length()).replace('/', '.');
102 if (!className.endsWith("Test"))
103 continue;
104 System.out.println("Loading test class: " + className);
105 System.out.flush();
106 Class<?> jarClass = cl.loadClass(className);
107 if (testCaseClass.isAssignableFrom(jarClass))
108 suite.addTestSuite(testCaseClass.asSubclass(testCaseClass));
96 try(JarFile jarFile = new JarFile(jarFileName)){
97 Enumeration<JarEntry> e = jarFile.entries();
98 while (e.hasMoreElements()) {
99 JarEntry entry = e.nextElement();
100 String entryName = entry.getName();
101 if (entryName.endsWith(".class")) {
102 String className = entryName.substring(0, entryName.length() - ".class".length()).replace('/', '.');
103 if (!className.endsWith("Test")) {
104 continue;
105 }
106 System.out.println("Loading test class: " + className);
107 System.out.flush();
108 Class<?> jarClass = cl.loadClass(className);
109 if (testCaseClass.isAssignableFrom(jarClass)) {
110 suite.addTestSuite(testCaseClass.asSubclass(testCaseClass));
111 }
112 }
109113 }
110114 }
111 jarFile.close();
112115
113116 return suite;
114117 }
121124 }
122125
123126 public void run(TestSuite suite, String how) {
124 if (how.equals("-textui")) {
127 if ("-textui".equals(how)) {
125128 junit.textui.TestRunner.run(suite);
126 } else if (how.equals("-swingui")) {
129 } else if ("-swingui".equals(how)) {
127130 // junit.swingui.TestRunner.run(suite);
128131 throw new UnsupportedOperationException("I don't know how to run the Swing UI on a test suite yet");
129 } else
132 } else {
130133 throw new IllegalArgumentException("Unknown option: " + how);
134 }
131135 }
132136
133137 public static void main(String[] argv) throws Exception {
143147 }
144148 String jarFileName = argv[arg++];
145149 JUnitJarRunner runner = new JUnitJarRunner(jarFileName);
146 if (arg < argv.length)
150 if (arg < argv.length) {
147151 runner.setClassPath(argv[arg++]);
152 }
148153 TestSuite suite = runner.buildTestSuite();
149154 runner.run(suite, how);
150155 }
151156 }
152
153 // vim:ts=4
5555 }
5656
5757 private static class XMLFile {
58 private String filename;
59
60 private Document document;
58 private final String filename;
59
60 private final Document document;
6161
6262 public XMLFile(String filename) throws DocumentException {
6363 this.filename = filename;
101101 }
102102
103103 public Attribute checkAttribute(Node node, String attrName) throws DocumentException {
104 if (!(node instanceof Element))
104 if (!(node instanceof Element)) {
105105 throw new CheckMessagesException("Node is not an element", this, node);
106 }
106107 Element element = (Element) node;
107108 Attribute attr = element.attribute(attrName);
108 if (attr == null)
109 if (attr == null) {
109110 throw new CheckMessagesException("Missing " + attrName + " attribute", this, node);
111 }
110112 return attr;
111113 }
112114
113115 public Element checkElement(Node node, String elementName) throws DocumentException {
114 if (!(node instanceof Element))
116 if (!(node instanceof Element)) {
115117 throw new CheckMessagesException("Node is not an element", this, node);
118 }
116119 Element element = (Element) node;
117120 Element child = element.element(elementName);
118 if (child == null)
121 if (child == null) {
119122 throw new CheckMessagesException("Missing " + elementName + " element", this, node);
123 }
120124 return child;
121125 }
122126
123127 public String checkNonEmptyText(Node node) throws DocumentException {
124 if (!(node instanceof Element))
128 if (!(node instanceof Element)) {
125129 throw new CheckMessagesException("Node is not an element", this, node);
130 }
126131 Element element = (Element) node;
127132 String text = element.getText();
128 if (text.equals(""))
133 if ("".equals(text)) {
129134 throw new CheckMessagesException("Empty text in element", this, node);
135 }
130136 return text;
131137 }
132138 }
133139
134 private Set<String> declaredDetectorsSet;
135
136 private Set<String> declaredAbbrevsSet;
140 private final Set<String> declaredDetectorsSet;
141
142 private final Set<String> declaredAbbrevsSet;
137143
138144 public CheckMessages(String pluginDescriptorFilename) throws DocumentException {
139145
146152
147153 /**
148154 * Check given messages file for validity.
149 *
155 *
150156 * @throws DocumentException
151157 * if the messages file is invalid
152158 */
195201 notDescribed.addAll(declared);
196202 notDescribed.removeAll(described);
197203
198 if (!notDescribed.isEmpty())
204 if (!notDescribed.isEmpty()) {
199205 throw new CheckMessagesException(description + ": " + notDescribed.toString(), xmlFile);
206 }
200207 }
201208
202209 public static void main(String[] argv) throws Exception {
224231 }
225232 }
226233
227 // vim:ts=3
0 <?xml version="1.0" encoding="UTF-8"?>
1 <!--
2 FindBugs - Find bugs in Java programs
3 Copyright (C) 2004,2005 University of Maryland
4 Copyright (C) 2005, Chris Nappin
5 Copyright (C) 2015, Brahim Djoudi (modifications)
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 -->
21 <xsl:stylesheet version="1.0"
22 xmlns="http://www.w3.org/1999/xhtml"
23 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
24
25 <xsl:output
26 method="xml"
27 omit-xml-declaration="yes"
28 doctype-system="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"
29 doctype-public="-//W3C//DTD XHTML 1.0 Transitional//EN"
30 indent="yes"
31 encoding="UTF-8"/>
32
33 <xsl:variable name="bugTableHeader">
34 <tr class="tableheader">
35 <th align="left">Warning</th>
36 <th align="center">Priority</th>
37 <th align="left">Details</th>
38 </tr>
39 </xsl:variable>
40
41 <xsl:template match="/">
42 <html>
43 <head>
44 <title>FindBugs Report</title>
45 <style type="text/css">
46 .tablerow0 {
47 background: #EEEEEE;
48 }
49
50 .tablerow1 {
51 background: white;
52 }
53
54 .detailrow0 {
55 background: #EEEEEE;
56 }
57
58 .detailrow1 {
59 background: white;
60 }
61 .long_message {
62 color:#220001;
63 background: -moz-linear-gradient(left, rgba(255,255,255,0) 0%, rgba(255,255,255,0.96) 24%, rgba(255,255,255,1) 25%, rgba(255,255,255,0) 100%); /* FF3.6+ */
64 background: -webkit-gradient(linear, left top, right top, color-stop(0%,rgba(255,255,255,0)), color-stop(24%,rgba(255,255,255,0.96)), color-stop(25%,rgba(255,255,255,1)), color-stop(100%,rgba(255,255,255,0))); /* Chrome,Safari4+ */
65 background: -webkit-linear-gradient(left, rgba(255,255,255,0) 0%,rgba(255,255,255,0.96) 24%,rgba(255,255,255,1) 25%,rgba(255,255,255,0) 100%); /* Chrome10+,Safari5.1+ */
66 background: -o-linear-gradient(left, rgba(255,255,255,0) 0%,rgba(255,255,255,0.96) 24%,rgba(255,255,255,1) 25%,rgba(255,255,255,0) 100%); /* Opera 11.10+ */
67 background: -ms-linear-gradient(left, rgba(255,255,255,0) 0%,rgba(255,255,255,0.96) 24%,rgba(255,255,255,1) 25%,rgba(255,255,255,0) 100%); /* IE10+ */
68 background: linear-gradient(to right, rgba(255,255,255,0) 0%,rgba(255,255,255,0.96) 24%,rgba(255,255,255,1) 25%,rgba(255,255,255,0) 100%); /* W3C */
69 filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#00ffffff', endColorstr='#00ffffff',GradientType=1 ); /* IE6-9 */
70 }
71
72 .tableheader {
73 font-size: larger;
74 background: -moz-linear-gradient(left, rgba(135,224,253,1) 0%, rgba(83,203,241,0.9) 40%, rgba(59,4,153,0.75) 100%); /* FF3.6+ */
75 background: -webkit-gradient(linear, left top, right top, color-stop(0%,rgba(135,224,253,1)), color-stop(40%,rgba(83,203,241,0.9)), color-stop(100%,rgba(59,4,153,0.75))); /* Chrome,Safari4+ */
76 background: -webkit-linear-gradient(left, rgba(135,224,253,1) 0%,rgba(83,203,241,0.9) 40%,rgba(59,4,153,0.75) 100%); /* Chrome10+,Safari5.1+ */
77 background: -o-linear-gradient(left, rgba(135,224,253,1) 0%,rgba(83,203,241,0.9) 40%,rgba(59,4,153,0.75) 100%); /* Opera 11.10+ */
78 background: -ms-linear-gradient(left, rgba(135,224,253,1) 0%,rgba(83,203,241,0.9) 40%,rgba(59,4,153,0.75) 100%); /* IE10+ */
79 background: linear-gradient(to right, rgba(135,224,253,1) 0%,rgba(83,203,241,0.9) 40%,rgba(59,4,153,0.75) 100%); /* W3C */
80 filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#87e0fd', endColorstr='#bf3b0499',GradientType=1 ); /* IE6-9 */
81
82 }
83 .high {
84 background: -moz-linear-gradient(top, rgba(239,187,110,0.93) 0%, rgba(255,26,0,0.97) 52%, rgba(239,187,110,1) 100%); /* FF3.6+ */
85 background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(239,187,110,0.93)), color-stop(52%,rgba(255,26,0,0.97)), color-stop(100%,rgba(239,187,110,1))); /* Chrome,Safari4+ */
86 background: -webkit-linear-gradient(top, rgba(239,187,110,0.93) 0%,rgba(255,26,0,0.97) 52%,rgba(239,187,110,1) 100%); /* Chrome10+,Safari5.1+ */
87 background: -o-linear-gradient(top, rgba(239,187,110,0.93) 0%,rgba(255,26,0,0.97) 52%,rgba(239,187,110,1) 100%); /* Opera 11.10+ */
88 background: -ms-linear-gradient(top, rgba(239,187,110,0.93) 0%,rgba(255,26,0,0.97) 52%,rgba(239,187,110,1) 100%); /* IE10+ */
89 background: linear-gradient(to bottom, rgba(239,187,110,0.93) 0%,rgba(255,26,0,0.97) 52%,rgba(239,187,110,1) 100%); /* W3C */
90 filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#edefbb6e', endColorstr='#efbb6e',GradientType=0 ); /* IE6-9 */
91 }
92 .medium {
93 background: -moz-linear-gradient(top, rgba(244,232,117,1) 0%, rgba(239,187,110,0.96) 52%, rgba(244,232,117,0.93) 100%); /* FF3.6+ */
94 background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(244,232,117,1)), color-stop(52%,rgba(239,187,110,0.96)), color-stop(100%,rgba(244,232,117,0.93))); /* Chrome,Safari4+ */
95 background: -webkit-linear-gradient(top, rgba(244,232,117,1) 0%,rgba(239,187,110,0.96) 52%,rgba(244,232,117,0.93) 100%); /* Chrome10+,Safari5.1+ */
96 background: -o-linear-gradient(top, rgba(244,232,117,1) 0%,rgba(239,187,110,0.96) 52%,rgba(244,232,117,0.93) 100%); /* Opera 11.10+ */
97 background: -ms-linear-gradient(top, rgba(244,232,117,1) 0%,rgba(239,187,110,0.96) 52%,rgba(244,232,117,0.93) 100%); /* IE10+ */
98 background: linear-gradient(to bottom, rgba(244,232,117,1) 0%,rgba(239,187,110,0.96) 52%,rgba(244,232,117,0.93) 100%); /* W3C */
99 filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#f4e875', endColorstr='#edf4e875',GradientType=0 ); /* IE6-9 */
100 }
101 .low {
102 background: -moz-linear-gradient(top, rgba(244,232,117,0.93) 0%, rgba(140,186,124,0.97) 53%, rgba(244,232,117,1) 100%); /* FF3.6+ */
103 background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,rgba(244,232,117,0.93)), color-stop(53%,rgba(140,186,124,0.97)), color-stop(100%,rgba(244,232,117,1))); /* Chrome,Safari4+ */
104 background: -webkit-linear-gradient(top, rgba(244,232,117,0.93) 0%,rgba(140,186,124,0.97) 53%,rgba(244,232,117,1) 100%); /* Chrome10+,Safari5.1+ */
105 background: -o-linear-gradient(top, rgba(244,232,117,0.93) 0%,rgba(140,186,124,0.97) 53%,rgba(244,232,117,1) 100%); /* Opera 11.10+ */
106 background: -ms-linear-gradient(top, rgba(244,232,117,0.93) 0%,rgba(140,186,124,0.97) 53%,rgba(244,232,117,1) 100%); /* IE10+ */
107 background: linear-gradient(to bottom, rgba(244,232,117,0.93) 0%,rgba(140,186,124,0.97) 53%,rgba(244,232,117,1) 100%); /* W3C */
108 filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#edf4e875', endColorstr='#f4e875',GradientType=0 ); /* IE6-9 */
109 }
110 pre {
111 font-family: "Bitstream Vera Sans Mono", Consolas, Inconsolata, "Lucida Console", "Courier New", Monospace !important;
112 box-shadow: 0 0;
113 color: black;
114 border-width: 1px 1px 1px 6px;
115 border-style: solid;
116 padding: 2ex;
117 margin: 2ex 2ex 2ex 2ex;
118 overflow: auto;
119 -moz-border-radius: 0px;
120 -webkit-border-radius: 0px;
121 -khtml-border-radius: 0px;
122 border-radius: 0px;
123 border-color: #996666;
124
125 background: rgb(232,239,244); /* Old browsers */
126 background: -moz-linear-gradient(left, rgba(232,239,244,1) 1%, rgba(244,249,249,1) 23%, rgba(249,250,246,1) 87%, rgba(241,242,236,1) 98%); /* FF3.6+ */
127 background: -webkit-gradient(linear, left top, right top, color-stop(1%,rgba(232,239,244,1)), color-stop(23%,rgba(244,249,249,1)), color-stop(87%,rgba(249,250,246,1)), color-stop(98%,rgba(241,242,236,1))); /* Chrome,Safari4+ */
128 background: -webkit-linear-gradient(left, rgba(232,239,244,1) 1%,rgba(244,249,249,1) 23%,rgba(249,250,246,1) 87%,rgba(241,242,236,1) 98%); /* Chrome10+,Safari5.1+ */
129 background: -o-linear-gradient(left, rgba(232,239,244,1) 1%,rgba(244,249,249,1) 23%,rgba(249,250,246,1) 87%,rgba(241,242,236,1) 98%); /* Opera 11.10+ */
130 background: -ms-linear-gradient(left, rgba(232,239,244,1) 1%,rgba(244,249,249,1) 23%,rgba(249,250,246,1) 87%,rgba(241,242,236,1) 98%); /* IE10+ */
131 background: linear-gradient(to right, rgba(232,239,244,1) 1%,rgba(244,249,249,1) 23%,rgba(249,250,246,1) 87%,rgba(241,242,236,1) 98%); /* W3C */
132 filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#e8eff4', endColorstr='#f1f2ec',GradientType=1 ); /* IE6-9 */
133 }
134 </style>
135 </head>
136
137 <xsl:variable name="unique-catkey" select="/BugCollection/BugCategory/@category"/>
138 <!--xsl:variable name="unique-catkey" select="/BugCollection/BugInstance[generate-id() = generate-id(key('bug-category-key',@category))]/@category"/-->
139
140 <body>
141
142 <h1>FindBugs Report</h1>
143 <p>Produced using <a href="http://findbugs.sourceforge.net">FindBugs </a> <xsl:value-of select="/BugCollection/@version"/>.</p>
144 <p>Project:
145 <xsl:choose>
146 <xsl:when test='string-length(/BugCollection/Project/@projectName)>0'><xsl:value-of select="/BugCollection/Project/@projectName" /></xsl:when>
147 <xsl:otherwise><xsl:value-of select="/BugCollection/Project/@filename" /></xsl:otherwise>
148 </xsl:choose>
149 </p>
150
151 <table style="width:90%;">
152 <tr>
153 <td>
154 <h2>Metrics</h2>
155 <xsl:apply-templates select="/BugCollection/FindBugsSummary"/>
156 </td>
157 <td>
158 <h2>Summary</h2>
159 <table cellpadding="5" cellspacing="2" style="width:90%;border-collapse: collapse;border-style:solid;border-width:thin;">
160 <tr class="tableheader">
161 <th align="left">Warning Type</th>
162 <th align="right">Number</th>
163 </tr>
164
165 <xsl:for-each select="$unique-catkey">
166 <xsl:sort select="." order="ascending"/>
167 <xsl:variable name="catkey" select="."/>
168 <xsl:variable name="catdesc" select="/BugCollection/BugCategory[@category=$catkey]/Description"/>
169 <xsl:variable name="styleclass">
170 <xsl:choose><xsl:when test="position() mod 2 = 1">tablerow0</xsl:when>
171 <xsl:otherwise>tablerow1</xsl:otherwise>
172 </xsl:choose>
173 </xsl:variable>
174
175 <tr class="{$styleclass}">
176 <td><a href="#Warnings_{$catkey}"><xsl:value-of select="$catdesc"/> Warnings</a></td>
177 <td align="right"><xsl:value-of select="count(/BugCollection/BugInstance[@category=$catkey])"/></td>
178 </tr>
179 </xsl:for-each>
180
181 <xsl:variable name="styleclass">
182 <xsl:choose><xsl:when test="count($unique-catkey) mod 2 = 0">tablerow0</xsl:when>
183 <xsl:otherwise>tablerow1</xsl:otherwise>
184 </xsl:choose>
185 </xsl:variable>
186 <tr class="{$styleclass}">
187 <td><b>Total</b></td>
188 <td align="right"><b><xsl:value-of select="count(/BugCollection/BugInstance)"/></b></td>
189 </tr>
190 </table>
191 </td>
192 </tr>
193 </table>
194
195 <p><br/><br/></p>
196
197 <h1>Warnings</h1>
198
199 <p>Click on each warning link to see a full description of the issue, and
200 details of how to resolve it.</p>
201
202 <xsl:for-each select="$unique-catkey">
203 <xsl:sort select="." order="ascending"/>
204 <xsl:variable name="catkey" select="."/>
205 <xsl:variable name="catdesc" select="/BugCollection/BugCategory[@category=$catkey]/Description"/>
206
207 <xsl:call-template name="generateWarningTable">
208 <xsl:with-param name="warningSet" select="/BugCollection/BugInstance[@category=$catkey]"/>
209 <xsl:with-param name="sectionTitle"><xsl:value-of select="$catdesc"/> Warnings</xsl:with-param>
210 <xsl:with-param name="sectionId">Warnings_<xsl:value-of select="$catkey"/></xsl:with-param>
211 </xsl:call-template>
212 </xsl:for-each>
213
214 <p><br/><br/></p>
215 <h1><a name="Details">Warning Types</a></h1>
216
217 <xsl:apply-templates select="/BugCollection/BugPattern">
218 <xsl:sort select="@abbrev"/>
219 <xsl:sort select="ShortDescription"/>
220 </xsl:apply-templates>
221
222 </body>
223 </html>
224 </xsl:template>
225
226 <xsl:template match="BugInstance">
227 <xsl:variable name="warningId"><xsl:value-of select="generate-id()"/></xsl:variable>
228
229 <tr>
230 <!-- class="tablerow{position() mod 2}" -->
231 <xsl:choose>
232 <xsl:when test="@priority = 1"><xsl:attribute name="class">high</xsl:attribute></xsl:when>
233 <xsl:when test="@priority = 2"><xsl:attribute name="class">medium</xsl:attribute></xsl:when>
234 <xsl:when test="@priority = 3"><xsl:attribute name="class">low</xsl:attribute></xsl:when>
235 <xsl:otherwise><xsl:attribute name="bgcolor">#fdfdfd</xsl:attribute></xsl:otherwise>
236 </xsl:choose>
237 <td width="20%" valign="top">
238 <a href="#{@type}"><xsl:value-of select="ShortMessage"/></a>
239 </td>
240 <td width="10%" valign="top" align="center">
241 <xsl:choose>
242 <xsl:when test="@priority = 1"><strong>High</strong></xsl:when>
243 <xsl:when test="@priority = 2">Medium</xsl:when>
244 <xsl:when test="@priority = 3">Low</xsl:when>
245 <xsl:otherwise>Unknown</xsl:otherwise>
246 </xsl:choose>
247 </td>
248 <td width="70%">
249 <dl>
250 <dt class='long_message'><xsl:value-of select="LongMessage"/></dt>
251 <dd>
252 <!-- add source filename and line number(s), if any -->
253 <xsl:if test="SourceLine">
254 In file <tt><strong><xsl:value-of select="SourceLine/@sourcefile"/></strong></tt>,
255 <xsl:choose>
256 <xsl:when test="SourceLine/@start = SourceLine/@end">
257 line <xsl:value-of select="SourceLine/@start"/>
258 </xsl:when>
259 <xsl:otherwise>
260 lines <xsl:value-of select="SourceLine/@start"/>
261 to <xsl:value-of select="SourceLine/@end"/>
262 </xsl:otherwise>
263 </xsl:choose>
264 </xsl:if>
265
266 <xsl:for-each select="./*/Message">
267 <br/><xsl:value-of select="text()"/>
268 </xsl:for-each>
269 </dd>
270 </dl>
271 </td>
272 </tr>
273 </xsl:template>
274
275 <xsl:template match="BugPattern">
276 <h2><a name="{@type}"><xsl:value-of select="ShortDescription"/></a></h2>
277 <xsl:value-of select="Details" disable-output-escaping="yes"/>
278 <p><br/><br/></p>
279 </xsl:template>
280
281 <xsl:template name="generateWarningTable">
282 <xsl:param name="warningSet"/>
283 <xsl:param name="sectionTitle"/>
284 <xsl:param name="sectionId"/>
285
286 <h2><a name="{$sectionId}"><xsl:value-of select="$sectionTitle"/></a></h2>
287 <table class="warningtable" cellspacing="2" cellpadding="5" style="width:100%;border-collapse: collapse;border-style:solid;border-width:thin;">
288 <xsl:copy-of select="$bugTableHeader"/>
289 <xsl:choose>
290 <xsl:when test="count($warningSet) &gt; 0">
291 <xsl:apply-templates select="$warningSet">
292 <xsl:sort select="@priority"/>
293 <xsl:sort select="@abbrev"/>
294 <xsl:sort select="Class/@classname"/>
295 </xsl:apply-templates>
296 </xsl:when>
297 <xsl:otherwise>
298 <tr><td colspan="2"><p><i>None</i></p></td></tr>
299 </xsl:otherwise>
300 </xsl:choose>
301 </table>
302 <p><br/><br/></p>
303 </xsl:template>
304
305 <xsl:template match="FindBugsSummary">
306 <xsl:variable name="kloc" select="@total_size div 1000.0"/>
307 <xsl:variable name="format" select="'#######0.00'"/>
308
309 <p><xsl:value-of select="@total_size"/> lines of code analysed,
310 in <xsl:value-of select="@total_classes"/> classes,
311 in <xsl:value-of select="@num_packages"/> packages.</p>
312 <table cellpadding="5" cellspacing="2" style="width:90%;border-collapse: collapse;border-style:solid;border-width:thin;">
313 <tr class="tableheader">
314 <th align="left">Metric</th>
315 <th align="right">Total</th>
316 <th align="right">Density*</th>
317 </tr>
318 <tr class="high" >
319 <td>High Priority Warnings</td>
320 <td align="right"><xsl:value-of select="@priority_1"/></td>
321 <td align="right"><xsl:value-of select="format-number(@priority_1 div $kloc, $format)"/></td>
322 </tr>
323 <tr class="medium">
324 <td>Medium Priority Warnings</td>
325 <td align="right"><xsl:value-of select="@priority_2"/></td>
326 <td align="right"><xsl:value-of select="format-number(@priority_2 div $kloc, $format)"/></td>
327 </tr>
328
329 <xsl:choose>
330 <xsl:when test="@priority_3">
331 <tr class="low">
332 <td>Low Priority Warnings</td>
333 <td align="right"><xsl:value-of select="@priority_3"/></td>
334 <td align="right"><xsl:value-of select="format-number(@priority_3 div $kloc, $format)"/></td>
335 </tr>
336 </xsl:when>
337 </xsl:choose>
338
339 <tr bgcolor="#f0f0f0">
340 <td><b>Total Warnings</b></td>
341 <td align="right"><b><xsl:value-of select="@total_bugs"/></b></td>
342 <td align="right"><b><xsl:value-of select="format-number(@total_bugs div $kloc, $format)"/></b></td>
343 </tr>
344 </table>
345 <p><i>(* Defects per thousand lines of non-commenting source statements)</i></p>
346 <p><br/><br/></p>
347
348 </xsl:template>
349
350 </xsl:stylesheet>