| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| package org.netbeans.modules.schema2beansdev; |
| |
| import java.io.*; |
| |
| import org.netbeans.modules.schema2beans.*; |
| |
| /** |
| * |
| * This class implement the Document Definition handler in order to build |
| * the internal tree representation of the DD DTD. |
| * |
| */ |
| public class DocDefParser extends GeneralParser implements SchemaParser { |
| |
| static class MissingEndOfEltException extends RuntimeException { |
| String propName; |
| |
| public MissingEndOfEltException(String propName) { |
| this.propName = propName; |
| } |
| } |
| |
| static private final int WORD_NO_CONTEXT = 0; |
| static private final int WORD_CHECK = 1; |
| static private final int WORD_COMMENT = 2; |
| static private final int WORD_ELEMENT1 = 3; |
| static private final int WORD_ELEMENT = 4; |
| static private final int WORD_ATTLIST1 = 5; |
| static private final int WORD_ATTLIST = 6; |
| static private final int WORD_PI = 7; |
| static private final int WORD_ENTITY1 = 10; |
| static private final int WORD_ENTITY = 11; |
| |
| static String errHeader = "DTD parsing failed: "; // NOI18N |
| |
| // Buffer used to read the file by chunks |
| private char buffer[] = new char[BUFFER_SIZE]; |
| |
| // Current size of the buffer |
| private int bufSize; |
| |
| // Reading offset in the buffer while parsing |
| private int bufScan; |
| |
| protected static int BUFFER_SIZE = 4096; |
| |
| // Handler to callback with the tokens found in the DTD. |
| private DocDefHandler handler; |
| |
| private GenBeans.Config config = null; |
| |
| public DocDefParser() { |
| } |
| |
| public DocDefParser(GenBeans.Config config, DocDefHandler handler) { |
| this.config = config; |
| this.filename = config.getFilename(); |
| this.schemaIn = config.getFileIn(); |
| this.handler = handler; |
| } |
| |
| protected @Override void startupReader() throws java.io.IOException { |
| if (schemaIn == null) { |
| schemaIn = new FileInputStream(filename); |
| } |
| EntityParser entityParser = new EntityParser(new InputStreamReader(schemaIn)); |
| entityParser.parse(); |
| reader = entityParser.getReader(); |
| } |
| |
| public void setHandler(DocDefHandler handler) { |
| this.handler = handler; |
| } |
| |
| public DocDefHandler getHandler() { |
| return this.handler; |
| } |
| |
| protected boolean checkBuffer() throws IOException { |
| if (this.bufScan >= this.bufSize) { |
| // Buffer either empty or already parsed - get more from the file |
| this.bufSize = reader.read(this.buffer); |
| if (this.bufSize == -1) |
| return false; |
| this.bufScan = 0; |
| } |
| return true; |
| } |
| |
| /** |
| * Returns the next character of the parsed file. |
| */ |
| protected char getNext() throws IOException { |
| if (this.checkBuffer()) |
| return this.buffer[this.bufScan++]; |
| else |
| return '\0'; |
| } |
| |
| /** |
| * Get the next character without moving the parser offset. |
| */ |
| protected char peekNext() throws IOException { |
| if (this.checkBuffer()) |
| return this.buffer[this.bufScan]; |
| else |
| return '\0'; |
| } |
| |
| /** |
| * Return the instance value associated with an element |
| */ |
| private static int getInstanceValue(char c) { |
| switch(c) { |
| case '*': |
| return Common.TYPE_0_N; |
| case '+': |
| return Common.TYPE_1_N; |
| case '?': |
| return Common.TYPE_0_1; |
| default: |
| // We assume this default value if nothing is specified |
| return Common.TYPE_1; |
| } |
| } |
| |
| /** |
| * Find out the type of the current word |
| */ |
| private int processWord(StringBuffer curWord, int wordContext) throws SchemaParseException{ |
| String word = curWord.toString(); |
| int len = word.length(); |
| |
| if (len >0) { |
| // We have some word to play with |
| switch (wordContext) { |
| case WORD_CHECK: |
| if (word.startsWith("--")) { // NOI18N |
| if (len > 2) |
| word = curWord.substring(2); |
| else |
| word = ""; // NOI18N |
| |
| this.handler.startElement(word, word, Common.COMMENT); |
| wordContext = WORD_COMMENT; |
| } else if (word.equals("ELEMENT")) // NOI18N |
| wordContext = WORD_ELEMENT1; |
| else if (word.equals("ATTLIST")) // NOI18N |
| wordContext = WORD_ATTLIST1; |
| else if (word.equals("ENTITY")) // NOI18N |
| wordContext = WORD_ENTITY1; |
| else { |
| //System.err.println("Error: found an unknown '<!' sequence (" + word + ")"); // NOI18N |
| throw new SchemaParseException("Error: found an unknown '<!' sequence (" + word + ")"); // NOI18N |
| } |
| break; |
| case WORD_COMMENT: |
| this.handler.element(word, word, 0); |
| break; |
| case WORD_ELEMENT1: |
| this.handler.startElement(word, word, Common.ELEMENT); |
| wordContext = WORD_ELEMENT; |
| break; |
| case WORD_ATTLIST1: |
| this.handler.startElement(word, word, Common.ATTLIST); |
| wordContext = WORD_ATTLIST; |
| break; |
| case WORD_ENTITY1: |
| wordContext = WORD_ENTITY; |
| break; |
| case WORD_ENTITY: |
| break; |
| case WORD_ELEMENT: |
| case WORD_ATTLIST: |
| // Find out the instance value (*, ? or +) |
| int instance = this.getInstanceValue(word.charAt(len-1)); |
| // Get rid of the extra character |
| if (instance != Common.TYPE_1) |
| word = curWord.substring(0, len-1); |
| |
| try { |
| this.handler.element(word, word, instance); |
| } catch(MissingEndOfEltException e) { |
| if (wordContext == WORD_ATTLIST) { |
| // |
| // The TreeBuilder is done with the previous |
| // attribute and would expect an end of ATTLIST |
| // declaration. |
| // We might have several attributes declared on the |
| // same ATTLIST declaration. |
| // Let's continue assuming so, the TreeBuilder |
| // checks the attribute semantic and will throw |
| // if this is not the case. |
| // |
| this.handler.startElement(e.propName, e.propName, |
| Common.ATTLIST); |
| this.handler.element(word, word, instance); |
| } |
| } |
| |
| break; |
| default: |
| } |
| curWord.delete(0, len); |
| } |
| return wordContext; |
| } |
| |
| /** |
| * Parse the document, calling back the handler |
| */ |
| void parse() throws IOException, SchemaParseException { |
| char c; |
| StringBuffer curWord = new StringBuffer(); |
| int wordContext = WORD_NO_CONTEXT; |
| int level = 0; |
| |
| while ((c=this.getNext()) != '\0') { |
| switch(c) { |
| case '<': |
| // Check if we have <! or <-- |
| char c1 = this.getNext(); |
| if (c1 == '!') { |
| // Check if the next word is reserved |
| if (wordContext != WORD_NO_CONTEXT |
| && wordContext != WORD_COMMENT) { |
| System.err.println("Error: found a '<!' sequence within another '<!' sequence"); // NOI18N |
| throw new SchemaParseException("Warning: found a '<!' sequence within another '<!' sequence"); // NOI18N |
| } |
| if (wordContext != WORD_COMMENT) |
| wordContext = WORD_CHECK; |
| } else if (c1 == '?') { |
| wordContext = WORD_PI; |
| } else { |
| curWord.append(c); |
| curWord.append(c1); |
| } |
| break; |
| case '>': |
| // Might be the end of a comment or <!element |
| switch (wordContext) { |
| case WORD_NO_CONTEXT: |
| //System.err.println("Error: Found '>' without '<!'");// NOI18N |
| throw new SchemaParseException("Error: Found '>' without '<!'"); // NOI18N |
| case WORD_PI: |
| String word = curWord.toString(); |
| int len = word.length(); |
| if (word.endsWith("?")) { // NOI18N |
| // Ignore any PI |
| curWord.delete(0, len); |
| wordContext = WORD_NO_CONTEXT; |
| } else |
| curWord.append(c); |
| break; |
| case WORD_COMMENT: |
| word = curWord.toString(); |
| len = word.length(); |
| if (word.endsWith("--")) { // NOI18N |
| this.handler.endElement(); |
| curWord.delete(0, len); |
| wordContext = WORD_NO_CONTEXT; |
| } else |
| curWord.append(c); |
| break; |
| case WORD_ENTITY: |
| wordContext = WORD_NO_CONTEXT; |
| break; |
| default: |
| wordContext = this.processWord(curWord, |
| wordContext); |
| this.handler.endElement(); |
| wordContext = WORD_NO_CONTEXT; |
| } |
| break; |
| case '(': |
| if (wordContext == WORD_ELEMENT |
| || wordContext == WORD_ATTLIST) { |
| wordContext = this.processWord(curWord, wordContext); |
| this.handler.startGroupElements(); |
| } else |
| curWord.append(c); |
| break; |
| case ')': |
| wordContext = this.processWord(curWord, wordContext); |
| if (wordContext == WORD_ELEMENT |
| || wordContext == WORD_ATTLIST) { |
| int instance = this.getInstanceValue(this.peekNext()); |
| // Get rid of the extra character |
| if (instance != Common.TYPE_1) |
| this.getNext(); |
| this.handler.endGroupElements(instance); |
| } else |
| curWord.append(c); |
| break; |
| case '|': |
| wordContext = this.processWord(curWord, wordContext); |
| if (wordContext == WORD_ELEMENT |
| || wordContext == WORD_ATTLIST) |
| this.handler.character(c); |
| else |
| curWord.append(c); |
| break; |
| case '\n': |
| case '\r': |
| case '\t': |
| case ' ': |
| case ',': |
| wordContext = this.processWord(curWord, wordContext); |
| break; |
| // |
| default: |
| curWord.append(c); |
| } |
| } |
| |
| if (wordContext != WORD_NO_CONTEXT) |
| System.out.println("Warning: unexpected EOF"); // NOI18N |
| } |
| |
| /** |
| * Start the DTD parsing (called by GenBeans class) |
| */ |
| public void process() throws java.io.IOException, Schema2BeansException { |
| if (this.filename == null && this.schemaIn == null) |
| throw new IllegalArgumentException(Common.getMessage( |
| "FilenameNotSpecified_msg", errHeader)); |
| |
| if (this.handler == null) |
| throw new IllegalArgumentException(Common.getMessage( |
| "HandlerNotSpecified_msg", errHeader)); |
| |
| if (config.isTraceParse()) { |
| config.messageOut.println("Parsing file " + this.filename.toString() + // NOI18N |
| " with handler " + this.handler.getClass()); // NOI18N |
| } |
| |
| try { |
| startupReader(); |
| this.handler.startDocument(config.getDocRoot()); |
| this.parse(); |
| shutdownReader(); |
| this.handler.endDocument(); |
| } catch(FileNotFoundException e) { |
| config.messageOut.println("Error: file " + this.filename.toString() + " not found"); // NOI18N |
| throw e; |
| /* |
| } catch (IllegalStateException e) { |
| throw e; |
| } catch (RuntimeException e) { |
| TraceLogger.error(e); |
| throw e; |
| */ |
| } |
| } |
| } |