/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun * Microsystems, Inc. All Rights Reserved. */ package org.openide.filesystems; import java.util.*; import java.lang.reflect.*; import java.net.URL; import java.io.*; import org.xml.sax.*; import org.openide.util.io.NbMarshalledObject; import org.openide.util.Utilities; import org.openide.util.SharedClassObject; import org.openide.util.io.NbObjectInputStream; /** *Holds in Map attributes: Map(String attrName,XMLMapAttr.Attr attribute). This map holds all atributes for one FileObject. *<BR><BR> *<H3>Detailed description</H3> * Each file object (file or folder element) can have 0..* attributes.<BR> <BR> * Each file object <I>atrribute</I> (attribute is here name of element) must have two attributes (here XML attribute).<BR> * <OL> * <LI>First attribute name is <I>id</I> , which is mandatory and value of this * attribute serve as identifier to distinguish many attributes for one file object. * Name of attribute can contain prefix <code>transient:</code>. Transient means that such * marked attribute won`t be copied together with FileObject. Be aware that for: * fo.setAttribute("transient:foo", "bar") is true that fo.getAttribute("foo").equals("bar") * <LI> Second attribute is also mandatory, but you can choose such attribute name and * attribute value, which correspond to desirable data type * (e.g. <I>stringValue</I>, <I>boolValue</I> etc.). * </OL> * Desirable data type can be one of primitive data types or object data types. * <BR> * <BR> * Moreover value of attribute can be passed: * <OL> * <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.). * 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...."). * This should ensure back compatibility. * <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 * (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. * 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). * So here is sugestion of declaration of such method: <I>public static Object myMethod(FileObject myFo,String myName)</I>. * * <A NAME="primitive"><H4>Primitive data types</H4> * </A> * Here is sugested list of attribute names for primitive data types * (I expect that from the name is obvious which type of value is expected): * <OL> * <I> * <LI> bytevalue * <LI> shortvalue * <LI> intvalue * <LI> longvalue * <LI> floatvalue * <LI> doublevalue * <LI> boolvalue * <LI> charvalue * </I> * </OL> * <BR><BR> *<A NAME="object"><H4>Object data types</H4></A> * <OL> * <LI> <I>methodvalue</I> - dynamic creation (for primitive data could be returned wrapper objects) <LI> <I>newvalue</I> - newInstance is called * <LI> <I>serialValue</I> - static creation * * </OL> <BR><BR> * Attributes are stored in xml file, then there must be used encoding for not permitted * chars. There are used Java-style <code>\uXXXX</code> Unicode escapes for ISO control characters and * minimal set of character entities <code><</code>, <code>&</code>, <code>'</code> * and <code>"</code>. * * @author rmatous * @version 1.0 */ final class XMLMapAttr implements Map { Map map; /** Creates new XMLMapAttr and delegetaor is instanced */ public XMLMapAttr() { this.map = new HashMap (5); } /** According to name of attribute returns attribute as object * @param p1 is name of attribute * @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 */ public Object get (final Object p1) { Object obj; try { obj = getAttribute (p1); } catch(Exception e) { obj = null; ExternalUtil.exception (e); } return obj; } /** According to name of attribute returns attribute as object * @param params has sense only for methodvalue invocation; and only 2 parametres will be used * @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 */ public Object get (final Object p1,Object[] params) { Object obj; try { obj = getAttribute (p1, params); } catch(Exception e) { obj = null; ExternalUtil.exception (e); } return obj; } /** implementation of Map.get. But fires Exception to have chance in * DefaultAttributes to catch and annotate*/ Object getAttribute (Object attrName) throws Exception { return getAttribute (attrName, null); } private Object getAttribute (Object attrName, Object[] params) throws Exception { Attr attr; String origAttrName = (String)attrName; Object[] keyValuePair = ModifiedAttribute.translateInto((String)attrName,null); attrName = (String)keyValuePair[0]; synchronized (this) { attr = (Attr)map.get(attrName); } Object retVal = null; try { retVal = (attr == null)?attr : attr.get(params); } catch (Exception e) { ExternalUtil.annotate (e, "attrName = "+attrName); //NOI18N throw e; } if (retVal instanceof ModifiedAttribute) { Object res = ((ModifiedAttribute)retVal).getValue (origAttrName); if (res instanceof Attr) return ((Attr)res).get (params); else return res; } return retVal; } /** * @param p1 is name of attribute * @param p2 is attribute as object * @return previous value associated with specified key, or null if there was no mapping for key. * A null return can also indicate that the HashMap previously associated null with the specified key. */ public synchronized Object put(final Object p1,final Object p2) { if (p1 == null || !(p1 instanceof String)) return null; Object[] keyValuePair = ModifiedAttribute.translateInto ((String)p1,p2); String key = (String)keyValuePair[0]; Object value = keyValuePair[1]; Object toStore = (value == null || value instanceof Attr) ? value : new Attr(value); return map.put(Attr.decode ((String)key).intern(), toStore ); } /** * Writes heading to XML file * @param pw where to write */ public static void writeHeading(PrintWriter pw) { pw.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); // NOI18N pw.println("<!DOCTYPE attributes PUBLIC \"-//NetBeans//DTD DefaultAttributes 1.0//EN\" \"http://www.netbeans.org/dtds/attributes-1_0.dtd\">");//NOI18N pw.println("<attributes version=\"1.0\">");// NOI18N } /** * Writes ending to XML file * @param pw where to write */ public static void writeEnding(PrintWriter pw) { pw.println("</attributes>");// NOI18N } /** * Writes all attributes for one FileObject with fileName * @param pw where to write * @param fileName * @param blockPrefix is prefix which is used before each line */ public synchronized void write(PrintWriter pw,final String fileName,String blockPrefix) { boolean isHeadingWr = false; if (isEmpty()) return; //pw.println(blockPrefix+"<fileobject name=\""+fileName+"\">");// NOI18N Iterator entryIter = entrySet().iterator(); while (entryIter.hasNext()) { Map.Entry entry = (Map.Entry)entryIter.next(); String attrName = (String)entry.getKey(); Attr attr = (Attr)entry.getValue(); if (attrName == null || attr == null || attrName.length() == 0 || attr.isValid() == -1 ) { if (attrName != null && attrName.length() != 0 && (attr == null || attr.isValid() == -1)) entryIter.remove (); continue; } if (attr != null) attr.transformMe (); if (!isHeadingWr) { isHeadingWr = true; String quotedFileName = fileName; try { quotedFileName = org.openide.xml.XMLUtil.toAttributeValue(fileName); } catch (IOException ignore) {} pw.println(blockPrefix+"<fileobject name=\"" + quotedFileName + "\">");// NOI18N } pw.println(blockPrefix+blockPrefix+"<attr name=\""+attr.getAttrNameForPrint (attrName)+"\" "+ attr.getKeyForPrint ()+"=\""+attr.getValueForPrint()+"\"/>");// NOI18N attr.maybeAddSerValueComment(pw,blockPrefix+blockPrefix); } if (isHeadingWr) pw.println(blockPrefix+"</fileobject>");// NOI18N } public synchronized void clear() { map.clear(); } public synchronized Object remove(Object p1) { return map.remove (p1); } public synchronized boolean containsValue(Object p1) { return map.containsValue(p1); } public synchronized int hashCode() { return map.hashCode(); } public synchronized java.util.Set keySet() { return map.keySet(); } public synchronized java.util.Collection values() { return map.values(); } public synchronized java.util.Set entrySet() { return map.entrySet(); } public synchronized void putAll(java.util.Map p1) { map.putAll(p1); } public synchronized boolean containsKey(Object p1) { return map.containsKey(p1); } public synchronized boolean isEmpty() { return map.isEmpty(); } public synchronized boolean equals(Object p1) { return map.equals(p1); } public synchronized int size() { return map.size(); } /** * Holds textual representation of one attribute. And on request construct new instance of * 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. * Detailed describtion is in <A HREF="XMLMapAttr.html">XMLMapAttr<A> */ final static class Attr extends java.lang.Object { private String value; private int keyIndex; private Object obj;//back compatibility // static final long serialVersionUID = -62733358015297232L; private static final String[] ALLOWED_ATTR_KEYS = {"bytevalue","shortvalue","intvalue","longvalue","floatvalue","doublevalue","boolvalue","charvalue","stringvalue","methodvalue","serialvalue","urlvalue","newvalue"}; // NOI18N //{"BYTEVALUE","SHORTVALUE","INTVALUE","LONGVALUE","FLOATVALUE","DOUBLEVALUE","BOOLVALUE","CHARVALUE","STRINGVALUE","METHODVALUE","SERIALVALUE","URLVALUE"}; /** * @param key One of the possible keys: * "bytevalue","shortvalue","intvalue","longvalue","floatvalue","doublevalue","boolvalue","charvalue","stringvalue","methodvalue","serialvalue","urlvalue" * @param value Corresponding value to key in textual form. */ Attr(String key, String value) { putEntry(key,value); } /** * This constructor is used for backward compatibility (serializated form of filesystem.attributes). * Mostly NbMarshalledObject is put in this constructor. * @param obj Arbitrary object */ Attr(Object obj) { this.obj = obj; } Attr(int index, String value) { this.keyIndex = index; if (isValid() != -1) putEntry(ALLOWED_ATTR_KEYS[this.keyIndex],value); else this.value = value; } /** * @return array of Strings. Each String is textual form of allowed type of attribute - textual form of key */ static String[] getAttrTypes () { return ALLOWED_ATTR_KEYS; } /** * Checks if key is valid and sets key and value * @param key Key of attribute. Defines type of attribute in textual form. * @param value Value of attribute. Defines value of attribute as literal or HEX expression of serialization. */ private final void putEntry(String key, String value) { int index; index = isValid(key); this.keyIndex = index; if (index == isValid ("stringvalue")) { // NOI18N this.value = decode (value).intern(); return; } this.value = value.intern(); } /** * added for future use - convert NbMarshalledObject to primitive data types and other supported types (if possible) */ static Object unMarshallObjectRecursively(Object mo) { Object o = mo; while(o instanceof NbMarshalledObject) { try { o = ((NbMarshalledObject)o).get (); } catch (IOException e) { ExternalUtil.exception (e); return mo; } catch (ClassNotFoundException e) { ExternalUtil.exception (e); return mo; } } return (o == null)? mo : o; } /**Method for back compatibility; called in write*/ private void transformMe () { int objType; if (obj == null) return; Object unObj = unMarshallObjectRecursively (obj); if (unObj != null) { if ((objType = XMLMapAttr.Attr.distinguishObject (unObj)) != XMLMapAttr.Attr.isValid("SERIALVALUE")) { // NOI18N obj = null; putEntry (ALLOWED_ATTR_KEYS[objType],unObj.toString()); } else { String newValue; try { newValue = encodeValue(unObj); } catch (IOException iox) { return; } obj = null; putEntry(ALLOWED_ATTR_KEYS[objType],newValue); } } } /** * added for future use - convert NbMarshalledObject to primitive data types and other supported types (if possible) */ static int distinguishObject(Object o) { if (o instanceof Byte) return isValid("BYTEVALUE");// NOI18N if (o instanceof Short) return isValid("SHORTVALUE");// NOI18N if (o instanceof Integer) return isValid("INTVALUE");// NOI18N if (o instanceof Long) return isValid("LONGVALUE");// NOI18N if (o instanceof Float) return isValid("FLOATVALUE");// NOI18N if (o instanceof Double) return isValid("DOUBLEVALUE");// NOI18N if (o instanceof Boolean) return isValid("BOOLVALUE");// NOI18N if (o instanceof Character) return isValid("CHARVALUE");// NOI18N if (o instanceof String) return isValid("STRINGVALUE");// NOI18N if (o instanceof URL) return isValid("URLVALUE");// NOI18N return isValid("SERIALVALUE");// NOI18N } static String encode(String inStr) { try { inStr = org.openide.xml.XMLUtil.toAttributeValue(inStr); } catch (Exception ignore) {} StringBuffer outStr = new StringBuffer(6*inStr.length()); for (int i = 0; i < inStr.length(); i++) { if (Character.isISOControl(inStr.charAt(i))) { outStr.append(encodeChar(inStr.charAt(i))); continue; } outStr.append(inStr.charAt(i)); } return outStr.toString(); } static String encodeChar (char ch) { String encChar= Integer.toString((int)ch,16); return "\\u"+"0000".substring(0,"0000".length()-encChar.length()).concat(encChar); // NOI18N } static String decode (String inStr) { StringBuffer outStr = new StringBuffer (inStr.length()); for (int i = 0; i < inStr.length(); i++) { char ch = inStr.charAt(i); if ( (i+5) < inStr.length() && ch == '\\' && inStr.charAt(i+1) == 'u' && Character.isDigit(inStr.charAt(i+2))) { String decChar = inStr.substring(i+2,i+6); outStr.append((char) Integer.parseInt(decChar,16)); i += 5; }else outStr.append(ch); } return outStr.toString(); } /** * Constructs new attribute as Object. Used for static creation from literal or serialValue. * @return new attribute as Object */ private Object get() throws Exception { return getObject(null);//getObject is ready to aobtain null } /** * Constructs new attribute as Object. Used for dynamic creation: methodvalue . * @param params has sense only for methodvalue invocation; and only 2 parametres will be used *@return new attribute as Object */ private Object get(Object[] obj) throws Exception { return getObject(obj); } /** * @return key. Key expresses type of this attribute (in textual form) or "" if internal error. */ final String getKey() { String keyArray[] = getAttrTypes(); if (obj != null) return "serialvalue";//back compatibility // NOI18N if (isValid() == -1) return ""; // NOI18N return keyArray[keyIndex]; } /** * @return value in textual format or "" if internal error. */ final String getValue() { if (obj != null) getValue(obj); return (value != null)?value : ""; // NOI18N } static final String getValue(Object obj) { try { return encodeValue(obj);//back compatibility } catch (IOException ioe) { return ""; // NOI18N } } final String getValueForPrint() { if (obj != null) { Attr modifAttr = null; if (obj instanceof ModifiedAttribute) modifAttr = (Attr)((ModifiedAttribute)obj).getValue (); return (modifAttr != null) ? encode(modifAttr.getValue ()) : encode(getValue ()); } return (value != null)?encode(value) : ""; // NOI18N } final String getKeyForPrint() { if (obj != null && obj instanceof ModifiedAttribute) { Attr modifAttr = (Attr)((ModifiedAttribute)obj).getValue (); int keyIdx = Attr.isValid ("SERIALVALUE");//NOI18N if (modifAttr != null) keyIdx = distinguishObject (modifAttr.getValue ()); String keyArray[] = getAttrTypes(); return keyArray[keyIdx]; } return getKey (); } final String getAttrNameForPrint (String attrName) { if (obj != null && obj instanceof ModifiedAttribute) { Object[] retVal = ModifiedAttribute.revert(attrName,obj); return encode ((String)retVal[0]); } return encode (attrName); } final void maybeAddSerValueComment(PrintWriter pw, String indent) { if (obj != null) { Object modifObj = null; if (obj instanceof ModifiedAttribute) { modifObj = ((Attr)((ModifiedAttribute)obj).getValue ()).getValue (); if (distinguishObject (modifObj) != Attr.isValid("SERIALVALUE")) //NOI18N return; } // Important for debugging to know what this stuff really is. // Note this comment is only written to disk when the attr is // first saved; after that successive saves will just know the // ser value and will not print the comment. So look at .nbattrs // immediately after setting something serialized. --jglick pw.print(indent); pw.print("<!-- "); // NOI18N String s = (modifObj != null) ? modifObj.toString():obj.toString(); if (s.indexOf("--") != -1) { // NOI18N // XML comment no-no. s = s.replace('-', '_'); // NOI18N } pw.print(s); pw.println(" -->"); // NOI18N } } /** * Creates serialized object, which was encoded in HEX format * @param value Encoded serialized object in HEX format * @return Created object from encoded HEX format * @throws IOException */ static Object decodeValue(String value) throws IOException { if ((value == null) ||(value.length() == 0)) return null; byte[] bytes = new byte[value.length()/2]; int tempI; int count = 0; for (int i = 0; i < value.length(); i += 2) { try { tempI = Integer.parseInt(value.substring(i,i+2),16); if (tempI > 127) tempI -=256; bytes[count++] = (byte) tempI; } catch (NumberFormatException e) { throw (IOException)ExternalUtil.copyAnnotation (new IOException (),e); } } ByteArrayInputStream bis = new ByteArrayInputStream(bytes, 0, count); try { ObjectInputStream ois = new NbObjectInputStream(bis); Object ret = ois.readObject(); return ret; } catch (Exception e) { throw (IOException)ExternalUtil.copyAnnotation (new IOException (),e); } /*unreachable code*/ //throw new InternalError (); } /** * Encodes Object into String encoded in HEX format * @param value Object, which will be encoded * @return serialized Object in String encoded in HEX format * @throws IOException */ static String encodeValue(Object value) throws IOException{ ByteArrayOutputStream bos = new ByteArrayOutputStream(); try { ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(value); oos.close(); } catch (Exception e) { throw (IOException)ExternalUtil.copyAnnotation (new IOException (),e); } byte bArray[] = bos.toByteArray(); StringBuffer strBuff = new StringBuffer(bArray.length*2); for(int i = 0; i < bArray.length;i++) { if (bArray[i] < 16 && bArray[i] >= 0) strBuff.append("0");// NOI18N strBuff.append(Integer.toHexString(bArray[i] < 0?bArray[i]+256:bArray[i])); } return strBuff.toString(); } /** * Encodes Object into String encoded in HEX format * @param params Array (2 length) of objects ( Object[] o = {fo,name}). Attribute is assigned to some fo-FileObject and has its name-String. * params can be null. * @return Object or null */ private Object getObject(Object[] params) throws Exception { int index; if (obj != null) return obj;//back compatibility if ((index = isValid()) != -1) { try { switch(index) { case 0: return new Byte(value); case 1: return new Short(value); case 2: return new Integer(value);//(objI); case 3: return new Long(value); case 4: return new Float(value); case 5: return new Double(value); case 6: return Boolean.valueOf(value); case 7: if (value.trim().length() != 1) break; return new Character(value.charAt(0)); case 8: return value; case 9: return methodValue (value,params); case 10: return decodeValue(value); case 11: return new URL(value); case 12: // special support for singletons Class cls = ExternalUtil.findClass (Utilities.translate (value)); if (SharedClassObject.class.isAssignableFrom(cls)) { return SharedClassObject.findObject(cls, true); } else { return cls.newInstance(); } } } catch (Exception exc) { ExternalUtil.annotate (exc, "value = "+value); //NOI18N throw exc; } catch (LinkageError e) { throw (ClassNotFoundException)ExternalUtil.annotate(new ClassNotFoundException(value), e); } } throw new InstantiationException (value); } /** Constructs new attribute as Object. Used for dynamic creation: methodvalue . * @param only 2 parametres will be used * @return Object or null */ private final Object methodValue (String value,Object[] params) throws Exception { int sepIdx = value.lastIndexOf('.'); if (sepIdx != -1) { String methodName = value.substring(sepIdx+1); Class cls = ExternalUtil.findClass (value.substring(0,sepIdx)); FileObject fo = null; String attrName = null; for (int i = 0; i < params.length; i++) { if (fo == null && params [i] instanceof FileObject) { fo = (FileObject)params[i]; } if (attrName == null && params [i] instanceof String) { attrName = (String)params[i]; } } Object[] paramArray = new Object [] { new Class[] {FileObject.class, String.class}, new Class[] {String.class, FileObject.class}, new Class[] {FileObject.class}, new Class[] {String.class}, new Class[] {} }; boolean both= (fo != null && attrName != null); Object[] objectsList = new Object [5]; objectsList [0] = (both) ? new Object[] {fo, attrName} : null; objectsList [1] = (both)? new Object[] {attrName, fo} :null; objectsList [2] = (fo != null) ? new Object[] {fo} : null; objectsList [3] = (attrName != null) ? new Object[] {attrName} : null; objectsList [4] = new Object[] {}; for (int i = 0; i < paramArray.length; i++) { Object[] objArray= (Object[])objectsList [i]; if (objArray == null) continue; try { Method method = cls.getDeclaredMethod(methodName, (Class[])paramArray [i]); if (method != null) { method.setAccessible(true); return method.invoke(null,objArray); } } catch (NoSuchMethodException nsmExc) { continue; } } } throw new InstantiationException(value); } /** * Checks if key is valid * @return Index to array of allowed keys or -1 which means error. */ final int isValid() { String keyArray[] = getAttrTypes(); if (obj != null) return isValid("SERIALVALUE");//back compatibility // NOI18N if (keyIndex >= keyArray.length || keyIndex < 0) return -1; return keyIndex; } /** * Checks if key is valid * @return Index to array of allowed keys or -1 which means error. */ final static int isValid(String key) { int index = -1,i; String strArray[] = getAttrTypes(); String trimmedKey = key.trim(); for (i = 0; i < strArray.length;i++) { if (trimmedKey.equalsIgnoreCase(strArray[i]) == true) { index = i; break; } } return index; } } /** * Helper class for decorating attributes with modifiers. * Object that is made persistent using setAttribute can contain also modifiers. * This class is wrapper class that holds original object and its modifiers. * Intended as replacer of original class in attributes. * Currently exists only one modifier: tranisent. * Transient modifier means that such attribute won`t be copied with FileObject. */ static class ModifiedAttribute implements java.io.Serializable { /** generated Serialized Version UID */ static final long serialVersionUID = 84214031923497718L; private final static String[] fragments = new String[] {"transient:"}; //NOI18N private int modifier = 0; private Object origAttrValue = null; /** Creates a new instance of AttributeFactory */ private ModifiedAttribute(Object origAttrValue) { this.origAttrValue = origAttrValue; } /** This method looks for modifiers in attribute name (currently transient:). * * @param attrName original name of attribute * @param value original value - can be null * @return Object array with size 2. * If there are no modifiers in attribute name, then is returned Object array with * , where first is placed unchanged attribute name, and then uchanged value. * If there are modifiers in attribute name, then as attribute name is returned * stripped original attribute name (without modifiers) and ModifiedAttribute object, * that wraps original object and also contain modifiers. */ static Object[] translateInto (String attrName, Object value) { String newAttrName = attrName; Object newValue = value; ModifiedAttribute attr = null; for (int i = 0; i < fragments.length; i++) { String fragment = fragments[i]; int idx = newAttrName.indexOf(fragment); if (idx != -1) { /** fragment is cleared away */ newAttrName = newAttrName.substring(0,idx) + newAttrName.substring(idx+fragment.length()); if (attr == null ) newValue = attr = new ModifiedAttribute(value); attr.modifier |= 1 << i;//set modifier } } return new Object[] {newAttrName, newValue}; } /** * This method is opposite to method translateInto */ static Object[] revert (String attrName, Object value) { if (!(value instanceof ModifiedAttribute) || value == null) return new Object [] {attrName, value}; ModifiedAttribute attr = (ModifiedAttribute)value; String newAttrName = attrName; Object newValue = attr; for (int i = 0; i < fragments.length; i++) { String fragment = fragments[i]; if ((attr.modifier & (1 << i)) != 0 && fragment != null) { /** fragment is cleared away */ newAttrName = fragment + newAttrName; if (newValue instanceof ModifiedAttribute) newValue = attr.origAttrValue; } } return new Object[] {newAttrName, newValue}; } /** ModifiedAttribute holds original value + modifiers. This method returns original value. * @return If there are no modifiers in attribute name, then returns original value * If there are modifiers in attribute name, then returns current instance of * ModifiedAttribute. */ Object getValue(String attrName) { for (int i = 0; i < fragments.length; i++) { String fragment = fragments[i]; int idx = attrName.indexOf(fragment); if (idx != -1) return this; } return origAttrValue; } /** ModifiedAttribute holds original value + modifiers. This method returns original value. * @return then returns original value */ Object getValue() { return getValue("");//NOI18N } /** * Decides if value stored in attributes is transient * @param fo fileobject where attribute is looked for * @param attrName name of attribute * @return true if transient */ static boolean isTransient(FileObject fo, String attrName) { Object value = fo.getAttribute(fragments[0] + attrName); if (value instanceof ModifiedAttribute) return ((((ModifiedAttribute)value).modifier & (1 << 0)) == 0) ? false:true; return false; } } }