|
What this is
Other links
The source code/* * Sun Public License Notice * * The contents of this file are subject to the Sun Public License * Version 1.0 (the "License"). You may not use this file except in * compliance with the License. A copy of the License is available at * http://www.sun.com/ * * The Original Code is NetBeans. The Initial Developer of the Original * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun * Microsystems, Inc. All Rights Reserved. */ package org.netbeans.editor.ext.html.dtd; import java.io.Reader; import java.io.PushbackReader; import java.io.IOException; import java.util.*; import org.netbeans.editor.ext.html.WeakHashSet; /** * !!! Includes !!!! String->DTD.Element * @author Petr Nejedly * @version 0.2 */ class DTDParser extends Object { // The provider used to provide the Readers for this DTD. private ReaderProvider provider = null; // Asks for Reader for given DTD. private Reader getReader( String identifier, String fileName ) { if( provider == null ) return null; return provider.getReaderForIdentifier( identifier, fileName ); } /** Weak set for holding already created strings, to not create more * instances of the same string */ private WeakHashSet stringCache = new WeakHashSet( 131, 0.75f ); /** Weak set of attributes - helps sharing common attributes */ private WeakHashSet attributes = new WeakHashSet( 23, 0.75f ); /** Weak set of models */ private WeakHashSet models = new WeakHashSet( 131, 0.75f ); /** Weak set of Contents */ private WeakHashSet contents = new WeakHashSet( 131, 0.75f ); /** Temporal storage of all ContentLeafs that needs to get their * elements filled in at the end of parsing */ Set leafs = new HashSet( 131, 0.75f ); /** Map of all character references. * Mapping is String name -> DTD.CharRef instance */ private SortedMap charRefs = new TreeMap(); /** Map holding partially completed instances of Element. * Mapping is String name -> DTD.Element instance */ private SortedMap elementMap = new TreeMap(); /** Map holding entities during creation of DTD. * Mapping is String name -> String content. * This map should not be used for direct put(..), because entities * are defined by first declaration and can not be overriden. */ private Map entityMap = new HashMap(); public DTD createDTD( ReaderProvider provider, String identifier, String fileName ) throws WrongDTDException { this.provider = provider; Reader reader = getReader( identifier, fileName ); if( reader == null ) throw new WrongDTDException( "Can't open Reader for public identifier " + identifier ); // NOI18N try { parseDTD( new PushbackReader( reader, 1024*128 ) ); } catch( IOException e ) { throw new WrongDTDException( "IOException during parsing: " + e.getMessage() ); // NOI18N } // fixup includes and excludes of all elements for( Iterator it = elementMap.values().iterator(); it.hasNext (); ) { DTD.Element elem = (DTD.Element)it.next(); ContentModelImpl cm = (ContentModelImpl)elem.getContentModel(); Set newIncs = new HashSet(); for( Iterator incIter = cm.included.iterator(); incIter.hasNext (); ) { Object oldElem; Object subElem = oldElem = incIter.next(); if( subElem instanceof String ) { subElem = elementMap.get( ((String)subElem).toUpperCase() ); } if( subElem == null ) { throw new WrongDTDException( "'" + oldElem + "' element referenced from " + elem.getName() + " not found throughout the DTD." ); // NOI18N } newIncs.add( subElem ); } cm.included = newIncs; Set newExcs = new HashSet(); for( Iterator excIter = cm.excluded.iterator(); excIter.hasNext (); ) { Object oldElem; Object subElem = oldElem = excIter.next(); if( subElem instanceof String ) { subElem = elementMap.get( ((String)subElem).toUpperCase() ); } if( subElem == null ) { throw new WrongDTDException( "'" + oldElem + "' element referenced from " + elem.getName() + " not found throughout the DTD." ); // NOI18N } newExcs.add( subElem ); } cm.excluded = newExcs; cm.hashcode = cm.content.hashCode() + 2*cm.included.hashCode() + 3*cm.excluded.hashCode(); } // fixup content leafs for( Iterator it = leafs.iterator(); it.hasNext (); ) { ContentLeafImpl leaf = (ContentLeafImpl)it.next(); leaf.elem = (DTD.Element)elementMap.get( leaf.elemName ); } return new DTDImpl( identifier, elementMap, charRefs ); } /** Method for adding new entities to their map. Obeys the rule that * entity, once defined, can not be overriden */ void addEntity( String name, String content ) { if( entityMap.get( name ) == null ) entityMap.put( name, content ); } /** Method for adding new entities to their map. Obeys the rule that * entity, once defined, can not be overriden */ void addPublicEntity( String name, String identifier, String file ) throws WrongDTDException { if( entityMap.get( name ) == null ) { StringBuffer sb = new StringBuffer(); char[] buffer = new char[16384]; Reader r = getReader( identifier, file ); try { int len; while( (len = r.read( buffer )) >= 0 ) { sb.append( buffer, 0, len ); } } catch( IOException e ) { throw new WrongDTDException( "Error reading included public entity " + name + " - " + e.getMessage() ); // NOI18N } entityMap.put( name, sb.toString() ); } } DTD.Value createValue( String name ) { return new ValueImpl( (String)stringCache.put( name ) ); } /** Creates new or lookups old ContentModel with given properites */ DTD.ContentModel createContentModel( DTD.Content content, Set included, Set excluded ) { DTD.ContentModel cm = new ContentModelImpl( content, included, excluded ); return (DTD.ContentModel)models.put( cm ); } /** Creates new or lookups old ContentLeaf with given properites */ DTD.Content createContentLeaf( String name ) { DTD.Content c = new ContentLeafImpl( name ); c = (DTD.Content)contents.put( c ); leafs.add( c ); // remember for final fixup return c; } /** Creates new or lookups old ContentNode with given properites */ DTD.Content createContentNode( char type, DTD.Content subContent ) { return (DTD.Content)contents.put( new UnaryContentNodeImpl( type, subContent ) ); } /** Creates new or lookups old ContentNode with given properites */ DTD.Content createContentNode( char type, DTD.Content[] subContent ) { return (DTD.Content)contents.put( new MultiContentNodeImpl( type, subContent ) ); } DTD.Element createElement( String name, DTD.ContentModel cm, boolean optStart, boolean optEnd) { DTD.Element retVal = new ElementImpl( name, cm, optStart, optEnd, new TreeMap() ); return retVal; } /** Creates new or lookups old attribute with given properites */ DTD.Attribute createAttribute( String name, int type, String baseType, String typeHelper, String defaultMode, SortedMap values ) { DTD.Attribute attr = new AttributeImpl( name, type, (String)stringCache.put( baseType ), (String)stringCache.put( typeHelper ), (String)stringCache.put( defaultMode ), values ); return (DTD.Attribute)attributes.put( attr ); } /** Adds given instance of DTD.Attribute to Element named elemName */ void addAttrToElement( String elemName, DTD.Attribute attr) throws WrongDTDException { ElementImpl elem = (ElementImpl)elementMap.get( elemName.toUpperCase() ); if( elem == null ) throw new WrongDTDException( "Attribute definition for unknown Element \"" + elemName +"\"." ); // NOI18N elem.addAttribute( attr ); } void createAddCharRef( String name, char value ) { DTD.CharRef ref = new CharRefImpl( name, value ); charRefs.put( name, ref ); } private boolean isNameChar( char c ) { return Character.isLetterOrDigit( c ) || c == '_' || c == '-' || c == '.' || c == ':'; } /*----------------------------------------------------------------------------*/ /*----------------------------- Parsing routines ---------------------------- */ /*----------------------------------------------------------------------------*/ private static final int DTD_INIT = 0; private static final int DTD_LT = 1; // after '<' private static final int DTD_EXC = 2; // after "' private void parseDTD( PushbackReader in ) throws IOException, WrongDTDException { int state = DTD_INIT; for( ;; ) { int i = in.read(); if( i == -1 ) { break; } switch( state ) { case DTD_INIT: switch( i ) { case '<': state = DTD_LT; break; case '%': parseEntityReference( in ); break; // Stay in DTD_INIT } break; case DTD_LT: if( i != '!' ) throw new WrongDTDException( "Unexpected char '" + (char)i + "' after '<'" ); // NOI18N state = DTD_EXC; break; case DTD_EXC: switch( i ) { case '-': state = DTD_MINUS; break; case '[': parseOptional( in ); state = DTD_INIT; break; default: in.unread( i ); parseMarkup( in ); state = DTD_INIT; break; } break; case DTD_MINUS: if( i != '-' ) throw new WrongDTDException( "Unexpected char '" + (char)i + "' after \"' ) throw new WrongDTDException( "Unexpected char '" + (char)i + "' after comment" ); // NOI18N state = DTD_INIT; break; } } if( state != DTD_INIT ) throw new WrongDTDException( "Premature end of DTD" ); // NOI18N } /** Parser that reads the markup type after ' */ private void parseMarkup( PushbackReader in ) throws IOException, WrongDTDException { StringBuffer sb = new StringBuffer(); for( ;; ) { int i = in.read(); if( i == -1 ) throw new WrongDTDException( "Premature end of DTD" ); // NOI18N EOF if( i == ' ' ) break; sb.append( (char)i ); // next char of name } String markup = sb.toString(); if( "ENTITY".equals( markup ) ) { // NOI18N parseEntityDefinition( in ); } else if( "ELEMENT".equals( markup ) ) { // NOI18N parseElement( in ); } else if( "ATTLIST".equals( markup ) ) { // NOI18N parseAttlist( in ); } else throw new WrongDTDException( "Wrong DTD markup ' ) { return; } if( i == '-' ) state = PED_AVAL_M; break; case PED_AVAL_M: if( i == '-' ) parseComment( in ); state = PED_AVAL; break; case PED_TYPE: if( Character.isWhitespace( (char)i ) ) { if( type.toString().equals( "PUBLIC" ) ) { // NOI18N state = PED_ATYPE; } else { throw new WrongDTDException( "Unexpected entity type \"" + type + "\"." ); // NOI18N } } else { type.append( (char)i ); } break; case PED_ATYPE: if( Character.isWhitespace( (char)i ) ) break; if( i == '"' ) { state = PED_ID; break; } throw new WrongDTDException( "Unexpected char '" + (char)i + "' in PUBLIC entity." ); // NOI18N case PED_ID: if( i == '"' ) { state = PED_AID; } else { identifier.append( (char)i ); } break; case PED_AID: if( Character.isWhitespace( (char)i ) ) break; if( i == '"' ) { state = PED_FILE; break; } if( i == '>' ) { addPublicEntity( name.toString(), identifier.toString(), null ); return; } throw new WrongDTDException( "Unexpected char '" + (char)i + "' in PUBLIC entity." ); // NOI18N case PED_FILE: if( i == '"' ) { state = PED_AFILE; } else { value.append( (char)i ); } break; case PED_AFILE: if( Character.isWhitespace( (char)i ) ) break; if( i == '-' ) { state = PED_AFILE_M; break; } if( i == '>' ) { addPublicEntity( name.toString(), identifier.toString(), value.toString() ); return; } throw new WrongDTDException( "Unexpected char '" + (char)i + "' in PUBLIC entity." ); // NOI18N case PED_AFILE_M: if( i == '-' ) { parseComment( in ); state = PED_FILE; break; } throw new WrongDTDException( "Unexpected sequence \"-" + (char)i + "\" in in PUBLIC entity." ); // NOI18N case PED_CHAR: if( Character.isWhitespace( (char)i ) ) { state = PED_ACHAR; } else { name.append( (char)i ); } break; case PED_ACHAR: if( Character.isWhitespace( (char)i ) ) break; else { type.append( (char)i ); state = PED_CH_TYPE; } break; case PED_CH_TYPE: if( Character.isWhitespace( (char)i ) ) { if( type.toString().equals( "CDATA" ) ) { // NOI18N state = PED_ATYPE; state = PED_CH_ATYPE; } else { throw new WrongDTDException( "Unexpected entity type \"" + type + "\"." ); // NOI18N } } else { type.append( (char)i ); } break; case PED_CH_ATYPE: if( Character.isWhitespace( (char)i ) ) break; else if( i == '"' ) { state = PED_CH_QUOT; } else { throw new WrongDTDException( "Unexpected char '" + (char)i + "' in entity." ); // NOI18N } break; case PED_CH_QUOT: if( i == '"' ) { value.delete( 0, 2 ); value.deleteCharAt( value.length() - 1 ); int code = Integer.parseInt( value.toString() ); createAddCharRef( name.toString(), (char)code ); state = PED_AVAL; } else { value.append( (char)i ); } } } } private static final int GR_INIT=0; private static final int GR_NAME=1; private static final int GR_ANAME=2; /** Parse group of names separated by '|' character and optional spaces * @return List of Strings containing names */ private List parseGroup( PushbackReader in ) throws IOException, WrongDTDException { int state = GR_INIT; StringBuffer name = new StringBuffer(); List list = new ArrayList(); for( ;; ) { int i = in.read(); if( i == -1 ) throw new WrongDTDException( "Premature end of DTD" ); // NOI18N EOF switch( state ) { case GR_INIT: if( Character.isWhitespace( (char)i ) ) break; if( i == '%' ) { parseEntityReference( in ); } else { name.append( (char)i ); state = GR_NAME; } break; case GR_NAME: if( isNameChar( (char)i ) ) { name.append( (char)i ); break; } switch( i ) { case ')': list.add( name.toString() ); return list; case '|': list.add( name.toString() ); name.setLength( 0 ); state = GR_INIT; break; default: if( Character.isWhitespace( (char)i ) ) { list.add( name.toString() ); name.setLength( 0 ); state = GR_ANAME; break; } else { throw new WrongDTDException( "Unexpected char '" + (char)i + "' in group definition." ); // NOI18N } } break; case GR_ANAME: if( Character.isWhitespace( (char)i ) ) break; switch( i ) { case ')': return list; case '|': state = GR_INIT; break; default: throw new WrongDTDException( "Unexpected char '" + (char)i + "' in group definition." ); // NOI18N } break; } } } private static final int EL_INIT = 0; private static final int EL_NAME = 1; private static final int EL_ANAME = 2; private static final int EL_ASTART = 3; private static final int EL_ACONTENT = 4; private static final int EL_PLUS = 5; private static final int EL_MINUS = 6; /** parse the whole element(s) definition including content model. * Create corresponding instances of DTD.Element filled with proper * informations. Make the same content models and their contents shared * across the DTD */ private void parseElement( PushbackReader in ) throws IOException, WrongDTDException { int state = EL_INIT; StringBuffer name = new StringBuffer(); List list = null; boolean optStart = false; boolean optEnd = false; DTD.Content content = null; Set inSet = new HashSet(); Set exSet = new HashSet(); for( ;; ) { int i = in.read(); if( i == -1 ) break; switch( state ) { case EL_INIT: if( Character.isWhitespace( (char)i ) ) break; switch( i ) { case '(': list = parseGroup( in ); state = EL_ANAME; break; case '%': parseEntityReference( in ); break; // Stay in EL_INIT default: name.append( (char)i ); state = EL_NAME; break; } break; case EL_NAME: if( Character.isWhitespace( (char)i ) ) { state = EL_ANAME; list = new ArrayList(); list.add( name.toString() ); } else { name.append( (char)i ); } break; case EL_ANAME: if( Character.isWhitespace( (char)i ) ) break; switch( i ) { case 'O': optStart = true; // fall fhrough case '-': state = EL_ASTART; break; default: throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ELEMENT optStart definition." ); // NOI18N } break; case EL_ASTART: if( Character.isWhitespace( (char)i ) ) break; switch( i ) { case 'O': optEnd = true; // fall fhrough case '-': content = parseContent( in ); state = EL_ACONTENT; break; default: throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ELEMENT optEnd definition." ); // NOI18N } break; case EL_ACONTENT: if( Character.isWhitespace( (char)i ) ) break; switch( i ) { case '+': state = EL_PLUS; break; case '-': state = EL_MINUS; break; case '>': DTD.ContentModel cm = createContentModel( content, inSet, exSet ); for( Iterator iter = list.iterator(); iter.hasNext(); ) { String key = ((String)iter.next()).toUpperCase(); elementMap.put( key, createElement( key, cm, optStart, optEnd) ); } return; default: throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ELEMENT definition." ); // NOI18N } break; case EL_PLUS: if( i == '(' ) { state = EL_ACONTENT; inSet.addAll( parseGroup( in ) ); } else { throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ELEMENT definition." ); // NOI18N } break; case EL_MINUS: switch( i ) { case '(': state = EL_ACONTENT; List l = parseGroup( in ); exSet.addAll( l ); break; case '-': state = EL_ACONTENT; parseComment( in ); break; default: throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ELEMENT definition." ); // NOI18N } break; } } //XXX } private static final int CO_INIT = 0; private static final int CO_NAME = 1; private static final int CO_AMODEL = 2; private static final int CO_AND = 3; private static final int CO_OR = 4; private static final int CO_SEQ = 5; private static final int CO_AGROUP = 6; /** This automata would parse content model definitions and return them * as a Content instance of root of generated CM tree */ private DTD.Content parseContent( PushbackReader in ) throws IOException, WrongDTDException { int state = EL_INIT; StringBuffer name = new StringBuffer(); ArrayList list = null; DTD.Content content = null; for( ;; ) { int i = in.read(); if( i == -1 ) break; switch( state ) { case CO_INIT: if( Character.isWhitespace( (char)i ) ) break; switch( i ) { case '%': parseEntityReference( in ); break; // Stay in CO_INIT case '(': content = parseContent( in ); state = CO_AMODEL; break; default: name.append( (char)i ); state = CO_NAME; break; } break; case CO_NAME: if( isNameChar( (char)i ) ) { name.append( (char)i ); } else { switch( i ) { case '?': case '+': case '*': DTD.Content leaf = createContentLeaf( name.toString() ); return createContentNode( (char)i, leaf ); default: in.unread( i ); return createContentLeaf( name.toString() ); } } break; case CO_AMODEL: if( Character.isWhitespace( (char)i ) ) break; switch( i ) { case '&': list = new ArrayList(); list.add( content ); list.add( parseContent( in ) ); state = CO_AND; break; case '|': list = new ArrayList(); list.add( content ); list.add( parseContent( in ) ); state = CO_OR; break; case ',': list = new ArrayList(); list.add( content ); list.add( parseContent( in ) ); state = CO_SEQ; break; case ')': state = CO_AGROUP; break; default: throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ELEMENT optEnd definition." ); // NOI18N } break; case CO_AND: if( Character.isWhitespace( (char)i ) ) break; switch( i ) { case '&': list.add( parseContent( in ) ); break; case ')': content = createContentNode( '&', (DTD.Content[])list.toArray( new DTD.Content[0] ) ); state = CO_AGROUP; break; default: throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ContentModel definition." ); // NOI18N } break; case CO_OR: if( Character.isWhitespace( (char)i ) ) break; switch( i ) { case '|': list.add( parseContent( in ) ); break; case ')': content = createContentNode( '|', (DTD.Content[])list.toArray( new DTD.Content[0] ) ); state = CO_AGROUP; break; default: throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ContentModel definition." ); // NOI18N } break; case CO_SEQ: if( Character.isWhitespace( (char)i ) ) break; switch( i ) { case ',': list.add( parseContent( in ) ); break; case ')': content = createContentNode( ',', (DTD.Content[])list.toArray( new DTD.Content[0] ) ); state = CO_AGROUP; break; default: throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ContentModel definition." ); // NOI18N } break; case CO_AGROUP: if( Character.isWhitespace( (char)i ) ) return content; switch( i ) { case '?': case '+': case '*': return createContentNode( (char)i, content ); default: in.unread( i ); return content; } } } throw new WrongDTDException( "Premature end of DTD" ); // NOI18N EOF } private static final int ATT_INIT = 0; private static final int ATT_NAME = 1; private static final int ATT_ANAME = 2; private static final int ATT_ANAME_M = 3; private static final int ATT_VAR = 4; private static final int ATT_AVAR = 5; private static final int ATT_TYPE = 6; private static final int ATT_ATYPE = 7; private static final int ATT_MODE = 8; private void parseAttlist( PushbackReader in ) throws IOException, WrongDTDException { int state = ATT_INIT; StringBuffer name = new StringBuffer(); List list = null; // List of tag names for which are these attribs StringBuffer attr = new StringBuffer(); // name of attribute List values = null; // (list of possible values StringBuffer type = new StringBuffer(); // OR the type of attribute ) String typeHelper = null; // AND name of entity StringBuffer mode = new StringBuffer(); // default mode of this attrib for( ;; ) { int i = in.read(); if( i == -1 ) break; switch( state ) { case ATT_INIT: if( Character.isWhitespace( (char)i ) ) break; switch( i ) { case '%': parseEntityReference( in ); break; // Stay in ATT_INIT case '(': list = parseGroup( in ); state = ATT_ANAME; break; default: name.append( (char)i ); state = ATT_NAME; break; } break; case ATT_NAME: if( Character.isWhitespace( (char)i ) ) { list = new ArrayList(); list.add( name.toString() ); state = ATT_ANAME; break; } name.append( (char)i ); break; case ATT_ANAME: if( Character.isWhitespace( (char)i ) ) break; switch( i ) { case '%': parseEntityReference( in ); break; // Stay in ATT_ANAME case '-': state = ATT_ANAME_M; break; case '>': return; default: attr.append( (char)i ); state = ATT_VAR; break; } break; case ATT_ANAME_M: if( i == '-' ) { parseComment( in ); // skip the comment state = ATT_ANAME; } else { throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ATTLIST definition." ); // NOI18N } break; case ATT_VAR: if( Character.isWhitespace( (char)i ) ) { state = ATT_AVAR; break; } attr.append( (char)i ); break; case ATT_AVAR: if( Character.isWhitespace( (char)i ) ) break; switch( i ) { case '%': typeHelper = parseEntityReference( in ); break; // Stay in ATT_AVAR case '(': values = parseGroup( in ); state = ATT_ATYPE; break; default: type.append( (char)i ); state = ATT_TYPE; break; } break; case ATT_TYPE: if( Character.isWhitespace( (char)i ) ) { state = ATT_ATYPE; break; } type.append( (char)i ); break; case ATT_ATYPE: if( Character.isWhitespace( (char)i ) ) break; switch( i ) { case '%': parseEntityReference( in ); break; // Stay in ATT_ATYPE default: mode.append( (char)i ); state = ATT_MODE; break; } break; case ATT_MODE: if( Character.isWhitespace( (char)i ) ) { // Create attr and add it to all tags DTD.Attribute a = null; if( values == null ) { // HOTSPOT for internation of strings!!! a = createAttribute( attr.toString(), DTD.Attribute.TYPE_BASE, type.toString(), typeHelper, mode.toString(), null ); } else if( values.size() == 1 ) { a = createAttribute( attr.toString(), DTD.Attribute.TYPE_BOOLEAN, null, typeHelper, mode.toString(), null ); } else { SortedMap vals = new TreeMap(); for( Iterator iter = values.iterator(); iter.hasNext(); ) { String valName = ((String)iter.next()).toLowerCase(); vals.put( valName, createValue( valName ) ); } a = createAttribute( attr.toString(), DTD.Attribute.TYPE_SET, null, typeHelper, mode.toString(), vals ); } for( Iterator iter = list.iterator(); iter.hasNext(); ) { addAttrToElement( (String)iter.next(), a ); } typeHelper = null; attr.setLength(0); type.setLength(0); mode.setLength(0); values = null; state = ATT_ANAME; break; } mode.append( (char)i ); break; } } } private static final int OPT_INIT = 0; private static final int OPT_PROCESS = 1; private static final int OPT_APROCESS = 2; private static final int OPT_CONTENT = 3; private static final int OPT_BRAC1 = 4; private static final int OPT_BRAC2 = 5; /** Parser that takes care of conditional inclusion/exclusion of part * of DTD. Gets the control just after "' ) { if( !ignore ) in.unread( content.toString().toCharArray() ); return; } throw new WrongDTDException( "Unexpected char '" + (char)i + "' in processing instruction." ); // NOI18N } } } private static final int COMM_TEXT = 0; // anywhere in text private static final int COMM_DASH = 1; // after '-' /** Parser that eats everything until two consecutive dashes (inclusive) */ private void parseComment( PushbackReader in ) throws IOException, WrongDTDException { int state = COMM_TEXT; for( ;; ) { int i = in.read(); if( i == -1 ) break; // EOF switch( state ) { case COMM_TEXT: if( i == '-' ) state = COMM_DASH; break; case COMM_DASH: if( i == '-' ) return; // finished eating comment state = COMM_TEXT; break; } } throw new WrongDTDException( "Premature end of DTD" ); // NOI18N } /** Parser that reads the name of entity reference and replace it with * the content of that entity (using the pushback capability of input). * It gets the control just after starting '%' * @returns the name of reference which was replaced. */ private String parseEntityReference( PushbackReader in ) throws IOException, WrongDTDException { StringBuffer sb = new StringBuffer(); for( ;; ) { int i = in.read(); if( i == -1 ) break; // EOF if( isNameChar( (char)i ) ) { sb.append( (char)i ); // next char of name } else { String entValue = (String)entityMap.get( sb.toString() ); //get the entity content if( entValue == null ) throw new WrongDTDException( "No such entity: \"" + sb + "\"" ); // NOI18N if( i != ';' ) in.unread( i ); in.unread( entValue.toCharArray() ); // push it back to stream return sb.toString(); } } throw new WrongDTDException( "Premature end of DTD" ); // NOI18N } public static class WrongDTDException extends Exception { public WrongDTDException( String reason ) { super( reason ); } } /*----------------------------------------------------------------------------*/ /*---------- Implementation of classes this factory uses as results ----------*/ /*----------------------------------------------------------------------------*/ /** Implementation of the DTD which this DTDcreator works as factory for. */ private static class DTDImpl implements DTD { private String id; private SortedMap elements; private SortedMap charRefs; DTDImpl( String identifier, SortedMap elements, SortedMap charRefs ) { this.id = identifier; this.elements = elements; this.charRefs = charRefs; } /** Identify this instance of DTD */ public String getIdentifier() { return id; } /** Get List of all Elements whose names starts with given prefix */ public List getElementList( String prefix ) { List l = new ArrayList(); prefix = prefix == null ? "" : prefix.toUpperCase(); Iterator i = elements.tailMap( prefix ).entrySet().iterator(); while( i.hasNext() ) { Map.Entry entry = (Map.Entry)i.next(); if( ((String)entry.getKey()).startsWith( prefix ) ) { l.add( entry.getValue() ); } else { // we're getting data from SortedSet, so when any break; // entry fails, all remaining entry would fail. } } return l; } /** Get the Element of given name. */ public DTD.Element getElement( String name ) { return (DTD.Element)elements.get( name ); } /** Get List of all CharRefs whose aliases starts with given prefix. */ public List getCharRefList( String prefix ) { List l = new ArrayList(); Iterator i = charRefs.tailMap(prefix).entrySet().iterator(); while( i.hasNext() ) { Map.Entry entry = (Map.Entry)i.next(); if( ((String)entry.getKey()).startsWith( prefix ) ) { l.add( entry.getValue() ); } else { // we're getting data from SortedSet, so when any break; // entry fails, all remaining entry would fail. } } return l; } /** Get the CharRef of given name */ public DTD.CharRef getCharRef( String name ) { return (DTD.CharRef)charRefs.get( name ); } public String toString() { return super.toString() + "[id=" + id + ", elements=" + elements + ",charRefs=" + charRefs + "]"; // NOI18N } } /** Implementation of Element used by this DTDcreator. */ private static class ElementImpl implements DTD.Element { private String name; private DTD.ContentModel model; private boolean optStart; private boolean optEnd; private SortedMap attributes; //these are sorted just by name private DTD dtd; ElementImpl( String name, DTD.ContentModel model, boolean optStart, boolean optEnd, SortedMap attributes ) { this.name = name; this.model = model; this.optStart = optStart; this.optEnd = optEnd; this.attributes = attributes; } /** Get the name of this Element */ public String getName() { return name; } /** Shorthand to resolving if content model of this Element is EMPTY */ public boolean isEmpty() { if( optEnd && model.getContent() instanceof DTD.ContentLeaf ) return true; //&& ((DTD.ContentLeaf)model.getContent()).getName().equals( "EMPTY" ) ) return true; return false; } /** Tells if this Element has optional Start Tag. */ public boolean hasOptionalStart() { return optStart; } /** Tells if this Element has optional End Tag. */ public boolean hasOptionalEnd() { return optEnd; } /** Get the List of Attributes of this Element, which starts with * given |
... this post is sponsored by my books ... | |
#1 New Release! |
FP Best Seller |
Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.
A percentage of advertising revenue from
pages under the /java/jwarehouse
URI on this website is
paid back to open source projects.