KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > settings > convertors > XMLSettingsSupport


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.settings.convertors;
21
22 import java.io.*;
23 import java.lang.reflect.Method JavaDoc;
24 import java.util.*;
25 import java.util.logging.Level JavaDoc;
26 import java.util.logging.Logger JavaDoc;
27
28 import org.openide.filesystems.*;
29 import org.openide.modules.ModuleInfo;
30 import org.openide.modules.SpecificationVersion;
31 import org.openide.util.Exceptions;
32 import org.openide.util.Lookup;
33 import org.openide.util.SharedClassObject;
34
35 import org.xml.sax.Attributes JavaDoc;
36 import org.xml.sax.SAXException JavaDoc;
37 import org.xml.sax.XMLReader JavaDoc;
38
39 /** Class provides support for storing and reading session settings.
40  *
41  * @see SerialDataConvertor
42  * @author Jan Pokorsky
43  */

44 final class XMLSettingsSupport {
45     
46     public static final String JavaDoc INSTANCE_DTD_ID = "-//NetBeans//DTD Session settings 1.0//EN"; // NOI18N
47
public static final String JavaDoc INSTANCE_DTD_WWW = "http://www.netbeans.org/dtds/sessionsettings-1_0.dtd"; // NOI18N
48
/** File extension for xml settings. */
49     public static final String JavaDoc XML_EXT = "settings"; //NOI18N
50
public static final String JavaDoc LINE_SEPARATOR = System.getProperty("line.separator");
51     
52     /** Logging for events in XML settings system. */
53     static final Logger JavaDoc err = Logger.getLogger(XMLSettingsSupport.class.getName()); // NOI18N
54

55     /** Store instanceof elements.
56      * @param classes everything what class extends or implements
57      * @param pw output
58      */

59     private static void storeInstanceOf (Set<Class JavaDoc> classes, PrintWriter pw) throws IOException {
60         SortedSet<String JavaDoc> clazzNames = new TreeSet<String JavaDoc>(); // sort them: #24661
61
for (Class JavaDoc clz: classes) {
62             clazzNames.add(clz.getName());
63         }
64         StringBuilder JavaDoc sb = new StringBuilder JavaDoc (200); // XXX estimate right capacity
65
for (String JavaDoc s: clazzNames) {
66             sb.append(" <instanceof class=\""). // NOI18N
67
append(s).
68             append("\"/>").append(LINE_SEPARATOR); // NOI18N
69
}
70         pw.print(sb.toString());
71     }
72     
73     /** Store settings version 1.0
74      * @param inst settings instance
75      * @param os output
76      */

77     public static void storeToXML10 (Object JavaDoc inst, Writer os, ModuleInfo mi)
78     throws IOException {
79         PrintWriter pw = new PrintWriter (os);
80         
81         pw.println ("<?xml version=\"1.0\"?>"); // NOI18N
82
pw.println ("<!DOCTYPE settings PUBLIC \""+INSTANCE_DTD_ID+ // NOI18N
83
"\" \""+INSTANCE_DTD_WWW+"\">"); // NOI18N
84
pw.println ("<settings version=\"1.0\">"); // NOI18N
85
storeModule(mi, pw);
86         storeInstanceOf(getSuperClasses(inst.getClass(), null), pw);
87         // default storage has been implemented by serialization
88
storeSerialData(inst, pw);
89         
90         pw.println ("</settings>"); // NOI18N
91
pw.flush ();
92     }
93     
94     /** Store a default instance. Ensure compatibility for settings declared in
95      * a manifest.
96      * @param clazz class of instance
97      * @param os output
98      */

99     private static void storeToXML10 (Class JavaDoc clazz, Writer os, ModuleInfo mi)
100     throws IOException {
101         
102         PrintWriter pw = new PrintWriter (os);
103         pw.println ("<?xml version=\"1.0\"?>"); // NOI18N
104
pw.println ("<!DOCTYPE settings PUBLIC \""+INSTANCE_DTD_ID+ // NOI18N
105
"\" \""+INSTANCE_DTD_WWW+"\">"); // NOI18N
106
pw.println ("<settings version=\"1.0\">"); // NOI18N
107
storeModule(mi, pw);
108         storeInstanceOf(getSuperClasses(clazz, null), pw);
109         pw.println (" <instance class=\""+clazz.getName()+"\"/>"); // NOI18N
110
pw.println ("</settings>"); // NOI18N
111
pw.flush ();
112     }
113     
114     private static void storeModule(ModuleInfo mi, PrintWriter pw)
115     throws IOException {
116         if (mi == null) return;
117         
118         String JavaDoc modulName = mi.getCodeName();
119         SpecificationVersion spec = mi.getSpecificationVersion();
120         StringBuffer JavaDoc sb = new StringBuffer JavaDoc (80);
121         sb.append(" <module"); // NOI18N
122
if (modulName != null && modulName.length() != 0) {
123             sb.append(" name=\"").append(modulName).append('"');// NOI18N
124
}
125         if (spec != null) {
126             sb.append(" spec=\"").append(spec.toString()).append('"');// NOI18N
127
}
128         sb.append("/>"); // NOI18N
129
pw.println(sb.toString());
130     }
131     
132     
133     /** This object output stream subclass is used for storing InstanceDataObject.
134      * More details in bug #15563
135      */

136     private static class SpecialObjectOutputStream extends org.openide.util.io.NbObjectOutputStream {
137         /** Is the stream expecting the first object in stream? */
138         private boolean first;
139         
140         public SpecialObjectOutputStream(OutputStream os) throws IOException {
141             super (os);
142             first = true;
143         }
144
145         /** Check if the first object in the stream is <CODE>null</CODE>.
146          * If so, throw InvalidObjectException.
147          */

148         public Object JavaDoc replaceObject (Object JavaDoc obj) throws IOException {
149             if (first) {
150                 if (obj == null)
151                     // Object doesn't want to be serialized.
152
throw new NotSerializableException();
153                 first = false;
154             }
155             return super.replaceObject(obj);
156         }
157         
158
159     }
160     
161     /** Stream allowing upgrade to a new class inside the origin .settings file
162      */

163     private static class SpecialObjectInputStream extends java.io.ObjectInputStream JavaDoc {
164         
165         public SpecialObjectInputStream(InputStream is) throws IOException {
166             super(is);
167             try {
168                 enableResolveObject (true);
169             } catch (SecurityException JavaDoc ex) {
170                 throw new IOException (ex.toString ());
171             }
172         }
173
174         /* Uses NetBeans module classloader to load the class.
175          * @param v description of the class to load
176          */

177         protected Class JavaDoc resolveClass(ObjectStreamClass v) throws IOException, ClassNotFoundException JavaDoc {
178             ClassLoader JavaDoc cl = getNBClassLoader();
179             try {
180                 return Class.forName(v.getName(), false, cl);
181             } catch (ClassNotFoundException JavaDoc cnfe) {
182                 String JavaDoc msg = "Offending classloader: " + cl; // NOI18N
183
Exceptions.attachMessage(cnfe, msg);
184                 throw cnfe;
185             }
186         }
187         
188         /** use Utilities.translate to try to upgrade to new setting's class.
189          * If the old class exists the origin descriptor is used and upgrade is
190          * postponed to readResolve;
191          * otherwise the same implementation like NbObjectInputStream.readClassDescriptor
192          */

193         protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException JavaDoc {
194             ObjectStreamClass ose = super.readClassDescriptor();
195
196             String JavaDoc name = ose.getName();
197             String JavaDoc newN = org.openide.util.Utilities.translate(name);
198
199             if (name == newN) {
200                 // no translation
201
return ose;
202             }
203
204             ClassLoader JavaDoc cl = getNBClassLoader();
205             try {
206                 Class JavaDoc origCl = Class.forName(name, false, cl);
207                 // translation postponed to readResolve
208
return ObjectStreamClass.lookup(origCl);
209             } catch (ClassNotFoundException JavaDoc ex) {
210                 // ok look up new descriptor
211
}
212             
213             Class JavaDoc clazz = Class.forName(newN, false, cl);
214             ObjectStreamClass newOse = ObjectStreamClass.lookup(clazz);
215
216             // #28021 - it is possible that lookup return null. In that case the conversion
217
// table contains class which is not Serializable or Externalizable.
218
if (newOse == null) {
219                 throw new java.io.NotSerializableException JavaDoc(newN);
220             }
221             
222             return newOse;
223         }
224     
225         /** Lazy create default NB classloader for use during deserialization. */
226         private static ClassLoader JavaDoc getNBClassLoader() {
227             ClassLoader JavaDoc c = (ClassLoader JavaDoc) org.openide.util.Lookup.getDefault().lookup(ClassLoader JavaDoc.class);
228             return c != null ? c : ClassLoader.getSystemClassLoader();
229         }
230         
231     }
232     
233     
234     // enlarged to not need do the test for negative byte values
235
private final static char[] HEXDIGITS = {'0', '1', '2', '3', '4', '5', '6', '7',
236                                              '8', '9', 'A', 'B', 'C', 'D', 'E', 'F',
237                                              '0', '1', '2', '3', '4', '5', '6', '7'};
238     
239     private static final int INDENT = 8;
240     private static final int BLOCK = 100;
241     private static final int BUFFSIZE = INDENT + BLOCK;
242     private static void storeSerialData (Object JavaDoc inst, PrintWriter pw) throws IOException {
243         pw.println (" <serialdata class=\""+inst.getClass().getName()+"\">"); // NOI18N
244

245         ByteArrayOutputStream baos = new ByteArrayOutputStream (1024);
246         ObjectOutput oo = new SpecialObjectOutputStream (baos);
247         oo.writeObject (inst);
248         byte[] bdata = baos.toByteArray ();
249         
250         char[] cdata = new char[BUFFSIZE];
251         for (int i=0; i < INDENT; i++ ) cdata[i] = ' ';
252         
253         int i = 0; // byte array pointer
254
int j; // char array pointer
255
int blen = bdata.length;
256         
257         while (i < blen) {
258             int mark = INDENT + Math.min( 2*(blen-i), BLOCK );
259             for (j=INDENT; j < mark; j += 2) {
260                 int b = ((int)bdata[i++]) + 256;
261                 cdata[j] = HEXDIGITS[b >> 4];
262                 cdata[j+1] = HEXDIGITS[b & 15];
263             }
264             pw.write(cdata, 0, j);
265             pw.println();
266         }
267         pw.println (" </serialdata>"); // NOI18N
268
pw.flush();
269     }
270     
271     /** Get everything what class extends or implements. */
272     private static Set<Class JavaDoc> getSuperClasses(Class JavaDoc clazz, Set<Class JavaDoc> classes) {
273         if (classes == null) {
274             classes = new HashSet<Class JavaDoc>();
275         }
276         
277         if (clazz == null || !classes.add(clazz)) {
278             return classes;
279         }
280         
281         Class JavaDoc[] cs = clazz.getInterfaces();
282         
283         // XXX following validation should help to identify a wrong IBM's
284
// implementation of Class.getInterfaces(). The implementation for some
285
// classes returns null. See issue #16257.
286
if (cs != null) {
287             for (int i = 0; i < cs.length; i++) {
288                 getSuperClasses(cs[i], classes);
289             }
290         } else {
291             err.severe(
292                 "Error: if you encounter this message, please attach " + //NOI18N
293
"the class name to the issue http://www.netbeans.org/issues/show_bug.cgi?id=16257. " + //NOI18N
294
"Class.getInterfaces() == null for the class: " + clazz); // NOI18N
295
}
296         
297         return getSuperClasses(clazz.getSuperclass(), classes);
298     }
299     
300     /** Class must be subclass of org.openide.ServiceType. */
301     private static Class JavaDoc getServiceTypeClass(Class JavaDoc type) {
302         if (!org.openide.ServiceType.class.isAssignableFrom(type))
303             throw new IllegalArgumentException JavaDoc();
304         // finds direct subclass of service type
305
while (type.getSuperclass () != org.openide.ServiceType.class) {
306             type = type.getSuperclass();
307         }
308         return type;
309     }
310     
311     /** Settings parser. */
312     final static class SettingsRecognizer extends org.xml.sax.helpers.DefaultHandler JavaDoc {
313         
314         private static final String JavaDoc ELM_SETTING = "settings"; // NOI18N
315
private static final String JavaDoc ATR_SETTING_VERSION = "version"; // NOI18N
316

317         private static final String JavaDoc ELM_MODULE = "module"; // NOI18N
318
private static final String JavaDoc ATR_MODULE_NAME = "name"; // NOI18N
319
private static final String JavaDoc ATR_MODULE_SPEC = "spec"; // NOI18N
320
private static final String JavaDoc ATR_MODULE_IMPL = "impl"; // NOI18N
321

322         private static final String JavaDoc ELM_INSTANCE = "instance"; // NOI18N
323
private static final String JavaDoc ATR_INSTANCE_CLASS = "class"; // NOI18N
324
private static final String JavaDoc ATR_INSTANCE_METHOD = "method"; // NOI18N
325

326         private static final String JavaDoc ELM_INSTANCEOF = "instanceof"; // NOI18N
327
private static final String JavaDoc ATR_INSTANCEOF_CLASS = "class"; // NOI18N
328

329         private static final String JavaDoc ELM_SERIALDATA = "serialdata"; // NOI18N
330
private static final String JavaDoc ATR_SERIALDATA_CLASS = "class"; // NOI18N
331

332         //private static final String VERSION = "1.0"; // NOI18N
333

334         private boolean header;
335         private Stack<String JavaDoc> stack;
336         
337         private String JavaDoc version;
338         private String JavaDoc instanceClass;
339         private String JavaDoc instanceMethod;
340         private Set<String JavaDoc> instanceOf = new HashSet<String JavaDoc>();
341         
342         private byte[] serialdata;
343         private CharArrayWriter chaos = null;
344         
345         private String JavaDoc codeName;
346         private String JavaDoc codeNameBase;
347         private int codeNameRelease;
348         private SpecificationVersion moduleSpec;
349         private String JavaDoc moduleImpl;
350         /** file with stored settings */
351         private final FileObject source;
352         
353         /** XML handler recognizing settings.
354          * @param header if true read just elements instanceof, module and attr classname.
355          * @param source file with stored settings
356          */

357         public SettingsRecognizer (boolean header, FileObject source) {
358             this.header = header;
359             this.source = source;
360         }
361         
362         public boolean isAllRead() {
363             return !header;
364         }
365         
366         public void setAllRead(boolean all) {
367             if (!header) return;
368             header = all;
369         }
370         
371         public String JavaDoc getSettingsVerison() {
372             return version;
373         }
374         
375         public String JavaDoc getCodeName() {
376             return codeName;
377         }
378         
379         public String JavaDoc getCodeNameBase() {
380             return codeNameBase;
381         }
382         
383         public int getCodeNameRelease() {
384             return codeNameRelease;
385         }
386         
387         public SpecificationVersion getSpecificationVersion() {
388             return moduleSpec;
389         }
390         
391         public String JavaDoc getModuleImpl() {
392             return moduleImpl;
393         }
394         
395         /** Set of names. */
396         public Set<String JavaDoc> getInstanceOf() {
397             return instanceOf;
398         }
399         
400         /** Method attribute from the instance element. */
401         public String JavaDoc getMethodName() {
402             return instanceMethod;
403         }
404         
405         /** Serialized instance, can be null. */
406         private InputStream getSerializedInstance() {
407             if (serialdata == null) return null;
408             return new ByteArrayInputStream(serialdata);
409         }
410         
411         public org.xml.sax.InputSource JavaDoc resolveEntity(String JavaDoc publicId, String JavaDoc systemId)
412         throws SAXException JavaDoc {
413             if (INSTANCE_DTD_ID.equals (publicId)) {
414                 return new org.xml.sax.InputSource JavaDoc (new ByteArrayInputStream (new byte[0]));
415             } else {
416                 return null; // i.e. follow advice of systemID
417
}
418         }
419         
420         public void characters(char[] values, int start, int length) throws SAXException JavaDoc {
421             if (header) return;
422             String JavaDoc element = stack.peek();
423             if (ELM_SERIALDATA.equals(element)) {
424                 // [PENDING] should be optimized to do not read all chars to memory
425
if (chaos == null) chaos = new CharArrayWriter(length);
426                 chaos.write(values, start, length);
427             }
428         }
429         
430         public void startElement(String JavaDoc uri, String JavaDoc localName, String JavaDoc qName, Attributes JavaDoc attribs) throws SAXException JavaDoc {
431             stack.push(qName);
432             if (ELM_SETTING.equals(qName)) {
433                 version = attribs.getValue(ATR_SETTING_VERSION);
434             } else if (ELM_MODULE.equals(qName)) {
435                 codeName = attribs.getValue(ATR_MODULE_NAME);
436                 resolveModuleElm(codeName);
437                 moduleImpl = attribs.getValue(ATR_MODULE_IMPL);
438                 try {
439                     String JavaDoc spec = attribs.getValue(ATR_MODULE_SPEC);
440                     moduleSpec = spec == null ? null : new SpecificationVersion(spec);
441                 } catch (NumberFormatException JavaDoc nfe) {
442                     throw new SAXException JavaDoc(nfe);
443                 }
444             } else if (ELM_INSTANCEOF.equals(qName)) {
445                 instanceOf.add(org.openide.util.Utilities.translate(
446                     attribs.getValue(ATR_INSTANCEOF_CLASS)));
447             } else if (ELM_INSTANCE.equals(qName)) {
448                 instanceClass = attribs.getValue(ATR_INSTANCE_CLASS);
449                 if (instanceClass == null) {
450                     System.err.println("Hint: NPE is caused by broken settings file: " + source ); // NOI18N
451
}
452                 instanceClass = org.openide.util.Utilities.translate(instanceClass);
453                 instanceMethod = attribs.getValue(ATR_INSTANCE_METHOD);
454             } else if (ELM_SERIALDATA.equals(qName)) {
455                 instanceClass = attribs.getValue(ATR_SERIALDATA_CLASS);
456                 instanceClass = org.openide.util.Utilities.translate(instanceClass);
457                 if (header) throw new StopSAXException();
458             }
459         }
460         
461         /** reade codenamebase + revision */
462         private void resolveModuleElm (String JavaDoc codeName) {
463             if (codeName != null) {
464                 int slash = codeName.indexOf ("/"); // NOI18N
465
if (slash == -1) {
466                     codeNameBase = codeName;
467                     codeNameRelease = -1;
468                 } else {
469                     codeNameBase = codeName.substring (0, slash);
470                     try {
471                         codeNameRelease = Integer.parseInt(codeName.substring(slash + 1));
472                     } catch (NumberFormatException JavaDoc ex) {
473                         Exceptions.attachLocalizedMessage(ex,
474                                                           "Content: \n" +
475                                                           getFileContent(source)); // NOI18N
476
Exceptions.attachLocalizedMessage(ex,
477                                                           "Source: " + source); // NOI18N
478
Logger.getLogger(XMLSettingsSupport.class.getName()).log(Level.WARNING, null, ex);
479                         codeNameRelease = -1;
480                     }
481                 }
482             } else {
483                 codeNameBase = null;
484                 codeNameRelease = -1;
485             }
486         }
487         
488         public void endElement(String JavaDoc uri, String JavaDoc localName, String JavaDoc qName) throws SAXException JavaDoc {
489             //if (header) return;
490
String JavaDoc element = stack.pop();
491             if (ELM_SERIALDATA.equals(element)) {
492                 if (chaos != null) {
493                     ByteArrayOutputStream baos = new ByteArrayOutputStream(chaos.size() >> 1);
494                     try {
495                         chars2Bytes(baos, chaos.toCharArray(), 0, chaos.size());
496                         serialdata = baos.toByteArray();
497                     } catch (IOException ex) {
498                         Logger.getLogger(XMLSettingsSupport.class.getName()).log(Level.WARNING, null, ex);
499                     } finally {
500                         chaos = null; // don't keep the info twice
501
try {
502                             baos.close();
503                         } catch (IOException ex) {
504                             // doesn't matter
505
}
506                     }
507                 }
508             }
509        }
510        
511         /** Tries to deserialize instance saved in is.
512          * @param is stream with stored object, can be null
513          * @return deserialized object or null
514          */

515         private Object JavaDoc readSerial(InputStream is) throws IOException, ClassNotFoundException JavaDoc {
516             if (is == null) return null;
517             try {
518                 ObjectInput oi = new SpecialObjectInputStream (is);
519                 try {
520                     Object JavaDoc o = oi.readObject ();
521                     return o;
522                 } finally {
523                     oi.close();
524                 }
525             } catch (IOException ex) {
526                 Exceptions.attachLocalizedMessage(ex,
527                                                   "Content: \n" +
528                                                   getFileContent(source)); // NOI18N
529
Exceptions.attachLocalizedMessage(ex, "Source: " + source); // NOI18N
530
Exceptions.attachLocalizedMessage(ex,
531                                                   "Cannot read class: " +
532                                                   instanceClass); // NOI18N
533
throw ex;
534             } catch (ClassNotFoundException JavaDoc ex) {
535                 Exceptions.attachLocalizedMessage(ex,
536                                                   "Content: \n" +
537                                                   getFileContent(source)); // NOI18N
538
Exceptions.attachLocalizedMessage(ex, "Source: " + source); // NOI18N
539
throw ex;
540             }
541         }
542         
543         /** Create an instance.
544          * @return the instance of type {@link #instanceClass}
545          * @exception IOException if an I/O error occured
546          * @exception ClassNotFoundException if a class was not found
547          */

548         public Object JavaDoc instanceCreate() throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc {
549             Object JavaDoc inst = null;
550             
551             // deserialize
552
inst = readSerial(getSerializedInstance());
553
554             // default instance
555
if (inst == null) {
556                 if (instanceMethod != null) {
557                     inst = createFromMethod(instanceClass, instanceMethod);
558                 } else {
559                     // use default constructor
560
Class JavaDoc<?> clazz = instanceClass();
561                     if (SharedClassObject.class.isAssignableFrom(clazz)) {
562                         inst = SharedClassObject.findObject(clazz.asSubclass(SharedClassObject.class), false);
563                         if (null != inst) {
564                             // instance already exists -> reset it to defaults
565
try {
566                                 Method JavaDoc method = SharedClassObject.class.getDeclaredMethod("reset", new Class JavaDoc[0]); // NOI18N
567
method.setAccessible(true);
568                                 method.invoke(inst, new Object JavaDoc[0]);
569                             } catch (Exception JavaDoc e) {
570                                 Exceptions.printStackTrace(e);
571                             }
572                         } else {
573                             inst = SharedClassObject.findObject(clazz.asSubclass(SharedClassObject.class), true);
574                         }
575                     } else {
576                         try {
577                             inst = clazz.newInstance();
578                         } catch (Exception JavaDoc ex) {
579                             IOException ioe = new IOException();
580                             ioe.initCause(ex);
581                             Exceptions.attachMessage(ioe,
582                                                               "Content: \n" +
583                                                               getFileContent(source)); // NOI18N
584
Exceptions.attachMessage(ioe,
585                                                               "Class: " + clazz); // NOI18N
586
Exceptions.attachMessage(ioe,
587                                                               "Source: " +
588                                                               source); // NOI18N
589
throw ioe;
590                         }
591                     }
592                 }
593             }
594             
595             return inst;
596         }
597         
598         /** Get file content as String. If some exception occures its stack trace
599           is return instead. */

600         private static String JavaDoc getFileContent (FileObject fo) {
601             try {
602                 InputStreamReader isr = new InputStreamReader(fo.getInputStream());
603                 char[] cbuf = new char[1024];
604                 int length;
605                 StringBuffer JavaDoc sbuf = new StringBuffer JavaDoc(1024);
606                 while (true) {
607                     length = isr.read(cbuf);
608                     if (length > 0) {
609                         sbuf.append(cbuf, 0, length);
610                     } else {
611                         return sbuf.toString();
612                     }
613                 }
614             } catch (Exception JavaDoc ex) {
615                 StringWriter sw = new StringWriter();
616                 ex.printStackTrace(new PrintWriter(sw));
617                 return sw.toString();
618             }
619         }
620         
621         /** create instance by invoking class method */
622         private Object JavaDoc createFromMethod(String JavaDoc srcClazz, String JavaDoc srcMethod)
623         throws ClassNotFoundException JavaDoc, IOException {
624             int dotIndex = instanceMethod.lastIndexOf('.');
625             String JavaDoc targetClass;
626             String JavaDoc targetMethod;
627             if (dotIndex > 0) {
628                 targetClass = srcMethod.substring(0, dotIndex);
629                 targetMethod = srcMethod.substring(dotIndex + 1);
630             } else {
631                 targetClass = srcClazz;
632                 targetMethod = srcMethod;
633             }
634
635             Class JavaDoc<?> clazz = loadClass(targetClass);
636
637             try {
638                 Object JavaDoc instance;
639                 try {
640                     Method JavaDoc method = clazz.getMethod(targetMethod, new Class JavaDoc[]{FileObject.class});
641                     method.setAccessible(true);
642                     instance = method.invoke(null, source);
643                 } catch (NoSuchMethodException JavaDoc ex) {
644                     Method JavaDoc method = clazz.getMethod(targetMethod);
645                     method.setAccessible(true);
646                     instance = method.invoke(null, new Object JavaDoc[0]);
647                 }
648                 if (instance == null) {
649                     // Strictly verboten. Cf. BT #4827173 for example.
650
throw new IOException("Null return not permitted from " + targetClass + "." + targetMethod); // NOI18N
651
}
652                 return instance;
653             } catch (Exception JavaDoc ex) {
654                 IOException ioe = new IOException("Error reading " + source +
655                                                   ": " + ex);
656
657                 ioe.initCause(ex);
658                 Exceptions.attachMessage(ioe,
659                                          "Content:\n" + getFileContent(source));
660                 Exceptions.attachMessage(ioe, "Method: " + srcMethod);
661                 Exceptions.attachMessage(ioe, "Class: " + clazz);
662                 throw ioe;
663             }
664         }
665         
666         /** The representation type that may be created as instances.
667          * Can be used to test whether the instance is of an appropriate
668          * class without actually creating it.
669          *
670          * @return the representation class of the instance
671          * @exception IOException if an I/O error occurred
672          * @exception ClassNotFoundException if a class was not found
673          */

674         public Class JavaDoc instanceClass() throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc {
675             if (instanceClass == null) {
676                 throw new ClassNotFoundException JavaDoc(source +
677                     ": missing 'class' attribute in 'instance' element"); //NOI18N
678
}
679             
680             return loadClass(instanceClass);
681         }
682         
683         /** try to load class from system and current classloader. */
684         private Class JavaDoc<?> loadClass(String JavaDoc clazz) throws ClassNotFoundException JavaDoc {
685             return Lookup.getDefault().lookup(ClassLoader JavaDoc.class).loadClass(clazz);
686         }
687         
688         /** get class name of instance */
689         public String JavaDoc instanceName() {
690             if (instanceClass == null) {
691                 return ""; // NOI18N
692
} else {
693                 return instanceClass;
694             }
695         }
696         
697         private int tr(char c) {
698             if (c >= '0' && c <= '9') return c - '0';
699             if (c >= 'A' && c <= 'F') return c - 'A' + 10;
700             if (c >= 'a' && c <= 'f') return c - 'a' + 10;
701             return -1;
702         }
703         
704         /** Converts array of chars to array of bytes. All whitespaces and
705          * unknown chars are skipped.
706          */

707         private void chars2Bytes (OutputStream os, char[] chars, int off, int length)
708         throws IOException {
709             byte rbyte;
710             int read;
711             
712             for (int i = off; i < length; ) {
713                 read = tr(chars[i++]);
714                 if (read >= 0) rbyte = (byte) (read << 4); // * 16;
715
else continue;
716                 
717                 while (i < length) {
718                     read = tr(chars[i++]);
719                     if (read >= 0) {
720                         rbyte += (byte) read;
721                         os.write(rbyte);
722                         break;
723                     }
724                 }
725             }
726         }
727          
728         /** Parse settings file. */
729         public void parse() throws IOException {
730             InputStream in = null;
731             
732             try {
733                 if (header) {
734                     if (err.isLoggable(Level.FINE) && source.getSize () < 12000) {
735                         // log the content of the stream
736
byte[] arr = new byte[(int)source.getSize ()];
737                         InputStream temp = source.getInputStream ();
738                         int len = temp.read (arr);
739                         if (len != arr.length) {
740                             throw new IOException ("Could not read " + arr.length + " bytes from " + source + " just " + len); // NOI18N
741
}
742                         
743                         err.finer("Parsing:" + new String JavaDoc (arr));
744                         
745                         temp.close ();
746                         
747                         in = new ByteArrayInputStream (arr);
748                     } else {
749                         in = source.getInputStream ();
750                     }
751                     Set<String JavaDoc> iofs = quickParse(new BufferedInputStream(in));
752                     if (iofs != null) {
753                         instanceOf = iofs;
754                         return;
755                     }
756                 }
757             } catch (IOException ioe) {
758                 // ignore - fallback to XML parser follows
759
} finally {
760                 if (in != null) in.close();
761             }
762             stack = new Stack<String JavaDoc>();
763             try {
764                 in = source.getInputStream();
765                 XMLReader JavaDoc reader = org.openide.xml.XMLUtil.createXMLReader();
766                 reader.setContentHandler(this);
767                 reader.setErrorHandler(this);
768                 reader.setEntityResolver(this);
769                 reader.parse(new org.xml.sax.InputSource JavaDoc(new BufferedInputStream(in)));
770             } catch (XMLSettingsSupport.StopSAXException ex) {
771                 // Ok, header is read
772
} catch (SAXException JavaDoc ex) {
773                 IOException ioe = new IOException(source.toString()); // NOI18N
774
ioe.initCause(ex);
775                 Exceptions.attachMessage(ioe,
776                                                   "Content: \n" +
777                                                   getFileContent(source)); // NOI18N
778
Exceptions.attachMessage(ioe, "Source: " + source); // NOI18N
779
throw ioe;
780             } finally {
781                 stack = null;
782                 try {
783                     if (in != null) {
784                         in.close();
785                     }
786                 } catch (IOException ex) {
787                     // ignore already closed
788
}
789             }
790         }
791         
792         /** Parse setting from source. */
793         public void parse(Reader JavaDoc source) throws IOException {
794             stack = new Stack<String JavaDoc>();
795             
796             try {
797                 XMLReader JavaDoc reader = org.openide.xml.XMLUtil.createXMLReader();
798                 reader.setContentHandler(this);
799                 reader.setErrorHandler(this);
800                 reader.setEntityResolver(this);
801                 reader.parse(new org.xml.sax.InputSource JavaDoc(source));
802             } catch (XMLSettingsSupport.StopSAXException ex) {
803                 // Ok, header is read
804
} catch (SAXException JavaDoc ex) {
805                 IOException ioe = new IOException(source.toString()); // NOI18N
806
ioe.initCause(ex);
807                 throw ioe;
808             } finally {
809                 stack = null;
810             }
811         }
812
813         // Encoding irrelevant for these getBytes() calls: all are ASCII...
814
// (unless someone has their system encoding set to UCS-16!)
815
private static final byte[] MODULE_SETTINGS_INTRO = "<?xml version=\"1.0\"?> <!DOCTYPE settings PUBLIC \"-//NetBeans//DTD Session settings 1.0//EN\" \"http://www.netbeans.org/dtds/sessionsettings-1_0.dtd\"> <settings version=\"".getBytes(); // NOI18N
816
private static final byte[] MODULE_SETTINGS_INTRO_END = "> <".getBytes(); // NOI18N
817
private static final byte[] MODULE_SETTINGS_MODULE_NAME = "odule name=\"".getBytes(); // NOI18N
818
private static final byte[] MODULE_SETTINGS_MODULE_SPEC = "spec=\"".getBytes(); // NOI18N
819
private static final byte[] MODULE_SETTINGS_MODULE_IMPL = "impl=\"".getBytes(); // NOI18N
820
private static final byte[] MODULE_SETTINGS_TAG_END = "> <".getBytes(); // NOI18N
821
private static final byte[] MODULE_SETTINGS_INSTANCE = "nstance".getBytes(); // NOI18N
822
private static final byte[] MODULE_SETTINGS_INSTANCE_CLZ = "class=\"".getBytes(); // NOI18N
823
private static final byte[] MODULE_SETTINGS_INSTANCE_MTD = "method=\"".getBytes(); // NOI18N
824
private static final byte[] MODULE_SETTINGS_OF = "f class=\"".getBytes(); // NOI18N
825
private static final byte[] MODULE_SETTINGS_SERIAL = "erialdata class=\"".getBytes(); // NOI18N
826
private static final byte[] MODULE_SETTINGS_END = "settings>".getBytes(); // NOI18N
827

828         /** Attempts to read the stream in the same way as SAX parser but avoids using it.
829          * If it does not manage to parse it this way, it returns null, in which case
830          * you have to use a real parser.
831          * @see "#36718"
832          */

833         private Set<String JavaDoc> quickParse(InputStream is) throws IOException {
834             Set<String JavaDoc> iofs = new HashSet<String JavaDoc>();
835
836             if (!expect(is, MODULE_SETTINGS_INTRO)) {
837                 err.fine("Could not read intro "+source); // NOI18N
838
return null;
839             }
840             version = readTo(is, '"');
841             if (version == null) {
842                 err.fine("Could not read version "+source); // NOI18N
843
return null;
844             }
845             if (!expect(is, MODULE_SETTINGS_INTRO_END)) {
846                 err.fine("Could not read stuff after cnb "+source); // NOI18N
847
return null;
848             }
849             // Now we have (module?, instanceof*, (instance | serialdata)).
850
int c;
851         PARSE:
852             while (true) {
853                 c = is.read();
854                 switch (c) {
855                 case 'm':
856                     // <module />
857
if (!expect(is, MODULE_SETTINGS_MODULE_NAME)) {
858                         err.fine("Could not read up to <module name=\" "+source); // NOI18N
859
return null;
860                     }
861                     String JavaDoc codeName = readTo(is, '"');
862                     if (codeName == null) {
863                         err.fine("Could not read module name value "+source); // NOI18N
864
return null;
865                     }
866                     codeName = codeName.intern();
867                     resolveModuleElm(codeName);
868                     c = is.read();
869                     if (c == '/') {
870                         if (!expect(is, MODULE_SETTINGS_TAG_END)) {
871                             err.fine("Could not read up to end of module tag "+source); // NOI18N
872
return null;
873                         }
874                         break;
875                     }
876                     else if (c != ' ') {
877                         err.fine("Could not space after module name "+source); // NOI18N
878
return null;
879                     }
880                     // <module spec/>
881
if (!expect(is, MODULE_SETTINGS_MODULE_SPEC)) {
882                         err.fine("Could not read up to spec=\" "+source); // NOI18N
883
return null;
884                     }
885                     String JavaDoc mspec = readTo(is, '"');
886                     if (mspec == null) {
887                         err.fine("Could not read module spec value "+source); // NOI18N
888
return null;
889                     }
890                     try {
891                         moduleSpec = new SpecificationVersion(mspec);
892                     } catch (NumberFormatException JavaDoc nfe) {
893                         return null;
894                     }
895                     c = is.read();
896                     if (c == '/') {
897                         if (!expect(is, MODULE_SETTINGS_TAG_END)) {
898                             err.fine("Could not read up to end of <module name spec/> tag "+source); // NOI18N
899
return null;
900                         }
901                         break;
902                     }
903                     else if (c != ' ') {
904                         err.fine("Could not read space after module name "+source); // NOI18N
905
return null;
906                     }
907                     // <module impl/>
908
if (!expect(is, MODULE_SETTINGS_MODULE_IMPL)) {
909                         err.fine("Could not read up to impl=\" "+source); // NOI18N
910
return null;
911                     }
912                     moduleImpl = readTo(is, '"');
913                     if (moduleImpl == null) {
914                         err.fine("Could not read module impl value "+source); // NOI18N
915
return null;
916                     }
917                     moduleImpl = moduleImpl.intern();
918                     // /> >
919
if (!expect(is, MODULE_SETTINGS_TAG_END)) {
920                         err.fine("Could not read up to /> < "+source); // NOI18N
921
return null;
922                     }
923                     break;
924                 case 'i':
925                     // <instanceof> or <instance>
926
if (!expect(is, MODULE_SETTINGS_INSTANCE)) {
927                         err.fine("Could not read up to instance "+source); // NOI18N
928
return null;
929                     }
930                     // Now we need to check which one
931
c = is.read();
932                     if (c == 'o') {
933                         if (!expect(is, MODULE_SETTINGS_OF)) {
934                             err.fine("Could not read up to instance"); // NOI18N
935
return null;
936                         }
937                         String JavaDoc iof = readTo(is, '"');
938                         if (iof == null) {
939                             err.fine("Could not read instanceof value "+source); // NOI18N
940
return null;
941                         }
942                         iof = org.openide.util.Utilities.translate(iof).intern();
943                         iofs.add (iof);
944                         if (is.read() != '/') {
945                             err.fine("No / at end of <instanceof> " + iof+" "+source); // NOI18N
946
return null;
947                         }
948                         if (!expect(is, MODULE_SETTINGS_TAG_END)) {
949                             err.fine("Could not read up to next tag after <instanceof> " + iof+" "+source); // NOI18N
950
return null;
951                         }
952                     }
953                     else if (c == ' ') {
954                         // read class and optional method
955
if (!expect(is, MODULE_SETTINGS_INSTANCE_CLZ)) {
956                             err.fine("Could not read up to class=\" "+source); // NOI18N
957
return null;
958                         }
959                         instanceClass = readTo(is, '"');
960                         if (instanceClass == null) {
961                             err.fine("Could not read instance class value "+source); // NOI18N
962
return null;
963                         }
964                         instanceClass = org.openide.util.Utilities.translate(instanceClass).intern();
965                         c = is.read();
966                         if (c == '/') {
967                             if (!expect(is, MODULE_SETTINGS_TAG_END)) {
968                                 err.fine("Could not read up to end of instance tag "+source); // NOI18N
969
return null;
970                             }
971                             break;
972                         }
973                         else if (c != ' ') {
974                             err.fine("Could not space after instance class "+source); // NOI18N
975
return null;
976                         }
977                         // <instance method/>
978
if (!expect(is, MODULE_SETTINGS_INSTANCE_MTD)) {
979                             err.fine("Could not read up to method=\" "+source); // NOI18N
980
return null;
981                         }
982                         instanceMethod = readTo(is, '"');
983                         if (instanceMethod == null) {
984                             err.fine("Could not read method value "+source); // NOI18N
985
return null;
986                         }
987                         instanceMethod = instanceMethod.intern();
988                         c = is.read();
989                         if (c == '/') {
990                             if (!expect(is, MODULE_SETTINGS_TAG_END)) {
991                                 err.fine("Could not read up to end of instance tag "+source); // NOI18N
992
return null;
993                             }
994                             break;
995                         }
996                         err.fine("Strange stuff after method attribute "+source); // NOI18N
997
return null;
998                     }
999                     else {
1000                        err.fine("Could not read after to instance "+source); // NOI18N
1001
return null;
1002                    }
1003                    break;
1004                case 's':
1005                    // <serialdata class
1006
if (!expect(is, MODULE_SETTINGS_SERIAL)) {
1007                        err.fine("Could not read up to <serialdata class=\" "+source); // NOI18N
1008
return null;
1009                    }
1010                    instanceClass = readTo(is, '"');
1011                    if (instanceClass == null) {
1012                        err.fine("Could not read serialdata class value "+source); // NOI18N
1013
return null;
1014                    }
1015                    instanceClass = org.openide.util.Utilities.translate(instanceClass).intern();
1016                    // here we are complete for header, otherwise we would need to go through serialdata stream
1017
c = is.read();
1018                    if (c != '>') {
1019                        err.fine("Could not read up to end of serialdata tag "+source); // NOI18N
1020
return null;
1021                    }
1022                    break PARSE;
1023                case '/':
1024                    // </settings
1025
// XXX do not read further is neader is set
1026
if (!expect(is, MODULE_SETTINGS_END)) {
1027                        err.fine("Could not read up to end of settings tag "+source); // NOI18N
1028
return null;
1029                    }
1030                    break PARSE;
1031                default:
1032                    err.fine("Strange stuff after <" + (char)c+" "+source); // NOI18N
1033
return null;
1034                }
1035            }
1036            if (instanceClass != null && !iofs.isEmpty()) {
1037                return iofs;
1038            }
1039            return null;
1040        }
1041
1042        /** Read some stuff from a stream and skip over it.
1043         * Newlines conventions and whitespaces are normalized to one space.
1044         * @return true upon success, false if stream contained something else
1045         */

1046        private boolean expect(InputStream is, byte[] stuff) throws IOException {
1047            int len = stuff.length;
1048            boolean inWhitespace = false;
1049            for (int i = 0; i < len; ) {
1050                int c = is.read();
1051                if (c == 10 || c == 13 || c == ' ' || c == '\t') {
1052                    // Normalize: s/[\t \r\n]+/\n/g
1053
if (inWhitespace) {
1054                        continue;
1055                    } else {
1056                        inWhitespace = true;
1057                        c = ' ';
1058                    }
1059                } else {
1060                    inWhitespace = false;
1061                }
1062                if (c != stuff[i++]) {
1063                    return false;
1064                }
1065            }
1066            if (stuff[len - 1] == 10) {
1067                // Expecting something ending in a \n - so we have to
1068
// read any further \r or \n and discard.
1069
if (!is.markSupported()) throw new IOException("Mark not supported"); // NOI18N
1070
is.mark(1);
1071                int c = is.read();
1072                if (c != -1 && c != 10 && c != 13) {
1073                    // Got some non-newline character, push it back!
1074
is.reset();
1075                }
1076            }
1077            return true;
1078        }
1079        /** Read a maximal string until delim is encountered (which will be removed from stream).
1080         * This impl reads only ASCII, for speed.
1081         * Newline conventions are normalized to Unix \n.
1082         * @return the read string, or null if the delim is not encountered before EOF.
1083         */

1084        private String JavaDoc readTo(InputStream is, char delim) throws IOException {
1085            if (delim == 10) {
1086                // Not implemented - stream might have "foo\r\n" and we would
1087
// return "foo" and leave "\n" in the stream.
1088
throw new IOException("Not implemented"); // NOI18N
1089
}
1090            CharArrayWriter caw = new CharArrayWriter(100);
1091            boolean inNewline = false;
1092            while (true) {
1093                int c = is.read();
1094                if (c == -1) return null;
1095                if (c > 126) return null;
1096                if (c == 10 || c == 13) {
1097                    // Normalize: s/[\r\n]+/\n/g
1098
if (inNewline) {
1099                        continue;
1100                    } else {
1101                        inNewline = true;
1102                        c = 10;
1103                    }
1104                } else if (c < 32 && c != 9) {
1105                    // Random control character!
1106
return null;
1107                } else {
1108                    inNewline = false;
1109                }
1110                if (c == delim) {
1111                    return caw.toString();
1112                } else {
1113                    caw.write(c);
1114                }
1115            }
1116        }
1117
1118    }
1119    
1120    final static class StopSAXException extends SAXException JavaDoc {
1121        public StopSAXException() {
1122            super("Parser stopped"); // NOI18N
1123
}
1124    }
1125    
1126    public static final class Convertor extends org.netbeans.spi.settings.Convertor {
1127        
1128        public Object JavaDoc read(java.io.Reader JavaDoc r) throws java.io.IOException JavaDoc, ClassNotFoundException JavaDoc {
1129            // XXX should be passed also FileObject to ensure full functionality
1130
// depends on #26076
1131
XMLSettingsSupport.SettingsRecognizer rec = new XMLSettingsSupport.SettingsRecognizer(false, null);
1132            rec.parse(r);
1133            return rec.instanceCreate();
1134        }
1135        
1136        public void registerSaver(Object JavaDoc inst, org.netbeans.spi.settings.Saver s) {
1137        }
1138        
1139        public void unregisterSaver(Object JavaDoc inst, org.netbeans.spi.settings.Saver s) {
1140        }
1141        
1142        public void write(java.io.Writer JavaDoc w, Object JavaDoc inst) throws java.io.IOException JavaDoc {
1143            XMLSettingsSupport.storeToXML10(inst, w, ModuleInfoManager.getDefault().getModuleInfo(inst.getClass()));
1144        }
1145        
1146    }
1147}
1148
Popular Tags