View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    *  to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    *  with the License.  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,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.creadur.whisker.app.analysis;
20  
21  import java.util.Collection;
22  import java.util.HashMap;
23  import java.util.Map;
24  import java.util.TreeSet;
25  
26  
27  import org.apache.commons.lang3.tuple.Pair;
28  import org.apache.creadur.whisker.model.Resource;
29  import org.apache.creadur.whisker.model.ResourceNamesCollator;
30  import org.apache.creadur.whisker.model.Descriptor;
31  import org.apache.creadur.whisker.scan.Directory;
32  
33  /**
34   * Analyses licenses.
35   */
36  public class LicenseAnalyst {
37  
38      /**
39       * Maps issues.
40       * @return not null
41       */
42      private static Map<ResourceDefinitionError,
43                          Collection<ResourceDescription>> buildIssueMap() {
44          final Map<ResourceDefinitionError,
45                      Collection<ResourceDescription>> results =
46                      new HashMap<ResourceDefinitionError,
47                          Collection<ResourceDescription>>();
48          for (final ResourceDefinitionError error:
49                  ResourceDefinitionError.values()) {
50              initIssues(results, error);
51          }
52          return results;
53      }
54  
55      /**
56       * Builds an initial map.
57       * @param results not null
58       * @param error not null
59       */
60      private static void initIssues(
61              final Map<
62                  ResourceDefinitionError,
63                  Collection<ResourceDescription>> results,
64              ResourceDefinitionError error) {
65          results.put(error, new TreeSet<ResourceDescription>());
66      }
67  
68      /** Directories analysed. */
69      private final Collection<Directory> directories;
70      /** Maps resource errors to resources. */
71      private final Map<ResourceDefinitionError,
72                      Collection<ResourceDescription>> issues;
73  
74      /**
75       * Constructs empty analyst.
76       */
77      public LicenseAnalyst() {
78          this(null);
79      }
80  
81      /**
82       * Analyse the given directories.
83       * @param directories not null
84       */
85      public LicenseAnalyst(final Collection<Directory> directories) {
86          super();
87          this.directories = directories;
88          issues = buildIssueMap();
89      }
90  
91      /**
92       * Discover discrepancies between meta-data and source directories.
93       * @param work not null
94       * @return this, not null
95       */
96      public LicenseAnalyst analyse(final Descriptor work) {
97          if (directories == null) {
98              final ResourceNamesCollator collator =
99                      new ResourceNamesCollator();
100             work.traverse(collator);
101             analyseDuplicates(collator);
102 
103             final ResourceSourceAuditor sourceAuditor =
104                     new ResourceSourceAuditor();
105             work.traverse(sourceAuditor);
106             analyse(sourceAuditor);
107         } else {
108             for (final Directory directory: directories) {
109                 final ResourceNamesCollator collator =
110                         new ResourceNamesCollator();
111                 work.traverseDirectory(collator, directory.getName());
112                 analyseLicenses(directory, collator);
113                 analyseDuplicates(collator);
114 
115                 final ResourceSourceAuditor sourceAuditor = new
116                         ResourceSourceAuditor();
117                 work.traverseDirectory(sourceAuditor, directory.getName());
118                 analyse(sourceAuditor);
119             }
120         }
121         return this;
122     }
123 
124     /**
125      * Analyse the directories with this auditor.
126      * @param sourceAuditor not null
127      */
128     private void analyse(final ResourceSourceAuditor sourceAuditor) {
129         addIssues(sourceAuditor.getResourcesMissingSource(),
130                 ResourceDefinitionError.MISSING_SOURCE);
131     }
132 
133     /**
134      * Were any errors found?
135      * @return true when the meta-data is valid,
136      * false otherwise
137      */
138     public boolean isValid() {
139         for (final ResourceDefinitionError error:
140                 ResourceDefinitionError.values()) {
141             if (!getIssues(error).isEmpty()) {
142                 return false;
143             }
144         }
145         return true;
146     }
147 
148     /**
149      * Checks the descriptor against the source directories.
150      * @param work not null
151      * @return valid meta-data
152      * @throws ResourceDefinitionException when issues are found
153      */
154     public Descriptor validate(final Descriptor work)
155             throws ResourceDefinitionException {
156         analyse(work);
157         if (isValid()) {
158             return work;
159         } else {
160             throw new ResourceDefinitionException(issues);
161         }
162 
163     }
164 
165     /**
166      * Discovers duplicates.
167      * @param collator not null
168      */
169     private void analyseDuplicates(
170             final ResourceNamesCollator collator) {
171         addIssues(collator.getDuplicates(),
172                 ResourceDefinitionError.DUPLICATE);
173     }
174 
175     /**
176      * Adds the issues to the store.
177      * @param resources not null
178      * @param error not null
179      */
180     private void addIssues(
181             final Collection<Pair<
182                 org.apache.creadur.whisker.model.WithinDirectory,
183                 Resource>> resources,
184             final ResourceDefinitionError error) {
185         for (Pair<
186                 org.apache.creadur.whisker.model.WithinDirectory,
187                 Resource> duplicate: resources) {
188             getIssues(error).add(
189                     new ResourceDescription(
190                             duplicate.getLeft().getName(),
191                             duplicate.getRight().getName()));
192         }
193     }
194 
195     /**
196      * Checks for too many or few licenses.
197      * @param directory not null
198      * @param collator not null
199      */
200     private void analyseLicenses(final Directory directory,
201             final ResourceNamesCollator collator) {
202         analyseExtraLicenses(directory, collator);
203         analyseMissingLicenses(directory, collator);
204     }
205 
206 
207     /**
208      * Checks for to many licenses.
209      * @param directory not null
210      * @param collator not null
211      */
212     @SuppressWarnings("PMD.EmptyIfStmt")
213     private void analyseExtraLicenses(final Directory directory,
214             final ResourceNamesCollator collator) {
215         final Collection<String> actualResources = directory.getContents();
216         for (final String resourceLicense: collator.getNames()) {
217             if (actualResources.contains(resourceLicense)) {
218                 // Fine
219             } else {
220                 getExtraLicenses().add(
221                         new ResourceDescription(
222                                 directory.getName(),
223                                 resourceLicense));
224             }
225         }
226     }
227 
228     /**
229      * Checks for too few licenses.
230      * @param directory not null
231      * @param collator not null
232      */
233     @SuppressWarnings("PMD.EmptyIfStmt")
234     private void analyseMissingLicenses(final Directory directory,
235             final ResourceNamesCollator collator) {
236         final Collection<String> licensedResources = collator.getNames();
237         for (final String actualResource: directory.getContents()) {
238             if (licensedResources.contains(actualResource)) {
239                 // Fine
240             } else {
241                 getMissingLicenses().add(
242                         new ResourceDescription(
243                                 directory.getName(),
244                                 actualResource));
245             }
246         }
247     }
248 
249     /**
250      * Gets resources whose sources are missing.
251      * @return not null, possibly empty
252      */
253     public Collection<ResourceDescription> getResourcesMissingSources() {
254         return getIssues(ResourceDefinitionError.MISSING_SOURCE);
255     }
256 
257     /**
258      * Gets surplus licenses.
259      * @return not null, possibly empty
260      */
261     public Collection<ResourceDescription> getExtraLicenses() {
262         return getIssues(ResourceDefinitionError.EXTRA_LICENSE);
263     }
264 
265     /**
266      * Gets missing license.
267      * @return not null, possibly empty
268      */
269     public Collection<ResourceDescription> getMissingLicenses() {
270         return getIssues(ResourceDefinitionError.MISSING_LICENSE);
271     }
272 
273     /**
274      * Gets duplicate resources.
275      * @return the duplicates
276      */
277     public Collection<ResourceDescription> getDuplicates() {
278         return getIssues(ResourceDefinitionError.DUPLICATE);
279     }
280 
281     /**
282      * Gets issues by type.
283      * @param ofType not null
284      * @return issues of given type, not null, possibly empty
285      */
286     public Collection<ResourceDescription> getIssues(
287             ResourceDefinitionError ofType) {
288         return issues.get(ofType);
289     }
290 
291     /**
292      * Describes suitably for logging.
293      * @return something suitable for logging
294      * @see java.lang.Object#toString()
295      */
296     @Override
297     public String toString() {
298         return "LicenseAnalyst [directories=" + directories + "]";
299     }
300 }