Lines 86-92
Link Here
|
86 |
import org.openide.loaders.DataObject; |
86 |
import org.openide.loaders.DataObject; |
87 |
import org.openide.loaders.DataShadow; |
87 |
import org.openide.loaders.DataShadow; |
88 |
import org.openide.modules.Dependency; |
88 |
import org.openide.modules.Dependency; |
89 |
import org.openide.modules.ModuleInfo; |
|
|
90 |
import org.openide.util.Lookup; |
89 |
import org.openide.util.Lookup; |
91 |
import org.openide.util.Mutex; |
90 |
import org.openide.util.Mutex; |
92 |
import org.openide.util.NbCollections; |
91 |
import org.openide.util.NbCollections; |
Lines 477-488
Link Here
|
477 |
assertNoErrors(errors.size() + " actions is not registered properly", errors); |
476 |
assertNoErrors(errors.size() + " actions is not registered properly", errors); |
478 |
} |
477 |
} |
479 |
|
478 |
|
480 |
public void testIfOneFileIsDefinedTwiceByDifferentModulesTheyNeedToHaveMutualDependency() throws Exception { |
479 |
public void testLayerOverrides() throws Exception { |
481 |
ClassLoader l = Lookup.getDefault().lookup(ClassLoader.class); |
480 |
ClassLoader l = Lookup.getDefault().lookup(ClassLoader.class); |
482 |
assertNotNull ("In the IDE mode, there always should be a classloader", l); |
481 |
assertNotNull ("In the IDE mode, there always should be a classloader", l); |
483 |
|
482 |
|
484 |
// String -> List<Modules> |
|
|
485 |
Map<String,List<String>> files = new HashMap<String,List<String>>(); |
486 |
class ContentAndAttrs { |
483 |
class ContentAndAttrs { |
487 |
final byte[] contents; |
484 |
final byte[] contents; |
488 |
final Map<String,Object> attrs; |
485 |
final Map<String,Object> attrs; |
Lines 506-524
Link Here
|
506 |
return Arrays.equals(contents, caa.contents) && attrs.equals(caa.attrs); |
503 |
return Arrays.equals(contents, caa.contents) && attrs.equals(caa.attrs); |
507 |
} |
504 |
} |
508 |
} |
505 |
} |
509 |
/* < FO path , { content, attributes } > */ |
506 |
Map</* path */String,Map</* owner */String,ContentAndAttrs>> files = new TreeMap<String,Map<String,ContentAndAttrs>>(); |
510 |
Map<String,ContentAndAttrs> contents = new HashMap<String,ContentAndAttrs>(); |
|
|
511 |
/* < FO path , < module name, { content, attributes } > > */ |
512 |
Map<String,Map<String,ContentAndAttrs>> differentContents = new HashMap<String,Map<String,ContentAndAttrs>>(); |
513 |
Map</* path */String,Map</* attr name */String,Map</* module name */String,/* attr value */Object>>> folderAttributes = |
507 |
Map</* path */String,Map</* attr name */String,Map</* module name */String,/* attr value */Object>>> folderAttributes = |
514 |
new TreeMap<String,Map<String,Map<String,Object>>>(); |
508 |
new TreeMap<String,Map<String,Map<String,Object>>>(); |
|
|
509 |
Map<String,Set<String>> directDeps = new HashMap<String,Set<String>>(); |
515 |
StringBuffer sb = new StringBuffer(); |
510 |
StringBuffer sb = new StringBuffer(); |
516 |
Map<String,URL> hiddenFiles = new HashMap<String, URL>(); |
511 |
Map<String,URL> hiddenFiles = new HashMap<String, URL>(); |
517 |
Set<String> allFiles = new HashSet<String>(); |
512 |
Set<String> allFiles = new HashSet<String>(); |
518 |
final String suffix = "_hidden"; |
513 |
final String suffix = "_hidden"; |
519 |
|
514 |
|
520 |
|
|
|
521 |
boolean atLeastOne = false; |
522 |
Enumeration<URL> en = l.getResources("META-INF/MANIFEST.MF"); |
515 |
Enumeration<URL> en = l.getResources("META-INF/MANIFEST.MF"); |
523 |
while (en.hasMoreElements ()) { |
516 |
while (en.hasMoreElements ()) { |
524 |
URL u = en.nextElement(); |
517 |
URL u = en.nextElement(); |
Lines 535-569
Link Here
|
535 |
} |
528 |
} |
536 |
String layer = mf.getMainAttributes ().getValue ("OpenIDE-Module-Layer"); |
529 |
String layer = mf.getMainAttributes ().getValue ("OpenIDE-Module-Layer"); |
537 |
if (layer == null) { |
530 |
if (layer == null) { |
|
|
531 |
// XXX should also consider META-INF/generated-layer.xml here |
538 |
continue; |
532 |
continue; |
539 |
} |
533 |
} |
|
|
534 |
String depsS = mf.getMainAttributes().getValue("OpenIDE-Module-Module-Dependencies"); |
535 |
if (depsS != null) { |
536 |
Set<String> deps = new HashSet<String>(); |
537 |
for (Dependency d : Dependency.create(Dependency.TYPE_MODULE, depsS)) { |
538 |
deps.add(d.getName().replaceFirst("/.+$", "")); |
539 |
} |
540 |
directDeps.put(module, deps); |
541 |
} |
540 |
|
542 |
|
541 |
atLeastOne = true; |
|
|
542 |
URL base = new URL(u, "../"); |
543 |
URL base = new URL(u, "../"); |
543 |
URL layerURL = new URL(base, layer); |
544 |
URL layerURL = new URL(base, layer); |
544 |
java.net.URLConnection connect = layerURL.openConnection (); |
545 |
URLConnection connect = layerURL.openConnection (); |
545 |
connect.setDefaultUseCaches (false); |
546 |
connect.setDefaultUseCaches (false); |
546 |
FileSystem fs = new XMLFileSystem(layerURL); |
547 |
FileSystem fs = new XMLFileSystem(layerURL); |
547 |
|
548 |
|
548 |
Enumeration<? extends FileObject> all = fs.getRoot().getChildren(true); |
549 |
Enumeration<? extends FileObject> all = fs.getRoot().getChildren(true); |
549 |
while (all.hasMoreElements ()) { |
550 |
while (all.hasMoreElements ()) { |
550 |
FileObject fo = all.nextElement (); |
551 |
FileObject fo = all.nextElement (); |
551 |
String path = fo.getPath(); |
552 |
String simplePath = fo.getPath(); |
552 |
|
553 |
|
553 |
if (path.endsWith(suffix)) { |
554 |
if (simplePath.endsWith(suffix)) { |
554 |
hiddenFiles.put(path, layerURL); |
555 |
hiddenFiles.put(simplePath, layerURL); |
555 |
} else { |
556 |
} else { |
556 |
allFiles.add(path); |
557 |
allFiles.add(simplePath); |
557 |
} |
558 |
} |
558 |
|
559 |
|
|
|
560 |
Number weight = (Number) fo.getAttribute("weight"); |
561 |
// XXX if weight != null, test that it is actually overriding something or being overridden |
562 |
String weightedPath = weight == null ? simplePath : simplePath + "#" + weight; |
563 |
|
559 |
Map<String,Object> attributes = getAttributes(fo, base); |
564 |
Map<String,Object> attributes = getAttributes(fo, base); |
560 |
|
565 |
|
561 |
if (fo.isFolder()) { |
566 |
if (fo.isFolder()) { |
562 |
for (Map.Entry<String,Object> attr : attributes.entrySet()) { |
567 |
for (Map.Entry<String,Object> attr : attributes.entrySet()) { |
563 |
Map<String,Map<String,Object>> m1 = folderAttributes.get(path); |
568 |
Map<String,Map<String,Object>> m1 = folderAttributes.get(weightedPath); |
564 |
if (m1 == null) { |
569 |
if (m1 == null) { |
565 |
m1 = new TreeMap<String,Map<String,Object>>(); |
570 |
m1 = new TreeMap<String,Map<String,Object>>(); |
566 |
folderAttributes.put(path, m1); |
571 |
folderAttributes.put(weightedPath, m1); |
567 |
} |
572 |
} |
568 |
Map<String,Object> m2 = m1.get(attr.getKey()); |
573 |
Map<String,Object> m2 = m1.get(attr.getKey()); |
569 |
if (m2 == null) { |
574 |
if (m2 == null) { |
Lines 575-677
Link Here
|
575 |
continue; |
580 |
continue; |
576 |
} |
581 |
} |
577 |
|
582 |
|
578 |
List<String> list = files.get(path); |
583 |
Map<String,ContentAndAttrs> overrides = files.get(weightedPath); |
579 |
if (list == null) { |
584 |
if (overrides == null) { |
580 |
list = new ArrayList<String>(); |
585 |
overrides = new TreeMap<String,ContentAndAttrs>(); |
581 |
files.put (path, list); |
586 |
files.put(weightedPath, overrides); |
582 |
list.add (module); |
|
|
583 |
contents.put(path, new ContentAndAttrs(fo.asBytes(), attributes, layerURL)); |
584 |
} else { |
585 |
ContentAndAttrs contentAttrs = contents.get(path); |
586 |
ContentAndAttrs nue = new ContentAndAttrs(fo.asBytes(), attributes, layerURL); |
587 |
if (!nue.equals(contentAttrs)) { |
588 |
//System.err.println("Found differences in " + path + " between " + nue + " and " + contentAttrs); |
589 |
Map<String,ContentAndAttrs> diffs = differentContents.get(path); |
590 |
if (diffs == null) { |
591 |
diffs = new HashMap<String,ContentAndAttrs>(); |
592 |
differentContents.put(path, diffs); |
593 |
diffs.put(list.get(0), contentAttrs); |
594 |
} |
595 |
diffs.put(module, nue); |
596 |
list.add (module); |
597 |
} |
598 |
} |
587 |
} |
|
|
588 |
overrides.put(module, new ContentAndAttrs(fo.asBytes(), attributes, layerURL)); |
599 |
} |
589 |
} |
600 |
// make sure the filesystem closes the stream |
590 |
// make sure the filesystem closes the stream |
601 |
connect.getInputStream ().close (); |
591 |
connect.getInputStream ().close (); |
602 |
} |
592 |
} |
603 |
contents = null; // Not needed any more |
593 |
assertFalse("At least one layer file is usually used", allFiles.isEmpty()); |
604 |
|
594 |
|
605 |
for (Map.Entry<String,List<String>> e : files.entrySet()) { |
595 |
for (Map.Entry<String,Map<String,ContentAndAttrs>> e : files.entrySet()) { |
606 |
List<String> list = e.getValue(); |
596 |
Map<String,ContentAndAttrs> overrides = e.getValue(); |
607 |
if (list.size() == 1) { |
597 |
if (overrides.size() == 1) { |
608 |
continue; |
598 |
continue; |
609 |
} |
599 |
} |
610 |
|
600 |
Set<String> overriders = overrides.keySet(); |
611 |
Collection<? extends ModuleInfo> res = Lookup.getDefault().lookupAll(ModuleInfo.class); |
601 |
String file = e.getKey(); |
612 |
assertFalse("Some modules found", res.isEmpty()); |
602 |
|
613 |
|
603 |
if (new HashSet<ContentAndAttrs>(overrides.values()).size() == 1) { |
614 |
List<String> list2 = new ArrayList<String>(list); |
604 |
// All the same. Check whether these are parallel declarations (e.g. CND debugger vs. Java debugger), or vertical. |
615 |
for (String name : list) { |
605 |
for (String overrider : overriders) { |
616 |
for (ModuleInfo info : res) { |
606 |
Set<String> deps = new HashSet<String>(directDeps.get(overrider)); |
617 |
if (name.equals (info.getCodeName ())) { |
607 |
deps.retainAll(overriders); |
618 |
// remove dependencies |
608 |
if (!deps.isEmpty()) { |
619 |
for (Dependency d : info.getDependencies()) { |
609 |
sb.append(file).append(" is pointlessly overridden in ").append(overrider). |
620 |
list2.remove(d.getName()); |
610 |
append(" relative to ").append(deps.iterator().next()).append('\n'); |
621 |
} |
|
|
622 |
} |
611 |
} |
623 |
} |
612 |
} |
624 |
} |
|
|
625 |
// ok, modules depend on each other |
626 |
if (list2.size() <= 1) { |
627 |
continue; |
613 |
continue; |
628 |
} |
614 |
} |
629 |
|
615 |
|
630 |
sb.append (e.getKey ()).append( " is provided by: " ).append(list).append('\n'); |
616 |
sb.append(file).append(" is provided by: ").append(overriders).append('\n'); |
631 |
Map<String,ContentAndAttrs> diffList = differentContents.get(e.getKey()); |
617 |
for (Map.Entry<String,ContentAndAttrs> entry : overrides.entrySet()) { |
632 |
if (diffList != null) { |
618 |
ContentAndAttrs contentAttrs = entry.getValue(); |
633 |
if (list.size() == 2) { |
619 |
sb.append(" ").append(entry.getKey()).append(": content = '").append(new String(contentAttrs.contents)). |
634 |
String module1 = list.get(0); |
620 |
append("', attributes = ").append(contentAttrs.attrs).append("\n"); |
635 |
String module2 = list.get(1); |
|
|
636 |
ContentAndAttrs contentAttrs1 = diffList.get(module1); |
637 |
ContentAndAttrs contentAttrs2 = diffList.get(module2); |
638 |
if (!Arrays.equals(contentAttrs1.contents, contentAttrs2.contents)) { |
639 |
sb.append(' ').append(module1).append(": content = '").append(new String(contentAttrs1.contents)).append('\n'); |
640 |
sb.append(' ').append(module2).append(": content = '").append(new String(contentAttrs2.contents)).append('\n'); |
641 |
} |
642 |
if (!contentAttrs1.attrs.equals(contentAttrs2.attrs)) { |
643 |
Map<String,Object> attr1 = contentAttrs1.attrs; |
644 |
Map<String,Object> attr2 = contentAttrs2.attrs; |
645 |
Set<String> keys = new HashSet<String>(attr1.keySet()); |
646 |
keys.retainAll(attr2.keySet()); |
647 |
for (String attribute : keys) { |
648 |
Object value1 = attr1.get(attribute); |
649 |
Object value2 = attr2.get(attribute); |
650 |
if (value1 == value2 || (value1 != null && value1.equals(value2))) { |
651 |
// Remove the common attributes so that just the differences show up |
652 |
attr1.remove(attribute); |
653 |
attr2.remove(attribute); |
654 |
} |
655 |
} |
656 |
sb.append(' ').append(module1).append(": different attributes = '").append(contentAttrs1.attrs).append('\n'); |
657 |
sb.append(' ').append(module2).append(": different attributes = '").append(contentAttrs2.attrs).append('\n'); |
658 |
} |
659 |
} else { |
660 |
for (String module : list) { |
661 |
ContentAndAttrs contentAttrs = diffList.get(module); |
662 |
sb.append(" ").append(module).append(": content = '").append(new String(contentAttrs.contents)). |
663 |
append("', attributes = ").append(contentAttrs.attrs).append("\n"); |
664 |
} |
665 |
} |
666 |
} |
621 |
} |
667 |
} |
622 |
} |
668 |
|
623 |
|
669 |
assertTrue ("At least one layer file is usually used", atLeastOne); |
|
|
670 |
|
671 |
for (Map.Entry<String,Map<String,Map<String,Object>>> entry1 : folderAttributes.entrySet()) { |
624 |
for (Map.Entry<String,Map<String,Map<String,Object>>> entry1 : folderAttributes.entrySet()) { |
672 |
for (Map.Entry<String,Map<String,Object>> entry2 : entry1.getValue().entrySet()) { |
625 |
for (Map.Entry<String,Map<String,Object>> entry2 : entry1.getValue().entrySet()) { |
673 |
if (new HashSet<Object>(entry2.getValue().values()).size() > 1) { |
626 |
if (new HashSet<Object>(entry2.getValue().values()).size() > 1) { |
674 |
// XXX currently do not check if the modules are unrelated by dependency. |
|
|
675 |
sb.append("Some modules conflict on the definition of ").append(entry2.getKey()).append(" for "). |
627 |
sb.append("Some modules conflict on the definition of ").append(entry2.getKey()).append(" for "). |
676 |
append(entry1.getKey()).append(": ").append(entry2.getValue()).append("\n"); |
628 |
append(entry1.getKey()).append(": ").append(entry2.getValue()).append("\n"); |
677 |
} |
629 |
} |
Lines 679-685
Link Here
|
679 |
} |
631 |
} |
680 |
|
632 |
|
681 |
if (sb.length () > 0) { |
633 |
if (sb.length () > 0) { |
682 |
fail ("Some modules override their files and do not depend on each other\n" + sb); |
634 |
fail("Some modules override some files without using the weight attribute correctly\n" + sb); |
683 |
} |
635 |
} |
684 |
|
636 |
|
685 |
|
637 |
|