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

« back to all changes in this revision

Viewing changes to openide/fs/src/org/openide/filesystems/XMLMapAttr.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
package org.openide.filesystems;
 
42
 
 
43
import org.openide.util.SharedClassObject;
 
44
import org.openide.util.Utilities;
 
45
import org.openide.util.io.NbMarshalledObject;
 
46
import org.openide.util.io.NbObjectInputStream;
 
47
 
 
48
import java.io.*;
 
49
 
 
50
import java.lang.reflect.*;
 
51
 
 
52
import java.net.URL;
 
53
 
 
54
import java.util.*;
 
55
import org.openide.util.Exceptions;
 
56
 
 
57
 
 
58
/**
 
59
 *Holds in Map attributes: Map(String attrName,XMLMapAttr.Attr attribute). This map holds all atributes for one FileObject.
 
60
 *<BR><BR>
 
61
 *<H3>Detailed description</H3>
 
62
 * Each file object (file or folder element) can have 0..* attributes.<BR> <BR>
 
63
 * Each file object <I>atrribute</I> (attribute is here name of element) must have two attributes (here XML attribute).<BR>
 
64
 * <OL>
 
65
 * <LI>First attribute name is <I>id</I> , which is mandatory and value of this
 
66
 * attribute serve as identifier to distinguish many attributes for one file object.
 
67
 * Name of attribute can contain prefix <code>transient:</code>. Transient means that such
 
68
 * marked attribute won`t be copied together with FileObject. Be aware that for:
 
69
 * fo.setAttribute("transient:foo", "bar") is true that fo.getAttribute("foo").equals("bar")
 
70
 * <LI> Second attribute is also mandatory, but you can choose such attribute name and
 
71
 * attribute value, which correspond to desirable data type
 
72
 * (e.g. <I>stringValue</I>, <I>boolValue</I> etc.).
 
73
 * </OL>
 
74
 * Desirable data type can be one of primitive data types or object data types.
 
75
 * <BR>
 
76
 * <BR>
 
77
 * Moreover value of attribute can be passed:
 
78
 * <OL>
 
79
 * <LI><I>statically</I> - means that you would be able to use literal of primitive data types (e.g. <I>stringvalue</I>="This is a literal",<I>boolvalue</I>="true" etc.).
 
80
 * If you want statically create instance of object data type you can use <I>serialValue</I>, which value is serialized byte stream (e.g.: <I>serialvalue</I>="092A54....").
 
81
 * This should ensure back compatibility.
 
82
 * <LI><I>dynamically</I> -means that instead of constant value (literal), you pass name of class including name of method, which will be used for dynamic creation of desirable object
 
83
 * (e.g.: <I>methodvalue</I>="org.openide.mypackage.MyClass.myMethod"). For dynamic creation of primitive data types could be used methods that return wrapper objects.
 
84
 * Implemetation of interface Attr will pass to method <I>myMethod</I> two parameters FileObject (file object which maintain this atrribute) and String (name of this attribute).
 
85
 * So here is sugestion of declaration of such method: <I>public static Object myMethod(FileObject myFo,String myName)</I>.
 
86
 *
 
87
 * <A NAME="primitive"><H4>Primitive data types</H4>
 
88
 * </A>
 
89
 * Here is sugested list of attribute names for primitive data types
 
90
 * (I expect that from the name is obvious which type of value is expected):
 
91
 * <OL>
 
92
 * <I>
 
93
 * <LI> bytevalue
 
94
 * <LI> shortvalue
 
95
 * <LI> intvalue
 
96
 * <LI> longvalue
 
97
 * <LI> floatvalue
 
98
 * <LI> doublevalue
 
99
 * <LI> boolvalue
 
100
 * <LI> charvalue
 
101
 * </I>
 
102
 * </OL>
 
103
 * <BR><BR>
 
104
 *<A NAME="object"><H4>Object data types</H4></A>
 
105
 * <OL>
 
106
 * <LI> <I>methodvalue</I> - dynamic creation (for primitive data could be returned wrapper objects)
 
107
  <LI> <I>newvalue</I> - newInstance is called
 
108
 * <LI> <I>serialValue</I> - static creation
 
109
 *
 
110
 * </OL>
 
111
  <BR><BR>
 
112
 * Attributes are stored in xml file, then there must be used encoding for not permitted
 
113
 * chars.  There are used Java-style <code>&#92;uXXXX</code> Unicode escapes for ISO control characters and
 
114
 * minimal set of character entities <code>&lt;</code>, <code>&amp;</code>, <code>'</code>
 
115
 * and <code>"</code>.
 
116
 *
 
117
 * @author rmatous
 
118
 */
 
119
@SuppressWarnings("unchecked")
 
120
final class XMLMapAttr implements Map {
 
121
    Map/*<String,Attr>*/ map;
 
122
 
 
123
    /** Creates new XMLMapAttr and delegetaor is instanced */
 
124
    public XMLMapAttr() {
 
125
        this.map = new HashMap(5);
 
126
    }
 
127
 
 
128
    static Attr createAttributeAndDecode(String key, String value) {
 
129
        if (Attr.isValid(key) == Attr.isValid("stringvalue")) { // NOI18N
 
130
            value = Attr.decode(value);
 
131
        }
 
132
 
 
133
        return new Attr(key, value);
 
134
    }
 
135
 
 
136
    static Attr createAttribute(int index, String value) {
 
137
        return new Attr(index, value);
 
138
    }
 
139
 
 
140
    /** According to name of attribute returns attribute as object
 
141
    * @param p1 is name of attribute
 
142
    * @return attribute, which is hold in XMLMapAttr.Attr or null if such attribute doesn`t exist or isn`t able to construct form String representation
 
143
    */
 
144
    public Object get(final Object p1) {
 
145
        Object obj;
 
146
 
 
147
        try {
 
148
            obj = getAttribute(p1);
 
149
        } catch (Exception e) {
 
150
            obj = null;
 
151
            ExternalUtil.exception(e);
 
152
        }
 
153
 
 
154
        return obj;
 
155
    }
 
156
 
 
157
    /** According to name of attribute returns attribute as object
 
158
     * @param params has sense only for methodvalue invocation; and only 2 parametres will be used
 
159
     * @return attribute, which is hold in XMLMapAttr.Attr or null if such attribute doesn`t exist or isn`t able to construct form String representation
 
160
     */
 
161
    public Object get(final Object p1, Object[] params) {
 
162
        Object obj;
 
163
 
 
164
        try {
 
165
            obj = getAttribute(p1, params);
 
166
        } catch (Exception e) {
 
167
            obj = null;
 
168
            ExternalUtil.exception(e);
 
169
        }
 
170
 
 
171
        return obj;
 
172
    }
 
173
 
 
174
    /** implementation of Map.get. But fires Exception to have chance in
 
175
     * DefaultAttributes to catch and annotate*/
 
176
    Object getAttribute(Object attrName) throws Exception {
 
177
        return getAttribute(attrName, null);
 
178
    }
 
179
 
 
180
    private Object getAttribute(Object attrName, Object[] params)
 
181
    throws Exception {
 
182
        Attr attr;
 
183
        String origAttrName = (String) attrName;
 
184
        Object[] keyValuePair = ModifiedAttribute.translateInto((String) attrName, null);
 
185
        attrName = (String) keyValuePair[0];
 
186
 
 
187
        synchronized (this) {
 
188
            attr = (Attr) map.get(attrName);
 
189
        }
 
190
 
 
191
        Object retVal = null;
 
192
 
 
193
        try {
 
194
            retVal = (attr == null) ? attr : attr.get(params);
 
195
        } catch (Exception e) {
 
196
            ExternalUtil.annotate(e, "attrName = " + attrName); //NOI18N                                                 
 
197
            throw e;
 
198
        }
 
199
 
 
200
        if (retVal instanceof ModifiedAttribute) {
 
201
            Object res = ((ModifiedAttribute) retVal).getValue(origAttrName);
 
202
 
 
203
            if (res instanceof Attr) {
 
204
                return ((Attr) res).get(params);
 
205
            } else {
 
206
                return res;
 
207
            }
 
208
        }
 
209
 
 
210
        return retVal;
 
211
    }
 
212
 
 
213
    /**
 
214
     * @param p1 is name of attribute
 
215
     * @param p2 is attribute as object
 
216
     * @return previous value associated with specified key, or null if there was no mapping for key.
 
217
     * A null return can also indicate that the HashMap previously associated null with the specified key.
 
218
     */
 
219
    public synchronized Object put(final Object p1, final Object p2) {
 
220
        return put(p1, p2, true);
 
221
    }
 
222
 
 
223
    synchronized Object put(final Object p1, final Object p2, boolean decode) {
 
224
        if ((p1 == null) || !(p1 instanceof String)) {
 
225
            return null;
 
226
        }
 
227
 
 
228
        Object[] keyValuePair = ModifiedAttribute.translateInto((String) p1, p2);
 
229
        String key = (String) keyValuePair[0];
 
230
        Object value = keyValuePair[1];
 
231
        Object toStore = ((value == null) || value instanceof Attr) ? value : new Attr(value);
 
232
 
 
233
        if (decode) {
 
234
            key = Attr.decode(key).intern();
 
235
        }
 
236
 
 
237
        return map.put(key, toStore);
 
238
    }
 
239
 
 
240
    /**
 
241
     * Writes heading to XML file
 
242
     * @param pw where to write
 
243
     */
 
244
    public static void writeHeading(PrintWriter pw) {
 
245
        pw.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); // NOI18N
 
246
        pw.println(
 
247
            "<!DOCTYPE attributes PUBLIC \"-//NetBeans//DTD DefaultAttributes 1.0//EN\" \"http://www.netbeans.org/dtds/attributes-1_0.dtd\">"
 
248
        ); //NOI18N
 
249
        pw.println("<attributes version=\"1.0\">"); // NOI18N
 
250
    }
 
251
 
 
252
    /**
 
253
     * Writes ending to XML file
 
254
     * @param pw where to write
 
255
     */
 
256
    public static void writeEnding(PrintWriter pw) {
 
257
        pw.println("</attributes>"); // NOI18N        
 
258
    }
 
259
 
 
260
    /**
 
261
     * Writes all attributes for one FileObject with fileName
 
262
     * @param pw where to write
 
263
     * @param fileName
 
264
     * @param blockPrefix is prefix which is used before each line
 
265
     */
 
266
    public synchronized void write(PrintWriter pw, final String fileName, String blockPrefix) {
 
267
        boolean isHeadingWr = false;
 
268
 
 
269
        if (isEmpty()) {
 
270
            return;
 
271
        }
 
272
 
 
273
        //pw.println(blockPrefix+"<fileobject name=\""+fileName+"\">");// NOI18N
 
274
        SortedSet<String> attrNames = new TreeSet<String>();
 
275
        Iterator entryIter = map.entrySet().iterator();
 
276
 
 
277
        while (entryIter.hasNext()) {
 
278
            Map.Entry entry = (Map.Entry) entryIter.next();
 
279
 
 
280
            String attrName = (String) entry.getKey();
 
281
            Attr attr = (Attr) entry.getValue();
 
282
 
 
283
            if ((attrName == null) || (attr == null) || (attrName.length() == 0) || (attr.isValid() == -1)) {
 
284
                if ((attrName != null) && (attrName.length() != 0) && ((attr == null) || (attr.isValid() == -1))) {
 
285
                    entryIter.remove();
 
286
                }
 
287
 
 
288
                continue;
 
289
            }
 
290
 
 
291
            attrNames.add(attrName);
 
292
        }
 
293
 
 
294
        entryIter = attrNames.iterator();
 
295
 
 
296
        while (entryIter.hasNext()) {
 
297
            String attrName = (String) entryIter.next();
 
298
            Attr attr = (Attr) map.get(attrName);
 
299
 
 
300
            if (attr != null) {
 
301
                attr.transformMe();
 
302
            }
 
303
 
 
304
            if (!isHeadingWr) {
 
305
                isHeadingWr = true;
 
306
 
 
307
                String quotedFileName = fileName;
 
308
 
 
309
                try {
 
310
                    quotedFileName = org.openide.xml.XMLUtil.toAttributeValue(fileName);
 
311
                } catch (IOException ignore) {
 
312
                }
 
313
 
 
314
                pw.println(blockPrefix + "<fileobject name=\"" + quotedFileName + "\">"); // NOI18N                
 
315
            }
 
316
 
 
317
            pw.println(
 
318
                blockPrefix + blockPrefix + "<attr name=\"" + attr.getAttrNameForPrint(attrName) + "\" " +
 
319
                attr.getKeyForPrint() + "=\"" + attr.getValueForPrint() + "\"/>"
 
320
            ); // NOI18N
 
321
            attr.maybeAddSerValueComment(pw, blockPrefix + blockPrefix);
 
322
        }
 
323
 
 
324
        if (isHeadingWr) {
 
325
            pw.println(blockPrefix + "</fileobject>"); // NOI18N        
 
326
        }
 
327
    }
 
328
 
 
329
    public synchronized void clear() {
 
330
        map.clear();
 
331
    }
 
332
 
 
333
    public synchronized Object remove(Object p1) {
 
334
        return map.remove(p1);
 
335
    }
 
336
 
 
337
    public synchronized boolean containsValue(Object p1) {
 
338
        return map.containsValue(p1);
 
339
    }
 
340
 
 
341
    public synchronized int hashCode() {
 
342
        return map.hashCode();
 
343
    }
 
344
 
 
345
    public synchronized java.util.Set<String> keySet() {
 
346
        return map.keySet();
 
347
    }
 
348
 
 
349
    public synchronized java.util.Collection values() {
 
350
        return map.values();
 
351
    }
 
352
 
 
353
    // XXX this is wrong - values not translated
 
354
    public synchronized java.util.Set entrySet() {
 
355
        return map.entrySet();
 
356
    }
 
357
 
 
358
    public synchronized void putAll(java.util.Map p1) {
 
359
        map.putAll(p1);
 
360
    }
 
361
 
 
362
    public synchronized boolean containsKey(Object p1) {
 
363
        return map.containsKey(p1);
 
364
    }
 
365
 
 
366
    public synchronized boolean isEmpty() {
 
367
        return map.isEmpty();
 
368
    }
 
369
 
 
370
    public synchronized boolean equals(Object p1) {
 
371
        return map.equals(p1);
 
372
    }
 
373
 
 
374
    public synchronized int size() {
 
375
        return map.size();
 
376
    }
 
377
 
 
378
    /**
 
379
     * Holds textual representation of one attribute. And on request construct new instance of
 
380
     * attribute a returns it as Object. Each Attr contains pair key and value. Key is type. Value is real value (in textual form) of this type.
 
381
     * Detailed describtion is in <A HREF="XMLMapAttr.html">XMLMapAttr<A>
 
382
     */
 
383
    final static class Attr extends java.lang.Object {
 
384
        // static final long serialVersionUID = -62733358015297232L;
 
385
        private static final String[] ALLOWED_ATTR_KEYS = {
 
386
            "bytevalue", "shortvalue", "intvalue", "longvalue", "floatvalue", "doublevalue", "boolvalue", "charvalue",
 
387
            "stringvalue", "methodvalue", "serialvalue", "urlvalue", "newvalue"
 
388
        }; // NOI18N
 
389
        private String value;
 
390
        private int keyIndex;
 
391
        private Object obj; //back compatibility
 
392
 
 
393
        private Attr(Object obj) {
 
394
            this.obj = obj;
 
395
        }
 
396
 
 
397
        private Attr(int index, String value) {
 
398
            keyIndex = index;
 
399
            this.value = (value != null) ? value.intern() : null;
 
400
        }
 
401
 
 
402
        /**
 
403
         * @param key One of the possible keys:
 
404
         * "bytevalue","shortvalue","intvalue","longvalue","floatvalue","doublevalue","boolvalue","charvalue","stringvalue","methodvalue","serialvalue","urlvalue"
 
405
         * @param value Corresponding value to key in textual form.
 
406
         */
 
407
        private Attr(String key, String value) {
 
408
            keyIndex = isValid(key);
 
409
            this.value = value.intern();
 
410
        }
 
411
 
 
412
        /**
 
413
         * @return array of Strings. Each String is textual form of allowed type of attribute - textual form of key
 
414
         */
 
415
        static String[] getAttrTypes() {
 
416
            return ALLOWED_ATTR_KEYS;
 
417
        }
 
418
 
 
419
        /**
 
420
         * Checks if key is valid and sets key and value
 
421
         * @param key Key of attribute. Defines type of attribute in textual form.
 
422
         * @param value Value of attribute. Defines value of attribute as literal or  HEX expression of serialization.
 
423
         */
 
424
        private final void putEntry(String key, String value) {
 
425
            this.keyIndex = isValid(key);
 
426
            this.value = value.intern();
 
427
        }
 
428
 
 
429
        /**
 
430
         *  added for future use - convert NbMarshalledObject to primitive data types and other supported types (if possible)
 
431
         */
 
432
        static Object unMarshallObjectRecursively(Object mo) {
 
433
            Object o = mo;
 
434
 
 
435
            while (o instanceof NbMarshalledObject) {
 
436
                try {
 
437
                    o = ((NbMarshalledObject) o).get();
 
438
                } catch (IOException e) {
 
439
                    ExternalUtil.exception(e);
 
440
 
 
441
                    return mo;
 
442
                } catch (ClassNotFoundException e) {
 
443
                    ExternalUtil.exception(e);
 
444
 
 
445
                    return mo;
 
446
                }
 
447
            }
 
448
 
 
449
            return (o == null) ? mo : o;
 
450
        }
 
451
 
 
452
        /**Method for back compatibility; called in write*/
 
453
        private void transformMe() {
 
454
            int objType;
 
455
 
 
456
            if (obj == null) {
 
457
                return;
 
458
            }
 
459
 
 
460
            Object unObj = unMarshallObjectRecursively(obj);
 
461
 
 
462
            if (unObj != null) {
 
463
                if ((objType = XMLMapAttr.Attr.distinguishObject(unObj)) != XMLMapAttr.Attr.isValid("SERIALVALUE")) { // NOI18N
 
464
                    obj = null;
 
465
                    putEntry(ALLOWED_ATTR_KEYS[objType], unObj.toString());
 
466
                } else {
 
467
                    String newValue;
 
468
 
 
469
                    try {
 
470
                        newValue = encodeValue(unObj);
 
471
                    } catch (IOException iox) {
 
472
                        return;
 
473
                    }
 
474
 
 
475
                    obj = null;
 
476
                    putEntry(ALLOWED_ATTR_KEYS[objType], newValue);
 
477
                }
 
478
            }
 
479
        }
 
480
 
 
481
        /**
 
482
         *  added for future use - convert NbMarshalledObject to primitive data types and other supported types (if possible)
 
483
         */
 
484
        static int distinguishObject(Object o) {
 
485
            if (o instanceof Byte) {
 
486
                return isValid("BYTEVALUE"); // NOI18N
 
487
            }
 
488
 
 
489
            if (o instanceof Short) {
 
490
                return isValid("SHORTVALUE"); // NOI18N        
 
491
            }
 
492
 
 
493
            if (o instanceof Integer) {
 
494
                return isValid("INTVALUE"); // NOI18N 
 
495
            }
 
496
 
 
497
            if (o instanceof Long) {
 
498
                return isValid("LONGVALUE"); // NOI18N        
 
499
            }
 
500
 
 
501
            if (o instanceof Float) {
 
502
                return isValid("FLOATVALUE"); // NOI18N
 
503
            }
 
504
 
 
505
            if (o instanceof Double) {
 
506
                return isValid("DOUBLEVALUE"); // NOI18N
 
507
            }
 
508
 
 
509
            if (o instanceof Boolean) {
 
510
                return isValid("BOOLVALUE"); // NOI18N
 
511
            }
 
512
 
 
513
            if (o instanceof Character) {
 
514
                return isValid("CHARVALUE"); // NOI18N
 
515
            }
 
516
 
 
517
            if (o instanceof String) {
 
518
                return isValid("STRINGVALUE"); // NOI18N         
 
519
            }
 
520
 
 
521
            if (o instanceof URL) {
 
522
                return isValid("URLVALUE"); // NOI18N            
 
523
            }
 
524
 
 
525
            return isValid("SERIALVALUE"); // NOI18N
 
526
        }
 
527
 
 
528
        static String encode(String inStr) {
 
529
            try {
 
530
                inStr = org.openide.xml.XMLUtil.toAttributeValue(inStr);
 
531
            } catch (Exception ignore) {
 
532
            }
 
533
 
 
534
            StringBuffer outStr = new StringBuffer(6 * inStr.length());
 
535
 
 
536
            for (int i = 0; i < inStr.length(); i++) {
 
537
                if (Character.isISOControl(inStr.charAt(i)) || isEncodedChar(i, inStr)) {
 
538
                    outStr.append(encodeChar(inStr.charAt(i)));
 
539
 
 
540
                    continue;
 
541
                }
 
542
 
 
543
                outStr.append(inStr.charAt(i));
 
544
            }
 
545
 
 
546
            return outStr.toString();
 
547
        }
 
548
 
 
549
        static String encodeChar(char ch) {
 
550
            String encChar = Integer.toString((int) ch, 16);
 
551
 
 
552
            return "\\u" + "0000".substring(0, "0000".length() - encChar.length()).concat(encChar); // NOI18N
 
553
        }
 
554
 
 
555
        static String decode(final String inStr) {
 
556
            StringBuffer outStr = new StringBuffer(inStr.length());
 
557
 
 
558
            try {
 
559
                for (int i = 0; i < inStr.length(); i++) {
 
560
                    if (isEncodedChar(i, inStr)) {
 
561
                        String decChar = inStr.substring(i + 2, i + 6);
 
562
                        outStr.append((char) Integer.parseInt(decChar, 16));
 
563
                        i += 5;
 
564
                    } else {
 
565
                        outStr.append(inStr.charAt(i));
 
566
                    }
 
567
                }
 
568
            } catch (NumberFormatException e) {
 
569
                Exceptions.printStackTrace(e);
 
570
 
 
571
                return inStr;
 
572
            }
 
573
 
 
574
            return outStr.toString();
 
575
        }
 
576
 
 
577
        private static boolean isEncodedChar(final int currentPosition, final String inStr) {
 
578
            boolean isEncodedChar = (currentPosition + 5) < inStr.length();
 
579
 
 
580
            if (isEncodedChar) {
 
581
                isEncodedChar &= ((inStr.charAt(currentPosition) == '\\') &&
 
582
                (inStr.charAt(currentPosition + 1) == 'u'));
 
583
 
 
584
                for (int i = currentPosition + 2; isEncodedChar && (i < (currentPosition + 6)); i++) {
 
585
                    char c = inStr.charAt(i);
 
586
                    isEncodedChar &= (Character.digit(c, 16) != -1);
 
587
                }
 
588
            }
 
589
 
 
590
            return isEncodedChar;
 
591
        }
 
592
 
 
593
        /**
 
594
        * Constructs new attribute as Object. Used for static creation from literal or serialValue.
 
595
        * @return new attribute as Object
 
596
        */
 
597
        private Object get() throws Exception {
 
598
            return getObject(null); //getObject is ready to aobtain null
 
599
        }
 
600
 
 
601
        /**
 
602
         * Constructs new attribute as Object. Used for dynamic creation: methodvalue .
 
603
         * @param objs has sense only for methodvalue invocation; and only 2 parametres will be used
 
604
         *@return new attribute as Object
 
605
         */
 
606
        private Object get(Object[] objs) throws Exception {
 
607
            return getObject(objs);
 
608
        }
 
609
 
 
610
        /**
 
611
         * @return key. Key expresses type of this attribute (in textual form) or "" if internal error.
 
612
         */
 
613
        final String getKey() {
 
614
            String[] keyArray = getAttrTypes();
 
615
 
 
616
            if (obj != null) {
 
617
                return "serialvalue"; //back compatibility // NOI18N
 
618
            }
 
619
 
 
620
            if (isValid() == -1) {
 
621
                return ""; // NOI18N
 
622
            }
 
623
 
 
624
            return keyArray[keyIndex];
 
625
        }
 
626
 
 
627
        /**
 
628
         * @return value in textual format or "" if internal error.
 
629
         */
 
630
        final String getValue() {
 
631
            if (obj != null) {
 
632
                getValue(obj);
 
633
            }
 
634
 
 
635
            return (value != null) ? value : ""; // NOI18N
 
636
        }
 
637
 
 
638
        static final String getValue(Object obj) {
 
639
            try {
 
640
                return encodeValue(obj); //back compatibility
 
641
            } catch (IOException ioe) {
 
642
                return ""; // NOI18N
 
643
            }
 
644
        }
 
645
 
 
646
        final String getValueForPrint() {
 
647
            if (obj != null) {
 
648
                Attr modifAttr = null;
 
649
 
 
650
                if (obj instanceof ModifiedAttribute) {
 
651
                    modifAttr = (Attr) ((ModifiedAttribute) obj).getValue();
 
652
                }
 
653
 
 
654
                return (modifAttr != null) ? encode(modifAttr.getValue()) : encode(getValue());
 
655
            }
 
656
 
 
657
            return (value != null) ? encode(value) : ""; // NOI18N
 
658
        }
 
659
 
 
660
        final String getKeyForPrint() {
 
661
            if ((obj != null) && obj instanceof ModifiedAttribute) {
 
662
                Attr modifAttr = (Attr) ((ModifiedAttribute) obj).getValue();
 
663
                int keyIdx = Attr.isValid("SERIALVALUE"); //NOI18N
 
664
 
 
665
                if (modifAttr != null) {
 
666
                    keyIdx = distinguishObject(modifAttr.getValue());
 
667
                }
 
668
 
 
669
                String[] keyArray = getAttrTypes();
 
670
 
 
671
                return keyArray[keyIdx];
 
672
            }
 
673
 
 
674
            return getKey();
 
675
        }
 
676
 
 
677
        final String getAttrNameForPrint(String attrName) {
 
678
            if ((obj != null) && obj instanceof ModifiedAttribute) {
 
679
                Object[] retVal = ModifiedAttribute.revert(attrName, obj);
 
680
 
 
681
                return encode((String) retVal[0]);
 
682
            }
 
683
 
 
684
            return encode(attrName);
 
685
        }
 
686
 
 
687
        final void maybeAddSerValueComment(PrintWriter pw, String indent) {
 
688
            if (obj != null) {
 
689
                Object modifObj = null;
 
690
 
 
691
                if (obj instanceof ModifiedAttribute) {
 
692
                    modifObj = ((Attr) ((ModifiedAttribute) obj).getValue()).getValue();
 
693
 
 
694
                    if (distinguishObject(modifObj) != Attr.isValid("SERIALVALUE")) { //NOI18N
 
695
 
 
696
                        return;
 
697
                    }
 
698
                }
 
699
 
 
700
                // Important for debugging to know what this stuff really is.
 
701
                // Note this comment is only written to disk when the attr is
 
702
                // first saved; after that successive saves will just know the
 
703
                // ser value and will not print the comment. So look at .nbattrs
 
704
                // immediately after setting something serialized. --jglick
 
705
                pw.print(indent);
 
706
                pw.print("<!-- "); // NOI18N
 
707
 
 
708
                String s = (modifObj != null) ? modifObj.toString() : obj.toString();
 
709
 
 
710
                if (s.indexOf("--") != -1) { // NOI18N
 
711
 
 
712
                    // XML comment no-no.
 
713
                    s = s.replace('-', '_'); // NOI18N
 
714
                }
 
715
 
 
716
                pw.print(s);
 
717
                pw.println(" -->"); // NOI18N
 
718
            }
 
719
        }
 
720
 
 
721
        /**
 
722
         * Creates serialized object, which was encoded in HEX format
 
723
         * @param value Encoded serialized object in HEX format
 
724
         * @return Created object from encoded HEX format
 
725
         * @throws IOException
 
726
         */
 
727
        static Object decodeValue(String value) throws IOException {
 
728
            if ((value == null) || (value.length() == 0)) {
 
729
                return null;
 
730
            }
 
731
 
 
732
            byte[] bytes = new byte[value.length() / 2];
 
733
            int tempI;
 
734
            int count = 0;
 
735
 
 
736
            for (int i = 0; i < value.length(); i += 2) {
 
737
                try {
 
738
                    tempI = Integer.parseInt(value.substring(i, i + 2), 16);
 
739
 
 
740
                    if (tempI > 127) {
 
741
                        tempI -= 256;
 
742
                    }
 
743
 
 
744
                    bytes[count++] = (byte) tempI;
 
745
                } catch (NumberFormatException e) {
 
746
                    throw (IOException) ExternalUtil.copyAnnotation(new IOException(), e);
 
747
                }
 
748
            }
 
749
 
 
750
            ByteArrayInputStream bis = new ByteArrayInputStream(bytes, 0, count);
 
751
 
 
752
            try {
 
753
                ObjectInputStream ois = new NbObjectInputStream(bis);
 
754
                Object ret = ois.readObject();
 
755
 
 
756
                return ret;
 
757
            } catch (Exception e) {
 
758
                throw (IOException) ExternalUtil.copyAnnotation(new IOException(), e);
 
759
            }
 
760
 
 
761
            /*unreachable code*/
 
762
 
 
763
            //throw new InternalError ();
 
764
        }
 
765
 
 
766
        /**
 
767
         * Encodes Object into String encoded in HEX format
 
768
         * @param value Object, which will be encoded
 
769
         * @return  serialized Object in String encoded in HEX format
 
770
         * @throws IOException
 
771
         */
 
772
        static String encodeValue(Object value) throws IOException {
 
773
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
 
774
 
 
775
            try {
 
776
                ObjectOutputStream oos = new ObjectOutputStream(bos);
 
777
                oos.writeObject(value);
 
778
                oos.close();
 
779
            } catch (Exception e) {
 
780
                throw (IOException) ExternalUtil.copyAnnotation(new IOException(), e);
 
781
            }
 
782
 
 
783
            byte[] bArray = bos.toByteArray();
 
784
            StringBuffer strBuff = new StringBuffer(bArray.length * 2);
 
785
 
 
786
            for (int i = 0; i < bArray.length; i++) {
 
787
                if ((bArray[i] < 16) && (bArray[i] >= 0)) {
 
788
                    strBuff.append("0"); // NOI18N
 
789
                }
 
790
 
 
791
                strBuff.append(Integer.toHexString((bArray[i] < 0) ? (bArray[i] + 256) : bArray[i]));
 
792
            }
 
793
 
 
794
            return strBuff.toString();
 
795
        }
 
796
 
 
797
        /**
 
798
         * Encodes Object into String encoded in HEX format
 
799
         * @param params Array (2 length) of objects ( Object[] o = {fo,name}).  Attribute is assigned to some fo-FileObject and has its name-String.
 
800
         * params can be null.
 
801
         * @return   Object or null
 
802
         */
 
803
        private Object getObject(Object[] params) throws Exception {
 
804
            int index;
 
805
 
 
806
            if (obj != null) {
 
807
                return obj; //back compatibility
 
808
            }
 
809
 
 
810
            if ((index = isValid()) != -1) {
 
811
                try {
 
812
                    switch (index) {
 
813
                    case 0:
 
814
                        return new Byte(value);
 
815
 
 
816
                    case 1:
 
817
                        return new Short(value);
 
818
 
 
819
                    case 2:
 
820
                        return new Integer(value); //(objI);
 
821
 
 
822
                    case 3:
 
823
                        return new Long(value);
 
824
 
 
825
                    case 4:
 
826
                        return new Float(value);
 
827
 
 
828
                    case 5:
 
829
                        return new Double(value);
 
830
 
 
831
                    case 6:
 
832
                        return Boolean.valueOf(value);
 
833
 
 
834
                    case 7:
 
835
 
 
836
                        if (value.trim().length() != 1) {
 
837
                            break;
 
838
                        }
 
839
 
 
840
                        return new Character(value.charAt(0));
 
841
 
 
842
                    case 8:
 
843
                        return value;
 
844
 
 
845
                    case 9:
 
846
                        return methodValue(value, params);
 
847
 
 
848
                    case 10:
 
849
                        return decodeValue(value);
 
850
 
 
851
                    case 11:
 
852
                        return new URL(value);
 
853
 
 
854
                    case 12:
 
855
 
 
856
                        // special support for singletons
 
857
                        Class cls = ExternalUtil.findClass(Utilities.translate(value));
 
858
 
 
859
                        if (SharedClassObject.class.isAssignableFrom(cls)) {
 
860
                            return SharedClassObject.findObject(cls, true);
 
861
                        } else {
 
862
                            return cls.newInstance();
 
863
                        }
 
864
                    }
 
865
                } catch (Exception exc) {
 
866
                    ExternalUtil.annotate(exc, "value = " + value); //NOI18N
 
867
                    throw exc;
 
868
                } catch (LinkageError e) {
 
869
                    throw (ClassNotFoundException) ExternalUtil.annotate(new ClassNotFoundException(value), e);
 
870
                }
 
871
            }
 
872
 
 
873
            throw new InstantiationException(value);
 
874
        }
 
875
 
 
876
        /** Constructs new attribute as Object. Used for dynamic creation: methodvalue .
 
877
         * @param params only 2 parametres will be used
 
878
         * @return   Object or null
 
879
         */
 
880
        private final Object methodValue(String value, Object[] params)
 
881
        throws Exception {
 
882
            int sepIdx = value.lastIndexOf('.');
 
883
 
 
884
            if (sepIdx != -1) {
 
885
                String methodName = value.substring(sepIdx + 1);
 
886
                Class cls = ExternalUtil.findClass(value.substring(0, sepIdx));
 
887
                FileObject fo = null;
 
888
                String attrName = null;
 
889
 
 
890
                for (int i = 0; i < params.length; i++) {
 
891
                    if ((fo == null) && params[i] instanceof FileObject) {
 
892
                        fo = (FileObject) params[i];
 
893
                    }
 
894
 
 
895
                    if ((attrName == null) && params[i] instanceof String) {
 
896
                        attrName = (String) params[i];
 
897
                    }
 
898
                }
 
899
 
 
900
                Object[] paramArray = new Object[] {
 
901
                        new Class[] { FileObject.class, String.class }, new Class[] { String.class, FileObject.class },
 
902
                        new Class[] { FileObject.class }, new Class[] { String.class }, new Class[] {  },
 
903
                        new Class[] { Map.class, String.class }, new Class[] { Map.class },
 
904
                    };
 
905
 
 
906
                boolean both = ((fo != null) && (attrName != null));
 
907
                Object[] objectsList = new Object[7];
 
908
                objectsList[0] = (both) ? new Object[] { fo, attrName } : null;
 
909
                objectsList[1] = (both) ? new Object[] { attrName, fo } : null;
 
910
                objectsList[2] = (fo != null) ? new Object[] { fo } : null;
 
911
                objectsList[3] = (attrName != null) ? new Object[] { attrName } : null;
 
912
                objectsList[4] = new Object[] {  };
 
913
 
 
914
                Map fileMap = wrapToMap(fo);
 
915
                objectsList[5] = attrName != null ? new Object[] { fileMap, attrName } : null;
 
916
                objectsList[6] = new Object[] { fileMap };
 
917
 
 
918
                for (int i = 0; i < paramArray.length; i++) {
 
919
                    Object[] objArray = (Object[]) objectsList[i];
 
920
 
 
921
                    if (objArray == null) {
 
922
                        continue;
 
923
                    }
 
924
 
 
925
                    try {
 
926
                        Method method = cls.getDeclaredMethod(methodName, (Class[]) paramArray[i]);
 
927
 
 
928
                        if (method != null) {
 
929
                            method.setAccessible(true);
 
930
 
 
931
                            return method.invoke(null, objArray);
 
932
                        }
 
933
                    } catch (NoSuchMethodException nsmExc) {
 
934
                        continue;
 
935
                    }
 
936
                }
 
937
            }
 
938
 
 
939
            throw new InstantiationException(value);
 
940
        }
 
941
 
 
942
        static final Map wrapToMap(FileObject fo) {
 
943
            return fo == null ? Collections.EMPTY_MAP : new FileMap(fo);
 
944
        }
 
945
 
 
946
        /**
 
947
         * Checks if key is valid
 
948
         * @return Index to array of allowed keys or -1 which means error.
 
949
         */
 
950
        final int isValid() {
 
951
            String[] keyArray = getAttrTypes();
 
952
 
 
953
            if (obj != null) {
 
954
                return isValid("SERIALVALUE"); //back compatibility // NOI18N
 
955
            }
 
956
 
 
957
            if ((keyIndex >= keyArray.length) || (keyIndex < 0)) {
 
958
                return -1;
 
959
            }
 
960
 
 
961
            return keyIndex;
 
962
        }
 
963
 
 
964
        /**
 
965
         * Checks if key is valid
 
966
         * @return Index to array of allowed keys or -1 which means error.
 
967
         */
 
968
        final static int isValid(String key) {
 
969
            int index = -1;
 
970
            int i;
 
971
            String[] strArray = getAttrTypes();
 
972
            String trimmedKey = key.trim();
 
973
 
 
974
            for (i = 0; i < strArray.length; i++) {
 
975
                if (trimmedKey.equalsIgnoreCase(strArray[i]) == true) {
 
976
                    index = i;
 
977
 
 
978
                    break;
 
979
                }
 
980
            }
 
981
 
 
982
            return index;
 
983
        }
 
984
 
 
985
        public boolean equals(Object obj) {
 
986
            if (obj instanceof Attr) {
 
987
                Attr other = (Attr)obj;
 
988
                
 
989
                if (other.keyIndex != keyIndex) {
 
990
                    return false;
 
991
                }
 
992
                
 
993
                return other.value.equals(value);
 
994
            }
 
995
            return false;
 
996
        }
 
997
 
 
998
        public int hashCode() {
 
999
            return 743 + keyIndex << 8 + value.hashCode();
 
1000
        }
 
1001
    }
 
1002
 
 
1003
    /**
 
1004
     * Helper class for decorating attributes with modifiers.
 
1005
     * Object that is made persistent using setAttribute can contain also modifiers.
 
1006
     * This class is wrapper class that holds original object and its modifiers.
 
1007
     * Intended as replacer of original class in attributes.
 
1008
     * Currently exists only one modifier: tranisent.
 
1009
     * Transient modifier means that such attribute won`t be copied with FileObject.
 
1010
     */
 
1011
    static class ModifiedAttribute implements java.io.Serializable {
 
1012
        /** generated Serialized Version UID */
 
1013
        static final long serialVersionUID = 84214031923497718L;
 
1014
        private final static String[] fragments = new String[] { "transient:" }; //NOI18N
 
1015
        private int modifier = 0;
 
1016
        private Object origAttrValue = null;
 
1017
 
 
1018
        /** Creates a new instance of AttributeFactory */
 
1019
        private ModifiedAttribute(Object origAttrValue) {
 
1020
            this.origAttrValue = origAttrValue;
 
1021
        }
 
1022
 
 
1023
        /** This method looks for modifiers in attribute name (currently transient:).
 
1024
         *
 
1025
         * @param attrName original name of attribute
 
1026
         * @param value original value - can be null
 
1027
         * @return Object array with size 2.
 
1028
         * If there are no modifiers in attribute name, then is returned Object array with
 
1029
         * , where first is placed unchanged attribute name, and then uchanged value.
 
1030
         * If there are modifiers in attribute name, then as attribute name is returned
 
1031
         * stripped original attribute name (without modifiers) and ModifiedAttribute object,
 
1032
         * that wraps original object and also contain modifiers.
 
1033
         */
 
1034
        static Object[] translateInto(String attrName, Object value) {
 
1035
            String newAttrName = attrName;
 
1036
            Object newValue = value;
 
1037
            ModifiedAttribute attr = null;
 
1038
 
 
1039
            for (int i = 0; i < fragments.length; i++) {
 
1040
                String fragment = fragments[i];
 
1041
                int idx = newAttrName.indexOf(fragment);
 
1042
 
 
1043
                if (idx != -1) {
 
1044
                    /** fragment is cleared away */
 
1045
                    newAttrName = newAttrName.substring(0, idx) + newAttrName.substring(idx + fragment.length());
 
1046
 
 
1047
                    if (attr == null) {
 
1048
                        newValue = attr = new ModifiedAttribute(value);
 
1049
                    }
 
1050
 
 
1051
                    attr.modifier |= (1 << i); //set modifier
 
1052
                }
 
1053
            }
 
1054
 
 
1055
            return new Object[] { newAttrName, newValue };
 
1056
        }
 
1057
 
 
1058
        /**
 
1059
         * This method is opposite to method translateInto
 
1060
         */
 
1061
        static Object[] revert(String attrName, Object value) {
 
1062
            if (!(value instanceof ModifiedAttribute) || (value == null)) {
 
1063
                return new Object[] { attrName, value };
 
1064
            }
 
1065
 
 
1066
            ModifiedAttribute attr = (ModifiedAttribute) value;
 
1067
            String newAttrName = attrName;
 
1068
            Object newValue = attr;
 
1069
 
 
1070
            for (int i = 0; i < fragments.length; i++) {
 
1071
                String fragment = fragments[i];
 
1072
 
 
1073
                if (((attr.modifier & (1 << i)) != 0) && (fragment != null)) {
 
1074
                    /** fragment is cleared away */
 
1075
                    newAttrName = fragment + newAttrName;
 
1076
 
 
1077
                    if (newValue instanceof ModifiedAttribute) {
 
1078
                        newValue = attr.origAttrValue;
 
1079
                    }
 
1080
                }
 
1081
            }
 
1082
 
 
1083
            return new Object[] { newAttrName, newValue };
 
1084
        }
 
1085
 
 
1086
        /** ModifiedAttribute holds original value + modifiers. This method returns original value.
 
1087
         * @return If there are no modifiers in attribute name, then returns original value
 
1088
         * If there are  modifiers in attribute name, then returns current instance of
 
1089
         * ModifiedAttribute.
 
1090
         */
 
1091
        Object getValue(String attrName) {
 
1092
            for (int i = 0; i < fragments.length; i++) {
 
1093
                String fragment = fragments[i];
 
1094
                int idx = attrName.indexOf(fragment);
 
1095
 
 
1096
                if (idx != -1) {
 
1097
                    return this;
 
1098
                }
 
1099
            }
 
1100
 
 
1101
            return origAttrValue;
 
1102
        }
 
1103
 
 
1104
        /** ModifiedAttribute holds original value + modifiers. This method returns original value.
 
1105
         * @return then returns original value
 
1106
         */
 
1107
        Object getValue() {
 
1108
            return getValue(""); //NOI18N
 
1109
        }
 
1110
 
 
1111
        /**
 
1112
         * Decides if value stored in attributes is transient
 
1113
         * @param fo fileobject where attribute is looked for
 
1114
         * @param attrName  name of attribute
 
1115
         * @return true if transient
 
1116
         */
 
1117
        static boolean isTransient(FileObject fo, String attrName) {
 
1118
            Object value = fo.getAttribute(fragments[0] + attrName);
 
1119
 
 
1120
            if (value instanceof ModifiedAttribute) {
 
1121
                return ((((ModifiedAttribute) value).modifier & (1 << 0)) == 0) ? false : true;
 
1122
            }
 
1123
 
 
1124
            return false;
 
1125
        }
 
1126
    }
 
1127
    
 
1128
    private static final class FileMap extends AbstractMap<String,Object> {
 
1129
        private FileObject fo;
 
1130
        
 
1131
        private FileMap (FileObject fo) {
 
1132
            this.fo = fo;
 
1133
}
 
1134
        
 
1135
        public Set<Map.Entry<String,Object>> entrySet() {
 
1136
            return new AttrFileSet(fo);
 
1137
        }
 
1138
 
 
1139
        public Object get(String key) {
 
1140
            return fo.getAttribute(key);
 
1141
        }
 
1142
 
 
1143
        public Object remove(Object key) {
 
1144
            throw new UnsupportedOperationException();
 
1145
        }
 
1146
 
 
1147
        public Object put(String key, Object value) {
 
1148
            throw new UnsupportedOperationException();
 
1149
        }
 
1150
        
 
1151
    }
 
1152
    private static final class AttrFileSet extends AbstractSet<Map.Entry<String,Object>> {
 
1153
        private FileObject fo;
 
1154
        
 
1155
        private AttrFileSet(FileObject fo) {
 
1156
            this.fo = fo;
 
1157
        }
 
1158
        
 
1159
        public Iterator<Map.Entry<String, Object>> iterator() {
 
1160
            class Iter implements Iterator<Map.Entry<String, Object>> {
 
1161
                Enumeration<String> attrs = fo.getAttributes();
 
1162
                
 
1163
                public boolean hasNext() {
 
1164
                    return attrs.hasMoreElements();
 
1165
                }
 
1166
                
 
1167
                public Map.Entry<String, Object> next() {
 
1168
                    String s = attrs.nextElement();
 
1169
                    return new FOEntry(fo, s);
 
1170
                }
 
1171
 
 
1172
                public void remove() {
 
1173
                    throw new UnsupportedOperationException();
 
1174
                }
 
1175
            }
 
1176
            return new Iter();
 
1177
        }
 
1178
 
 
1179
        public int size() {
 
1180
            Enumeration<String> all = fo.getAttributes();
 
1181
            int cnt = 0;
 
1182
            while (all.hasMoreElements()) {
 
1183
                cnt++;
 
1184
                all.nextElement();
 
1185
            }
 
1186
            return cnt;
 
1187
        }
 
1188
 
 
1189
        public boolean remove(Object o) {
 
1190
            throw new UnsupportedOperationException();
 
1191
        }
 
1192
    } // end of AttrFileSet
 
1193
    
 
1194
    private static final class FOEntry implements Map.Entry<String, Object> {
 
1195
        private FileObject fo;
 
1196
        private String attr;
 
1197
        
 
1198
        private FOEntry(FileObject fo, String attr) {
 
1199
            this.fo = fo;
 
1200
            this.attr = attr;
 
1201
        }
 
1202
 
 
1203
        public String getKey() {
 
1204
            return attr;
 
1205
        }
 
1206
 
 
1207
        public Object getValue() {
 
1208
            return fo.getAttribute(attr);
 
1209
        }
 
1210
 
 
1211
        public Object setValue(Object value) {
 
1212
            throw new UnsupportedOperationException();
 
1213
        }
 
1214
    } // end of FOEntry
 
1215
}