1 package de.matthias_burbach.deputy.core.project;
2
3 import java.util.ArrayList;
4 import java.util.Collections;
5 import java.util.HashMap;
6 import java.util.HashSet;
7 import java.util.Iterator;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Set;
11
12 import de.matthias_burbach.deputy.core.rule.RuleSet;
13
14 /***
15 * Represents a Maven project object model (only the parts relevant for Deputy).
16 *
17 * @author Matthias Burbach
18 */
19 public class Project implements ProjectQualifier {
20 /***
21 * Whether this project is the root of the dependency graph currently
22 * opened in Deputy. If so, it is the project which can be edited and
23 * changed by rules to update and save its dependencies.
24 */
25 private boolean isRootProject = false;
26
27 /***
28 * Whether this project is an assembly or a component. By definition, an
29 * assembly contains the recursive hull of all its dependencies in the
30 * project.xml whereas a component does not.
31 */
32 private boolean isAssembly = false;
33
34 /***
35 * The group id of this project.
36 */
37 private String groupId;
38
39 /***
40 * The artifact id of this project.
41 * <p/>
42 * Is a mandatory property because many processings depend on it.<br/>
43 * Some processings assume that the artifact id is sufficient and needs not
44 * be complemented by the group id to uniquely identify the artifact.
45 */
46 private String artifactId;
47
48 /***
49 * The version of this project. Is a mandatory property because many
50 * processings depend on it.
51 */
52 private String version;
53
54 /***
55 * The optional name of this project.
56 */
57 private String name;
58
59 /***
60 * The optional URL of this project's homepage.
61 */
62 private String url;
63
64 /***
65 * The type of this project. This is a non-standard element in the
66 * project.xml. Its value is one of 'jar', 'war', ejb', 'dll'.<br/>
67 * It is optional and defaults to 'jar'.
68 */
69 private String type;
70
71 /***
72 * The name of jar file if it doesn't respect the
73 * <artifactId>-<version>.jar pattern.
74 */
75 private String jar;
76
77 /***
78 * The projects of type {@link Project} which depend on this project.
79 */
80 private List clients = new ArrayList();
81
82 /***
83 * The projects of type {@link Project} this project depends on
84 * (literally as defined in the project.xml or
85 * as a decision made by Deputy).
86 */
87 private List dependencies = new ArrayList();
88
89 /***
90 * The projects of type {@link Project} this project actually depends on
91 * according to the literal entries in the project.xml, but which are
92 * deprecated, i. e. overruled by a decision made by Deputy.
93 */
94 private List deprecatedDependencies = new ArrayList();
95
96 /***
97 * The non-top-level dependencies, i. e. dependencies of dependencies of
98 * this project which are not direct dependencies of this project at the
99 * same time.
100 */
101 private List indirectDependencies = null;
102
103 /***
104 * The version conflicts in the dependency graph that roots in this project.
105 */
106 private List conflicts;
107
108 /***
109 * The set of rules defined for this project. They are used by Deputy to
110 * decide which version to take of which dependency in the dependency graph
111 * that roots in this project.
112 */
113 private RuleSet ruleSet = new RuleSet();
114
115 /***
116 * The change listeners that need to be informed when this project changes
117 * state.
118 */
119 private List changeListeners = new ArrayList();
120
121 /***
122 * Helps to sort projects in the dependency lists.
123 */
124 private ProjectComparator projectComparator = new ProjectComparator();
125
126 /***
127 * Maps artifactIds of type {@link java.lang.String} to lists of
128 * XML property elements of type {@link org.jdom.Element} that were found as
129 * child elements of the properties XML element of direct and indirect
130 * dependencies defined in this project's POM file.
131 * <p/>
132 * These are re-written to the project.xml on save to preserve them. Note,
133 * that properties will even be preserved if the version of the
134 * corresponding dependency gets changed. This might not be appropriate
135 * in all cases.
136 * <p/>
137 * The dependency property 'top' is excluded from these and treated in a
138 * special way because it's a property defined by Deputy.
139 */
140 private Map dependencyProperties = new HashMap();
141
142 /***
143 * Maps dependencies of type {@link Project} to the corresponding
144 * dependencies of type {@link ProjectQualifier}
145 * that caused the dependency during the application of the rules.
146 * If there is no mapping the project caused itself.
147 */
148 private Map dependencyCauses = new HashMap();
149
150 /***
151 * Maps clients of type {@link Project} to their literal dependencies
152 * of type {@link ProjectQualifier} that caused the clientship during the
153 * application of the rules.
154 * If there is no mapping the project caused itself.
155 */
156 private Map clientCauses = new HashMap();
157
158 /***
159 * Maps clients of type {@link Project} to their literal dependencies
160 * of type {@link ProjectQualifier} that caused the clientship during the
161 * application of the rules.
162 * If there is no mapping the project caused itself.
163 *
164 * @return The map described. Maybe empty but not <code>null</code>.
165 */
166 public Map getClientCauses() {
167 return clientCauses;
168 }
169
170 /***
171 * Maps dependencies of type {@link Project} to the corresponding
172 * dependencies of type {@link ProjectQualifier}
173 * that caused the dependency during the application of the rules.
174 * If there is no mapping the project caused itself.
175 *
176 * @return The map described. Maybe empty but not <code>null</code>.
177 */
178 public Map getDependencyCauses() {
179 return dependencyCauses;
180 }
181
182 /***
183 * @return The group id of this project.
184 */
185 public String getGroupId() {
186 return groupId;
187 }
188
189 /***
190 * @param groupId The group id of this project.
191 */
192 public void setGroupId(final String groupId) {
193 this.groupId = groupId;
194 }
195
196 /***
197 * @return The artifact id of this project.
198 */
199 public String getArtifactId() {
200 return artifactId;
201 }
202
203 /***
204 * @param artifactId The artifact id of this project.
205 */
206 public void setArtifactId(final String artifactId) {
207 this.artifactId = artifactId;
208 }
209
210 /***
211 * @return The version of this project.
212 */
213 public String getVersion() {
214 return version;
215 }
216
217 /***
218 * @param version The version of this project.
219 */
220 public void setVersion(final String version) {
221 this.version = version;
222 }
223
224 /***
225 * @return The type of this project. This is a non-standard element in the
226 * project.xml. Its value is one of 'jar', 'war', ejb', 'dll'.
227 * It is optional and defaults to 'jar'.
228 */
229 public String getType() {
230 return type;
231 }
232
233 /***
234 * @param type The type of this project. This is a non-standard element in
235 * the project.xml. Its value is one of 'jar', 'war', ejb',
236 * 'dll'. It is optional and defaults to 'jar'.
237 */
238 public void setType(final String type) {
239 this.type = type;
240 }
241
242 /***
243 * @return The name of jar file if it doesn't respect the
244 * <artifactId>-<version>.jar pattern.
245 */
246 public String getJar() {
247 return jar;
248 }
249
250 /***
251 * @param jar The name of jar file if it doesn't respect the
252 * <artifactId>-<version>.jar pattern.
253 */
254 public void setJar(final String jar) {
255 this.jar = jar;
256 }
257
258 /***
259 * @return Whether this project is the root of the dependency graph
260 * currently opened in Deputy. If so, it is the project which can be
261 * edited and changed by rules to update and save its dependencies.
262 */
263 public boolean isRootProject() {
264 return isRootProject;
265 }
266
267 /***
268 * @param rootProject Whether this project is the root of the dependency
269 * graph currently opened in Deputy. If so, it is the
270 * project which can be edited and changed by rules to
271 * update and save its dependencies.
272 */
273 public void setRootProject(final boolean rootProject) {
274 this.isRootProject = rootProject;
275 }
276
277 /***
278 * @return The optional URL of this project's homepage.
279 */
280 public String getUrl() {
281 return url;
282 }
283
284 /***
285 * @return The default URL of this project's homepage.
286 */
287 public String getDefaultUrl() {
288 String result = null;
289
290
291
292 if (artifactId.startsWith("vrp-")) {
293 result = "http://bugz-e.tui.de/maven/vrp/projects/";
294 if (getName().indexOf("assembly") == -1) {
295 result = result + "component.";
296 }
297 result = result + getName() + "/release-notes.html";
298 }
299 return result;
300 }
301
302 /***
303 * @param url The optional URL of this project's homepage.
304 */
305 public void setUrl(final String url) {
306 this.url = url;
307 }
308
309 /***
310 * @return The optional name of this project
311 * (for display purposes and the like).
312 */
313 public String getName() {
314
315
316
317 if (name == null) {
318 if (artifactId.startsWith("vrp-")) {
319 name = artifactId.substring("vrp-".length());
320 }
321 }
322 if (artifactId.startsWith("vrp-")) {
323 name = name.replace('-', '.');
324 }
325 return name;
326 }
327
328 /***
329 * @param name The optional name of this project
330 * (for display purposes and the like).
331 */
332 public void setName(final String name) {
333 this.name = name;
334 }
335
336 /***
337 * Tries to split off the numerical major version from the version.
338 * The major version is defined to be the numerical prefix of the version
339 * up to the first dot in the version or the whole version if it is an
340 * integer without any dots.
341 *
342 * @return The major version part of the version string or <code>null</code>
343 * if parsing this fails.
344 */
345 public Integer getMajorVersion() {
346 Integer result = null;
347 String[] parts = version.split("//.");
348 if (parts.length > 0) {
349 try {
350 result = new Integer(Integer.parseInt(parts[0]));
351 } catch (NumberFormatException e) {
352
353 result = null;
354 }
355 }
356 return result;
357 }
358
359 /***
360 * Tries to split off the numerical minor version from the version between
361 * the first and the second dot in the version string.
362 * If the version is literally only a major version and nothing more,
363 * the minor version defaults to 0.
364 *
365 * @return The minor version part of the version string or <code>null</code>
366 * if parsing this fails.
367 */
368 public Integer getMinorVersion() {
369 if (getMajorVersion() != null) {
370 if (getMajorVersion().toString().length() == version.length()) {
371 return new Integer(0);
372 }
373 }
374 Integer result = null;
375 String[] parts = version.split("//.");
376 if (parts.length > 1) {
377 try {
378 result = new Integer(Integer.parseInt(parts[1]));
379 } catch (NumberFormatException e) {
380
381 result = null;
382 }
383 }
384 return result;
385 }
386
387 /***
388 * Tries to split off the numerical micro version from the version after
389 * the second dot in the version string. The micro version either extends up
390 * to the third dot, if it exists, or up to the end of the version.<br/>
391 * If the version is literally only a major version and nothing more, or a
392 * major version, a dot and a minor version and nothing more,
393 * the micro version defaults to 0.
394 *
395 * @return The micro version part of the version string or <code>null</code>
396 * if parsing this fails.
397 */
398 public Integer getMicroVersion() {
399 if (getMajorVersion() != null) {
400 if (getMajorVersion().toString().length() == version.length()) {
401 return new Integer(0);
402 }
403 }
404 if (getMajorVersion() != null && getMinorVersion() != null) {
405 if (getMajorVersion().toString().length()
406 + ".".length()
407 + getMinorVersion().toString().length()
408 == version.length()) {
409 return new Integer(0);
410 }
411 }
412 Integer result = null;
413 String[] parts = version.split("//.");
414 if (parts.length > 2) {
415 try {
416 result = new Integer(Integer.parseInt(parts[2]));
417 } catch (NumberFormatException e) {
418
419 result = null;
420 }
421 }
422 return result;
423 }
424
425 /***
426 * @return The rule set of this project.
427 */
428 public RuleSet getRuleSet() {
429 return ruleSet;
430 }
431
432 /***
433 * @param ruleSet The rule set of this project.
434 */
435 public void setRuleSet(final RuleSet ruleSet) {
436 this.ruleSet = ruleSet;
437 }
438
439 /***
440 * @return <code>true</code> whether this project is an assembly for which
441 * indirect dependencies need to be persisted in the project.xml.
442 */
443 public boolean isAssembly() {
444 return isAssembly;
445 }
446
447 /***
448 * @param assembly <code>true</code> whether this project is an assembly
449 * for which indirect dependencies need to be persisted in
450 * the project.xml.
451 */
452 public void setAssembly(final boolean assembly) {
453 this.isAssembly = assembly;
454 }
455
456 /***
457 * @param project The project to check.
458 * @return <code>true</code> if this project has a dependency to the project
459 * passed in.
460 */
461 public boolean hasDependency(final Project project) {
462 boolean result =
463 projectOccursInProjectList(project, dependencies, true);
464 return result;
465 }
466
467 /***
468 * @param project The project to check.
469 * @return <code>true</code> if this project has a dependency to the same
470 * artifact represented by the project passed in.
471 */
472 public boolean hasDependencyToSameArtifact(final Project project) {
473 boolean result =
474 projectOccursInProjectList(project, dependencies, false);
475 return result;
476 }
477
478 /***
479 * @param project Holds the artifact to find the dependency for.
480 * @return The dependency with the same artifact id as the project passed in
481 * has or <code>null</code>.
482 */
483 public Project getDependencyToSameArtifact(final Project project) {
484 Project result =
485 getProjectFromProjectList(project, dependencies, false);
486 return result;
487 }
488
489 /***
490 * @return All direct dependencies of type {@link Project} of this project.
491 */
492 public Iterator getDependencies() {
493 return dependencies.iterator();
494 }
495
496 /***
497 * Adds a direct dependency to this project and notifies change listeners.
498 *
499 * @param dependency The dependency to add.
500 */
501 public void addDependency(final Project dependency) {
502 dependencies.add(dependency);
503 Collections.sort(dependencies, projectComparator);
504 int index = dependencies.indexOf(dependency);
505 fireAddedDependency(dependency, index);
506 }
507
508 /***
509 * Removes a direct dependency from this project and notifies change
510 * listeners.
511 *
512 * @param dependency The dependency to remove.
513 */
514 public void removeDependency(final Project dependency) {
515 dependencies.remove(dependency);
516 fireRemovedDependency(dependency);
517 }
518
519 /***
520 * @param project The project to check if it is an indirect dependency.
521 * @return <code>true</code> if the project passed in is an indirect
522 * dependency of this project.
523 */
524 public boolean hasIndirectDependency(final Project project) {
525 if (indirectDependencies == null) {
526 indirectDependencies = findIndirectDependencies();
527 }
528 boolean result =
529 projectOccursInProjectList(project, indirectDependencies, true);
530 return result;
531 }
532
533 /***
534 * @param project Holds the artifact to check the dependency for.
535 * @return <code>true</code> if this project has an indirect dependency to
536 * the same artifact as the project passed in has.
537 */
538 public boolean hasIndirectDependencyToSameArtifact(final Project project) {
539 if (indirectDependencies == null) {
540 indirectDependencies = findIndirectDependencies();
541 }
542 boolean result =
543 projectOccursInProjectList(project, indirectDependencies, false);
544 return result;
545 }
546
547 /***
548 * @param project Holds the artifact to find the indirect dependency for.
549 * @return The indirect dependency with the same artifact id as the project
550 * passed in has or <code>null</code>.
551 */
552 public Project getIndirectDependencyToSameArtifact(final Project project) {
553 if (indirectDependencies == null) {
554 indirectDependencies = findIndirectDependencies();
555 }
556 Project result =
557 getProjectFromProjectList(project, indirectDependencies, false);
558 return result;
559 }
560
561 /***
562 * @return The indirect dependencies of type {@link Project}.
563 */
564 public Iterator getIndirectDependencies() {
565 if (indirectDependencies == null) {
566 indirectDependencies = findIndirectDependencies();
567 }
568 return indirectDependencies.iterator();
569 }
570
571 /***
572 * Adds an indirect dependency to this project. Does <i>not</i> notify
573 * change listeners (is not needed because a user will never directly add
574 * such a dependency through the user interface).
575 *
576 * @param dependency The indirect dependency to add.
577 */
578 public void addIndirectDependency(final Project dependency) {
579 if (indirectDependencies == null) {
580 indirectDependencies = new ArrayList();
581 }
582 indirectDependencies.add(dependency);
583 Collections.sort(indirectDependencies, projectComparator);
584 }
585
586 /***
587 * Removes the indirect dependency and notifies change listeners.
588 *
589 * @param dependency The dependency to remove.
590 */
591 public void removeIndirectDependency(final Project dependency) {
592 if (indirectDependencies == null) {
593 indirectDependencies = findIndirectDependencies();
594 }
595 indirectDependencies.remove(dependency);
596 fireRemovedIndirectDependency(dependency);
597 }
598
599 /***
600 * @param indirectDependencies The list of indirect dependencies of type
601 * {@link Project}.
602 */
603 public void setIndirectDependencies(final List indirectDependencies) {
604 Collections.sort(indirectDependencies, projectComparator);
605 this.indirectDependencies = indirectDependencies;
606 }
607
608 /***
609 * @param project The dependency to check for.
610 * @return <code>true</code> if this project has a direct or indirect
611 * dependency to the project passed in.
612 */
613 public boolean hasDirectOrIndirectDependency(final Project project) {
614 boolean result =
615 hasDependency(project) || hasIndirectDependency(project);
616 return result;
617 }
618
619 /***
620 * Adds a dependency stated literally in this project's project.xml but
621 * which was overruled by some other version of the same artifact.
622 *
623 * @param deprecatedDependency The dependency to add.
624 */
625 public void addDeprecatedDependency(final Project deprecatedDependency) {
626 deprecatedDependencies.add(deprecatedDependency);
627 Collections.sort(deprecatedDependencies, projectComparator);
628 }
629
630 /***
631 * @return The merged and globally sorted list of both the direct and the
632 * indirect dependencies of this project. Elements of the list are
633 * of type {@link Project}.
634 */
635 public List getAllDependencies() {
636 List result = new ArrayList(dependencies);
637 if (indirectDependencies == null) {
638 indirectDependencies = findIndirectDependencies();
639 }
640 result.addAll(indirectDependencies);
641 Collections.sort(result, projectComparator);
642 return result;
643 }
644
645 /***
646 * @return The projects of type {@link Project} which depend on this
647 * project.
648 */
649 public List getClients() {
650 return clients;
651 }
652
653 /***
654 * @return The version conflicts in the dependency graph that roots in this
655 * project.
656 */
657 public List getConflicts() {
658 if (conflicts == null) {
659 conflicts = findConflicts();
660 }
661 return conflicts;
662 }
663
664 /***
665 * Adds a list of properties for the artifact id to this project.
666 *
667 * @param dependencyArtifactId The artifact id of the dependency that has
668 * these properties defined in this project's
669 * POM file.
670 * @param propertyXmlElements The list of associated properties of type
671 * {@link org.jdom.Element}.
672 */
673 public void addDependencyPropertyList(
674 final String dependencyArtifactId,
675 final List propertyXmlElements) {
676 dependencyProperties.put(dependencyArtifactId, propertyXmlElements);
677 }
678
679 /***
680 * @param dependencyArtifactId The artifact id of the dependency to retrieve
681 * properties for.
682 * @return A list of properties of type {@link org.jdom.Element} or
683 * <code>null</code>.
684 */
685 public List getDependencyPropertyList(final String dependencyArtifactId) {
686 List result = (List) dependencyProperties.get(dependencyArtifactId);
687 return result;
688 }
689
690 /***
691 * Adds a change listener to this project which will be notified when this
692 * project changed in defined ways.
693 *
694 * @param listener The listener to add.
695 */
696 public void addChangeListener(final ProjectChangeListener listener) {
697 if (!changeListeners.contains(listener)) {
698 changeListeners.add(listener);
699 }
700 }
701
702 /***
703 * Removes a change listener from this project.
704 *
705 * @param listener The listener to remove.
706 */
707 public void removeChangeListener(final ProjectChangeListener listener) {
708 changeListeners.remove(listener);
709 }
710
711
712
713
714 /***
715 * {@inheritDoc}
716 */
717 public String toString() {
718 StringBuffer buffer = new StringBuffer(groupId + "");
719 buffer.append("/");
720 buffer.append(artifactId);
721 buffer.append("-");
722 buffer.append(version);
723 return buffer.toString();
724 }
725
726 /***
727 * @param project The project to check for in the <code>projectList</code>.
728 * @param projectList The projectList with objects of type {@link Project}.
729 * @param exactMatch Whether to compare by artifact id and version (=exact)
730 * or only by artifact id.
731 * @return <code>true</code> if the project logically occurs in the project
732 * list.
733 */
734 private boolean projectOccursInProjectList(
735 final Project project,
736 final List projectList,
737 final boolean exactMatch) {
738 boolean result =
739 getProjectFromProjectList(project, projectList, exactMatch) != null;
740 return result;
741 }
742
743 /***
744 * @param project The project to check for in the <code>projectList</code>.
745 * @param projectList The projectList with objects of type {@link Project}.
746 * @param exactMatch Whether to compare by artifact id and version (=exact)
747 * or only by artifact id.
748 * @return The project from the project list which is logically equivalent
749 * to <code>project</code>.
750 */
751 private Project getProjectFromProjectList(
752 final Project project,
753 final List projectList,
754 final boolean exactMatch) {
755 Project result = null;
756 for (Iterator iter = projectList.iterator(); iter.hasNext();) {
757 Project dependency = (Project) iter.next();
758 if (dependency.getGroupId().equals(project.getGroupId())
759 && dependency.getArtifactId().equals(
760 project.getArtifactId())) {
761 if (exactMatch) {
762 if (dependency.getVersion().equals(project.getVersion())) {
763 result = dependency;
764 }
765 } else {
766 result = dependency;
767 }
768 if (result != null) {
769 break;
770 }
771 }
772 }
773 return result;
774 }
775
776 /***
777 * Flattens the graph of indirect dependencies of this project and returns
778 * them without duplicates in a list.
779 *
780 * @return The list of indirect dependencies of type {@link Project} of this
781 * project. Maybe empty but not <code>null</code>.
782 */
783 private List findIndirectDependencies() {
784 List flattenedDependencies = new ArrayList();
785 flattenDependenciesForIndirectDependencies(this, flattenedDependencies);
786 sortProjects(flattenedDependencies);
787 return flattenedDependencies;
788 }
789
790 /***
791 * @return The list of version conflicts of type
792 * {@link ProjectConflictGroup} of this project.
793 */
794 private List findConflicts() {
795 List flattenedDependencies = new ArrayList();
796 flattenDependenciesForConflicts(this, flattenedDependencies);
797 sortProjects(flattenedDependencies);
798 List results = new ArrayList();
799 ProjectConflictGroup conflictGroup = new ProjectConflictGroup();
800 String conflictArtifactId = "";
801 for (Iterator iter = flattenedDependencies.iterator();
802 iter.hasNext();) {
803 Project project = (Project) iter.next();
804 if (conflictGroup.getProjects().size() == 0) {
805 conflictGroup.getProjects().add(project);
806 conflictArtifactId = project.getArtifactId();
807 conflictGroup.setArtifactId(conflictArtifactId);
808 } else if (project.getArtifactId().equals(conflictArtifactId)) {
809 conflictGroup.getProjects().add(project);
810 } else {
811 if (conflictGroup.getProjects().size() > 1) {
812 results.add(conflictGroup);
813 }
814 conflictGroup = new ProjectConflictGroup();
815 conflictGroup.getProjects().add(project);
816 conflictArtifactId = project.getArtifactId();
817 conflictGroup.setArtifactId(conflictArtifactId);
818 }
819 }
820 return results;
821 }
822
823 /***
824 * Flattens all dependencies and all overruled dependencies as the basis
825 * to find version conflicts in the dependency graph.
826 *
827 * @param project The project to be used as root of the (sub) graph to be
828 * flattened and collected in the list.
829 * @param flattenedDependencies The list to collect all flattened
830 * dependencies in.
831 */
832 private void flattenDependenciesForConflicts(
833 final Project project,
834 final List flattenedDependencies) {
835 Set mergedDependencies = new HashSet(project.dependencies);
836 mergedDependencies.addAll(project.deprecatedDependencies);
837 for (Iterator iter = mergedDependencies.iterator(); iter.hasNext();) {
838 Project dependencyProject = (Project) iter.next();
839 if (!flattenedDependencies.contains(dependencyProject)) {
840 flattenedDependencies.add(dependencyProject);
841 flattenDependenciesForConflicts(
842 dependencyProject, flattenedDependencies);
843 }
844 }
845 }
846
847 /***
848 * Flattens all dependencies as the basis to find all indirect dependencies.
849 *
850 * @param project The project to be used as root of the (sub) graph to be
851 * flattened and collected in the list. The
852 * <code>project</code> itself will not be added to the list.
853 * @param flattenedDependencies The list to collect all flattened
854 * dependencies in.
855 */
856 private void flattenDependenciesForIndirectDependencies(
857 final Project project,
858 final List flattenedDependencies) {
859 Set dependencySet = new HashSet(project.dependencies);
860 for (Iterator iter = dependencySet.iterator(); iter.hasNext();) {
861 Project dependencyProject = (Project) iter.next();
862 if (!flattenedDependencies.contains(dependencyProject)) {
863 flattenedDependencies.add(dependencyProject);
864 flattenDependenciesForIndirectDependencies(
865 dependencyProject, flattenedDependencies);
866 }
867 }
868 if (project == this) {
869 flattenedDependencies.removeAll(dependencies);
870 }
871 }
872
873 /***
874 * @param projects The list of Maven projects to sort.
875 */
876 private void sortProjects(final List projects) {
877 Collections.sort(projects, new ProjectComparator());
878 }
879
880 /***
881 * Notifies listeners that a direct dependency has been added to this
882 * project.
883 *
884 * @param addedDependency The added dependency.
885 * @param index The index of the added dependency in the sorted list of all
886 * dependencies of this project.
887 */
888 private void fireAddedDependency(
889 final Project addedDependency,
890 final int index) {
891 for (Iterator iter = changeListeners.iterator(); iter.hasNext();) {
892 ProjectChangeListener listener =
893 (ProjectChangeListener) iter.next();
894 listener.addedDependency(addedDependency, index);
895 }
896 }
897
898 /***
899 * Notifies listeners that a direct dependency has been removed from this
900 * project.
901 *
902 * @param removedDependency The removed dependency.
903 */
904 private void fireRemovedDependency(final Project removedDependency) {
905 for (Iterator iter = changeListeners.iterator(); iter.hasNext();) {
906 ProjectChangeListener listener =
907 (ProjectChangeListener) iter.next();
908 listener.removedDependency(removedDependency);
909 }
910 }
911
912 /***
913 * Notifies listeners that an indirect dependency has been removed from this
914 * project.
915 *
916 * @param removedDependency The removed dependency.
917 */
918 private void fireRemovedIndirectDependency(
919 final Project removedDependency) {
920 for (Iterator iter = changeListeners.iterator(); iter.hasNext();) {
921 ProjectChangeListener listener =
922 (ProjectChangeListener) iter.next();
923 listener.removedIndirectDependency(removedDependency);
924 }
925 }
926 }