KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > apisupport > project > layers > WritableXMLFileSystem


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.apisupport.project.layers;
21
22 import java.beans.PropertyChangeEvent JavaDoc;
23 import java.beans.PropertyChangeListener JavaDoc;
24 import java.io.ByteArrayInputStream JavaDoc;
25 import java.io.ByteArrayOutputStream JavaDoc;
26 import java.io.FileNotFoundException JavaDoc;
27 import java.io.FilterOutputStream JavaDoc;
28 import java.io.IOException JavaDoc;
29 import java.io.InputStream JavaDoc;
30 import java.io.NotSerializableException JavaDoc;
31 import java.io.ObjectOutputStream JavaDoc;
32 import java.io.OutputStream JavaDoc;
33 import java.io.UnsupportedEncodingException JavaDoc;
34 import java.net.MalformedURLException JavaDoc;
35 import java.net.URI JavaDoc;
36 import java.net.URL JavaDoc;
37 import java.net.URLConnection JavaDoc;
38 import java.util.ArrayList JavaDoc;
39 import java.util.Arrays JavaDoc;
40 import java.util.Collection JavaDoc;
41 import java.util.Collections JavaDoc;
42 import java.util.Comparator JavaDoc;
43 import java.util.Date JavaDoc;
44 import java.util.Enumeration JavaDoc;
45 import java.util.HashSet JavaDoc;
46 import java.util.Iterator JavaDoc;
47 import java.util.LinkedHashMap JavaDoc;
48 import java.util.LinkedHashSet JavaDoc;
49 import java.util.Map JavaDoc;
50 import java.util.Set JavaDoc;
51 import java.util.SortedSet JavaDoc;
52 import java.util.StringTokenizer JavaDoc;
53 import java.util.TreeSet JavaDoc;
54 import org.netbeans.api.java.classpath.ClassPath;
55 import org.netbeans.modules.apisupport.project.Util;
56 import org.netbeans.modules.xml.tax.cookies.TreeEditorCookie;
57 import org.netbeans.tax.InvalidArgumentException;
58 import org.netbeans.tax.ReadOnlyException;
59 import org.netbeans.tax.TreeAttribute;
60 import org.netbeans.tax.TreeCDATASection;
61 import org.netbeans.tax.TreeChild;
62 import org.netbeans.tax.TreeDocumentRoot;
63 import org.netbeans.tax.TreeElement;
64 import org.netbeans.tax.TreeException;
65 import org.netbeans.tax.TreeObjectList;
66 import org.netbeans.tax.TreeParentNode;
67 import org.netbeans.tax.TreeText;
68 import org.openide.ErrorManager;
69 import org.openide.filesystems.AbstractFileSystem;
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.FileUtil;
77 import org.openide.filesystems.URLMapper;
78 import org.openide.util.Enumerations;
79 import org.openide.util.NbCollections;
80 import org.openide.util.TopologicalSortException;
81 import org.openide.util.Utilities;
82 import org.openide.util.WeakListeners;
83
84 /**
85  * A filesystem which is based on a TAX document and implements
86  * the same syntax as XMLFileSystem, from which inspiration is taken.
87  * Not implemented similarly to XMLFileSystem because this is writable
88  * and designed specifically to write human-readable XML and work nicely
89  * as an authoring tool. The filesystem expects to get an XML document
90  * according to DTD "-//NetBeans//DTD Filesystem 1.0//EN" (or 1.1 is OK).
91  * When it is changed via FileSystems API, it will fire TAX
92  * events. Not intended to be efficient or terribly robust, since it
93  * is development-time only.
94  * @author Jesse Glick
95  */

96 final class WritableXMLFileSystem extends AbstractFileSystem
97         implements AbstractFileSystem.Attr,
98         AbstractFileSystem.Change,
99         AbstractFileSystem.Info,
100         AbstractFileSystem.List,
101         //AbstractFileSystem.Transfer,
102
FileChangeListener,
103         PropertyChangeListener JavaDoc {
104     
105     private final TreeEditorCookie cookie;
106     private TreeDocumentRoot doc; // may be null if malformed
107
private URL JavaDoc location;
108     private String JavaDoc suffix; // for branding/localization like "_f4j_ce_ja"; never null, at worst ""
109
private final FileChangeListener fileChangeListener;
110     private ClassPath classpath; // OK to be null
111

112     public WritableXMLFileSystem(URL JavaDoc location, TreeEditorCookie cookie, ClassPath classpath) {
113         this.attr = this;
114         this.change = this;
115         this.info = this;
116         this.list = this;
117         //this.transfer = this;
118
this.cookie = cookie;
119         try {
120             doc = cookie.openDocumentRoot();
121         } catch (TreeException e) {
122             Util.err.notify(ErrorManager.INFORMATIONAL, e);
123         } catch (IOException JavaDoc e) {
124             Util.err.notify(ErrorManager.INFORMATIONAL, e);
125         }
126         fileChangeListener = FileUtil.weakFileChangeListener(this, null);
127         cookie.addPropertyChangeListener(WeakListeners.propertyChange(this, cookie));
128         setLocation(location);
129         setClasspath(classpath);
130     }
131     
132     private void writeObject(ObjectOutputStream JavaDoc out) throws IOException JavaDoc {
133         throw new NotSerializableException JavaDoc("WritableXMLFileSystem is not persistent"); // NOI18N
134
}
135     
136     private void setLocation(URL JavaDoc location) {
137         String JavaDoc u = location.toExternalForm();
138         if (u.endsWith("/")) { // NOI18N
139
throw new IllegalArgumentException JavaDoc(u);
140         }
141         this.location = location;
142     }
143     
144     private void setClasspath(ClassPath classpath) {
145         this.classpath = classpath;
146     }
147     
148     public String JavaDoc getDisplayName() {
149         FileObject fo = URLMapper.findFileObject(location);
150         if (fo != null) {
151             return FileUtil.getFileDisplayName(fo);
152         } else {
153             return location.toExternalForm();
154         }
155     }
156     
157     public boolean isReadOnly() {
158         return false;
159     }
160     
161     private TreeElement getRootElement() {
162         if (doc == null) {
163             return null;
164         }
165         Iterator JavaDoc it;
166         it = doc.getChildNodes().iterator();
167         while (it.hasNext()) {
168             Object JavaDoc next = it.next();
169             if (next instanceof TreeElement) {
170                 return (TreeElement) next;
171             }
172         }
173         return null;
174     }
175     
176     /** Given a resource name, find the matching DOM element.
177      * @return a <folder> or <file> or <filesystem> element, or null if file does not exist
178      */

179     private TreeElement findElement(String JavaDoc name) {
180         return findElementIn(getRootElement(), name);
181     }
182     /** helper method only */
183     private static TreeElement findElementIn(TreeElement el, String JavaDoc name) {
184         if (el == null) return null;
185         if (name.equals("")) { // NOI18N
186
return el;
187         } else {
188             int idx = name.indexOf((char) '/');
189             String JavaDoc nextName, remainder;
190             if (idx == -1) {
191                 nextName = name;
192                 remainder = ""; // NOI18N
193
} else {
194                 nextName = name.substring(0, idx);
195                 remainder = name.substring(idx + 1);
196             }
197             TreeElement subel = null;
198             Iterator JavaDoc it = el.getChildNodes(TreeElement.class).iterator();
199             while (it.hasNext()) {
200                 TreeElement e = (TreeElement) it.next();
201                 if (e.getLocalName().equals("file") || // NOI18N
202
e.getLocalName().equals("folder")) { // NOI18N
203
TreeAttribute attr = e.getAttribute("name"); // NOI18N
204
if (attr != null && attr.getValue().equals(nextName)) {
205                         subel = e;
206                         break;
207                     }
208                 }
209             }
210             return findElementIn(subel, remainder);
211         }
212     }
213     
214     public boolean folder(String JavaDoc name) {
215         TreeElement el = findElement(name);
216         if (el == null) {
217             //System.err.println("folder <" + name + ">: false, no such element");
218
return false;
219         }
220         boolean res = el.getLocalName().equals("folder"); // NOI18N
221
//System.err.println("folder <" + name + ">: " + res);
222
return res;
223     }
224     
225     /*
226     private static final Set warnedAboutDupeKids = new HashSet(1); // Set<String>
227      */

228     public String JavaDoc[] children(String JavaDoc f) {
229         TreeElement el = findElement(f);
230         if (el == null) {
231             //System.err.println("children <" + f + ">: none, no such element");
232
return new String JavaDoc[] {};
233         }
234         Iterator JavaDoc it = el.getChildNodes(TreeElement.class).iterator();
235         ArrayList JavaDoc kids = new ArrayList JavaDoc(); // List<String>
236
Set JavaDoc allNames = new HashSet JavaDoc(); // Set<String>
237
while (it.hasNext()) {
238             TreeElement sub = (TreeElement) it.next();
239             if (sub.getLocalName().equals("file") || // NOI18N
240
sub.getLocalName().equals("folder")) { // NOI18N
241
TreeAttribute attr = sub.getAttribute("name"); // NOI18N
242
if (attr == null) {
243                     continue;
244                 }
245                 String JavaDoc name = attr.getValue(); // NOI18N
246
if (allNames.add(name)) {
247                     kids.add(name);
248                         /*
249                 } else {
250                     if (warnedAboutDupeKids.add(location + ":" + f + "/" + name)) { // NOI18N
251                         // #18699: will deadlock if you try to change anything.
252                         if (f.equals("")) { // NOI18N
253                             LayerDataNode.getErr().println("WARNING: in " + xmlfile + " the root folder contains the child " + name + " more than once.");
254                         } else {
255                             LayerDataNode.getErr().println("WARNING: in " + xmlfile + " the folder " + f + " contains the child " + name + " more than once.");
256                         }
257                         //LayerDataNode.getErr().println("The Open APIs Support module will not work properly with such a layer.");
258                         //LayerDataNode.getErr().println("Please edit the XML text and merge together all children of a <folder> with the same name.");
259                     }
260                          */

261                 }
262             }
263         }
264         //System.err.println("children <" + f + ">: " + kids);
265
return (String JavaDoc[]) kids.toArray(new String JavaDoc[kids.size()]);
266     }
267     
268     /** retrieve byte contents of a named resource */
269     private byte[] getContentsOf(final String JavaDoc name) throws FileNotFoundException JavaDoc {
270         TreeElement el = findElement(name);
271         if (el == null) throw new FileNotFoundException JavaDoc(name);
272         TreeAttribute attr = el.getAttribute("url"); // NOI18N
273
if (attr != null) {
274             try {
275                 URL JavaDoc[] u = LayerUtils.currentify(new URL JavaDoc(location, attr.getValue()), suffix, classpath);
276                 URLConnection JavaDoc conn = u[0].openConnection();
277                 conn.connect();
278                 InputStream JavaDoc is = conn.getInputStream();
279                 byte[] buf = new byte[conn.getContentLength()];
280                 if (is.read(buf) != buf.length) throw new IOException JavaDoc("wrong content length"); // NOI18N
281
// Also listen to changes in it.
282
FileObject fo = URLMapper.findFileObject(u[0]);
283                 if (fo != null) {
284                     fo.removeFileChangeListener(fileChangeListener);
285                     fo.addFileChangeListener(fileChangeListener);
286                 }
287                 return buf;
288             } catch (IOException JavaDoc ioe) {
289                 throw new FileNotFoundException JavaDoc(ioe.getMessage());
290             }
291         } else {
292             StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
293             Iterator JavaDoc it = el.getChildNodes().iterator();
294             while (it.hasNext()) {
295                 Object JavaDoc o = it.next();
296                 if (o instanceof TreeCDATASection) {
297                     buf.append(((TreeCDATASection) o).getData());
298                 } else if (o instanceof TreeText) {
299                     buf.append(((TreeText) o).getData().trim());
300                 }
301             }
302             try {
303                 // This encoding is intentional...
304
return buf.toString().getBytes("UTF-8"); // NOI18N
305
} catch (UnsupportedEncodingException JavaDoc uee) {
306                 throw new FileNotFoundException JavaDoc(uee.getMessage());
307             }
308         }
309     }
310     
311     // [PENDING] should I/O from/to external text files be done via EditorCookie?
312
// Not clear if this is safe (call from FS -> DS) even tho in separate FSs...
313

314     public InputStream JavaDoc inputStream(String JavaDoc name) throws FileNotFoundException JavaDoc {
315         return new ByteArrayInputStream JavaDoc(getContentsOf(name));
316     }
317     
318     public OutputStream JavaDoc outputStream(final String JavaDoc name) throws IOException JavaDoc {
319         final TreeElement el = findElement(name);
320         if (el == null) {
321             throw new FileNotFoundException JavaDoc(name);
322         }
323         TreeAttribute attr = el.getAttribute("url"); // NOI18N
324
if (attr != null) {
325             String JavaDoc u = attr.getValue();
326             if (URI.create(u).isAbsolute()) {
327                 // What to do? Can't overwrite it, obviously.
328
throw new IOException JavaDoc(name);
329             }
330             // We have an existing external file. Try to write to that instead.
331
FileObject external = URLMapper.findFileObject(new URL JavaDoc(location, u));
332             if (external == null) {
333                 throw new FileNotFoundException JavaDoc(name);
334             }
335             final FileLock lock = external.lock();
336             return new FilterOutputStream JavaDoc(external.getOutputStream(lock)) {
337                 public void close() throws IOException JavaDoc {
338                     super.close();
339                     lock.releaseLock();
340                 }
341             };
342         }
343         // We will change the layer file.
344
return new ByteArrayOutputStream JavaDoc() {
345             public void close() throws IOException JavaDoc {
346                 super.close();
347                 byte[] contents = toByteArray();
348                 /* If desired to kill any existing inline content:
349                 Iterator it = el.getChildNodes().iterator();
350                 ArrayList/ *<TreeCDATASection>* / allCdata = new ArrayList();
351                 while (it.hasNext()) {
352                     Object o = it.next();
353                     if (o instanceof TreeCDATASection) {
354                         allCdata.add(o);
355                     } else if (o instanceof TreeText &&
356                             ((TreeText) o).getData().trim().length() > 0) {
357                         el.removeChild((TreeText) o);
358                     }
359                 }
360                 Iterator it = allCdata.iterator();
361                 while (it.hasNext()) {
362                     el.removeChild((CDATASection) it.next());
363                 }
364                  */

365                 FileObject parent = findLayerParent();
366                 String JavaDoc externalName = LayerUtils.findGeneratedName(parent, name);
367                 assert externalName.indexOf('/') == -1 : externalName;
368                 FileObject externalFile = parent.createData(externalName);
369                 FileLock lock = externalFile.lock();
370                 try {
371                     OutputStream JavaDoc os = externalFile.getOutputStream(lock);
372                     try {
373                         os.write(contents);
374                     } finally {
375                         os.close();
376                     }
377                 } finally {
378                     lock.releaseLock();
379                 }
380                 externalFile.addFileChangeListener(fileChangeListener);
381                 try {
382                     el.addAttribute("url", externalName); // NOI18N
383
} catch (ReadOnlyException e) {
384                     throw (IOException JavaDoc) new IOException JavaDoc(e.toString()).initCause(e);
385                 } catch (InvalidArgumentException e) {
386                     assert false : e;
387                 }
388             }
389         };
390     }
391     private FileObject findLayerParent() throws IOException JavaDoc {
392         String JavaDoc loc = location.toExternalForm();
393         int slash = loc.lastIndexOf('/');
394         assert slash != -1 : loc;
395         FileObject parent = URLMapper.findFileObject(new URL JavaDoc(loc.substring(0, slash + 1)));
396         if (parent == null) {
397             throw new IOException JavaDoc(loc);
398         }
399         return parent;
400     }
401     
402     private void createFileOrFolder(String JavaDoc name, boolean folder) throws IOException JavaDoc {
403         String JavaDoc parentName, baseName;
404         int idx = name.lastIndexOf('/');
405         if (idx == -1) {
406             parentName = ""; // NOI18N
407
baseName = name;
408         } else {
409             parentName = name.substring(0, idx);
410             baseName = name.substring(idx + 1);
411         }
412         TreeElement el = findElement(parentName);
413         if (el == null) {
414             throw new FileNotFoundException JavaDoc(parentName);
415         }
416         try {
417             TreeElement nue = new TreeElement(folder ? "folder" : "file", true); // NOI18N
418
nue.addAttribute("name", baseName); // NOI18N
419
appendWithIndent(el, nue);
420         } catch (InvalidArgumentException e) {
421             assert false : e;
422         } catch (ReadOnlyException e) {
423             throw (IOException JavaDoc) new IOException JavaDoc(e.toString()).initCause(e);
424         }
425     }
426     
427     public void createFolder(String JavaDoc name) throws IOException JavaDoc {
428         createFileOrFolder(name, true);
429     }
430     
431     public void createData(String JavaDoc name) throws IOException JavaDoc {
432         createFileOrFolder(name, false);
433     }
434     
435     public void delete(String JavaDoc name) throws IOException JavaDoc {
436         TreeElement el = findElement(name);
437         if (el == null) {
438             throw new FileNotFoundException JavaDoc(name);
439         }
440         TreeAttribute externalName = el.getAttribute("url"); // NOI18N
441
if (externalName != null && !URI.create(externalName.getValue()).isAbsolute()) {
442             // Delete the external file if it can be found.
443
FileObject externalFile = URLMapper.findFileObject(new URL JavaDoc(location, externalName.getValue()));
444             if (externalFile != null) {
445                 externalFile.removeFileChangeListener(fileChangeListener);
446                 externalFile.delete();
447             }
448         }
449         try {
450             deleteWithIndent((TreeChild) el);
451         } catch (ReadOnlyException e) {
452             throw (IOException JavaDoc) new IOException JavaDoc(e.toString()).initCause(e);
453         }
454     }
455     
456     public void rename(String JavaDoc oldName, String JavaDoc newName) throws IOException JavaDoc {
457         TreeElement el = findElement(oldName);
458         if (el == null) {
459             throw new FileNotFoundException JavaDoc(oldName);
460         }
461         int idx = newName.lastIndexOf('/') + 1;
462         if (idx != oldName.lastIndexOf('/') + 1 || !oldName.substring(0, idx).equals(newName.substring(0, idx))) {
463             throw new IOException JavaDoc("Cannot rename to a different dir: " + oldName + " -> " + newName); // NOI18N
464
}
465         String JavaDoc newBaseName = newName.substring(idx);
466         assert newBaseName.indexOf('/') == -1;
467         assert newBaseName.length() > 0;
468         try {
469             el.getAttribute("name").setValue(newBaseName); // NOI18N
470
} catch (ReadOnlyException e) {
471             throw (IOException JavaDoc) new IOException JavaDoc(e.toString()).initCause(e);
472         } catch (InvalidArgumentException e) {
473             assert false : e;
474         }
475     }
476     
477     /*
478     public boolean copy(String name, Transfer target, String targetName) throws IOException {
479         if (! (target instanceof WritableXMLFileSystem)) return false;
480         WritableXMLFileSystem otherfs = (WritableXMLFileSystem) target;
481         Element el = findElement(name);
482         if (el == null) throw new FileNotFoundException(name);
483         Element el2;
484         if (otherfs == this) {
485             el2 = (Element) el.cloneNode(true);
486         } else {
487             el2 = (Element) otherfs.doc.importNode(el, true);
488         }
489         String path, base;
490         int idx = targetName.lastIndexOf('/');
491         if (idx == -1) {
492             path = ""; // NOI18N
493             base = targetName;
494         } else {
495             path = targetName.substring(0, idx);
496             base = targetName.substring(idx + 1);
497         }
498         Element parent = otherfs.findElement(path);
499         if (parent == null) throw new FileNotFoundException(path);
500         el2.setAttribute("name", base); // NOI18N
501         Element old = otherfs.findElement(targetName);
502         if (old != null) {
503             parent.replaceChild(el2, old);
504         } else {
505             appendWithIndent(parent, el2);
506         }
507         return true;
508     }
509     
510     public boolean move(String name, Transfer target, String targetName) throws IOException {
511         if (! (target instanceof WritableXMLFileSystem)) return false;
512         WritableXMLFileSystem otherfs = (WritableXMLFileSystem) target;
513         Element el = findElement(name);
514         if (el == null) throw new FileNotFoundException(name);
515         Element el2;
516         if (otherfs == this) {
517             // Just move it, no need to clone.
518             el2 = el;
519         } else {
520             el2 = (Element) otherfs.doc.importNode(el, true);
521         }
522         String path, base;
523         int idx = targetName.lastIndexOf('/');
524         if (idx == -1) {
525             path = ""; // NOI18N
526             base = targetName;
527         } else {
528             path = targetName.substring(0, idx);
529             base = targetName.substring(idx + 1);
530         }
531         Element parent = otherfs.findElement(path);
532         if (parent == null) throw new FileNotFoundException(path);
533         el2.setAttribute("name", base); // NOI18N
534         Element old = otherfs.findElement(targetName);
535         if (el != el2) {
536             // Cross-document import, so need to remove old one.
537             el.getParentNode().removeChild(el);
538         }
539         if (old != null) {
540             parent.replaceChild(el2, old);
541         } else {
542             appendWithIndent(parent, el2);
543         }
544         return true;
545     }
546      */

547     
548     public Enumeration JavaDoc attributes(String JavaDoc name) {
549         TreeElement el = findElement(name);
550         if (el == null) {
551             return Enumerations.empty();
552         }
553         java.util.List JavaDoc<String JavaDoc> l = new ArrayList JavaDoc(10);
554         Iterator JavaDoc it = el.getChildNodes(TreeElement.class).iterator();
555         while (it.hasNext()) {
556             TreeElement sub = (TreeElement) it.next();
557             if (sub.getLocalName().equals("attr")) { // NOI18N
558
TreeAttribute attr = sub.getAttribute("name"); // NOI18N
559
if (attr == null) {
560                     // Malformed.
561
continue;
562                 }
563                 l.add(attr.getValue());
564             }
565         }
566         return Collections.enumeration(l);
567     }
568     
569     public Object JavaDoc readAttribute(String JavaDoc name, String JavaDoc attrName) {
570         if (attrName.equals("WritableXMLFileSystem.cp")) { // NOI18N
571
// XXX currently unused
572
return classpath;
573         }
574         if (attrName.equals("WritableXMLFileSystem.location")) { // NOI18N
575
// XXX used from PickIconAction; use a constant instead
576
return location;
577         }
578         if (attrName.equals("DataFolder.Index.reorderable")) { // NOI18N
579
return Boolean.TRUE;
580         }
581         TreeElement el = findElement(name);
582         if (el == null) {
583             return null;
584         }
585         boolean literal = false;
586         if (attrName.startsWith("literal:")) { // NOI18N
587
attrName = attrName.substring("literal:".length()); // NOI18N
588
literal = true;
589         }
590         Iterator JavaDoc it = el.getChildNodes(TreeElement.class).iterator();
591         while (it.hasNext()) {
592             TreeElement sub = (TreeElement) it.next();
593             if (!sub.getLocalName().equals("attr")) { // NOI18N
594
continue;
595             }
596             TreeAttribute attr = sub.getAttribute("name"); // NOI18N
597
if (attr == null) {
598                 // Malformed.
599
continue;
600             }
601             if (!attrName.equals(attr.getValue())) {
602                 continue;
603             }
604             try {
605                 if ((attr = sub.getAttribute("stringvalue")) != null) { // NOI18N
606
// Stolen from XMLMapAttr, with tweaks:
607
String JavaDoc inStr = attr.getValue();
608                     StringBuffer JavaDoc outStr = new StringBuffer JavaDoc(inStr.length());
609                     for (int j = 0; j < inStr.length(); j++) {
610                         char ch = inStr.charAt(j);
611                         if (ch == '\\' && inStr.charAt(j + 1) == 'u' && j + 5 < inStr.length()) {
612                             String JavaDoc hex = inStr.substring(j + 2, j + 6);
613                             try {
614                                 outStr.append((char) Integer.parseInt(hex, 16));
615                                 j += 5;
616                             } catch (NumberFormatException JavaDoc e) {
617                                 // OK, just treat as literal text.
618
outStr.append(ch);
619                             }
620                         } else {
621                             outStr.append(ch);
622                         }
623                     }
624                     return outStr.toString();
625                 } else if ((attr = sub.getAttribute("boolvalue")) != null) { // NOI18N
626
return Boolean.valueOf(attr.getValue());
627                 } else if ((attr = sub.getAttribute("urlvalue")) != null) { // NOI18N
628
return new URL JavaDoc(attr.getValue());
629                 } else if ((attr = sub.getAttribute("newvalue")) != null) { // NOI18N
630
String JavaDoc clazz = attr.getValue();
631                     if (literal) {
632                         return "new:" + clazz; // NOI18N
633
} // else XXX
634
} else if ((attr = sub.getAttribute("methodvalue")) != null) { // NOI18N
635
String JavaDoc clazz = attr.getValue();
636                     if (literal) {
637                         return "method:" + clazz; // NOI18N
638
} // else XXX
639
}
640             } catch (Exception JavaDoc e) {
641                 // MalformedURLException, etc.
642
// XXX notify?
643
return null;
644             }
645             // XXX warn that this attr had no recognized *value?
646
}
647         return null;
648         /*
649                         if ((v = sub.getAttributeNode("bytevalue")) != null) { // NOI18N
650                             return new Byte(v.getValue());
651                         } else if ((v = sub.getAttributeNode("shortvalue")) != null) { // NOI18N
652                             return new Short(v.getValue());
653                         } else if ((v = sub.getAttributeNode("intvalue")) != null) { // NOI18N
654                             return new Integer(v.getValue());
655                         } else if ((v = sub.getAttributeNode("longvalue")) != null) { // NOI18N
656                             return new Long(v.getValue());
657                         } else if ((v = sub.getAttributeNode("floatvalue")) != null) { // NOI18N
658                             return new Float(v.getValue());
659                         } else if ((v = sub.getAttributeNode("doublevalue")) != null) { // NOI18N
660                             // When was the last time you set a file attribute to a double?!
661                             // Useless list of primitives...
662                             return new Double(v.getValue());
663                         } else if ((v = sub.getAttributeNode("charvalue")) != null) { // NOI18N
664                             return new Character(v.getValue().charAt(0));
665                         } else if ((v = sub.getAttributeNode("methodvalue")) != null) { // NOI18N
666                             String value = v.getValue();
667                             Object[] params = new Object[] { findResource(name), attrName };
668                             // Stolen from XMLMapAttr:
669                             String className,methodName;
670                             int j = value.lastIndexOf('.');
671                             if (j != -1) {
672          
673                                 methodName = value.substring(j+1);
674          
675                                 className = value.substring(0,j);
676                                 ClassLoader cl = cp.getClassLoader(true);
677                                 Class cls = Class.forName(className, true, cl);
678                                 // Note that unlike XMLMapAttr, we want to use currentClassLoader.
679          
680                                 Object objArray[][] = {null,null,null};
681                                 Method methArray[] = {null,null,null};
682          
683          
684                                 Class fParam = null, sParam = null;
685          
686                                 if (params != null) {
687                                     if (params.length > 0) fParam = params[0].getClass();
688                                     if (params.length > 1) sParam = params[1].getClass();
689                                 }
690          
691                                 Method[] allMethods = cls.getDeclaredMethods();
692                                 Class[] paramClss;
693          
694                                 for (int k=0; k < allMethods.length; k++) {
695          
696                                     if (!allMethods[k].getName().equals(methodName)) continue;
697          
698          
699                                     paramClss = allMethods[k].getParameterTypes();
700          
701                                     if (params == null || params.length == 0 || paramClss.length == 0) {
702                                         if (paramClss.length == 0 && methArray[0] == null && objArray[0] == null) {
703                                             methArray[paramClss.length] = allMethods[k];
704                                             objArray[paramClss.length] = new Object[] {};
705                                             continue;
706                                         }
707          
708                                         continue;
709                                     }
710          
711          
712                                     if (paramClss.length == 2 && params.length >= 2 && methArray[2] == null && objArray[2] == null) {
713                                         if (paramClss[0].isAssignableFrom(fParam) && paramClss[1].isAssignableFrom(sParam)) {
714                                             methArray[paramClss.length] = allMethods[k];
715                                             objArray[paramClss.length] = new Object[] {params[0],params[1]};
716                                             break;
717                                         }
718          
719                                         if (paramClss[0].isAssignableFrom(sParam) && paramClss[1].isAssignableFrom(fParam)) {
720                                             methArray[paramClss.length] = allMethods[k];
721                                             objArray[paramClss.length] = new Object[] {params[1],params[0]};
722                                             break;
723                                         }
724          
725                                         continue;
726                                     }
727          
728                                     if (paramClss.length == 1 && params.length >= 1 && methArray[1] == null && objArray[1] == null) {
729                                         if (paramClss[0].isAssignableFrom(fParam)) {
730                                             methArray[paramClss.length] = allMethods[k];
731                                             objArray[paramClss.length] = new Object[] {params[0]};
732                                             continue;
733                                         }
734          
735                                         if (paramClss[0].isAssignableFrom(sParam)) {
736                                             methArray[paramClss.length] = allMethods[k];
737                                             objArray[paramClss.length] = new Object[] {params[1]};
738                                             continue;
739                                         }
740          
741                                         continue;
742                                     }
743          
744                                 }
745          
746                                 for (int l = methArray.length-1; l >= 0; l-- ) {//clsArray.length
747                                     if (methArray[l] != null && objArray[l] != null) {
748                                         //Method meth = cls.getDeclaredMethod(methodName,clsArray[l]);
749                                         methArray[l].setAccessible(true); //otherwise cannot invoke private
750                                         return methArray[l].invoke(null,objArray[l]);
751                                     }
752                                 }
753                             }
754                             // Some message to logFile
755                             throw new InstantiationException(value);
756                         } else if ((v = sub.getAttributeNode("serialvalue")) != null) { // NOI18N
757                             // Copied from XMLMapAttr:
758                             String value = v.getValue();
759                             if (value.length() == 0) return null;
760          
761                             byte[] bytes = new byte[value.length()/2];
762                             int tempJ;
763                             int count = 0;
764                             for (int j = 0; j < value.length(); j += 2) {
765                                 tempJ = Integer.parseInt(value.substring(j,j+2),16);
766                                 if (tempJ > 127) tempJ -=256;
767                                 bytes[count++] = (byte) tempJ;
768                             }
769          
770                             ByteArrayInputStream bis = new ByteArrayInputStream(bytes, 0, count);
771                             // XXX this should be using a subclass that specifies the right class loader:
772                             ObjectInputStream ois = new NbObjectInputStream(bis);
773                             return ois.readObject();
774                         } else if ((v = sub.getAttributeNode("newvalue")) != null) { // NOI18N
775                             ClassLoader cl = cp.getClassLoader(true);
776                             return Class.forName(v.getValue(), true, cl).newInstance();
777                         }
778          */

779     }
780     
781     private final Set JavaDoc orderAbsorbers = new HashSet JavaDoc(); // Set<String>
782
public void writeAttribute(String JavaDoc name, String JavaDoc attrName, Object JavaDoc v) throws IOException JavaDoc {
783         //System.err.println("wA: " + name + " " + attrName + " " + v/* + " orderAbsorbers=" + orderAbsorbers*/);
784
if (v != null && v.getClass().getName().equals("org.openide.filesystems.MultiFileObject$VoidValue")) { // NOI18N
785
// XXX is this legitimate? Definitely not pretty. But needed for testOpenideFolderOrder to pass.
786
v = null;
787         }
788         if (v == null && attrName.indexOf('/') != -1) {
789             String JavaDoc mebbeOrder = name + '/' + attrName; // NOI18N
790
if (orderAbsorbers.remove(mebbeOrder)) {
791                 //System.err.println(" absorbed");
792
return; // see below
793
}
794             // Was not absorbed; we really needed to cancel this ordering.
795
v = Boolean.FALSE;
796         }
797         if (name.indexOf('/') == -1) {
798             // Cancel this hack - something else has happened.
799
Iterator JavaDoc it = orderAbsorbers.iterator();
800             while (it.hasNext()) {
801                 String JavaDoc n = (String JavaDoc) it.next();
802                 if (n.startsWith(name + '/')) {
803                     it.remove();
804                 }
805             }
806         }
807         if (attrName.equals("OpenIDE-Folder-Order") && (v instanceof String JavaDoc)) { // NOI18N
808
// This is a special case. We do not want to store a fully fixed order in a layer.
809
// Rather, compute some reasonable orderings from it.
810
java.util.List JavaDoc<String JavaDoc> order = Collections.list(NbCollections.checkedEnumerationByFilter(new StringTokenizer JavaDoc((String JavaDoc) v, "/"), String JavaDoc.class, true)); // NOI18N
811
if (!order.isEmpty()) {
812                 // XXX this is not really right. We just cannot know which attrs were already defined
813
// in the merged FS from other layers. So this will fail to store needed attrs sometimes.
814
// But better that than storing too many.
815
Set JavaDoc<String JavaDoc> myOwn = new HashSet JavaDoc(Arrays.asList(children(name)));
816                 Iterator JavaDoc it = order.iterator();
817                 String JavaDoc prev = (String JavaDoc) it.next();
818                 while (it.hasNext()) {
819                     String JavaDoc next = (String JavaDoc) it.next();
820                     if (myOwn.contains(prev) || myOwn.contains(next)) {
821                         writeAttribute(name, prev + '/' + next, Boolean.TRUE); // NOI18N
822
}
823                     prev = next;
824                 }
825             }
826             // FolderOrder.write tries to cancel old orders immediately after writing!
827
// Don't let it. We don't know exactly what they might be, but we can guess.
828
for (int i = 0; i < order.size() - 1; i++) {
829                 for (int j = i + 1; j < order.size(); j++) {
830                     orderAbsorbers.add(name + '/' + (String JavaDoc) order.get(i) + '/' + (String JavaDoc) order.get(j));
831                 }
832             }
833             //System.err.println("orderAbsorbers=" + orderAbsorbers);
834
return;
835         }
836         TreeElement el = findElement(name);
837         if (el == null) {
838             throw new FileNotFoundException JavaDoc(name);
839         }
840         // Find any existing <attr>.
841
TreeChild existingAttr = null;
842         Iterator JavaDoc it = el.getChildNodes(TreeElement.class).iterator();
843         while (it.hasNext()) {
844             TreeElement sub = (TreeElement) it.next();
845             if (sub.getLocalName().equals("attr")) { // NOI18N
846
TreeAttribute attr = sub.getAttribute("name"); // NOI18N
847
if (attr == null) {
848                     // Malformed.
849
continue;
850                 }
851                 if (attr.getValue().equals(attrName)) {
852                     existingAttr = sub;
853                     break;
854                 }
855             }
856         }
857         TreeElement attr;
858         try {
859             attr = new TreeElement("attr", true); // NOI18N
860
attr.addAttribute("name", attrName); // NOI18N
861
if (v instanceof String JavaDoc) {
862                 String JavaDoc inStr = (String JavaDoc) v;
863                 String JavaDoc newValueMagic = "newvalue:"; // NOI18N
864
String JavaDoc methodValueMagic = "methodvalue:"; // NOI18N
865
if (inStr.startsWith(newValueMagic)) {
866                     // Impossible to set this (reliably) as a real value, so use this magic technique instead:
867
attr.addAttribute("newvalue", inStr.substring(newValueMagic.length())); // NOI18N
868
} else if (inStr.startsWith(methodValueMagic)) {
869                     // Same here:
870
attr.addAttribute("methodvalue", inStr.substring(methodValueMagic.length())); // NOI18N
871
} else {
872                     // Regular string value.
873
// Stolen from XMLMapAttr w/ mods:
874
StringBuffer JavaDoc outStr = new StringBuffer JavaDoc();
875                     for (int i = 0; i < inStr.length(); i++) {
876                         char c = inStr.charAt(i);
877                         if (Character.isISOControl(c) || c == '&' || c == '<' || c == '>' || c == '"' || c == '\'') {
878                             outStr.append(encodeChar(c));
879                         } else {
880                             outStr.append(c);
881                         }
882                     }
883                     attr.addAttribute("stringvalue", outStr.toString()); // NOI18N
884
}
885             } else if (v instanceof URL JavaDoc) {
886                 attr.addAttribute("urlvalue", ((URL JavaDoc) v).toExternalForm()); // NOI18N
887
} else if (v instanceof Boolean JavaDoc) {
888                 attr.addAttribute("boolvalue", v.toString()); // NOI18N
889
} else if (v instanceof Character JavaDoc) {
890                 attr.addAttribute("charvalue", v.toString()); // NOI18N
891
} else if (v instanceof Integer JavaDoc) {
892                 attr.addAttribute("intvalue", v.toString()); // NOI18N
893
} else if (v != null) {
894                 throw new UnsupportedOperationException JavaDoc("XXX: " + v); // NOI18N
895
}
896             if (v != null && existingAttr == null) {
897                 appendWithIndent(el, attr);
898             } else if (v != null) {
899                 ((TreeParentNode) el).replaceChild(existingAttr, attr);
900             } else if (existingAttr != null) {
901                 deleteWithIndent(existingAttr);
902             }
903         } catch (InvalidArgumentException e) {
904             throw new AssertionError JavaDoc(e);
905         } catch (ReadOnlyException e) {
906             throw (IOException JavaDoc) new IOException JavaDoc(e.toString()).initCause(e);
907         }
908         /*
909         if (v instanceof Byte) {
910             attr.setAttribute("bytevalue", v.toString()); // NOI18N
911         } else if (v instanceof Short) {
912             attr.setAttribute("shortvalue", v.toString()); // NOI18N
913         } else if (v instanceof Integer) {
914             attr.setAttribute("intvalue", v.toString()); // NOI18N
915         } else if (v instanceof Long) {
916             attr.setAttribute("longvalue", v.toString()); // NOI18N
917         } else if (v instanceof Float) {
918             attr.setAttribute("floatvalue", v.toString()); // NOI18N
919         } else if (v instanceof Double) {
920             attr.setAttribute("doublevalue", v.toString()); // NOI18N
921         } else if (v instanceof Character) {
922             attr.setAttribute("charvalue", v.toString()); // NOI18N
923         } else {
924             // Stolen from XMLMapAttr, mostly.
925             ByteArrayOutputStream bos = new ByteArrayOutputStream();
926             ObjectOutputStream oos = new ObjectOutputStream(bos);
927             oos.writeObject(v);
928             oos.close();
929             byte bArray[] = bos.toByteArray();
930             // Check to see if this is the same as a default instance.
931             Class clazz = v.getClass();
932             boolean usenewinstance = false;
933             try {
934                 Object v2 = clazz.newInstance();
935                 bos = new ByteArrayOutputStream();
936                 oos = new ObjectOutputStream(bos);
937                 oos.writeObject(v2);
938                 oos.close();
939                 byte[] bArray2 = bos.toByteArray();
940                 usenewinstance = Utilities.compareObjects(bArray, bArray2);
941             } catch (Exception e) {
942                 // quite expectable - ignore
943             }
944             if (usenewinstance) {
945                 attr.setAttribute("newvalue", clazz.getName()); // NOI18N
946             } else {
947                 StringBuffer strBuff = new StringBuffer(bArray.length*2);
948                 for(int i = 0; i < bArray.length;i++) {
949                     if (bArray[i] < 16 && bArray[i] >= 0) strBuff.append("0");// NOI18N
950                     strBuff.append(Integer.toHexString(bArray[i] < 0?bArray[i]+256:bArray[i]));
951                 }
952                 attr.setAttribute("serialvalue", strBuff.toString()); // NOI18N
953                 // Also mention what the original value was, for reference.
954                 // Do it after adding element since otherwise we cannot indent correctly.
955                 String asString;
956                 if (clazz.isArray()) {
957                     // Default toString sucks for arrays. Pretty common so worth special-casing.
958                     asString = Arrays.asList((Object[]) v).toString();
959                 } else {
960                     asString = v.toString();
961                 }
962                 serialComment = " (" + attrName + "=" + asString + ") ";
963             }
964         }
965         if (adding) {
966             // XXX if an ordering attr, try to put it next to file; else for folders, put it before any file
967             appendWithIndent(el, attr);
968         }
969         // Deal with serial comments now.
970         Comment comment = findSerialComment(el, attrName);
971         if (serialComment != null) {
972             if (comment != null) {
973                 comment.setData(serialComment);
974             } else {
975                 appendWithIndent(el, doc.createComment(serialComment));
976             }
977         } else if (comment != null) {
978             // Changed from some serialvalue to simple value; kill comment.
979             el.removeChild(comment);
980         }
981         if (attrName.startsWith("SystemFileSystem")) { // NOI18N
982             fireFileStatusChanged(new FileStatusEvent(this, findResource(name), true, true));
983         }
984     }
985     private Comment findSerialComment(Element el, String attrName) {
986         NodeList nl = el.getChildNodes();
987         for (int i = 0; i < nl.getLength(); i++) {
988             if (nl.item(i).getNodeType() == Node.COMMENT_NODE) {
989                 String comm = nl.item(i).getNodeValue();
990                 if (comm.startsWith(" (" + attrName + "=") && comm.endsWith(") ")) {
991                     return (Comment) nl.item(i);
992                 }
993             }
994         }
995         return null;
996          */

997     }
998     
999     /** stolen from XMLMapAttr */
1000    private static String JavaDoc encodeChar(char ch) {
1001        String JavaDoc encChar = Integer.toString((int) ch, 16);
1002        return "\\u" + "0000".substring(0, "0000".length() - encChar.length()).concat(encChar); // NOI18N
1003
}
1004    
1005    public void renameAttributes(String JavaDoc oldName, String JavaDoc newName) {
1006        // do nothing
1007
}
1008    
1009    public void deleteAttributes(String JavaDoc name) {
1010        // do nothing
1011
}
1012    
1013    public boolean readOnly(String JavaDoc name) {
1014        return false;
1015    }
1016    
1017    public String JavaDoc mimeType(String JavaDoc name) {
1018        return null; // i.e. use default resolvers
1019
}
1020    
1021    public long size(String JavaDoc name) {
1022        try {
1023            return getContentsOf(name).length;
1024        } catch (FileNotFoundException JavaDoc fnfe) {
1025            return 0;
1026        }
1027    }
1028    
1029    public void markUnimportant(String JavaDoc name) {
1030        // do nothing
1031
}
1032    
1033    public Date JavaDoc lastModified(String JavaDoc name) {
1034        final TreeElement el = findElement(name);
1035        if (el == null) {
1036            return new Date JavaDoc(0L);
1037        }
1038        TreeAttribute attr = el.getAttribute("url"); // NOI18N
1039
if (attr == null) {
1040            return new Date JavaDoc(0L);
1041        }
1042        String JavaDoc u = attr.getValue();
1043        if (URI.create(u).isAbsolute()) {
1044            return new Date JavaDoc(0L);
1045        }
1046        FileObject external;
1047        try {
1048            external = URLMapper.findFileObject(new URL JavaDoc(location, u));
1049        } catch (MalformedURLException JavaDoc e) {
1050            assert false : e;
1051            return new Date JavaDoc(0L);
1052        }
1053        if (external == null) {
1054            return new Date JavaDoc(0L);
1055        }
1056        return external.lastModified();
1057    }
1058    
1059    // These are not important for us:
1060

1061    public void lock(String JavaDoc name) throws IOException JavaDoc {
1062        // [PENDING] should this try to lock the XML document??
1063
// (not clear if it is safe to do so from FS call, even tho
1064
// on a different FS)
1065
}
1066    
1067    public void unlock(String JavaDoc name) {
1068        // do nothing
1069
}
1070    
1071    // don't bother making configurable; or could use an indentation engine
1072
private static final int INDENT_STEP = 4;
1073    /**
1074     * Add a new element to a parent in the correct position.
1075     * Inserts whitespace to retain indentation rules.
1076     */

1077    private static void appendWithIndent(TreeElement parent, TreeChild child) throws ReadOnlyException {
1078        TreeParentNode doc = parent;
1079        int depth = -2; // will get <filesystem> then TreeDocument then null
1080
while (doc != null) {
1081            doc = ((TreeChild) doc).getParentNode();
1082            depth++;
1083        }
1084        TreeChild position = insertBefore(parent, child);
1085        try {
1086            if (position != null) {
1087                parent.insertBefore(child, position);
1088                parent.insertBefore(new TreeText("\n" + spaces((depth + 1) * INDENT_STEP)), position); // NOI18N
1089
} else {
1090                if (/*XXX this is clumsy*/ parent.hasChildNodes()) {
1091                    parent.appendChild(new TreeText(spaces(INDENT_STEP)));
1092                } else {
1093                    parent.appendChild(new TreeText("\n" + spaces((depth + 1) * INDENT_STEP))); // NOI18N
1094
}
1095                parent.appendChild(child);
1096                parent.appendChild(new TreeText("\n" + spaces(depth * INDENT_STEP))); // NOI18N
1097
}
1098            parent.normalize();
1099            TreeElement childe = (TreeElement) child;
1100            if (childe.getQName().equals("attr") && childe.getAttribute("name").getValue().indexOf('/') != -1) { // NOI18N
1101
// Check for ordering attributes, which we have to handle specially.
1102
resort(parent);
1103            }
1104        } catch (InvalidArgumentException e) {
1105            assert false : e;
1106        }
1107    }
1108    /**
1109     * Find the proper location for a newly inserted element.
1110     * Rules:
1111     * 1. <file> or <folder> must be added in alphabetical order w.r.t. existing ones.
1112     * 2. <attr> w/o '/' in name must be added to top of element in alpha order w.r.t. existing ones.
1113     * 3. <attr> w/ '/' should be added wherever - will rearrange everything later...
1114     * Returns a position to insert before (null for end).
1115     */

1116    private static TreeChild insertBefore(TreeElement parent, TreeChild child) throws ReadOnlyException {
1117        if (!(child instanceof TreeElement)) {
1118            return null; // TBD for now
1119
}
1120        TreeElement childe = (TreeElement) child;
1121        if (childe.getQName().equals("file") || childe.getQName().equals("folder")) { // NOI18N
1122
String JavaDoc name = childe.getAttribute("name").getValue(); // NOI18N
1123
Iterator JavaDoc it = parent.getChildNodes(TreeElement.class).iterator();
1124            while (it.hasNext()) {
1125                TreeElement kid = (TreeElement) it.next();
1126                if (kid.getQName().equals("file") || kid.getQName().equals("folder")) { // NOI18N
1127
TreeAttribute attr = kid.getAttribute("name"); // NOI18N
1128
if (attr != null) { // #66816 - layer might be malformed
1129
String JavaDoc kidname = attr.getValue();
1130                        if (kidname.compareTo(name) > 0) {
1131                            return kid;
1132                        }
1133                    }
1134                }
1135            }
1136            return null;
1137        } else if (childe.getQName().equals("attr")) { // NOI18N
1138
String JavaDoc name = childe.getAttribute("name").getValue(); // NOI18N
1139
int slash = name.indexOf('/');
1140            if (slash == -1) {
1141                // Regular attribute.
1142
Iterator JavaDoc it = parent.getChildNodes(TreeElement.class).iterator();
1143                while (it.hasNext()) {
1144                    TreeElement kid = (TreeElement) it.next();
1145                    if (kid.getQName().equals("file") || kid.getQName().equals("folder")) { // NOI18N
1146
return kid;
1147                    } else if (kid.getQName().equals("attr")) { // NOI18N
1148
TreeAttribute attr = kid.getAttribute("name"); // NOI18N
1149
if (attr != null) {
1150                            String JavaDoc kidname = attr.getValue();
1151                            if (kidname.compareTo(name) > 0) {
1152                                return kid;
1153                            }
1154                        }
1155                    } else {
1156                        throw new AssertionError JavaDoc("Weird child: " + kid.getQName());
1157                    }
1158                }
1159                return null;
1160            } else {
1161                // Ordering attribute. Will be ordered later, so skip it now.
1162
return null;
1163                /*
1164                String former = name.substring(0, slash);
1165                String latter = name.substring(slash + 1);
1166                TreeElement formerMatch = findElementIn(parent, former);
1167                TreeElement latterMatch = findElementIn(parent, latter);
1168                if (formerMatch == null) {
1169                    if (latterMatch == null) {
1170                        // OK, just stick it somewhere.
1171                        return positionForRegularAttr(parent, name);
1172                    } else {
1173                        return latterMatch;
1174                    }
1175                } else { // formerMatch != null
1176                    if (latterMatch == null) {
1177                        return null; // XXX OK?
1178                    } else {
1179                        int formerIdx = parent.indexOf(formerMatch);
1180                        int latterIdx = parent.indexOf(latterMatch);
1181                        if (formerIdx > latterIdx) {
1182                            // Out of order. Swap them.
1183                            // XXX does this need to be smarter?
1184                            parent.removeChild(formerMatch);
1185                            parent.removeChild(latterMatch);
1186                            parent.insertChildAt(formerMatch, latterIdx);
1187                            parent.insertChildAt(latterMatch, formerIdx);
1188                        }
1189                        return latterMatch;
1190                    }
1191                }
1192                 */

1193            }
1194        } else {
1195            throw new AssertionError JavaDoc("Weird child: " + childe.getQName());
1196        }
1197    }
1198    /**
1199     * Resort all files and folders and attributes in a folder context. The order is:
1200     * 1. Files and folders are alpha-sorted if they have no relative attrs.
1201     * 2. But existing relative attrs change that order. Unordered files/folders go at the end.
1202     * 3. Most attributes are alpha-sorted at the top.
1203     * 4. Relative attrs are sorted between files or folders they order, if possible.
1204     * 5. Relative attrs with one missing referent are ordered right after the existing referent.
1205     * 6. Relative attrs with both missing referents are ordered after other attrs.
1206     */

1207    private static void resort(TreeElement parent) throws ReadOnlyException {
1208        class Item {
1209            public TreeElement child;
1210            boolean isAttr() {
1211                return child.getQName().equals("attr"); // NOI18N
1212
}
1213            String JavaDoc getName() {
1214                TreeAttribute attr = child.getAttribute("name"); // NOI18N
1215
return attr != null ? attr.getValue() : "";
1216            }
1217            boolean isOrderingAttr() {
1218                return isAttr() && getName().indexOf('/') != -1;
1219            }
1220            String JavaDoc getFormer() {
1221                String JavaDoc n = getName();
1222                return n.substring(0, n.indexOf('/'));
1223            }
1224            String JavaDoc getLatter() {
1225                String JavaDoc n = getName();
1226                return n.substring(n.indexOf('/') + 1);
1227            }
1228        }
1229        Set JavaDoc<Item> items = new LinkedHashSet JavaDoc();
1230        SortedSet JavaDoc<Integer JavaDoc> indices = new TreeSet JavaDoc();
1231        for (int i = 0; i < parent.getChildrenNumber(); i++) {
1232            TreeChild child = (TreeChild) parent.getChildNodes().get(i);
1233            if (child instanceof TreeElement) {
1234                Item item = new Item();
1235                item.child = (TreeElement) child;
1236                items.add(item);
1237                indices.add(new Integer JavaDoc(i));
1238            }
1239        }
1240        Map JavaDoc<Item,Collection JavaDoc<Item>> edges = new LinkedHashMap JavaDoc();
1241        Map JavaDoc<String JavaDoc,Item> filesAndFolders = new LinkedHashMap JavaDoc();
1242        Map JavaDoc<String JavaDoc,Item> attrs = new LinkedHashMap JavaDoc();
1243        Set JavaDoc<String JavaDoc> orderedFilesAndFolders = new LinkedHashSet JavaDoc();
1244        Iterator JavaDoc it = items.iterator();
1245        while (it.hasNext()) {
1246            Item item = (Item) it.next();
1247            String JavaDoc name = item.getName();
1248            if (item.isAttr()) {
1249                attrs.put(name, item);
1250                if (item.isOrderingAttr()) {
1251                    orderedFilesAndFolders.add(item.getFormer());
1252                    orderedFilesAndFolders.add(item.getLatter());
1253                }
1254            } else {
1255                filesAndFolders.put(name, item);
1256            }
1257        }
1258        class NameComparator implements Comparator JavaDoc {
1259            public int compare(Object JavaDoc o1, Object JavaDoc o2) {
1260                Item i1 = (Item) o1;
1261                Item i2 = (Item) o2;
1262                return i1.getName().compareTo(i2.getName());
1263            }
1264        }
1265        Set JavaDoc<Item> sortedAttrs = new TreeSet JavaDoc(new NameComparator());
1266        Set JavaDoc<Item> sortedFilesAndFolders = new TreeSet JavaDoc(new NameComparator());
1267        Set JavaDoc<Item> orderableItems = new LinkedHashSet JavaDoc();
1268        it = items.iterator();
1269        while (it.hasNext()) {
1270            Item item = (Item) it.next();
1271            String JavaDoc name = item.getName();
1272            if (item.isAttr()) {
1273                if (item.isOrderingAttr()) {
1274                    Item former = (Item) filesAndFolders.get(item.getFormer());
1275                    if (former != null) {
1276                        Set JavaDoc<Item> formerConstraints = (Set JavaDoc) edges.get(former);
1277                        if (formerConstraints == null) {
1278                            formerConstraints = new LinkedHashSet JavaDoc();
1279                            edges.put(former, formerConstraints);
1280                        }
1281                        formerConstraints.add(item);
1282                    }
1283                    Item latter = (Item) filesAndFolders.get(item.getLatter());
1284                    if (latter != null) {
1285                        Set JavaDoc<Item> constraints = new LinkedHashSet JavaDoc();
1286                        constraints.add(latter);
1287                        edges.put(item, constraints);
1288                    }
1289                    orderableItems.add(item);
1290                } else {
1291                    sortedAttrs.add(item);
1292                }
1293            } else {
1294                if (orderedFilesAndFolders.contains(name)) {
1295                    orderableItems.add(item);
1296                } else {
1297                    sortedFilesAndFolders.add(item);
1298                }
1299            }
1300        }
1301        java.util.List JavaDoc<Item> orderedItems;
1302        try {
1303            orderedItems = Utilities.topologicalSort(orderableItems, edges);
1304        } catch (TopologicalSortException e) {
1305            // OK, ignore.
1306
return;
1307        }
1308        it = items.iterator();
1309        while (it.hasNext()) {
1310            Item item = (Item) it.next();
1311            parent.removeChild(item.child);
1312        }
1313        java.util.List JavaDoc<Item> allOrderedItems = new ArrayList JavaDoc(sortedAttrs);
1314        allOrderedItems.addAll(orderedItems);
1315        allOrderedItems.addAll(sortedFilesAndFolders);
1316        assert new HashSet JavaDoc(allOrderedItems).equals(items);
1317        it = allOrderedItems.iterator();
1318        Iterator JavaDoc indexIt = indices.iterator();
1319        while (it.hasNext()) {
1320            Item item = (Item) it.next();
1321            int index = ((Integer JavaDoc) indexIt.next()).intValue();
1322            parent.insertChildAt(item.child, index);
1323        }
1324    }
1325    private static String JavaDoc spaces(int size) {
1326        char[] chars = new char[size];
1327        for (int i = 0; i < size; i++) {
1328            chars[i] = ' ';
1329        }
1330        return new String JavaDoc(chars);
1331    }
1332    /**
1333     * Remove an element (and its children), deleting any surrounding newlines and indentation too.
1334     */

1335    private static void deleteWithIndent(TreeChild child) throws ReadOnlyException {
1336        TreeChild next = child.getNextSibling();
1337        // XXX better might be to delete any maximal [ \t]+ previous plus \n next (means splitting up TreeText's)
1338
if (next instanceof TreeText && ((TreeText) next).getData().matches("(\r|\n|\r\n)[ \t]+")) { // NOI18N
1339
next.removeFromContext();
1340        } else {
1341            TreeChild previous = child.getPreviousSibling();
1342            if (previous instanceof TreeText && ((TreeText) previous).getData().matches("(\r|\n|\r\n)[ \t]+")) { // NOI18N
1343
previous.removeFromContext();
1344            } else {
1345                // Well, not sure what is here, so skip it.
1346
}
1347        }
1348        TreeElement parent = (TreeElement) child.getParentNode();
1349        TreeObjectList list = parent.getChildNodes();
1350        boolean kill = true;
1351        Iterator JavaDoc it = list.iterator();
1352        while (it.hasNext()) {
1353            Object JavaDoc o = it.next();
1354            if (o == child) {
1355                continue;
1356            }
1357            if (!(o instanceof TreeText)) {
1358                kill = false;
1359                break;
1360            }
1361            if (((TreeText) o).getData().trim().length() > 0) {
1362                kill = false;
1363                break;
1364            }
1365        }
1366        if (kill) {
1367            try {
1368                // Special case for root of filesystem.
1369
if (((TreeChild) parent).getParentNode() instanceof TreeDocumentRoot) {
1370                    it = list.iterator();
1371                    while (it.hasNext()) {
1372                        ((TreeChild) it.next()).removeFromContext();
1373                    }
1374                    parent.appendChild(new TreeText("\n")); // NOI18N
1375
} else {
1376                    // Make sure we convert it to an empty tag (seems to only affect elements
1377
// which were originally parsed?):
1378
TreeElement parent2 = new TreeElement(parent.getQName(), true);
1379                    TreeAttribute attr = parent.getAttribute("name"); // NOI18N
1380
if (attr != null) {
1381                        parent2.addAttribute("name", attr.getValue()); // NOI18N
1382
}
1383                    TreeParentNode grandparent = ((TreeChild) parent).getParentNode();
1384                    // TreeElement.empty is sticky - cannot be changed retroactively (argh!).
1385
grandparent.replaceChild(parent, parent2);
1386                    parent = parent2; // for normalize() below
1387
}
1388            } catch (InvalidArgumentException e) {
1389                assert false : e;
1390            }
1391        }
1392        child.removeFromContext();
1393        parent.normalize();
1394    }
1395    
1396    // Listen to changes in files used as url= external contents. If these change,
1397
// the filesystem needs to show something else. Properly we would
1398
// keep track of *which* file changed and thus which of our resources
1399
// is affected. Practically this would be a lot of work and gain
1400
// very little.
1401
public void fileDeleted(FileEvent fe) {
1402        someFileChange();
1403    }
1404    public void fileFolderCreated(FileEvent fe) {
1405        // does not apply to us
1406
}
1407    public void fileDataCreated(FileEvent fe) {
1408        // not interesting here
1409
}
1410    public void fileAttributeChanged(FileAttributeEvent fe) {
1411        // don't care about attributes on included files...
1412
}
1413    public void fileRenamed(FileRenameEvent fe) {
1414        someFileChange();
1415    }
1416    public void fileChanged(FileEvent fe) {
1417        someFileChange();
1418    }
1419    private void someFileChange() {
1420        // If used as url=, refresh contents, timestamp, ...
1421
refreshResource("", true);
1422    }
1423    
1424    public void propertyChange(PropertyChangeEvent JavaDoc evt) {
1425        if (!evt.getPropertyName().equals(TreeEditorCookie.PROP_DOCUMENT_ROOT)) {
1426            return;
1427        }
1428        if (cookie.getStatus() == TreeEditorCookie.STATUS_OK || cookie.getStatus() == TreeEditorCookie.STATUS_NOT) {
1429            // Document was modified, and reparsed OK. See what changed.
1430
try {
1431                doc = cookie.openDocumentRoot();
1432                /* Neither of the following work:
1433                refreshResource("", true); // only works on root folder
1434                refreshRoot(); // seems to do nothing at all
1435                 */

1436                Enumeration JavaDoc<? extends FileObject> e = existingFileObjects(getRoot());
1437                while (e.hasMoreElements()) {
1438                    FileObject fo = (FileObject) e.nextElement();
1439                    // fo.refresh() does not work
1440
refreshResource(fo.getPath(), true);
1441                }
1442                //System.err.println("got changes; new files: " + Collections.list(getRoot().getChildren(true)));
1443
//Thread.dumpStack();
1444
} catch (TreeException e) {
1445                Util.err.notify(ErrorManager.INFORMATIONAL, e);
1446            } catch (IOException JavaDoc e) {
1447                Util.err.notify(ErrorManager.INFORMATIONAL, e);
1448            }
1449        }
1450    }
1451    
1452}
1453
Popular Tags