PageRenderTime 51ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 1ms

/projects/netbeans-7.3/html.editor.lib/src/org/netbeans/modules/html/editor/lib/dtd/DTDParser.java

https://gitlab.com/essere.lab.public/qualitas.class-corpus
Java | 1183 lines | 959 code | 121 blank | 103 comment | 200 complexity | 6f832cb01c641e912652bb8969fd7324 MD5 | raw file
  1. /*
  2. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
  3. *
  4. * Copyright 1997-2010 Oracle and/or its affiliates. All rights reserved.
  5. *
  6. * Oracle and Java are registered trademarks of Oracle and/or its affiliates.
  7. * Other names may be trademarks of their respective owners.
  8. *
  9. * The contents of this file are subject to the terms of either the GNU
  10. * General Public License Version 2 only ("GPL") or the Common
  11. * Development and Distribution License("CDDL") (collectively, the
  12. * "License"). You may not use this file except in compliance with the
  13. * License. You can obtain a copy of the License at
  14. * http://www.netbeans.org/cddl-gplv2.html
  15. * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
  16. * specific language governing permissions and limitations under the
  17. * License. When distributing the software, include this License Header
  18. * Notice in each file and include the License file at
  19. * nbbuild/licenses/CDDL-GPL-2-CP. Oracle designates this
  20. * particular file as subject to the "Classpath" exception as provided
  21. * by Oracle in the GPL Version 2 section of the License file that
  22. * accompanied this code. If applicable, add the following below the
  23. * License Header, with the fields enclosed by brackets [] replaced by
  24. * your own identifying information:
  25. * "Portions Copyrighted [year] [name of copyright owner]"
  26. *
  27. * Contributor(s):
  28. *
  29. * The Original Software is NetBeans. The Initial Developer of the Original
  30. * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
  31. * Microsystems, Inc. All Rights Reserved.
  32. *
  33. * If you wish your version of this file to be governed by only the CDDL
  34. * or only the GPL Version 2, indicate your decision by adding
  35. * "[Contributor] elects to include this software in this distribution
  36. * under the [CDDL or GPL Version 2] license." If you do not indicate a
  37. * single choice of license, a recipient has the option to distribute
  38. * your version of this file under either the CDDL, the GPL Version 2 or
  39. * to extend the choice of license to its licensees as provided above.
  40. * However, if you add GPL Version 2 code and therefore, elected the GPL
  41. * Version 2 license, then the option applies only if the new code is
  42. * made subject to such option by the copyright holder.
  43. */
  44. package org.netbeans.modules.html.editor.lib.dtd;
  45. import org.netbeans.modules.html.editor.lib.api.dtd.ReaderProvider;
  46. import java.io.Reader;
  47. import java.io.PushbackReader;
  48. import java.io.IOException;
  49. import java.lang.ref.WeakReference;
  50. import java.util.*;
  51. /**
  52. * !!! Includes !!!! String->DTD.Element
  53. * @author Petr Nejedly
  54. * @version 0.2
  55. */
  56. class DTDParser extends Object {
  57. // The provider used to provide the Readers for this DTD.
  58. private ReaderProvider provider = null;
  59. // Asks for Reader for given DTD.
  60. private Reader getReader( String identifier, String fileName ) {
  61. if( provider == null ) return null;
  62. return provider.getReaderForIdentifier( identifier, fileName );
  63. }
  64. /** Weak set for holding already created strings, to not create more
  65. * instances of the same string */
  66. private WeakHashSet stringCache = new WeakHashSet( 131, 0.75f );
  67. /** Weak set of attributes - helps sharing common attributes */
  68. private WeakHashSet attributes = new WeakHashSet( 23, 0.75f );
  69. /** Weak set of models */
  70. private WeakHashSet models = new WeakHashSet( 131, 0.75f );
  71. /** Weak set of Contents */
  72. private WeakHashSet contents = new WeakHashSet( 131, 0.75f );
  73. /** Temporal storage of all ContentLeafs that needs to get their
  74. * elements filled in at the end of parsing
  75. */
  76. Set leafs = new HashSet( 131, 0.75f );
  77. /** Map of all character references.
  78. * Mapping is String name -> DTD.CharRef instance */
  79. private SortedMap charRefs = new TreeMap();
  80. /** Map holding partially completed instances of Element.
  81. * Mapping is String name -> DTD.Element instance */
  82. private SortedMap elementMap = new TreeMap();
  83. /** Map holding entities during creation of DTD.
  84. * Mapping is String name -> String content.
  85. * This map should not be used for direct put(..), because entities
  86. * are defined by first declaration and can not be overriden.
  87. */
  88. private Map entityMap = new HashMap();
  89. public boolean xmlDTD; //if true we parse XML DTD, not SGML!
  90. public DTD createDTD( ReaderProvider provider, String identifier, String fileName ) throws WrongDTDException {
  91. this.provider = provider;
  92. xmlDTD = provider.isXMLContent(identifier);
  93. Reader reader = getReader( identifier, fileName );
  94. if( reader == null ) throw new WrongDTDException( "Can't open Reader for public identifier " + identifier ); // NOI18N
  95. try {
  96. parseDTD( new PushbackReader( reader, 1024*128 ) );
  97. } catch( IOException e ) {
  98. throw new WrongDTDException( "IOException during parsing: " + e.getMessage() ); // NOI18N
  99. }
  100. // fixup includes and excludes of all elements
  101. for( Iterator it = elementMap.values().iterator(); it.hasNext (); ) {
  102. DTD.Element elem = (DTD.Element)it.next();
  103. ContentModelImpl cm = (ContentModelImpl)elem.getContentModel();
  104. Set newIncs = new HashSet();
  105. for( Iterator incIter = cm.included.iterator(); incIter.hasNext (); ) {
  106. Object oldElem;
  107. Object subElem = oldElem = incIter.next();
  108. if( subElem instanceof String ) {
  109. String key = (String)subElem;
  110. subElem = elementMap.get( xmlDTD ? key : key.toUpperCase(Locale.ENGLISH) );
  111. }
  112. if( subElem == null ) {
  113. throw new WrongDTDException( "'" + oldElem + "' element referenced from " + elem.getName() + " not found throughout the DTD." ); // NOI18N
  114. }
  115. newIncs.add( subElem );
  116. }
  117. cm.included = newIncs;
  118. Set newExcs = new HashSet();
  119. for( Iterator excIter = cm.excluded.iterator(); excIter.hasNext (); ) {
  120. Object oldElem;
  121. Object subElem = oldElem = excIter.next();
  122. if( subElem instanceof String ) {
  123. String key = (String)subElem;
  124. subElem = elementMap.get( xmlDTD ? key : key.toUpperCase(Locale.ENGLISH) );
  125. }
  126. if( subElem == null ) {
  127. throw new WrongDTDException( "'" + oldElem + "' element referenced from " + elem.getName() + " not found throughout the DTD." ); // NOI18N
  128. }
  129. newExcs.add( subElem );
  130. }
  131. cm.excluded = newExcs;
  132. cm.hashcode = cm.content.hashCode() + 2*cm.included.hashCode() + 3*cm.excluded.hashCode();
  133. }
  134. // fixup content leafs
  135. for( Iterator it = leafs.iterator(); it.hasNext (); ) {
  136. ContentLeafImpl leaf = (ContentLeafImpl)it.next();
  137. leaf.elem = (DTD.Element)elementMap.get( leaf.elemName );
  138. }
  139. return new DTDImpl( identifier, elementMap, charRefs, xmlDTD );
  140. }
  141. /** Method for adding new entities to their map. Obeys the rule that
  142. * entity, once defined, can not be overriden */
  143. void addEntity( String name, String content ) {
  144. if( entityMap.get( name ) == null ) entityMap.put( name, content );
  145. }
  146. /** Method for adding new entities to their map. Obeys the rule that
  147. * entity, once defined, can not be overriden */
  148. void addPublicEntity( String name, String identifier, String file ) throws WrongDTDException {
  149. if( entityMap.get( name ) == null ) {
  150. StringBuffer sb = new StringBuffer();
  151. char[] buffer = new char[16384];
  152. Reader r = getReader( identifier, file );
  153. try {
  154. int len;
  155. while( (len = r.read( buffer )) >= 0 ) {
  156. sb.append( buffer, 0, len );
  157. }
  158. } catch( IOException e ) {
  159. throw new WrongDTDException( "Error reading included public entity " + name + " - " + e.getMessage() ); // NOI18N
  160. }
  161. entityMap.put( name, sb.toString() );
  162. }
  163. }
  164. DTD.Value createValue( String name ) {
  165. return new ValueImpl( (String)stringCache.put( name ) );
  166. }
  167. /** Creates new or lookups old ContentModel with given properites */
  168. DTD.ContentModel createContentModel( DTD.Content content, Set included, Set excluded ) {
  169. DTD.ContentModel cm = new ContentModelImpl( content, included, excluded );
  170. return (DTD.ContentModel)models.put( cm );
  171. }
  172. /** Creates new or lookups old ContentLeaf with given properites */
  173. DTD.Content createContentLeaf( String name ) {
  174. DTD.Content c = new ContentLeafImpl( name );
  175. c = (DTD.Content)contents.put( c );
  176. leafs.add( c ); // remember for final fixup
  177. return c;
  178. }
  179. /** Creates new or lookups old ContentNode with given properites */
  180. DTD.Content createContentNode( char type, DTD.Content subContent ) {
  181. return (DTD.Content)contents.put( new UnaryContentNodeImpl( type, subContent ) );
  182. }
  183. /** Creates new or lookups old ContentNode with given properites */
  184. DTD.Content createContentNode( char type, DTD.Content[] subContent ) {
  185. return (DTD.Content)contents.put( new MultiContentNodeImpl( type, subContent ) );
  186. }
  187. DTD.Element createElement( String name, DTD.ContentModel cm, boolean optStart, boolean optEnd, boolean xmlDTD) {
  188. DTD.Element retVal = new ElementImpl( name, cm, optStart, optEnd, new TreeMap(), xmlDTD );
  189. return retVal;
  190. }
  191. /** Creates new or lookups old attribute with given properites */
  192. DTD.Attribute createAttribute( String name, int type, String baseType, String typeHelper, String defaultMode, SortedMap values, boolean xmlDTD ) {
  193. DTD.Attribute attr = new AttributeImpl( name, type,
  194. (String)stringCache.put( baseType ),
  195. (String)stringCache.put( typeHelper ),
  196. (String)stringCache.put( defaultMode ),
  197. values, xmlDTD
  198. );
  199. return (DTD.Attribute)attributes.put( attr );
  200. }
  201. /** Adds given instance of DTD.Attribute to Element named elemName */
  202. void addAttrToElement( String elemName, DTD.Attribute attr) throws WrongDTDException {
  203. String key = xmlDTD ? elemName : elemName.toUpperCase(Locale.ENGLISH);
  204. ElementImpl elem = (ElementImpl)elementMap.get( key );
  205. if( elem == null ) throw new WrongDTDException( "Attribute definition for unknown Element \"" + elemName +"\"." ); // NOI18N
  206. elem.addAttribute( attr );
  207. }
  208. void createAddCharRef( String name, char value ) {
  209. DTD.CharRef ref = new CharRefImpl( name, value );
  210. charRefs.put( name, ref );
  211. }
  212. private boolean isNameChar( char c ) {
  213. return Character.isLetterOrDigit( c ) || c == '_' || c == '-' || c == '.' || c == ':';
  214. }
  215. /*----------------------------------------------------------------------------*/
  216. /*----------------------------- Parsing routines ---------------------------- */
  217. /*----------------------------------------------------------------------------*/
  218. private static final int DTD_INIT = 0;
  219. private static final int DTD_LT = 1; // after '<'
  220. private static final int DTD_EXC = 2; // after "<!"
  221. private static final int DTD_MINUS = 3; // after "<!-"
  222. private static final int DTD_ACOMMENT = 4; // after comment was parsed, awaiting '>'
  223. private void parseDTD( PushbackReader in ) throws IOException, WrongDTDException {
  224. int state = DTD_INIT;
  225. for( ;; ) {
  226. int i = in.read();
  227. if( i == -1 ) {
  228. break;
  229. }
  230. switch( state ) {
  231. case DTD_INIT:
  232. switch( i ) {
  233. case '<':
  234. state = DTD_LT;
  235. break;
  236. case '%':
  237. parseEntityReference( in );
  238. break; // Stay in DTD_INIT
  239. }
  240. break;
  241. case DTD_LT:
  242. if( i != '!' ) throw new WrongDTDException( "Unexpected char '" + (char)i + "' after '<'" ); // NOI18N
  243. state = DTD_EXC;
  244. break;
  245. case DTD_EXC:
  246. switch( i ) {
  247. case '-':
  248. state = DTD_MINUS;
  249. break;
  250. case '[':
  251. parseOptional( in );
  252. state = DTD_INIT;
  253. break;
  254. default:
  255. in.unread( i );
  256. parseMarkup( in );
  257. state = DTD_INIT;
  258. break;
  259. }
  260. break;
  261. case DTD_MINUS:
  262. if( i != '-' ) throw new WrongDTDException( "Unexpected char '" + (char)i + "' after \"<!-\"" ); // NOI18N
  263. parseComment( in );
  264. state = DTD_ACOMMENT;
  265. break;
  266. case DTD_ACOMMENT:
  267. if( i != '>' ) throw new WrongDTDException( "Unexpected char '" + (char)i + "' after comment" ); // NOI18N
  268. state = DTD_INIT;
  269. break;
  270. }
  271. }
  272. if( state != DTD_INIT ) throw new WrongDTDException( "Premature end of DTD" ); // NOI18N
  273. }
  274. /** Parser that reads the markup type after <!. Recognizes ENTITY, ELEMENT
  275. * and ATTLIST markup and forwards their processing to proper parser.
  276. * It gets the control just after starting "<!" and releases after eating
  277. * final '>' */
  278. private void parseMarkup( PushbackReader in ) throws IOException, WrongDTDException {
  279. StringBuffer sb = new StringBuffer();
  280. for( ;; ) {
  281. int i = in.read();
  282. if( i == -1 ) throw new WrongDTDException( "Premature end of DTD" ); // NOI18N EOF
  283. if( i == ' ' ) break;
  284. sb.append( (char)i ); // next char of name
  285. }
  286. String markup = sb.toString();
  287. if( "ENTITY".equals( markup ) ) { // NOI18N
  288. parseEntityDefinition( in );
  289. } else if( "ELEMENT".equals( markup ) ) { // NOI18N
  290. parseElement( in );
  291. } else if( "ATTLIST".equals( markup ) ) { // NOI18N
  292. parseAttlist( in );
  293. } else throw new WrongDTDException( "Wrong DTD markup <!" + markup ); // NOI18N
  294. }
  295. private static final int PED_INIT = 0;
  296. private static final int PED_PERCENT = 1;
  297. private static final int PED_CHAR = 2;
  298. private static final int PED_NAME = 3;
  299. private static final int PED_ANAME = 4;
  300. private static final int PED_VAL = 5;
  301. private static final int PED_TYPE = 6;
  302. private static final int PED_AVAL = 7;
  303. private static final int PED_AVAL_M = 8;
  304. private static final int PED_ATYPE = 9;
  305. private static final int PED_ID = 10;
  306. private static final int PED_AID = 11;
  307. private static final int PED_FILE = 12;
  308. private static final int PED_AFILE = 13;
  309. private static final int PED_AFILE_M = 14;
  310. private static final int PED_ACHAR = 15;
  311. private static final int PED_CH_TYPE = 16;
  312. private static final int PED_CH_ATYPE = 17;
  313. private static final int PED_CH_QUOT = 18;
  314. /* TODO: Parsing fo character references */
  315. private void parseEntityDefinition( PushbackReader in ) throws IOException, WrongDTDException {
  316. int state = PED_INIT;
  317. StringBuffer name = new StringBuffer();
  318. StringBuffer value = new StringBuffer();
  319. StringBuffer type = new StringBuffer();
  320. StringBuffer identifier = new StringBuffer();
  321. for( ;; ) {
  322. int i = in.read();
  323. if( i == -1 ) throw new WrongDTDException( "Premature end of DTD" ); // NOI18N EOF
  324. switch( state ) {
  325. case PED_INIT:
  326. if( Character.isWhitespace( (char)i ) ) break;
  327. if( i == '%' ) state = PED_PERCENT;
  328. else {
  329. name.append( (char)i );
  330. state = PED_CHAR;
  331. }
  332. break;
  333. case PED_PERCENT:
  334. if( Character.isWhitespace( (char)i ) ) break;
  335. name.append( (char)i );
  336. state = PED_NAME;
  337. break;
  338. case PED_NAME:
  339. if( Character.isWhitespace( (char)i ) ) {
  340. state = PED_ANAME;
  341. } else {
  342. name.append( (char)i );
  343. }
  344. break;
  345. case PED_ANAME:
  346. if( Character.isWhitespace( (char)i ) ) break;
  347. if( i == '"' ) state = PED_VAL;
  348. else {
  349. in.unread( i );
  350. state = PED_TYPE;
  351. }
  352. break;
  353. case PED_VAL:
  354. if( i == '"' ) {
  355. addEntity( name.toString(), value.toString() );
  356. state = PED_AVAL;
  357. } else {
  358. value.append( (char)i );
  359. }
  360. break;
  361. case PED_AVAL:
  362. if( i == '>' ) {
  363. return;
  364. }
  365. if( i == '-' ) state = PED_AVAL_M;
  366. break;
  367. case PED_AVAL_M:
  368. if( i == '-' ) parseComment( in );
  369. state = PED_AVAL;
  370. break;
  371. case PED_TYPE:
  372. if( Character.isWhitespace( (char)i ) ) {
  373. if( type.toString().equals( "PUBLIC" ) ) { // NOI18N
  374. state = PED_ATYPE;
  375. } else {
  376. throw new WrongDTDException( "Unexpected entity type \"" + type + "\"." ); // NOI18N
  377. }
  378. } else {
  379. type.append( (char)i );
  380. }
  381. break;
  382. case PED_ATYPE:
  383. if( Character.isWhitespace( (char)i ) ) break;
  384. if( i == '"' ) {
  385. state = PED_ID;
  386. break;
  387. }
  388. throw new WrongDTDException( "Unexpected char '" + (char)i + "' in PUBLIC entity." ); // NOI18N
  389. case PED_ID:
  390. if( i == '"' ) {
  391. state = PED_AID;
  392. } else {
  393. identifier.append( (char)i );
  394. }
  395. break;
  396. case PED_AID:
  397. if( Character.isWhitespace( (char)i ) ) break;
  398. if( i == '"' ) {
  399. state = PED_FILE;
  400. break;
  401. }
  402. if( i == '>' ) {
  403. addPublicEntity( name.toString(), identifier.toString(), null );
  404. return;
  405. }
  406. throw new WrongDTDException( "Unexpected char '" + (char)i + "' in PUBLIC entity." ); // NOI18N
  407. case PED_FILE:
  408. if( i == '"' ) {
  409. state = PED_AFILE;
  410. } else {
  411. value.append( (char)i );
  412. }
  413. break;
  414. case PED_AFILE:
  415. if( Character.isWhitespace( (char)i ) ) break;
  416. if( i == '-' ) {
  417. state = PED_AFILE_M;
  418. break;
  419. }
  420. if( i == '>' ) {
  421. addPublicEntity( name.toString(), identifier.toString(), value.toString() );
  422. return;
  423. }
  424. throw new WrongDTDException( "Unexpected char '" + (char)i + "' in PUBLIC entity." ); // NOI18N
  425. case PED_AFILE_M:
  426. if( i == '-' ) {
  427. parseComment( in );
  428. state = PED_FILE;
  429. break;
  430. }
  431. throw new WrongDTDException( "Unexpected sequence \"-" + (char)i + "\" in in PUBLIC entity." ); // NOI18N
  432. case PED_CHAR:
  433. if( Character.isWhitespace( (char)i ) ) {
  434. state = PED_ACHAR;
  435. } else {
  436. name.append( (char)i );
  437. }
  438. break;
  439. case PED_ACHAR:
  440. if( Character.isWhitespace( (char)i ) ) break;
  441. else {
  442. //name read
  443. if(xmlDTD) {
  444. in.unread(i); //backup the char
  445. type.append("CDATA");
  446. state = PED_CH_ATYPE;
  447. break; //reread i
  448. } else {
  449. type.append( (char)i );
  450. state = PED_CH_TYPE;
  451. }
  452. }
  453. break;
  454. case PED_CH_TYPE:
  455. if( Character.isWhitespace( (char)i ) ) {
  456. if( type.toString().equals( "CDATA" ) ) { // NOI18N
  457. state = PED_CH_ATYPE;
  458. } else {
  459. throw new WrongDTDException( "Unexpected entity type \"" + type + "\"." ); // NOI18N
  460. }
  461. } else {
  462. type.append( (char)i );
  463. }
  464. break;
  465. case PED_CH_ATYPE:
  466. if( Character.isWhitespace( (char)i ) ) break;
  467. else if( i == '"' ) {
  468. state = PED_CH_QUOT;
  469. } else {
  470. throw new WrongDTDException( "Unexpected char '" + (char)i + "' in entity." ); // NOI18N
  471. }
  472. break;
  473. case PED_CH_QUOT:
  474. if( i == '"' ) {
  475. if(xmlDTD) {
  476. //resolve '&' char reference
  477. String replaced = value.toString().replace("&#38;", "&");
  478. value.replace(0, value.length(), replaced);
  479. }
  480. value.delete( 0, 2 );
  481. value.deleteCharAt( value.length() - 1 );
  482. int code = Integer.parseInt( value.toString() );
  483. createAddCharRef( name.toString(), (char)code );
  484. state = PED_AVAL;
  485. } else {
  486. value.append( (char)i );
  487. }
  488. }
  489. }
  490. }
  491. private static final int GR_INIT=0;
  492. private static final int GR_NAME=1;
  493. private static final int GR_ANAME=2;
  494. /** Parse group of names separated by '|' character and optional spaces
  495. * @return List of Strings containing names */
  496. private List parseGroup( PushbackReader in ) throws IOException, WrongDTDException {
  497. int state = GR_INIT;
  498. StringBuffer name = new StringBuffer();
  499. List list = new ArrayList();
  500. for( ;; ) {
  501. int i = in.read();
  502. if( i == -1 ) throw new WrongDTDException( "Premature end of DTD" ); // NOI18N EOF
  503. switch( state ) {
  504. case GR_INIT:
  505. if( Character.isWhitespace( (char)i ) ) break;
  506. if( i == '%' ) {
  507. parseEntityReference( in );
  508. } else {
  509. name.append( (char)i );
  510. state = GR_NAME;
  511. }
  512. break;
  513. case GR_NAME:
  514. if( isNameChar( (char)i ) ) {
  515. name.append( (char)i );
  516. break;
  517. }
  518. switch( i ) {
  519. case ')':
  520. list.add( name.toString() );
  521. return list;
  522. case '|':
  523. list.add( name.toString() );
  524. name.setLength( 0 );
  525. state = GR_INIT;
  526. break;
  527. default:
  528. if( Character.isWhitespace( (char)i ) ) {
  529. list.add( name.toString() );
  530. name.setLength( 0 );
  531. state = GR_ANAME;
  532. break;
  533. } else {
  534. throw new WrongDTDException( "Unexpected char '" + (char)i + "' in group definition." ); // NOI18N
  535. }
  536. }
  537. break;
  538. case GR_ANAME:
  539. if( Character.isWhitespace( (char)i ) ) break;
  540. switch( i ) {
  541. case ')':
  542. return list;
  543. case '|':
  544. state = GR_INIT;
  545. break;
  546. default:
  547. throw new WrongDTDException( "Unexpected char '" + (char)i + "' in group definition." ); // NOI18N
  548. }
  549. break;
  550. }
  551. }
  552. }
  553. private static final int EL_INIT = 0;
  554. private static final int EL_NAME = 1;
  555. private static final int EL_ANAME = 2;
  556. private static final int EL_ASTART = 3;
  557. private static final int EL_ACONTENT = 4;
  558. private static final int EL_PLUS = 5;
  559. private static final int EL_MINUS = 6;
  560. private static final int EL_ANAME_XML = 7;
  561. /** parse the whole element(s) definition including content model.
  562. * Create corresponding instances of DTD.Element filled with proper
  563. * informations. Make the same content models and their contents shared
  564. * across the DTD */
  565. private void parseElement( PushbackReader in ) throws IOException, WrongDTDException {
  566. int state = EL_INIT;
  567. StringBuffer name = new StringBuffer();
  568. List list = null;
  569. boolean optStart = false;
  570. boolean optEnd = false;
  571. DTD.Content content = null;
  572. Set inSet = new HashSet();
  573. Set exSet = new HashSet();
  574. for( ;; ) {
  575. int i = in.read();
  576. if( i == -1 ) break;
  577. switch( state ) {
  578. case EL_INIT:
  579. if( Character.isWhitespace( (char)i ) ) break;
  580. switch( i ) {
  581. case '(':
  582. list = parseGroup( in );
  583. state = EL_ANAME;
  584. break;
  585. case '%':
  586. parseEntityReference( in );
  587. break; // Stay in EL_INIT
  588. default:
  589. name.append( (char)i );
  590. state = EL_NAME;
  591. break;
  592. }
  593. break;
  594. case EL_NAME:
  595. if( Character.isWhitespace( (char)i ) ) {
  596. state = xmlDTD ? EL_ANAME_XML : EL_ANAME;
  597. list = new ArrayList();
  598. list.add( name.toString() );
  599. } else {
  600. name.append( (char)i );
  601. }
  602. break;
  603. case EL_ANAME_XML:
  604. if( Character.isWhitespace( (char)i ) ) break;
  605. in.unread(i); //backup
  606. content = parseContent( in );
  607. //optStart = false; optEnd = false; //default
  608. state = EL_ACONTENT;
  609. break;
  610. case EL_ANAME:
  611. if( Character.isWhitespace( (char)i ) ) break;
  612. switch( i ) {
  613. case 'O':
  614. optStart = true; // fall fhrough
  615. case '-':
  616. state = EL_ASTART;
  617. break;
  618. default:
  619. throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ELEMENT optStart definition." ); // NOI18N
  620. }
  621. break;
  622. case EL_ASTART:
  623. if( Character.isWhitespace( (char)i ) ) break;
  624. switch( i ) {
  625. case 'O':
  626. optEnd = true; // fall fhrough
  627. case '-':
  628. content = parseContent( in );
  629. state = EL_ACONTENT;
  630. break;
  631. default:
  632. throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ELEMENT optEnd definition." ); // NOI18N
  633. }
  634. break;
  635. case EL_ACONTENT:
  636. if( Character.isWhitespace( (char)i ) ) break;
  637. switch( i ) {
  638. case '+':
  639. state = EL_PLUS;
  640. break;
  641. case '-':
  642. state = EL_MINUS;
  643. break;
  644. case '>':
  645. DTD.ContentModel cm = createContentModel( content, inSet, exSet );
  646. for( Iterator iter = list.iterator(); iter.hasNext(); ) {
  647. String key = (String)iter.next();
  648. key = xmlDTD ? key : key.toUpperCase(Locale.ENGLISH);
  649. elementMap.put( key, createElement( key, cm, optStart, optEnd, xmlDTD) );
  650. }
  651. return;
  652. default:
  653. throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ELEMENT definition." ); // NOI18N
  654. }
  655. break;
  656. case EL_PLUS:
  657. if( i == '(' ) {
  658. state = EL_ACONTENT;
  659. inSet.addAll( parseGroup( in ) );
  660. } else {
  661. throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ELEMENT definition." ); // NOI18N
  662. }
  663. break;
  664. case EL_MINUS:
  665. switch( i ) {
  666. case '(':
  667. state = EL_ACONTENT;
  668. List l = parseGroup( in );
  669. exSet.addAll( l );
  670. break;
  671. case '-':
  672. state = EL_ACONTENT;
  673. parseComment( in );
  674. break;
  675. default:
  676. throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ELEMENT definition." ); // NOI18N
  677. }
  678. break;
  679. }
  680. }
  681. //XXX
  682. }
  683. private static final int CO_INIT = 0;
  684. private static final int CO_NAME = 1;
  685. private static final int CO_AMODEL = 2;
  686. private static final int CO_AND = 3;
  687. private static final int CO_OR = 4;
  688. private static final int CO_SEQ = 5;
  689. private static final int CO_AGROUP = 6;
  690. /** This automata would parse content model definitions and return them
  691. * as a Content instance of root of generated CM tree */
  692. private DTD.Content parseContent( PushbackReader in ) throws IOException, WrongDTDException {
  693. int state = EL_INIT;
  694. StringBuffer name = new StringBuffer();
  695. ArrayList list = null;
  696. DTD.Content content = null;
  697. for( ;; ) {
  698. int i = in.read();
  699. if( i == -1 ) break;
  700. switch( state ) {
  701. case CO_INIT:
  702. if( Character.isWhitespace( (char)i ) ) break;
  703. switch( i ) {
  704. case '%':
  705. parseEntityReference( in );
  706. break; // Stay in CO_INIT
  707. case '(':
  708. content = parseContent( in );
  709. state = CO_AMODEL;
  710. break;
  711. default:
  712. name.append( (char)i );
  713. state = CO_NAME;
  714. break;
  715. }
  716. break;
  717. case CO_NAME:
  718. if( isNameChar( (char)i ) ) {
  719. name.append( (char)i );
  720. } else {
  721. switch( i ) {
  722. case '?':
  723. case '+':
  724. case '*':
  725. DTD.Content leaf = createContentLeaf( name.toString() );
  726. return createContentNode( (char)i, leaf );
  727. default:
  728. in.unread( i );
  729. return createContentLeaf( name.toString() );
  730. }
  731. }
  732. break;
  733. case CO_AMODEL:
  734. if( Character.isWhitespace( (char)i ) ) break;
  735. switch( i ) {
  736. case '&':
  737. list = new ArrayList();
  738. list.add( content );
  739. list.add( parseContent( in ) );
  740. state = CO_AND;
  741. break;
  742. case '|':
  743. list = new ArrayList();
  744. list.add( content );
  745. list.add( parseContent( in ) );
  746. state = CO_OR;
  747. break;
  748. case ',':
  749. list = new ArrayList();
  750. list.add( content );
  751. list.add( parseContent( in ) );
  752. state = CO_SEQ;
  753. break;
  754. case ')':
  755. state = CO_AGROUP;
  756. break;
  757. default:
  758. throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ELEMENT optEnd definition." ); // NOI18N
  759. }
  760. break;
  761. case CO_AND:
  762. if( Character.isWhitespace( (char)i ) ) break;
  763. switch( i ) {
  764. case '&':
  765. list.add( parseContent( in ) );
  766. break;
  767. case ')':
  768. content = createContentNode( '&', (DTD.Content[])list.toArray( new DTD.Content[0] ) );
  769. state = CO_AGROUP;
  770. break;
  771. default:
  772. throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ContentModel definition." ); // NOI18N
  773. }
  774. break;
  775. case CO_OR:
  776. if( Character.isWhitespace( (char)i ) ) break;
  777. switch( i ) {
  778. case '|':
  779. list.add( parseContent( in ) );
  780. break;
  781. case ')':
  782. content = createContentNode( '|', (DTD.Content[])list.toArray( new DTD.Content[0] ) );
  783. state = CO_AGROUP;
  784. break;
  785. default:
  786. throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ContentModel definition." ); // NOI18N
  787. }
  788. break;
  789. case CO_SEQ:
  790. if( Character.isWhitespace( (char)i ) ) break;
  791. switch( i ) {
  792. case ',':
  793. list.add( parseContent( in ) );
  794. break;
  795. case ')':
  796. content = createContentNode( ',', (DTD.Content[])list.toArray( new DTD.Content[0] ) );
  797. state = CO_AGROUP;
  798. break;
  799. default:
  800. throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ContentModel definition." ); // NOI18N
  801. }
  802. break;
  803. case CO_AGROUP:
  804. if( Character.isWhitespace( (char)i ) ) return content;
  805. switch( i ) {
  806. case '?':
  807. case '+':
  808. case '*':
  809. return createContentNode( (char)i, content );
  810. default:
  811. in.unread( i );
  812. return content;
  813. }
  814. }
  815. }
  816. throw new WrongDTDException( "Premature end of DTD" ); // NOI18N EOF
  817. }
  818. private static final int ATT_INIT = 0;
  819. private static final int ATT_NAME = 1;
  820. private static final int ATT_ANAME = 2;
  821. private static final int ATT_ANAME_M = 3;
  822. private static final int ATT_VAR = 4;
  823. private static final int ATT_AVAR = 5;
  824. private static final int ATT_TYPE = 6;
  825. private static final int ATT_ATYPE = 7;
  826. private static final int ATT_MODE = 8;
  827. private static final int ATT_FIXED_VALUE = 9;
  828. private static final int ATT_FIXED_VALUE_SQ = 10;
  829. private static final int ATT_FIXED_VALUE_DQ = 11;
  830. private void parseAttlist( PushbackReader in ) throws IOException, WrongDTDException {
  831. int state = ATT_INIT;
  832. StringBuffer name = new StringBuffer();
  833. List list = null; // List of tag names for which are these attribs
  834. StringBuffer attr = new StringBuffer(); // name of attribute
  835. List values = null; // (list of possible values
  836. StringBuffer type = new StringBuffer(); // OR the type of attribute )
  837. String typeHelper = null; // AND name of entity
  838. StringBuffer mode = new StringBuffer(); // default mode of this attrib
  839. for( ;; ) {
  840. int i = in.read();
  841. if( i == -1 ) break;
  842. switch( state ) {
  843. case ATT_INIT:
  844. if( Character.isWhitespace( (char)i ) ) break;
  845. switch( i ) {
  846. case '%':
  847. parseEntityReference( in );
  848. break; // Stay in ATT_INIT
  849. case '(':
  850. list = parseGroup( in );
  851. state = ATT_ANAME;
  852. break;
  853. default:
  854. name.append( (char)i );
  855. state = ATT_NAME;
  856. break;
  857. }
  858. break;
  859. case ATT_NAME:
  860. if( Character.isWhitespace( (char)i ) ) {
  861. list = new ArrayList();
  862. list.add( name.toString() );
  863. state = ATT_ANAME;
  864. break;
  865. }
  866. name.append( (char)i );
  867. break;
  868. case ATT_ANAME:
  869. if( Character.isWhitespace( (char)i ) ) break;
  870. switch( i ) {
  871. case '%':
  872. parseEntityReference( in );
  873. break; // Stay in ATT_ANAME
  874. case '-':
  875. state = ATT_ANAME_M;
  876. break;
  877. case '>':
  878. return;
  879. default:
  880. attr.append( (char)i );
  881. state = ATT_VAR;
  882. break;
  883. }
  884. break;
  885. case ATT_ANAME_M:
  886. if( i == '-' ) {
  887. parseComment( in ); // skip the comment
  888. state = ATT_ANAME;
  889. } else {
  890. throw new WrongDTDException( "Unexpected char '" + (char)i + "' in ATTLIST definition." ); // NOI18N
  891. }
  892. break;
  893. case ATT_VAR:
  894. if( Character.isWhitespace( (char)i ) ) {
  895. state = ATT_AVAR;
  896. break;
  897. }
  898. attr.append( (char)i );
  899. break;
  900. case ATT_AVAR:
  901. if( Character.isWhitespace( (char)i ) ) break;
  902. switch( i ) {
  903. case '%':
  904. typeHelper = parseEntityReference( in );
  905. break; // Stay in ATT_AVAR
  906. case '(':
  907. values = parseGroup( in );
  908. state = ATT_ATYPE;
  909. break;
  910. default:
  911. type.append( (char)i );
  912. state = ATT_TYPE;
  913. break;
  914. }
  915. break;
  916. case ATT_TYPE:
  917. if( Character.isWhitespace( (char)i ) ) {
  918. state = ATT_ATYPE;
  919. break;
  920. }
  921. type.append( (char)i );
  922. break;
  923. case ATT_ATYPE:
  924. if( Character.isWhitespace( (char)i ) ) break;
  925. switch( i ) {
  926. case '%':
  927. parseEntityReference( in );
  928. break; // Stay in ATT_ATYPE
  929. default:
  930. mode.append( (char)i );
  931. state = ATT_MODE;
  932. break;
  933. }
  934. break;
  935. case ATT_MODE:
  936. if( Character.isWhitespace( (char)i ) || i == '>') {
  937. // Create attr and add it to all tags
  938. DTD.Attribute a = null;
  939. if( values == null ) { // HOTSPOT for internation of strings!!!
  940. a = createAttribute( attr.toString(),
  941. DTD.Attribute.TYPE_BASE, type.toString(),
  942. typeHelper, mode.toString(), null, xmlDTD );
  943. } else if( values.size() == 1 ) {
  944. a = createAttribute( attr.toString(),
  945. DTD.Attribute.TYPE_BOOLEAN, null, typeHelper,
  946. mode.toString(), null, xmlDTD );
  947. } else {
  948. SortedMap vals = new TreeMap();
  949. for( Iterator iter = values.iterator(); iter.hasNext(); ) {
  950. String key = (String)iter.next();
  951. String valName = xmlDTD ? key : key.toLowerCase(Locale.ENGLISH);
  952. vals.put( valName, createValue( valName ) );
  953. }
  954. a = createAttribute( attr.toString(),
  955. DTD.Attribute.TYPE_SET, null, typeHelper,
  956. mode.toString(), vals, xmlDTD );
  957. }
  958. for( Iterator iter = list.iterator(); iter.hasNext(); ) {
  959. addAttrToElement( (String)iter.next(), a );
  960. }
  961. typeHelper = null;
  962. attr.setLength(0);
  963. type.setLength(0);
  964. mode.setLength(0);
  965. values = null;
  966. if(xmlDTD && a.getDefaultMode().equals(DTD.Attribute.MODE_FIXED)) {
  967. //skip the fixed value
  968. state = ATT_FIXED_VALUE;
  969. } else {
  970. state = ATT_ANAME;
  971. }
  972. if(i == '>') {
  973. return ;
  974. }
  975. break;
  976. }
  977. mode.append( (char)i );
  978. break;
  979. case ATT_FIXED_VALUE:
  980. if( Character.isWhitespace( (char)i ) ) break;
  981. if(i == '\'') {
  982. state = ATT_FIXED_VALUE_SQ;
  983. } else if(i == '"') {
  984. state = ATT_FIXED_VALUE_DQ;
  985. }
  986. break;
  987. case ATT_FIXED_VALUE_SQ:
  988. if(i == '\'') {
  989. state = ATT_ANAME;
  990. break;
  991. }
  992. break;
  993. case ATT_FIXED_VALUE_DQ:
  994. if(i == '"') {
  995. state = ATT_ANAME;
  996. break;
  997. }
  998. break;
  999. }
  1000. }
  1001. }
  1002. private static final int OPT_INIT = 0;
  1003. private static final int OPT_PROCESS = 1;
  1004. private static final int OPT_APROCESS = 2;
  1005. private static final int OPT_CONTENT = 3;
  1006. private static final int OPT_BRAC1 = 4;
  1007. private static final int OPT_BRAC2 = 5;
  1008. /** Parser that takes care of conditional inclusion/exclusion of part
  1009. * of DTD. Gets the control just after "<![" */
  1010. private void parseOptional( PushbackReader in ) throws IOException, WrongDTDException {
  1011. int state = OPT_INIT;
  1012. StringBuffer process = new StringBuffer();
  1013. StringBuffer content = new StringBuffer();
  1014. boolean ignore = false;
  1015. for( ;; ) {
  1016. int i = in.read();
  1017. if( i == -1 ) break; // EOF
  1018. switch( state ) {
  1019. case OPT_INIT:
  1020. if( Character.isWhitespace( (char)i ) ) break;
  1021. if( i == '%' ) {
  1022. parseEntityReference( in );
  1023. break;
  1024. }
  1025. process.append( (char)i );
  1026. state = OPT_PROCESS;
  1027. break;
  1028. case OPT_PROCESS:
  1029. if( Character.isWhitespace( (char)i ) ) {
  1030. String s = process.toString();
  1031. if( "IGNORE".equals( s ) ) ignore = true; // NOI18N
  1032. else if( ! "INCLUDE".equals( s ) ) throw new WrongDTDException( "Unexpected processing instruction " + s ); // NOI18N
  1033. state = OPT_APROCESS;
  1034. } else {
  1035. process.append( (char)i );
  1036. }
  1037. break;
  1038. case OPT_APROCESS:
  1039. if( Character.isWhitespace( (char)i ) ) break;
  1040. if( i == '[' ) state = OPT_CONTENT;
  1041. else throw new WrongDTDException( "Unexpected char '" + (char)i + "' in processing instruction." ); // NOI18N
  1042. break;
  1043. case OPT_CONTENT:
  1044. if( i == ']' ) state = OPT_BRAC1;
  1045. else content.append( (char)i );
  1046. break;
  1047. case OPT_BRAC1:
  1048. if( i == ']' ) state = OPT_BRAC2;
  1049. else {
  1050. content.append( ']' ).append( (char)i );
  1051. state = OPT_CONTENT;
  1052. }
  1053. break;
  1054. case OPT_BRAC2:
  1055. if( Character.isWhitespace( (char)i ) ) break;
  1056. if( i == '>' ) {