0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041: package org.openide.filesystems;
0042:
0043: import org.openide.util.SharedClassObject;
0044: import org.openide.util.Utilities;
0045: import org.openide.util.io.NbMarshalledObject;
0046: import org.openide.util.io.NbObjectInputStream;
0047:
0048: import java.io.*;
0049:
0050: import java.lang.reflect.*;
0051:
0052: import java.net.URL;
0053:
0054: import java.util.*;
0055: import org.openide.util.Exceptions;
0056:
0057: /**
0058: *Holds in Map attributes: Map(String attrName,XMLMapAttr.Attr attribute). This map holds all atributes for one FileObject.
0059: *<BR><BR>
0060: *<H3>Detailed description</H3>
0061: * Each file object (file or folder element) can have 0..* attributes.<BR> <BR>
0062: * Each file object <I>atrribute</I> (attribute is here name of element) must have two attributes (here XML attribute).<BR>
0063: * <OL>
0064: * <LI>First attribute name is <I>id</I> , which is mandatory and value of this
0065: * attribute serve as identifier to distinguish many attributes for one file object.
0066: * Name of attribute can contain prefix <code>transient:</code>. Transient means that such
0067: * marked attribute won`t be copied together with FileObject. Be aware that for:
0068: * fo.setAttribute("transient:foo", "bar") is true that fo.getAttribute("foo").equals("bar")
0069: * <LI> Second attribute is also mandatory, but you can choose such attribute name and
0070: * attribute value, which correspond to desirable data type
0071: * (e.g. <I>stringValue</I>, <I>boolValue</I> etc.).
0072: * </OL>
0073: * Desirable data type can be one of primitive data types or object data types.
0074: * <BR>
0075: * <BR>
0076: * Moreover value of attribute can be passed:
0077: * <OL>
0078: * <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.).
0079: * 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....").
0080: * This should ensure back compatibility.
0081: * <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
0082: * (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.
0083: * 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).
0084: * So here is sugestion of declaration of such method: <I>public static Object myMethod(FileObject myFo,String myName)</I>.
0085: *
0086: * <A NAME="primitive"><H4>Primitive data types</H4>
0087: * </A>
0088: * Here is sugested list of attribute names for primitive data types
0089: * (I expect that from the name is obvious which type of value is expected):
0090: * <OL>
0091: * <I>
0092: * <LI> bytevalue
0093: * <LI> shortvalue
0094: * <LI> intvalue
0095: * <LI> longvalue
0096: * <LI> floatvalue
0097: * <LI> doublevalue
0098: * <LI> boolvalue
0099: * <LI> charvalue
0100: * </I>
0101: * </OL>
0102: * <BR><BR>
0103: *<A NAME="object"><H4>Object data types</H4></A>
0104: * <OL>
0105: * <LI> <I>methodvalue</I> - dynamic creation (for primitive data could be returned wrapper objects)
0106: <LI> <I>newvalue</I> - newInstance is called
0107: * <LI> <I>serialValue</I> - static creation
0108: *
0109: * </OL>
0110: <BR><BR>
0111: * Attributes are stored in xml file, then there must be used encoding for not permitted
0112: * chars. There are used Java-style <code>\uXXXX</code> Unicode escapes for ISO control characters and
0113: * minimal set of character entities <code><</code>, <code>&</code>, <code>'</code>
0114: * and <code>"</code>.
0115: *
0116: * @author rmatous
0117: */
0118: @SuppressWarnings("unchecked")
0119: final class XMLMapAttr implements Map {
0120: Map/*<String,Attr>*/map;
0121:
0122: /** Creates new XMLMapAttr and delegetaor is instanced */
0123: public XMLMapAttr() {
0124: this .map = new HashMap(5);
0125: }
0126:
0127: static Attr createAttributeAndDecode(String key, String value) {
0128: if (Attr.isValid(key) == Attr.isValid("stringvalue")) { // NOI18N
0129: value = Attr.decode(value);
0130: }
0131:
0132: return new Attr(key, value);
0133: }
0134:
0135: static Attr createAttribute(int index, String value) {
0136: return new Attr(index, value);
0137: }
0138:
0139: /** According to name of attribute returns attribute as object
0140: * @param p1 is name of attribute
0141: * @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
0142: */
0143: public Object get(final Object p1) {
0144: Object obj;
0145:
0146: try {
0147: obj = getAttribute(p1);
0148: } catch (Exception e) {
0149: obj = null;
0150: ExternalUtil.exception(e);
0151: }
0152:
0153: return obj;
0154: }
0155:
0156: /** According to name of attribute returns attribute as object
0157: * @param params has sense only for methodvalue invocation; and only 2 parametres will be used
0158: * @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
0159: */
0160: public Object get(final Object p1, Object[] params) {
0161: Object obj;
0162:
0163: try {
0164: obj = getAttribute(p1, params);
0165: } catch (Exception e) {
0166: obj = null;
0167: ExternalUtil.exception(e);
0168: }
0169:
0170: return obj;
0171: }
0172:
0173: /** implementation of Map.get. But fires Exception to have chance in
0174: * DefaultAttributes to catch and annotate*/
0175: Object getAttribute(Object attrName) throws Exception {
0176: return getAttribute(attrName, null);
0177: }
0178:
0179: private Object getAttribute(Object attrName, Object[] params)
0180: throws Exception {
0181: Attr attr;
0182: String origAttrName = (String) attrName;
0183: Object[] keyValuePair = ModifiedAttribute.translateInto(
0184: (String) attrName, null);
0185: attrName = (String) keyValuePair[0];
0186:
0187: synchronized (this ) {
0188: attr = (Attr) map.get(attrName);
0189: }
0190:
0191: Object retVal = null;
0192:
0193: try {
0194: retVal = (attr == null) ? attr : attr.get(params);
0195: } catch (Exception e) {
0196: ExternalUtil.annotate(e, "attrName = " + attrName); //NOI18N
0197: throw e;
0198: }
0199:
0200: if (retVal instanceof ModifiedAttribute) {
0201: Object res = ((ModifiedAttribute) retVal)
0202: .getValue(origAttrName);
0203:
0204: if (res instanceof Attr) {
0205: return ((Attr) res).get(params);
0206: } else {
0207: return res;
0208: }
0209: }
0210:
0211: return retVal;
0212: }
0213:
0214: /**
0215: * @param p1 is name of attribute
0216: * @param p2 is attribute as object
0217: * @return previous value associated with specified key, or null if there was no mapping for key.
0218: * A null return can also indicate that the HashMap previously associated null with the specified key.
0219: */
0220: public synchronized Object put(final Object p1, final Object p2) {
0221: return put(p1, p2, true);
0222: }
0223:
0224: synchronized Object put(final Object p1, final Object p2,
0225: boolean decode) {
0226: if ((p1 == null) || !(p1 instanceof String)) {
0227: return null;
0228: }
0229:
0230: Object[] keyValuePair = ModifiedAttribute.translateInto(
0231: (String) p1, p2);
0232: String key = (String) keyValuePair[0];
0233: Object value = keyValuePair[1];
0234: Object toStore = ((value == null) || value instanceof Attr) ? value
0235: : new Attr(value);
0236:
0237: if (decode) {
0238: key = Attr.decode(key).intern();
0239: }
0240:
0241: return map.put(key, toStore);
0242: }
0243:
0244: /**
0245: * Writes heading to XML file
0246: * @param pw where to write
0247: */
0248: public static void writeHeading(PrintWriter pw) {
0249: pw.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); // NOI18N
0250: pw
0251: .println("<!DOCTYPE attributes PUBLIC \"-//NetBeans//DTD DefaultAttributes 1.0//EN\" \"http://www.netbeans.org/dtds/attributes-1_0.dtd\">"); //NOI18N
0252: pw.println("<attributes version=\"1.0\">"); // NOI18N
0253: }
0254:
0255: /**
0256: * Writes ending to XML file
0257: * @param pw where to write
0258: */
0259: public static void writeEnding(PrintWriter pw) {
0260: pw.println("</attributes>"); // NOI18N
0261: }
0262:
0263: /**
0264: * Writes all attributes for one FileObject with fileName
0265: * @param pw where to write
0266: * @param fileName
0267: * @param blockPrefix is prefix which is used before each line
0268: */
0269: public synchronized void write(PrintWriter pw,
0270: final String fileName, String blockPrefix) {
0271: boolean isHeadingWr = false;
0272:
0273: if (isEmpty()) {
0274: return;
0275: }
0276:
0277: //pw.println(blockPrefix+"<fileobject name=\""+fileName+"\">");// NOI18N
0278: SortedSet<String> attrNames = new TreeSet<String>();
0279: Iterator entryIter = map.entrySet().iterator();
0280:
0281: while (entryIter.hasNext()) {
0282: Map.Entry entry = (Map.Entry) entryIter.next();
0283:
0284: String attrName = (String) entry.getKey();
0285: Attr attr = (Attr) entry.getValue();
0286:
0287: if ((attrName == null) || (attr == null)
0288: || (attrName.length() == 0)
0289: || (attr.isValid() == -1)) {
0290: if ((attrName != null) && (attrName.length() != 0)
0291: && ((attr == null) || (attr.isValid() == -1))) {
0292: entryIter.remove();
0293: }
0294:
0295: continue;
0296: }
0297:
0298: attrNames.add(attrName);
0299: }
0300:
0301: entryIter = attrNames.iterator();
0302:
0303: while (entryIter.hasNext()) {
0304: String attrName = (String) entryIter.next();
0305: Attr attr = (Attr) map.get(attrName);
0306:
0307: if (attr != null) {
0308: attr.transformMe();
0309: }
0310:
0311: if (!isHeadingWr) {
0312: isHeadingWr = true;
0313:
0314: String quotedFileName = fileName;
0315:
0316: try {
0317: quotedFileName = org.openide.xml.XMLUtil
0318: .toAttributeValue(fileName);
0319: } catch (IOException ignore) {
0320: }
0321:
0322: pw.println(blockPrefix + "<fileobject name=\""
0323: + quotedFileName + "\">"); // NOI18N
0324: }
0325:
0326: pw.println(blockPrefix + blockPrefix + "<attr name=\""
0327: + attr.getAttrNameForPrint(attrName) + "\" "
0328: + attr.getKeyForPrint() + "=\""
0329: + attr.getValueForPrint() + "\"/>"); // NOI18N
0330: attr.maybeAddSerValueComment(pw, blockPrefix + blockPrefix);
0331: }
0332:
0333: if (isHeadingWr) {
0334: pw.println(blockPrefix + "</fileobject>"); // NOI18N
0335: }
0336: }
0337:
0338: public synchronized void clear() {
0339: map.clear();
0340: }
0341:
0342: public synchronized Object remove(Object p1) {
0343: return map.remove(p1);
0344: }
0345:
0346: public synchronized boolean containsValue(Object p1) {
0347: return map.containsValue(p1);
0348: }
0349:
0350: public synchronized int hashCode() {
0351: return map.hashCode();
0352: }
0353:
0354: public synchronized java.util.Set<String> keySet() {
0355: return map.keySet();
0356: }
0357:
0358: public synchronized java.util.Collection values() {
0359: return map.values();
0360: }
0361:
0362: // XXX this is wrong - values not translated
0363: public synchronized java.util.Set entrySet() {
0364: return map.entrySet();
0365: }
0366:
0367: public synchronized void putAll(java.util.Map p1) {
0368: map.putAll(p1);
0369: }
0370:
0371: public synchronized boolean containsKey(Object p1) {
0372: return map.containsKey(p1);
0373: }
0374:
0375: public synchronized boolean isEmpty() {
0376: return map.isEmpty();
0377: }
0378:
0379: public synchronized boolean equals(Object p1) {
0380: return map.equals(p1);
0381: }
0382:
0383: public synchronized int size() {
0384: return map.size();
0385: }
0386:
0387: /**
0388: * Holds textual representation of one attribute. And on request construct new instance of
0389: * 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.
0390: * Detailed describtion is in <A HREF="XMLMapAttr.html">XMLMapAttr<A>
0391: */
0392: final static class Attr extends java.lang.Object {
0393: // static final long serialVersionUID = -62733358015297232L;
0394: private static final String[] ALLOWED_ATTR_KEYS = {
0395: "bytevalue", "shortvalue", "intvalue", "longvalue",
0396: "floatvalue", "doublevalue", "boolvalue", "charvalue",
0397: "stringvalue", "methodvalue", "serialvalue",
0398: "urlvalue", "newvalue" }; // NOI18N
0399: private String value;
0400: private int keyIndex;
0401: private Object obj; //back compatibility
0402:
0403: private Attr(Object obj) {
0404: this .obj = obj;
0405: }
0406:
0407: private Attr(int index, String value) {
0408: keyIndex = index;
0409: this .value = (value != null) ? value.intern() : null;
0410: }
0411:
0412: /**
0413: * @param key One of the possible keys:
0414: * "bytevalue","shortvalue","intvalue","longvalue","floatvalue","doublevalue","boolvalue","charvalue","stringvalue","methodvalue","serialvalue","urlvalue"
0415: * @param value Corresponding value to key in textual form.
0416: */
0417: private Attr(String key, String value) {
0418: keyIndex = isValid(key);
0419: this .value = value.intern();
0420: }
0421:
0422: /**
0423: * @return array of Strings. Each String is textual form of allowed type of attribute - textual form of key
0424: */
0425: static String[] getAttrTypes() {
0426: return ALLOWED_ATTR_KEYS;
0427: }
0428:
0429: /**
0430: * Checks if key is valid and sets key and value
0431: * @param key Key of attribute. Defines type of attribute in textual form.
0432: * @param value Value of attribute. Defines value of attribute as literal or HEX expression of serialization.
0433: */
0434: private final void putEntry(String key, String value) {
0435: this .keyIndex = isValid(key);
0436: this .value = value.intern();
0437: }
0438:
0439: /**
0440: * added for future use - convert NbMarshalledObject to primitive data types and other supported types (if possible)
0441: */
0442: static Object unMarshallObjectRecursively(Object mo) {
0443: Object o = mo;
0444:
0445: while (o instanceof NbMarshalledObject) {
0446: try {
0447: o = ((NbMarshalledObject) o).get();
0448: } catch (IOException e) {
0449: ExternalUtil.exception(e);
0450:
0451: return mo;
0452: } catch (ClassNotFoundException e) {
0453: ExternalUtil.exception(e);
0454:
0455: return mo;
0456: }
0457: }
0458:
0459: return (o == null) ? mo : o;
0460: }
0461:
0462: /**Method for back compatibility; called in write*/
0463: private void transformMe() {
0464: int objType;
0465:
0466: if (obj == null) {
0467: return;
0468: }
0469:
0470: Object unObj = unMarshallObjectRecursively(obj);
0471:
0472: if (unObj != null) {
0473: if ((objType = XMLMapAttr.Attr.distinguishObject(unObj)) != XMLMapAttr.Attr
0474: .isValid("SERIALVALUE")) { // NOI18N
0475: obj = null;
0476: putEntry(ALLOWED_ATTR_KEYS[objType], unObj
0477: .toString());
0478: } else {
0479: String newValue;
0480:
0481: try {
0482: newValue = encodeValue(unObj);
0483: } catch (IOException iox) {
0484: return;
0485: }
0486:
0487: obj = null;
0488: putEntry(ALLOWED_ATTR_KEYS[objType], newValue);
0489: }
0490: }
0491: }
0492:
0493: /**
0494: * added for future use - convert NbMarshalledObject to primitive data types and other supported types (if possible)
0495: */
0496: static int distinguishObject(Object o) {
0497: if (o instanceof Byte) {
0498: return isValid("BYTEVALUE"); // NOI18N
0499: }
0500:
0501: if (o instanceof Short) {
0502: return isValid("SHORTVALUE"); // NOI18N
0503: }
0504:
0505: if (o instanceof Integer) {
0506: return isValid("INTVALUE"); // NOI18N
0507: }
0508:
0509: if (o instanceof Long) {
0510: return isValid("LONGVALUE"); // NOI18N
0511: }
0512:
0513: if (o instanceof Float) {
0514: return isValid("FLOATVALUE"); // NOI18N
0515: }
0516:
0517: if (o instanceof Double) {
0518: return isValid("DOUBLEVALUE"); // NOI18N
0519: }
0520:
0521: if (o instanceof Boolean) {
0522: return isValid("BOOLVALUE"); // NOI18N
0523: }
0524:
0525: if (o instanceof Character) {
0526: return isValid("CHARVALUE"); // NOI18N
0527: }
0528:
0529: if (o instanceof String) {
0530: return isValid("STRINGVALUE"); // NOI18N
0531: }
0532:
0533: if (o instanceof URL) {
0534: return isValid("URLVALUE"); // NOI18N
0535: }
0536:
0537: return isValid("SERIALVALUE"); // NOI18N
0538: }
0539:
0540: static String encode(String inStr) {
0541: try {
0542: inStr = org.openide.xml.XMLUtil.toAttributeValue(inStr);
0543: } catch (Exception ignore) {
0544: }
0545:
0546: StringBuffer outStr = new StringBuffer(6 * inStr.length());
0547:
0548: for (int i = 0; i < inStr.length(); i++) {
0549: if (Character.isISOControl(inStr.charAt(i))
0550: || isEncodedChar(i, inStr)) {
0551: outStr.append(encodeChar(inStr.charAt(i)));
0552:
0553: continue;
0554: }
0555:
0556: outStr.append(inStr.charAt(i));
0557: }
0558:
0559: return outStr.toString();
0560: }
0561:
0562: static String encodeChar(char ch) {
0563: String encChar = Integer.toString((int) ch, 16);
0564:
0565: return "\\u"
0566: + "0000".substring(0,
0567: "0000".length() - encChar.length()).concat(
0568: encChar); // NOI18N
0569: }
0570:
0571: static String decode(final String inStr) {
0572: StringBuffer outStr = new StringBuffer(inStr.length());
0573:
0574: try {
0575: for (int i = 0; i < inStr.length(); i++) {
0576: if (isEncodedChar(i, inStr)) {
0577: String decChar = inStr.substring(i + 2, i + 6);
0578: outStr.append((char) Integer.parseInt(decChar,
0579: 16));
0580: i += 5;
0581: } else {
0582: outStr.append(inStr.charAt(i));
0583: }
0584: }
0585: } catch (NumberFormatException e) {
0586: Exceptions.printStackTrace(e);
0587:
0588: return inStr;
0589: }
0590:
0591: return outStr.toString();
0592: }
0593:
0594: private static boolean isEncodedChar(final int currentPosition,
0595: final String inStr) {
0596: boolean isEncodedChar = (currentPosition + 5) < inStr
0597: .length();
0598:
0599: if (isEncodedChar) {
0600: isEncodedChar &= ((inStr.charAt(currentPosition) == '\\') && (inStr
0601: .charAt(currentPosition + 1) == 'u'));
0602:
0603: for (int i = currentPosition + 2; isEncodedChar
0604: && (i < (currentPosition + 6)); i++) {
0605: char c = inStr.charAt(i);
0606: isEncodedChar &= (Character.digit(c, 16) != -1);
0607: }
0608: }
0609:
0610: return isEncodedChar;
0611: }
0612:
0613: /**
0614: * Constructs new attribute as Object. Used for static creation from literal or serialValue.
0615: * @return new attribute as Object
0616: */
0617: private Object get() throws Exception {
0618: return getObject(null); //getObject is ready to aobtain null
0619: }
0620:
0621: /**
0622: * Constructs new attribute as Object. Used for dynamic creation: methodvalue .
0623: * @param objs has sense only for methodvalue invocation; and only 2 parametres will be used
0624: *@return new attribute as Object
0625: */
0626: private Object get(Object[] objs) throws Exception {
0627: return getObject(objs);
0628: }
0629:
0630: /**
0631: * @return key. Key expresses type of this attribute (in textual form) or "" if internal error.
0632: */
0633: final String getKey() {
0634: String[] keyArray = getAttrTypes();
0635:
0636: if (obj != null) {
0637: return "serialvalue"; //back compatibility // NOI18N
0638: }
0639:
0640: if (isValid() == -1) {
0641: return ""; // NOI18N
0642: }
0643:
0644: return keyArray[keyIndex];
0645: }
0646:
0647: /**
0648: * @return value in textual format or "" if internal error.
0649: */
0650: final String getValue() {
0651: if (obj != null) {
0652: getValue(obj);
0653: }
0654:
0655: return (value != null) ? value : ""; // NOI18N
0656: }
0657:
0658: static final String getValue(Object obj) {
0659: try {
0660: return encodeValue(obj); //back compatibility
0661: } catch (IOException ioe) {
0662: return ""; // NOI18N
0663: }
0664: }
0665:
0666: final String getValueForPrint() {
0667: if (obj != null) {
0668: Attr modifAttr = null;
0669:
0670: if (obj instanceof ModifiedAttribute) {
0671: modifAttr = (Attr) ((ModifiedAttribute) obj)
0672: .getValue();
0673: }
0674:
0675: return (modifAttr != null) ? encode(modifAttr
0676: .getValue()) : encode(getValue());
0677: }
0678:
0679: return (value != null) ? encode(value) : ""; // NOI18N
0680: }
0681:
0682: final String getKeyForPrint() {
0683: if ((obj != null) && obj instanceof ModifiedAttribute) {
0684: Attr modifAttr = (Attr) ((ModifiedAttribute) obj)
0685: .getValue();
0686: int keyIdx = Attr.isValid("SERIALVALUE"); //NOI18N
0687:
0688: if (modifAttr != null) {
0689: keyIdx = distinguishObject(modifAttr.getValue());
0690: }
0691:
0692: String[] keyArray = getAttrTypes();
0693:
0694: return keyArray[keyIdx];
0695: }
0696:
0697: return getKey();
0698: }
0699:
0700: final String getAttrNameForPrint(String attrName) {
0701: if ((obj != null) && obj instanceof ModifiedAttribute) {
0702: Object[] retVal = ModifiedAttribute.revert(attrName,
0703: obj);
0704:
0705: return encode((String) retVal[0]);
0706: }
0707:
0708: return encode(attrName);
0709: }
0710:
0711: final void maybeAddSerValueComment(PrintWriter pw, String indent) {
0712: if (obj != null) {
0713: Object modifObj = null;
0714:
0715: if (obj instanceof ModifiedAttribute) {
0716: modifObj = ((Attr) ((ModifiedAttribute) obj)
0717: .getValue()).getValue();
0718:
0719: if (distinguishObject(modifObj) != Attr
0720: .isValid("SERIALVALUE")) { //NOI18N
0721:
0722: return;
0723: }
0724: }
0725:
0726: // Important for debugging to know what this stuff really is.
0727: // Note this comment is only written to disk when the attr is
0728: // first saved; after that successive saves will just know the
0729: // ser value and will not print the comment. So look at .nbattrs
0730: // immediately after setting something serialized. --jglick
0731: pw.print(indent);
0732: pw.print("<!-- "); // NOI18N
0733:
0734: String s = (modifObj != null) ? modifObj.toString()
0735: : obj.toString();
0736:
0737: if (s.indexOf("--") != -1) { // NOI18N
0738:
0739: // XML comment no-no.
0740: s = s.replace('-', '_'); // NOI18N
0741: }
0742:
0743: pw.print(s);
0744: pw.println(" -->"); // NOI18N
0745: }
0746: }
0747:
0748: /**
0749: * Creates serialized object, which was encoded in HEX format
0750: * @param value Encoded serialized object in HEX format
0751: * @return Created object from encoded HEX format
0752: * @throws IOException
0753: */
0754: static Object decodeValue(String value) throws IOException {
0755: if ((value == null) || (value.length() == 0)) {
0756: return null;
0757: }
0758:
0759: byte[] bytes = new byte[value.length() / 2];
0760: int tempI;
0761: int count = 0;
0762:
0763: for (int i = 0; i < value.length(); i += 2) {
0764: try {
0765: tempI = Integer.parseInt(value.substring(i, i + 2),
0766: 16);
0767:
0768: if (tempI > 127) {
0769: tempI -= 256;
0770: }
0771:
0772: bytes[count++] = (byte) tempI;
0773: } catch (NumberFormatException e) {
0774: throw (IOException) ExternalUtil.copyAnnotation(
0775: new IOException(), e);
0776: }
0777: }
0778:
0779: ByteArrayInputStream bis = new ByteArrayInputStream(bytes,
0780: 0, count);
0781:
0782: try {
0783: ObjectInputStream ois = new NbObjectInputStream(bis);
0784: Object ret = ois.readObject();
0785:
0786: return ret;
0787: } catch (Exception e) {
0788: throw (IOException) ExternalUtil.copyAnnotation(
0789: new IOException(), e);
0790: }
0791:
0792: /*unreachable code*/
0793:
0794: //throw new InternalError ();
0795: }
0796:
0797: /**
0798: * Encodes Object into String encoded in HEX format
0799: * @param value Object, which will be encoded
0800: * @return serialized Object in String encoded in HEX format
0801: * @throws IOException
0802: */
0803: static String encodeValue(Object value) throws IOException {
0804: ByteArrayOutputStream bos = new ByteArrayOutputStream();
0805:
0806: try {
0807: ObjectOutputStream oos = new ObjectOutputStream(bos);
0808: oos.writeObject(value);
0809: oos.close();
0810: } catch (Exception e) {
0811: throw (IOException) ExternalUtil.copyAnnotation(
0812: new IOException(), e);
0813: }
0814:
0815: byte[] bArray = bos.toByteArray();
0816: StringBuffer strBuff = new StringBuffer(bArray.length * 2);
0817:
0818: for (int i = 0; i < bArray.length; i++) {
0819: if ((bArray[i] < 16) && (bArray[i] >= 0)) {
0820: strBuff.append("0"); // NOI18N
0821: }
0822:
0823: strBuff
0824: .append(Integer
0825: .toHexString((bArray[i] < 0) ? (bArray[i] + 256)
0826: : bArray[i]));
0827: }
0828:
0829: return strBuff.toString();
0830: }
0831:
0832: /**
0833: * Encodes Object into String encoded in HEX format
0834: * @param params Array (2 length) of objects ( Object[] o = {fo,name}). Attribute is assigned to some fo-FileObject and has its name-String.
0835: * params can be null.
0836: * @return Object or null
0837: */
0838: private Object getObject(Object[] params) throws Exception {
0839: int index;
0840:
0841: if (obj != null) {
0842: return obj; //back compatibility
0843: }
0844:
0845: if ((index = isValid()) != -1) {
0846: try {
0847: switch (index) {
0848: case 0:
0849: return new Byte(value);
0850:
0851: case 1:
0852: return new Short(value);
0853:
0854: case 2:
0855: return new Integer(value); //(objI);
0856:
0857: case 3:
0858: return new Long(value);
0859:
0860: case 4:
0861: return new Float(value);
0862:
0863: case 5:
0864: return new Double(value);
0865:
0866: case 6:
0867: return Boolean.valueOf(value);
0868:
0869: case 7:
0870:
0871: if (value.trim().length() != 1) {
0872: break;
0873: }
0874:
0875: return new Character(value.charAt(0));
0876:
0877: case 8:
0878: return value;
0879:
0880: case 9:
0881: return methodValue(value, params);
0882:
0883: case 10:
0884: return decodeValue(value);
0885:
0886: case 11:
0887: return new URL(value);
0888:
0889: case 12:
0890:
0891: // special support for singletons
0892: Class cls = ExternalUtil.findClass(Utilities
0893: .translate(value));
0894:
0895: if (SharedClassObject.class
0896: .isAssignableFrom(cls)) {
0897: return SharedClassObject.findObject(cls,
0898: true);
0899: } else {
0900: return cls.newInstance();
0901: }
0902: }
0903: } catch (Exception exc) {
0904: ExternalUtil.annotate(exc, "value = " + value); //NOI18N
0905: throw exc;
0906: } catch (LinkageError e) {
0907: throw (ClassNotFoundException) ExternalUtil
0908: .annotate(
0909: new ClassNotFoundException(value),
0910: e);
0911: }
0912: }
0913:
0914: throw new InstantiationException(value);
0915: }
0916:
0917: /** Constructs new attribute as Object. Used for dynamic creation: methodvalue .
0918: * @param params only 2 parametres will be used
0919: * @return Object or null
0920: */
0921: private final Object methodValue(String value, Object[] params)
0922: throws Exception {
0923: int sepIdx = value.lastIndexOf('.');
0924:
0925: if (sepIdx != -1) {
0926: String methodName = value.substring(sepIdx + 1);
0927: Class cls = ExternalUtil.findClass(value.substring(0,
0928: sepIdx));
0929: FileObject fo = null;
0930: String attrName = null;
0931:
0932: for (int i = 0; i < params.length; i++) {
0933: if ((fo == null) && params[i] instanceof FileObject) {
0934: fo = (FileObject) params[i];
0935: }
0936:
0937: if ((attrName == null)
0938: && params[i] instanceof String) {
0939: attrName = (String) params[i];
0940: }
0941: }
0942:
0943: Object[] paramArray = new Object[] {
0944: new Class[] { FileObject.class, String.class },
0945: new Class[] { String.class, FileObject.class },
0946: new Class[] { FileObject.class },
0947: new Class[] { String.class }, new Class[] {},
0948: new Class[] { Map.class, String.class },
0949: new Class[] { Map.class }, };
0950:
0951: boolean both = ((fo != null) && (attrName != null));
0952: Object[] objectsList = new Object[7];
0953: objectsList[0] = (both) ? new Object[] { fo, attrName }
0954: : null;
0955: objectsList[1] = (both) ? new Object[] { attrName, fo }
0956: : null;
0957: objectsList[2] = (fo != null) ? new Object[] { fo }
0958: : null;
0959: objectsList[3] = (attrName != null) ? new Object[] { attrName }
0960: : null;
0961: objectsList[4] = new Object[] {};
0962:
0963: Map fileMap = wrapToMap(fo);
0964: objectsList[5] = attrName != null ? new Object[] {
0965: fileMap, attrName } : null;
0966: objectsList[6] = new Object[] { fileMap };
0967:
0968: for (int i = 0; i < paramArray.length; i++) {
0969: Object[] objArray = (Object[]) objectsList[i];
0970:
0971: if (objArray == null) {
0972: continue;
0973: }
0974:
0975: try {
0976: Method method = cls.getDeclaredMethod(
0977: methodName, (Class[]) paramArray[i]);
0978:
0979: if (method != null) {
0980: method.setAccessible(true);
0981:
0982: return method.invoke(null, objArray);
0983: }
0984: } catch (NoSuchMethodException nsmExc) {
0985: continue;
0986: }
0987: }
0988: }
0989:
0990: throw new InstantiationException(value);
0991: }
0992:
0993: static final Map wrapToMap(FileObject fo) {
0994: return fo == null ? Collections.EMPTY_MAP : new FileMap(fo);
0995: }
0996:
0997: /**
0998: * Checks if key is valid
0999: * @return Index to array of allowed keys or -1 which means error.
1000: */
1001: final int isValid() {
1002: String[] keyArray = getAttrTypes();
1003:
1004: if (obj != null) {
1005: return isValid("SERIALVALUE"); //back compatibility // NOI18N
1006: }
1007:
1008: if ((keyIndex >= keyArray.length) || (keyIndex < 0)) {
1009: return -1;
1010: }
1011:
1012: return keyIndex;
1013: }
1014:
1015: /**
1016: * Checks if key is valid
1017: * @return Index to array of allowed keys or -1 which means error.
1018: */
1019: final static int isValid(String key) {
1020: int index = -1;
1021: int i;
1022: String[] strArray = getAttrTypes();
1023: String trimmedKey = key.trim();
1024:
1025: for (i = 0; i < strArray.length; i++) {
1026: if (trimmedKey.equalsIgnoreCase(strArray[i]) == true) {
1027: index = i;
1028:
1029: break;
1030: }
1031: }
1032:
1033: return index;
1034: }
1035:
1036: public boolean equals(Object obj) {
1037: if (obj instanceof Attr) {
1038: Attr other = (Attr) obj;
1039:
1040: if (other.keyIndex != keyIndex) {
1041: return false;
1042: }
1043:
1044: return other.value.equals(value);
1045: }
1046: return false;
1047: }
1048:
1049: public int hashCode() {
1050: return 743 + keyIndex << 8 + value.hashCode();
1051: }
1052: }
1053:
1054: /**
1055: * Helper class for decorating attributes with modifiers.
1056: * Object that is made persistent using setAttribute can contain also modifiers.
1057: * This class is wrapper class that holds original object and its modifiers.
1058: * Intended as replacer of original class in attributes.
1059: * Currently exists only one modifier: tranisent.
1060: * Transient modifier means that such attribute won`t be copied with FileObject.
1061: */
1062: static class ModifiedAttribute implements java.io.Serializable {
1063: /** generated Serialized Version UID */
1064: static final long serialVersionUID = 84214031923497718L;
1065: private final static String[] fragments = new String[] { "transient:" }; //NOI18N
1066: private int modifier = 0;
1067: private Object origAttrValue = null;
1068:
1069: /** Creates a new instance of AttributeFactory */
1070: private ModifiedAttribute(Object origAttrValue) {
1071: this .origAttrValue = origAttrValue;
1072: }
1073:
1074: /** This method looks for modifiers in attribute name (currently transient:).
1075: *
1076: * @param attrName original name of attribute
1077: * @param value original value - can be null
1078: * @return Object array with size 2.
1079: * If there are no modifiers in attribute name, then is returned Object array with
1080: * , where first is placed unchanged attribute name, and then uchanged value.
1081: * If there are modifiers in attribute name, then as attribute name is returned
1082: * stripped original attribute name (without modifiers) and ModifiedAttribute object,
1083: * that wraps original object and also contain modifiers.
1084: */
1085: static Object[] translateInto(String attrName, Object value) {
1086: String newAttrName = attrName;
1087: Object newValue = value;
1088: ModifiedAttribute attr = null;
1089:
1090: for (int i = 0; i < fragments.length; i++) {
1091: String fragment = fragments[i];
1092: int idx = newAttrName.indexOf(fragment);
1093:
1094: if (idx != -1) {
1095: /** fragment is cleared away */
1096: newAttrName = newAttrName.substring(0, idx)
1097: + newAttrName.substring(idx
1098: + fragment.length());
1099:
1100: if (attr == null) {
1101: newValue = attr = new ModifiedAttribute(value);
1102: }
1103:
1104: attr.modifier |= (1 << i); //set modifier
1105: }
1106: }
1107:
1108: return new Object[] { newAttrName, newValue };
1109: }
1110:
1111: /**
1112: * This method is opposite to method translateInto
1113: */
1114: static Object[] revert(String attrName, Object value) {
1115: if (!(value instanceof ModifiedAttribute)
1116: || (value == null)) {
1117: return new Object[] { attrName, value };
1118: }
1119:
1120: ModifiedAttribute attr = (ModifiedAttribute) value;
1121: String newAttrName = attrName;
1122: Object newValue = attr;
1123:
1124: for (int i = 0; i < fragments.length; i++) {
1125: String fragment = fragments[i];
1126:
1127: if (((attr.modifier & (1 << i)) != 0)
1128: && (fragment != null)) {
1129: /** fragment is cleared away */
1130: newAttrName = fragment + newAttrName;
1131:
1132: if (newValue instanceof ModifiedAttribute) {
1133: newValue = attr.origAttrValue;
1134: }
1135: }
1136: }
1137:
1138: return new Object[] { newAttrName, newValue };
1139: }
1140:
1141: /** ModifiedAttribute holds original value + modifiers. This method returns original value.
1142: * @return If there are no modifiers in attribute name, then returns original value
1143: * If there are modifiers in attribute name, then returns current instance of
1144: * ModifiedAttribute.
1145: */
1146: Object getValue(String attrName) {
1147: for (int i = 0; i < fragments.length; i++) {
1148: String fragment = fragments[i];
1149: int idx = attrName.indexOf(fragment);
1150:
1151: if (idx != -1) {
1152: return this ;
1153: }
1154: }
1155:
1156: return origAttrValue;
1157: }
1158:
1159: /** ModifiedAttribute holds original value + modifiers. This method returns original value.
1160: * @return then returns original value
1161: */
1162: Object getValue() {
1163: return getValue(""); //NOI18N
1164: }
1165:
1166: /**
1167: * Decides if value stored in attributes is transient
1168: * @param fo fileobject where attribute is looked for
1169: * @param attrName name of attribute
1170: * @return true if transient
1171: */
1172: static boolean isTransient(FileObject fo, String attrName) {
1173: Object value = fo.getAttribute(fragments[0] + attrName);
1174:
1175: if (value instanceof ModifiedAttribute) {
1176: return ((((ModifiedAttribute) value).modifier & (1 << 0)) == 0) ? false
1177: : true;
1178: }
1179:
1180: return false;
1181: }
1182: }
1183:
1184: private static final class FileMap extends
1185: AbstractMap<String, Object> {
1186: private FileObject fo;
1187:
1188: private FileMap(FileObject fo) {
1189: this .fo = fo;
1190: }
1191:
1192: public Set<Map.Entry<String, Object>> entrySet() {
1193: return new AttrFileSet(fo);
1194: }
1195:
1196: public Object get(String key) {
1197: return fo.getAttribute(key);
1198: }
1199:
1200: public Object remove(Object key) {
1201: throw new UnsupportedOperationException();
1202: }
1203:
1204: public Object put(String key, Object value) {
1205: throw new UnsupportedOperationException();
1206: }
1207:
1208: }
1209:
1210: private static final class AttrFileSet extends
1211: AbstractSet<Map.Entry<String, Object>> {
1212: private FileObject fo;
1213:
1214: private AttrFileSet(FileObject fo) {
1215: this .fo = fo;
1216: }
1217:
1218: public Iterator<Map.Entry<String, Object>> iterator() {
1219: class Iter implements Iterator<Map.Entry<String, Object>> {
1220: Enumeration<String> attrs = fo.getAttributes();
1221:
1222: public boolean hasNext() {
1223: return attrs.hasMoreElements();
1224: }
1225:
1226: public Map.Entry<String, Object> next() {
1227: String s = attrs.nextElement();
1228: return new FOEntry(fo, s);
1229: }
1230:
1231: public void remove() {
1232: throw new UnsupportedOperationException();
1233: }
1234: }
1235: return new Iter();
1236: }
1237:
1238: public int size() {
1239: Enumeration<String> all = fo.getAttributes();
1240: int cnt = 0;
1241: while (all.hasMoreElements()) {
1242: cnt++;
1243: all.nextElement();
1244: }
1245: return cnt;
1246: }
1247:
1248: public boolean remove(Object o) {
1249: throw new UnsupportedOperationException();
1250: }
1251: } // end of AttrFileSet
1252:
1253: private static final class FOEntry implements
1254: Map.Entry<String, Object> {
1255: private FileObject fo;
1256: private String attr;
1257:
1258: private FOEntry(FileObject fo, String attr) {
1259: this .fo = fo;
1260: this .attr = attr;
1261: }
1262:
1263: public String getKey() {
1264: return attr;
1265: }
1266:
1267: public Object getValue() {
1268: return fo.getAttribute(attr);
1269: }
1270:
1271: public Object setValue(Object value) {
1272: throw new UnsupportedOperationException();
1273: }
1274: } // end of FOEntry
1275: }
|