KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > editor > ext > html > dtd > DTDParser


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 package org.netbeans.editor.ext.html.dtd;
20
21 import java.io.Reader JavaDoc;
22 import java.io.PushbackReader JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.util.*;
25
26 import org.netbeans.editor.ext.html.WeakHashSet;
27
28 /**
29  * !!! Includes !!!! String->DTD.Element
30  * @author Petr Nejedly
31  * @version 0.2
32  */

33 class DTDParser extends Object JavaDoc {
34
35     // The provider used to provide the Readers for this DTD.
36
private ReaderProvider provider = null;
37
38     // Asks for Reader for given DTD.
39
private Reader JavaDoc getReader( String JavaDoc identifier, String JavaDoc fileName ) {
40         if( provider == null ) return null;
41         return provider.getReaderForIdentifier( identifier, fileName );
42     }
43         
44     /** Weak set for holding already created strings, to not create more
45      * instances of the same string */

46     private WeakHashSet stringCache = new WeakHashSet( 131, 0.75f );
47     
48     /** Weak set of attributes - helps sharing common attributes */
49     private WeakHashSet attributes = new WeakHashSet( 23, 0.75f );
50     
51     /** Weak set of models */
52     private WeakHashSet models = new WeakHashSet( 131, 0.75f );
53     
54     /** Weak set of Contents */
55     private WeakHashSet contents = new WeakHashSet( 131, 0.75f );
56     
57     /** Temporal storage of all ContentLeafs that needs to get their
58      * elements filled in at the end of parsing
59      */

60     Set leafs = new HashSet( 131, 0.75f );
61
62     /** Map of all character references.
63      * Mapping is String name -> DTD.CharRef instance */

64     private SortedMap charRefs = new TreeMap();
65     
66     /** Map holding partially completed instances of Element.
67      * Mapping is String name -> DTD.Element instance */

68     private SortedMap elementMap = new TreeMap();
69     
70     /** Map holding entities during creation of DTD.
71      * Mapping is String name -> String content.
72      * This map should not be used for direct put(..), because entities
73      * are defined by first declaration and can not be overriden.
74      */

75     private Map entityMap = new HashMap();
76     
77     
78     public DTD createDTD( ReaderProvider provider, String JavaDoc identifier, String JavaDoc fileName ) throws WrongDTDException {
79         this.provider = provider;
80         
81         Reader JavaDoc reader = getReader( identifier, fileName );
82         if( reader == null ) throw new WrongDTDException( "Can't open Reader for public identifier " + identifier ); // NOI18N
83
try {
84             parseDTD( new PushbackReader JavaDoc( reader, 1024*128 ) );
85         } catch( IOException JavaDoc e ) {
86             throw new WrongDTDException( "IOException during parsing: " + e.getMessage() ); // NOI18N
87
}
88
89         // fixup includes and excludes of all elements
90
for( Iterator it = elementMap.values().iterator(); it.hasNext (); ) {
91             DTD.Element elem = (DTD.Element)it.next();
92             ContentModelImpl cm = (ContentModelImpl)elem.getContentModel();
93
94             Set newIncs = new HashSet();
95             for( Iterator incIter = cm.included.iterator(); incIter.hasNext (); ) {
96                 Object JavaDoc oldElem;
97                 Object JavaDoc subElem = oldElem = incIter.next();
98                 if( subElem instanceof String JavaDoc ) {
99                     subElem = elementMap.get( ((String JavaDoc)subElem).toUpperCase() );
100                 }
101                 if( subElem == null ) {
102                     throw new WrongDTDException( "'" + oldElem + "' element referenced from " + elem.getName() + " not found throughout the DTD." ); // NOI18N
103
}
104                 newIncs.add( subElem );
105             }
106             cm.included = newIncs;
107     
108             Set newExcs = new HashSet();
109             for( Iterator excIter = cm.excluded.iterator(); excIter.hasNext (); ) {
110                 Object JavaDoc oldElem;
111                 Object JavaDoc subElem = oldElem = excIter.next();
112                 if( subElem instanceof String JavaDoc ) {
113                     subElem = elementMap.get( ((String JavaDoc)subElem).toUpperCase() );
114                 }
115                 if( subElem == null ) {
116                     throw new WrongDTDException( "'" + oldElem + "' element referenced from " + elem.getName() + " not found throughout the DTD." ); // NOI18N
117
}
118                 newExcs.add( subElem );
119             }
120             cm.excluded = newExcs;
121             cm.hashcode = cm.content.hashCode() + 2*cm.included.hashCode() + 3*cm.excluded.hashCode();
122         }
123
124         
125         // fixup content leafs
126
for( Iterator it = leafs.iterator(); it.hasNext (); ) {
127             ContentLeafImpl leaf = (ContentLeafImpl)it.next();
128             leaf.elem = (DTD.Element)elementMap.get( leaf.elemName );
129         }
130         
131         return new DTDImpl( identifier, elementMap, charRefs );
132     }
133
134
135     /** Method for adding new entities to their map. Obeys the rule that
136      * entity, once defined, can not be overriden */

137     void addEntity( String JavaDoc name, String JavaDoc content ) {
138         if( entityMap.get( name ) == null ) entityMap.put( name, content );
139     }
140     
141     /** Method for adding new entities to their map. Obeys the rule that
142      * entity, once defined, can not be overriden */

143     void addPublicEntity( String JavaDoc name, String JavaDoc identifier, String JavaDoc file ) throws WrongDTDException {
144         if( entityMap.get( name ) == null ) {
145
146             StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
147             char[] buffer = new char[16384];
148             Reader JavaDoc r = getReader( identifier, file );
149             try {
150                 int len;
151                 while( (len = r.read( buffer )) >= 0 ) {
152                     sb.append( buffer, 0, len );
153                 }
154             } catch( IOException JavaDoc e ) {
155                 throw new WrongDTDException( "Error reading included public entity " + name + " - " + e.getMessage() ); // NOI18N
156
}
157             
158             entityMap.put( name, sb.toString() );
159         }
160     }
161     
162     DTD.Value createValue( String JavaDoc name ) {
163         return new ValueImpl( (String JavaDoc)stringCache.put( name ) );
164     }
165     
166     /** Creates new or lookups old ContentModel with given properites */
167     DTD.ContentModel createContentModel( DTD.Content content, Set included, Set excluded ) {
168         
169         DTD.ContentModel cm = new ContentModelImpl( content, included, excluded );
170         return (DTD.ContentModel)models.put( cm );
171     }
172
173     /** Creates new or lookups old ContentLeaf with given properites */
174     DTD.Content createContentLeaf( String JavaDoc name ) {
175         DTD.Content c = new ContentLeafImpl( name );
176         c = (DTD.Content)contents.put( c );
177         leafs.add( c ); // remember for final fixup
178
return c;
179     }
180
181     /** Creates new or lookups old ContentNode with given properites */
182     DTD.Content createContentNode( char type, DTD.Content subContent ) {
183         return (DTD.Content)contents.put( new UnaryContentNodeImpl( type, subContent ) );
184     }
185
186     /** Creates new or lookups old ContentNode with given properites */
187     DTD.Content createContentNode( char type, DTD.Content[] subContent ) {
188         return (DTD.Content)contents.put( new MultiContentNodeImpl( type, subContent ) );
189     }
190
191     DTD.Element createElement( String JavaDoc name, DTD.ContentModel cm, boolean optStart, boolean optEnd) {
192         DTD.Element retVal = new ElementImpl( name, cm, optStart, optEnd, new TreeMap() );
193         return retVal;
194     }
195     
196     /** Creates new or lookups old attribute with given properites */
197     DTD.Attribute createAttribute( String JavaDoc name, int type, String JavaDoc baseType, String JavaDoc typeHelper, String JavaDoc defaultMode, SortedMap values ) {
198         DTD.Attribute attr = new AttributeImpl( name, type,
199             (String JavaDoc)stringCache.put( baseType ),
200             (String JavaDoc)stringCache.put( typeHelper ),
201             (String JavaDoc)stringCache.put( defaultMode ),
202             values
203         );
204         return (DTD.Attribute)attributes.put( attr );
205     }
206     
207     /** Adds given instance of DTD.Attribute to Element named elemName */
208     void addAttrToElement( String JavaDoc elemName, DTD.Attribute attr) throws WrongDTDException {
209         ElementImpl elem = (ElementImpl)elementMap.get( elemName.toUpperCase() );
210         if( elem == null ) throw new WrongDTDException( "Attribute definition for unknown Element \"" + elemName +"\"." ); // NOI18N
211
elem.addAttribute( attr );
212     }
213     
214     void createAddCharRef( String JavaDoc name, char value ) {
215         DTD.CharRef ref = new CharRefImpl( name, value );
216         charRefs.put( name, ref );
217     }
218
219     private boolean isNameChar( char c ) {
220         return Character.isLetterOrDigit( c ) || c == '_' || c == '-' || c == '.' || c == ':';
221     }
222     
223     
224 /*----------------------------------------------------------------------------*/
225 /*----------------------------- Parsing routines ---------------------------- */
226 /*----------------------------------------------------------------------------*/
227     private static final int DTD_INIT = 0;
228     private static final int DTD_LT = 1; // after '<'
229
private static final int DTD_EXC = 2; // after "<!"
230
private static final int DTD_MINUS = 3; // after "<!-"
231
private static final int DTD_ACOMMENT = 4; // after comment was parsed, awaiting '>'
232

233     private void parseDTD( PushbackReader JavaDoc in ) throws IOException JavaDoc, WrongDTDException {
234         int state = DTD_INIT;
235         for( ;; ) {
236             int i = in.read();
237             if( i == -1 ) {
238                 break;
239             }
240             switch( state ) {
241                 case DTD_INIT:
242                     switch( i ) {
243                         case '<':
244                             state = DTD_LT;
245                             break;
246                         case '%':
247                             parseEntityReference( in );
248                             break; // Stay in DTD_INIT
249
}
250                     break;
251                     
252                 case DTD_LT:
253                     if( i != '!' ) throw new WrongDTDException( "Unexpected char '" + (char)i + "' after '<'" ); // NOI18N
254
state = DTD_EXC;
255                     break;
256                     
257                 case DTD_EXC:
258                     switch( i ) {
259                         case '-':
260                             state = DTD_MINUS;
261                             break;
262                         case '[':
263                             parseOptional( in );
264                             state = DTD_INIT;
265                             break;
266                         default:
267                             in.unread( i );
268                             parseMarkup( in );
269                             state = DTD_INIT;
270                             break;
271                     }
272                     break;
273                     
274                 case DTD_MINUS:
275                     if( i != '-' ) throw new WrongDTDException( "Unexpected char '" + (char)i + "' after \"<!-\"" ); // NOI18N
276
parseComment( in );
277                     state = DTD_ACOMMENT;
278                     break;
279                     
280                 case DTD_ACOMMENT:
281                     if( i != '>' ) throw new WrongDTDException( "Unexpected char '" + (char)i + "' after comment" ); // NOI18N
282
state = DTD_INIT;
283                     break;
284                     
285             }
286         }
287         if( state != DTD_INIT ) throw new WrongDTDException( "Premature end of DTD" ); // NOI18N
288
}
289     
290     /** Parser that reads the markup type after <!. Recognizes ENTITY, ELEMENT
291      * and ATTLIST markup and forwards their processing to proper parser.
292      * It gets the control just after starting "<!" and releases after eating
293      * final '>' */

294     private void parseMarkup( PushbackReader JavaDoc in ) throws IOException JavaDoc, WrongDTDException {
295         StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
296         for( ;; ) {
297             int i = in.read();
298             if( i == -1 ) throw new WrongDTDException( "Premature end of DTD" ); // NOI18N EOF
299
if( i == ' ' ) break;
300             sb.append( (char)i ); // next char of name
301
}
302         
303         String JavaDoc markup = sb.toString();
304         
305         if( "ENTITY".equals( markup ) ) { // NOI18N
306
parseEntityDefinition( in );
307         } else if( "ELEMENT".equals( markup ) ) { // NOI18N
308
parseElement( in );
309         } else if( "ATTLIST".equals( markup ) ) { // NOI18N
310
parseAttlist( in );
311         } else throw new WrongDTDException( "Wrong DTD markup <!" + markup ); // NOI18N
312
}
313     
314     
315     private static final int PED_INIT = 0;
316     private static final int PED_PERCENT = 1;
317     private static final int PED_CHAR = 2;
318     private static final int PED_NAME = 3;
319     private static final int PED_ANAME = 4;
320     private static final int PED_VAL = 5;
321     private static final int PED_TYPE = 6;
322     private static final int PED_AVAL = 7;
323     private static final int PED_AVAL_M = 8;
324     private static final int PED_ATYPE = 9;
325     private static final int PED_ID = 10;
326     private static final int PED_AID = 11;
327     private static final int PED_FILE = 12;
328     private static final int PED_AFILE = 13;
329     private static final int PED_AFILE_M = 14;
330     private static final int PED_ACHAR = 15;
331     private static final int PED_CH_TYPE = 16;
332     private static final int PED_CH_ATYPE = 17;
333     private static final int PED_CH_QUOT = 18;
334     
335     
336     /* TODO: Parsing fo character references */
337     private void parseEntityDefinition( PushbackReader JavaDoc in ) throws IOException JavaDoc, WrongDTDException {
338         int state = PED_INIT;
339         StringBuffer JavaDoc name = new StringBuffer JavaDoc();
340         StringBuffer JavaDoc value = new StringBuffer JavaDoc();
341         StringBuffer JavaDoc type = new StringBuffer JavaDoc();
342         StringBuffer JavaDoc identifier = new StringBuffer JavaDoc();
343         
344         for( ;; ) {
345             int i = in.read();
346             if( i == -1 ) throw new WrongDTDException( "Premature end of DTD" ); // NOI18N EOF
347
switch( state ) {
348                 case PED_INIT:
349                     if( Character.isWhitespace( (char)i ) ) break;
350                     if( i == '%' ) state = PED_PERCENT;
351                     else {
352                         name.append( (char)i );
353                         state = PED_CHAR;
354                     }
355                     break;
356                     
357                 case PED_PERCENT:
358                     if( Character.isWhitespace( (char)i ) ) break;
359                     name.append( (char)i );
360                     state = PED_NAME;
361                     break;
362                     
363                 case PED_NAME:
364                     if( Character.isWhitespace( (char)i ) ) {
365                         state = PED_ANAME;
366                     } else {
367                         name.append( (char)i );
368                     }
369                     break;
370                     
371                 case PED_ANAME:
372                     if( Character.isWhitespace( (char)i ) ) break;
373                     if( i == '"' ) state = PED_VAL;
374                     else {
375                         in.unread( i );
376                         state = PED_TYPE;
377                     }
378                     break;
379                     
380                 case PED_VAL:
381                     if( i == '"' ) {
382                         addEntity( name.toString(), value.toString() );
383                         state = PED_AVAL;
384                     } else {
385                         value.append( (char)i );
386                     }
387                     break;
388                     
389                 case PED_AVAL:
390                     if( i == '>' ) {
391                         return;
392                     }
393                     if( i == '-' ) state = PED_AVAL_M;
394                     break;
395                     
396                 case PED_AVAL_M:
397                     if( i == '-' ) parseComment( in );
398                     state = PED_AVAL;
399                     break;
400                     
401                 case PED_TYPE:
402                     if( Character.isWhitespace( (char)i ) ) {
403                         if( type.toString().equals( "PUBLIC" ) ) { // NOI18N
404
state = PED_ATYPE;
405                         } else {
406                             throw new WrongDTDException( "Unexpected entity type \"" + type + "\"." ); // NOI18N
407
}
408                     } else {
409                         type.append( (char)i );
410                     }
411                     break;
412                     
413                 case PED_ATYPE:
414                     if( Character.isWhitespace( (char)i ) ) break;
415                     if( i == '"' ) {
416                         state = PED_ID;
417                         break;
418                     }
419                     throw new WrongDTDException( "Unexpected char '" + (char)i + "' in PUBLIC entity." ); // NOI18N
420

421                 case PED_ID:
422                     if( i == '"' ) {
423                         state = PED_AID;
424                     } else {
425                         identifier.append( (char)i );
426                     }
427                     break;
428                     
429                 case PED_AID:
430                     if( Character.isWhitespace( (char)i ) ) break;
431                     if( i == '"' ) {
432                         state = PED_FILE;
433                         break;
434                     }
435                     if( i == '>' ) {
436                         addPublicEntity( name.toString(), identifier.toString(), null );
437                         return;
438                     }
439                     throw new WrongDTDException( "Unexpected char '" + (char)i + "' in PUBLIC entity." ); // NOI18N
440

441                 case PED_FILE:
442                     if( i == '"' ) {
443                         state = PED_AFILE;
444                     } else {
445                         value.append( (char)i );
446                     }
447                     break;
448                     
449                 case PED_AFILE:
450                     if( Character.isWhitespace( (char)i ) ) break;
451                     if( i == '-' ) {
452                         state = PED_AFILE_M;
453                         break;
454                     }
455                     if( i == '>' ) {
456                         addPublicEntity( name.toString(), identifier.toString(), value.toString() );
457                         return;
458                     }
459                     throw new WrongDTDException( "Unexpected char '" + (char)i + "' in PUBLIC entity." ); // NOI18N
460

461                 case PED_AFILE_M:
462                     if( i == '-' ) {
463                         parseComment( in );
464                         state = PED_FILE;
465                         break;
466                     }
467                     throw new WrongDTDException( "Unexpected sequence \"-" + (char)i + "\" in in PUBLIC entity." ); // NOI18N
468

469                 case PED_CHAR:
470                     if( Character.isWhitespace( (char)i ) ) {
471                         state = PED_ACHAR;
472                     } else {
473                         name.append( (char)i );
474                     }
475                     break;
476                     
477                 case PED_ACHAR:
478                     if( Character.isWhitespace( (char)i ) ) break;
479                     else {
480                         type.append( (char)i );
481                         state = PED_CH_TYPE;
482                     }
483                     break;
484                     
485                 case PED_CH_TYPE:
486                     if( Character.isWhitespace( (char)i ) ) {
487                         if( type.toString().equals( "CDATA" ) ) { // NOI18N
488
state = PED_ATYPE;
489                             state = PED_CH_ATYPE;
490                         } else {
491                             throw new WrongDTDException( "Unexpected entity type \"" + type + "\"." ); // NOI18N
492
}
493                     } else {
494                         type.append( (char)i );
495                     }
496                     break;
497                     
498                 case PED_CH_ATYPE:
499                     if( Character.isWhitespace( (char)i ) ) break;
500                     else if( i == '"' ) {
501                         state = PED_CH_QUOT;
502                     } else {
503                         throw new WrongDTDException( "Unexpected char '" + (char)i + "' in entity." ); // NOI18N
504
}
505                     break;
506                     
507                 case PED_CH_QUOT:
508                     if( i == '"' ) {
509                         value.delete( 0, 2 );
510                         value.deleteCharAt( value.length() - 1 );
511                         int code = Integer.parseInt( value.toString() );
512                         createAddCharRef( name.toString(), (char)code );
513                         state = PED_AVAL;
514                     } else {
515                         value.append( (char)i );
516                     }
517             }
518             
519         }
520     }
521     
522     private static final int GR_INIT=0;
523     private static final int GR_NAME=1;
524     private static final int GR_ANAME=2;
525     /** Parse group of names separated by '|' character and optional spaces
526      * @return List of Strings containing names */

527     private List parseGroup( PushbackReader JavaDoc in ) throws IOException JavaDoc, WrongDTDException {
528         int state = GR_INIT;
529         StringBuffer JavaDoc name = new StringBuffer JavaDoc();
530         List list = new ArrayList();
531         
532         for( ;; ) {
533             int i = in.read();
534             if( i == -1 ) throw new WrongDTDException( "Premature end of DTD" ); // NOI18N EOF
535
switch( state ) {
536                 case GR_INIT:
537                     if( Character.isWhitespace( (char)i ) ) break;
538                     if( i == '%' ) {
539                         parseEntityReference( in );
540                     } else {
541                         name.append( (char)i );
542                         state = GR_NAME;
543                     }
544                     break;
545                     
546                 case GR_NAME:
547                     if( isNameChar( (char)i ) ) {
548                         name.append( (char)i );
549                         break;
550                     }
551                     switch( i ) {
552                         case ')':
553                             list.add( name.toString() );
554                             return list;
555                         case '|':
556                             list.add( name.toString() );
557                             name.setLength( 0 );
558                             state = GR_INIT;
559                             break;
560                         default:
561                             if( Character.isWhitespace( (char)i ) ) {
562                                 list.add( name.toString() );
563                                 name.setLength( 0 );
564                                 state = GR_ANAME;
565                                 break;
566                             } else {
567                                 throw new WrongDTDException( "Unexpected char '" + (char)i + "' in group definition." ); // NOI18N
568
}
569                     }
570                     break;
571                     
572                 case GR_ANAME:
573                     if( Character.isWhitespace( (char)i ) ) break;
574                     switch( i ) {
575                         case ')':
576                             return list;
577                         case '|':
578                             state = GR_INIT;
579                             break;
580                         default:
581                             throw new WrongDTDException( "Unexpected char '" + (char)i + "' in group definition." ); // NOI18N
582
}
583                     break;
584             }
585         }
586                     
587     }
588     
589     private static final int EL_INIT = 0;
590     private static final int EL_NAME = 1;
591     private static final int EL_ANAME = 2;
592     private static final int EL_ASTART = 3;
593     private static final int EL_ACONTENT = 4;
594     private static final int EL_PLUS = 5;
595     private static final int EL_MINUS = 6;
596     
597     /** parse the whole element(s) definition including content model.
598      * Create corresponding instances of DTD.Element filled with proper
599      * informations. Make the same content models and their contents shared
600      * across the DTD */

601     private void parseElement( PushbackReader JavaDoc in ) throws IOException JavaDoc, WrongDTDException {
602         int state = EL_INIT;
603         StringBuffer JavaDoc name = new StringBuffer JavaDoc();
604         List list = null;
605         boolean optStart = false;
606         boolean optEnd = false;
607         DTD.Content content = null;
608         Set inSet = new HashSet();
609         Set exSet = new HashSet();
610         
611         for( ;; ) {
612             int i = in.read();
613             if( i == -1 ) break;
614             switch( state ) {
615                 case EL_INIT:
616                     if( Character.isWhitespace( (char)i ) ) break;
617                     switch( i ) {
618                         case '(':
619                             list = parseGroup( in );
620                             state = EL_ANAME;
621                             break;
622                         case '%':
623                             parseEntityReference( in );
624                             break; // Stay in EL_INIT
625
default:
626                             name.append( (char)i );
627                             state = EL_NAME;
628                             break;
629                     }
630                     break;
631                     
632                 case EL_NAME:
633                     if( Character.isWhitespace( (char)i ) ) {
634                         state = EL_ANAME;
635                         list = new ArrayList();
636                         list.add( name.toString() );
637                     } else {
638                             name.append( (char)i );
639                     }
640                     break;
641                     
642                    
643                 case EL_ANAME:
644                     if( Character.isWhitespace( (char)i ) ) break;
645                     switch( i ) {
646                         case 'O':
647                             optStart = true; // fall fhrough
648
case '-':
649                             state = EL_ASTART;
650                             break;
651                         default:
652                             throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ELEMENT optStart definition." ); // NOI18N
653
}
654                     break;
655
656                 case EL_ASTART:
657                     if( Character.isWhitespace( (char)i ) ) break;
658                     switch( i ) {
659                         case 'O':
660                             optEnd = true; // fall fhrough
661
case '-':
662                             content = parseContent( in );
663                             state = EL_ACONTENT;
664                             break;
665                         default:
666                             throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ELEMENT optEnd definition." ); // NOI18N
667
}
668                     break;
669                     
670                 case EL_ACONTENT:
671                     if( Character.isWhitespace( (char)i ) ) break;
672                     switch( i ) {
673                         case '+':
674                             state = EL_PLUS;
675                             break;
676                         case '-':
677                             state = EL_MINUS;
678                             break;
679                         case '>':
680                             DTD.ContentModel cm = createContentModel( content, inSet, exSet );
681                             for( Iterator iter = list.iterator(); iter.hasNext(); ) {
682                                 String JavaDoc key = ((String JavaDoc)iter.next()).toUpperCase();
683                                 elementMap.put( key, createElement( key, cm, optStart, optEnd) );
684                             }
685                             return;
686                         default:
687                             throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ELEMENT definition." ); // NOI18N
688
}
689                     break;
690                     
691                 case EL_PLUS:
692                     if( i == '(' ) {
693                         state = EL_ACONTENT;
694                         inSet.addAll( parseGroup( in ) );
695                     } else {
696                         throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ELEMENT definition." ); // NOI18N
697
}
698                     break;
699                     
700                 case EL_MINUS:
701                     switch( i ) {
702                         case '(':
703                             state = EL_ACONTENT;
704                             List l = parseGroup( in );
705                             exSet.addAll( l );
706                             break;
707                         case '-':
708                             state = EL_ACONTENT;
709                             parseComment( in );
710                             break;
711                         default:
712                             throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ELEMENT definition." ); // NOI18N
713
}
714                     break;
715             }
716         }
717
718         //XXX
719
}
720     
721     private static final int CO_INIT = 0;
722     private static final int CO_NAME = 1;
723     private static final int CO_AMODEL = 2;
724     private static final int CO_AND = 3;
725     private static final int CO_OR = 4;
726     private static final int CO_SEQ = 5;
727     private static final int CO_AGROUP = 6;
728     /** This automata would parse content model definitions and return them
729      * as a Content instance of root of generated CM tree */

730     private DTD.Content parseContent( PushbackReader JavaDoc in ) throws IOException JavaDoc, WrongDTDException {
731         int state = EL_INIT;
732         StringBuffer JavaDoc name = new StringBuffer JavaDoc();
733         ArrayList list = null;
734         DTD.Content content = null;
735         
736         for( ;; ) {
737             int i = in.read();
738             if( i == -1 ) break;
739             switch( state ) {
740                 case CO_INIT:
741                     if( Character.isWhitespace( (char)i ) ) break;
742                     switch( i ) {
743                         case '%':
744                             parseEntityReference( in );
745                             break; // Stay in CO_INIT
746
case '(':
747                             content = parseContent( in );
748                             state = CO_AMODEL;
749                             break;
750                         default:
751                             name.append( (char)i );
752                             state = CO_NAME;
753                             break;
754                     }
755                     break;
756                     
757                 case CO_NAME:
758                     if( isNameChar( (char)i ) ) {
759                         name.append( (char)i );
760                     } else {
761                         switch( i ) {
762                             case '?':
763                             case '+':
764                             case '*':
765                                 DTD.Content leaf = createContentLeaf( name.toString() );
766                                 return createContentNode( (char)i, leaf );
767
768                             default:
769                                 in.unread( i );
770                                 return createContentLeaf( name.toString() );
771                         }
772                     }
773                     break;
774
775                 case CO_AMODEL:
776                     if( Character.isWhitespace( (char)i ) ) break;
777                     switch( i ) {
778                         case '&':
779                             list = new ArrayList();
780                             list.add( content );
781                             list.add( parseContent( in ) );
782                             state = CO_AND;
783                             break;
784                         case '|':
785                             list = new ArrayList();
786                             list.add( content );
787                             list.add( parseContent( in ) );
788                             state = CO_OR;
789                             break;
790                         case ',':
791                             list = new ArrayList();
792                             list.add( content );
793                             list.add( parseContent( in ) );
794                             state = CO_SEQ;
795                             break;
796                         case ')':
797                             state = CO_AGROUP;
798                             break;
799                         default:
800                             throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ELEMENT optEnd definition." ); // NOI18N
801
}
802                     break;
803                     
804                 case CO_AND:
805                     if( Character.isWhitespace( (char)i ) ) break;
806                     switch( i ) {
807                         case '&':
808                             list.add( parseContent( in ) );
809                             break;
810                         case ')':
811                             content = createContentNode( '&', (DTD.Content[])list.toArray( new DTD.Content[0] ) );
812                             state = CO_AGROUP;
813                             break;
814                         default:
815                             throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ContentModel definition." ); // NOI18N
816
}
817                     break;
818
819                 case CO_OR:
820                     if( Character.isWhitespace( (char)i ) ) break;
821                     switch( i ) {
822                         case '|':
823                             list.add( parseContent( in ) );
824                             break;
825                         case ')':
826                             content = createContentNode( '|', (DTD.Content[])list.toArray( new DTD.Content[0] ) );
827                             state = CO_AGROUP;
828                             break;
829                         default:
830                             throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ContentModel definition." ); // NOI18N
831
}
832                     break;
833                     
834                 case CO_SEQ:
835                     if( Character.isWhitespace( (char)i ) ) break;
836                     switch( i ) {
837                         case ',':
838                             list.add( parseContent( in ) );
839                             break;
840                         case ')':
841                             content = createContentNode( ',', (DTD.Content[])list.toArray( new DTD.Content[0] ) );
842                             state = CO_AGROUP;
843                             break;
844                         default:
845                             throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ContentModel definition." ); // NOI18N
846
}
847                     break;
848
849                 case CO_AGROUP:
850                     if( Character.isWhitespace( (char)i ) ) return content;
851                     switch( i ) {
852                         case '?':
853                         case '+':
854                         case '*':
855                             return createContentNode( (char)i, content );
856                         default:
857                             in.unread( i );
858                             return content;
859                     }
860             }
861         }
862         
863         throw new WrongDTDException( "Premature end of DTD" ); // NOI18N EOF
864

865     }
866     private static final int ATT_INIT = 0;
867     private static final int ATT_NAME = 1;
868     private static final int ATT_ANAME = 2;
869     private static final int ATT_ANAME_M = 3;
870     private static final int ATT_VAR = 4;
871     private static final int ATT_AVAR = 5;
872     private static final int ATT_TYPE = 6;
873     private static final int ATT_ATYPE = 7;
874     private static final int ATT_MODE = 8;
875     private void parseAttlist( PushbackReader JavaDoc in ) throws IOException JavaDoc, WrongDTDException {
876         int state = ATT_INIT;
877         StringBuffer JavaDoc name = new StringBuffer JavaDoc();
878         List list = null; // List of tag names for which are these attribs
879
StringBuffer JavaDoc attr = new StringBuffer JavaDoc(); // name of attribute
880
List values = null; // (list of possible values
881
StringBuffer JavaDoc type = new StringBuffer JavaDoc(); // OR the type of attribute )
882
String JavaDoc typeHelper = null; // AND name of entity
883
StringBuffer JavaDoc mode = new StringBuffer JavaDoc(); // default mode of this attrib
884
for( ;; ) {
885             int i = in.read();
886             if( i == -1 ) break;
887             switch( state ) {
888                 case ATT_INIT:
889                     if( Character.isWhitespace( (char)i ) ) break;
890                     switch( i ) {
891                         case '%':
892                             parseEntityReference( in );
893                             break; // Stay in ATT_INIT
894
case '(':
895                             list = parseGroup( in );
896                             state = ATT_ANAME;
897                             break;
898                         default:
899                             name.append( (char)i );
900                             state = ATT_NAME;
901                             break;
902                     }
903                     break;
904                     
905                 case ATT_NAME:
906                     if( Character.isWhitespace( (char)i ) ) {
907                         list = new ArrayList();
908                         list.add( name.toString() );
909                         state = ATT_ANAME;
910                         break;
911                     }
912                     name.append( (char)i );
913                     break;
914                     
915                 case ATT_ANAME:
916                     if( Character.isWhitespace( (char)i ) ) break;
917                     switch( i ) {
918                         case '%':
919                             parseEntityReference( in );
920                             break; // Stay in ATT_ANAME
921
case '-':
922                             state = ATT_ANAME_M;
923                             break;
924                         case '>':
925                             return;
926                         default:
927                             attr.append( (char)i );
928                             state = ATT_VAR;
929                             break;
930                     }
931                     break;
932                     
933                 case ATT_ANAME_M:
934                     if( i == '-' ) {
935                         parseComment( in ); // skip the comment
936
state = ATT_ANAME;
937                     } else {
938                         throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ATTLIST definition." ); // NOI18N
939
}
940                     break;
941                     
942                 case ATT_VAR:
943                     if( Character.isWhitespace( (char)i ) ) {
944                         state = ATT_AVAR;
945                         break;
946                     }
947                     attr.append( (char)i );
948                     break;
949                     
950                 case ATT_AVAR:
951                     if( Character.isWhitespace( (char)i ) ) break;
952                     switch( i ) {
953                         case '%':
954                             typeHelper = parseEntityReference( in );
955                             break; // Stay in ATT_AVAR
956
case '(':
957                             values = parseGroup( in );
958                             state = ATT_ATYPE;
959                             break;
960                         default:
961                             type.append( (char)i );
962                             state = ATT_TYPE;
963                             break;
964                     }
965                     break;
966
967                 case ATT_TYPE:
968                     if( Character.isWhitespace( (char)i ) ) {
969                         state = ATT_ATYPE;
970                         break;
971                     }
972                     type.append( (char)i );
973                     break;
974                     
975                 case ATT_ATYPE:
976                     if( Character.isWhitespace( (char)i ) ) break;
977                     switch( i ) {
978                         case '%':
979                             parseEntityReference( in );
980                             break; // Stay in ATT_ATYPE
981
default:
982                             mode.append( (char)i );
983                             state = ATT_MODE;
984                             break;
985                     }
986                     break;
987                     
988                 case ATT_MODE:
989                     if( Character.isWhitespace( (char)i ) ) {
990                         // Create attr and add it to all tags
991
DTD.Attribute a = null;
992                         
993                         if( values == null ) { // HOTSPOT for internation of strings!!!
994
a = createAttribute( attr.toString(),
995                                 DTD.Attribute.TYPE_BASE, type.toString(),
996                                 typeHelper, mode.toString(), null );
997                         } else if( values.size() == 1 ) {
998                             a = createAttribute( attr.toString(),
999                                 DTD.Attribute.TYPE_BOOLEAN, null, typeHelper,
1000                                mode.toString(), null );
1001                        } else {
1002                            SortedMap vals = new TreeMap();
1003                            for( Iterator iter = values.iterator(); iter.hasNext(); ) {
1004                                String JavaDoc valName = ((String JavaDoc)iter.next()).toLowerCase();
1005                                vals.put( valName, createValue( valName ) );
1006                            }
1007                            a = createAttribute( attr.toString(),
1008                                DTD.Attribute.TYPE_SET, null, typeHelper,
1009                                mode.toString(), vals );
1010                        }
1011                        for( Iterator iter = list.iterator(); iter.hasNext(); ) {
1012                            addAttrToElement( (String JavaDoc)iter.next(), a );
1013                        }
1014                        
1015                        typeHelper = null;
1016                        attr.setLength(0);
1017                        type.setLength(0);
1018                        mode.setLength(0);
1019                        values = null;
1020                        
1021                        state = ATT_ANAME;
1022                        break;
1023                    }
1024                    mode.append( (char)i );
1025                    break;
1026            }
1027        }
1028    }
1029    
1030    
1031    private static final int OPT_INIT = 0;
1032    private static final int OPT_PROCESS = 1;
1033    private static final int OPT_APROCESS = 2;
1034    private static final int OPT_CONTENT = 3;
1035    private static final int OPT_BRAC1 = 4;
1036    private static final int OPT_BRAC2 = 5;
1037    /** Parser that takes care of conditional inclusion/exclusion of part
1038     * of DTD. Gets the control just after "<![" */

1039    private void parseOptional( PushbackReader JavaDoc in ) throws IOException JavaDoc, WrongDTDException {
1040        int state = OPT_INIT;
1041        StringBuffer JavaDoc process = new StringBuffer JavaDoc();
1042        StringBuffer JavaDoc content = new StringBuffer JavaDoc();
1043        boolean ignore = false;
1044        
1045        for( ;; ) {
1046            int i = in.read();
1047            if( i == -1 ) break; // EOF
1048
switch( state ) {
1049                case OPT_INIT:
1050                    if( Character.isWhitespace( (char)i ) ) break;
1051                    if( i == '%' ) {
1052                        parseEntityReference( in );
1053                        break;
1054                    }
1055                    process.append( (char)i );
1056                    state = OPT_PROCESS;
1057                    break;
1058                    
1059                case OPT_PROCESS:
1060                    if( Character.isWhitespace( (char)i ) ) {
1061                        String JavaDoc s = process.toString();
1062                        if( "IGNORE".equals( s ) ) ignore = true; // NOI18N
1063
else if( ! "INCLUDE".equals( s ) ) throw new WrongDTDException( "Unexpected processing instruction " + s ); // NOI18N
1064
state = OPT_APROCESS;
1065                    } else {
1066                        process.append( (char)i );
1067                    }
1068                    break;
1069                    
1070                case OPT_APROCESS:
1071                    if( Character.isWhitespace( (char)i ) ) break;
1072                    if( i == '[' ) state = OPT_CONTENT;
1073                    else throw new WrongDTDException( "Unexpected char '" + (char)i + "' in processing instruction." ); // NOI18N
1074
break;
1075                    
1076                case OPT_CONTENT:
1077                    if( i == ']' ) state = OPT_BRAC1;
1078                    else content.append( (char)i );
1079                    break;
1080                    
1081                case OPT_BRAC1:
1082                    if( i == ']' ) state = OPT_BRAC2;
1083                    else {
1084                        content.append( ']' ).append( (char)i );
1085                        state = OPT_CONTENT;
1086                    }
1087                    break;
1088                    
1089                case OPT_BRAC2:
1090                    if( Character.isWhitespace( (char)i ) ) break;
1091                    if( i == '>' ) {
1092                        if( !ignore ) in.unread( content.toString().toCharArray() );
1093                        return;
1094                    }
1095                    throw new WrongDTDException( "Unexpected char '" + (char)i + "' in processing instruction." ); // NOI18N
1096
}
1097        }
1098        
1099    }
1100    
1101    private static final int COMM_TEXT = 0; // anywhere in text
1102
private static final int COMM_DASH = 1; // after '-'
1103
/** Parser that eats everything until two consecutive dashes (inclusive) */
1104    private void parseComment( PushbackReader JavaDoc in ) throws IOException JavaDoc, WrongDTDException {
1105        int state = COMM_TEXT;
1106        for( ;; ) {
1107            int i = in.read();
1108            if( i == -1 ) break; // EOF
1109
switch( state ) {
1110                case COMM_TEXT:
1111                    if( i == '-' ) state = COMM_DASH;
1112                    break;
1113                case COMM_DASH:
1114                    if( i == '-' ) return; // finished eating comment
1115
state = COMM_TEXT;
1116                    break;
1117            }
1118        }
1119        throw new WrongDTDException( "Premature end of DTD" ); // NOI18N
1120
}
1121    
1122    /** Parser that reads the name of entity reference and replace it with
1123     * the content of that entity (using the pushback capability of input).
1124     * It gets the control just after starting '%'
1125     * @returns the name of reference which was replaced. */

1126    private String JavaDoc parseEntityReference( PushbackReader JavaDoc in ) throws IOException JavaDoc, WrongDTDException {
1127        StringBuffer JavaDoc sb = new StringBuffer JavaDoc();
1128        for( ;; ) {
1129            int i = in.read();
1130            if( i == -1 ) break; // EOF
1131
if( isNameChar( (char)i ) ) {
1132                sb.append( (char)i ); // next char of name
1133
} else {
1134                String JavaDoc entValue = (String JavaDoc)entityMap.get( sb.toString() ); //get the entity content
1135
if( entValue == null )
1136                    throw new WrongDTDException( "No such entity: \"" + sb + "\"" ); // NOI18N
1137

1138                if( i != ';' ) in.unread( i );
1139                in.unread( entValue.toCharArray() ); // push it back to stream
1140
return sb.toString();
1141            }
1142        }
1143        throw new WrongDTDException( "Premature end of DTD" ); // NOI18N
1144
}
1145    
1146    
1147    public static class WrongDTDException extends Exception JavaDoc {
1148        public WrongDTDException( String JavaDoc reason ) {
1149            super( reason );
1150        }
1151    }
1152    
1153/*----------------------------------------------------------------------------*/
1154/*---------- Implementation of classes this factory uses as results ----------*/
1155/*----------------------------------------------------------------------------*/
1156    
1157    /** Implementation of the DTD which this DTDcreator works as factory for. */
1158    private static class DTDImpl implements DTD {
1159        private String JavaDoc id;
1160        private SortedMap elements;
1161        private SortedMap charRefs;
1162        
1163        DTDImpl( String JavaDoc identifier, SortedMap elements, SortedMap charRefs ) {
1164            this.id = identifier;
1165            this.elements = elements;
1166            this.charRefs = charRefs;
1167        }
1168        
1169        /** Identify this instance of DTD */
1170        public String JavaDoc getIdentifier() {
1171            return id;
1172        }
1173        
1174        /** Get List of all Elements whose names starts with given prefix */
1175        public List getElementList( String JavaDoc prefix ) {
1176            List l = new ArrayList();
1177            prefix = prefix == null ? "" : prefix.toUpperCase();
1178            Iterator i = elements.tailMap( prefix ).entrySet().iterator();
1179            
1180            while( i.hasNext() ) {
1181                Map.Entry entry = (Map.Entry)i.next();
1182                if( ((String JavaDoc)entry.getKey()).startsWith( prefix ) ) {
1183                    l.add( entry.getValue() );
1184                } else { // we're getting data from SortedSet, so when any
1185
break; // entry fails, all remaining entry would fail.
1186
}
1187            }
1188            
1189            return l;
1190        }
1191        
1192        /** Get the Element of given name. */
1193        public DTD.Element getElement( String JavaDoc name ) {
1194            return (DTD.Element)elements.get( name );
1195        }
1196        
1197        /** Get List of all CharRefs whose aliases starts with given prefix. */
1198        public List getCharRefList( String JavaDoc prefix ) {
1199            List l = new ArrayList();
1200            Iterator i = charRefs.tailMap(prefix).entrySet().iterator();
1201            
1202            while( i.hasNext() ) {
1203                Map.Entry entry = (Map.Entry)i.next();
1204                if( ((String JavaDoc)entry.getKey()).startsWith( prefix ) ) {
1205                    l.add( entry.getValue() );
1206                } else { // we're getting data from SortedSet, so when any
1207
break; // entry fails, all remaining entry would fail.
1208
}
1209            }
1210            
1211            return l;
1212        }
1213        
1214       /** Get the CharRef of given name */
1215        public DTD.CharRef getCharRef( String JavaDoc name ) {
1216            return (DTD.CharRef)charRefs.get( name );
1217        }
1218        
1219        public String JavaDoc toString() {
1220            return super.toString() + "[id=" + id + ", elements=" + elements + ",charRefs=" + charRefs + "]"; // NOI18N
1221
}
1222    }
1223    
1224    /** Implementation of Element used by this DTDcreator. */
1225    private static class ElementImpl implements DTD.Element {
1226        
1227        private String JavaDoc name;
1228        private DTD.ContentModel model;
1229        private boolean optStart;
1230        private boolean optEnd;
1231        private SortedMap attributes; //these are sorted just by name
1232
private DTD dtd;
1233        
1234        
1235        ElementImpl( String JavaDoc name, DTD.ContentModel model, boolean optStart, boolean optEnd, SortedMap attributes ) {
1236            this.name = name;
1237            this.model = model;
1238            this.optStart = optStart;
1239            this.optEnd = optEnd;
1240            this.attributes = attributes;
1241        }
1242
1243        /** Get the name of this Element */
1244        public String JavaDoc getName() {
1245            return name;
1246        }
1247        
1248        /** Shorthand to resolving if content model of this Element is EMPTY */
1249        public boolean isEmpty() {
1250            if( optEnd && model.getContent() instanceof DTD.ContentLeaf ) return true;
1251            //&& ((DTD.ContentLeaf)model.getContent()).getName().equals( "EMPTY" ) ) return true;
1252
return false;
1253        }
1254        
1255        /** Tells if this Element has optional Start Tag. */
1256        public boolean hasOptionalStart() {
1257            return optStart;
1258        }
1259        
1260        /** Tells if this Element has optional End Tag. */
1261        public boolean hasOptionalEnd() {
1262            return optEnd;
1263        }
1264        
1265        /** Get the List of Attributes of this Element, which starts with
1266         * given <CODE>prefix</CODE>. */

1267        public List getAttributeList( String JavaDoc prefix ) {
1268            TreeSet set = new TreeSet(new Comparator() {
1269                public int compare( Object JavaDoc o1, Object JavaDoc o2 ) {
1270                    if( isRequired( o1 ) && ! isRequired( o2 ) ) return -1;
1271                    if( ! isRequired( o1 ) && isRequired( o2 ) ) return 1;
1272                    return ((DTD.Attribute)o1).getName().compareTo( ((DTD.Attribute)o2).getName() );
1273                }
1274                
1275                private final boolean isRequired( Object JavaDoc o ) {
1276                    return ((DTD.Attribute)o).getDefaultMode().equals( DTD.Attribute.MODE_REQUIRED );
1277                }
1278            });
1279            prefix = prefix.toLowerCase();
1280            Iterator i = attributes.tailMap(prefix).entrySet().iterator();
1281            
1282            while( i.hasNext() ) {
1283                Map.Entry entry = (Map.Entry)i.next();
1284                if( ((String JavaDoc)entry.getKey()).startsWith( prefix ) ) {
1285                    set.add( entry.getValue() );
1286                } else { // we're getting data from SortedSet, so when any
1287
break; // entry fails, all remaining entry would fail.
1288
}
1289            }
1290            return new ArrayList( set );
1291        }
1292        
1293        /** Get the Attribute of given name. */
1294        public DTD.Attribute getAttribute( String JavaDoc name ) {
1295            return (DTD.Attribute)attributes.get( name );
1296        }
1297        
1298        void addAttribute( DTD.Attribute attr ) {
1299            attributes.put( attr.getName(), attr );
1300        }
1301        
1302        /** Get the content model of this Element */
1303        public DTD.ContentModel getContentModel() {
1304            return model;
1305        }
1306        
1307        public String JavaDoc toString() {
1308            return super.toString() + "[" + name + (optStart ? " O" : " -") + (optEnd ? " O " : " - ") + model + " attribs=" + attributes + "]"; // NOI18 // NOI18N
1309
}
1310    }
1311    
1312    /** */
1313    public static class AttributeImpl implements DTD.Attribute {
1314        
1315        private String JavaDoc name;
1316        private int type;
1317        private String JavaDoc baseType;
1318        private String JavaDoc typeHelper;
1319        private String JavaDoc defaultMode;
1320        private SortedMap values;
1321        private int hashcode;
1322        
1323        public AttributeImpl( String JavaDoc name, int type, String JavaDoc baseType, String JavaDoc typeHelper, String JavaDoc defaultMode, SortedMap values ) {
1324            this.name = name;
1325            this.type = type;
1326            this.baseType = baseType;
1327            this.typeHelper = typeHelper;
1328            this.defaultMode = defaultMode;
1329            this.values = values;
1330            hashcode = name.hashCode() * (type + 1) *
1331                (baseType == null ? 1 : baseType.hashCode()) +
1332                (typeHelper == null ? 1 : typeHelper.hashCode()) +
1333                defaultMode.hashCode() +
1334                (values == null ? 1 : values.hashCode() );
1335        }
1336        
1337        /** @return name of this attribute */
1338        public String JavaDoc getName() {
1339            return name;
1340        }
1341        
1342        /** @return type of this attribute */
1343        public int getType() {
1344            return type;
1345        }
1346
1347        public String JavaDoc getBaseType() {
1348            return baseType;
1349        }
1350        
1351        /** The last entity name through which was this Attribute's type defined. */
1352        public String JavaDoc getTypeHelper() {
1353            return typeHelper;
1354        }
1355        
1356        /** This method is used to obtain default value information. */
1357        public String JavaDoc getDefaultMode() {
1358            return defaultMode;
1359        }
1360        
1361        /** Shorthand for determining if defaultMode is "#REQUIRED" */
1362        public boolean isRequired() {
1363            return defaultMode.equals( MODE_REQUIRED );
1364        }
1365        
1366        /** The way how to obtain possible values for TYPE_SET Attributes
1367         * @param prefix required prefix, or <CODE>null</CODE>, if all
1368         * possible values are required.
1369         * @return List of Values starting with prefix, from this attribute
1370         * if it is of TYPE_SET. For other types, it doesn't make a sense
1371         * and returns null.
1372         */

1373        public List getValueList( String JavaDoc prefix ) {
1374            if( type != TYPE_SET ) return null;
1375
1376            if( prefix == null ) prefix = ""; else prefix = prefix.toLowerCase();
1377
1378            List retVal = new ArrayList();
1379            Iterator i = values.tailMap(prefix).entrySet().iterator();
1380            
1381            while( i.hasNext() ) {
1382                Map.Entry entry = (Map.Entry)i.next();
1383                if( ((String JavaDoc)entry.getKey()).startsWith( prefix ) ) {
1384                    retVal.add( entry.getValue() );
1385                } else { // we're getting data from SortedSet, so when any
1386
break; // entry fails, all remaining entry would fail.
1387
}
1388            }
1389            return retVal;
1390        }
1391        
1392        /** Get the value of given name. */
1393        public DTD.Value getValue( String JavaDoc name ) {
1394            return (DTD.Value)values.get( name );
1395        }
1396        
1397        public String JavaDoc toString() {
1398            if( type == TYPE_SET ) {
1399                return name + " " + values + "[" + typeHelper + "] " + defaultMode; // NOI18 // NOI18N
1400
} else if( type == TYPE_BOOLEAN ) {
1401                return name + " (" + name + ")[" + typeHelper + "] " + defaultMode; // NOI18 // NOI18N
1402
} else {
1403                return name + " " + baseType + "[" + typeHelper + "] " + defaultMode; // NOI18 // NOI18N
1404
}
1405        }
1406        
1407        public int hashCode() {
1408            return hashcode;
1409        }
1410        
1411        public boolean equals( Object JavaDoc obj ) {
1412            if( !(obj instanceof AttributeImpl) ) return false;
1413            AttributeImpl a = (AttributeImpl)obj;
1414            return(
1415                hashcode == a.hashcode &&
1416                name.equals( a.name ) &&
1417                type == a.type &&
1418                (baseType == a.baseType || baseType != null && baseType.equals( a.baseType ) ) &&
1419                (typeHelper == a.typeHelper || typeHelper != null && typeHelper.equals( a.typeHelper ) ) &&
1420                defaultMode.equals( a.defaultMode ) &&
1421                (values == a.values || values != null && values.equals( a.values ) )
1422            );
1423        }
1424    }
1425    
1426    
1427    private static class ValueImpl implements DTD.Value {
1428        String JavaDoc name;
1429        
1430        ValueImpl( String JavaDoc name ) {
1431            this.name = name;
1432        }
1433        
1434        public String JavaDoc getName() {
1435            return name;
1436        }
1437        
1438        public boolean equals( Object JavaDoc obj ) {
1439            if( ! (obj instanceof ValueImpl) ) return false;
1440            return name.equals( ((ValueImpl)obj).name );
1441        }
1442        
1443        public int hashCode() {
1444            return name.hashCode();
1445        }
1446        
1447        public String JavaDoc toString() {
1448            return name;
1449        }
1450    }
1451    
1452    private static class CharRefImpl implements DTD.CharRef {
1453        private String JavaDoc name;
1454        private char value;
1455        
1456        CharRefImpl( String JavaDoc name, char value ) {
1457            this.name = name;
1458            this.value = value;
1459        }
1460        
1461        /** @return alias to this CharRef */
1462        public String JavaDoc getName() {
1463            return name;
1464        }
1465        
1466        /** @return the character this alias is for */
1467        public char getValue() {
1468            return value;
1469        }
1470        
1471        public String JavaDoc toString() {
1472            return name + "->'" + value + "'(&#" + (int)value +";)"; // NOI18N
1473
}
1474        
1475        public boolean equals( Object JavaDoc obj ) {
1476            if( ! (obj instanceof CharRefImpl) ) return false;
1477            return name.equals( ((CharRefImpl)obj).name ) &&
1478            value == ((CharRefImpl)obj).value;
1479        }
1480        
1481        public int hashCode() {
1482            return name.hashCode() * value;
1483        }
1484    }
1485    
1486    /** The implementation of ContentModel. It is immutable */
1487    private static class ContentModelImpl implements DTD.ContentModel {
1488        int hashcode;
1489        DTD.Content content;
1490        Set included;
1491        Set excluded;
1492        
1493        public ContentModelImpl( DTD.Content content, Set included, Set excluded ) {
1494            this.content = content;
1495            this.included = included;
1496            this.excluded = excluded;
1497            hashcode = content.hashCode() + 2*included.hashCode() + 3*excluded.hashCode();
1498        }
1499        
1500        /** @return the Content tree part of this model */
1501        public DTD.Content getContent() {
1502            return content;
1503        }
1504        
1505        /** @return Set of Element names which are recursively included. */
1506        public Set getIncludes() {
1507            return included;
1508        }
1509        
1510        /** @return Set of Element names which are recursively excluded. */
1511        public Set getExcludes() {
1512            return excluded;
1513        }
1514        
1515        public String JavaDoc toString() {
1516            StringBuffer JavaDoc sb = new StringBuffer JavaDoc( content.toString() );
1517            
1518            if( ! included.isEmpty() ) {
1519                sb.append( " +(" ); // NOI18N
1520
Iterator i = included.iterator();
1521                for( ;; ) {
1522                    sb.append( ((DTD.Element)i.next()).getName() );
1523                    if( i.hasNext() ) sb.append( "|" ); // NOI18N
1524
else break;
1525                }
1526                sb.append( ")" ); // NOI18N
1527
}
1528            
1529            if( ! excluded.isEmpty() ) {
1530                sb.append( " -(" ); // NOI18N
1531
Iterator i = excluded.iterator();
1532                for( ;; ) {
1533                    sb.append( ((DTD.Element)i.next()).getName() );
1534                    if( i.hasNext() ) sb.append( "|" ); // NOI18N
1535
else break;
1536                }
1537                sb.append( ")" ); // NOI18N
1538
}
1539            
1540            return sb.toString();
1541        }
1542        
1543        public boolean equals( Object JavaDoc obj ) {
1544            if( ! (obj instanceof ContentModelImpl) ) return false;
1545            ContentModelImpl cmi = (ContentModelImpl)obj;
1546            return content.equals( cmi.content ) &&
1547            included.equals( cmi.included ) &&
1548            excluded.equals( cmi.excluded );
1549        }
1550        
1551        public int hashCode() {
1552            return hashcode;
1553        }
1554        
1555    }
1556    
1557    /** ContentLeaf is leaf of content tree, matches just one Element name (String)*/
1558    static class ContentLeafImpl implements DTD.ContentLeaf {
1559        String JavaDoc elemName;
1560        DTD.Element elem;
1561        
1562        public ContentLeafImpl( String JavaDoc name ) {
1563            this.elemName = name;
1564        }
1565        
1566        
1567        /** get the name of leaf Element */
1568        public String JavaDoc getName() {
1569            return elemName;
1570        }
1571        
1572        public DTD.Element getElement() {
1573            return elem;
1574        }
1575        
1576        public boolean equals( Object JavaDoc obj ) {
1577            if( ! (obj instanceof ContentLeafImpl) ) return false;
1578            return elemName.equals( ((ContentLeafImpl)obj).elemName );
1579        }
1580        
1581        public int hashCode() {
1582            return elemName.hashCode();
1583        }
1584        
1585        public String JavaDoc toString() {
1586            return elemName;
1587        }
1588        
1589        /** ContentLeaf can't be discarded as it hac no operation associated */
1590        public boolean isDiscardable() {
1591            return false;
1592        }
1593        
1594        /** ContentLeaf is either reduced to EMPTY_CONTENT or doesn't
1595         * match at all */

1596        public DTD.Content reduce(String JavaDoc elementName) {
1597            if( elemName.equals( elementName ) ) return EMPTY_CONTENT;
1598            return null;
1599        }
1600
1601        public Set getPossibleElements() {
1602            Set s = new HashSet();
1603            s.add( elem );
1604            return s;
1605        }
1606    }
1607    
1608    /** ContentNode is node of content tree */
1609    private static class UnaryContentNodeImpl implements DTD.ContentNode {
1610        int hashcode;
1611        char type;
1612        DTD.Content content;
1613        
1614        /* Constructor for unary ContentNodes */
1615        public UnaryContentNodeImpl( char type, DTD.Content content ) {
1616            // sanity check:
1617
if( type != '*' && type != '?' && type != '+' ) {
1618                throw new IllegalArgumentException JavaDoc( "Unknown unary content type '" + type + "'" ); // NOI18N
1619
}
1620            
1621            this.type = type;
1622            this.content = content;
1623            hashcode = type + content.hashCode();
1624        }
1625        
1626        /** This is node, always return false */
1627        public boolean isLeaf() { return false; }
1628        
1629        /** Get the operator for this node */
1630        public char getType() { return type; }
1631        
1632        /** Get the content of this node */
1633        public DTD.Content[] getContent() {
1634            return new DTD.Content[] { content };
1635        }
1636        
1637        public boolean equals( Object JavaDoc obj ) {
1638            if( !(obj instanceof UnaryContentNodeImpl) ) return false;
1639            return type == ((UnaryContentNodeImpl)obj).type &&
1640                content.equals( ((UnaryContentNodeImpl)obj).content );
1641        }
1642        
1643        public int hashCode() { return hashcode; }
1644
1645        public String JavaDoc toString() {
1646            return content.toString() + type;
1647        }
1648                
1649        public boolean isDiscardable() {
1650            if( type == '*' || type == '?' ) return true;
1651            // The only remaining type, don't check: if( type == '+' )
1652
return content.isDiscardable();
1653        }
1654
1655        public DTD.Content reduce(String JavaDoc elementName) {
1656            DTD.Content sub = content.reduce( elementName );
1657            if( sub == null ) return null;
1658            if( sub == EMPTY_CONTENT ) {
1659                if( type == '?' ) return EMPTY_CONTENT;
1660                if( type == '*' ) return this;
1661                // '+' is the last one: if( type == '+' )
1662
// we fullfilled the '+' rule, remainder is '*'
1663
return new UnaryContentNodeImpl( '*', content );
1664            }
1665            if( type == '?' ) return sub;
1666            DTD.Content second = (type == '*') ? this : new UnaryContentNodeImpl( '*', content );
1667            return new MultiContentNodeImpl( ',', new DTD.Content[] { sub, second } );
1668        }
1669
1670        public Set getPossibleElements() {
1671            return content.getPossibleElements();
1672        }
1673        
1674    }
1675
1676    
1677    /** ContentNodeImpl is n-ary node of content tree */
1678    private static class MultiContentNodeImpl implements DTD.ContentNode {
1679        int hashcode;
1680        char type;
1681        DTD.Content[] content;
1682        
1683        /* Constructor for n-ary ContentNodes */
1684        public MultiContentNodeImpl( char type, DTD.Content[] content ) {
1685            // sanity check:
1686
if( type != '|' && type != '&' && type != ',' ) {
1687                throw new IllegalArgumentException JavaDoc( "Unknown n-ary content type '" + type + "'" ); // NOI18N
1688
}
1689
1690            this.type = type;
1691            this.content = content;
1692            hashcode = type;
1693            for( int i=0; i<content.length; i++ ) {
1694                hashcode += content[i].hashCode();
1695            }
1696        }
1697        
1698        /** This is node, always return false */
1699        public boolean isLeaf() { return false; }
1700        
1701        /** Get the operator for this node */
1702        public char getType() { return type; }
1703        
1704        /** Get the content of this node */
1705        public DTD.Content[] getContent() { return content; }
1706        
1707        public boolean equals( Object JavaDoc obj ) {
1708            if( ! (obj instanceof MultiContentNodeImpl) ) return false;
1709            return type == ((MultiContentNodeImpl)obj).type &&
1710            Arrays.equals( content, ((MultiContentNodeImpl)obj).content );
1711        }
1712        
1713        public String JavaDoc toString() {
1714            StringBuffer JavaDoc sb = new StringBuffer JavaDoc( "(" ); // NOI18N
1715
for( int i=0; i<content.length; i++ ) {
1716                sb.append( content[i].toString() );
1717                if( i+1 < content.length ) sb.append( type );
1718            }
1719            sb.append( ')' );
1720            return sb.toString();
1721        }
1722
1723        public int hashCode() { return hashcode; }
1724                
1725        public boolean isDiscardable() {
1726            if( type == '&' || type == ',' ) {
1727                for( int i = 0; i < content.length; i++ ) {
1728                    if( ! content[i].isDiscardable() ) return false;
1729                }
1730                return true;
1731            }
1732            // The only remaining type, don't check: if( type == '|' )
1733
for( int i = 0; i < content.length; i++ ) {
1734                if( content[i].isDiscardable() ) return true;
1735            }
1736            return false;
1737        }
1738
1739        public DTD.Content reduce(String JavaDoc elementName) {
1740            
1741            if( type == '|' ) {
1742                for( int index = 0; index < content.length; index++ ) {
1743                    DTD.Content sub = content[index].reduce( elementName );
1744                    if( sub != null ) return sub;
1745                }
1746                return null;
1747            } else if( type == ',' ) {
1748                 // everything before index doesn't match and is discardable
1749
int index = 0;
1750                
1751                while( index < content.length) {
1752                    DTD.Content sub = content[index].reduce( elementName );
1753                    // element of sequence still don't match and isn't discardable:
1754
if( sub == null && !content[index].isDiscardable() ) return null;
1755                    
1756                    // Element matches fully:
1757
if( sub == EMPTY_CONTENT ) {
1758                        int newLen = content.length - index - 1;
1759                        if( newLen > 1 ) { // resulting sequence contains 2+ elements
1760
DTD.Content[] newSub = new DTD.Content[newLen];
1761                            System.arraycopy( content, index + 1, newSub, 0, newLen );
1762                            return new MultiContentNodeImpl( ',', newSub );
1763                        } else { // resulting sequence is one-item only
1764
return content[index+1];
1765                        }
1766                    }
1767                    
1768                    // Element matches and is modified
1769
if( sub != null ) {
1770                        int newLen = content.length - index;
1771                        if( newLen > 1 ) { // resulting sequence contains 2+ elements
1772
DTD.Content[] newSub = new DTD.Content[newLen];
1773                            System.arraycopy( content, index + 1, newSub, 1, newLen-1 );
1774                            newSub[0] = sub;
1775                            return new MultiContentNodeImpl( ',', newSub );
1776                        } else { // resulting sequence is one modified item only
1777
return sub;
1778                        }
1779                    }
1780                    index++; //discard the first element and try again
1781
}
1782                
1783                return null; // Doesn't match at all
1784
} else { // only '&' remains: if( type == '&' ) {
1785
for( int index = 0; index < content.length; index++ ) {
1786                    DTD.Content sub = content[index].reduce( elementName );
1787                    if( sub == EMPTY_CONTENT ) {
1788                        int newLen = content.length - 1;
1789                        if( newLen > 1 ) {
1790                            DTD.Content[] newSub = new DTD.Content[ newLen ];
1791                            System.arraycopy( content, 0, newSub, 0, index );
1792                            if( index < newSub.length ) {
1793                                System.arraycopy( content, index + 1, newSub, index, newLen - index );
1794                            }
1795                            return new MultiContentNodeImpl( '&', newSub );
1796                        } else {
1797                            return content[ 1 - index];
1798                        }
1799                    }
1800                    if( sub != null ) {
1801                        DTD.Content right;
1802                        if( content.length > 1 ) {
1803                            int newLen = content.length - 1;
1804                            DTD.Content[] newSub = new DTD.Content[ newLen ];
1805                            System.arraycopy( content, 0, newSub, 0, index );
1806                            if( index < newSub.length ) {
1807                                System.arraycopy( content, index + 1, newSub, index, newLen - index );
1808                            }
1809                            right = new MultiContentNodeImpl( '&', newSub );
1810                        } else {
1811                            right = content[ 1 - index ];
1812                        }
1813                        return new MultiContentNodeImpl( ',', new DTD.Content[] { sub, right } );
1814                    }
1815                }
1816                return null;
1817                
1818            }
1819        }
1820
1821        public Set getPossibleElements() {
1822            Set retVal = new HashSet( 11 );
1823            
1824            if( type == '|' || type == '&' ) {
1825                for( int index = 0; index < content.length; index++ )
1826                    retVal.addAll( content[index].getPossibleElements() );
1827
1828            } else { // only ',' remains if( type == ',' ) {}
1829
int index = 0;
1830                while( index < content.length) {
1831                    retVal.addAll( content[index].getPossibleElements() );
1832                    if( !content[index].isDiscardable() ) break;
1833                    index++;
1834                }
1835            }
1836            return retVal;
1837        }
1838    }
1839
1840
1841}
1842
Popular Tags