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      // constructors -----------------------------------------------------------
48  
49      public ProjectDependencyAnalysis()
50      {
51          this( null, null, null );
52      }
53  
54      public ProjectDependencyAnalysis( Set<Artifact> usedDeclaredArtifacts, Set<Artifact> usedUndeclaredArtifacts,
55                                        Set<Artifact> unusedDeclaredArtifacts )
56      {
57          this.usedDeclaredArtifacts = safeCopy( usedDeclaredArtifacts );
58          this.usedUndeclaredArtifacts = safeCopy( usedUndeclaredArtifacts );
59          this.unusedDeclaredArtifacts = safeCopy( unusedDeclaredArtifacts );
60      }
61  
62      // public methods ---------------------------------------------------------
63  
64      /**
65       * Used and declared artifacts.
66       * @return {@link Artifact}
67       */
68      public Set<Artifact> getUsedDeclaredArtifacts()
69      {
70          return usedDeclaredArtifacts;
71      }
72  
73      /**
74       * Used but not declared artifacts.
75       * @return {@link Artifact}
76       */
77      public Set<Artifact> getUsedUndeclaredArtifacts()
78      {
79          return usedUndeclaredArtifacts;
80      }
81  
82      /**
83       * Unused but declared artifacts.
84       * @return {@link Artifact}
85       */
86      public Set<Artifact> getUnusedDeclaredArtifacts()
87      {
88          return unusedDeclaredArtifacts;
89      }
90  
91      /**
92       * Filter not-compile scoped artifacts from unused declared.
93       * 
94       * @return updated project dependency analysis
95       * @since 1.3
96       */
97      public ProjectDependencyAnalysis ignoreNonCompile()
98      {
99          Set<Artifact> filteredUnusedDeclared = new HashSet<Artifact>( unusedDeclaredArtifacts );
100         for ( Iterator<Artifact> iter = filteredUnusedDeclared.iterator(); iter.hasNext(); )
101         {
102             Artifact artifact = iter.next();
103             if ( !artifact.getScope().equals( Artifact.SCOPE_COMPILE ) )
104             {
105                 iter.remove();
106             }
107         }
108 
109         return new ProjectDependencyAnalysis( usedDeclaredArtifacts, usedUndeclaredArtifacts, filteredUnusedDeclared );
110     }
111 
112     /**
113      * Force use status of some declared dependencies, to manually fix consequences of bytecode-level analysis which
114      * happens to not detect some effective use (constants, annotation with source-retention, javadoc).
115      * 
116      * @param forceUsedDependencies dependencies to move from "unused-declared" to "used-declared", with
117      *            <code>groupId:artifactId</code> format
118      * @return updated project dependency analysis
119      * @throws ProjectDependencyAnalyzerException if dependencies forced were either not declared or already detected as
120      *             used
121      * @since 1.3
122      */
123     public ProjectDependencyAnalysis forceDeclaredDependenciesUsage( String[] forceUsedDependencies )
124         throws ProjectDependencyAnalyzerException
125     {
126         Set<String> forced = new HashSet<String>( Arrays.asList( forceUsedDependencies ) );
127 
128         Set<Artifact> forcedUnusedDeclared = new HashSet<Artifact>( unusedDeclaredArtifacts );
129         Set<Artifact> forcedUsedDeclared = new HashSet<Artifact>( usedDeclaredArtifacts );
130 
131         for ( Iterator<Artifact> iter = forcedUnusedDeclared.iterator(); iter.hasNext(); )
132         {
133             Artifact artifact = iter.next();
134 
135             if ( forced.remove( artifact.getGroupId() + ':' + artifact.getArtifactId() ) )
136             {
137                 // ok, change artifact status from unused-declared to used-declared
138                 iter.remove();
139                 forcedUsedDeclared.add( artifact );
140             }
141         }
142 
143         if ( !forced.isEmpty() )
144         {
145             // trying to force dependencies as used-declared which were not declared or already detected as used
146             Set<String> used = new HashSet<String>();
147             for ( Artifact artifact : usedDeclaredArtifacts )
148             {
149                 String id = artifact.getGroupId() + ':' + artifact.getArtifactId();
150                 if ( forced.remove( id ) )
151                 {
152                     used.add( id );
153                 }
154             }
155 
156             StringBuilder builder = new StringBuilder();
157             if ( !forced.isEmpty() )
158             {
159                 builder.append( "not declared: " ).append( forced );
160             }
161             if ( !used.isEmpty() )
162             {
163                 if ( builder.length() > 0 )
164                 {
165                     builder.append( " and " );
166                 }
167                 builder.append( "declared but already detected as used: " ).append( used );
168             }
169             throw new ProjectDependencyAnalyzerException( "Trying to force use of dependencies which are " + builder );
170         }
171 
172         return new ProjectDependencyAnalysis( forcedUsedDeclared, usedUndeclaredArtifacts, forcedUnusedDeclared );
173     }
174 
175     // Object methods ---------------------------------------------------------
176 
177     /*
178      * @see java.lang.Object#hashCode()
179      */
180     public int hashCode()
181     {
182         int hashCode = getUsedDeclaredArtifacts().hashCode();
183         hashCode = ( hashCode * 37 ) + getUsedUndeclaredArtifacts().hashCode();
184         hashCode = ( hashCode * 37 ) + getUnusedDeclaredArtifacts().hashCode();
185 
186         return hashCode;
187     }
188 
189     /*
190      * @see java.lang.Object#equals(java.lang.Object)
191      */
192     public boolean equals( Object object )
193     {
194         if ( object instanceof ProjectDependencyAnalysis )
195         {
196             ProjectDependencyAnalysis analysis = (ProjectDependencyAnalysis) object;
197 
198             return getUsedDeclaredArtifacts().equals( analysis.getUsedDeclaredArtifacts() )
199                 && getUsedUndeclaredArtifacts().equals( analysis.getUsedUndeclaredArtifacts() )
200                 && getUnusedDeclaredArtifacts().equals( analysis.getUnusedDeclaredArtifacts() );
201         }
202 
203         return false;
204     }
205 
206     /*
207      * @see java.lang.Object#toString()
208      */
209     public String toString()
210     {
211         StringBuilder buffer = new StringBuilder();
212 
213         if ( !getUsedDeclaredArtifacts().isEmpty() )
214         {
215             buffer.append( "usedDeclaredArtifacts=" ).append( getUsedDeclaredArtifacts() );
216         }
217 
218         if ( !getUsedUndeclaredArtifacts().isEmpty() )
219         {
220             if ( buffer.length() > 0 )
221             {
222                 buffer.append( "," );
223             }
224 
225             buffer.append( "usedUndeclaredArtifacts=" ).append( getUsedUndeclaredArtifacts() );
226         }
227 
228         if ( !getUnusedDeclaredArtifacts().isEmpty() )
229         {
230             if ( buffer.length() > 0 )
231             {
232                 buffer.append( "," );
233             }
234 
235             buffer.append( "unusedDeclaredArtifacts=" ).append( getUnusedDeclaredArtifacts() );
236         }
237 
238         buffer.insert( 0, "[" );
239         buffer.insert( 0, getClass().getName() );
240 
241         buffer.append( "]" );
242 
243         return buffer.toString();
244     }
245 
246     // private methods --------------------------------------------------------
247 
248     private Set<Artifact> safeCopy( Set<Artifact> set )
249     {
250         return ( set == null ) ? Collections.<Artifact>emptySet()
251                         : Collections.unmodifiableSet( new LinkedHashSet<Artifact>( set ) );
252     }
253 }