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.modules.schema2beansdev;
043:
044: import java.util.*;
045: import java.io.*;
046:
047: import org.netbeans.modules.schema2beans.*;
048:
049: /**
050: *
051: * This class implement the Document Definition handler in order to build
052: * the internal tree representation of the DD DTD.
053: *
054: */
055: public class DocDefParser extends GeneralParser implements SchemaParser {
056:
057: static class MissingEndOfEltException extends RuntimeException {
058: String propName;
059:
060: public MissingEndOfEltException(String propName) {
061: this .propName = propName;
062: }
063: }
064:
065: static private final int WORD_NO_CONTEXT = 0;
066: static private final int WORD_CHECK = 1;
067: static private final int WORD_COMMENT = 2;
068: static private final int WORD_ELEMENT1 = 3;
069: static private final int WORD_ELEMENT = 4;
070: static private final int WORD_ATTLIST1 = 5;
071: static private final int WORD_ATTLIST = 6;
072: static private final int WORD_PI = 7;
073: static private final int WORD_ENTITY1 = 10;
074: static private final int WORD_ENTITY = 11;
075:
076: static String errHeader = "DTD parsing failed: "; // NOI18N
077:
078: // Buffer used to read the file by chunks
079: private char buffer[] = new char[BUFFER_SIZE];
080:
081: // Current size of the buffer
082: private int bufSize;
083:
084: // Reading offset in the buffer while parsing
085: private int bufScan;
086:
087: protected static int BUFFER_SIZE = 4096;
088:
089: // Handler to callback with the tokens found in the DTD.
090: private DocDefHandler handler;
091:
092: private GenBeans.Config config = null;
093:
094: public DocDefParser() {
095: }
096:
097: public DocDefParser(GenBeans.Config config, DocDefHandler handler) {
098: this .config = config;
099: this .filename = config.getFilename();
100: this .schemaIn = config.getFileIn();
101: this .handler = handler;
102: }
103:
104: protected void startupReader() throws java.io.IOException {
105: ;
106: if (schemaIn == null) {
107: EntityParser entityParser = new EntityParser(filename);
108: entityParser.parse();
109: reader = entityParser.getReader();
110: } else
111: reader = new InputStreamReader(schemaIn);
112: }
113:
114: public void setFilename(File filename) {
115: this .filename = filename;
116: }
117:
118: public File getFilename() {
119: return filename;
120: }
121:
122: public void setHandler(DocDefHandler handler) {
123: this .handler = handler;
124: }
125:
126: public DocDefHandler getHandler() {
127: return this .handler;
128: }
129:
130: protected boolean checkBuffer() throws IOException {
131: if (this .bufScan >= this .bufSize) {
132: // Buffer either empty or already parsed - get more from the file
133: this .bufSize = reader.read(this .buffer);
134: if (this .bufSize == -1)
135: return false;
136: this .bufScan = 0;
137: }
138: return true;
139: }
140:
141: /**
142: * Returns the next character of the parsed file.
143: */
144: protected char getNext() throws IOException {
145: if (this .checkBuffer())
146: return this .buffer[this .bufScan++];
147: else
148: return '\0';
149: }
150:
151: /**
152: * Get the next character without moving the parser offset.
153: */
154: protected char peekNext() throws IOException {
155: if (this .checkBuffer())
156: return this .buffer[this .bufScan];
157: else
158: return '\0';
159: }
160:
161: /**
162: * Return the instance value associated with an element
163: */
164: private static int getInstanceValue(char c) {
165: switch (c) {
166: case '*':
167: return Common.TYPE_0_N;
168: case '+':
169: return Common.TYPE_1_N;
170: case '?':
171: return Common.TYPE_0_1;
172: default:
173: // We assume this default value if nothing is specified
174: return Common.TYPE_1;
175: }
176: }
177:
178: /**
179: * Find out the type of the current word
180: */
181: private int processWord(StringBuffer curWord, int wordContext)
182: throws SchemaParseException {
183: String word = curWord.toString();
184: int len = word.length();
185:
186: if (len > 0) {
187: // We have some word to play with
188: switch (wordContext) {
189: case WORD_CHECK:
190: if (word.startsWith("--")) { // NOI18N
191: if (len > 2)
192: word = curWord.substring(2);
193: else
194: word = ""; // NOI18N
195:
196: this .handler.startElement(word, word,
197: Common.COMMENT);
198: wordContext = WORD_COMMENT;
199: } else if (word.equals("ELEMENT")) // NOI18N
200: wordContext = WORD_ELEMENT1;
201: else if (word.equals("ATTLIST")) // NOI18N
202: wordContext = WORD_ATTLIST1;
203: else if (word.equals("ENTITY")) // NOI18N
204: wordContext = WORD_ENTITY1;
205: else {
206: //System.err.println("Error: found an unknown '<!' sequence (" + word + ")"); // NOI18N
207: throw new SchemaParseException(
208: "Error: found an unknown '<!' sequence ("
209: + word + ")"); // NOI18N
210: }
211: break;
212: case WORD_COMMENT:
213: this .handler.element(word, word, 0);
214: break;
215: case WORD_ELEMENT1:
216: this .handler.startElement(word, word, Common.ELEMENT);
217: wordContext = WORD_ELEMENT;
218: break;
219: case WORD_ATTLIST1:
220: this .handler.startElement(word, word, Common.ATTLIST);
221: wordContext = WORD_ATTLIST;
222: break;
223: case WORD_ENTITY1:
224: wordContext = WORD_ENTITY;
225: break;
226: case WORD_ENTITY:
227: break;
228: case WORD_ELEMENT:
229: case WORD_ATTLIST:
230: // Find out the instance value (*, ? or +)
231: int instance = this .getInstanceValue(word
232: .charAt(len - 1));
233: // Get rid of the extra character
234: if (instance != Common.TYPE_1)
235: word = curWord.substring(0, len - 1);
236:
237: try {
238: this .handler.element(word, word, instance);
239: } catch (MissingEndOfEltException e) {
240: if (wordContext == WORD_ATTLIST) {
241: //
242: // The TreeBuilder is done with the previous
243: // attribute and would expect an end of ATTLIST
244: // declaration.
245: // We might have several attributes declared on the
246: // same ATTLIST declaration.
247: // Let's continue assuming so, the TreeBuilder
248: // checks the attribute semantic and will throw
249: // if this is not the case.
250: //
251: this .handler.startElement(e.propName,
252: e.propName, Common.ATTLIST);
253: this .handler.element(word, word, instance);
254: }
255: }
256:
257: break;
258: default:
259: }
260: curWord.delete(0, len);
261: }
262: return wordContext;
263: }
264:
265: /**
266: * Parse the document, calling back the handler
267: */
268: void parse() throws IOException, SchemaParseException {
269: char c;
270: StringBuffer curWord = new StringBuffer();
271: int wordContext = WORD_NO_CONTEXT;
272: int level = 0;
273:
274: while ((c = this .getNext()) != '\0') {
275: switch (c) {
276: case '<':
277: // Check if we have <! or <--
278: char c1 = this .getNext();
279: if (c1 == '!') {
280: // Check if the next word is reserved
281: if (wordContext != WORD_NO_CONTEXT
282: && wordContext != WORD_COMMENT) {
283: System.err
284: .println("Error: found a '<!' sequence within another '<!' sequence"); // NOI18N
285: throw new SchemaParseException(
286: "Warning: found a '<!' sequence within another '<!' sequence"); // NOI18N
287: }
288: if (wordContext != WORD_COMMENT)
289: wordContext = WORD_CHECK;
290: } else if (c1 == '?') {
291: wordContext = WORD_PI;
292: } else {
293: curWord.append(c);
294: curWord.append(c1);
295: }
296: break;
297: case '>':
298: // Might be the end of a comment or <!element
299: switch (wordContext) {
300: case WORD_NO_CONTEXT:
301: //System.err.println("Error: Found '>' without '<!'");// NOI18N
302: throw new SchemaParseException(
303: "Error: Found '>' without '<!'"); // NOI18N
304: case WORD_PI:
305: String word = curWord.toString();
306: int len = word.length();
307: if (word.endsWith("?")) { // NOI18N
308: // Ignore any PI
309: curWord.delete(0, len);
310: wordContext = WORD_NO_CONTEXT;
311: } else
312: curWord.append(c);
313: break;
314: case WORD_COMMENT:
315: word = curWord.toString();
316: len = word.length();
317: if (word.endsWith("--")) { // NOI18N
318: this .handler.endElement();
319: curWord.delete(0, len);
320: wordContext = WORD_NO_CONTEXT;
321: } else
322: curWord.append(c);
323: break;
324: case WORD_ENTITY:
325: wordContext = WORD_NO_CONTEXT;
326: break;
327: default:
328: wordContext = this
329: .processWord(curWord, wordContext);
330: this .handler.endElement();
331: wordContext = WORD_NO_CONTEXT;
332: }
333: break;
334: case '(':
335: if (wordContext == WORD_ELEMENT
336: || wordContext == WORD_ATTLIST) {
337: wordContext = this
338: .processWord(curWord, wordContext);
339: this .handler.startGroupElements();
340: } else
341: curWord.append(c);
342: break;
343: case ')':
344: wordContext = this .processWord(curWord, wordContext);
345: if (wordContext == WORD_ELEMENT
346: || wordContext == WORD_ATTLIST) {
347: int instance = this .getInstanceValue(this
348: .peekNext());
349: // Get rid of the extra character
350: if (instance != Common.TYPE_1)
351: this .getNext();
352: this .handler.endGroupElements(instance);
353: } else
354: curWord.append(c);
355: break;
356: case '|':
357: wordContext = this .processWord(curWord, wordContext);
358: if (wordContext == WORD_ELEMENT
359: || wordContext == WORD_ATTLIST)
360: this .handler.character(c);
361: else
362: curWord.append(c);
363: break;
364: case '\n':
365: case '\r':
366: case '\t':
367: case ' ':
368: case ',':
369: wordContext = this .processWord(curWord, wordContext);
370: break;
371: //
372: default:
373: curWord.append(c);
374: }
375: }
376:
377: if (wordContext != WORD_NO_CONTEXT)
378: System.out.println("Warning: unexpected EOF"); // NOI18N
379: }
380:
381: /**
382: * Start the DTD parsing (called by GenBeans class)
383: */
384: public void process() throws java.io.IOException,
385: Schema2BeansException {
386: if (this .filename == null && this .schemaIn == null)
387: throw new IllegalArgumentException(Common.getMessage(
388: "FilenameNotSpecified_msg", errHeader));
389:
390: if (this .handler == null)
391: throw new IllegalArgumentException(Common.getMessage(
392: "HandlerNotSpecified_msg", errHeader));
393:
394: if (config.isTraceParse()) {
395: config.messageOut.println("Parsing file "
396: + this .filename.toString() + // NOI18N
397: " with handler " + this .handler.getClass()); // NOI18N
398: }
399:
400: try {
401: startupReader();
402: this .handler.startDocument(config.getDocRoot());
403: this .parse();
404: shutdownReader();
405: this .handler.endDocument();
406: } catch (FileNotFoundException e) {
407: config.messageOut.println("Error: file "
408: + this .filename.toString() + " not found"); // NOI18N
409: throw e;
410: /*
411: } catch (IllegalStateException e) {
412: throw e;
413: } catch (RuntimeException e) {
414: TraceLogger.error(e);
415: throw e;
416: */
417: }
418: }
419: }
|