📄 xmlwriter.java
字号:
* output more or less readable. * * <p> At this writing, structural indentation and line wrapping are * enabled when pretty printing is enabled and the <em>xml:space</em> * attribute has the value <em>default</em> (its other legal value is * <em>preserve</em>, as defined in the XML specification). The three * XHTML element types which use another value are recognized by their * names (namespaces are ignored). * * <p> Also, for the record, the "pretty" aspect of printing here * is more to provide basic structure on outputs that would otherwise * risk being a single long line of text. For now, expect the * structure to be ragged ... unless you'd like to submit a patch * to make this be more strictly formatted! * * @exception IllegalStateException thrown if this method is invoked * after output has begun. */ final public void setPrettyPrinting (boolean value) { if (locator != null) throw new IllegalStateException ("started parsing"); prettyPrinting = value; if (prettyPrinting) canonical = false; } /** * Returns value of flag controlling pretty printing. */ final public boolean isPrettyPrinting () { return prettyPrinting; } /** * Sets the output style to be canonicalized. Input events must * meet requirements that are slightly more stringent than the * basic well-formedness ones, and include: <ul> * * <li> Namespace prefixes must not have been changed from those * in the original document. (This may only be ensured by setting * the SAX2 XMLReader <em>namespace-prefixes</em> feature flag; * by default, it is cleared.) * * <li> Redundant namespace declaration attributes have been * removed. (If an ancestor element defines a namespace prefix * and that declaration hasn't been overriden, an element must * not redeclare it.) * * <li> If comments are not to be included in the canonical output, * they must first be removed from the input event stream; this * <em>Canonical XML with comments</em> by default. * * <li> If the input character encoding was not UCS-based, the * character data must have been normalized using Unicode * Normalization Form C. (UTF-8 and UTF-16 are UCS-based.) * * <li> Attribute values must have been normalized, as is done * by any conformant XML processor which processes all external * parameter entities. * * <li> Similarly, attribute value defaulting has been performed. * * </ul> * * <p> Note that fragments of XML documents, as specified by an XPath * node set, may be canonicalized. In such cases, elements may need * some fixup (for <em>xml:*</em> attributes and application-specific * context). * * @exception IllegalArgumentException if the output encoding * is anything other than UTF-8. */ final public void setCanonical (boolean value) { if (value && !"UTF-8".equals (encoding)) throw new IllegalArgumentException ("encoding != UTF-8"); canonical = value; if (canonical) { prettyPrinting = xhtml = false; expandingEntities = true; eol = "\n"; } } /** * Returns value of flag controlling canonical output. */ final public boolean isCanonical () { return canonical; } /** * Flushes the output stream. When this handler is used in long lived * pipelines, it can be important to flush buffered state, for example * so that it can reach the disk as part of a state checkpoint. */ final public void flush () throws IOException { if (out != null) out.flush (); } // convenience routines// FIXME: probably want a subclass that holds a lot of these...// and maybe more! /** * Writes the string as if characters() had been called on the contents * of the string. This is particularly useful when applications act as * producers and write data directly to event consumers. */ final public void write (String data) throws SAXException { char buf [] = data.toCharArray (); characters (buf, 0, buf.length); } /** * Writes an element that has content consisting of a single string. * @see #writeEmptyElement * @see #startElement */ public void writeElement ( String uri, String localName, String qName, Attributes atts, String content ) throws SAXException { if (content == null || content.length () == 0) { writeEmptyElement (uri, localName, qName, atts); return; } startElement (uri, localName, qName, atts); char chars [] = content.toCharArray (); characters (chars, 0, chars.length); endElement (uri, localName, qName); } /** * Writes an element that has content consisting of a single integer, * encoded as a decimal string. * @see #writeEmptyElement * @see #startElement */ public void writeElement ( String uri, String localName, String qName, Attributes atts, int content ) throws SAXException { writeElement (uri, localName, qName, atts, Integer.toString (content)); } // SAX1 ContentHandler /** <b>SAX1</b>: provides parser status information */ final public void setDocumentLocator (Locator l) { locator = l; } // URL for dtd that validates against all normal HTML constructs private static final String xhtmlFullDTD = "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"; /** * <b>SAX1</b>: indicates the beginning of a document parse. * If you're writing (well formed) fragments of XML, neither * this nor endDocument should be called. */ // NOT final public void startDocument () throws SAXException { try { if (out == null) throw new IllegalStateException ( "null Writer given to XMLWriter"); // Not all parsers provide the locator we want; this also // flags whether events are being sent to this object yet. // We could only have this one call if we only printed whole // documents ... but we also print fragments, so most of the // callbacks here replicate this test. if (locator == null) locator = new LocatorImpl (); // Unless the data is in US-ASCII or we're canonicalizing, write // the XML declaration if we know the encoding. US-ASCII won't // normally get mangled by web server confusion about the // character encodings used. Plus, it's an easy way to // ensure we can write ASCII that's unlikely to confuse // elderly HTML parsers. if (!canonical && dangerMask != (short) 0xff80 && encoding != null) { rawWrite ("<?xml version='1.0'"); rawWrite (" encoding='" + encoding + "'"); rawWrite ("?>"); newline (); } if (xhtml) { rawWrite ("<!DOCTYPE html PUBLIC"); newline (); rawWrite (" '-//W3C//DTD XHTML 1.0 Transitional//EN'"); newline (); rawWrite (" '"); // NOTE: URL (above) matches the REC rawWrite (xhtmlFullDTD); rawWrite ("'>"); newline (); newline (); // fake the rest of the handler into ignoring // everything until the root element, so any // XHTML DTD comments, PIs, etc are ignored startedDoctype = true; } entityNestLevel = 0; } catch (IOException e) { fatal ("can't write", e); } } /** * <b>SAX1</b>: indicates the completion of a parse. * Note that all complete SAX event streams make this call, even * if an error is reported during a parse. */ // NOT final public void endDocument () throws SAXException { try { if (!canonical) { newline (); newline (); } out.close (); out = null; locator = null; } catch (IOException e) { fatal ("can't write", e); } } // XHTML elements declared as EMPTY print differently final private static boolean isEmptyElementTag (String tag) { switch (tag.charAt (0)) { case 'a': return "area".equals (tag); case 'b': return "base".equals (tag) || "basefont".equals (tag) || "br".equals (tag); case 'c': return "col".equals (tag); case 'f': return "frame".equals (tag); case 'h': return "hr".equals (tag); case 'i': return "img".equals (tag) || "input".equals (tag) || "isindex".equals (tag); case 'l': return "link".equals (tag); case 'm': return "meta".equals (tag); case 'p': return "param".equals (tag); } return false; } private static boolean indentBefore (String tag) { // basically indent before block content // and within structure like tables, lists switch (tag.charAt (0)) { case 'a': return "applet".equals (tag); case 'b': return "body".equals (tag) || "blockquote".equals (tag); case 'c': return "center".equals (tag); case 'f': return "frame".equals (tag) || "frameset".equals (tag); case 'h': return "head".equals (tag); case 'm': return "meta".equals (tag); case 'o': return "object".equals (tag); case 'p': return "param".equals (tag) || "pre".equals (tag); case 's': return "style".equals (tag); case 't': return "title".equals (tag) || "td".equals (tag) || "th".equals (tag); } // ... but not inline elements like "em", "b", "font" return false; } private static boolean spaceBefore (String tag) { // blank line AND INDENT before certain structural content switch (tag.charAt (0)) { case 'h': return "h1".equals (tag) || "h2".equals (tag) || "h3".equals (tag) || "h4".equals (tag) || "h5".equals (tag) || "h6".equals (tag) || "hr".equals (tag); case 'l': return "li".equals (tag); case 'o': return "ol".equals (tag); case 'p': return "p".equals (tag); case 't': return "table".equals (tag) || "tr".equals (tag); case 'u': return "ul".equals (tag); } return false; } // XHTML DTDs say these three have xml:space="preserve" private static boolean spacePreserve (String tag) { return "pre".equals (tag) || "style".equals (tag) || "script".equals (tag); } /** * <b>SAX2</b>: ignored. */ final public void startPrefixMapping (String prefix, String uri) {} /** * <b>SAX2</b>: ignored. */ final public void endPrefixMapping (String prefix) {} private void writeStartTag ( String name, Attributes atts, boolean isEmpty ) throws SAXException, IOException { rawWrite ('<'); rawWrite (name); // write out attributes ... sorting is particularly useful // with output that's been heavily defaulted. if (atts != null && atts.getLength () != 0) { // Set up to write, with optional sorting int indices [] = new int [atts.getLength ()]; for (int i= 0; i < indices.length; i++) indices [i] = i; // optionally sort// FIXME: canon xml demands xmlns nodes go first,// and sorting by URI first (empty first) then localname// it should maybe use a different sort if (canonical || prettyPrinting) { // insertion sort by attribute name for (int i = 1; i < indices.length; i++) { int n = indices [i], j; String s = atts.getQName (n); for (j = i - 1; j >= 0; j--) { if (s.compareTo (atts.getQName (indices [j])) >= 0) break; indices [j + 1] = indices [j]; } indices [j + 1] = n; } } // write, sorted or no for (int i= 0; i < indices.length; i++) { String s = atts.getQName (indices [i]); if (s == null || "".equals (s)) throw new IllegalArgumentException ("no XML name"); rawWrite (" "); rawWrite (s); rawWrite ("="); writeQuotedValue (atts.getValue (indices [i]), CTX_ATTRIBUTE); } } if (isEmpty) rawWrite (" /"); rawWrite ('>'); } /** * <b>SAX2</b>: indicates the start of an element. * When XHTML is in use, avoid attribute values with * line breaks or multiple whitespace characters, since * not all user agents handle them correctly. */ final public void startElement ( String uri, String localName, String qName, Attributes atts ) throws SAXException { startedDoctype = false; if (locator == null) locator = new LocatorImpl (); if (qName == null || "".equals (qName)) throw new IllegalArgumentException ("no XML name"); try { if (entityNestLevel != 0) return; if (prettyPrinting) { String whitespace = null; if (xhtml && spacePreserve (qName)) whitespace = "preserve"; else if (atts != null) whitespace = atts.getValue ("xml:space"); if (whitespace == null) whitespace = (String) space.peek (); space.push (whitespace); if ("default".equals (whitespace)) { if (xhtml) { if (spaceBefore (qName)) { newline (); doIndent (); } else if (indentBefore (qName)) doIndent (); // else it's inlined, modulo line length // FIXME: incrementing element nest level // for inlined elements causes ugliness } else doIndent (); } } elementNestLevel++; writeStartTag (qName, atts, xhtml && isEmptyElementTag (qName)); if (xhtml) {// FIXME: if this is an XHTML "pre" element, turn// off automatic wrapping. } } catch (IOException e) { fatal ("can't write", e); } } /** * Writes an empty element. * @see #startElement */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -