001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.upgrade.systemoptions;
043:
044: import java.io.BufferedInputStream;
045: import java.io.ByteArrayInputStream;
046: import java.io.ByteArrayOutputStream;
047: import java.io.CharArrayWriter;
048: import java.io.IOException;
049: import java.io.InputStream;
050: import java.io.InputStreamReader;
051: import java.io.ObjectInput;
052: import java.io.ObjectInputStream;
053: import java.io.OutputStream;
054: import java.io.PrintWriter;
055: import java.io.Reader;
056: import java.io.StringWriter;
057: import java.lang.reflect.Method;
058: import java.util.HashSet;
059: import java.util.Set;
060: import java.util.Stack;
061: import org.openide.ErrorManager;
062: import org.openide.filesystems.FileObject; //import org.openide.modules.SpecificationVersion;
063: import org.openide.util.Lookup;
064: import org.openide.util.SharedClassObject;
065: import org.xml.sax.Attributes;
066: import org.xml.sax.SAXException;
067: import org.xml.sax.XMLReader;
068:
069: /**
070: * Copy of XMLSettingsSupport.SettingsRecognizer by Jan Pokorsky
071: */
072: public class SettingsRecognizer extends
073: org.xml.sax.helpers.DefaultHandler {
074: public static final String INSTANCE_DTD_ID = "-//NetBeans//DTD Session settings 1.0//EN"; // NOI18N
075: static final ErrorManager err = ErrorManager.getDefault()
076: .getInstance(SettingsRecognizer.class.getName()); // NOI18N
077:
078: private static final String ELM_SETTING = "settings"; // NOI18N
079: private static final String ATR_SETTING_VERSION = "version"; // NOI18N
080:
081: private static final String ELM_MODULE = "module"; // NOI18N
082: private static final String ATR_MODULE_NAME = "name"; // NOI18N
083: private static final String ATR_MODULE_SPEC = "spec"; // NOI18N
084: private static final String ATR_MODULE_IMPL = "impl"; // NOI18N
085:
086: private static final String ELM_INSTANCE = "instance"; // NOI18N
087: private static final String ATR_INSTANCE_CLASS = "class"; // NOI18N
088: private static final String ATR_INSTANCE_METHOD = "method"; // NOI18N
089:
090: private static final String ELM_INSTANCEOF = "instanceof"; // NOI18N
091: private static final String ATR_INSTANCEOF_CLASS = "class"; // NOI18N
092:
093: private static final String ELM_SERIALDATA = "serialdata"; // NOI18N
094: private static final String ATR_SERIALDATA_CLASS = "class"; // NOI18N
095:
096: //private static final String VERSION = "1.0"; // NOI18N
097:
098: private boolean header;
099: private Stack<String> stack;
100:
101: private String version;
102: private String instanceClass;
103: private String instanceMethod;
104: private Set<String> instanceOf = new HashSet<String>();
105:
106: private byte[] serialdata;
107: private CharArrayWriter chaos = null;
108:
109: private String codeName;
110: private String codeNameBase;
111: private int codeNameRelease;
112: //private SpecificationVersion moduleSpec;
113: private String moduleImpl;
114: /** file with stored settings */
115: private final FileObject source;
116:
117: /** XML handler recognizing settings.
118: * @param header if true read just elements instanceof, module and attr classname.
119: * @param source file with stored settings
120: */
121: public SettingsRecognizer(boolean header, FileObject source) {
122: this .header = header;
123: this .source = source;
124: }
125:
126: public boolean isAllRead() {
127: return !header;
128: }
129:
130: public void setAllRead(boolean all) {
131: if (!header)
132: return;
133: header = all;
134: }
135:
136: public String getSettingsVerison() {
137: return version;
138: }
139:
140: public String getCodeName() {
141: return codeName;
142: }
143:
144: public String getCodeNameBase() {
145: return codeNameBase;
146: }
147:
148: public int getCodeNameRelease() {
149: return codeNameRelease;
150: }
151:
152: /*public SpecificationVersion getSpecificationVersion() {
153: return moduleSpec;
154: }*/
155:
156: public String getModuleImpl() {
157: return moduleImpl;
158: }
159:
160: /** Set of names. */
161: public Set getInstanceOf() {
162: return instanceOf;
163: }
164:
165: /** Method attribute from the instance element. */
166: public String getMethodName() {
167: return instanceMethod;
168: }
169:
170: /** Serialized instance, can be null. */
171: public InputStream getSerializedInstance() {
172: if (serialdata == null)
173: return null;
174: return new ByteArrayInputStream(serialdata);
175: }
176:
177: public org.xml.sax.InputSource resolveEntity(String publicId,
178: String systemId) throws SAXException {
179: if (INSTANCE_DTD_ID.equals(publicId)) {
180: return new org.xml.sax.InputSource(
181: new ByteArrayInputStream(new byte[0]));
182: } else {
183: return null; // i.e. follow advice of systemID
184: }
185: }
186:
187: public void characters(char[] values, int start, int length)
188: throws SAXException {
189: if (header)
190: return;
191: String element = stack.peek();
192: if (ELM_SERIALDATA.equals(element)) {
193: // [PENDING] should be optimized to do not read all chars to memory
194: if (chaos == null)
195: chaos = new CharArrayWriter(length);
196: chaos.write(values, start, length);
197: }
198: }
199:
200: public void startElement(String uri, String localName,
201: String qName, Attributes attribs) throws SAXException {
202: stack.push(qName);
203: if (ELM_SETTING.equals(qName)) {
204: version = attribs.getValue(ATR_SETTING_VERSION);
205: } else if (ELM_MODULE.equals(qName)) {
206: codeName = attribs.getValue(ATR_MODULE_NAME);
207: resolveModuleElm(codeName);
208: moduleImpl = attribs.getValue(ATR_MODULE_IMPL);
209: try {
210: String spec = attribs.getValue(ATR_MODULE_SPEC);
211: //moduleSpec = spec == null ? null : new SpecificationVersion(spec);
212: } catch (NumberFormatException nfe) {
213: throw new SAXException(nfe);
214: }
215: } else if (ELM_INSTANCEOF.equals(qName)) {
216: instanceOf.add(org.openide.util.Utilities.translate(attribs
217: .getValue(ATR_INSTANCEOF_CLASS)));
218: } else if (ELM_INSTANCE.equals(qName)) {
219: instanceClass = attribs.getValue(ATR_INSTANCE_CLASS);
220: if (instanceClass == null) {
221: System.err
222: .println("Hint: NPE is caused by broken settings file: "
223: + source); // NOI18N
224: }
225: instanceClass = org.openide.util.Utilities
226: .translate(instanceClass);
227: instanceMethod = attribs.getValue(ATR_INSTANCE_METHOD);
228: } else if (ELM_SERIALDATA.equals(qName)) {
229: instanceClass = attribs.getValue(ATR_SERIALDATA_CLASS);
230: instanceClass = org.openide.util.Utilities
231: .translate(instanceClass);
232: if (header)
233: throw new StopSAXException();
234: }
235: }
236:
237: /** reade codenamebase + revision */
238: private void resolveModuleElm(String codeName) {
239: if (codeName != null) {
240: int slash = codeName.indexOf("/"); // NOI18N
241: if (slash == -1) {
242: codeNameBase = codeName;
243: codeNameRelease = -1;
244: } else {
245: codeNameBase = codeName.substring(0, slash);
246: try {
247: codeNameRelease = Integer.parseInt(codeName
248: .substring(slash + 1));
249: } catch (NumberFormatException ex) {
250: ErrorManager emgr = ErrorManager.getDefault();
251: emgr.annotate(ex, "Content: \n"
252: + getFileContent(source)); // NOI18N
253: emgr.annotate(ex, "Source: " + source); // NOI18N
254: emgr.notify(ErrorManager.INFORMATIONAL, ex);
255: codeNameRelease = -1;
256: }
257: }
258: } else {
259: codeNameBase = null;
260: codeNameRelease = -1;
261: }
262: }
263:
264: public void endElement(String uri, String localName, String qName)
265: throws SAXException {
266: //if (header) return;
267: String element = stack.pop();
268: if (ELM_SERIALDATA.equals(element)) {
269: if (chaos != null) {
270: ByteArrayOutputStream baos = new ByteArrayOutputStream(
271: chaos.size() >> 1);
272: try {
273: chars2Bytes(baos, chaos.toCharArray(), 0, chaos
274: .size());
275: serialdata = baos.toByteArray();
276: } catch (IOException ex) {
277: ErrorManager.getDefault().notify(
278: ErrorManager.WARNING, ex);
279: } finally {
280: chaos = null; // don't keep the info twice
281: try {
282: baos.close();
283: } catch (IOException ex) {
284: // doesn't matter
285: }
286: }
287: }
288: }
289: }
290:
291: /** Tries to deserialize instance saved in is.
292: * @param is stream with stored object, can be null
293: * @return deserialized object or null
294: */
295: private Object readSerial(InputStream is) throws IOException,
296: ClassNotFoundException {
297: if (is == null)
298: return null;
299: try {
300: ObjectInput oi = new ObjectInputStream(is);
301: try {
302: Object o = oi.readObject();
303: return o;
304: } finally {
305: oi.close();
306: }
307: } catch (IOException ex) {
308: ErrorManager emgr = ErrorManager.getDefault();
309: emgr.annotate(ex, "Content: \n" + getFileContent(source)); // NOI18N
310: emgr.annotate(ex, "Source: " + source); // NOI18N
311: emgr.annotate(ex, "Cannot read class: " + instanceClass); // NOI18N
312: throw ex;
313: } catch (ClassNotFoundException ex) {
314: ErrorManager emgr = ErrorManager.getDefault();
315: emgr.annotate(ex, "Content: \n" + getFileContent(source)); // NOI18N
316: emgr.annotate(ex, "Source: " + source); // NOI18N
317: throw ex;
318: }
319: }
320:
321: /** Create an instance.
322: * @return the instance of type {@link #instanceClass}
323: * @exception IOException if an I/O error occured
324: * @exception ClassNotFoundException if a class was not found
325: */
326: public Object instanceCreate() throws java.io.IOException,
327: ClassNotFoundException {
328: Object inst = null;
329:
330: // deserialize
331: inst = readSerial(getSerializedInstance());
332:
333: // default instance
334: if (inst == null) {
335: if (instanceMethod != null) {
336: inst = createFromMethod(instanceClass, instanceMethod);
337: } else {
338: // use default constructor
339: Class<?> clazz = instanceClass();
340: if (SharedClassObject.class.isAssignableFrom(clazz)) {
341: inst = SharedClassObject
342: .findObject(
343: clazz
344: .asSubclass(SharedClassObject.class),
345: false);
346: if (null != inst) {
347: // instance already exists -> reset it to defaults
348: try {
349: Method method = SharedClassObject.class
350: .getDeclaredMethod("reset",
351: new Class[0]); // NOI18N
352: method.setAccessible(true);
353: method.invoke(inst, new Object[0]);
354: } catch (Exception e) {
355: ErrorManager.getDefault().notify(e);
356: }
357: } else {
358: inst = SharedClassObject.findObject(clazz
359: .asSubclass(SharedClassObject.class),
360: true);
361: }
362: } else {
363: try {
364: inst = clazz.newInstance();
365: } catch (Exception ex) {
366: IOException ioe = new IOException();
367: ErrorManager emgr = ErrorManager.getDefault();
368: emgr.annotate(ioe, ex);
369: emgr.annotate(ioe, "Content: \n"
370: + getFileContent(source)); // NOI18N
371: emgr.annotate(ioe, "Class: " + clazz); // NOI18N
372: emgr.annotate(ioe, "Source: " + source); // NOI18N
373: throw ioe;
374: }
375: }
376: }
377: }
378:
379: return inst;
380: }
381:
382: /** Get file content as String. If some exception occures its stack trace
383: * is return instead. */
384: private static String getFileContent(FileObject fo) {
385: try {
386: InputStreamReader isr = new InputStreamReader(fo
387: .getInputStream());
388: char[] cbuf = new char[1024];
389: int length;
390: StringBuffer sbuf = new StringBuffer(1024);
391: while (true) {
392: length = isr.read(cbuf);
393: if (length > 0) {
394: sbuf.append(cbuf, 0, length);
395: } else {
396: return sbuf.toString();
397: }
398: }
399: } catch (Exception ex) {
400: StringWriter sw = new StringWriter();
401: ex.printStackTrace(new PrintWriter(sw));
402: return sw.toString();
403: }
404: }
405:
406: /** create instance by invoking class method */
407: private Object createFromMethod(String srcClazz, String srcMethod)
408: throws ClassNotFoundException, IOException {
409: int dotIndex = instanceMethod.lastIndexOf('.');
410: String targetClass;
411: String targetMethod;
412: if (dotIndex > 0) {
413: targetClass = srcMethod.substring(0, dotIndex);
414: targetMethod = srcMethod.substring(dotIndex + 1);
415: } else {
416: targetClass = srcClazz;
417: targetMethod = srcMethod;
418: }
419:
420: Class<?> clazz = loadClass(targetClass);
421:
422: try {
423: Object instance;
424: try {
425: Method method = clazz.getMethod(targetMethod,
426: new Class[] { FileObject.class });
427: method.setAccessible(true);
428: instance = method.invoke(null, source);
429: } catch (NoSuchMethodException ex) {
430: Method method = clazz.getMethod(targetMethod);
431: method.setAccessible(true);
432: instance = method.invoke(null, new Object[0]);
433: }
434: if (instance == null) {
435: // Strictly verboten. Cf. BT #4827173 for example.
436: throw new IOException("Null return not permitted from "
437: + targetClass + "." + targetMethod); // NOI18N
438: }
439: return instance;
440: } catch (Exception ex) {
441: IOException ioe = new IOException("Error reading " + source
442: + ": " + ex); // NOI18N
443: ErrorManager emgr = ErrorManager.getDefault();
444: emgr.annotate(ioe, "Class: " + clazz); // NOI18N
445: emgr.annotate(ioe, "Method: " + srcMethod); // NOI18N
446: emgr.annotate(ioe, ex);
447: emgr.annotate(ioe, "Content:\n" + getFileContent(source)); // NOI18N
448: throw ioe;
449: }
450: }
451:
452: /** The representation type that may be created as instances.
453: * Can be used to test whether the instance is of an appropriate
454: * class without actually creating it.
455: *
456: * @return the representation class of the instance
457: * @exception IOException if an I/O error occurred
458: * @exception ClassNotFoundException if a class was not found
459: */
460: public Class instanceClass() throws java.io.IOException,
461: ClassNotFoundException {
462: if (instanceClass == null) {
463: throw new ClassNotFoundException(
464: source
465: + ": missing 'class' attribute in 'instance' element"); //NOI18N
466: }
467:
468: return loadClass(instanceClass);
469: }
470:
471: /** try to load class from system and current classloader. */
472: private Class loadClass(String clazz) throws ClassNotFoundException {
473: return ((ClassLoader) Lookup.getDefault().lookup(
474: ClassLoader.class)).loadClass(clazz);
475: }
476:
477: /** get class name of instance */
478: public String instanceName() {
479: if (instanceClass == null) {
480: return ""; // NOI18N
481: } else {
482: return instanceClass;
483: }
484: }
485:
486: private int tr(char c) {
487: if (c >= '0' && c <= '9')
488: return c - '0';
489: if (c >= 'A' && c <= 'F')
490: return c - 'A' + 10;
491: if (c >= 'a' && c <= 'f')
492: return c - 'a' + 10;
493: return -1;
494: }
495:
496: /** Converts array of chars to array of bytes. All whitespaces and
497: * unknown chars are skipped.
498: */
499: private void chars2Bytes(OutputStream os, char[] chars, int off,
500: int length) throws IOException {
501: byte rbyte;
502: int read;
503:
504: for (int i = off; i < length;) {
505: read = tr(chars[i++]);
506: if (read >= 0)
507: rbyte = (byte) (read << 4); // * 16;
508: else
509: continue;
510:
511: while (i < length) {
512: read = tr(chars[i++]);
513: if (read >= 0) {
514: rbyte += (byte) read;
515: os.write(rbyte);
516: break;
517: }
518: }
519: }
520: }
521:
522: /** Parse settings file. */
523: public void parse() throws IOException {
524: InputStream in = null;
525:
526: try {
527: if (header) {
528: if (err.isLoggable(err.INFORMATIONAL)
529: && source.getSize() < 12000) {
530: // log the content of the stream
531: byte[] arr = new byte[(int) source.getSize()];
532: InputStream temp = source.getInputStream();
533: int len = temp.read(arr);
534: if (len != arr.length) {
535: throw new IOException("Could not read "
536: + arr.length + " bytes from " + source
537: + " just " + len); // NOI18N
538: }
539:
540: err.log("Parsing:" + new String(arr));
541:
542: temp.close();
543:
544: in = new ByteArrayInputStream(arr);
545: } else {
546: in = new BufferedInputStream(source
547: .getInputStream());
548: }
549: Set<String> iofs = quickParse(new BufferedInputStream(
550: in));
551: if (iofs != null) {
552: instanceOf = iofs;
553: return;
554: }
555: }
556: } catch (IOException ioe) {
557: // ignore - fallback to XML parser follows
558: } finally {
559: if (in != null)
560: in.close();
561: }
562: stack = new Stack<String>();
563: try {
564: in = source.getInputStream();
565: XMLReader reader = org.openide.xml.XMLUtil
566: .createXMLReader();
567: reader.setContentHandler(this );
568: reader.setErrorHandler(this );
569: reader.setEntityResolver(this );
570: reader.parse(new org.xml.sax.InputSource(
571: new BufferedInputStream(in)));
572: } catch (SettingsRecognizer.StopSAXException ex) {
573: // Ok, header is read
574: } catch (SAXException ex) {
575: IOException ioe = new IOException(source.toString()); // NOI18N
576: ErrorManager emgr = ErrorManager.getDefault();
577: emgr.annotate(ioe, ex);
578: if (ex.getException() != null) {
579: emgr.annotate(ioe, ex.getException());
580: }
581: emgr.annotate(ioe, "Content: \n" + getFileContent(source)); // NOI18N
582: emgr.annotate(ioe, "Source: " + source); // NOI18N
583: throw ioe;
584: } finally {
585: stack = null;
586: try {
587: if (in != null) {
588: in.close();
589: }
590: } catch (IOException ex) {
591: // ignore already closed
592: }
593: }
594: }
595:
596: /** Parse setting from source. */
597: public void parse(Reader source) throws IOException {
598: stack = new Stack<String>();
599:
600: try {
601: XMLReader reader = org.openide.xml.XMLUtil
602: .createXMLReader();
603: reader.setContentHandler(this );
604: reader.setErrorHandler(this );
605: reader.setEntityResolver(this );
606: reader.parse(new org.xml.sax.InputSource(source));
607: } catch (SettingsRecognizer.StopSAXException ex) {
608: // Ok, header is read
609: } catch (SAXException ex) {
610: IOException ioe = new IOException(source.toString()); // NOI18N
611: ErrorManager emgr = ErrorManager.getDefault();
612: emgr.annotate(ioe, ex);
613: if (ex.getException() != null) {
614: emgr.annotate(ioe, ex.getException());
615: }
616: throw ioe;
617: } finally {
618: stack = null;
619: }
620: }
621:
622: // Encoding irrelevant for these getBytes() calls: all are ASCII...
623: // (unless someone has their system encoding set to UCS-16!)
624: 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=\""
625: .getBytes(); // NOI18N
626: private static final byte[] MODULE_SETTINGS_INTRO_END = "> <"
627: .getBytes(); // NOI18N
628: private static final byte[] MODULE_SETTINGS_MODULE_NAME = "odule name=\""
629: .getBytes(); // NOI18N
630: private static final byte[] MODULE_SETTINGS_MODULE_SPEC = "spec=\""
631: .getBytes(); // NOI18N
632: private static final byte[] MODULE_SETTINGS_MODULE_IMPL = "impl=\""
633: .getBytes(); // NOI18N
634: private static final byte[] MODULE_SETTINGS_TAG_END = "> <"
635: .getBytes(); // NOI18N
636: private static final byte[] MODULE_SETTINGS_INSTANCE = "nstance"
637: .getBytes(); // NOI18N
638: private static final byte[] MODULE_SETTINGS_INSTANCE_CLZ = "class=\""
639: .getBytes(); // NOI18N
640: private static final byte[] MODULE_SETTINGS_INSTANCE_MTD = "method=\""
641: .getBytes(); // NOI18N
642: private static final byte[] MODULE_SETTINGS_OF = "f class=\""
643: .getBytes(); // NOI18N
644: private static final byte[] MODULE_SETTINGS_SERIAL = "erialdata class=\""
645: .getBytes(); // NOI18N
646: private static final byte[] MODULE_SETTINGS_END = "settings>"
647: .getBytes(); // NOI18N
648:
649: /** Attempts to read the stream in the same way as SAX parser but avoids using it.
650: * If it does not manage to parse it this way, it returns null, in which case
651: * you have to use a real parser.
652: * @see "#36718"
653: */
654: private Set<String> quickParse(InputStream is) throws IOException {
655: Set<String> iofs = new HashSet<String>(); // <String>
656:
657: if (!expect(is, MODULE_SETTINGS_INTRO)) {
658: err.log("Could not read intro " + source); // NOI18N
659: return null;
660: }
661: version = readTo(is, '"');
662: if (version == null) {
663: err.log("Could not read version " + source); // NOI18N
664: return null;
665: }
666: if (!expect(is, MODULE_SETTINGS_INTRO_END)) {
667: err.log("Could not read stuff after cnb " + source); // NOI18N
668: return null;
669: }
670: // Now we have (module?, instanceof*, (instance | serialdata)).
671: int c;
672: PARSE: while (true) {
673: c = is.read();
674: switch (c) {
675: case 'm':
676: // <module />
677: if (!expect(is, MODULE_SETTINGS_MODULE_NAME)) {
678: err.log("Could not read up to <module name=\" "
679: + source); // NOI18N
680: return null;
681: }
682: String codeName = readTo(is, '"');
683: if (codeName == null) {
684: err.log("Could not read module name value "
685: + source); // NOI18N
686: return null;
687: }
688: codeName = codeName.intern();
689: resolveModuleElm(codeName);
690: c = is.read();
691: if (c == '/') {
692: if (!expect(is, MODULE_SETTINGS_TAG_END)) {
693: err
694: .log("Could not read up to end of module tag "
695: + source); // NOI18N
696: return null;
697: }
698: break;
699: } else if (c != ' ') {
700: err.log("Could not space after module name "
701: + source); // NOI18N
702: return null;
703: }
704: // <module spec/>
705: if (!expect(is, MODULE_SETTINGS_MODULE_SPEC)) {
706: err.log("Could not read up to spec=\" " + source); // NOI18N
707: return null;
708: }
709: String mspec = readTo(is, '"');
710: if (mspec == null) {
711: err.log("Could not read module spec value "
712: + source); // NOI18N
713: return null;
714: }
715: try {
716: //moduleSpec = new SpecificationVersion(mspec);
717: } catch (NumberFormatException nfe) {
718: return null;
719: }
720: c = is.read();
721: if (c == '/') {
722: if (!expect(is, MODULE_SETTINGS_TAG_END)) {
723: err
724: .log("Could not read up to end of <module name spec/> tag "
725: + source); // NOI18N
726: return null;
727: }
728: break;
729: } else if (c != ' ') {
730: err.log("Could not read space after module name "
731: + source); // NOI18N
732: return null;
733: }
734: // <module impl/>
735: if (!expect(is, MODULE_SETTINGS_MODULE_IMPL)) {
736: err.log("Could not read up to impl=\" " + source); // NOI18N
737: return null;
738: }
739: moduleImpl = readTo(is, '"');
740: if (moduleImpl == null) {
741: err.log("Could not read module impl value "
742: + source); // NOI18N
743: return null;
744: }
745: moduleImpl = moduleImpl.intern();
746: // /> >
747: if (!expect(is, MODULE_SETTINGS_TAG_END)) {
748: err.log("Could not read up to /> < " + source); // NOI18N
749: return null;
750: }
751: break;
752: case 'i':
753: // <instanceof> or <instance>
754: if (!expect(is, MODULE_SETTINGS_INSTANCE)) {
755: err.log("Could not read up to instance " + source); // NOI18N
756: return null;
757: }
758: // Now we need to check which one
759: c = is.read();
760: if (c == 'o') {
761: if (!expect(is, MODULE_SETTINGS_OF)) {
762: err.log("Could not read up to instance"); // NOI18N
763: return null;
764: }
765: String iof = readTo(is, '"');
766: if (iof == null) {
767: err.log("Could not read instanceof value "
768: + source); // NOI18N
769: return null;
770: }
771: iof = org.openide.util.Utilities.translate(iof)
772: .intern();
773: iofs.add(iof);
774: if (is.read() != '/') {
775: err.log("No / at end of <instanceof> " + iof
776: + " " + source); // NOI18N
777: return null;
778: }
779: if (!expect(is, MODULE_SETTINGS_TAG_END)) {
780: err
781: .log("Could not read up to next tag after <instanceof> "
782: + iof + " " + source); // NOI18N
783: return null;
784: }
785: } else if (c == ' ') {
786: // read class and optional method
787: if (!expect(is, MODULE_SETTINGS_INSTANCE_CLZ)) {
788: err.log("Could not read up to class=\" "
789: + source); // NOI18N
790: return null;
791: }
792: instanceClass = readTo(is, '"');
793: if (instanceClass == null) {
794: err.log("Could not read instance class value "
795: + source); // NOI18N
796: return null;
797: }
798: instanceClass = org.openide.util.Utilities
799: .translate(instanceClass).intern();
800: c = is.read();
801: if (c == '/') {
802: if (!expect(is, MODULE_SETTINGS_TAG_END)) {
803: err
804: .log("Could not read up to end of instance tag "
805: + source); // NOI18N
806: return null;
807: }
808: break;
809: } else if (c != ' ') {
810: err.log("Could not space after instance class "
811: + source); // NOI18N
812: return null;
813: }
814: // <instance method/>
815: if (!expect(is, MODULE_SETTINGS_INSTANCE_MTD)) {
816: err.log("Could not read up to method=\" "
817: + source); // NOI18N
818: return null;
819: }
820: instanceMethod = readTo(is, '"');
821: if (instanceMethod == null) {
822: err
823: .log("Could not read method value "
824: + source); // NOI18N
825: return null;
826: }
827: instanceMethod = instanceMethod.intern();
828: c = is.read();
829: if (c == '/') {
830: if (!expect(is, MODULE_SETTINGS_TAG_END)) {
831: err
832: .log("Could not read up to end of instance tag "
833: + source); // NOI18N
834: return null;
835: }
836: break;
837: }
838: err.log("Strange stuff after method attribute "
839: + source); // NOI18N
840: return null;
841: } else {
842: err.log("Could not read after to instance "
843: + source); // NOI18N
844: return null;
845: }
846: break;
847: case 's':
848: // <serialdata class
849: if (!expect(is, MODULE_SETTINGS_SERIAL)) {
850: err
851: .log("Could not read up to <serialdata class=\" "
852: + source); // NOI18N
853: return null;
854: }
855: instanceClass = readTo(is, '"');
856: if (instanceClass == null) {
857: err.log("Could not read serialdata class value "
858: + source); // NOI18N
859: return null;
860: }
861: instanceClass = org.openide.util.Utilities.translate(
862: instanceClass).intern();
863: // here we are complete for header, otherwise we would need to go through serialdata stream
864: c = is.read();
865: if (c != '>') {
866: err
867: .log("Could not read up to end of serialdata tag "
868: + source); // NOI18N
869: return null;
870: }
871: break PARSE;
872: case '/':
873: // </settings
874: // XXX do not read further is neader is set
875: if (!expect(is, MODULE_SETTINGS_END)) {
876: err.log("Could not read up to end of settings tag "
877: + source); // NOI18N
878: return null;
879: }
880: break PARSE;
881: default:
882: err.log("Strange stuff after <" + (char) c + " "
883: + source); // NOI18N
884: return null;
885: }
886: }
887: if (instanceClass != null && !iofs.isEmpty()) {
888: return iofs;
889: }
890: return null;
891: }
892:
893: /** Read some stuff from a stream and skip over it.
894: * Newlines conventions and whitespaces are normalized to one space.
895: * @return true upon success, false if stream contained something else
896: */
897: private boolean expect(InputStream is, byte[] stuff)
898: throws IOException {
899: int len = stuff.length;
900: boolean inWhitespace = false;
901: for (int i = 0; i < len;) {
902: int c = is.read();
903: if (c == 10 || c == 13 || c == ' ' || c == '\t') {
904: // Normalize: s/[\t \r\n]+/\n/g
905: if (inWhitespace) {
906: continue;
907: } else {
908: inWhitespace = true;
909: c = ' ';
910: }
911: } else {
912: inWhitespace = false;
913: }
914: if (c != stuff[i++]) {
915: return false;
916: }
917: }
918: if (stuff[len - 1] == 10) {
919: // Expecting something ending in a \n - so we have to
920: // read any further \r or \n and discard.
921: if (!is.markSupported())
922: throw new IOException("Mark not supported"); // NOI18N
923: is.mark(1);
924: int c = is.read();
925: if (c != -1 && c != 10 && c != 13) {
926: // Got some non-newline character, push it back!
927: is.reset();
928: }
929: }
930: return true;
931: }
932:
933: /** Read a maximal string until delim is encountered (which will be removed from stream).
934: * This impl reads only ASCII, for speed.
935: * Newline conventions are normalized to Unix \n.
936: * @return the read string, or null if the delim is not encountered before EOF.
937: */
938: private String readTo(InputStream is, char delim)
939: throws IOException {
940: if (delim == 10) {
941: // Not implemented - stream might have "foo\r\n" and we would
942: // return "foo" and leave "\n" in the stream.
943: throw new IOException("Not implemented"); // NOI18N
944: }
945: CharArrayWriter caw = new CharArrayWriter(100);
946: boolean inNewline = false;
947: while (true) {
948: int c = is.read();
949: if (c == -1)
950: return null;
951: if (c > 126)
952: return null;
953: if (c == 10 || c == 13) {
954: // Normalize: s/[\r\n]+/\n/g
955: if (inNewline) {
956: continue;
957: } else {
958: inNewline = true;
959: c = 10;
960: }
961: } else if (c < 32 && c != 9) {
962: // Random control character!
963: return null;
964: } else {
965: inNewline = false;
966: }
967: if (c == delim) {
968: return caw.toString();
969: } else {
970: caw.write(c);
971: }
972: }
973: }
974:
975: final static class StopSAXException extends SAXException {
976: public StopSAXException() {
977: super ("Parser stopped"); // NOI18N
978: }
979: }
980:
981: }
|