changeset 42338 | a60f280f803c |
parent 39050 | 9de41b79ec7e |
child 42703 | 20c39ea4a507 |
42148:7a4a59859ac0 | 42338:a60f280f803c |
---|---|
24 */ |
24 */ |
25 |
25 |
26 package java.lang.module; |
26 package java.lang.module; |
27 |
27 |
28 import java.io.PrintStream; |
28 import java.io.PrintStream; |
29 import java.lang.module.ModuleDescriptor.Provides; |
|
29 import java.lang.module.ModuleDescriptor.Requires.Modifier; |
30 import java.lang.module.ModuleDescriptor.Requires.Modifier; |
31 import java.lang.reflect.Layer; |
|
30 import java.util.ArrayDeque; |
32 import java.util.ArrayDeque; |
31 import java.util.ArrayList; |
33 import java.util.ArrayList; |
34 import java.util.Arrays; |
|
32 import java.util.Collection; |
35 import java.util.Collection; |
33 import java.util.Deque; |
36 import java.util.Deque; |
34 import java.util.HashMap; |
37 import java.util.HashMap; |
35 import java.util.HashSet; |
38 import java.util.HashSet; |
36 import java.util.LinkedHashSet; |
39 import java.util.LinkedHashSet; |
45 import jdk.internal.module.ModuleHashes; |
48 import jdk.internal.module.ModuleHashes; |
46 |
49 |
47 /** |
50 /** |
48 * The resolver used by {@link Configuration#resolveRequires} and |
51 * The resolver used by {@link Configuration#resolveRequires} and |
49 * {@link Configuration#resolveRequiresAndUses}. |
52 * {@link Configuration#resolveRequiresAndUses}. |
53 * |
|
54 * @implNote The resolver is used at VM startup and so deliberately avoids |
|
55 * using lambda and stream usages in code paths used during startup. |
|
50 */ |
56 */ |
51 |
57 |
52 final class Resolver { |
58 final class Resolver { |
53 |
59 |
54 private final ModuleFinder beforeFinder; |
60 private final ModuleFinder beforeFinder; |
55 private final Configuration parent; |
61 private final List<Configuration> parents; |
56 private final ModuleFinder afterFinder; |
62 private final ModuleFinder afterFinder; |
57 private final PrintStream traceOutput; |
63 private final PrintStream traceOutput; |
58 |
64 |
59 // maps module name to module reference |
65 // maps module name to module reference |
60 private final Map<String, ModuleReference> nameToReference = new HashMap<>(); |
66 private final Map<String, ModuleReference> nameToReference = new HashMap<>(); |
61 |
67 |
62 |
68 |
63 Resolver(ModuleFinder beforeFinder, |
69 Resolver(ModuleFinder beforeFinder, |
64 Configuration parent, |
70 List<Configuration> parents, |
65 ModuleFinder afterFinder, |
71 ModuleFinder afterFinder, |
66 PrintStream traceOutput) { |
72 PrintStream traceOutput) { |
67 this.beforeFinder = beforeFinder; |
73 this.beforeFinder = beforeFinder; |
68 this.parent = parent; |
74 this.parents = parents; |
69 this.afterFinder = afterFinder; |
75 this.afterFinder = afterFinder; |
70 this.traceOutput = traceOutput; |
76 this.traceOutput = traceOutput; |
71 } |
77 } |
72 |
78 |
73 |
79 |
83 for (String root : roots) { |
89 for (String root : roots) { |
84 |
90 |
85 // find root module |
91 // find root module |
86 ModuleReference mref = findWithBeforeFinder(root); |
92 ModuleReference mref = findWithBeforeFinder(root); |
87 if (mref == null) { |
93 if (mref == null) { |
88 if (parent.findModule(root).isPresent()) { |
94 |
95 if (findInParent(root) != null) { |
|
89 // in parent, nothing to do |
96 // in parent, nothing to do |
90 continue; |
97 continue; |
91 } |
98 } |
99 |
|
92 mref = findWithAfterFinder(root); |
100 mref = findWithAfterFinder(root); |
93 if (mref == null) { |
101 if (mref == null) { |
94 fail("Module %s not found", root); |
102 fail("Module %s not found", root); |
95 } |
103 } |
96 } |
104 } |
123 ModuleDescriptor descriptor = q.poll(); |
131 ModuleDescriptor descriptor = q.poll(); |
124 assert nameToReference.containsKey(descriptor.name()); |
132 assert nameToReference.containsKey(descriptor.name()); |
125 |
133 |
126 // process dependences |
134 // process dependences |
127 for (ModuleDescriptor.Requires requires : descriptor.requires()) { |
135 for (ModuleDescriptor.Requires requires : descriptor.requires()) { |
136 |
|
137 // only required at compile-time |
|
138 if (requires.modifiers().contains(Modifier.STATIC)) |
|
139 continue; |
|
140 |
|
128 String dn = requires.name(); |
141 String dn = requires.name(); |
129 |
142 |
130 // find dependence |
143 // find dependence |
131 ModuleReference mref = findWithBeforeFinder(dn); |
144 ModuleReference mref = findWithBeforeFinder(dn); |
132 if (mref == null) { |
145 if (mref == null) { |
133 if (parent.findModule(dn).isPresent()) |
146 |
147 if (findInParent(dn) != null) { |
|
148 // dependence is in parent |
|
134 continue; |
149 continue; |
150 } |
|
135 |
151 |
136 mref = findWithAfterFinder(dn); |
152 mref = findWithAfterFinder(dn); |
137 if (mref == null) { |
153 if (mref == null) { |
138 fail("Module %s not found, required by %s", |
154 fail("Module %s not found, required by %s", |
139 dn, descriptor.name()); |
155 dn, descriptor.name()); |
172 Map<String, Set<ModuleReference>> availableProviders = new HashMap<>(); |
188 Map<String, Set<ModuleReference>> availableProviders = new HashMap<>(); |
173 for (ModuleReference mref : findAll()) { |
189 for (ModuleReference mref : findAll()) { |
174 ModuleDescriptor descriptor = mref.descriptor(); |
190 ModuleDescriptor descriptor = mref.descriptor(); |
175 if (!descriptor.provides().isEmpty()) { |
191 if (!descriptor.provides().isEmpty()) { |
176 |
192 |
177 for (String sn : descriptor.provides().keySet()) { |
193 for (Provides provides : descriptor.provides()) { |
194 String sn = provides.service(); |
|
195 |
|
178 // computeIfAbsent |
196 // computeIfAbsent |
179 Set<ModuleReference> providers = availableProviders.get(sn); |
197 Set<ModuleReference> providers = availableProviders.get(sn); |
180 if (providers == null) { |
198 if (providers == null) { |
181 providers = new HashSet<>(); |
199 providers = new HashSet<>(); |
182 availableProviders.put(sn, providers); |
200 availableProviders.put(sn, providers); |
189 |
207 |
190 // create the visit stack |
208 // create the visit stack |
191 Deque<ModuleDescriptor> q = new ArrayDeque<>(); |
209 Deque<ModuleDescriptor> q = new ArrayDeque<>(); |
192 |
210 |
193 // the initial set of modules that may use services |
211 // the initial set of modules that may use services |
194 Set<ModuleDescriptor> candidateConsumers = new HashSet<>(); |
212 Set<ModuleDescriptor> initialConsumers; |
195 Configuration p = parent; |
213 if (Layer.boot() == null) { |
196 while (p != null) { |
214 initialConsumers = new HashSet<>(); |
197 candidateConsumers.addAll(p.descriptors()); |
215 } else { |
198 p = p.parent().orElse(null); |
216 initialConsumers = parents.stream() |
217 .flatMap(Configuration::configurations) |
|
218 .distinct() |
|
219 .flatMap(c -> c.descriptors().stream()) |
|
220 .collect(Collectors.toSet()); |
|
199 } |
221 } |
200 for (ModuleReference mref : nameToReference.values()) { |
222 for (ModuleReference mref : nameToReference.values()) { |
201 candidateConsumers.add(mref.descriptor()); |
223 initialConsumers.add(mref.descriptor()); |
202 } |
224 } |
203 |
|
204 |
225 |
205 // Where there is a consumer of a service then resolve all modules |
226 // Where there is a consumer of a service then resolve all modules |
206 // that provide an implementation of that service |
227 // that provide an implementation of that service |
228 Set<ModuleDescriptor> candidateConsumers = initialConsumers; |
|
207 do { |
229 do { |
208 for (ModuleDescriptor descriptor : candidateConsumers) { |
230 for (ModuleDescriptor descriptor : candidateConsumers) { |
209 if (!descriptor.uses().isEmpty()) { |
231 if (!descriptor.uses().isEmpty()) { |
210 for (String service : descriptor.uses()) { |
232 for (String service : descriptor.uses()) { |
211 Set<ModuleReference> mrefs = availableProviders.get(service); |
233 Set<ModuleReference> mrefs = availableProviders.get(service); |
232 } |
254 } |
233 } |
255 } |
234 } |
256 } |
235 |
257 |
236 candidateConsumers = resolve(q); |
258 candidateConsumers = resolve(q); |
237 |
|
238 } while (!candidateConsumers.isEmpty()); |
259 } while (!candidateConsumers.isEmpty()); |
239 |
260 |
240 return this; |
261 return this; |
241 } |
262 } |
242 |
263 |
427 |
448 |
428 String algorithm = hashes.algorithm(); |
449 String algorithm = hashes.algorithm(); |
429 for (String dn : hashes.names()) { |
450 for (String dn : hashes.names()) { |
430 ModuleReference other = nameToReference.get(dn); |
451 ModuleReference other = nameToReference.get(dn); |
431 if (other == null) { |
452 if (other == null) { |
432 other = parent.findModule(dn) |
453 ResolvedModule resolvedModule = findInParent(dn); |
433 .map(ResolvedModule::reference) |
454 if (resolvedModule != null) |
434 .orElse(null); |
455 other = resolvedModule.reference(); |
435 } |
456 } |
436 |
457 |
437 // skip checking the hash if the module has been patched |
458 // skip checking the hash if the module has been patched |
438 if (other != null && !other.isPatched()) { |
459 if (other != null && !other.isPatched()) { |
439 String recordedHash = hashes.hashFor(dn); |
460 byte[] recordedHash = hashes.hashFor(dn); |
440 String actualHash = other.computeHash(algorithm); |
461 byte[] actualHash = other.computeHash(algorithm); |
441 if (actualHash == null) |
462 if (actualHash == null) |
442 fail("Unable to compute the hash of module %s", dn); |
463 fail("Unable to compute the hash of module %s", dn); |
443 if (!recordedHash.equals(actualHash)) { |
464 if (!Arrays.equals(recordedHash, actualHash)) { |
444 fail("Hash of %s (%s) differs to expected hash (%s)" + |
465 fail("Hash of %s (%s) differs to expected hash (%s)" + |
445 " recorded in %s", dn, actualHash, recordedHash, |
466 " recorded in %s", dn, toHexString(actualHash), |
446 descriptor.name()); |
467 toHexString(recordedHash), descriptor.name()); |
447 } |
468 } |
448 } |
469 } |
449 } |
470 } |
450 |
471 |
451 } |
472 } |
473 } |
|
474 |
|
475 private static String toHexString(byte[] ba) { |
|
476 StringBuilder sb = new StringBuilder(ba.length * 2); |
|
477 for (byte b: ba) { |
|
478 sb.append(String.format("%02x", b & 0xff)); |
|
479 } |
|
480 return sb.toString(); |
|
452 } |
481 } |
453 |
482 |
454 |
483 |
455 /** |
484 /** |
456 * Computes the readability graph for the modules in the given Configuration. |
485 * Computes the readability graph for the modules in the given Configuration. |
457 * |
486 * |
458 * The readability graph is created by propagating "requires" through the |
487 * The readability graph is created by propagating "requires" through the |
459 * "public requires" edges of the module dependence graph. So if the module |
488 * "requires transitive" edges of the module dependence graph. So if the |
460 * dependence graph has m1 requires m2 && m2 requires public m3 then the |
489 * module dependence graph has m1 requires m2 && m2 requires transitive m3 |
461 * resulting readability graph will contain m1 reads m2, m1 reads m3, and |
490 * then the resulting readability graph will contain m1 reads m2, m1 reads m3, |
462 * m2 reads m3. |
491 * and m2 reads m3. |
463 */ |
492 */ |
464 private Map<ResolvedModule, Set<ResolvedModule>> makeGraph(Configuration cf) { |
493 private Map<ResolvedModule, Set<ResolvedModule>> makeGraph(Configuration cf) { |
494 |
|
495 // initial capacity of maps to avoid resizing |
|
496 int capacity = 1 + (4 * nameToReference.size())/ 3; |
|
465 |
497 |
466 // the "reads" graph starts as a module dependence graph and |
498 // the "reads" graph starts as a module dependence graph and |
467 // is iteratively updated to be the readability graph |
499 // is iteratively updated to be the readability graph |
468 Map<ResolvedModule, Set<ResolvedModule>> g1 = new HashMap<>(); |
500 Map<ResolvedModule, Set<ResolvedModule>> g1 = new HashMap<>(capacity); |
469 |
501 |
470 // the "requires public" graph, contains requires public edges only |
502 // the "requires transitive" graph, contains requires transitive edges only |
471 Map<ResolvedModule, Set<ResolvedModule>> g2 = new HashMap<>(); |
503 Map<ResolvedModule, Set<ResolvedModule>> g2; |
472 |
504 |
473 |
505 // need "requires transitive" from the modules in parent configurations |
474 // need "requires public" from the modules in parent configurations as |
506 // as there may be selected modules that have a dependency on modules in |
475 // there may be selected modules that have a dependency on modules in |
|
476 // the parent configuration. |
507 // the parent configuration. |
477 |
508 if (Layer.boot() == null) { |
478 Configuration p = parent; |
509 g2 = new HashMap<>(capacity); |
479 while (p != null) { |
510 } else { |
480 for (ModuleDescriptor descriptor : p.descriptors()) { |
511 g2 = parents.stream() |
481 String name = descriptor.name(); |
512 .flatMap(Configuration::configurations) |
482 ResolvedModule m1 = p.findModule(name) |
513 .distinct() |
483 .orElseThrow(() -> new InternalError(name + " not found")); |
514 .flatMap(c -> |
484 for (ModuleDescriptor.Requires requires : descriptor.requires()) { |
515 c.modules().stream().flatMap(m1 -> |
485 if (requires.modifiers().contains(Modifier.PUBLIC)) { |
516 m1.descriptor().requires().stream() |
486 String dn = requires.name(); |
517 .filter(r -> r.modifiers().contains(Modifier.TRANSITIVE)) |
487 ResolvedModule m2 = p.findModule(dn) |
518 .flatMap(r -> { |
488 .orElseThrow(() -> new InternalError(dn + " not found")); |
519 Optional<ResolvedModule> m2 = c.findModule(r.name()); |
489 g2.computeIfAbsent(m1, k -> new HashSet<>()).add(m2); |
520 assert m2.isPresent() |
490 } |
521 || r.modifiers().contains(Modifier.STATIC); |
491 } |
522 return m2.stream(); |
492 } |
523 }) |
493 |
524 .map(m2 -> Map.entry(m1, m2)) |
494 p = p.parent().orElse(null); |
525 ) |
526 ) |
|
527 // stream of m1->m2 |
|
528 .collect(Collectors.groupingBy(Map.Entry::getKey, |
|
529 HashMap::new, |
|
530 Collectors.mapping(Map.Entry::getValue, Collectors.toSet()) |
|
531 )); |
|
495 } |
532 } |
496 |
533 |
497 // populate g1 and g2 with the dependences from the selected modules |
534 // populate g1 and g2 with the dependences from the selected modules |
498 |
535 |
499 Map<String, ResolvedModule> nameToResolved = new HashMap<>(); |
536 Map<String, ResolvedModule> nameToResolved = new HashMap<>(capacity); |
500 |
537 |
501 for (ModuleReference mref : nameToReference.values()) { |
538 for (ModuleReference mref : nameToReference.values()) { |
502 ModuleDescriptor descriptor = mref.descriptor(); |
539 ModuleDescriptor descriptor = mref.descriptor(); |
503 String name = descriptor.name(); |
540 String name = descriptor.name(); |
504 |
541 |
505 ResolvedModule m1 = computeIfAbsent(nameToResolved, name, cf, mref); |
542 ResolvedModule m1 = computeIfAbsent(nameToResolved, name, cf, mref); |
506 |
543 |
507 Set<ResolvedModule> reads = new HashSet<>(); |
544 Set<ResolvedModule> reads = new HashSet<>(); |
508 Set<ResolvedModule> requiresPublic = new HashSet<>(); |
545 Set<ResolvedModule> requiresTransitive = new HashSet<>(); |
509 |
546 |
510 for (ModuleDescriptor.Requires requires : descriptor.requires()) { |
547 for (ModuleDescriptor.Requires requires : descriptor.requires()) { |
511 String dn = requires.name(); |
548 String dn = requires.name(); |
512 |
549 |
513 ResolvedModule m2; |
550 ResolvedModule m2 = null; |
514 ModuleReference mref2 = nameToReference.get(dn); |
551 ModuleReference mref2 = nameToReference.get(dn); |
515 if (mref2 != null) { |
552 if (mref2 != null) { |
516 // same configuration |
553 // same configuration |
517 m2 = computeIfAbsent(nameToResolved, dn, cf, mref2); |
554 m2 = computeIfAbsent(nameToResolved, dn, cf, mref2); |
518 } else { |
555 } else { |
519 // parent configuration |
556 // parent configuration |
520 m2 = parent.findModule(dn).orElse(null); |
557 m2 = findInParent(dn); |
521 if (m2 == null) { |
558 if (m2 == null) { |
559 assert requires.modifiers().contains(Modifier.STATIC); |
|
522 continue; |
560 continue; |
523 } |
561 } |
524 } |
562 } |
525 |
563 |
526 // m1 requires m2 => m1 reads m2 |
564 // m1 requires m2 => m1 reads m2 |
527 reads.add(m2); |
565 reads.add(m2); |
528 |
566 |
529 // m1 requires public m2 |
567 // m1 requires transitive m2 |
530 if (requires.modifiers().contains(Modifier.PUBLIC)) { |
568 if (requires.modifiers().contains(Modifier.TRANSITIVE)) { |
531 requiresPublic.add(m2); |
569 requiresTransitive.add(m2); |
532 } |
570 } |
533 |
571 |
534 } |
572 } |
535 |
573 |
536 // automatic modules read all selected modules and all modules |
574 // automatic modules read all selected modules and all modules |
537 // in parent configurations |
575 // in parent configurations |
538 if (descriptor.isAutomatic()) { |
576 if (descriptor.isAutomatic()) { |
539 |
577 |
540 // reads all selected modules |
578 // reads all selected modules |
541 // `requires public` all selected automatic modules |
579 // `requires transitive` all selected automatic modules |
542 for (ModuleReference mref2 : nameToReference.values()) { |
580 for (ModuleReference mref2 : nameToReference.values()) { |
543 ModuleDescriptor descriptor2 = mref2.descriptor(); |
581 ModuleDescriptor descriptor2 = mref2.descriptor(); |
544 String name2 = descriptor2.name(); |
582 String name2 = descriptor2.name(); |
545 |
583 |
546 if (!name.equals(name2)) { |
584 if (!name.equals(name2)) { |
547 ResolvedModule m2 |
585 ResolvedModule m2 |
548 = computeIfAbsent(nameToResolved, name2, cf, mref2); |
586 = computeIfAbsent(nameToResolved, name2, cf, mref2); |
549 reads.add(m2); |
587 reads.add(m2); |
550 if (descriptor2.isAutomatic()) |
588 if (descriptor2.isAutomatic()) |
551 requiresPublic.add(m2); |
589 requiresTransitive.add(m2); |
552 } |
590 } |
553 } |
591 } |
554 |
592 |
555 // reads all modules in parent configurations |
593 // reads all modules in parent configurations |
556 // `requires public` all automatic modules in parent configurations |
594 // `requires transitive` all automatic modules in parent |
557 p = parent; |
595 // configurations |
558 while (p != null) { |
596 for (Configuration parent : parents) { |
559 for (ResolvedModule m : p.modules()) { |
597 parent.configurations() |
560 reads.add(m); |
598 .map(Configuration::modules) |
561 if (m.reference().descriptor().isAutomatic()) |
599 .flatMap(Set::stream) |
562 requiresPublic.add(m); |
600 .forEach(m -> { |
563 } |
601 reads.add(m); |
564 p = p.parent().orElse(null); |
602 if (m.reference().descriptor().isAutomatic()) |
565 } |
603 requiresTransitive.add(m); |
566 |
604 }); |
605 } |
|
567 } |
606 } |
568 |
607 |
569 g1.put(m1, reads); |
608 g1.put(m1, reads); |
570 g2.put(m1, requiresPublic); |
609 g2.put(m1, requiresTransitive); |
571 } |
610 } |
572 |
611 |
573 // Iteratively update g1 until there are no more requires public to propagate |
612 // Iteratively update g1 until there are no more requires transitive |
613 // to propagate |
|
574 boolean changed; |
614 boolean changed; |
575 Set<ResolvedModule> toAdd = new HashSet<>(); |
615 List<ResolvedModule> toAdd = new ArrayList<>(); |
576 do { |
616 do { |
577 changed = false; |
617 changed = false; |
578 for (Set<ResolvedModule> m1Reads : g1.values()) { |
618 for (Set<ResolvedModule> m1Reads : g1.values()) { |
579 for (ResolvedModule m2 : m1Reads) { |
619 for (ResolvedModule m2 : m1Reads) { |
580 Set<ResolvedModule> m2RequiresPublic = g2.get(m2); |
620 Set<ResolvedModule> m2RequiresTransitive = g2.get(m2); |
581 if (m2RequiresPublic != null) { |
621 if (m2RequiresTransitive != null) { |
582 for (ResolvedModule m3 : m2RequiresPublic) { |
622 for (ResolvedModule m3 : m2RequiresTransitive) { |
583 if (!m1Reads.contains(m3)) { |
623 if (!m1Reads.contains(m3)) { |
584 // m1 reads m2, m2 requires public m3 |
624 // m1 reads m2, m2 requires transitive m3 |
585 // => need to add m1 reads m3 |
625 // => need to add m1 reads m3 |
586 toAdd.add(m3); |
626 toAdd.add(m3); |
587 } |
627 } |
588 } |
628 } |
589 } |
629 } |
653 } |
693 } |
654 |
694 |
655 // source is exported to descriptor2 |
695 // source is exported to descriptor2 |
656 String source = export.source(); |
696 String source = export.source(); |
657 ModuleDescriptor other |
697 ModuleDescriptor other |
658 = packageToExporter.put(source, descriptor2); |
698 = packageToExporter.putIfAbsent(source, descriptor2); |
659 |
699 |
660 if (other != null && other != descriptor2) { |
700 if (other != null && other != descriptor2) { |
661 // package might be local to descriptor1 |
701 // package might be local to descriptor1 |
662 if (other == descriptor1) { |
702 if (other == descriptor1) { |
663 fail("Module %s contains package %s" |
703 fail("Module %s contains package %s" |
690 descriptor1.name(), pn); |
730 descriptor1.name(), pn); |
691 } |
731 } |
692 } |
732 } |
693 |
733 |
694 // provides S |
734 // provides S |
695 for (Map.Entry<String, ModuleDescriptor.Provides> entry : |
735 for (ModuleDescriptor.Provides provides : descriptor1.provides()) { |
696 descriptor1.provides().entrySet()) { |
736 String pn = packageName(provides.service()); |
697 String service = entry.getKey(); |
|
698 ModuleDescriptor.Provides provides = entry.getValue(); |
|
699 |
|
700 String pn = packageName(service); |
|
701 if (!packageToExporter.containsKey(pn)) { |
737 if (!packageToExporter.containsKey(pn)) { |
702 fail("Module %s does not read a module that exports %s", |
738 fail("Module %s does not read a module that exports %s", |
703 descriptor1.name(), pn); |
739 descriptor1.name(), pn); |
704 } |
740 } |
705 |
741 |
713 |
749 |
714 } |
750 } |
715 |
751 |
716 } |
752 } |
717 |
753 |
754 } |
|
755 |
|
756 /** |
|
757 * Find a module of the given name in the parent configurations |
|
758 */ |
|
759 private ResolvedModule findInParent(String mn) { |
|
760 for (Configuration parent : parents) { |
|
761 Optional<ResolvedModule> om = parent.findModule(mn); |
|
762 if (om.isPresent()) |
|
763 return om.get(); |
|
764 } |
|
765 return null; |
|
718 } |
766 } |
719 |
767 |
720 |
768 |
721 /** |
769 /** |
722 * Invokes the beforeFinder to find method to find the given module. |
770 * Invokes the beforeFinder to find method to find the given module. |
753 Set<ModuleReference> afterModules = afterFinder.findAll(); |
801 Set<ModuleReference> afterModules = afterFinder.findAll(); |
754 |
802 |
755 if (afterModules.isEmpty()) |
803 if (afterModules.isEmpty()) |
756 return beforeModules; |
804 return beforeModules; |
757 |
805 |
758 if (beforeModules.isEmpty() && parent == Configuration.empty()) |
806 if (beforeModules.isEmpty() |
807 && parents.size() == 1 |
|
808 && parents.get(0) == Configuration.empty()) |
|
759 return afterModules; |
809 return afterModules; |
760 |
810 |
761 Set<ModuleReference> result = new HashSet<>(beforeModules); |
811 Set<ModuleReference> result = new HashSet<>(beforeModules); |
762 for (ModuleReference mref : afterModules) { |
812 for (ModuleReference mref : afterModules) { |
763 String name = mref.descriptor().name(); |
813 String name = mref.descriptor().name(); |
764 if (!beforeFinder.find(name).isPresent() |
814 if (!beforeFinder.find(name).isPresent() |
765 && !parent.findModule(name).isPresent()) |
815 && findInParent(name) == null) { |
766 result.add(mref); |
816 result.add(mref); |
817 } |
|
767 } |
818 } |
768 |
819 |
769 return result; |
820 return result; |
770 |
821 |
771 } catch (FindException e) { |
822 } catch (FindException e) { |