2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
41
package org.openide.filesystems;
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;
50
import java.lang.reflect.*;
55
import org.openide.util.Exceptions;
59
*Holds in Map attributes: Map(String attrName,XMLMapAttr.Attr attribute). This map holds all atributes for one FileObject.
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>
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.).
74
* Desirable data type can be one of primitive data types or object data types.
77
* Moreover value of attribute can be passed:
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>.
87
* <A NAME="primitive"><H4>Primitive data types</H4>
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):
104
*<A NAME="object"><H4>Object data types</H4></A>
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
112
* Attributes are stored in xml file, then there must be used encoding for not permitted
113
* chars. There are used Java-style <code>\uXXXX</code> Unicode escapes for ISO control characters and
114
* minimal set of character entities <code><</code>, <code>&</code>, <code>'</code>
115
* and <code>"</code>.
119
@SuppressWarnings("unchecked")
120
final class XMLMapAttr implements Map {
121
Map/*<String,Attr>*/ map;
123
/** Creates new XMLMapAttr and delegetaor is instanced */
124
public XMLMapAttr() {
125
this.map = new HashMap(5);
128
static Attr createAttributeAndDecode(String key, String value) {
129
if (Attr.isValid(key) == Attr.isValid("stringvalue")) { // NOI18N
130
value = Attr.decode(value);
133
return new Attr(key, value);
136
static Attr createAttribute(int index, String value) {
137
return new Attr(index, value);
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
144
public Object get(final Object p1) {
148
obj = getAttribute(p1);
149
} catch (Exception e) {
151
ExternalUtil.exception(e);
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
161
public Object get(final Object p1, Object[] params) {
165
obj = getAttribute(p1, params);
166
} catch (Exception e) {
168
ExternalUtil.exception(e);
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);
180
private Object getAttribute(Object attrName, Object[] params)
183
String origAttrName = (String) attrName;
184
Object[] keyValuePair = ModifiedAttribute.translateInto((String) attrName, null);
185
attrName = (String) keyValuePair[0];
187
synchronized (this) {
188
attr = (Attr) map.get(attrName);
191
Object retVal = null;
194
retVal = (attr == null) ? attr : attr.get(params);
195
} catch (Exception e) {
196
ExternalUtil.annotate(e, "attrName = " + attrName); //NOI18N
200
if (retVal instanceof ModifiedAttribute) {
201
Object res = ((ModifiedAttribute) retVal).getValue(origAttrName);
203
if (res instanceof Attr) {
204
return ((Attr) res).get(params);
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.
219
public synchronized Object put(final Object p1, final Object p2) {
220
return put(p1, p2, true);
223
synchronized Object put(final Object p1, final Object p2, boolean decode) {
224
if ((p1 == null) || !(p1 instanceof String)) {
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);
234
key = Attr.decode(key).intern();
237
return map.put(key, toStore);
241
* Writes heading to XML file
242
* @param pw where to write
244
public static void writeHeading(PrintWriter pw) {
245
pw.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); // NOI18N
247
"<!DOCTYPE attributes PUBLIC \"-//NetBeans//DTD DefaultAttributes 1.0//EN\" \"http://www.netbeans.org/dtds/attributes-1_0.dtd\">"
249
pw.println("<attributes version=\"1.0\">"); // NOI18N
253
* Writes ending to XML file
254
* @param pw where to write
256
public static void writeEnding(PrintWriter pw) {
257
pw.println("</attributes>"); // NOI18N
261
* Writes all attributes for one FileObject with fileName
262
* @param pw where to write
264
* @param blockPrefix is prefix which is used before each line
266
public synchronized void write(PrintWriter pw, final String fileName, String blockPrefix) {
267
boolean isHeadingWr = false;
273
//pw.println(blockPrefix+"<fileobject name=\""+fileName+"\">");// NOI18N
274
SortedSet<String> attrNames = new TreeSet<String>();
275
Iterator entryIter = map.entrySet().iterator();
277
while (entryIter.hasNext()) {
278
Map.Entry entry = (Map.Entry) entryIter.next();
280
String attrName = (String) entry.getKey();
281
Attr attr = (Attr) entry.getValue();
283
if ((attrName == null) || (attr == null) || (attrName.length() == 0) || (attr.isValid() == -1)) {
284
if ((attrName != null) && (attrName.length() != 0) && ((attr == null) || (attr.isValid() == -1))) {
291
attrNames.add(attrName);
294
entryIter = attrNames.iterator();
296
while (entryIter.hasNext()) {
297
String attrName = (String) entryIter.next();
298
Attr attr = (Attr) map.get(attrName);
307
String quotedFileName = fileName;
310
quotedFileName = org.openide.xml.XMLUtil.toAttributeValue(fileName);
311
} catch (IOException ignore) {
314
pw.println(blockPrefix + "<fileobject name=\"" + quotedFileName + "\">"); // NOI18N
318
blockPrefix + blockPrefix + "<attr name=\"" + attr.getAttrNameForPrint(attrName) + "\" " +
319
attr.getKeyForPrint() + "=\"" + attr.getValueForPrint() + "\"/>"
321
attr.maybeAddSerValueComment(pw, blockPrefix + blockPrefix);
325
pw.println(blockPrefix + "</fileobject>"); // NOI18N
329
public synchronized void clear() {
333
public synchronized Object remove(Object p1) {
334
return map.remove(p1);
337
public synchronized boolean containsValue(Object p1) {
338
return map.containsValue(p1);
341
public synchronized int hashCode() {
342
return map.hashCode();
345
public synchronized java.util.Set<String> keySet() {
349
public synchronized java.util.Collection values() {
353
// XXX this is wrong - values not translated
354
public synchronized java.util.Set entrySet() {
355
return map.entrySet();
358
public synchronized void putAll(java.util.Map p1) {
362
public synchronized boolean containsKey(Object p1) {
363
return map.containsKey(p1);
366
public synchronized boolean isEmpty() {
367
return map.isEmpty();
370
public synchronized boolean equals(Object p1) {
371
return map.equals(p1);
374
public synchronized int size() {
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>
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"
389
private String value;
390
private int keyIndex;
391
private Object obj; //back compatibility
393
private Attr(Object obj) {
397
private Attr(int index, String value) {
399
this.value = (value != null) ? value.intern() : null;
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.
407
private Attr(String key, String value) {
408
keyIndex = isValid(key);
409
this.value = value.intern();
413
* @return array of Strings. Each String is textual form of allowed type of attribute - textual form of key
415
static String[] getAttrTypes() {
416
return ALLOWED_ATTR_KEYS;
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.
424
private final void putEntry(String key, String value) {
425
this.keyIndex = isValid(key);
426
this.value = value.intern();
430
* added for future use - convert NbMarshalledObject to primitive data types and other supported types (if possible)
432
static Object unMarshallObjectRecursively(Object mo) {
435
while (o instanceof NbMarshalledObject) {
437
o = ((NbMarshalledObject) o).get();
438
} catch (IOException e) {
439
ExternalUtil.exception(e);
442
} catch (ClassNotFoundException e) {
443
ExternalUtil.exception(e);
449
return (o == null) ? mo : o;
452
/**Method for back compatibility; called in write*/
453
private void transformMe() {
460
Object unObj = unMarshallObjectRecursively(obj);
463
if ((objType = XMLMapAttr.Attr.distinguishObject(unObj)) != XMLMapAttr.Attr.isValid("SERIALVALUE")) { // NOI18N
465
putEntry(ALLOWED_ATTR_KEYS[objType], unObj.toString());
470
newValue = encodeValue(unObj);
471
} catch (IOException iox) {
476
putEntry(ALLOWED_ATTR_KEYS[objType], newValue);
482
* added for future use - convert NbMarshalledObject to primitive data types and other supported types (if possible)
484
static int distinguishObject(Object o) {
485
if (o instanceof Byte) {
486
return isValid("BYTEVALUE"); // NOI18N
489
if (o instanceof Short) {
490
return isValid("SHORTVALUE"); // NOI18N
493
if (o instanceof Integer) {
494
return isValid("INTVALUE"); // NOI18N
497
if (o instanceof Long) {
498
return isValid("LONGVALUE"); // NOI18N
501
if (o instanceof Float) {
502
return isValid("FLOATVALUE"); // NOI18N
505
if (o instanceof Double) {
506
return isValid("DOUBLEVALUE"); // NOI18N
509
if (o instanceof Boolean) {
510
return isValid("BOOLVALUE"); // NOI18N
513
if (o instanceof Character) {
514
return isValid("CHARVALUE"); // NOI18N
517
if (o instanceof String) {
518
return isValid("STRINGVALUE"); // NOI18N
521
if (o instanceof URL) {
522
return isValid("URLVALUE"); // NOI18N
525
return isValid("SERIALVALUE"); // NOI18N
528
static String encode(String inStr) {
530
inStr = org.openide.xml.XMLUtil.toAttributeValue(inStr);
531
} catch (Exception ignore) {
534
StringBuffer outStr = new StringBuffer(6 * inStr.length());
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)));
543
outStr.append(inStr.charAt(i));
546
return outStr.toString();
549
static String encodeChar(char ch) {
550
String encChar = Integer.toString((int) ch, 16);
552
return "\\u" + "0000".substring(0, "0000".length() - encChar.length()).concat(encChar); // NOI18N
555
static String decode(final String inStr) {
556
StringBuffer outStr = new StringBuffer(inStr.length());
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));
565
outStr.append(inStr.charAt(i));
568
} catch (NumberFormatException e) {
569
Exceptions.printStackTrace(e);
574
return outStr.toString();
577
private static boolean isEncodedChar(final int currentPosition, final String inStr) {
578
boolean isEncodedChar = (currentPosition + 5) < inStr.length();
581
isEncodedChar &= ((inStr.charAt(currentPosition) == '\\') &&
582
(inStr.charAt(currentPosition + 1) == 'u'));
584
for (int i = currentPosition + 2; isEncodedChar && (i < (currentPosition + 6)); i++) {
585
char c = inStr.charAt(i);
586
isEncodedChar &= (Character.digit(c, 16) != -1);
590
return isEncodedChar;
594
* Constructs new attribute as Object. Used for static creation from literal or serialValue.
595
* @return new attribute as Object
597
private Object get() throws Exception {
598
return getObject(null); //getObject is ready to aobtain null
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
606
private Object get(Object[] objs) throws Exception {
607
return getObject(objs);
611
* @return key. Key expresses type of this attribute (in textual form) or "" if internal error.
613
final String getKey() {
614
String[] keyArray = getAttrTypes();
617
return "serialvalue"; //back compatibility // NOI18N
620
if (isValid() == -1) {
624
return keyArray[keyIndex];
628
* @return value in textual format or "" if internal error.
630
final String getValue() {
635
return (value != null) ? value : ""; // NOI18N
638
static final String getValue(Object obj) {
640
return encodeValue(obj); //back compatibility
641
} catch (IOException ioe) {
646
final String getValueForPrint() {
648
Attr modifAttr = null;
650
if (obj instanceof ModifiedAttribute) {
651
modifAttr = (Attr) ((ModifiedAttribute) obj).getValue();
654
return (modifAttr != null) ? encode(modifAttr.getValue()) : encode(getValue());
657
return (value != null) ? encode(value) : ""; // NOI18N
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
665
if (modifAttr != null) {
666
keyIdx = distinguishObject(modifAttr.getValue());
669
String[] keyArray = getAttrTypes();
671
return keyArray[keyIdx];
677
final String getAttrNameForPrint(String attrName) {
678
if ((obj != null) && obj instanceof ModifiedAttribute) {
679
Object[] retVal = ModifiedAttribute.revert(attrName, obj);
681
return encode((String) retVal[0]);
684
return encode(attrName);
687
final void maybeAddSerValueComment(PrintWriter pw, String indent) {
689
Object modifObj = null;
691
if (obj instanceof ModifiedAttribute) {
692
modifObj = ((Attr) ((ModifiedAttribute) obj).getValue()).getValue();
694
if (distinguishObject(modifObj) != Attr.isValid("SERIALVALUE")) { //NOI18N
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
706
pw.print("<!-- "); // NOI18N
708
String s = (modifObj != null) ? modifObj.toString() : obj.toString();
710
if (s.indexOf("--") != -1) { // NOI18N
712
// XML comment no-no.
713
s = s.replace('-', '_'); // NOI18N
717
pw.println(" -->"); // NOI18N
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
727
static Object decodeValue(String value) throws IOException {
728
if ((value == null) || (value.length() == 0)) {
732
byte[] bytes = new byte[value.length() / 2];
736
for (int i = 0; i < value.length(); i += 2) {
738
tempI = Integer.parseInt(value.substring(i, i + 2), 16);
744
bytes[count++] = (byte) tempI;
745
} catch (NumberFormatException e) {
746
throw (IOException) ExternalUtil.copyAnnotation(new IOException(), e);
750
ByteArrayInputStream bis = new ByteArrayInputStream(bytes, 0, count);
753
ObjectInputStream ois = new NbObjectInputStream(bis);
754
Object ret = ois.readObject();
757
} catch (Exception e) {
758
throw (IOException) ExternalUtil.copyAnnotation(new IOException(), e);
763
//throw new InternalError ();
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
772
static String encodeValue(Object value) throws IOException {
773
ByteArrayOutputStream bos = new ByteArrayOutputStream();
776
ObjectOutputStream oos = new ObjectOutputStream(bos);
777
oos.writeObject(value);
779
} catch (Exception e) {
780
throw (IOException) ExternalUtil.copyAnnotation(new IOException(), e);
783
byte[] bArray = bos.toByteArray();
784
StringBuffer strBuff = new StringBuffer(bArray.length * 2);
786
for (int i = 0; i < bArray.length; i++) {
787
if ((bArray[i] < 16) && (bArray[i] >= 0)) {
788
strBuff.append("0"); // NOI18N
791
strBuff.append(Integer.toHexString((bArray[i] < 0) ? (bArray[i] + 256) : bArray[i]));
794
return strBuff.toString();
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
803
private Object getObject(Object[] params) throws Exception {
807
return obj; //back compatibility
810
if ((index = isValid()) != -1) {
814
return new Byte(value);
817
return new Short(value);
820
return new Integer(value); //(objI);
823
return new Long(value);
826
return new Float(value);
829
return new Double(value);
832
return Boolean.valueOf(value);
836
if (value.trim().length() != 1) {
840
return new Character(value.charAt(0));
846
return methodValue(value, params);
849
return decodeValue(value);
852
return new URL(value);
856
// special support for singletons
857
Class cls = ExternalUtil.findClass(Utilities.translate(value));
859
if (SharedClassObject.class.isAssignableFrom(cls)) {
860
return SharedClassObject.findObject(cls, true);
862
return cls.newInstance();
865
} catch (Exception exc) {
866
ExternalUtil.annotate(exc, "value = " + value); //NOI18N
868
} catch (LinkageError e) {
869
throw (ClassNotFoundException) ExternalUtil.annotate(new ClassNotFoundException(value), e);
873
throw new InstantiationException(value);
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
880
private final Object methodValue(String value, Object[] params)
882
int sepIdx = value.lastIndexOf('.');
885
String methodName = value.substring(sepIdx + 1);
886
Class cls = ExternalUtil.findClass(value.substring(0, sepIdx));
887
FileObject fo = null;
888
String attrName = null;
890
for (int i = 0; i < params.length; i++) {
891
if ((fo == null) && params[i] instanceof FileObject) {
892
fo = (FileObject) params[i];
895
if ((attrName == null) && params[i] instanceof String) {
896
attrName = (String) params[i];
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 },
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[] { };
914
Map fileMap = wrapToMap(fo);
915
objectsList[5] = attrName != null ? new Object[] { fileMap, attrName } : null;
916
objectsList[6] = new Object[] { fileMap };
918
for (int i = 0; i < paramArray.length; i++) {
919
Object[] objArray = (Object[]) objectsList[i];
921
if (objArray == null) {
926
Method method = cls.getDeclaredMethod(methodName, (Class[]) paramArray[i]);
928
if (method != null) {
929
method.setAccessible(true);
931
return method.invoke(null, objArray);
933
} catch (NoSuchMethodException nsmExc) {
939
throw new InstantiationException(value);
942
static final Map wrapToMap(FileObject fo) {
943
return fo == null ? Collections.EMPTY_MAP : new FileMap(fo);
947
* Checks if key is valid
948
* @return Index to array of allowed keys or -1 which means error.
950
final int isValid() {
951
String[] keyArray = getAttrTypes();
954
return isValid("SERIALVALUE"); //back compatibility // NOI18N
957
if ((keyIndex >= keyArray.length) || (keyIndex < 0)) {
965
* Checks if key is valid
966
* @return Index to array of allowed keys or -1 which means error.
968
final static int isValid(String key) {
971
String[] strArray = getAttrTypes();
972
String trimmedKey = key.trim();
974
for (i = 0; i < strArray.length; i++) {
975
if (trimmedKey.equalsIgnoreCase(strArray[i]) == true) {
985
public boolean equals(Object obj) {
986
if (obj instanceof Attr) {
987
Attr other = (Attr)obj;
989
if (other.keyIndex != keyIndex) {
993
return other.value.equals(value);
998
public int hashCode() {
999
return 743 + keyIndex << 8 + value.hashCode();
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.
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;
1018
/** Creates a new instance of AttributeFactory */
1019
private ModifiedAttribute(Object origAttrValue) {
1020
this.origAttrValue = origAttrValue;
1023
/** This method looks for modifiers in attribute name (currently transient:).
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.
1034
static Object[] translateInto(String attrName, Object value) {
1035
String newAttrName = attrName;
1036
Object newValue = value;
1037
ModifiedAttribute attr = null;
1039
for (int i = 0; i < fragments.length; i++) {
1040
String fragment = fragments[i];
1041
int idx = newAttrName.indexOf(fragment);
1044
/** fragment is cleared away */
1045
newAttrName = newAttrName.substring(0, idx) + newAttrName.substring(idx + fragment.length());
1048
newValue = attr = new ModifiedAttribute(value);
1051
attr.modifier |= (1 << i); //set modifier
1055
return new Object[] { newAttrName, newValue };
1059
* This method is opposite to method translateInto
1061
static Object[] revert(String attrName, Object value) {
1062
if (!(value instanceof ModifiedAttribute) || (value == null)) {
1063
return new Object[] { attrName, value };
1066
ModifiedAttribute attr = (ModifiedAttribute) value;
1067
String newAttrName = attrName;
1068
Object newValue = attr;
1070
for (int i = 0; i < fragments.length; i++) {
1071
String fragment = fragments[i];
1073
if (((attr.modifier & (1 << i)) != 0) && (fragment != null)) {
1074
/** fragment is cleared away */
1075
newAttrName = fragment + newAttrName;
1077
if (newValue instanceof ModifiedAttribute) {
1078
newValue = attr.origAttrValue;
1083
return new Object[] { newAttrName, newValue };
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.
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);
1101
return origAttrValue;
1104
/** ModifiedAttribute holds original value + modifiers. This method returns original value.
1105
* @return then returns original value
1108
return getValue(""); //NOI18N
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
1117
static boolean isTransient(FileObject fo, String attrName) {
1118
Object value = fo.getAttribute(fragments[0] + attrName);
1120
if (value instanceof ModifiedAttribute) {
1121
return ((((ModifiedAttribute) value).modifier & (1 << 0)) == 0) ? false : true;
1128
private static final class FileMap extends AbstractMap<String,Object> {
1129
private FileObject fo;
1131
private FileMap (FileObject fo) {
1135
public Set<Map.Entry<String,Object>> entrySet() {
1136
return new AttrFileSet(fo);
1139
public Object get(String key) {
1140
return fo.getAttribute(key);
1143
public Object remove(Object key) {
1144
throw new UnsupportedOperationException();
1147
public Object put(String key, Object value) {
1148
throw new UnsupportedOperationException();
1152
private static final class AttrFileSet extends AbstractSet<Map.Entry<String,Object>> {
1153
private FileObject fo;
1155
private AttrFileSet(FileObject fo) {
1159
public Iterator<Map.Entry<String, Object>> iterator() {
1160
class Iter implements Iterator<Map.Entry<String, Object>> {
1161
Enumeration<String> attrs = fo.getAttributes();
1163
public boolean hasNext() {
1164
return attrs.hasMoreElements();
1167
public Map.Entry<String, Object> next() {
1168
String s = attrs.nextElement();
1169
return new FOEntry(fo, s);
1172
public void remove() {
1173
throw new UnsupportedOperationException();
1180
Enumeration<String> all = fo.getAttributes();
1182
while (all.hasMoreElements()) {
1189
public boolean remove(Object o) {
1190
throw new UnsupportedOperationException();
1192
} // end of AttrFileSet
1194
private static final class FOEntry implements Map.Entry<String, Object> {
1195
private FileObject fo;
1196
private String attr;
1198
private FOEntry(FileObject fo, String attr) {
1203
public String getKey() {
1207
public Object getValue() {
1208
return fo.getAttribute(attr);
1211
public Object setValue(Object value) {
1212
throw new UnsupportedOperationException();