View Javadoc
1   package org.codehaus.mojo.clirr;
2   
3   /*
4    * Copyright 2006 The Apache Software Foundation.
5    *
6    * Licensed under the Apache License, Version 2.0 (the "License");
7    * you may not use this file except in compliance with the License.
8    * You may obtain a copy of the License at
9    *
10   *      http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  import net.sf.clirr.core.Checker;
20  import net.sf.clirr.core.CheckerException;
21  import net.sf.clirr.core.ClassFilter;
22  import net.sf.clirr.core.DiffListener;
23  import net.sf.clirr.core.PlainDiffListener;
24  import net.sf.clirr.core.Severity;
25  import net.sf.clirr.core.internal.bcel.BcelJavaType;
26  import net.sf.clirr.core.internal.bcel.BcelTypeArrayBuilder;
27  import net.sf.clirr.core.spi.JavaType;
28  import org.apache.bcel.classfile.ClassParser;
29  import org.apache.bcel.classfile.JavaClass;
30  import org.apache.bcel.util.ClassLoaderRepository;
31  import org.apache.bcel.util.Repository;
32  import org.apache.maven.artifact.Artifact;
33  import org.apache.maven.artifact.factory.ArtifactFactory;
34  import org.apache.maven.artifact.metadata.ArtifactMetadataRetrievalException;
35  import org.apache.maven.artifact.metadata.ArtifactMetadataSource;
36  import org.apache.maven.artifact.repository.ArtifactRepository;
37  import org.apache.maven.artifact.resolver.ArtifactNotFoundException;
38  import org.apache.maven.artifact.resolver.ArtifactResolutionException;
39  import org.apache.maven.artifact.resolver.ArtifactResolutionResult;
40  import org.apache.maven.artifact.resolver.ArtifactResolver;
41  import org.apache.maven.artifact.versioning.ArtifactVersion;
42  import org.apache.maven.artifact.versioning.InvalidVersionSpecificationException;
43  import org.apache.maven.artifact.versioning.OverConstrainedVersionException;
44  import org.apache.maven.artifact.versioning.VersionRange;
45  import org.apache.maven.plugin.AbstractMojo;
46  import org.apache.maven.plugin.MojoExecutionException;
47  import org.apache.maven.plugin.MojoFailureException;
48  import org.apache.maven.project.MavenProject;
49  import org.apache.maven.project.MavenProjectBuilder;
50  import org.apache.maven.project.ProjectBuildingException;
51  import org.apache.maven.project.artifact.InvalidDependencyVersionException;
52  import org.codehaus.plexus.util.DirectoryScanner;
53  import org.codehaus.plexus.util.IOUtil;
54  import org.codehaus.plexus.util.ReaderFactory;
55  import org.codehaus.plexus.util.xml.XmlStreamReader;
56  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
57  
58  import java.io.File;
59  import java.io.FileInputStream;
60  import java.io.IOException;
61  import java.io.InputStream;
62  import java.net.MalformedURLException;
63  import java.net.URL;
64  import java.net.URLClassLoader;
65  import java.util.ArrayList;
66  import java.util.Arrays;
67  import java.util.Collection;
68  import java.util.Collections;
69  import java.util.HashSet;
70  import java.util.Iterator;
71  import java.util.List;
72  import java.util.Set;
73  
74  /**
75   * Base parameters for Clirr check and report.
76   *
77   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
78   * @todo i18n exceptions, log messages
79   * @requiresDependencyResolution compile
80   */
81  public abstract class AbstractClirrMojo
82      extends AbstractMojo
83  {
84      /**
85       * Flag to easily skip execution.
86       *
87       * @parameter property="clirr.skip" default-value="false"
88       */
89      protected boolean skip;
90  
91      /**
92       * @parameter property="project"
93       * @required
94       * @readonly
95       */
96      protected MavenProject project;
97  
98      /**
99       * @component
100      */
101     protected ArtifactResolver resolver;
102 
103     /**
104      * @component
105      */
106     protected ArtifactFactory factory;
107 
108     /**
109      * @parameter default-value="${localRepository}"
110      * @required
111      * @readonly
112      */
113     protected ArtifactRepository localRepository;
114 
115     /**
116      * @component
117      */
118     private ArtifactMetadataSource metadataSource;
119 
120     /**
121      * @component
122      */
123     private MavenProjectBuilder mavenProjectBuilder;
124 
125     /**
126      * The classes of this project to compare the last release against.
127      *
128      * @parameter default-value="${project.build.outputDirectory}
129      */
130     protected File classesDirectory;
131 
132     /**
133      * Version to compare the current code against.
134      *
135      * @parameter property="comparisonVersion" default-value="(,${project.version})"
136      */
137     protected String comparisonVersion;
138 
139     /**
140      * List of artifacts to compare the current code against. This
141      * overrides <code>comparisonVersion</code>, if present.
142      * Each <code>comparisonArtifact</code> is made of a <code>groupId</code>, an <code>artifactId</code> and
143      * a <code>version</code> number. Optionally it may have a <code>classifier</code>
144      * (default null) and a <code>type</code> (default "jar").
145      *
146      * @parameter
147      */
148     protected ArtifactSpecification[] comparisonArtifacts;
149 
150     /**
151      * Show only messages of this severity or higher. Valid values are
152      * <code>info</code>, <code>warning</code> and <code>error</code>.
153      *
154      * @parameter property="minSeverity" default-value="warning"
155      */
156     protected String minSeverity;
157 
158     /**
159      * A text output file to render to. If omitted, no output is rendered to a text file.
160      *
161      * @parameter property="textOutputFile"
162      */
163     protected File textOutputFile;
164 
165     /**
166      * An XML file to render to. If omitted, no output is rendered to an XML file.
167      *
168      * @parameter property="xmlOutputFile"
169      */
170     protected File xmlOutputFile;
171 
172     /**
173      * A list of classes to include. Anything not included is excluded. If omitted, all are assumed to be included.
174      * Values are specified in path pattern notation, e.g. <code>org/codehaus/mojo/**</code>.
175      *
176      * @parameter
177      */
178     protected String[] includes;
179 
180     /**
181      * A list of classes to exclude. These classes are excluded from the list of classes that are included.
182      * Values are specified in path pattern notation, e.g. <code>org/codehaus/mojo/**</code>.
183      *
184      * @parameter
185      */
186     protected String[] excludes;
187 
188     /**
189      * A list of differences reported by Clirr that should be ignored when producing the final report.
190      * Values specified here will be joined with the ones specified using the "ignoredDifferencesFile"
191      * parameter.
192      *
193      * @parameter
194      * @since 2.5
195      */
196     protected Difference[] ignored;
197 
198     /**
199      * A path to the XML file containing the ignored differences definitions.
200      * Values specified int the file will be joined with the ones specified using the "ignored"
201      * parameter.
202      * <br/>
203      * File Format
204      * <pre>
205      * &lt;differences&gt;
206      *   ...
207      *   &lt;difference&gt;
208      *     &lt;className&gt;com.thoughtworks.qdox.parser.impl.Parser&lt;/className&gt;
209      *     &lt;field&gt;*&lt;/field&gt;
210      *   &lt;/difference&gt;
211      *   ...
212      * &lt;/differences&gt;
213      * </pre>
214      *
215      * @parameter property="clirr.ignoredDifferencesFile"
216      * @since 2.5
217      */
218     protected File ignoredDifferencesFile;
219 
220     /**
221      * Whether to log the results to the console or not.
222      *
223      * @parameter property="logResults" default-value="false"
224      */
225     protected boolean logResults;
226 
227     /**
228      * @parameter property="clirr.skipArtifactTypeTest" default-value="false"
229      */
230     private boolean skipArtifactTypeTest;
231 
232     private static final URL[] EMPTY_URL_ARRAY = new URL[0];
233 
234     public void execute()
235         throws MojoExecutionException, MojoFailureException
236     {
237         if ( skip )
238         {
239             getLog().info( "Skipping execution" );
240         }
241         else
242         {
243             doExecute();
244         }
245     }
246 
247     protected abstract void doExecute()
248         throws MojoExecutionException, MojoFailureException;
249 
250     public ClirrDiffListener executeClirr()
251         throws MojoExecutionException, MojoFailureException
252     {
253         return executeClirr( null );
254     }
255 
256     protected ClirrDiffListener executeClirr( Severity minSeverity )
257         throws MojoExecutionException, MojoFailureException
258     {
259         ClirrDiffListener listener = new ClirrDiffListener();
260 
261         ClassFilter classFilter = new ClirrClassFilter( includes, excludes );
262 
263         JavaType[] origClasses = resolvePreviousReleaseClasses( classFilter );
264 
265         JavaType[] currentClasses = resolveCurrentClasses( classFilter );
266 
267         // Create a Clirr checker and execute
268         Checker checker = new Checker();
269 
270         List<DiffListener> listeners = new ArrayList<DiffListener>();
271 
272         listeners.add( listener );
273 
274         if ( xmlOutputFile != null )
275         {
276             try
277             {
278                 listeners.add( new TypeRevealingXmlDiffListener( xmlOutputFile.getAbsolutePath() ) );
279             }
280             catch ( IOException e )
281             {
282                 throw new MojoExecutionException( "Error adding '" + xmlOutputFile + "' for output: " + e.getMessage(),
283                                                   e );
284             }
285         }
286 
287         if ( textOutputFile != null )
288         {
289             try
290             {
291                 listeners.add( new PlainDiffListener( textOutputFile.getAbsolutePath() ) );
292             }
293             catch ( IOException e )
294             {
295                 throw new MojoExecutionException( "Error adding '" + textOutputFile + "' for output: " + e.getMessage(),
296                                                   e );
297             }
298         }
299 
300         if ( logResults )
301         {
302             listeners.add( new LogDiffListener( getLog() ) );
303         }
304 
305         checker.addDiffListener( new DelegatingListener( listeners, minSeverity, getAllIgnored() ) );
306 
307         reportDiffs( checker, origClasses, currentClasses );
308 
309         return listener;
310     }
311 
312     protected List<Difference> getAllIgnored()
313     {
314         Difference[] ret = ignored;
315 
316         if ( ignoredDifferencesFile != null && ignoredDifferencesFile.exists() )
317         {
318             XmlStreamReader rdr = null;
319             try
320             {
321                 rdr = ReaderFactory.newXmlReader( ignoredDifferencesFile );
322 
323                 Difference[] diffs = Difference.parseXml( rdr );
324 
325                 int ignoredLength = ignored == null ? 0 : ignored.length;
326 
327                 Difference[] tmp = new Difference[ignoredLength + diffs.length];
328 
329                 if ( ignored != null )
330                 {
331                     System.arraycopy( ignored, 0, tmp, 0, ignoredLength );
332                 }
333 
334                 System.arraycopy( diffs, 0, tmp, ignoredLength, diffs.length );
335 
336                 ret = tmp;
337             }
338             catch ( IOException e )
339             {
340                 getLog().error( "Could not read the ignored differences file.", e );
341             }
342             catch ( XmlPullParserException e )
343             {
344                 getLog().error( "Could not read the ignored differences file.", e );
345             }
346             finally
347             {
348                 IOUtil.close( rdr );
349             }
350         }
351 
352         return ret == null ? Collections.<Difference>emptyList() : Arrays.asList( ret );
353     }
354 
355     private JavaType[] resolveCurrentClasses( ClassFilter classFilter )
356         throws MojoExecutionException
357     {
358         try
359         {
360             ClassLoader currentDepCL = createClassLoader( project.getArtifacts(), null );
361             return createClassSet( classesDirectory, currentDepCL, classFilter );
362         }
363         catch ( MalformedURLException e )
364         {
365             throw new MojoExecutionException( "Error creating classloader for current classes", e );
366         }
367     }
368 
369     private JavaType[] resolvePreviousReleaseClasses( ClassFilter classFilter )
370         throws MojoFailureException, MojoExecutionException
371     {
372         final Set previousArtifacts;
373         final Artifact firstPreviousArtifact;
374         if ( comparisonArtifacts == null )
375         {
376             firstPreviousArtifact = getComparisonArtifact();
377             comparisonVersion = firstPreviousArtifact.getVersion();
378             getLog().info( "Comparing to version: " + comparisonVersion );
379             previousArtifacts = Collections.singleton( firstPreviousArtifact );
380         }
381         else
382         {
383             previousArtifacts = resolveArtifacts( comparisonArtifacts );
384             Artifact a = null;
385             for ( Iterator iter = previousArtifacts.iterator(); iter.hasNext(); )
386             {
387                 Artifact artifact = (Artifact) iter.next();
388                 if ( a == null )
389                 {
390                     a = artifact;
391                 }
392                 getLog().debug( "Comparing to " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":"
393                                     + artifact.getVersion() + ":" + artifact.getClassifier() + ":"
394                                     + artifact.getType() );
395             }
396             firstPreviousArtifact = a;
397         }
398 
399         try
400         {
401             for ( Iterator iter = previousArtifacts.iterator(); iter.hasNext(); )
402             {
403                 Artifact artifact = (Artifact) iter.next();
404                 resolver.resolve( artifact, project.getRemoteArtifactRepositories(), localRepository );
405             }
406 
407             final List dependencies = getTransitiveDependencies( previousArtifacts );
408 
409             ClassLoader origDepCL = createClassLoader( dependencies, previousArtifacts );
410             final Set files = new HashSet();
411             for ( Iterator iter = previousArtifacts.iterator(); iter.hasNext(); )
412             {
413                 Artifact artifact = (Artifact) iter.next();
414                 // Clirr expects JAR files, so let's not pass other artifact files.
415                 // MCLIRR-39 Support for Maven Plugins, which are also JARs
416                 // MCLIRR-61: jenkins plugin so test if isAddedToClasspath
417                 if ( "jar".equals( artifact.getType() ) || "maven-plugin".equals( artifact.getType() )
418                     || "bundle".equals( artifact.getType() ) || artifact.getArtifactHandler().isAddedToClasspath()
419                     || skipArtifactTypeTest )
420                 {
421                     files.add( new File( localRepository.getBasedir(), localRepository.pathOf( artifact ) ) );
422                 }
423             }
424             return BcelTypeArrayBuilder.createClassSet( (File[]) files.toArray( new File[files.size()] ), origDepCL,
425                                                         classFilter );
426         }
427         catch ( ProjectBuildingException e )
428         {
429             throw new MojoExecutionException( "Failed to build project for previous artifact: " + e.getMessage(), e );
430         }
431         catch ( InvalidDependencyVersionException e )
432         {
433             throw new MojoExecutionException( e.getMessage(), e );
434         }
435         catch ( ArtifactResolutionException e )
436         {
437             throw new MissingPreviousException( "Error resolving previous version: " + e.getMessage(), e );
438         }
439         catch ( ArtifactNotFoundException e )
440         {
441             getLog().warn( "Impossible to find previous version" );
442             return new JavaType[0];
443             //throw new MojoExecutionException( "Error finding previous version: " + e.getMessage(), e );
444         }
445         catch ( MalformedURLException e )
446         {
447             throw new MojoExecutionException( "Error creating classloader for previous version's classes", e );
448         }
449     }
450 
451     protected List getTransitiveDependencies( final Set previousArtifacts )
452         throws ProjectBuildingException, InvalidDependencyVersionException, ArtifactResolutionException,
453         ArtifactNotFoundException
454     {
455         final List dependencies = new ArrayList();
456         for ( Iterator iter = previousArtifacts.iterator(); iter.hasNext(); )
457         {
458             final Artifact a = (Artifact) iter.next();
459             final Artifact pomArtifact =
460                 factory.createArtifact( a.getGroupId(), a.getArtifactId(), a.getVersion(), a.getScope(), "pom" );
461             final MavenProject pomProject =
462                 mavenProjectBuilder.buildFromRepository( pomArtifact, project.getRemoteArtifactRepositories(),
463                                                          localRepository );
464             final Set pomProjectArtifacts = pomProject.createArtifacts( factory, null, null );
465             final ArtifactResolutionResult result =
466                 resolver.resolveTransitively( pomProjectArtifacts, pomArtifact, localRepository,
467                                               project.getRemoteArtifactRepositories(), metadataSource, null );
468             dependencies.addAll( result.getArtifacts() );
469         }
470         return dependencies;
471     }
472 
473     private Artifact resolveArtifact( ArtifactSpecification artifactSpec )
474         throws MojoFailureException, MojoExecutionException
475     {
476         final String groupId = artifactSpec.getGroupId();
477         if ( groupId == null )
478         {
479             throw new MojoFailureException( "An artifacts groupId is required." );
480         }
481         final String artifactId = artifactSpec.getArtifactId();
482         if ( artifactId == null )
483         {
484             throw new MojoFailureException( "An artifacts artifactId is required." );
485         }
486         final String version = artifactSpec.getVersion();
487         if ( version == null )
488         {
489             throw new MojoFailureException( "An artifacts version number is required." );
490         }
491         final VersionRange versionRange = VersionRange.createFromVersion( version );
492         String type = artifactSpec.getType();
493         if ( type == null )
494         {
495             type = "jar";
496         }
497 
498         Artifact artifact =
499             factory.createDependencyArtifact( groupId, artifactId, versionRange, type, artifactSpec.getClassifier(),
500                                               Artifact.SCOPE_COMPILE );
501         return artifact;
502     }
503 
504     protected Set resolveArtifacts( ArtifactSpecification[] artifacts )
505         throws MojoFailureException, MojoExecutionException
506     {
507         Set artifactSet = new HashSet();
508         Artifact[] result = new Artifact[artifacts.length];
509         for ( int i = 0; i < result.length; i++ )
510         {
511             artifactSet.add( resolveArtifact( artifacts[i] ) );
512         }
513         return artifactSet;
514     }
515 
516     private Artifact getComparisonArtifact()
517         throws MojoFailureException, MojoExecutionException
518     {
519         // Find the previous version JAR and resolve it, and it's dependencies
520         VersionRange range;
521         try
522         {
523             range = VersionRange.createFromVersionSpec( comparisonVersion );
524         }
525         catch ( InvalidVersionSpecificationException e )
526         {
527             throw new MojoFailureException( "Invalid comparison version: " + e.getMessage() );
528         }
529 
530         Artifact previousArtifact;
531         try
532         {
533             previousArtifact = factory.createDependencyArtifact( project.getGroupId(), project.getArtifactId(), range,
534                                                                  project.getPackaging(), null, Artifact.SCOPE_COMPILE );
535 
536             if ( !previousArtifact.getVersionRange().isSelectedVersionKnown( previousArtifact ) )
537             {
538                 getLog().debug( "Searching for versions in range: " + previousArtifact.getVersionRange() );
539                 List availableVersions = metadataSource.retrieveAvailableVersions( previousArtifact, localRepository,
540                                                                                    project.getRemoteArtifactRepositories() );
541                 filterSnapshots( availableVersions );
542                 ArtifactVersion version = range.matchVersion( availableVersions );
543                 if ( version != null )
544                 {
545                     previousArtifact.selectVersion( version.toString() );
546                 }
547             }
548         }
549         catch ( OverConstrainedVersionException e1 )
550         {
551             throw new MojoFailureException( "Invalid comparison version: " + e1.getMessage() );
552         }
553         catch ( ArtifactMetadataRetrievalException e11 )
554         {
555             throw new MojoExecutionException( "Error determining previous version: " + e11.getMessage(), e11 );
556         }
557 
558         if ( previousArtifact.getVersion() == null )
559         {
560             getLog().info( "Unable to find a previous version of the project in the repository" );
561         }
562 
563         return previousArtifact;
564     }
565 
566     private void filterSnapshots( List versions )
567     {
568         for ( Iterator versionIterator = versions.iterator(); versionIterator.hasNext(); )
569         {
570             ArtifactVersion version = (ArtifactVersion) versionIterator.next();
571             if ( "SNAPSHOT".equals( version.getQualifier() ) )
572             {
573                 versionIterator.remove();
574             }
575         }
576     }
577 
578     public static JavaType[] createClassSet( File classes, ClassLoader thirdPartyClasses, ClassFilter classFilter )
579         throws MalformedURLException
580     {
581         ClassLoader classLoader = new URLClassLoader( new URL[]{ classes.toURI().toURL() }, thirdPartyClasses );
582 
583         Repository repository = new ClassLoaderRepository( classLoader );
584 
585         List selected = new ArrayList();
586 
587         DirectoryScanner scanner = new DirectoryScanner();
588         scanner.setBasedir( classes );
589         scanner.setIncludes( new String[]{ "**/*.class" } );
590         scanner.scan();
591 
592         String[] files = scanner.getIncludedFiles();
593 
594         for ( int i = 0; i < files.length; i++ )
595         {
596             File f = new File( classes, files[i] );
597             JavaClass clazz = extractClass( f, repository );
598             if ( classFilter.isSelected( clazz ) )
599             {
600                 selected.add( new BcelJavaType( clazz ) );
601                 repository.storeClass( clazz );
602             }
603         }
604 
605         JavaType[] ret = new JavaType[selected.size()];
606         selected.toArray( ret );
607         return ret;
608     }
609 
610     private static JavaClass extractClass( File f, Repository repository )
611         throws CheckerException
612     {
613         InputStream is = null;
614         try
615         {
616             is = new FileInputStream( f );
617 
618             ClassParser parser = new ClassParser( is, f.getName() );
619             JavaClass clazz = parser.parse();
620             clazz.setRepository( repository );
621             return clazz;
622         }
623         catch ( IOException ex )
624         {
625             throw new CheckerException( "Cannot read " + f, ex );
626         }
627         finally
628         {
629             IOUtil.close( is );
630         }
631     }
632 
633     /**
634      * Create a ClassLoader, which includes the artifacts in <code>artifacts</code>,
635      * but excludes the artifacts in <code>previousArtifacts</code>. The intention is,
636      * that we let BCEL inspect the artifacts in the latter set, using a
637      * {@link ClassLoader}, which contains the dependencies. However, the
638      * {@link ClassLoader} must not contain the jar files, which are being inspected.
639      *
640      * @param artifacts         The artifacts, from which to build a {@link ClassLoader}.
641      * @param previousArtifacts The artifacts being inspected, or null, if te
642      *                          returned {@link ClassLoader} should contain all the elements of
643      *                          <code>artifacts</code>.
644      * @return A {@link ClassLoader} which may be used to inspect the classes in
645      *         previousArtifacts.
646      * @throws MalformedURLException Failed to convert a file to an URL.
647      */
648     protected static ClassLoader createClassLoader( Collection artifacts, Set previousArtifacts )
649         throws MalformedURLException
650     {
651         URLClassLoader cl = null;
652         if ( !artifacts.isEmpty() )
653         {
654             List urls = new ArrayList( artifacts.size() );
655             for ( Iterator i = artifacts.iterator(); i.hasNext(); )
656             {
657                 Artifact artifact = (Artifact) i.next();
658                 if ( previousArtifacts == null || !previousArtifacts.contains( artifact ) )
659                 {
660                     urls.add( artifact.getFile().toURI().toURL() );
661                 }
662             }
663             if ( !urls.isEmpty() )
664             {
665                 cl = new URLClassLoader( (URL[]) urls.toArray( EMPTY_URL_ARRAY ) );
666             }
667         }
668         return cl;
669     }
670 
671     protected static Severity convertSeverity( String minSeverity )
672     {
673         Severity s;
674         if ( "info".equals( minSeverity ) )
675         {
676             s = Severity.INFO;
677         }
678         else if ( "warning".equals( minSeverity ) )
679         {
680             s = Severity.WARNING;
681         }
682         else if ( "error".equals( minSeverity ) )
683         {
684             s = Severity.ERROR;
685         }
686         else
687         {
688             s = null;
689         }
690         return s;
691     }
692 
693     protected boolean canGenerate()
694         throws MojoFailureException, MojoExecutionException
695     {
696         boolean classes = false;
697 
698         if ( classesDirectory.exists() )
699         {
700             classes = true;
701         }
702         else
703         {
704             getLog().debug( "Classes directory not found: " + classesDirectory );
705         }
706 
707         if ( !classes )
708         {
709             getLog().info( "Not generating Clirr report as there are no classes generated by the project" );
710             return false;
711         }
712 
713         if ( comparisonArtifacts == null || comparisonArtifacts.length == 0 )
714         {
715             Artifact previousArtifact = getComparisonArtifact();
716             if ( previousArtifact.getVersion() == null )
717             {
718                 getLog().info(
719                     "Not generating Clirr report as there is no previous version of the library to compare against" );
720                 return false;
721             }
722         }
723 
724         return true;
725     }
726 
727     /**
728      * Calls {@link Checker#reportDiffs(JavaType[], JavaType[])} and take care of BCEL errors.
729      *
730      * @param checker        not null
731      * @param origClasses    not null
732      * @param currentClasses not null
733      * @see Checker#reportDiffs(JavaType[], JavaType[])
734      */
735     private void reportDiffs( Checker checker, JavaType[] origClasses, JavaType[] currentClasses )
736     {
737         try
738         {
739             checker.reportDiffs( origClasses, currentClasses );
740         }
741         catch ( CheckerException e )
742         {
743             getLog().error( e.getMessage() );
744 
745             // remove class with errors
746             int matchingClasses = 0;
747             int j = 0;
748             for ( int i = 0; i < origClasses.length; i++ )
749             {
750                 if ( !e.getMessage().endsWith( origClasses[i].getName() ) )
751                 {
752                     matchingClasses++;
753                 }
754             }
755             JavaType[] origClasses2 = new JavaType[matchingClasses];
756             for ( int i = 0; i < origClasses.length; i++ )
757             {
758                 if ( !e.getMessage().endsWith( origClasses[i].getName() ) )
759                 {
760                     origClasses2[j++] = origClasses[i];
761                 }
762             }
763 
764             matchingClasses = 0;
765             j = 0;
766             for ( int i = 0; i < currentClasses.length; i++ )
767             {
768                 if ( !e.getMessage().endsWith( currentClasses[i].getName() ) )
769                 {
770                     matchingClasses++;
771                 }
772             }
773             JavaType[] currentClasses2 = new JavaType[matchingClasses];
774             for ( int i = 0; i < currentClasses.length; i++ )
775             {
776                 if ( !e.getMessage().endsWith( currentClasses[i].getName() ) )
777                 {
778                     currentClasses2[j++] = currentClasses[i];
779                 }
780             }
781 
782             reportDiffs( checker, origClasses2, currentClasses2 );
783         }
784     }
785 }