~ubuntu-branches/ubuntu/quantal/netbeans/quantal

« back to all changes in this revision

Viewing changes to openide/fs/src/org/openide/filesystems/DefaultAttributes.java

  • Committer: Bazaar Package Importer
  • Author(s): Marek Slama
  • Date: 2008-01-29 14:11:22 UTC
  • Revision ID: james.westby@ubuntu.com-20080129141122-fnzjbo11ntghxfu7
Tags: upstream-6.0.1
ImportĀ upstreamĀ versionĀ 6.0.1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 
3
 *
 
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
 
5
 *
 
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]"
 
23
 *
 
24
 * Contributor(s):
 
25
 *
 
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.
 
29
 *
 
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.
 
40
 */
 
41
 
 
42
package org.openide.filesystems;
 
43
 
 
44
import java.io.BufferedInputStream;
 
45
import java.io.BufferedOutputStream;
 
46
import java.io.Externalizable;
 
47
import java.io.File;
 
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;
 
66
import java.util.Map;
 
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;
 
81
 
 
82
/** Implementation of <code>AbstractFileSystem.Attr</code> using a special file
 
83
 * in each folder for holding attributes.
 
84
 * It needs to hide
 
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).
 
88
 *
 
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>
 
108
 *
 
109
 * @author Jaroslav Tulach
 
110
 */
 
111
@SuppressWarnings("unchecked")
 
112
public class DefaultAttributes extends Object implements AbstractFileSystem.Attr, AbstractFileSystem.List {
 
113
    static final long serialVersionUID = -5801291358293736478L;
 
114
 
 
115
    /** File name of special file in each folder where attributes are saved.
 
116
     * @deprecated does not handle XML attributes
 
117
     */
 
118
    @Deprecated
 
119
    public final static String ATTR_NAME = "filesystem"; // NOI18N
 
120
 
 
121
    /** Extension of special file in each folder where attributes are saved.
 
122
     * @deprecated does not handle XML attributes
 
123
     */
 
124
    @Deprecated
 
125
    public final static String ATTR_EXT = "attributes"; // NOI18N
 
126
 
 
127
    /** Name with extension of special file in each folder where attributes are saved.
 
128
     * @deprecated does not handle XML attributes
 
129
     */
 
130
    @Deprecated
 
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"
 
134
        ); // NOI18N
 
135
 
 
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
 
142
     */
 
143
    private final static String READONLY_ATTRIBUTES = "readOnlyAttrs"; //NOI18N
 
144
 
 
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    
 
150
 
 
151
    /** description of the fs to work on - info about files */
 
152
    private AbstractFileSystem.Info info;
 
153
 
 
154
    /** description of the fs to work on - work with files */
 
155
    private AbstractFileSystem.Change change;
 
156
 
 
157
    /** description of the fs to work on - listing of files */
 
158
    private AbstractFileSystem.List list;
 
159
 
 
160
    /** file name of attributes (default value corresponds to ATTR_NAME_EXT_XML) */
 
161
    private String fileName;
 
162
 
 
163
    /** Cache of attributes.
 
164
    * For name of folder gives map of maps of attibutes
 
165
    * (String, Reference (Table))
 
166
    */
 
167
    private transient Map cache;
 
168
 
 
169
    /** Constructor.
 
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)
 
173
    */
 
174
    public DefaultAttributes(
 
175
        AbstractFileSystem.Info info, AbstractFileSystem.Change change, AbstractFileSystem.List list
 
176
    ) {
 
177
        this.info = info;
 
178
        this.change = change;
 
179
        this.list = list;
 
180
        fileName = ATTR_NAME_EXT_XML;
 
181
    }
 
182
 
 
183
    /** Constructor.
 
184
     *
 
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)
 
188
     * @param fileName
 
189
     * @since 4.35
 
190
     */
 
191
    protected DefaultAttributes(
 
192
        AbstractFileSystem.Info info, AbstractFileSystem.Change change, AbstractFileSystem.List list, String fileName
 
193
    ) {
 
194
        this(info, change, list);
 
195
        this.fileName = fileName;
 
196
    }
 
197
 
 
198
    /** Methods to ensure backward compatibility for storing and
 
199
    * loading classes.
 
200
    */
 
201
    private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
 
202
        ObjectInputStream.GetField fields = ois.readFields();
 
203
 
 
204
        Object o1 = AbstractFileSystem.readImpl("change", fields); // NOI18N
 
205
        Object o2 = AbstractFileSystem.readImpl("info", fields); // NOI18N
 
206
        Object o3 = AbstractFileSystem.readImpl("list", fields); // NOI18N
 
207
 
 
208
        change = (AbstractFileSystem.Change) o1;
 
209
        info = (AbstractFileSystem.Info) o2;
 
210
        list = (AbstractFileSystem.List) o3;
 
211
    }
 
212
 
 
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.
 
218
    *
 
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)
 
221
    */
 
222
    public String[] children(String f) {
 
223
        String[] arr = list.children(f);
 
224
        int lookUpIndex = 0;
 
225
 
 
226
        if (arr == null) {
 
227
            return null;
 
228
        }
 
229
 
 
230
        int size = arr.length;
 
231
 
 
232
        if (size == 1) {
 
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"
 
236
            //
 
237
            if ((Utilities.getOperatingSystem() == Utilities.OS_VMS) && (arr[0] != null) && (f != null)) {
 
238
                if (arr[0].equalsIgnoreCase("_nbattrs.")) {
 
239
                    try {
 
240
                        deleteFile(f + "/" + arr[0]); // NOI18N
 
241
                    } catch (IOException ioe) {
 
242
                    }
 
243
 
 
244
                    arr[0] = getFileName();
 
245
                }
 
246
            }
 
247
 
 
248
            if ((getFileName().equals(arr[0]) || ATTR_NAME_EXT_XML.equals(arr[0]) || ATTR_NAME_EXT.equals(arr[0]))) {
 
249
                try {
 
250
                    this.change.delete(f + "/" + arr[0]);
 
251
                } catch (IOException iox) {
 
252
                }
 
253
 
 
254
                return new String[] {  };
 
255
            }
 
256
        }
 
257
 
 
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"
 
262
            //
 
263
            if ((Utilities.getOperatingSystem() == Utilities.OS_VMS) && (arr[i] != null) && (f != null)) {
 
264
                if (arr[i].equalsIgnoreCase("_nbattrs.")) {
 
265
                    try {
 
266
                        File fp = new File(f + "/" + ".nbattrs");
 
267
 
 
268
                        if (!fp.exists()) {
 
269
                            cache = null;
 
270
                            copyVMSAttrFile(f);
 
271
                        }
 
272
                    } catch (IOException ioe) {
 
273
                    }
 
274
 
 
275
                    arr[i] = getFileName();
 
276
                }
 
277
            }
 
278
 
 
279
            String safeNbAttrsCopy = getFileName() + "~"; //NOI18N
 
280
 
 
281
            if (
 
282
                getFileName().equals(arr[i]) || ATTR_NAME_EXT.equals(arr[i]) || ATTR_NAME_EXT_XML.equals(arr[i]) ||
 
283
                    safeNbAttrsCopy.equals(arr[i])
 
284
            ) {
 
285
                // exclude this index
 
286
                arr[i] = null;
 
287
 
 
288
                // there can be two files with attributes
 
289
                if (++lookUpIndex >= 2) {
 
290
                    break;
 
291
                }
 
292
            }
 
293
        }
 
294
 
 
295
        return arr;
 
296
    }
 
297
 
 
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 "."
 
303
     *
 
304
     *  @param f the folder containg the attribute file
 
305
     */
 
306
    private void copyVMSAttrFile(String f) throws IOException {
 
307
        InputStream is = null;
 
308
        OutputStream os = null;
 
309
 
 
310
        try {
 
311
            change.createData(f + "/" + getFileName());
 
312
            is = info.inputStream(f + "/" + "_nbattrs.");
 
313
            os = info.outputStream(f + "/" + getFileName());
 
314
 
 
315
            byte[] buf = new byte[256];
 
316
            int readi;
 
317
 
 
318
            while ((readi = is.read(buf, 0, 256)) >= 0x0) {
 
319
                os.write(buf, 0, readi);
 
320
            }
 
321
 
 
322
            is.close();
 
323
 
 
324
            //change.delete (f+"/"+"_nbattrs.");
 
325
            is = null;
 
326
        } catch (IOException ie) {
 
327
        } finally {
 
328
            if (is != null) {
 
329
                is.close();
 
330
            }
 
331
 
 
332
            if (os != null) {
 
333
                os.close();
 
334
            }
 
335
        }
 
336
    }
 
337
 
 
338
    // JST: Description
 
339
    //
 
340
    //
 
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
 
347
    // marshall.get ().
 
348
    //
 
349
    // JST: Got it?
 
350
 
 
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)
 
355
    */
 
356
    public Object readAttribute(String name, String attrName) {
 
357
        Table t;
 
358
        String[] arr = new String[2];
 
359
        split(name, arr);
 
360
 
 
361
        /** At the momement substitutes lack of API */
 
362
        if (attrName.equals(READONLY_ATTRIBUTES)) {
 
363
            return info.readOnly(arr[0]) ? Boolean.TRUE : Boolean.FALSE;
 
364
        }
 
365
 
 
366
        synchronized (this) {
 
367
            // synchronized so only one table for each folder
 
368
            // can exist
 
369
            t = loadTable(arr[0]);
 
370
        }
 
371
 
 
372
        // JST:
 
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)
 
376
        //
 
377
        // this leads to deadlocks between FS & DefaultAttributes implementation
 
378
        //
 
379
        // I do not know if the table should not be somehow synchronized,
 
380
        // but it seems ok.
 
381
        return t.getAttr(arr[1], attrName);
 
382
    }
 
383
 
 
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}.
 
389
    */
 
390
    public void writeAttribute(String name, String attrName, Object value)
 
391
    throws IOException {
 
392
        // create object that should be serialized
 
393
        //NbMarshalledObject marshall = new NbMarshalledObject (value);
 
394
        int objType;
 
395
 
 
396
        String[] arr = new String[2];
 
397
        split(name, arr);
 
398
 
 
399
        for (;;) {
 
400
            int version;
 
401
            Table t;
 
402
 
 
403
            synchronized (this) {
 
404
                t = loadTable(arr[0]);
 
405
                version = t.version;
 
406
            }
 
407
 
 
408
            // Tests if the attribute is changing
 
409
            Object prev = t.getAttr(arr[1], attrName);
 
410
 
 
411
            if (prev == value /*|| (value != null && value.equals (prev))*/    ) {
 
412
                return;
 
413
            }
 
414
 
 
415
            synchronized (this) {
 
416
                Table t2 = loadTable(arr[0]);
 
417
 
 
418
                if ((t == t2) && (version == t2.version)) {
 
419
                    // no modification between reading of the value =>
 
420
                    // save!
 
421
                    //Class cls = value.getClass();                    
 
422
                    if (value == null) {
 
423
                        t.setAttr(arr[1], attrName, null); // clear the attribute
 
424
                    } else {
 
425
                        if (
 
426
                            (objType = XMLMapAttr.Attr.distinguishObject(value)) == XMLMapAttr.Attr.isValid(
 
427
                                    "SERIALVALUE"
 
428
                                )
 
429
                        ) { // NOI18N
 
430
                            t.setAttr(arr[1], attrName, value); //change value instead of marshall
 
431
                        } else {
 
432
                            t.setAttr(arr[1], attrName, XMLMapAttr.createAttribute(objType, value.toString()));
 
433
                        }
 
434
                    }
 
435
 
 
436
                    saveTable(arr[0], t);
 
437
 
 
438
                    // ok, saved
 
439
                    return;
 
440
                }
 
441
            }
 
442
 
 
443
            // otherwise try it again
 
444
        }
 
445
    }
 
446
 
 
447
    /* Get all file attribute names for the file.
 
448
    * @param name the file
 
449
    * @return enumeration of keys (as strings)
 
450
    */
 
451
    public synchronized Enumeration<String> attributes(String name) {
 
452
        String[] arr = new String[2];
 
453
        split(name, arr);
 
454
 
 
455
        Table t = loadTable(arr[0]);
 
456
 
 
457
        return t.attrs(arr[1]);
 
458
    }
 
459
 
 
460
    /* Called when a file is renamed, to appropriatelly update its attributes.
 
461
    * <p>
 
462
    * @param oldName old name of the file
 
463
    * @param newName new name of the file
 
464
    */
 
465
    public synchronized void renameAttributes(String oldName, String newName) {
 
466
        try {
 
467
            String[] arr = new String[2];
 
468
            split(oldName, arr);
 
469
 
 
470
            Table t = loadTable(arr[0]);
 
471
            Map v = (Map) t.remove(arr[1]);
 
472
 
 
473
            //      System.out.println ("ARg[0] = " + arr[0] + " arr[1] = " + arr[1] + " value: " + v); // NOI18N
 
474
            if (v == null) {
 
475
                // no attrs no change
 
476
                return;
 
477
            }
 
478
 
 
479
            split(newName, arr);
 
480
 
 
481
            // Remove transient attributes:
 
482
            Iterator it = v.entrySet().iterator();
 
483
 
 
484
            while (it.hasNext()) {
 
485
                Map.Entry pair = (Map.Entry) it.next();
 
486
 
 
487
                if (FileUtil.transientAttributes.contains(pair.getKey())) {
 
488
                    it.remove();
 
489
                }
 
490
            }
 
491
            t.put(arr[1], v);
 
492
 
 
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);
 
497
        }
 
498
    }
 
499
 
 
500
    /* Called when a file is deleted to also delete its attributes.
 
501
    *
 
502
    * @param name name of the file
 
503
    */
 
504
    public synchronized void deleteAttributes(String name) {
 
505
        try {
 
506
            String[] arr = new String[2];
 
507
            split(name, arr);
 
508
 
 
509
            Table t = loadTable(arr[0]);
 
510
 
 
511
            if (t.remove(arr[1]) != null) {
 
512
                // if there is a change
 
513
                saveTable(arr[0], t);
 
514
            }
 
515
        } catch (IOException e) {
 
516
            ExternalUtil.exception(e);
 
517
        }
 
518
    }
 
519
 
 
520
    /** Getter for the cache.
 
521
    */
 
522
    private Map getCache() {
 
523
        if (cache == null) {
 
524
            cache = new HashMap(31);
 
525
        }
 
526
 
 
527
        return cache;
 
528
    }
 
529
 
 
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
 
533
    */
 
534
    private static void split(String name, String[] arr) {
 
535
        int i = name.lastIndexOf('/');
 
536
 
 
537
        if (i == -1) {
 
538
            arr[0] = ""; // NOI18N
 
539
            arr[1] = name;
 
540
 
 
541
            return;
 
542
        }
 
543
 
 
544
        // folder name
 
545
        arr[0] = name.substring(0, i);
 
546
 
 
547
        // increase the i to be beyond the length
 
548
        if (++i == name.length()) {
 
549
            arr[1] = ""; // NOI18N
 
550
        } else {
 
551
            // split it
 
552
            arr[1] = name.substring(i);
 
553
        }
 
554
    }
 
555
 
 
556
    /** Save attributes.
 
557
    * @param name name of folder to save attributes for
 
558
    * @param map map to save
 
559
    */
 
560
    private void saveTable(String name, Table map) throws IOException {
 
561
        String fullName = ((name.length() == 0) ? "" : (name + '/')) + getFileName(); // NOI18N
 
562
 
 
563
        /** OpenVMS now supports various special characters including "~"*/
 
564
        String safeName = fullName + "~"; // NOI18N        
 
565
 
 
566
        if (info.folder(fullName)) {
 
567
            if (map.size() == 0) {
 
568
                // ok no need to delete
 
569
                return;
 
570
            }
 
571
 
 
572
            // find parent
 
573
            change.createData(fullName);
 
574
        } else {
 
575
            if (map.size() == 0) {
 
576
                deleteFile(fullName);
 
577
 
 
578
                return;
 
579
            }
 
580
        }
 
581
 
 
582
        PrintWriter pw = null;
 
583
        IOException ioexc = null;
 
584
 
 
585
        try {
 
586
            pw = new PrintWriter(new OutputStreamWriter(new BufferedOutputStream(info.outputStream(safeName)), "UTF8")); // NOI18N            
 
587
            map.writeToXML(pw);
 
588
            pw.flush();
 
589
        } catch (IOException iex) {
 
590
            ioexc = iex;
 
591
        } finally {
 
592
            if (pw != null) {
 
593
                pw.close();
 
594
            }
 
595
 
 
596
            if (ioexc != null) {
 
597
                deleteFile(safeName);
 
598
                throw ioexc;
 
599
            } else {
 
600
                try {
 
601
                    deleteFile(fullName);
 
602
                } catch (IOException iex2) {
 
603
                    /** if delete fails, then also rename fails and exception will
 
604
                     * be fired
 
605
                     */
 
606
                }
 
607
 
 
608
                this.change.rename(safeName, fullName);
 
609
            }
 
610
        }
 
611
    }
 
612
 
 
613
    /** Load attributes from cache or
 
614
    * from disk.
 
615
    * @param name of folder to load data from
 
616
    */
 
617
    private Table loadTable(String name) { //throws IOException {
 
618
 
 
619
        Reference r = (Reference) getCache().get(name);
 
620
 
 
621
        if (r != null) {
 
622
            Table m = (Table) r.get();
 
623
 
 
624
            if (m != null) {
 
625
                return m;
 
626
            }
 
627
        }
 
628
 
 
629
        // have to load new table
 
630
        Table t = load(name);
 
631
        t.attach(name, this);
 
632
 
 
633
        getCache().put(name, new SoftReference(t));
 
634
 
 
635
        return t;
 
636
    }
 
637
 
 
638
    /** Loads the table. Does no initialization.
 
639
    */
 
640
    private Table load(String name) {
 
641
        String[] acceptNames = {
 
642
            ((name.length() == 0) ? "" : (name + '/')) + getFileName(), // NOI18N
 
643
            ((name.length() == 0) ? "" : (name + '/')) + ATTR_NAME_EXT
 
644
        }; // NOI18N
 
645
 
 
646
        for (int i = 0; i < acceptNames.length; i++) {
 
647
            if (info.size(acceptNames[i]) > 0L) {
 
648
                try {
 
649
                    InputStream fis = info.inputStream(acceptNames[i]);
 
650
 
 
651
                    try {
 
652
                        return loadTable(fis, acceptNames[i]);
 
653
                    } finally {
 
654
                        try {
 
655
                            fis.close();
 
656
                        } catch (IOException e) {
 
657
                            // ignore--who cares?
 
658
                        }
 
659
                    }
 
660
                } catch (FileNotFoundException ex) {
 
661
                    ExternalUtil.exception(ex);
 
662
                }
 
663
            }
 
664
        }
 
665
 
 
666
        return new Table();
 
667
    }
 
668
 
 
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
 
673
    */
 
674
    static Table loadTable(InputStream is, String folderName) {
 
675
        Table retTable = new Table();
 
676
        PushbackInputStream pbStream = null;
 
677
        boolean isSerialized = false;
 
678
 
 
679
        try {
 
680
            if (folderName.endsWith(ATTR_NAME_EXT)) {
 
681
                pbStream = new PushbackInputStream(is, 4); //is.available()
 
682
                isSerialized = isSerialized(pbStream);
 
683
            }
 
684
 
 
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();
 
689
 
 
690
                if (o instanceof Table) {
 
691
                    return (Table) o;
 
692
                }
 
693
            } else {
 
694
                BufferedInputStream bis = (pbStream != null) ? new BufferedInputStream(pbStream)
 
695
                                                             : new BufferedInputStream(is);
 
696
                retTable.readFromXML(bis, false);
 
697
 
 
698
                return retTable;
 
699
            }
 
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
 
704
                );
 
705
            ExternalUtil.copyAnnotation(summaryEx, e);
 
706
            ExternalUtil.exception(summaryEx);
 
707
        }
 
708
 
 
709
        // create empty table, what else
 
710
        return new Table();
 
711
    }
 
712
 
 
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
 
716
    */
 
717
    static private final boolean isSerialized(PushbackInputStream pbStream)
 
718
    throws IOException {
 
719
        int[] serialPattern = { '\u00AC', '\u00ED', '\u0000', '\u0005' }; //NOI18N patern for serialized objects
 
720
        byte[] checkedArray = new byte[serialPattern.length];
 
721
        int unsignedConv = 0;
 
722
 
 
723
        pbStream.read(checkedArray, 0, checkedArray.length);
 
724
        pbStream.unread(checkedArray);
 
725
 
 
726
        for (int i = 0; i < checkedArray.length; i++) {
 
727
            unsignedConv = (checkedArray[i] < 0) ? (checkedArray[i] + 256) : checkedArray[i];
 
728
 
 
729
            if (serialPattern[i] != unsignedConv) {
 
730
                return false;
 
731
            }
 
732
        }
 
733
 
 
734
        return true;
 
735
    }
 
736
 
 
737
    /** Remove from cache */
 
738
    synchronized void removeTable(String name) {
 
739
        getCache().remove(name);
 
740
    }
 
741
 
 
742
    //
 
743
    // FileUtil.extractJar methods
 
744
    //
 
745
 
 
746
    /** Does the name seems like file with extended attributes?
 
747
    * @param name the name
 
748
    * @return true if so
 
749
    */
 
750
    static boolean acceptName(String name) {
 
751
        return (name.endsWith(ATTR_NAME_EXT) || name.endsWith(ATTR_NAME_EXT_XML));
 
752
    }
 
753
 
 
754
    private String getFileName() {
 
755
        if (fileName == null) {
 
756
            fileName = ATTR_NAME_EXT_XML;
 
757
        }
 
758
 
 
759
        return fileName;
 
760
    }
 
761
    
 
762
    private void deleteFile(String name) throws IOException {
 
763
        OutputStream os = null;
 
764
        try {
 
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);
 
770
        } finally {
 
771
            if (os != null) {
 
772
                os.close();
 
773
            }            
 
774
            this.info.unlock(name);
 
775
        }
 
776
    }
 
777
    
 
778
 
 
779
    /** Table that hold mapping between files and attributes.
 
780
    * Hold mapping of type (String, Map (String, Object))
 
781
    */
 
782
    final static class Table extends HashMap implements Externalizable {
 
783
        static final long serialVersionUID = 2353458763249746934L;
 
784
 
 
785
        /** name of folder we belong to */
 
786
        private transient String name;
 
787
 
 
788
        /** attributes to belong to */
 
789
        private transient DefaultAttributes attrs;
 
790
 
 
791
        /** version counting */
 
792
        private transient int version = 0;
 
793
 
 
794
        /** Constructor */
 
795
        public Table() {
 
796
            super(11);
 
797
        }
 
798
 
 
799
        /** Attaches to file in attributes */
 
800
        public void attach(String name, DefaultAttributes attrs) {
 
801
            this.name = name;
 
802
            this.attrs = attrs;
 
803
        }
 
804
 
 
805
        /** Remove itself from the cache if finalized.
 
806
        */
 
807
        protected void finalize() {
 
808
            //      System.out.println ("Finalizing table for: " + name); // NOI18N
 
809
            attrs.removeTable(name);
 
810
        }
 
811
 
 
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)
 
816
         */
 
817
        public Object getAttr(String fileName, String attrName) {
 
818
            XMLMapAttr m = (XMLMapAttr) get(fileName);
 
819
 
 
820
            if (m != null) {
 
821
                Object o = null;
 
822
 
 
823
                try {
 
824
                    o = m.getAttribute(attrName);
 
825
                } catch (Exception e) {
 
826
                    ExternalUtil.annotate(e, "fileName = " + fileName); //NOI18N                                     
 
827
                    ExternalUtil.exception(e);
 
828
                }
 
829
 
 
830
                if (o == null) {
 
831
                    return null;
 
832
                }
 
833
 
 
834
                if (!(o instanceof NbMarshalledObject)) {
 
835
                    return o;
 
836
                }
 
837
 
 
838
                NbMarshalledObject mo = (NbMarshalledObject) o;
 
839
 
 
840
                try {
 
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);
 
848
                }
 
849
            }
 
850
 
 
851
            return null;
 
852
        }
 
853
 
 
854
        /** Sets an marshaled attribute to the table.
 
855
        */
 
856
        final void setMarshalledAttr(String fileName, String attrName, NbMarshalledObject obj) {
 
857
            setAttr(fileName, attrName, obj);
 
858
        }
 
859
 
 
860
        /**
 
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
 
866
         */
 
867
        final void setAttr(String fileName, String attrName, Object obj) {
 
868
            XMLMapAttr m = (XMLMapAttr) get(fileName);
 
869
 
 
870
            if (m == null) {
 
871
                m = new XMLMapAttr(); //HashMap (7);//XMLMapAttr();
 
872
                put(fileName, m);
 
873
            }
 
874
 
 
875
            m.put(attrName, obj, false);
 
876
 
 
877
            if ((obj == null) && (m.size() == 1)) {
 
878
                remove(fileName);
 
879
            }
 
880
 
 
881
            // increments the version
 
882
            version++;
 
883
        }
 
884
 
 
885
        /** Enum of attributes for one file.
 
886
        */
 
887
        public Enumeration<String> attrs(String fileName) {
 
888
            Map m = (Map) get(fileName);
 
889
 
 
890
            if (m == null) {
 
891
                return Enumerations.empty();
 
892
            } else {
 
893
                HashSet s = new HashSet(m.keySet());
 
894
 
 
895
                return Collections.enumeration(s);
 
896
            }
 
897
        }
 
898
 
 
899
        /**
 
900
         * Parses element:  <CODE><Attributes version="1.0"></CODE>
 
901
         * @return new instance of subclass (anonymous class)of ElementHandler
 
902
         */
 
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
 
907
 
 
908
                    public void internalStartElement(String elemName, HashMap mapMandatory, HashMap mapAllowed)
 
909
                    throws SAXException {
 
910
                        // later can check version
 
911
                    }
 
912
 
 
913
                    protected String[] getKeys() {
 
914
                        return ELM_KEYS;
 
915
                    }
 
916
 
 
917
                    protected String[] getMandatoryAttrs() {
 
918
                        return MANDAT_ATTR_KEYS;
 
919
                    }
 
920
                };
 
921
 
 
922
            return elemService;
 
923
        }
 
924
 
 
925
        /**
 
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
 
929
         */
 
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
 
934
 
 
935
                    public void internalStartElement(String elemName, HashMap mapMandatory, HashMap mapAllowed)
 
936
                    throws SAXException {
 
937
                        String temp;
 
938
                        fileName.delete(0, fileName.length());
 
939
                        temp = (String) mapMandatory.get("NAME"); // NOI18N
 
940
 
 
941
                        if (temp == null) {
 
942
                            temp = (String) mapMandatory.get("name"); // NOI18N
 
943
                        }
 
944
 
 
945
                        if (temp != null) {
 
946
                            fileName.append(temp);
 
947
                        }
 
948
                    }
 
949
 
 
950
                    public void endElement(String elementName)
 
951
                    throws SAXException {
 
952
                    }
 
953
 
 
954
                    protected String[] getKeys() {
 
955
                        return ELM_KEYS;
 
956
                    }
 
957
 
 
958
                    protected String[] getMandatoryAttrs() {
 
959
                        return MANDAT_ATTR_KEYS;
 
960
                    }
 
961
                };
 
962
 
 
963
            return elemService;
 
964
        }
 
965
 
 
966
        /**
 
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
 
970
         */
 
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
 
975
 
 
976
                    public void internalStartElement(String elemName, HashMap mapMandatory, HashMap mapAllowed)
 
977
                    throws SAXException {
 
978
                        String attrName;
 
979
 
 
980
                        if (mapAllowed.isEmpty()) {
 
981
                            return;
 
982
                        }
 
983
 
 
984
                        attrName = (String) mapMandatory.get("NAME"); // NOI18N
 
985
 
 
986
                        if (attrName == null) {
 
987
                            attrName = (String) mapMandatory.get("name"); // NOI18N
 
988
                        }
 
989
 
 
990
                        if (attrName == null) {
 
991
                            return;
 
992
                        }
 
993
 
 
994
                        Iterator it = mapAllowed.entrySet().iterator();
 
995
 
 
996
                        while (it.hasNext()) {
 
997
                            Map.Entry pair = (Map.Entry) it.next();
 
998
 
 
999
                            if (XMLMapAttr.Attr.isValid((String) pair.getKey()) != -1) {
 
1000
                                XMLMapAttr.Attr attr = XMLMapAttr.createAttributeAndDecode(
 
1001
                                        (String) pair.getKey(), (String) pair.getValue()
 
1002
                                    );
 
1003
                                setAttr(fileName.toString(), attrName, attr);
 
1004
                            }
 
1005
                        }
 
1006
                    }
 
1007
 
 
1008
                    protected String[] getKeys() {
 
1009
                        return ELM_KEYS;
 
1010
                    }
 
1011
 
 
1012
                    protected String[] getMandatoryAttrs() {
 
1013
                        return MANDAT_ATTR_KEYS;
 
1014
                    }
 
1015
 
 
1016
                    protected String[] getAllowedAttrs() {
 
1017
                        return XMLMapAttr.Attr.getAttrTypes();
 
1018
                    }
 
1019
                     //ALLOWED_ATTR_KEYS
 
1020
                };
 
1021
 
 
1022
            return elemService;
 
1023
        }
 
1024
 
 
1025
        /** Writes itself to XML
 
1026
         * @param pw is PrintWriter
 
1027
         */
 
1028
        public void writeToXML(PrintWriter pw) /*throws IOException */ {
 
1029
            // list of names
 
1030
            Iterator it = new TreeSet(keySet()).iterator();
 
1031
            XMLMapAttr.writeHeading(pw);
 
1032
 
 
1033
            while (it.hasNext()) {
 
1034
                String file = (String) it.next();
 
1035
                XMLMapAttr attr = (XMLMapAttr) get(file);
 
1036
 
 
1037
                if ((attr != null) && !attr.isEmpty()) {
 
1038
                    attr.write(pw, file, "    "); // NOI18N
 
1039
                }
 
1040
            }
 
1041
 
 
1042
            XMLMapAttr.writeEnding(pw);
 
1043
        }
 
1044
 
 
1045
        /**
 
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)
 
1049
         * @return Table
 
1050
         */
 
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);
 
1057
 
 
1058
            try {
 
1059
                parser.parseXML(is, validate);
 
1060
            } catch (Exception ioe) {
 
1061
                throw (SAXException) ExternalUtil.copyAnnotation(
 
1062
                    new SAXException(NbBundle.getMessage(DefaultAttributes.class, "EXC_DefAttrReadErr")), ioe
 
1063
                );
 
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
 
1068
                );
 
1069
            }
 
1070
        }
 
1071
 
 
1072
        /** Writes external.
 
1073
         * @param oo
 
1074
         * @throws IOException  */
 
1075
        public void writeExternal(ObjectOutput oo) throws IOException {
 
1076
            // list of names
 
1077
            Iterator it = keySet().iterator();
 
1078
 
 
1079
            while (it.hasNext()) {
 
1080
                String file = (String) it.next();
 
1081
                Map attr = (Map) get(file);
 
1082
 
 
1083
                if ((attr != null) && !attr.isEmpty()) {
 
1084
                    oo.writeObject(file);
 
1085
 
 
1086
                    Iterator entries = attr.entrySet().iterator();
 
1087
 
 
1088
                    while (entries.hasNext()) {
 
1089
                        Map.Entry entry = (Map.Entry) entries.next();
 
1090
                        String key = (String) entry.getKey();
 
1091
                        Object value = entry.getValue();
 
1092
 
 
1093
                        if ((key != null) && (value != null)) {
 
1094
                            oo.writeObject(key);
 
1095
                            oo.writeObject(value);
 
1096
                        }
 
1097
                    }
 
1098
 
 
1099
                    oo.writeObject(null);
 
1100
                }
 
1101
            }
 
1102
 
 
1103
            oo.writeObject(null);
 
1104
        }
 
1105
 
 
1106
        /** Reads external.
 
1107
        */
 
1108
        public void readExternal(ObjectInput oi) throws IOException, ClassNotFoundException {
 
1109
            for (;;) {
 
1110
                String file = (String) oi.readObject();
 
1111
 
 
1112
                if (file == null) {
 
1113
                    break;
 
1114
                }
 
1115
 
 
1116
                for (;;) {
 
1117
                    String attr = (String) oi.readObject();
 
1118
 
 
1119
                    if (attr == null) {
 
1120
                        break;
 
1121
                    }
 
1122
 
 
1123
                    Object o = oi.readObject();
 
1124
 
 
1125
                    // backward compatibility
 
1126
                    if (o instanceof java.rmi.MarshalledObject) {
 
1127
                        o = ((java.rmi.MarshalledObject) o).get();
 
1128
                        o = new NbMarshalledObject(o);
 
1129
                    }
 
1130
 
 
1131
                    // end of backward compatibility
 
1132
                    if (o instanceof NbMarshalledObject) {
 
1133
                        setAttr(file, attr, o);
 
1134
                    }
 
1135
                }
 
1136
            }
 
1137
        }
 
1138
    }
 
1139
 
 
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 {}
 
1150
     */
 
1151
    static abstract class ElementHandler {
 
1152
        private static final String[] EMPTY = {  };
 
1153
        private int mandatAttrCount;
 
1154
 
 
1155
        public void startElement(String elemName, Attributes attrs)
 
1156
        throws SAXException {
 
1157
            HashMap mapAllowed = new HashMap();
 
1158
            HashMap mapMandatory = new HashMap();
 
1159
 
 
1160
            if (checkAttributes(attrs, mapMandatory, mapAllowed) == false) {
 
1161
                throw new SAXException(
 
1162
                    NbBundle.getMessage(DefaultAttributes.class, "XML_InaccurateParam") + ": " + elemName
 
1163
                ); // NOI18N 
 
1164
            }
 
1165
 
 
1166
            internalStartElement(elemName, mapMandatory, mapAllowed);
 
1167
        }
 
1168
 
 
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
 
1174
         */
 
1175
        protected void internalStartElement(String elemName, HashMap mapMandatory, HashMap mapAllowed)
 
1176
        throws SAXException {
 
1177
        }
 
1178
 
 
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
 
1184
         */
 
1185
        protected void characters(char[] ch, int start, int length)
 
1186
        throws SAXException {
 
1187
        }
 
1188
 
 
1189
        /** Inner parser calls this method to notify this class that end element was parsed
 
1190
         * @param elemName  name of element
 
1191
         * @throws SAXException
 
1192
         */
 
1193
        protected void endElement(String elemName) throws SAXException {
 
1194
        }
 
1195
 
 
1196
        /** @return names of elements which this class can process
 
1197
         */
 
1198
        protected String[] getKeys() {
 
1199
            return EMPTY;
 
1200
        }
 
1201
 
 
1202
        /** @return names of attributes which are checked and are mandatory
 
1203
         */
 
1204
        protected String[] getMandatoryAttrs() {
 
1205
            return getKeys();
 
1206
        }
 
1207
 
 
1208
        /** @return names of attributes which are allowed, are expected, but are not mandatory
 
1209
         */
 
1210
        protected String[] getAllowedAttrs() {
 
1211
            return EMPTY;
 
1212
        }
 
1213
 
 
1214
        private int isMyTag(String name) {
 
1215
            return isInArray(name, getKeys());
 
1216
        }
 
1217
 
 
1218
        private int isAllowedAttr(String name) {
 
1219
            return isInArray(name, getAllowedAttrs());
 
1220
        }
 
1221
 
 
1222
        private boolean isMandatOK() {
 
1223
            return (mandatAttrCount == getMandatoryAttrs().length);
 
1224
        }
 
1225
 
 
1226
        private int isMandatoryAttr(String name) {
 
1227
            int retValue = isInArray(name, getMandatoryAttrs());
 
1228
 
 
1229
            if (retValue != -1) {
 
1230
                mandatAttrCount++;
 
1231
            }
 
1232
 
 
1233
            return retValue;
 
1234
        }
 
1235
 
 
1236
        private int isInArray(String name, String[] arr) {
 
1237
            if ((arr == null) || (name == null)) {
 
1238
                return -1;
 
1239
            }
 
1240
 
 
1241
            String correctStr = name.trim();
 
1242
 
 
1243
            for (int i = 0; i < arr.length; i++) {
 
1244
                if (correctStr.equalsIgnoreCase(arr[i]) == true) {
 
1245
                    return i;
 
1246
                }
 
1247
            }
 
1248
 
 
1249
            return -1;
 
1250
        }
 
1251
 
 
1252
        private boolean checkAttributes(Attributes attrList, HashMap mapMandatory, HashMap mapAllowed) {
 
1253
            String temp;
 
1254
            mandatAttrCount = 0;
 
1255
 
 
1256
            if (attrList == null) {
 
1257
                return false;
 
1258
            }
 
1259
 
 
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));
 
1264
 
 
1265
                    continue;
 
1266
                }
 
1267
 
 
1268
                if (isAllowedAttr(attrList.getQName(i)) != -1) {
 
1269
                    temp = attrList.getQName(i).toUpperCase(Locale.ENGLISH);
 
1270
                    mapAllowed.put(temp, attrList.getValue(i));
 
1271
 
 
1272
                    continue;
 
1273
                }
 
1274
            }
 
1275
 
 
1276
            return isMandatOK();
 
1277
        }
 
1278
    }
 
1279
 
 
1280
    /** Class that can be used to parse XML document (Expects array of ElementHandler clasess).  Calls handler methods of ElementHandler clasess.
 
1281
     */
 
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;
 
1287
 
 
1288
        InnerParser(String publicId, String publicURL, ElementHandler[] elmKeyService) {
 
1289
            this.elmKeyService = elmKeyService;
 
1290
            this.publicId = publicId;
 
1291
            this.publicURL = publicURL;
 
1292
        }
 
1293
 
 
1294
        /** Starts parsing document, that can be localized by means of uri parameter
 
1295
         * @param validate
 
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);
 
1303
            parser.parse(uri);
 
1304
        }
 
1305
 
 
1306
        /** Starts parsing document - if you have document`s InputStream
 
1307
         * @param validate
 
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);
 
1317
        }
 
1318
 
 
1319
        private XMLReader getParser(boolean validate)
 
1320
        throws SAXException, ParserConfigurationException, FactoryConfigurationError {
 
1321
            XMLReader parser = XMLUtil.createXMLReader(validate);
 
1322
 
 
1323
            // create document handler and register it
 
1324
            parser.setEntityResolver(this);
 
1325
            parser.setContentHandler(this);
 
1326
            parser.setErrorHandler(this);
 
1327
 
 
1328
            return parser;
 
1329
        }
 
1330
 
 
1331
        public void error(SAXParseException exception)
 
1332
        throws SAXException {
 
1333
            throw exception;
 
1334
        }
 
1335
 
 
1336
        public void warning(SAXParseException exception)
 
1337
        throws SAXException {
 
1338
            throw exception;
 
1339
        }
 
1340
 
 
1341
        public void fatalError(SAXParseException exception)
 
1342
        throws SAXException {
 
1343
            throw exception;
 
1344
        }
 
1345
 
 
1346
        public void startElement(String uri, String lname, String name, Attributes attrs)
 
1347
        throws SAXException {
 
1348
            tagInProcess = name = name.trim();
 
1349
 
 
1350
            for (int i = 0; i < elmKeyService.length; i++) {
 
1351
                if (elmKeyService[i].isMyTag(name) != -1) {
 
1352
                    elmKeyService[i].startElement(name, attrs);
 
1353
 
 
1354
                    return;
 
1355
                }
 
1356
            }
 
1357
 
 
1358
            throw new SAXException(NbBundle.getMessage(DefaultAttributes.class, "XML_UnknownElement") + " " + name); // NOI18N
 
1359
        }
 
1360
 
 
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());
 
1365
 
 
1366
                    return;
 
1367
                }
 
1368
            }
 
1369
 
 
1370
            throw new SAXException(NbBundle.getMessage(DefaultAttributes.class, "XML_UnknownElement") + " " + name); // NOI18N
 
1371
        }
 
1372
 
 
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);
 
1378
 
 
1379
                    return;
 
1380
                }
 
1381
            }
 
1382
 
 
1383
            throw new SAXException(
 
1384
                NbBundle.getMessage(DefaultAttributes.class, "XML_UnknownElement") + " " + tagInProcess
 
1385
            ); // NOI18N
 
1386
        }
 
1387
 
 
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);
 
1392
            }
 
1393
 
 
1394
            return new InputSource(sid);
 
1395
        }
 
1396
    }
 
1397
}