View Javadoc

1   package de.matthias_burbach.deputy.core.rule;
2   
3   import java.util.ArrayList;
4   import java.util.Collection;
5   import java.util.Collections;
6   import java.util.HashMap;
7   import java.util.Iterator;
8   import java.util.List;
9   import java.util.Map;
10  
11  import de.matthias_burbach.deputy.core.project.ProjectComparator;
12  import de.matthias_burbach.deputy.core.project.ProjectQualifier;
13  
14  /***
15   * Holds all the rules to be used in the dependency recursion of a project.
16   *
17   * @author Matthias Burbach
18   */
19  public class RuleSet {
20      /***
21       * The default rule 'LATEST RELEASE'.
22       */
23      public static final String DEFAULT_RULE_LATEST_RELEASE = "LATEST RELEASE";
24  
25      /***
26       * The default rule 'SNAPSHOT'.
27       */
28      public static final String DEFAULT_RULE_SNAPSHOT = "SNAPSHOT";
29  
30      /***
31       * The default rule 'LATEST RELEASE (no scan)'.
32       * <p/>
33       * The latest release will only be searched for in the scope of the
34       * dependency graph. No repository will be scanned for later releases
35       * even if it is configured to be 'scannable'.
36       */
37      public static final String DEFAULT_RULE_LATEST_RELEASE_NO_SCAN
38          = "LATEST RELEASE (no scan)";
39  
40      /***
41       * The default rule 'PRESENT RELEASE'.
42       * <p/>
43       * This rule is particularly useful if you want to minimize the number of
44       * changes that occur when applying the rules. Whenever a default choice
45       * must be performed, it will be the version that existed in the POM just
46       * before applying the rules. If the artifact was not present in the POM
47       * so far or if it was a SNAPSHOT the latest release will be chosen as
48       * default.
49       */
50      public static final String DEFAULT_RULE_PRESENT_RELEASE
51          = "PRESENT RELEASE";
52  
53      /***
54       * The current default rule. See DEFAULT_RULE_* constants.
55       */
56      private String defaultRule = DEFAULT_RULE_LATEST_RELEASE;
57  
58      /***
59      * Maps artifactId to object of type EnforcementRule.
60      */
61      private Map enforcements = new HashMap();
62  
63      /***
64       * Maps artifactId and version to object of type DeprecationRule.
65       */
66      private Map deprecations = new HashMap();
67  
68      /***
69       * Maps artifactId (and possibly version) to object of type ReplacementRule.
70       */
71      private Map replacements = new HashMap();
72  
73      /***
74       * Maps artifactId (and possibly version) to object of type RemovalRule.
75       */
76      private Map removals = new HashMap();
77  
78      /***
79       * Maps artifactId (and possibly version) to object of type RetentionRule.
80       */
81      private Map retentions = new HashMap();
82  
83      /***
84       * The change listeners to be notified when this rule set changes.
85       */
86      private List changeListeners = new ArrayList();
87  
88      /***
89       * The comparator used to sort lists of enforcement and deprecation rules.
90       */
91      private ProjectComparator projectComparator = new ProjectComparator();
92  
93      /***
94       * The comparator used to sort lists of replacement rules.
95       */
96      private ProjectComparator replacementComparator = new ProjectComparator() {
97          /*
98           * (non-Javadoc)
99           * @see de.matthias_burbach.deputy.core.ProjectComparator
100          *          #getArtifactId1(
101          *              de.matthias_burbach.deputy.core.ProjectQualifier,
102          *              de.matthias_burbach.deputy.core.ProjectQualifier)
103          */
104         /***
105          * {@inheritDoc}
106          */
107         protected String getArtifactId1(
108                 final ProjectQualifier pq1, final ProjectQualifier pq2) {
109             return ((ReplacementRule) pq1).getDisplacedArtifactId();
110         }
111 
112         /*
113          * (non-Javadoc)
114          * @see de.matthias_burbach.deputy.core.ProjectComparator
115          *          #getArtifactId2(
116          *              de.matthias_burbach.deputy.core.ProjectQualifier,
117          *              de.matthias_burbach.deputy.core.ProjectQualifier)
118          */
119         /***
120          * {@inheritDoc}
121          */
122         protected String getArtifactId2(
123                 final ProjectQualifier pq1, final ProjectQualifier pq2) {
124             return ((ReplacementRule) pq2).getDisplacedArtifactId();
125         }
126 
127         /*
128          * (non-Javadoc)
129          * @see de.matthias_burbach.deputy.core.ProjectComparator
130          *          #getVersion1(
131          *              de.matthias_burbach.deputy.core.ProjectQualifier,
132          *              de.matthias_burbach.deputy.core.ProjectQualifier)
133          */
134         /***
135          * {@inheritDoc}
136          */
137         protected String getVersion1(
138                 final ProjectQualifier pq1, final ProjectQualifier pq2) {
139             String result = ((ReplacementRule) pq1).getDisplacedVersion();
140             if (result == null) {
141                 result = "";
142             }
143             return result;
144         }
145 
146         /*
147          * (non-Javadoc)
148          * @see de.matthias_burbach.deputy.core.ProjectComparator
149          *          #getVersion2(
150          *              de.matthias_burbach.deputy.core.ProjectQualifier,
151          *              de.matthias_burbach.deputy.core.ProjectQualifier)
152          */
153         /***
154          * {@inheritDoc}
155          */
156         protected String getVersion2(
157                 final ProjectQualifier pq1, final ProjectQualifier pq2) {
158             String result = ((ReplacementRule) pq2).getDisplacedVersion();
159             if (result == null) {
160                 result = "";
161             }
162             return result;
163         }
164     };
165 
166     /***
167      * The comparator used to sort lists of removal rules.
168      */
169     private ProjectComparator removalComparator = new ProjectComparator() {
170         /*
171          * (non-Javadoc)
172          * @see de.matthias_burbach.deputy.core.ProjectComparator
173          *          #getVersion1(
174          *              de.matthias_burbach.deputy.core.ProjectQualifier,
175          *              de.matthias_burbach.deputy.core.ProjectQualifier)
176          */
177         /***
178          * {@inheritDoc}
179          */
180         protected String getVersion1(
181                 final ProjectQualifier pq1, final ProjectQualifier pq2) {
182             String result = pq1.getVersion();
183             if (result == null) {
184                 result = "";
185             }
186             return result;
187         }
188 
189         /*
190          * (non-Javadoc)
191          * @see de.matthias_burbach.deputy.core.ProjectComparator
192          *          #getVersion2(
193          *              de.matthias_burbach.deputy.core.ProjectQualifier,
194          *              de.matthias_burbach.deputy.core.ProjectQualifier)
195          */
196         /***
197          * {@inheritDoc}
198          */
199         protected String getVersion2(
200                 final ProjectQualifier pq1, final ProjectQualifier pq2) {
201             String result = pq2.getVersion();
202             if (result == null) {
203                 result = "";
204             }
205             return result;
206         }
207     };
208 
209     /***
210      * Adds a rule to this rule set.
211      *
212      * @param rule The rule to add.
213      */
214     public void add(final Rule rule) {
215         String key = getKey(rule);
216         Rule oldRule = (Rule) getMap(rule).get(key);
217         if (oldRule != null) {
218             remove(oldRule);
219         }
220         getMap(rule).put(key, rule);
221         int index = getAllRules().indexOf(rule);
222         fireAddedRule(rule, index);
223     }
224 
225     /***
226      * Removes a rule from this rule set.
227      *
228      * @param rule The rule to remove.
229      */
230     public void remove(final Rule rule) {
231         if (rule != null) {
232             Object o = null;
233             if (enforcements.containsValue(rule)) {
234                 o = enforcements.remove(getKey(rule));
235             } else if (deprecations.containsValue(rule)) {
236                 o = deprecations.remove(getKey(rule));
237             } else if (replacements.containsValue(rule)) {
238                 o = replacements.remove(getKey(rule));
239             } else if (replacements.containsValue(rule)) {
240                 o = replacements.remove(getKey(rule));
241             } else if (removals.containsValue(rule)) {
242                 o = removals.remove(getKey(rule));
243             } else if (retentions.containsValue(rule)) {
244                 o = retentions.remove(getKey(rule));
245             }
246             if (o != null) {
247                 fireRemovedRule(rule);
248             }
249         }
250     }
251 
252     /***
253      * @return The number of rules enforcing a SNAPSHOT version of some
254      *         artifact.
255      */
256     public int getNumberOfSnapshotEnforcements() {
257         int result = 0;
258         Collection rules = enforcements.values();
259         for (Iterator iter = rules.iterator(); iter.hasNext();) {
260             EnforcementRule rule = (EnforcementRule) iter.next();
261             if (rule.getVersion().indexOf("SNAPSHOT") != -1) {
262                 result = result + 1;
263             }
264         }
265         return result;
266     }
267 
268     /***
269      * @return The number of derived rules.
270      */
271     public int getNumberOfDerivedRules() {
272         int result = 0;
273         Collection rules = getAllRules();
274         for (Iterator iter = rules.iterator(); iter.hasNext();) {
275             Rule rule = (Rule) iter.next();
276             if (rule.isDerived()) {
277                 result = result + 1;
278             }
279         }
280         return result;
281     }
282 
283     /***
284      * Removes all SNAPSHOT enforcement rules at once.
285      */
286     public void removeAllSnapshotEnforcements() {
287         List clone = new ArrayList(enforcements.values());
288         for (Iterator iter = clone.iterator(); iter.hasNext();) {
289             EnforcementRule rule = (EnforcementRule) iter.next();
290             if (rule.getVersion().indexOf("SNAPSHOT") != -1) {
291                 remove(rule);
292             }
293         }
294     }
295 
296     /***
297      * Adds all rules of the rule set to this rule set.
298      *
299      * @param ruleSet The rule set whose rules to add.
300      */
301     public void add(final RuleSet ruleSet) {
302         enforcements.putAll(ruleSet.enforcements);
303         deprecations.putAll(ruleSet.deprecations);
304         replacements.putAll(ruleSet.replacements);
305         removals.putAll(ruleSet.removals);
306         retentions.putAll(ruleSet.retentions);
307     }
308 
309     /***
310      * @return The list of rules of type {@link EnforcementRule}.
311      */
312     public List getEnforcementRules() {
313         List enforcementsList = new ArrayList(enforcements.values());
314         Collections.sort(enforcementsList, projectComparator);
315         return enforcementsList;
316     }
317 
318     /***
319      * @return The list of rules of type {@link DeprecationRule}.
320      */
321     public List getDeprecationRules() {
322         List deprecationsList = new ArrayList(deprecations.values());
323         Collections.sort(deprecationsList, projectComparator);
324         return deprecationsList;
325     }
326 
327     /***
328      * @return The list of rules of type {@link ReplacementRule}.
329      */
330     public List getReplacementRules() {
331         List replacementsList = new ArrayList(replacements.values());
332         Collections.sort(replacementsList, replacementComparator);
333         return replacementsList;
334     }
335 
336     /***
337      * @return The list of rules of type {@link RemovalRule}.
338      */
339     public List getRemovalRules() {
340         List removalsList = new ArrayList(removals.values());
341         Collections.sort(removalsList, removalComparator);
342         return removalsList;
343     }
344 
345     /***
346      * @return The list of rules of type {@link RetentionRule}.
347      */
348     public List getRetentionRules() {
349         List retentionsList = new ArrayList(retentions.values());
350         Collections.sort(retentionsList, removalComparator);
351         return retentionsList;
352     }
353 
354     /***
355      * @return The list of all rules of base type {@link Rule}.
356      */
357     public List getAllRules() {
358         List result = getEnforcementRules();
359         result.addAll(getDeprecationRules());
360         result.addAll(getReplacementRules());
361         result.addAll(getRemovalRules());
362         result.addAll(getRetentionRules());
363         return result;
364     }
365 
366     /***
367      * @param artifactId The id of the artifact to get the version to enforce
368      *                   for
369      * @return The version to be enforced for the artifact accoding to this rule
370      *         set.
371      */
372     public String getEnforcedVersion(final String artifactId) {
373         String result = null;
374         EnforcementRule rule = (EnforcementRule) enforcements.get(artifactId);
375         if (rule != null) {
376             result = rule.getVersion();
377         }
378         return result;
379     }
380 
381     /***
382      * @param artifactId The id of the artifact to get an enforcement rule for.
383      * @return An enforcement rule or <code>null</code>.
384      */
385     public EnforcementRule getEnforcementRule(final String artifactId) {
386         EnforcementRule result = (EnforcementRule) enforcements.get(artifactId);
387         return result;
388     }
389 
390     /***
391      * @param artifactId The id of the artifact to get a deprecation rule for.
392      * @param version The version of the artifact to get a deprecation rule for.
393      * @return A deprecation rule or <code>null</code>.
394      */
395     public DeprecationRule getDeprecationRule(
396             final String artifactId,
397             final String version) {
398         DeprecationRule result =
399             (DeprecationRule) deprecations.get(artifactId + "-" + version);
400         return result;
401     }
402 
403     /***
404      * @param artifactId The id of the artifact to check for.
405      * @param version The version of the artifact to check for.
406      * @return <code>true</code> if the version of this artifact is deprecated
407      */
408     public boolean isDeprecated(final String artifactId, final String version) {
409         boolean result = false;
410         DeprecationRule rule =
411             (DeprecationRule) deprecations.get(artifactId + "-" + version);
412         if (rule != null) {
413             result = true;
414         }
415         return result;
416     }
417 
418     /***
419      * @param artifactId The id of the artifact to get a replacement rule for.
420      * @param version The version of the artifact to get a replacement rule for.
421      *                Can be <code>null</code>.
422      * @return A replacement rule or <code>null</code>.
423      */
424     public ReplacementRule getReplacementRule(
425             final String artifactId,
426             final String version) {
427         ReplacementRule result = null;
428         if (version != null) {
429             result =
430                 (ReplacementRule) replacements.get(artifactId + "-" + version);
431         }
432         if (result == null) {
433             result = (ReplacementRule) replacements.get(artifactId);
434         }
435         return result;
436     }
437 
438     /***
439      * @param artifactId The id of the artifact to get a retention rule for.
440      * @param version The version of the artifact to get a retention rule for.
441      *                Can be <code>null</code>.
442      * @return A retention rule or <code>null</code>.
443      */
444     public RetentionRule getRetentionRule(
445             final String artifactId,
446             final String version) {
447         RetentionRule result = null;
448         if (version != null) {
449             result =
450                 (RetentionRule) retentions.get(artifactId + "-" + version);
451         }
452         if (result == null) {
453             result = (RetentionRule) retentions.get(artifactId);
454         }
455         return result;
456     }
457 
458     /***
459      * @param artifactId The id of the artifact to check for.
460      * @param version The version of the artifact to check for.
461      * @return <code>true</code> if the version of this artifact is removed
462      */
463     public boolean isRemoved(final String artifactId, final String version) {
464         boolean result = false;
465         RemovalRule rule =
466             (RemovalRule) removals.get(artifactId + "-" + version);
467         if (rule == null) {
468             rule = (RemovalRule) removals.get(artifactId);
469         }
470         if (rule != null) {
471             result = true;
472         }
473         return result;
474     }
475 
476     /***
477      * @return The default rule of this rule set. See DEFAULT_RULE_* constants.
478      */
479     public String getDefaultRule() {
480         return defaultRule;
481     }
482 
483     /***
484      * @param defaultRule The default rule to set. See DEFAULT_RULE_* constants.
485      */
486     public void setDefaultRule(final String defaultRule) {
487         if (this.defaultRule != defaultRule) {
488             this.defaultRule = defaultRule;
489             fireChangedDefaultRule();
490         }
491     }
492 
493     /***
494      * @param listener The change listener to add.
495      */
496     public void addChangeListener(final RuleSetChangeListener listener) {
497         if (!changeListeners.contains(listener)) {
498             changeListeners.add(listener);
499         }
500     }
501 
502     /***
503      * @param listener The change listener to remove.
504      */
505     public void removeChangeListener(final RuleSetChangeListener listener) {
506         changeListeners.remove(listener);
507     }
508 
509     /***
510      * Notifies all change listeners that a rule has been added to this rule
511      * set.
512      *
513      * @param addedRule The rule that was added.
514      * @param index The index of this rule in the list of all rules.
515      */
516     private void fireAddedRule(
517             final Rule addedRule,
518             final int index) {
519         for (Iterator iter = changeListeners.iterator(); iter.hasNext();) {
520             RuleSetChangeListener listener =
521                 (RuleSetChangeListener) iter.next();
522             listener.addedRule(addedRule, index);
523         }
524     }
525 
526     /***
527      * Notifies all change listeners that a rule has been removed from this rule
528      * set.
529      *
530      * @param removedRule The removed rule.
531      */
532     private void fireRemovedRule(final Rule removedRule) {
533         for (Iterator iter = changeListeners.iterator(); iter.hasNext();) {
534             RuleSetChangeListener listener =
535                 (RuleSetChangeListener) iter.next();
536             listener.removedRule(removedRule);
537         }
538     }
539 
540     /***
541      * Notifies all change listeners that the default rule has been changed.
542      */
543     private void fireChangedDefaultRule() {
544         for (Iterator iter = changeListeners.iterator(); iter.hasNext();) {
545             RuleSetChangeListener listener =
546                 (RuleSetChangeListener) iter.next();
547             listener.changedDefaultRule();
548         }
549     }
550 
551     /***
552      * Computes the internal key of this rule used for hashing.
553      *
554      * @param rule The rule to get the key for.
555      * @return The key computed.
556      */
557     private String getKey(final Rule rule) {
558         String key = null;
559         if (rule instanceof EnforcementRule) {
560             key = rule.getArtifactId();
561         } else if (rule instanceof DeprecationRule) {
562             key = rule.getArtifactId() + "-" + rule.getVersion();
563         } else if (rule instanceof ReplacementRule) {
564             ReplacementRule replacmentRule = (ReplacementRule) rule;
565             key = replacmentRule.getDisplacedArtifactId();
566             if (replacmentRule.getDisplacedVersion() != null) {
567                 key = key + "-" + replacmentRule.getDisplacedVersion();
568             }
569         } else if (rule instanceof RemovalRule) {
570             RemovalRule removalRule = (RemovalRule) rule;
571             key = removalRule.getArtifactId();
572             if (removalRule.getVersion() != null) {
573                 key = key + "-" + removalRule.getVersion();
574             }
575         } else if (rule instanceof RetentionRule) {
576             RetentionRule retentionRule = (RetentionRule) rule;
577             key = retentionRule.getArtifactId();
578             if (retentionRule.getVersion() != null) {
579                 key = key + "-" + retentionRule.getVersion();
580             }
581         }
582         return key;
583     }
584 
585     /***
586      * @param rule The rule to check for.
587      * @return <code>true</code> if this rule set has a rule under the same key.
588      */
589     public boolean hasRuleForKey(final Rule rule) {
590         String key = getKey(rule);
591         return getMap(rule).get(key) != null;
592     }
593 
594     /***
595      * @param rule The rule to check for.
596      * @return <code>true</code> if this rule set has exactly this object
597      *         instance of the rule passed in.
598      */
599     public boolean hasSameRule(final Rule rule) {
600         boolean result = false;
601         String key = getKey(rule);
602         Rule sameKeyRule = (Rule) getMap(rule).get(key);
603         if (sameKeyRule != null
604                 && sameKeyRule.toString().equals(rule.toString())) {
605             result = true;
606         }
607         return result;
608     }
609 
610     /***
611      * @param rule The rule instance to find under the same key.
612      * @return The rule instance found or <code>null</code>.
613      */
614     public Rule getRuleForKey(final Rule rule) {
615         return (Rule) getMap(rule).get(getKey(rule));
616     }
617 
618     /***
619      * @param rule The rule to find the appropriate map of rules for.
620      * @return The map to store rules of the passed in rule's type in.
621      */
622     private Map getMap(final Rule rule) {
623         Map result = null;
624         if (rule instanceof EnforcementRule) {
625             result = enforcements;
626         } else if (rule instanceof DeprecationRule) {
627             result = deprecations;
628         } else if (rule instanceof ReplacementRule) {
629             result = replacements;
630         } else if (rule instanceof RemovalRule) {
631             result = removals;
632         } else if (rule instanceof RetentionRule) {
633             result = retentions;
634         }
635         return result;
636     }
637 
638     /***
639      * Removes all rules of type Rule if their property 'derived'
640      * is true.
641      */
642     public void removeAllDerivedRules() {
643         List clone = getAllRules();
644         for (Iterator iter = clone.iterator(); iter.hasNext();) {
645             Rule rule = (Rule) iter.next();
646             if (rule.isDerived()) {
647                 remove(rule);
648             }
649         }
650     }
651 
652     /***
653      * Removes all rules of type EnforcementRule if their property 'derived'
654      * is true.
655      */
656     public void removeAllDerivedEnforcementRules() {
657         List clone = new ArrayList(enforcements.values());
658         for (Iterator iter = clone.iterator(); iter.hasNext();) {
659             EnforcementRule rule = (EnforcementRule) iter.next();
660             if (rule.isDerived()) {
661                 remove(rule);
662             }
663         }
664     }
665 
666     /***
667      * Checks whether there is a retention rule that governs to retain the
668      * version of the artifact when encountered during the process of applying
669      * the rules.
670      *
671      * @param artifactId The artifact id of the project to check.
672      * @param version The version of the project to check.
673      * @return <code>true</code> if and only if there is a retention rule in
674      *         this rule set that matches the artifact id (and the version).
675      */
676     public boolean isRetained(final String artifactId, final String version) {
677         boolean result = false;
678         RetentionRule rule = getRetentionRule(artifactId, version);
679         if (rule != null) {
680             result = true;
681         }
682         return result;
683     }
684 }