2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
package org.openide.filesystems;
44
import java.io.BufferedInputStream;
45
import java.io.BufferedOutputStream;
46
import java.io.Externalizable;
48
import java.io.FileNotFoundException;
49
import java.io.IOException;
50
import java.io.InputStream;
51
import java.io.ObjectInput;
52
import java.io.ObjectInputStream;
53
import java.io.ObjectOutput;
54
import java.io.OutputStream;
55
import java.io.OutputStreamWriter;
56
import java.io.PrintWriter;
57
import java.io.PushbackInputStream;
58
import java.lang.ref.Reference;
59
import java.lang.ref.SoftReference;
60
import java.util.Collections;
61
import java.util.Enumeration;
62
import java.util.HashMap;
63
import java.util.HashSet;
64
import java.util.Iterator;
65
import java.util.Locale;
67
import java.util.TreeSet;
68
import javax.xml.parsers.FactoryConfigurationError;
69
import javax.xml.parsers.ParserConfigurationException;
70
import org.openide.util.Enumerations;
71
import org.openide.util.NbBundle;
72
import org.openide.util.Utilities;
73
import org.openide.util.io.NbMarshalledObject;
74
import org.openide.xml.XMLUtil;
75
import org.xml.sax.Attributes;
76
import org.xml.sax.InputSource;
77
import org.xml.sax.SAXException;
78
import org.xml.sax.SAXParseException;
79
import org.xml.sax.XMLReader;
80
import org.xml.sax.helpers.DefaultHandler;
82
/** Implementation of <code>AbstractFileSystem.Attr</code> using a special file
83
* in each folder for holding attributes.
85
* the file from the rest of system, so it also implements
86
* <code>AbstractFileSystem.List</code> to exclude the file from the children list
87
* (it can then serve to filter a plain list implementation).
89
*Description of format of special file ilustrates best DTD file that is showed in next lines:
90
*<!ELEMENT attributes (fileobject)*>
91
* <!ATTLIST attributes version CDATA #REQUIRED>
92
* <!ELEMENT fileobject (attr)*>
93
* <!ATTLIST fileobject name CDATA #REQUIRED>
94
* <!ELEMENT attr EMPTY>
95
* <!ATTLIST attr name CDATA #REQUIRED>
96
* <!ATTLIST attr bytevalue CDATA #IMPLIED>
97
* <!ATTLIST attr shortvalue CDATA #IMPLIED>
98
* <!ATTLIST attr intvalue CDATA #IMPLIED>
99
* <!ATTLIST attr longvalue CDATA #IMPLIED>
100
* <!ATTLIST attr floatvalue CDATA #IMPLIED>
101
* <!ATTLIST attr doublevalue CDATA #IMPLIED>
102
* <!ATTLIST attr boolvalue CDATA #IMPLIED>
103
* <!ATTLIST attr charvalue CDATA #IMPLIED>
104
* <!ATTLIST attr stringvalue CDATA #IMPLIED>
105
* <!ATTLIST attr methodvalue CDATA #IMPLIED>
106
* <!ATTLIST attr serialvalue CDATA #IMPLIED>
107
* <!ATTLIST attr urlvalue CDATA #IMPLIED>
109
* @author Jaroslav Tulach
111
@SuppressWarnings("unchecked")
112
public class DefaultAttributes extends Object implements AbstractFileSystem.Attr, AbstractFileSystem.List {
113
static final long serialVersionUID = -5801291358293736478L;
115
/** File name of special file in each folder where attributes are saved.
116
* @deprecated does not handle XML attributes
119
public final static String ATTR_NAME = "filesystem"; // NOI18N
121
/** Extension of special file in each folder where attributes are saved.
122
* @deprecated does not handle XML attributes
125
public final static String ATTR_EXT = "attributes"; // NOI18N
127
/** Name with extension of special file in each folder where attributes are saved.
128
* @deprecated does not handle XML attributes
131
public final static String ATTR_NAME_EXT = ATTR_NAME + '.' + ATTR_EXT;
132
private final static String ATTR_NAME_EXT_XML = System.getProperty(
133
"org.openide.filesystems.DefaultAttributes.ATTR_NAME_EXT_XML", ".nbattrs"
136
/** readOnlyAttrs is name of virtual attribute. This name of virtual attribute
137
* is shared between classes (and cannot be changed without breaking compatibility):
138
* - org.openide.filesystems.DefaultAttributes
139
* - org.openide.loaders.ExecutionSupport
140
* - org.openide.loaders.CompilerSupport
141
* - org.netbeans.core.ExJarFileSystem
143
private final static String READONLY_ATTRIBUTES = "readOnlyAttrs"; //NOI18N
145
// <?xml version="1.0"?>
146
// <!DOCTYPE filesystem PUBLIC "-//NetBeans//DTD DefaultAttributes 1.0//EN" "http://www.netbeans.org/dtds/attributes-1_0.dtd">
147
// <attributes>...</attributes>
148
private static final String PUBLIC_ID = "-//NetBeans//DTD DefaultAttributes 1.0//EN"; // NOI18N
149
private static final String DTD_PATH = "org/openide/filesystems/attributes.dtd"; // NOI18N
151
/** description of the fs to work on - info about files */
152
private AbstractFileSystem.Info info;
154
/** description of the fs to work on - work with files */
155
private AbstractFileSystem.Change change;
157
/** description of the fs to work on - listing of files */
158
private AbstractFileSystem.List list;
160
/** file name of attributes (default value corresponds to ATTR_NAME_EXT_XML) */
161
private String fileName;
163
/** Cache of attributes.
164
* For name of folder gives map of maps of attibutes
165
* (String, Reference (Table))
167
private transient Map cache;
170
* @param info file object information to use
171
* @param change file change hooks to use
172
* @param list list to filter (can be <code>null</code>, but then this object cannot work as a list)
174
public DefaultAttributes(
175
AbstractFileSystem.Info info, AbstractFileSystem.Change change, AbstractFileSystem.List list
178
this.change = change;
180
fileName = ATTR_NAME_EXT_XML;
185
* @param info file object information to use
186
* @param change file change hooks to use
187
* @param list list to filter (can be <code>null</code>, but then this object cannot work as a list)
191
protected DefaultAttributes(
192
AbstractFileSystem.Info info, AbstractFileSystem.Change change, AbstractFileSystem.List list, String fileName
194
this(info, change, list);
195
this.fileName = fileName;
198
/** Methods to ensure backward compatibility for storing and
201
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
202
ObjectInputStream.GetField fields = ois.readFields();
204
Object o1 = AbstractFileSystem.readImpl("change", fields); // NOI18N
205
Object o2 = AbstractFileSystem.readImpl("info", fields); // NOI18N
206
Object o3 = AbstractFileSystem.readImpl("list", fields); // NOI18N
208
change = (AbstractFileSystem.Change) o1;
209
info = (AbstractFileSystem.Info) o2;
210
list = (AbstractFileSystem.List) o3;
213
/** Get the children list, filtering out the special attributes file.
214
* You <em>must</em> have provided a non-<code>null</code> {@link AbstractFileSystem.List}
215
* in the constructor for this to work. If you did not, the rest of the class will work
216
* fine, but this method should not be called and this object should not be used
217
* as a <code>List</code> implementation.
219
* @param f the folder, by name; e.g. <code>top/next/afterthat</code>
220
* @return a list of children of the folder, as <code>file.ext</code> (no path)
222
public String[] children(String f) {
223
String[] arr = list.children(f);
230
int size = arr.length;
233
// In NB 3.2.x for OpenVMS, we had to use "_nbattrs." as a attribute file.
234
// However, OpenVMS now supports a file name beginning with "."
235
// So we now have to copy the existing "_nbattrs." file into ".nbattrs"
237
if ((Utilities.getOperatingSystem() == Utilities.OS_VMS) && (arr[0] != null) && (f != null)) {
238
if (arr[0].equalsIgnoreCase("_nbattrs.")) {
240
deleteFile(f + "/" + arr[0]); // NOI18N
241
} catch (IOException ioe) {
244
arr[0] = getFileName();
248
if ((getFileName().equals(arr[0]) || ATTR_NAME_EXT_XML.equals(arr[0]) || ATTR_NAME_EXT.equals(arr[0]))) {
250
this.change.delete(f + "/" + arr[0]);
251
} catch (IOException iox) {
254
return new String[] { };
258
for (int i = 0; i < size; i++) {
259
// In NB 3.2.x for OpenVMS, we had to use "_nbattrs." as a attribute file.
260
// However, OpenVMS now supports a file name beginning with "."
261
// So we now have to copy the existing "_nbattrs." file into ".nbattrs"
263
if ((Utilities.getOperatingSystem() == Utilities.OS_VMS) && (arr[i] != null) && (f != null)) {
264
if (arr[i].equalsIgnoreCase("_nbattrs.")) {
266
File fp = new File(f + "/" + ".nbattrs");
272
} catch (IOException ioe) {
275
arr[i] = getFileName();
279
String safeNbAttrsCopy = getFileName() + "~"; //NOI18N
282
getFileName().equals(arr[i]) || ATTR_NAME_EXT.equals(arr[i]) || ATTR_NAME_EXT_XML.equals(arr[i]) ||
283
safeNbAttrsCopy.equals(arr[i])
285
// exclude this index
288
// there can be two files with attributes
289
if (++lookUpIndex >= 2) {
298
/** Renames the attribute file for OpenVMS platform.
299
* The method renames "_nbattrs." into ".nbattrs".
300
* We cannot simply use the change.rename method
301
* because of the special property of OpenVMS having to do with
302
* a file name starting with "."
304
* @param f the folder containg the attribute file
306
private void copyVMSAttrFile(String f) throws IOException {
307
InputStream is = null;
308
OutputStream os = null;
311
change.createData(f + "/" + getFileName());
312
is = info.inputStream(f + "/" + "_nbattrs.");
313
os = info.outputStream(f + "/" + getFileName());
315
byte[] buf = new byte[256];
318
while ((readi = is.read(buf, 0, 256)) >= 0x0) {
319
os.write(buf, 0, readi);
324
//change.delete (f+"/"+"_nbattrs.");
326
} catch (IOException ie) {
341
// The class should be written in such a way that the access to disk is
342
// synchronized (this). But during the access nobody is allowed to
343
// perform serialization and deserialization
344
// of unknown objects, so all objects should be wrapped into NbMarshalledObject
345
// serialized or in reverse target NbMarshalledObject should be deserialized
346
// and then not holding the lock the object obtained from it by a call to
351
/* Get the file attribute with the specified name.
352
* @param name the file
353
* @param attrName name of the attribute
354
* @return appropriate (serializable) value or <CODE>null</CODE> if the attribute is unset (or could not be properly restored for some reason)
356
public Object readAttribute(String name, String attrName) {
358
String[] arr = new String[2];
361
/** At the momement substitutes lack of API */
362
if (attrName.equals(READONLY_ATTRIBUTES)) {
363
return info.readOnly(arr[0]) ? Boolean.TRUE : Boolean.FALSE;
366
synchronized (this) {
367
// synchronized so only one table for each folder
369
t = loadTable(arr[0]);
373
// had to split the code to do getAttr out of synchronized block
374
// because the attribute can be serialized FileObject and
375
// so the code returns back to FileSystem (that is usually synchronized)
377
// this leads to deadlocks between FS & DefaultAttributes implementation
379
// I do not know if the table should not be somehow synchronized,
381
return t.getAttr(arr[1], attrName);
384
/* Set the file attribute with the specified name.
385
* @param name the file
386
* @param attrName name of the attribute
387
* @param value new value or <code>null</code> to clear the attribute. Must be serializable, although particular filesystems may or may not use serialization to store attribute values.
388
* @exception IOException if the attribute cannot be set. If serialization is used to store it, this may in fact be a subclass such as {@link NotSerializableException}.
390
public void writeAttribute(String name, String attrName, Object value)
392
// create object that should be serialized
393
//NbMarshalledObject marshall = new NbMarshalledObject (value);
396
String[] arr = new String[2];
403
synchronized (this) {
404
t = loadTable(arr[0]);
408
// Tests if the attribute is changing
409
Object prev = t.getAttr(arr[1], attrName);
411
if (prev == value /*|| (value != null && value.equals (prev))*/ ) {
415
synchronized (this) {
416
Table t2 = loadTable(arr[0]);
418
if ((t == t2) && (version == t2.version)) {
419
// no modification between reading of the value =>
421
//Class cls = value.getClass();
423
t.setAttr(arr[1], attrName, null); // clear the attribute
426
(objType = XMLMapAttr.Attr.distinguishObject(value)) == XMLMapAttr.Attr.isValid(
430
t.setAttr(arr[1], attrName, value); //change value instead of marshall
432
t.setAttr(arr[1], attrName, XMLMapAttr.createAttribute(objType, value.toString()));
436
saveTable(arr[0], t);
443
// otherwise try it again
447
/* Get all file attribute names for the file.
448
* @param name the file
449
* @return enumeration of keys (as strings)
451
public synchronized Enumeration<String> attributes(String name) {
452
String[] arr = new String[2];
455
Table t = loadTable(arr[0]);
457
return t.attrs(arr[1]);
460
/* Called when a file is renamed, to appropriatelly update its attributes.
462
* @param oldName old name of the file
463
* @param newName new name of the file
465
public synchronized void renameAttributes(String oldName, String newName) {
467
String[] arr = new String[2];
470
Table t = loadTable(arr[0]);
471
Map v = (Map) t.remove(arr[1]);
473
// System.out.println ("ARg[0] = " + arr[0] + " arr[1] = " + arr[1] + " value: " + v); // NOI18N
475
// no attrs no change
481
// Remove transient attributes:
482
Iterator it = v.entrySet().iterator();
484
while (it.hasNext()) {
485
Map.Entry pair = (Map.Entry) it.next();
487
if (FileUtil.transientAttributes.contains(pair.getKey())) {
493
// System.out.println ("xyz[0] = " + arr[0] + " xyz[1] = " + arr[1] + " value: " + v); // NOI18N
494
saveTable(arr[0], t);
495
} catch (IOException e) {
496
ExternalUtil.exception(e);
500
/* Called when a file is deleted to also delete its attributes.
502
* @param name name of the file
504
public synchronized void deleteAttributes(String name) {
506
String[] arr = new String[2];
509
Table t = loadTable(arr[0]);
511
if (t.remove(arr[1]) != null) {
512
// if there is a change
513
saveTable(arr[0], t);
515
} catch (IOException e) {
516
ExternalUtil.exception(e);
520
/** Getter for the cache.
522
private Map getCache() {
524
cache = new HashMap(31);
530
/** Splits name of a file to name of folder and to name of the file.
531
* @param name of file
532
* @param arr arr[0] will hold name of folder and arr[1] name of the file
534
private static void split(String name, String[] arr) {
535
int i = name.lastIndexOf('/');
538
arr[0] = ""; // NOI18N
545
arr[0] = name.substring(0, i);
547
// increase the i to be beyond the length
548
if (++i == name.length()) {
549
arr[1] = ""; // NOI18N
552
arr[1] = name.substring(i);
557
* @param name name of folder to save attributes for
558
* @param map map to save
560
private void saveTable(String name, Table map) throws IOException {
561
String fullName = ((name.length() == 0) ? "" : (name + '/')) + getFileName(); // NOI18N
563
/** OpenVMS now supports various special characters including "~"*/
564
String safeName = fullName + "~"; // NOI18N
566
if (info.folder(fullName)) {
567
if (map.size() == 0) {
568
// ok no need to delete
573
change.createData(fullName);
575
if (map.size() == 0) {
576
deleteFile(fullName);
582
PrintWriter pw = null;
583
IOException ioexc = null;
586
pw = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(info.outputStream(safeName)), "UTF8")); // NOI18N
589
} catch (IOException iex) {
597
deleteFile(safeName);
601
deleteFile(fullName);
602
} catch (IOException iex2) {
603
/** if delete fails, then also rename fails and exception will
608
this.change.rename(safeName, fullName);
613
/** Load attributes from cache or
615
* @param name of folder to load data from
617
private Table loadTable(String name) { //throws IOException {
619
Reference r = (Reference) getCache().get(name);
622
Table m = (Table) r.get();
629
// have to load new table
630
Table t = load(name);
631
t.attach(name, this);
633
getCache().put(name, new SoftReference(t));
638
/** Loads the table. Does no initialization.
640
private Table load(String name) {
641
String[] acceptNames = {
642
((name.length() == 0) ? "" : (name + '/')) + getFileName(), // NOI18N
643
((name.length() == 0) ? "" : (name + '/')) + ATTR_NAME_EXT
646
for (int i = 0; i < acceptNames.length; i++) {
647
if (info.size(acceptNames[i]) > 0L) {
649
InputStream fis = info.inputStream(acceptNames[i]);
652
return loadTable(fis, acceptNames[i]);
656
} catch (IOException e) {
657
// ignore--who cares?
660
} catch (FileNotFoundException ex) {
661
ExternalUtil.exception(ex);
669
/** Loads the Table of extended attributes for a input stream from binary serialized file or from XML.
670
* @param is input stream
671
* @param folderName name of file for better error message
672
* @return the attributes table for this input stream
674
static Table loadTable(InputStream is, String folderName) {
675
Table retTable = new Table();
676
PushbackInputStream pbStream = null;
677
boolean isSerialized = false;
680
if (folderName.endsWith(ATTR_NAME_EXT)) {
681
pbStream = new PushbackInputStream(is, 4); //is.available()
682
isSerialized = isSerialized(pbStream);
685
if (isSerialized && (pbStream != null)) {
686
BufferedInputStream fis = new BufferedInputStream(pbStream);
687
ObjectInputStream ois = new org.openide.util.io.NbObjectInputStream(fis);
688
Object o = ois.readObject();
690
if (o instanceof Table) {
694
BufferedInputStream bis = (pbStream != null) ? new BufferedInputStream(pbStream)
695
: new BufferedInputStream(is);
696
retTable.readFromXML(bis, false);
700
} catch (Exception e) {
701
// [PENDING] use multi-arg getMessage (MessageFormat-style) properly here:
702
IOException summaryEx = new IOException(
703
NbBundle.getMessage(DefaultAttributes.class, "EXC_DefAttrReadErr") + ": " + folderName
705
ExternalUtil.copyAnnotation(summaryEx, e);
706
ExternalUtil.exception(summaryEx);
709
// create empty table, what else
713
/** Tests whether InputStream contains serialized data
714
* @param pbStream is pushback input stream; tests 4 bytes and then returns them back
715
* @return true if the file has serialized form
717
static private final boolean isSerialized(PushbackInputStream pbStream)
719
int[] serialPattern = { '\u00AC', '\u00ED', '\u0000', '\u0005' }; //NOI18N patern for serialized objects
720
byte[] checkedArray = new byte[serialPattern.length];
721
int unsignedConv = 0;
723
pbStream.read(checkedArray, 0, checkedArray.length);
724
pbStream.unread(checkedArray);
726
for (int i = 0; i < checkedArray.length; i++) {
727
unsignedConv = (checkedArray[i] < 0) ? (checkedArray[i] + 256) : checkedArray[i];
729
if (serialPattern[i] != unsignedConv) {
737
/** Remove from cache */
738
synchronized void removeTable(String name) {
739
getCache().remove(name);
743
// FileUtil.extractJar methods
746
/** Does the name seems like file with extended attributes?
747
* @param name the name
750
static boolean acceptName(String name) {
751
return (name.endsWith(ATTR_NAME_EXT) || name.endsWith(ATTR_NAME_EXT_XML));
754
private String getFileName() {
755
if (fileName == null) {
756
fileName = ATTR_NAME_EXT_XML;
762
private void deleteFile(String name) throws IOException {
763
OutputStream os = null;
765
this.info.lock(name);
766
//added because of mutual exclusion of streams (waits a while until stream is closed)
767
os = this.info.outputStream(name);
768
os.close(); os = null;
769
this.change.delete(name);
774
this.info.unlock(name);
779
/** Table that hold mapping between files and attributes.
780
* Hold mapping of type (String, Map (String, Object))
782
final static class Table extends HashMap implements Externalizable {
783
static final long serialVersionUID = 2353458763249746934L;
785
/** name of folder we belong to */
786
private transient String name;
788
/** attributes to belong to */
789
private transient DefaultAttributes attrs;
791
/** version counting */
792
private transient int version = 0;
799
/** Attaches to file in attributes */
800
public void attach(String name, DefaultAttributes attrs) {
805
/** Remove itself from the cache if finalized.
807
protected void finalize() {
808
// System.out.println ("Finalizing table for: " + name); // NOI18N
809
attrs.removeTable(name);
812
/** For given file finds requested attribute.
813
* @param fileName name of the file
814
* @param attrName name of the attribute
815
* @return attribute or null (if not found)
817
public Object getAttr(String fileName, String attrName) {
818
XMLMapAttr m = (XMLMapAttr) get(fileName);
824
o = m.getAttribute(attrName);
825
} catch (Exception e) {
826
ExternalUtil.annotate(e, "fileName = " + fileName); //NOI18N
827
ExternalUtil.exception(e);
834
if (!(o instanceof NbMarshalledObject)) {
838
NbMarshalledObject mo = (NbMarshalledObject) o;
841
return (mo == null) ? null : mo.get();
842
} catch (IOException e) {
843
ExternalUtil.log("Cannot load attribute " + attrName + " from " + fileName); // NOI18N
844
ExternalUtil.exception(e);
845
} catch (ClassNotFoundException e) {
846
ExternalUtil.log("Cannot load attribute " + attrName + " from " + fileName); // NOI18N
847
ExternalUtil.exception(e);
854
/** Sets an marshaled attribute to the table.
856
final void setMarshalledAttr(String fileName, String attrName, NbMarshalledObject obj) {
857
setAttr(fileName, attrName, obj);
861
* Sets an attribute to the table.
862
* New added - for Sandwich project (XML format instead of serialization) .
863
* @param fileName - name of file
864
* @param attrName - name of attribute
865
* @param obj - attribute
867
final void setAttr(String fileName, String attrName, Object obj) {
868
XMLMapAttr m = (XMLMapAttr) get(fileName);
871
m = new XMLMapAttr(); //HashMap (7);//XMLMapAttr();
875
m.put(attrName, obj, false);
877
if ((obj == null) && (m.size() == 1)) {
881
// increments the version
885
/** Enum of attributes for one file.
887
public Enumeration<String> attrs(String fileName) {
888
Map m = (Map) get(fileName);
891
return Enumerations.empty();
893
HashSet s = new HashSet(m.keySet());
895
return Collections.enumeration(s);
900
* Parses element: <CODE><Attributes version="1.0"></CODE>
901
* @return new instance of subclass (anonymous class)of ElementHandler
903
private ElementHandler parseFirstLevel() {
904
ElementHandler elemService = new ElementHandler() {
905
private final String[] ELM_KEYS = { "ATTRIBUTES" }; // NOI18N
906
private final String[] MANDAT_ATTR_KEYS = { "VERSION" }; // NOI18N
908
public void internalStartElement(String elemName, HashMap mapMandatory, HashMap mapAllowed)
909
throws SAXException {
910
// later can check version
913
protected String[] getKeys() {
917
protected String[] getMandatoryAttrs() {
918
return MANDAT_ATTR_KEYS;
926
* Parses element: <CODE><fileobject name="fileName"></CODE>
927
* @param fileName is parsed from XML
928
* @return new instance of subclass (anonymous class)of ElementHandler
930
private ElementHandler parseSecondLevel(final StringBuffer fileName) {
931
ElementHandler elemService = new ElementHandler() {
932
private final String[] ELM_KEYS = { "FILEOBJECT" }; // NOI18N
933
private final String[] MANDAT_ATTR_KEYS = { "NAME" }; // NOI18N
935
public void internalStartElement(String elemName, HashMap mapMandatory, HashMap mapAllowed)
936
throws SAXException {
938
fileName.delete(0, fileName.length());
939
temp = (String) mapMandatory.get("NAME"); // NOI18N
942
temp = (String) mapMandatory.get("name"); // NOI18N
946
fileName.append(temp);
950
public void endElement(String elementName)
951
throws SAXException {
954
protected String[] getKeys() {
958
protected String[] getMandatoryAttrs() {
959
return MANDAT_ATTR_KEYS;
967
* Parses element: <CODE><attr StringValue="This is attribute"></CODE>
968
* @param fileName is name of fileobject, which is assigned to attribute
969
* @return new instance of subclass (anonymous class)of ElementHandler
971
private ElementHandler parseThirdLevel(final StringBuffer fileName) {
972
ElementHandler elemService = new ElementHandler() {
973
private final String[] ELM_KEYS = { "ATTR" }; // NOI18N
974
private final String[] MANDAT_ATTR_KEYS = { "NAME" }; // NOI18N
976
public void internalStartElement(String elemName, HashMap mapMandatory, HashMap mapAllowed)
977
throws SAXException {
980
if (mapAllowed.isEmpty()) {
984
attrName = (String) mapMandatory.get("NAME"); // NOI18N
986
if (attrName == null) {
987
attrName = (String) mapMandatory.get("name"); // NOI18N
990
if (attrName == null) {
994
Iterator it = mapAllowed.entrySet().iterator();
996
while (it.hasNext()) {
997
Map.Entry pair = (Map.Entry) it.next();
999
if (XMLMapAttr.Attr.isValid((String) pair.getKey()) != -1) {
1000
XMLMapAttr.Attr attr = XMLMapAttr.createAttributeAndDecode(
1001
(String) pair.getKey(), (String) pair.getValue()
1003
setAttr(fileName.toString(), attrName, attr);
1008
protected String[] getKeys() {
1012
protected String[] getMandatoryAttrs() {
1013
return MANDAT_ATTR_KEYS;
1016
protected String[] getAllowedAttrs() {
1017
return XMLMapAttr.Attr.getAttrTypes();
1025
/** Writes itself to XML
1026
* @param pw is PrintWriter
1028
public void writeToXML(PrintWriter pw) /*throws IOException */ {
1030
Iterator it = new TreeSet(keySet()).iterator();
1031
XMLMapAttr.writeHeading(pw);
1033
while (it.hasNext()) {
1034
String file = (String) it.next();
1035
XMLMapAttr attr = (XMLMapAttr) get(file);
1037
if ((attr != null) && !attr.isEmpty()) {
1038
attr.write(pw, file, " "); // NOI18N
1042
XMLMapAttr.writeEnding(pw);
1046
* Reads itself from XML format
1047
* New added - for Sandwich project (XML format instead of serialization) .
1048
* @param is input stream (which is parsed)
1051
public void readFromXML(InputStream is, boolean validate)
1052
throws SAXException {
1053
StringBuffer fileName = new StringBuffer();
1054
ElementHandler[] elmKeyService = { parseFirstLevel(), parseSecondLevel(fileName), parseThirdLevel(fileName) }; //
1055
String dtd = getClass().getClassLoader().getResource(DTD_PATH).toExternalForm();
1056
InnerParser parser = new InnerParser(PUBLIC_ID, dtd, elmKeyService);
1059
parser.parseXML(is, validate);
1060
} catch (Exception ioe) {
1061
throw (SAXException) ExternalUtil.copyAnnotation(
1062
new SAXException(NbBundle.getMessage(DefaultAttributes.class, "EXC_DefAttrReadErr")), ioe
1064
} catch (FactoryConfigurationError fce) {
1065
// ??? see http://openide.netbeans.org/servlets/ReadMsg?msgId=340881&listName=dev
1066
throw (SAXException) ExternalUtil.copyAnnotation(
1067
new SAXException(NbBundle.getMessage(DefaultAttributes.class, "EXC_DefAttrReadErr")), fce
1072
/** Writes external.
1074
* @throws IOException */
1075
public void writeExternal(ObjectOutput oo) throws IOException {
1077
Iterator it = keySet().iterator();
1079
while (it.hasNext()) {
1080
String file = (String) it.next();
1081
Map attr = (Map) get(file);
1083
if ((attr != null) && !attr.isEmpty()) {
1084
oo.writeObject(file);
1086
Iterator entries = attr.entrySet().iterator();
1088
while (entries.hasNext()) {
1089
Map.Entry entry = (Map.Entry) entries.next();
1090
String key = (String) entry.getKey();
1091
Object value = entry.getValue();
1093
if ((key != null) && (value != null)) {
1094
oo.writeObject(key);
1095
oo.writeObject(value);
1099
oo.writeObject(null);
1103
oo.writeObject(null);
1108
public void readExternal(ObjectInput oi) throws IOException, ClassNotFoundException {
1110
String file = (String) oi.readObject();
1117
String attr = (String) oi.readObject();
1123
Object o = oi.readObject();
1125
// backward compatibility
1126
if (o instanceof java.rmi.MarshalledObject) {
1127
o = ((java.rmi.MarshalledObject) o).get();
1128
o = new NbMarshalledObject(o);
1131
// end of backward compatibility
1132
if (o instanceof NbMarshalledObject) {
1133
setAttr(file, attr, o);
1140
/** Element handler should be used as superclass for future classes. These future classes should be passed
1141
* to constructor of InnerParser as Array. (ElementHandler[]). Each subclass of ElementHandler is responsible for
1142
* processing one or more elements in XML file.
1143
* Each subclass of ElementHandler should overwrite one or more of these methods:
1144
* - protected String[] getKeys()
1145
* - protected String[] getMandatoryAttrs()
1146
* - protected String[] getAllowedAttrs()
1147
* - protected void endElement(String name) throws SAXException {}
1148
* - protected void characters(char[] ch, int start, int length) throws SAXException {}
1149
* - protected void internalStartElement(String elemName, HashMap mapMandatory,HashMap mapAllowed) throws SAXException {}
1151
static abstract class ElementHandler {
1152
private static final String[] EMPTY = { };
1153
private int mandatAttrCount;
1155
public void startElement(String elemName, Attributes attrs)
1156
throws SAXException {
1157
HashMap mapAllowed = new HashMap();
1158
HashMap mapMandatory = new HashMap();
1160
if (checkAttributes(attrs, mapMandatory, mapAllowed) == false) {
1161
throw new SAXException(
1162
NbBundle.getMessage(DefaultAttributes.class, "XML_InaccurateParam") + ": " + elemName
1166
internalStartElement(elemName, mapMandatory, mapAllowed);
1169
/** Inner parser calls this method to notify this class that start element was parsed (<someelement>)
1170
* @param elemName name of element
1171
* @param mapMandatory map(String attributeName,String attributeValue) which holds pairs attributeName and attributeValue, which are mandatory for this element
1172
* @param mapAllowed map(String attributeName,String attributeValue) which holds pairs attributeName and attributeValue, which are optional for this element
1173
* @throws SAXException
1175
protected void internalStartElement(String elemName, HashMap mapMandatory, HashMap mapAllowed)
1176
throws SAXException {
1179
/** Inner parser calls this method to notify this class that there is content between start element and end element
1180
* @param ch[] array of characters found between start and end element
1181
* @param start is start position in ch[]
1182
* @param length is length of content
1183
* @throws SAXException
1185
protected void characters(char[] ch, int start, int length)
1186
throws SAXException {
1189
/** Inner parser calls this method to notify this class that end element was parsed
1190
* @param elemName name of element
1191
* @throws SAXException
1193
protected void endElement(String elemName) throws SAXException {
1196
/** @return names of elements which this class can process
1198
protected String[] getKeys() {
1202
/** @return names of attributes which are checked and are mandatory
1204
protected String[] getMandatoryAttrs() {
1208
/** @return names of attributes which are allowed, are expected, but are not mandatory
1210
protected String[] getAllowedAttrs() {
1214
private int isMyTag(String name) {
1215
return isInArray(name, getKeys());
1218
private int isAllowedAttr(String name) {
1219
return isInArray(name, getAllowedAttrs());
1222
private boolean isMandatOK() {
1223
return (mandatAttrCount == getMandatoryAttrs().length);
1226
private int isMandatoryAttr(String name) {
1227
int retValue = isInArray(name, getMandatoryAttrs());
1229
if (retValue != -1) {
1236
private int isInArray(String name, String[] arr) {
1237
if ((arr == null) || (name == null)) {
1241
String correctStr = name.trim();
1243
for (int i = 0; i < arr.length; i++) {
1244
if (correctStr.equalsIgnoreCase(arr[i]) == true) {
1252
private boolean checkAttributes(Attributes attrList, HashMap mapMandatory, HashMap mapAllowed) {
1254
mandatAttrCount = 0;
1256
if (attrList == null) {
1260
for (int i = 0; i < attrList.getLength(); i++) {
1261
if (isMandatoryAttr(attrList.getQName(i)) != -1) {
1262
temp = attrList.getQName(i).toUpperCase(Locale.ENGLISH);
1263
mapMandatory.put(temp, attrList.getValue(i));
1268
if (isAllowedAttr(attrList.getQName(i)) != -1) {
1269
temp = attrList.getQName(i).toUpperCase(Locale.ENGLISH);
1270
mapAllowed.put(temp, attrList.getValue(i));
1276
return isMandatOK();
1280
/** Class that can be used to parse XML document (Expects array of ElementHandler clasess). Calls handler methods of ElementHandler clasess.
1282
static class InnerParser extends DefaultHandler {
1283
private ElementHandler[] elmKeyService; // = {fileSystemElement(attrStack),folderElement(attrStack),fileElement(attrStack),attrElement(attrStack)};
1284
private String tagInProcess = ""; // NOI18N
1285
private String publicId;
1286
private String publicURL;
1288
InnerParser(String publicId, String publicURL, ElementHandler[] elmKeyService) {
1289
this.elmKeyService = elmKeyService;
1290
this.publicId = publicId;
1291
this.publicURL = publicURL;
1294
/** Starts parsing document, that can be localized by means of uri parameter
1296
* @param uri adress of document, that will be parsed
1297
* @throws ParserConfigurationException
1298
* @throws IOException
1299
* @throws SAXException */
1300
public void parseXML(String uri, boolean validate)
1301
throws IOException, SAXException, ParserConfigurationException, FactoryConfigurationError {
1302
XMLReader parser = getParser(validate);
1306
/** Starts parsing document - if you have document`s InputStream
1308
* @param is document`s InputStream
1309
* @throws ParserConfigurationException
1310
* @throws IOException
1311
* @throws SAXException */
1312
public void parseXML(InputStream is, boolean validate)
1313
throws IOException, SAXException, ParserConfigurationException, FactoryConfigurationError {
1314
InputSource iSource = new InputSource(is);
1315
XMLReader parser = getParser(validate);
1316
parser.parse(iSource);
1319
private XMLReader getParser(boolean validate)
1320
throws SAXException, ParserConfigurationException, FactoryConfigurationError {
1321
XMLReader parser = XMLUtil.createXMLReader(validate);
1323
// create document handler and register it
1324
parser.setEntityResolver(this);
1325
parser.setContentHandler(this);
1326
parser.setErrorHandler(this);
1331
public void error(SAXParseException exception)
1332
throws SAXException {
1336
public void warning(SAXParseException exception)
1337
throws SAXException {
1341
public void fatalError(SAXParseException exception)
1342
throws SAXException {
1346
public void startElement(String uri, String lname, String name, Attributes attrs)
1347
throws SAXException {
1348
tagInProcess = name = name.trim();
1350
for (int i = 0; i < elmKeyService.length; i++) {
1351
if (elmKeyService[i].isMyTag(name) != -1) {
1352
elmKeyService[i].startElement(name, attrs);
1358
throw new SAXException(NbBundle.getMessage(DefaultAttributes.class, "XML_UnknownElement") + " " + name); // NOI18N
1361
public void endElement(String uri, String lname, String name) throws SAXException {
1362
for (int i = 0; i < elmKeyService.length; i++) {
1363
if (elmKeyService[i].isMyTag(name.trim()) != -1) {
1364
elmKeyService[i].endElement(name.trim());
1370
throw new SAXException(NbBundle.getMessage(DefaultAttributes.class, "XML_UnknownElement") + " " + name); // NOI18N
1373
public void characters(char[] ch, int start, int length)
1374
throws SAXException {
1375
for (int i = 0; i < elmKeyService.length; i++) {
1376
if (elmKeyService[i].isMyTag(tagInProcess) != -1) {
1377
elmKeyService[i].characters(ch, start, length);
1383
throw new SAXException(
1384
NbBundle.getMessage(DefaultAttributes.class, "XML_UnknownElement") + " " + tagInProcess
1388
public InputSource resolveEntity(java.lang.String pid, java.lang.String sid)
1389
throws SAXException {
1390
if ((pid != null) && pid.equals(publicId)) {
1391
return new InputSource(publicURL);
1394
return new InputSource(sid);