1. /*
  2. * The Apache Software License, Version 1.1
  3. *
  4. *
  5. * Copyright (c) 1999-2003 The Apache Software Foundation.
  6. * All rights reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The end-user documentation included with the redistribution,
  21. * if any, must include the following acknowledgment:
  22. * "This product includes software developed by the
  23. * Apache Software Foundation (http://www.apache.org/)."
  24. * Alternately, this acknowledgment may appear in the software itself,
  25. * if and wherever such third-party acknowledgments normally appear.
  26. *
  27. * 4. The names "Xerces" and "Apache Software Foundation" must
  28. * not be used to endorse or promote products derived from this
  29. * software without prior written permission. For written
  30. * permission, please contact apache@apache.org.
  31. *
  32. * 5. Products derived from this software may not be called "Apache",
  33. * nor may "Apache" appear in their name, without prior written
  34. * permission of the Apache Software Foundation.
  35. *
  36. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
  37. * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  38. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  39. * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
  40. * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  41. * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  42. * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
  43. * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  44. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
  45. * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  46. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  47. * SUCH DAMAGE.
  48. * ====================================================================
  49. *
  50. * This software consists of voluntary contributions made by many
  51. * individuals on behalf of the Apache Software Foundation and was
  52. * originally based on software copyright (c) 2002, International
  53. * Business Machines, Inc., http://www.apache.org. For more
  54. * information on the Apache Software Foundation, please see
  55. * <http://www.apache.org/>.
  56. */
  57. package com.sun.org.apache.xerces.internal.impl;
  58. import java.io.IOException;
  59. import com.sun.org.apache.xerces.internal.impl.dtd.XMLDTDValidatorFilter;
  60. import com.sun.org.apache.xerces.internal.impl.msg.XMLMessageFormatter;
  61. import com.sun.org.apache.xerces.internal.util.XMLAttributesImpl;
  62. import com.sun.org.apache.xerces.internal.util.XMLSymbols;
  63. import com.sun.org.apache.xerces.internal.xni.NamespaceContext;
  64. import com.sun.org.apache.xerces.internal.xni.QName;
  65. import com.sun.org.apache.xerces.internal.xni.XMLDocumentHandler;
  66. import com.sun.org.apache.xerces.internal.xni.XNIException;
  67. import com.sun.org.apache.xerces.internal.xni.parser.XMLComponentManager;
  68. import com.sun.org.apache.xerces.internal.xni.parser.XMLConfigurationException;
  69. import com.sun.org.apache.xerces.internal.xni.parser.XMLDocumentSource;
  70. /**
  71. * The scanner acts as the source for the document
  72. * information which is communicated to the document handler.
  73. *
  74. * This class scans an XML document, checks if document has a DTD, and if
  75. * DTD is not found the scanner will remove the DTD Validator from the pipeline and perform
  76. * namespace binding.
  77. *
  78. * Note: This scanner should only be used when the namespace processing is on!
  79. *
  80. * <p>
  81. * This component requires the following features and properties from the
  82. * component manager that uses it:
  83. * <ul>
  84. * <li>http://xml.org/sax/features/namespaces {true} -- if the value of this
  85. * feature is set to false this scanner must not be used.</li>
  86. * <li>http://xml.org/sax/features/validation</li>
  87. * <li>http://apache.org/xml/features/nonvalidating/load-external-dtd</li>
  88. * <li>http://apache.org/xml/features/scanner/notify-char-refs</li>
  89. * <li>http://apache.org/xml/features/scanner/notify-builtin-refs</li>
  90. * <li>http://apache.org/xml/properties/internal/symbol-table</li>
  91. * <li>http://apache.org/xml/properties/internal/error-reporter</li>
  92. * <li>http://apache.org/xml/properties/internal/entity-manager</li>
  93. * <li>http://apache.org/xml/properties/internal/dtd-scanner</li>
  94. * </ul>
  95. *
  96. * @author Elena Litani, IBM
  97. * @author Michael Glavassevich, IBM
  98. *
  99. * @version $Id: XML11NSDocumentScannerImpl.java,v 1.11 2004/04/30 15:36:38 mrglavas Exp $
  100. */
  101. public class XML11NSDocumentScannerImpl extends XML11DocumentScannerImpl {
  102. /**
  103. * If is true, the dtd validator is no longer in the pipeline
  104. * and the scanner should bind namespaces
  105. */
  106. protected boolean fBindNamespaces;
  107. /**
  108. * If validating parser, make sure we report an error in the
  109. * scanner if DTD grammar is missing.
  110. */
  111. protected boolean fPerformValidation;
  112. // private data
  113. //
  114. /** DTD validator */
  115. private XMLDTDValidatorFilter fDTDValidator;
  116. /**
  117. * Saw spaces after element name or between attributes.
  118. *
  119. * This is reserved for the case where scanning of a start element spans
  120. * several methods, as is the case when scanning the start of a root element
  121. * where a DTD external subset may be read after scanning the element name.
  122. */
  123. private boolean fSawSpace;
  124. /**
  125. * The scanner is responsible for removing DTD validator
  126. * from the pipeline if it is not needed.
  127. *
  128. * @param validator the DTD validator from the pipeline
  129. */
  130. public void setDTDValidator(XMLDTDValidatorFilter validator) {
  131. fDTDValidator = validator;
  132. }
  133. /**
  134. * Scans a start element. This method will handle the binding of
  135. * namespace information and notifying the handler of the start
  136. * of the element.
  137. * <p>
  138. * <pre>
  139. * [44] EmptyElemTag ::= '<' Name (S Attribute)* S? '/>'
  140. * [40] STag ::= '<' Name (S Attribute)* S? '>'
  141. * </pre>
  142. * <p>
  143. * <strong>Note:</strong> This method assumes that the leading
  144. * '<' character has been consumed.
  145. * <p>
  146. * <strong>Note:</strong> This method uses the fElementQName and
  147. * fAttributes variables. The contents of these variables will be
  148. * destroyed. The caller should copy important information out of
  149. * these variables before calling this method.
  150. *
  151. * @return True if element is empty. (i.e. It matches
  152. * production [44].
  153. */
  154. protected boolean scanStartElement() throws IOException, XNIException {
  155. if (DEBUG_CONTENT_SCANNING)
  156. System.out.println(">>> scanStartElementNS()");
  157. // Note: namespace processing is on by default
  158. fEntityScanner.scanQName(fElementQName);
  159. // REVISIT - [Q] Why do we need this local variable? -- mrglavas
  160. String rawname = fElementQName.rawname;
  161. if (fBindNamespaces) {
  162. fNamespaceContext.pushContext();
  163. if (fScannerState == SCANNER_STATE_ROOT_ELEMENT) {
  164. if (fPerformValidation) {
  165. fErrorReporter.reportError(
  166. XMLMessageFormatter.XML_DOMAIN,
  167. "MSG_GRAMMAR_NOT_FOUND",
  168. new Object[] { rawname },
  169. XMLErrorReporter.SEVERITY_ERROR);
  170. if (fDoctypeName == null
  171. || !fDoctypeName.equals(rawname)) {
  172. fErrorReporter.reportError(
  173. XMLMessageFormatter.XML_DOMAIN,
  174. "RootElementTypeMustMatchDoctypedecl",
  175. new Object[] { fDoctypeName, rawname },
  176. XMLErrorReporter.SEVERITY_ERROR);
  177. }
  178. }
  179. }
  180. }
  181. // push element stack
  182. fCurrentElement = fElementStack.pushElement(fElementQName);
  183. // attributes
  184. boolean empty = false;
  185. fAttributes.removeAllAttributes();
  186. do {
  187. // spaces
  188. boolean sawSpace = fEntityScanner.skipSpaces();
  189. // end tag?
  190. int c = fEntityScanner.peekChar();
  191. if (c == '>') {
  192. fEntityScanner.scanChar();
  193. break;
  194. } else if (c == '/') {
  195. fEntityScanner.scanChar();
  196. if (!fEntityScanner.skipChar('>')) {
  197. reportFatalError(
  198. "ElementUnterminated",
  199. new Object[] { rawname });
  200. }
  201. empty = true;
  202. break;
  203. } else if (!isValidNameStartChar(c) || !sawSpace) {
  204. // Second chance. Check if this character is a high
  205. // surrogate of a valid name start character.
  206. if (!isValidNameStartHighSurrogate(c) || !sawSpace) {
  207. reportFatalError(
  208. "ElementUnterminated",
  209. new Object[] { rawname });
  210. }
  211. }
  212. // attributes
  213. scanAttribute(fAttributes);
  214. } while (true);
  215. if (fBindNamespaces) {
  216. // REVISIT: is it required? forbit xmlns prefix for element
  217. if (fElementQName.prefix == XMLSymbols.PREFIX_XMLNS) {
  218. fErrorReporter.reportError(
  219. XMLMessageFormatter.XMLNS_DOMAIN,
  220. "ElementXMLNSPrefix",
  221. new Object[] { fElementQName.rawname },
  222. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  223. }
  224. // bind the element
  225. String prefix =
  226. fElementQName.prefix != null
  227. ? fElementQName.prefix
  228. : XMLSymbols.EMPTY_STRING;
  229. // assign uri to the element
  230. fElementQName.uri = fNamespaceContext.getURI(prefix);
  231. // make sure that object in the element stack is updated as well
  232. fCurrentElement.uri = fElementQName.uri;
  233. if (fElementQName.prefix == null && fElementQName.uri != null) {
  234. fElementQName.prefix = XMLSymbols.EMPTY_STRING;
  235. // making sure that the object in the element stack is updated too.
  236. fCurrentElement.prefix = XMLSymbols.EMPTY_STRING;
  237. }
  238. if (fElementQName.prefix != null && fElementQName.uri == null) {
  239. fErrorReporter.reportError(
  240. XMLMessageFormatter.XMLNS_DOMAIN,
  241. "ElementPrefixUnbound",
  242. new Object[] {
  243. fElementQName.prefix,
  244. fElementQName.rawname },
  245. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  246. }
  247. // bind attributes (xmlns are already bound bellow)
  248. int length = fAttributes.getLength();
  249. for (int i = 0; i < length; i++) {
  250. fAttributes.getName(i, fAttributeQName);
  251. String aprefix =
  252. fAttributeQName.prefix != null
  253. ? fAttributeQName.prefix
  254. : XMLSymbols.EMPTY_STRING;
  255. String uri = fNamespaceContext.getURI(aprefix);
  256. // REVISIT: try removing the first "if" and see if it is faster.
  257. //
  258. if (fAttributeQName.uri != null
  259. && fAttributeQName.uri == uri) {
  260. continue;
  261. }
  262. if (aprefix != XMLSymbols.EMPTY_STRING) {
  263. fAttributeQName.uri = uri;
  264. if (uri == null) {
  265. fErrorReporter.reportError(
  266. XMLMessageFormatter.XMLNS_DOMAIN,
  267. "AttributePrefixUnbound",
  268. new Object[] {
  269. fElementQName.rawname,
  270. fAttributeQName.rawname,
  271. aprefix },
  272. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  273. }
  274. fAttributes.setURI(i, uri);
  275. }
  276. }
  277. if (length > 1) {
  278. QName name = fAttributes.checkDuplicatesNS();
  279. if (name != null) {
  280. if (name.uri != null) {
  281. fErrorReporter.reportError(
  282. XMLMessageFormatter.XMLNS_DOMAIN,
  283. "AttributeNSNotUnique",
  284. new Object[] {
  285. fElementQName.rawname,
  286. name.localpart,
  287. name.uri },
  288. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  289. } else {
  290. fErrorReporter.reportError(
  291. XMLMessageFormatter.XMLNS_DOMAIN,
  292. "AttributeNotUnique",
  293. new Object[] {
  294. fElementQName.rawname,
  295. name.rawname },
  296. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  297. }
  298. }
  299. }
  300. }
  301. // call handler
  302. if (fDocumentHandler != null) {
  303. if (empty) {
  304. //decrease the markup depth..
  305. fMarkupDepth--;
  306. // check that this element was opened in the same entity
  307. if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
  308. reportFatalError(
  309. "ElementEntityMismatch",
  310. new Object[] { fCurrentElement.rawname });
  311. }
  312. fDocumentHandler.emptyElement(fElementQName, fAttributes, null);
  313. if (fBindNamespaces) {
  314. fNamespaceContext.popContext();
  315. }
  316. //pop the element off the stack..
  317. fElementStack.popElement(fElementQName);
  318. } else {
  319. fDocumentHandler.startElement(fElementQName, fAttributes, null);
  320. }
  321. }
  322. if (DEBUG_CONTENT_SCANNING)
  323. System.out.println("<<< scanStartElement(): " + empty);
  324. return empty;
  325. } // scanStartElement():boolean
  326. /**
  327. * Scans the name of an element in a start or empty tag.
  328. *
  329. * @see #scanStartElement()
  330. */
  331. protected void scanStartElementName ()
  332. throws IOException, XNIException {
  333. // Note: namespace processing is on by default
  334. fEntityScanner.scanQName(fElementQName);
  335. // Must skip spaces here because the DTD scanner
  336. // would consume them at the end of the external subset.
  337. fSawSpace = fEntityScanner.skipSpaces();
  338. } // scanStartElementName()
  339. /**
  340. * Scans the remainder of a start or empty tag after the element name.
  341. *
  342. * @see #scanStartElement
  343. * @return True if element is empty.
  344. */
  345. protected boolean scanStartElementAfterName()
  346. throws IOException, XNIException {
  347. // REVISIT - [Q] Why do we need this local variable? -- mrglavas
  348. String rawname = fElementQName.rawname;
  349. if (fBindNamespaces) {
  350. fNamespaceContext.pushContext();
  351. if (fScannerState == SCANNER_STATE_ROOT_ELEMENT) {
  352. if (fPerformValidation) {
  353. fErrorReporter.reportError(
  354. XMLMessageFormatter.XML_DOMAIN,
  355. "MSG_GRAMMAR_NOT_FOUND",
  356. new Object[] { rawname },
  357. XMLErrorReporter.SEVERITY_ERROR);
  358. if (fDoctypeName == null
  359. || !fDoctypeName.equals(rawname)) {
  360. fErrorReporter.reportError(
  361. XMLMessageFormatter.XML_DOMAIN,
  362. "RootElementTypeMustMatchDoctypedecl",
  363. new Object[] { fDoctypeName, rawname },
  364. XMLErrorReporter.SEVERITY_ERROR);
  365. }
  366. }
  367. }
  368. }
  369. // push element stack
  370. fCurrentElement = fElementStack.pushElement(fElementQName);
  371. // attributes
  372. boolean empty = false;
  373. fAttributes.removeAllAttributes();
  374. do {
  375. // end tag?
  376. int c = fEntityScanner.peekChar();
  377. if (c == '>') {
  378. fEntityScanner.scanChar();
  379. break;
  380. } else if (c == '/') {
  381. fEntityScanner.scanChar();
  382. if (!fEntityScanner.skipChar('>')) {
  383. reportFatalError(
  384. "ElementUnterminated",
  385. new Object[] { rawname });
  386. }
  387. empty = true;
  388. break;
  389. } else if (!isValidNameStartChar(c) || !fSawSpace) {
  390. // Second chance. Check if this character is a high
  391. // surrogate of a valid name start character.
  392. if (!isValidNameStartHighSurrogate(c) || !fSawSpace) {
  393. reportFatalError(
  394. "ElementUnterminated",
  395. new Object[] { rawname });
  396. }
  397. }
  398. // attributes
  399. scanAttribute(fAttributes);
  400. // spaces
  401. fSawSpace = fEntityScanner.skipSpaces();
  402. } while (true);
  403. if (fBindNamespaces) {
  404. // REVISIT: is it required? forbit xmlns prefix for element
  405. if (fElementQName.prefix == XMLSymbols.PREFIX_XMLNS) {
  406. fErrorReporter.reportError(
  407. XMLMessageFormatter.XMLNS_DOMAIN,
  408. "ElementXMLNSPrefix",
  409. new Object[] { fElementQName.rawname },
  410. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  411. }
  412. // bind the element
  413. String prefix =
  414. fElementQName.prefix != null
  415. ? fElementQName.prefix
  416. : XMLSymbols.EMPTY_STRING;
  417. // assign uri to the element
  418. fElementQName.uri = fNamespaceContext.getURI(prefix);
  419. // make sure that object in the element stack is updated as well
  420. fCurrentElement.uri = fElementQName.uri;
  421. if (fElementQName.prefix == null && fElementQName.uri != null) {
  422. fElementQName.prefix = XMLSymbols.EMPTY_STRING;
  423. // making sure that the object in the element stack is updated too.
  424. fCurrentElement.prefix = XMLSymbols.EMPTY_STRING;
  425. }
  426. if (fElementQName.prefix != null && fElementQName.uri == null) {
  427. fErrorReporter.reportError(
  428. XMLMessageFormatter.XMLNS_DOMAIN,
  429. "ElementPrefixUnbound",
  430. new Object[] {
  431. fElementQName.prefix,
  432. fElementQName.rawname },
  433. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  434. }
  435. // bind attributes (xmlns are already bound bellow)
  436. int length = fAttributes.getLength();
  437. for (int i = 0; i < length; i++) {
  438. fAttributes.getName(i, fAttributeQName);
  439. String aprefix =
  440. fAttributeQName.prefix != null
  441. ? fAttributeQName.prefix
  442. : XMLSymbols.EMPTY_STRING;
  443. String uri = fNamespaceContext.getURI(aprefix);
  444. // REVISIT: try removing the first "if" and see if it is faster.
  445. //
  446. if (fAttributeQName.uri != null
  447. && fAttributeQName.uri == uri) {
  448. continue;
  449. }
  450. if (aprefix != XMLSymbols.EMPTY_STRING) {
  451. fAttributeQName.uri = uri;
  452. if (uri == null) {
  453. fErrorReporter.reportError(
  454. XMLMessageFormatter.XMLNS_DOMAIN,
  455. "AttributePrefixUnbound",
  456. new Object[] {
  457. fElementQName.rawname,
  458. fAttributeQName.rawname,
  459. aprefix },
  460. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  461. }
  462. fAttributes.setURI(i, uri);
  463. }
  464. }
  465. if (length > 1) {
  466. QName name = fAttributes.checkDuplicatesNS();
  467. if (name != null) {
  468. if (name.uri != null) {
  469. fErrorReporter.reportError(
  470. XMLMessageFormatter.XMLNS_DOMAIN,
  471. "AttributeNSNotUnique",
  472. new Object[] {
  473. fElementQName.rawname,
  474. name.localpart,
  475. name.uri },
  476. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  477. } else {
  478. fErrorReporter.reportError(
  479. XMLMessageFormatter.XMLNS_DOMAIN,
  480. "AttributeNotUnique",
  481. new Object[] {
  482. fElementQName.rawname,
  483. name.rawname },
  484. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  485. }
  486. }
  487. }
  488. }
  489. // call handler
  490. if (fDocumentHandler != null) {
  491. if (empty) {
  492. //decrease the markup depth..
  493. fMarkupDepth--;
  494. // check that this element was opened in the same entity
  495. if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
  496. reportFatalError(
  497. "ElementEntityMismatch",
  498. new Object[] { fCurrentElement.rawname });
  499. }
  500. fDocumentHandler.emptyElement(fElementQName, fAttributes, null);
  501. if (fBindNamespaces) {
  502. fNamespaceContext.popContext();
  503. }
  504. //pop the element off the stack..
  505. fElementStack.popElement(fElementQName);
  506. } else {
  507. fDocumentHandler.startElement(fElementQName, fAttributes, null);
  508. }
  509. }
  510. if (DEBUG_CONTENT_SCANNING)
  511. System.out.println("<<< scanStartElementAfterName(): " + empty);
  512. return empty;
  513. } // scanStartElementAfterName()
  514. /**
  515. * Scans an attribute.
  516. * <p>
  517. * <pre>
  518. * [41] Attribute ::= Name Eq AttValue
  519. * </pre>
  520. * <p>
  521. * <strong>Note:</strong> This method assumes that the next
  522. * character on the stream is the first character of the attribute
  523. * name.
  524. * <p>
  525. * <strong>Note:</strong> This method uses the fAttributeQName and
  526. * fQName variables. The contents of these variables will be
  527. * destroyed.
  528. *
  529. * @param attributes The attributes list for the scanned attribute.
  530. */
  531. protected void scanAttribute(XMLAttributesImpl attributes)
  532. throws IOException, XNIException {
  533. if (DEBUG_CONTENT_SCANNING)
  534. System.out.println(">>> scanAttribute()");
  535. // name
  536. fEntityScanner.scanQName(fAttributeQName);
  537. // equals
  538. fEntityScanner.skipSpaces();
  539. if (!fEntityScanner.skipChar('=')) {
  540. reportFatalError(
  541. "EqRequiredInAttribute",
  542. new Object[] {
  543. fCurrentElement.rawname,
  544. fAttributeQName.rawname });
  545. }
  546. fEntityScanner.skipSpaces();
  547. // content
  548. int attrIndex;
  549. if (fBindNamespaces) {
  550. attrIndex = attributes.getLength();
  551. attributes.addAttributeNS(
  552. fAttributeQName,
  553. XMLSymbols.fCDATASymbol,
  554. null);
  555. } else {
  556. int oldLen = attributes.getLength();
  557. attrIndex =
  558. attributes.addAttribute(
  559. fAttributeQName,
  560. XMLSymbols.fCDATASymbol,
  561. null);
  562. // WFC: Unique Att Spec
  563. if (oldLen == attributes.getLength()) {
  564. reportFatalError(
  565. "AttributeNotUnique",
  566. new Object[] {
  567. fCurrentElement.rawname,
  568. fAttributeQName.rawname });
  569. }
  570. }
  571. //REVISIT: one more case needs to be included: external PE and standalone is no
  572. boolean isVC = fHasExternalDTD && !fStandalone;
  573. // REVISIT: it seems that this function should not take attributes, and length
  574. scanAttributeValue(
  575. this.fTempString,
  576. fTempString2,
  577. fAttributeQName.rawname,
  578. isVC,
  579. fCurrentElement.rawname);
  580. String value = fTempString.toString();
  581. attributes.setValue(attrIndex, value);
  582. attributes.setNonNormalizedValue(attrIndex, fTempString2.toString());
  583. attributes.setSpecified(attrIndex, true);
  584. // record namespace declarations if any.
  585. if (fBindNamespaces) {
  586. String localpart = fAttributeQName.localpart;
  587. String prefix =
  588. fAttributeQName.prefix != null
  589. ? fAttributeQName.prefix
  590. : XMLSymbols.EMPTY_STRING;
  591. // when it's of form xmlns="..." or xmlns:prefix="...",
  592. // it's a namespace declaration. but prefix:xmlns="..." isn't.
  593. if (prefix == XMLSymbols.PREFIX_XMLNS
  594. || prefix == XMLSymbols.EMPTY_STRING
  595. && localpart == XMLSymbols.PREFIX_XMLNS) {
  596. // get the internalized value of this attribute
  597. String uri = fSymbolTable.addSymbol(value);
  598. // 1. "xmlns" can't be bound to any namespace
  599. if (prefix == XMLSymbols.PREFIX_XMLNS
  600. && localpart == XMLSymbols.PREFIX_XMLNS) {
  601. fErrorReporter.reportError(
  602. XMLMessageFormatter.XMLNS_DOMAIN,
  603. "CantBindXMLNS",
  604. new Object[] { fAttributeQName },
  605. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  606. }
  607. // 2. the namespace for "xmlns" can't be bound to any prefix
  608. if (uri == NamespaceContext.XMLNS_URI) {
  609. fErrorReporter.reportError(
  610. XMLMessageFormatter.XMLNS_DOMAIN,
  611. "CantBindXMLNS",
  612. new Object[] { fAttributeQName },
  613. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  614. }
  615. // 3. "xml" can't be bound to any other namespace than it's own
  616. if (localpart == XMLSymbols.PREFIX_XML) {
  617. if (uri != NamespaceContext.XML_URI) {
  618. fErrorReporter.reportError(
  619. XMLMessageFormatter.XMLNS_DOMAIN,
  620. "CantBindXML",
  621. new Object[] { fAttributeQName },
  622. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  623. }
  624. }
  625. // 4. the namespace for "xml" can't be bound to any other prefix
  626. else {
  627. if (uri == NamespaceContext.XML_URI) {
  628. fErrorReporter.reportError(
  629. XMLMessageFormatter.XMLNS_DOMAIN,
  630. "CantBindXML",
  631. new Object[] { fAttributeQName },
  632. XMLErrorReporter.SEVERITY_FATAL_ERROR);
  633. }
  634. }
  635. prefix =
  636. localpart != XMLSymbols.PREFIX_XMLNS
  637. ? localpart
  638. : XMLSymbols.EMPTY_STRING;
  639. // Declare prefix in context. Removing the association between a prefix and a
  640. // namespace name is permitted in XML 1.1, so if the uri value is the empty string,
  641. // the prefix is being unbound. -- mrglavas
  642. fNamespaceContext.declarePrefix(
  643. prefix,
  644. uri.length() != 0 ? uri : null);
  645. // bind namespace attribute to a namespace
  646. attributes.setURI(
  647. attrIndex,
  648. fNamespaceContext.getURI(XMLSymbols.PREFIX_XMLNS));
  649. } else {
  650. // attempt to bind attribute
  651. if (fAttributeQName.prefix != null) {
  652. attributes.setURI(
  653. attrIndex,
  654. fNamespaceContext.getURI(fAttributeQName.prefix));
  655. }
  656. }
  657. }
  658. if (DEBUG_CONTENT_SCANNING)
  659. System.out.println("<<< scanAttribute()");
  660. } // scanAttribute(XMLAttributes)
  661. /**
  662. * Scans an end element.
  663. * <p>
  664. * <pre>
  665. * [42] ETag ::= '</' Name S? '>'
  666. * </pre>
  667. * <p>
  668. * <strong>Note:</strong> This method uses the fElementQName variable.
  669. * The contents of this variable will be destroyed. The caller should
  670. * copy the needed information out of this variable before calling
  671. * this method.
  672. *
  673. * @return The element depth.
  674. */
  675. protected int scanEndElement() throws IOException, XNIException {
  676. if (DEBUG_CONTENT_SCANNING)
  677. System.out.println(">>> scanEndElement()");
  678. // pop context
  679. fElementStack.popElement(fElementQName);
  680. // Take advantage of the fact that next string _should_ be "fElementQName.rawName",
  681. //In scanners most of the time is consumed on checks done for XML characters, we can
  682. // optimize on it and avoid the checks done for endElement,
  683. //we will also avoid symbol table lookup - neeraj.bajaj@sun.com
  684. // this should work both for namespace processing true or false...
  685. //REVISIT: if the string is not the same as expected.. we need to do better error handling..
  686. //We can skip this for now... In any case if the string doesn't match -- document is not well formed.
  687. if (!fEntityScanner.skipString(fElementQName.rawname)) {
  688. reportFatalError(
  689. "ETagRequired",
  690. new Object[] { fElementQName.rawname });
  691. }
  692. // end
  693. fEntityScanner.skipSpaces();
  694. if (!fEntityScanner.skipChar('>')) {
  695. reportFatalError(
  696. "ETagUnterminated",
  697. new Object[] { fElementQName.rawname });
  698. }
  699. fMarkupDepth--;
  700. //we have increased the depth for two markup "<" characters
  701. fMarkupDepth--;
  702. // check that this element was opened in the same entity
  703. if (fMarkupDepth < fEntityStack[fEntityDepth - 1]) {
  704. reportFatalError(
  705. "ElementEntityMismatch",
  706. new Object[] { fCurrentElement.rawname });
  707. }
  708. // call handler
  709. if (fDocumentHandler != null) {
  710. fDocumentHandler.endElement(fElementQName, null);
  711. if (fBindNamespaces) {
  712. fNamespaceContext.popContext();
  713. }
  714. }
  715. return fMarkupDepth;
  716. } // scanEndElement():int
  717. public void reset(XMLComponentManager componentManager)
  718. throws XMLConfigurationException {
  719. super.reset(componentManager);
  720. fPerformValidation = false;
  721. fBindNamespaces = false;
  722. }
  723. /** Creates a content dispatcher. */
  724. protected Dispatcher createContentDispatcher() {
  725. return new NS11ContentDispatcher();
  726. } // createContentDispatcher():Dispatcher
  727. /**
  728. * Dispatcher to handle content scanning.
  729. */
  730. protected final class NS11ContentDispatcher extends ContentDispatcher {
  731. /**
  732. * Scan for root element hook. This method is a hook for
  733. * subclasses to add code that handles scanning for the root
  734. * element. This method will also attempt to remove DTD validator
  735. * from the pipeline, if there is no DTD grammar. If DTD validator
  736. * is no longer in the pipeline bind namespaces in the scanner.
  737. *
  738. *
  739. * @return True if the caller should stop and return true which
  740. * allows the scanner to switch to a new scanning
  741. * dispatcher. A return value of false indicates that
  742. * the content dispatcher should continue as normal.
  743. */
  744. protected boolean scanRootElementHook()
  745. throws IOException, XNIException {
  746. if (fExternalSubsetResolver != null && !fSeenDoctypeDecl
  747. && !fDisallowDoctype && (fValidation || fLoadExternalDTD)) {
  748. scanStartElementName();
  749. resolveExternalSubsetAndRead();
  750. reconfigurePipeline();
  751. if (scanStartElementAfterName()) {
  752. setScannerState(SCANNER_STATE_TRAILING_MISC);
  753. setDispatcher(fTrailingMiscDispatcher);
  754. return true;
  755. }
  756. }
  757. else {
  758. reconfigurePipeline();
  759. if (scanStartElement()) {
  760. setScannerState(SCANNER_STATE_TRAILING_MISC);
  761. setDispatcher(fTrailingMiscDispatcher);
  762. return true;
  763. }
  764. }
  765. return false;
  766. } // scanRootElementHook():boolean
  767. /**
  768. * Re-configures pipeline by removing the DTD validator
  769. * if no DTD grammar exists. If no validator exists in the
  770. * pipeline or there is no DTD grammar, namespace binding
  771. * is performed by the scanner in the enclosing class.
  772. */
  773. private void reconfigurePipeline() {
  774. if (fDTDValidator == null) {
  775. fBindNamespaces = true;
  776. }
  777. else if (!fDTDValidator.hasGrammar()) {
  778. fBindNamespaces = true;
  779. fPerformValidation = fDTDValidator.validate();
  780. // re-configure pipeline
  781. XMLDocumentSource source = fDTDValidator.getDocumentSource();
  782. XMLDocumentHandler handler = fDTDValidator.getDocumentHandler();
  783. source.setDocumentHandler(handler);
  784. if (handler != null)
  785. handler.setDocumentSource(source);
  786. fDTDValidator.setDocumentSource(null);
  787. fDTDValidator.setDocumentHandler(null);
  788. }
  789. } // reconfigurePipeline()
  790. }
  791. }