View Javadoc
1   package org.apache.maven.shared.dependency.analyzer;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.util.Arrays;
23  import java.util.Collections;
24  import java.util.HashSet;
25  import java.util.Iterator;
26  import java.util.LinkedHashSet;
27  import java.util.Set;
28  
29  import org.apache.maven.artifact.Artifact;
30  
31  /**
32   * Project dependencies analysis result.
33   * 
34   * @author <a href="mailto:markhobson@gmail.com">Mark Hobson</a>
35   * @version $Id$
36   */
37  public class ProjectDependencyAnalysis
38  {
39      // fields -----------------------------------------------------------------
40  
41      private final Set<Artifact> usedDeclaredArtifacts;
42  
43      private final Set<Artifact> usedUndeclaredArtifacts;
44  
45      private final Set<Artifact> unusedDeclaredArtifacts;
46  
47      private final Set<Artifact> testArtifactsWithNonTestScope;
48  
49      // constructors -----------------------------------------------------------
50  
51      public ProjectDependencyAnalysis()
52      {
53          this( null, null, null, null );
54      }
55  
56      // constructor to maintain compatibility with old API
57      public ProjectDependencyAnalysis( Set<Artifact> usedDeclaredArtifacts, Set<Artifact> usedUndeclaredArtifacts,
58                                        Set<Artifact> unusedDeclaredArtifacts )
59      {
60          this.usedDeclaredArtifacts = safeCopy( usedDeclaredArtifacts );
61          this.usedUndeclaredArtifacts = safeCopy( usedUndeclaredArtifacts );
62          this.unusedDeclaredArtifacts = safeCopy( unusedDeclaredArtifacts );
63          this.testArtifactsWithNonTestScope = new HashSet<>();
64      }
65  
66      public ProjectDependencyAnalysis( Set<Artifact> usedDeclaredArtifacts, Set<Artifact> usedUndeclaredArtifacts,
67                                        Set<Artifact> unusedDeclaredArtifacts,
68                                        Set<Artifact> testArtifactsWithNonTestScope )
69      {
70          this.usedDeclaredArtifacts = safeCopy( usedDeclaredArtifacts );
71          this.usedUndeclaredArtifacts = safeCopy( usedUndeclaredArtifacts );
72          this.unusedDeclaredArtifacts = safeCopy( unusedDeclaredArtifacts );
73          this.testArtifactsWithNonTestScope = safeCopy( testArtifactsWithNonTestScope );
74      }
75  
76      // public methods ---------------------------------------------------------
77  
78      /**
79       * Used and declared artifacts.
80       * @return {@link Artifact}
81       */
82      public Set<Artifact> getUsedDeclaredArtifacts()
83      {
84          return safeCopy( usedDeclaredArtifacts );
85      }
86  
87      /**
88       * Used but not declared artifacts.
89       * @return {@link Artifact}
90       */
91      public Set<Artifact> getUsedUndeclaredArtifacts()
92      {
93          return safeCopy( usedUndeclaredArtifacts );
94      }
95  
96      /**
97       * Unused but declared artifacts.
98       * @return {@link Artifact}
99       */
100     public Set<Artifact> getUnusedDeclaredArtifacts()
101     {
102         return safeCopy( unusedDeclaredArtifacts );
103     }
104 
105     /**
106      * Test Artifacts that have a non-test scope
107      * @return {@link Artifact}
108      */
109     public Set<Artifact> getTestArtifactsWithNonTestScope()
110     {
111         return safeCopy( testArtifactsWithNonTestScope );
112     }
113 
114     /**
115      * Filter not-compile scoped artifacts from unused declared.
116      * 
117      * @return updated project dependency analysis
118      * @since 1.3
119      */
120     public ProjectDependencyAnalysis ignoreNonCompile()
121     {
122         Set<Artifact> filteredUnusedDeclared = new HashSet<Artifact>( unusedDeclaredArtifacts );
123         for ( Iterator<Artifact> iter = filteredUnusedDeclared.iterator(); iter.hasNext(); )
124         {
125             Artifact artifact = iter.next();
126             if ( !artifact.getScope().equals( Artifact.SCOPE_COMPILE ) )
127             {
128                 iter.remove();
129             }
130         }
131 
132         return new ProjectDependencyAnalysis( usedDeclaredArtifacts, usedUndeclaredArtifacts, filteredUnusedDeclared,
133                 testArtifactsWithNonTestScope );
134     }
135 
136     /**
137      * Force use status of some declared dependencies, to manually fix consequences of bytecode-level analysis which
138      * happens to not detect some effective use (constants, annotation with source-retention, javadoc).
139      * 
140      * @param forceUsedDependencies dependencies to move from "unused-declared" to "used-declared", with
141      *            <code>groupId:artifactId</code> format
142      * @return updated project dependency analysis
143      * @throws ProjectDependencyAnalyzerException if dependencies forced were either not declared or already detected as
144      *             used
145      * @since 1.3
146      */
147     public ProjectDependencyAnalysis forceDeclaredDependenciesUsage( String[] forceUsedDependencies )
148         throws ProjectDependencyAnalyzerException
149     {
150         Set<String> forced = new HashSet<String>( Arrays.asList( forceUsedDependencies ) );
151 
152         Set<Artifact> forcedUnusedDeclared = new HashSet<Artifact>( unusedDeclaredArtifacts );
153         Set<Artifact> forcedUsedDeclared = new HashSet<Artifact>( usedDeclaredArtifacts );
154 
155         for ( Iterator<Artifact> iter = forcedUnusedDeclared.iterator(); iter.hasNext(); )
156         {
157             Artifact artifact = iter.next();
158 
159             if ( forced.remove( artifact.getGroupId() + ':' + artifact.getArtifactId() ) )
160             {
161                 // ok, change artifact status from unused-declared to used-declared
162                 iter.remove();
163                 forcedUsedDeclared.add( artifact );
164             }
165         }
166 
167         if ( !forced.isEmpty() )
168         {
169             // trying to force dependencies as used-declared which were not declared or already detected as used
170             Set<String> used = new HashSet<String>();
171             for ( Artifact artifact : usedDeclaredArtifacts )
172             {
173                 String id = artifact.getGroupId() + ':' + artifact.getArtifactId();
174                 if ( forced.remove( id ) )
175                 {
176                     used.add( id );
177                 }
178             }
179 
180             StringBuilder builder = new StringBuilder();
181             if ( !forced.isEmpty() )
182             {
183                 builder.append( "not declared: " ).append( forced );
184             }
185             if ( !used.isEmpty() )
186             {
187                 if ( builder.length() > 0 )
188                 {
189                     builder.append( " and " );
190                 }
191                 builder.append( "declared but already detected as used: " ).append( used );
192             }
193             throw new ProjectDependencyAnalyzerException( "Trying to force use of dependencies which are " + builder );
194         }
195 
196         return new ProjectDependencyAnalysis( forcedUsedDeclared, usedUndeclaredArtifacts, forcedUnusedDeclared,
197                 testArtifactsWithNonTestScope );
198     }
199 
200     // Object methods ---------------------------------------------------------
201 
202     /*
203      * @see java.lang.Object#hashCode()
204      */
205     public int hashCode()
206     {
207         int hashCode = getUsedDeclaredArtifacts().hashCode();
208         hashCode = ( hashCode * 37 ) + getUsedUndeclaredArtifacts().hashCode();
209         hashCode = ( hashCode * 37 ) + getUnusedDeclaredArtifacts().hashCode();
210         hashCode = ( hashCode * 37 ) + getTestArtifactsWithNonTestScope().hashCode();
211 
212         return hashCode;
213     }
214 
215     /*
216      * @see java.lang.Object#equals(java.lang.Object)
217      */
218     public boolean equals( Object object )
219     {
220         if ( object instanceof ProjectDependencyAnalysis )
221         {
222             ProjectDependencyAnalysis analysis = (ProjectDependencyAnalysis) object;
223 
224             return getUsedDeclaredArtifacts().equals( analysis.getUsedDeclaredArtifacts() )
225                 && getUsedUndeclaredArtifacts().equals( analysis.getUsedUndeclaredArtifacts() )
226                 && getUnusedDeclaredArtifacts().equals( analysis.getUnusedDeclaredArtifacts() )
227                 && getTestArtifactsWithNonTestScope().equals( analysis.getTestArtifactsWithNonTestScope() );
228         }
229 
230         return false;
231     }
232 
233     /*
234      * @see java.lang.Object#toString()
235      */
236     public String toString()
237     {
238         StringBuilder buffer = new StringBuilder();
239 
240         if ( !getUsedDeclaredArtifacts().isEmpty() )
241         {
242             buffer.append( "usedDeclaredArtifacts=" ).append( getUsedDeclaredArtifacts() );
243         }
244 
245         if ( !getUsedUndeclaredArtifacts().isEmpty() )
246         {
247             if ( buffer.length() > 0 )
248             {
249                 buffer.append( "," );
250             }
251 
252             buffer.append( "usedUndeclaredArtifacts=" ).append( getUsedUndeclaredArtifacts() );
253         }
254 
255         if ( !getUnusedDeclaredArtifacts().isEmpty() )
256         {
257             if ( buffer.length() > 0 )
258             {
259                 buffer.append( "," );
260             }
261 
262             buffer.append( "unusedDeclaredArtifacts=" ).append( getUnusedDeclaredArtifacts() );
263         }
264 
265         if ( !getTestArtifactsWithNonTestScope().isEmpty() )
266         {
267             if ( buffer.length() > 0 )
268             {
269                 buffer.append( "," );
270             }
271 
272             buffer.append( "testArtifactsWithNonTestScope=" ).append( getTestArtifactsWithNonTestScope() );
273         }
274 
275         buffer.insert( 0, "[" );
276         buffer.insert( 0, getClass().getName() );
277 
278         buffer.append( "]" );
279 
280         return buffer.toString();
281     }
282 
283     // private methods --------------------------------------------------------
284 
285     private Set<Artifact> safeCopy( Set<Artifact> set )
286     {
287         return ( set == null ) ? Collections.<Artifact>emptySet()
288                         : Collections.unmodifiableSet( new LinkedHashSet<Artifact>( set ) );
289     }
290 }