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
99
100
101
102
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
114
115
116
117
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
129
130
131
132
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
148
149
150
151
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
172
173
174
175
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
191
192
193
194
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 }