1 19 20 package org.netbeans.modules.apisupport.project.layers; 21 22 import java.beans.PropertyChangeEvent ; 23 import java.beans.PropertyChangeListener ; 24 import java.beans.PropertyChangeSupport ; 25 import java.beans.PropertyVetoException ; 26 import java.io.File ; 27 import java.io.IOException ; 28 import java.io.OutputStream ; 29 import java.net.URL ; 30 import java.util.ArrayList ; 31 import java.util.Arrays ; 32 import java.util.Collections ; 33 import java.util.Enumeration ; 34 import java.util.HashSet ; 35 import java.util.Iterator ; 36 import java.util.LinkedList ; 37 import java.util.List ; 38 import java.util.Locale ; 39 import java.util.Map ; 40 import java.util.Set ; 41 import java.util.WeakHashMap ; 42 import java.util.jar.Manifest ; 43 import java.util.regex.Matcher ; 44 import java.util.regex.Pattern ; 45 import org.netbeans.api.java.classpath.ClassPath; 46 import org.netbeans.api.project.Project; 47 import org.netbeans.api.project.ProjectManager; 48 import org.netbeans.modules.apisupport.project.EditableManifest; 49 import org.netbeans.modules.apisupport.project.ManifestManager; 50 import org.netbeans.modules.apisupport.project.NbModuleProject; 51 import org.netbeans.modules.apisupport.project.NbModuleProjectGenerator; 52 import org.netbeans.modules.apisupport.project.Util; 53 import org.netbeans.modules.apisupport.project.Util; 54 import org.netbeans.modules.apisupport.project.spi.NbModuleProvider; 55 import org.netbeans.modules.apisupport.project.suite.SuiteProject; 56 import org.netbeans.modules.apisupport.project.ui.customizer.SuiteProperties; 57 import org.netbeans.modules.apisupport.project.ui.customizer.SuiteUtils; 58 import org.netbeans.modules.apisupport.project.universe.ModuleEntry; 59 import org.netbeans.modules.apisupport.project.universe.ModuleList; 60 import org.netbeans.modules.apisupport.project.universe.NbPlatform; 61 import org.netbeans.modules.xml.tax.cookies.TreeEditorCookie; 62 import org.netbeans.modules.xml.tax.parser.XMLParsingSupport; 63 import org.netbeans.spi.java.classpath.support.ClassPathSupport; 64 import org.netbeans.spi.project.support.ant.PropertyEvaluator; 65 import org.netbeans.tax.TreeDocumentRoot; 66 import org.netbeans.tax.TreeException; 67 import org.netbeans.tax.TreeObject; 68 import org.netbeans.tax.io.TreeStreamResult; 69 import org.openide.ErrorManager; 70 import org.openide.filesystems.FileAttributeEvent; 71 import org.openide.filesystems.FileChangeListener; 72 import org.openide.filesystems.FileEvent; 73 import org.openide.filesystems.FileLock; 74 import org.openide.filesystems.FileObject; 75 import org.openide.filesystems.FileRenameEvent; 76 import org.openide.filesystems.FileStateInvalidException; 77 import org.openide.filesystems.FileSystem; 78 import org.openide.filesystems.FileUtil; 79 import org.openide.filesystems.MultiFileSystem; 80 import org.openide.filesystems.XMLFileSystem; 81 import org.openide.util.Task; 82 import org.xml.sax.InputSource ; 83 import org.xml.sax.SAXException ; 84 85 89 public class LayerUtils { 90 91 private LayerUtils() {} 92 93 99 static URL [] currentify(URL u, String suffix, ClassPath cp) { 100 if (cp == null) { 101 return new URL [] {u}; 102 } 103 try { 104 if (u.getProtocol().equals("nbres")) { String path = u.getFile(); 106 if (path.startsWith("/")) path = path.substring(1); FileObject fo = cp.findResource(path); 108 if (fo != null) { 109 return new URL [] {fo.getURL()}; 110 } 111 } else if (u.getProtocol().equals("nbresloc")) { List <URL > urls = new ArrayList (); 113 String path = u.getFile(); 114 if (path.startsWith("/")) path = path.substring(1); int idx = path.lastIndexOf('/'); 116 String folder; 117 String nameext; 118 if (idx == -1) { 119 folder = ""; nameext = path; 121 } else { 122 folder = path.substring(0, idx + 1); 123 nameext = path.substring(idx + 1); 124 } 125 idx = nameext.lastIndexOf('.'); 126 String name; 127 String ext; 128 if (idx == -1) { 129 name = nameext; 130 ext = ""; } else { 132 name = nameext.substring(0, idx); 133 ext = nameext.substring(idx); 134 } 135 List suffixes = new ArrayList (computeSubVariants(suffix)); 136 suffixes.add(suffix); 137 Collections.reverse(suffixes); 138 Iterator it = suffixes.iterator(); 139 while (it.hasNext()) { 140 String trysuffix = (String ) it.next(); 141 String trypath = folder + name + trysuffix + ext; 142 FileObject fo = cp.findResource(trypath); 143 if (fo != null) { 144 urls.add(fo.getURL()); 145 } 146 } 147 if (!urls.isEmpty()) { 148 return (URL []) urls.toArray(new URL [urls.size()]); 149 } 150 } 151 } catch (FileStateInvalidException fsie) { 152 Util.err.notify(ErrorManager.WARNING, fsie); 153 } 154 return new URL [] {u}; 155 } 156 157 private static List <String > computeSubVariants(String name) { 163 int idx = name.indexOf('_'); 164 if (idx == -1) { 165 return Collections.EMPTY_LIST; 166 } else { 167 String base = name.substring(0, idx); 168 String suffix = name.substring(idx); 169 List l = computeSubVariants(base, suffix); 170 return l.subList(0, l.size() - 1); 171 } 172 } 173 private static List <String > computeSubVariants(String base, String suffix) { 174 int idx = suffix.indexOf('_', 1); 175 if (idx == -1) { 176 List l = new LinkedList (); 177 l.add(base); 178 l.add(base + suffix); 179 return l; 180 } else { 181 String remainder = suffix.substring(idx); 182 List l1 = computeSubVariants(base, remainder); 183 List l2 = computeSubVariants(base + suffix.substring(0, idx), remainder); 184 List l = new LinkedList (l1); 185 l.addAll(l2); 186 return l; 187 } 188 } 189 190 private static final Map <Project,LayerHandle> layerHandleCache = new WeakHashMap (); 192 193 196 public static LayerHandle layerForProject(Project project) { 197 LayerHandle handle = (LayerHandle) layerHandleCache.get(project); 198 if (handle == null) { 199 handle = new LayerHandle(project); 200 layerHandleCache.put(project, handle); 201 } 202 return handle; 203 } 204 205 private static final Set <String > XML_LIKE_TYPES = new HashSet (); 206 static { 207 XML_LIKE_TYPES.add(".settings"); XML_LIKE_TYPES.add(".wstcref"); XML_LIKE_TYPES.add(".wsmode"); XML_LIKE_TYPES.add(".wsgrp"); XML_LIKE_TYPES.add(".wsmgr"); } 213 220 public static String findGeneratedName(FileObject parent, String layerPath) { 221 Matcher m = Pattern.compile("(.+/)?([^/.]+)(\\.[^/]+)?").matcher(layerPath); if (!m.matches()) { 223 throw new IllegalArgumentException (layerPath); 224 } 225 String base = m.group(2); 226 String ext = m.group(3); 227 if (ext == null) { 228 ext = ""; 229 } else if (ext.equals(".java")) { ext = "_java"; } else if (XML_LIKE_TYPES.contains(ext)) { 232 String upper = ext.substring(1,2).toUpperCase(Locale.ENGLISH); 233 base = base + upper + ext.substring(2); 234 ext = ".xml"; } 236 String name = base + ext; 237 if (parent == null || parent.getFileObject(name) == null) { 238 return name; 239 } else { 240 for (int i = 1; true; i++) { 241 name = base + '_' + i + ext; 242 if (parent.getFileObject(name) == null) { 243 return name; 244 } 245 } 246 } 247 } 248 249 252 interface SavableTreeEditorCookie extends TreeEditorCookie { 253 254 255 String PROP_DIRTY = "dirty"; 257 258 boolean isDirty(); 259 260 261 void save() throws IOException ; 262 263 } 264 265 private static final class CookieImpl implements SavableTreeEditorCookie, FileChangeListener { 266 private TreeDocumentRoot root; 267 private boolean dirty; 268 private Exception problem; 269 private final FileObject f; 270 private final PropertyChangeSupport pcs = new PropertyChangeSupport (this); 271 private boolean saving; 272 public CookieImpl(FileObject f) { 273 this.f = f; 275 f.addFileChangeListener(FileUtil.weakFileChangeListener(this, f)); 276 } 277 public TreeDocumentRoot getDocumentRoot() { 278 return root; 279 } 280 public int getStatus() { 281 if (problem != null) { 282 return TreeEditorCookie.STATUS_ERROR; 283 } else if (root != null) { 284 return TreeEditorCookie.STATUS_OK; 285 } else { 286 return TreeEditorCookie.STATUS_NOT; 287 } 288 } 289 public TreeDocumentRoot openDocumentRoot() throws IOException , TreeException { 290 if (root == null) { 291 try { 292 boolean oldDirty = dirty; 294 int oldStatus = getStatus(); 295 root = new XMLParsingSupport().parse(new InputSource (f.getURL().toExternalForm())); 296 problem = null; 297 dirty = false; 298 pcs.firePropertyChange(PROP_DIRTY, oldDirty, false); 299 pcs.firePropertyChange(PROP_STATUS, oldStatus, TreeEditorCookie.STATUS_OK); 300 } catch (IOException e) { 302 problem = e; 303 throw e; 304 } catch (TreeException e) { 305 problem = e; 306 throw e; 307 } 308 ((TreeObject) root).addPropertyChangeListener(new PropertyChangeListener () { 309 public void propertyChange(PropertyChangeEvent evt) { 310 modified(); 312 } 313 }); 314 } 315 return root; 316 } 317 public Task prepareDocumentRoot() { 318 throw new UnsupportedOperationException (); 319 } 320 public void addPropertyChangeListener(PropertyChangeListener listener) { 321 pcs.addPropertyChangeListener(listener); 322 } 323 public void removePropertyChangeListener(PropertyChangeListener listener) { 324 pcs.removePropertyChangeListener(listener); 325 } 326 private void modified() { 327 if (!dirty) { 329 dirty = true; 330 pcs.firePropertyChange(PROP_DIRTY, false, true); 331 } 332 } 333 public boolean isDirty() { 334 return dirty; 335 } 336 public synchronized void save() throws IOException { 337 if (root == null || !dirty) { 339 return; 340 } 341 assert !saving; 342 saving = true; 343 try { 345 FileLock lock = f.lock(); 346 try { 347 OutputStream os = f.getOutputStream(lock); 348 try { 349 new TreeStreamResult(os).getWriter(root).writeDocument(); 350 } catch (TreeException e) { 351 throw (IOException ) new IOException (e.toString()).initCause(e); 352 } finally { 353 os.close(); 354 } 355 } finally { 356 lock.releaseLock(); 357 } 358 } finally { 359 saving = false; 360 } 362 dirty = false; 363 pcs.firePropertyChange(PROP_DIRTY, true, false); 364 } 365 public void fileChanged(FileEvent fe) { 366 changed(); 367 } 368 public void fileDeleted(FileEvent fe) { 369 changed(); 370 } 371 public void fileRenamed(FileRenameEvent fe) { 372 changed(); 373 } 374 public void fileAttributeChanged(FileAttributeEvent fe) { 375 } 377 public void fileFolderCreated(FileEvent fe) { 378 assert false; 379 } 380 public void fileDataCreated(FileEvent fe) { 381 assert false; 382 } 383 private void changed() { 384 synchronized (this) { 386 if (saving) { 387 return; 388 } 389 problem = null; 390 dirty = false; 391 root = null; 392 } 393 pcs.firePropertyChange(PROP_DOCUMENT_ROOT, null, null); 394 } 395 } 396 397 static SavableTreeEditorCookie cookieForFile(FileObject f) { 398 return new CookieImpl(f); 399 } 400 401 404 public static final class LayerHandle { 405 406 private final Project project; 407 private FileSystem fs; 408 private SavableTreeEditorCookie cookie; 409 private boolean autosave; 410 411 LayerHandle(Project project) { 412 this.project = project; 414 } 415 416 423 public synchronized FileSystem layer(boolean create) { 424 if (fs == null) { 425 FileObject xml = getLayerFile(); 426 if (xml == null) { 427 if (!create) { 428 return null; 429 } 430 try { 431 NbModuleProvider module = project.getLookup().lookup(NbModuleProvider.class); 432 String layerSrcPath = ManifestManager.getInstance(Util.getManifest(module.getManifestFile()), false).getLayer(); 434 if (layerSrcPath == null) { 435 layerSrcPath = newLayerPath(); 436 FileObject manifest = module.getManifestFile(); 437 EditableManifest m = Util.loadManifest(manifest); 438 m.setAttribute(ManifestManager.OPENIDE_MODULE_LAYER, layerSrcPath, null); 439 Util.storeManifest(manifest, m); 440 } 441 xml = NbModuleProjectGenerator.createLayer(project.getProjectDirectory(), module.getResourceDirectoryPath(false) + '/' + newLayerPath()); 442 } catch (IOException e) { 443 Util.err.notify(ErrorManager.INFORMATIONAL, e); 444 return fs = FileUtil.createMemoryFileSystem(); 445 } 446 } 447 try { 448 fs = new WritableXMLFileSystem(xml.getURL(), cookie = cookieForFile(xml), null); 449 } catch (FileStateInvalidException e) { 450 throw new AssertionError (e); 451 } 452 cookie.addPropertyChangeListener(new PropertyChangeListener () { 453 public void propertyChange(PropertyChangeEvent evt) { 454 if (autosave && SavableTreeEditorCookie.PROP_DIRTY.equals(evt.getPropertyName())) { 456 try { 458 save(); 459 } catch (IOException e) { 460 Util.err.notify(ErrorManager.INFORMATIONAL, e); 461 } 462 } 463 } 464 }); 465 } 466 return fs; 467 } 468 469 473 public void save() throws IOException { 474 if (cookie == null) { 475 throw new IOException ("Cannot save a nonexistent layer"); } 477 cookie.save(); 478 } 479 480 484 public FileObject getLayerFile() { 485 NbModuleProvider module = project.getLookup().lookup(NbModuleProvider.class); 486 Manifest mf = Util.getManifest(module.getManifestFile()); 487 if (mf == null) { 488 return null; 489 } 490 String path = ManifestManager.getInstance(mf, false).getLayer(); 491 if (path == null) { 492 return null; 493 } 494 return Util.getResourceDirectory(project).getFileObject(path); 495 } 496 497 501 public void setAutosave(boolean autosave) { 502 this.autosave = autosave; 503 if (autosave && cookie != null) { 504 try { 505 cookie.save(); 506 } catch (IOException e) { 507 Util.err.notify(ErrorManager.INFORMATIONAL, e); 508 } 509 } 510 } 511 512 515 public boolean isAutosave() { 516 return autosave; 517 } 518 519 522 private String newLayerPath() { 523 NbModuleProvider module = project.getLookup().lookup(NbModuleProvider.class); 524 return module.getCodeNameBase().replace('.', '/') + "/layer.xml"; } 526 527 } 528 529 559 public static FileSystem getEffectiveSystemFilesystem(Project p) throws IOException { 560 561 NbModuleProvider.NbModuleType type = Util.getModuleType(p); 562 FileSystem projectLayer = layerForProject(p).layer(false); 563 if (type == NbModuleProvider.STANDALONE) { 564 Set <File > jars = 565 getPlatformJarsForStandaloneProject(p); 566 FileSystem[] platformLayers = getPlatformLayers(jars); 567 ClassPath cp = createLayerClasspath(Collections.singleton(p), jars); 568 return mergeFilesystems(projectLayer, platformLayers, cp); 569 } else if (type == NbModuleProvider.SUITE_COMPONENT) { 570 SuiteProject suite = SuiteUtils.findSuite(p); 571 if (suite == null) { 572 throw new IOException ("Could not load suite for " + p); } 574 List <FileSystem> readOnlyLayers = new ArrayList (); 575 Set <? extends Project> modules = SuiteUtils.getSubProjects(suite); 576 Iterator it = modules.iterator(); 577 while (it.hasNext()) { 578 NbModuleProject sister = (NbModuleProject) it.next(); 579 if (sister == p) { 580 continue; 581 } 582 LayerHandle handle = layerForProject(sister); 583 FileSystem roLayer = handle.layer(false); 584 if (roLayer != null) { 585 readOnlyLayers.add(roLayer); 586 } 587 } 588 Set <File > jars = getPlatformJarsForSuiteComponentProject(p, suite); 589 readOnlyLayers.addAll(Arrays.asList(getPlatformLayers(jars))); 590 ClassPath cp = createLayerClasspath(modules, jars); 591 return mergeFilesystems(projectLayer, (FileSystem[]) readOnlyLayers.toArray(new FileSystem[readOnlyLayers.size()]), cp); 592 } else if (type == NbModuleProvider.NETBEANS_ORG) { 593 NbModuleProject nbprj = p.getLookup().lookup(NbModuleProject.class); 595 Set <? extends Project> projects = getProjectsForNetBeansOrgProject(nbprj); 596 List <URL > otherLayerURLs = new ArrayList (); 597 Iterator it = projects.iterator(); 598 while (it.hasNext()) { 599 NbModuleProject p2 = (NbModuleProject) it.next(); 600 ManifestManager mm = ManifestManager.getInstance(p2.getManifest(), false); 601 String layer = mm.getLayer(); 602 if (layer == null) { 603 continue; 604 } 605 FileObject src = p2.getSourceDirectory(); 606 if (src == null) { 607 continue; 608 } 609 FileObject layerXml = src.getFileObject(layer); 610 if (layerXml == null) { 611 continue; 612 } 613 otherLayerURLs.add(layerXml.getURL()); 614 } 615 XMLFileSystem xfs = new XMLFileSystem(); 616 try { 617 xfs.setXmlUrls((URL []) otherLayerURLs.toArray(new URL [otherLayerURLs.size()])); 618 } catch (PropertyVetoException ex) { 619 assert false : ex; 620 } 621 ClassPath cp = createLayerClasspath(projects, Collections.EMPTY_SET); 622 return mergeFilesystems(projectLayer, new FileSystem[] {xfs}, cp); 623 } else { 624 throw new AssertionError (type); 625 } 626 } 627 628 631 public static Set <File > getPlatformJarsForStandaloneProject(Project project) { 632 NbModuleProvider mod = project.getLookup().lookup(NbModuleProvider.class); 633 NbPlatform platform = null; 635 File platformDir = mod.getActivePlatformLocation(); 636 if (platformDir != null) { 637 platform = NbPlatform.getPlatformByDestDir(platformDir); 638 } 639 if (platform == null || !platform.isValid()) { 640 platform = NbPlatform.getDefaultPlatform(); 641 } 642 return getPlatformJars(platform, null, null, null); 643 } 644 645 public static Set <File > getPlatformJarsForSuiteComponentProject(Project project, SuiteProject suite) { 646 NbPlatform platform = suite.getPlatform(true); 647 PropertyEvaluator eval = suite.getEvaluator(); 648 String [] includedClusters = SuiteProperties.getArrayProperty(eval, SuiteProperties.ENABLED_CLUSTERS_PROPERTY); 649 String [] excludedClusters = SuiteProperties.getArrayProperty(eval, SuiteProperties.DISABLED_CLUSTERS_PROPERTY); 650 String [] excludedModules = SuiteProperties.getArrayProperty(eval, SuiteProperties.DISABLED_MODULES_PROPERTY); 651 return getPlatformJars(platform, includedClusters, excludedClusters, excludedModules); 652 } 653 654 public static Set <NbModuleProject> getProjectsForNetBeansOrgProject(NbModuleProject project) throws IOException { 655 ModuleList list = project.getModuleList(); 656 Set <NbModuleProject> projects = new HashSet (); 657 projects.add(project); 658 Iterator it = list.getAllEntriesSoft().iterator(); 659 while (it.hasNext()) { 660 ModuleEntry other = (ModuleEntry) it.next(); 661 if (other.getClusterDirectory().getName().equals("extra")) { continue; 663 } 664 File root = other.getSourceLocation(); 665 assert root != null : other; 666 NbModuleProject p2 = (NbModuleProject) ProjectManager.getDefault().findProject(FileUtil.toFileObject(root)); 667 if (p2 == null) { 668 continue; 669 } 670 projects.add(p2); 671 } 672 return projects; 673 } 674 675 679 private static Set <File > getPlatformJars(NbPlatform platform, String [] includedClusters, String [] excludedClusters, String [] excludedModules) { 680 if (platform == null) { 681 return Collections.EMPTY_SET; 682 } 683 Set <String > includedClustersS = (includedClusters != null) ? new HashSet (Arrays.asList(includedClusters)) : Collections.EMPTY_SET; 684 Set <String > excludedClustersS = (excludedClusters != null) ? new HashSet (Arrays.asList(excludedClusters)) : Collections.EMPTY_SET; 685 Set <String > excludedModulesS = (excludedModules != null) ? new HashSet (Arrays.asList(excludedModules)) : Collections.EMPTY_SET; 686 ModuleEntry[] entries = platform.getModules(); 687 Set <File > jars = new HashSet (entries.length); 688 for (int i = 0; i < entries.length; i++) { 689 if (!includedClustersS.isEmpty() && !includedClustersS.contains(entries[i].getClusterDirectory().getName())) { 690 continue; 691 } 692 if (includedClustersS.isEmpty() && excludedClustersS.contains(entries[i].getClusterDirectory().getName())) { 693 continue; 694 } 695 if (excludedModulesS.contains(entries[i].getCodeNameBase())) { 696 continue; 697 } 698 jars.add(entries[i].getJarLocation()); 699 } 700 return jars; 701 } 702 703 706 private static FileSystem[] getPlatformLayers(Set <File > platformJars) throws IOException { 707 List <FileSystem> layers = new ArrayList (); 708 Iterator it = platformJars.iterator(); 709 JAR: while (it.hasNext()) { 710 File jar = (File ) it.next(); 711 ManifestManager mm = ManifestManager.getInstanceFromJAR(jar); 712 String [] toks = mm.getRequiredTokens(); 713 for (int i = 0; i < toks.length; i++) { 714 if (toks[i].startsWith("org.openide.modules.os.")) { continue JAR; 717 } 718 } 719 String layer = mm.getLayer(); 720 if (layer != null) { 721 URL u = new URL ("jar:" + jar.toURI() + "!/" + layer); 722 try { 723 FileSystem xfs = new XMLFileSystem(u); 726 boolean hasMasks = false; 727 Enumeration e = xfs.getRoot().getChildren(true); 728 while (e.hasMoreElements()) { 729 FileObject f = (FileObject) e.nextElement(); 730 if (f.getNameExt().endsWith("_hidden")) { hasMasks = true; 733 break; 734 } 735 } 736 if (hasMasks) { 737 layers.add(0, xfs); 738 } else { 739 layers.add(xfs); 740 } 741 } catch (SAXException e) { 742 throw (IOException ) new IOException (e.toString()).initCause(e); 743 } 744 } 745 } 746 return (FileSystem[]) layers.toArray(new FileSystem[layers.size()]); 747 } 748 749 752 static ClassPath createLayerClasspath(Set <? extends Project> moduleProjects, Set <File > platformJars) throws IOException { 753 List <URL > roots = new ArrayList (); 754 for (Project p : moduleProjects) { 755 NbModuleProvider mod = p.getLookup().lookup(NbModuleProvider.class); 756 FileObject src = mod.getSourceDirectory(); 757 if (src != null) { 758 roots.add(src.getURL()); 759 } 760 } 761 for (File jar : platformJars) { 762 roots.add(FileUtil.getArchiveRoot(jar.toURI().toURL())); 763 File locale = new File (jar.getParentFile(), "locale"); if (locale.isDirectory()) { 765 String n = jar.getName(); 766 int x = n.lastIndexOf('.'); 767 if (x == -1) { 768 x = n.length(); 769 } 770 String base = n.substring(0, x); 771 String ext = n.substring(x); 772 String [] variants = locale.list(); 773 if (variants != null) { 774 for (int i = 0; i < variants.length; i++) { 775 if (variants[i].startsWith(base) && variants[i].endsWith(ext) && variants[i].charAt(x) == '_') { 776 roots.add(FileUtil.getArchiveRoot(new File (locale, variants[i]).toURI().toURL())); 777 } 778 } 779 } 780 } 781 } 782 return ClassPathSupport.createClassPath((URL []) roots.toArray(new URL [roots.size()])); 784 } 785 786 790 private static FileSystem mergeFilesystems(FileSystem writableLayer, FileSystem[] readOnlyLayers, final ClassPath cp) { 791 if (writableLayer == null) { 792 writableLayer = new XMLFileSystem(); 793 } 794 final FileSystem[] layers = new FileSystem[readOnlyLayers.length + 1]; 795 layers[0] = writableLayer; 796 System.arraycopy(readOnlyLayers, 0, layers, 1, readOnlyLayers.length); 797 class BadgingMergedFileSystem extends MultiFileSystem { 798 private final BadgingSupport status; 799 public BadgingMergedFileSystem() { 800 super(layers); 801 status = new BadgingSupport(this); 802 status.setClasspath(cp); 803 status.setSuffix("_" + Locale.getDefault()); 804 } 806 public FileSystem.Status getStatus() { 807 return status; 808 } 809 } 810 return new BadgingMergedFileSystem(); 811 } 812 813 } 814 | Popular Tags |