1 23 24 package org.enhydra.xml.io; 25 26 import java.io.IOException ; 27 28 import org.enhydra.xml.dom.DOMAccess; 29 import org.enhydra.xml.dom.DOMOps; 30 import org.w3c.dom.Attr ; 31 import org.w3c.dom.CDATASection ; 32 import org.w3c.dom.Document ; 33 import org.w3c.dom.DocumentFragment ; 34 import org.w3c.dom.DocumentType ; 35 import org.w3c.dom.Element ; 36 import org.w3c.dom.Node ; 37 import org.w3c.dom.ProcessingInstruction ; 38 import org.w3c.dom.Text ; 39 40 47 48 51 final class XMLFormatter extends BaseDOMFormatter implements Formatter { 52 55 private static final String DEFAULT_XML_ENCODING = "UTF-8"; 56 57 60 private static final String XML_VERSION = "1.0"; 61 62 66 private static final boolean[] fEntityQuickCheck 67 = new boolean[MAX_ENTITY_QUICK_CHECK_CHAR+1]; 68 69 72 private boolean fHandleText = false; 73 74 77 private boolean fNextSiblingText = false; 78 79 82 static { 83 fEntityQuickCheck['<'] = true; 84 fEntityQuickCheck['>'] = true; 85 fEntityQuickCheck['"'] = true; 86 fEntityQuickCheck['\''] = true; 87 fEntityQuickCheck['&'] = true; 88 } 89 90 93 public XMLFormatter(Node node, 94 OutputOptions outputOptions, 95 boolean forPreFormatting) { 96 super(node, outputOptions, forPreFormatting, DEFAULT_XML_ENCODING, fEntityQuickCheck); 97 } 98 99 104 static OutputOptions getDefaultOutputOptions() { 105 return new OutputOptions(); } 107 108 111 protected final String getCharacterEntity(char textChar) { 112 switch (textChar) { 113 case '<': 114 return "lt"; 115 case '>': 116 return "gt"; 117 case '"': 118 return "quot"; 119 case '\'': 120 return "apos"; 121 case '&': 122 return "amp"; 123 default: 124 return null; 125 } 126 } 127 128 131 private void writeXMLHeader(Document document) throws IOException { 132 fOut.write("<?xml version=\""); 133 fOut.write(XML_VERSION); 134 fOut.write("\""); 135 136 if (!fOptions.getOmitEncoding()) { 138 fOut.write(" encoding=\""); 139 fOut.write(getMIMEEncoding()); 140 fOut.write('"'); 141 } 142 143 try { 146 if (DOMOps.getStandalone(document)) { 147 fOut.write(" standalone=\"yes\""); 148 } 149 } catch (UnsupportedOperationException e) { 150 } 152 153 fOut.write("?>"); 154 writeln(); 155 } 156 157 161 public void handleDocument(Document document) throws IOException { 162 if (!fOptions.getOmitXMLHeader()) { 163 writeXMLHeader(document); 164 } 165 fTraverser.processDocumentType(document); 166 fTraverser.processChildren(document); 167 } 168 169 173 public void handleDocumentType(DocumentType documentType) throws IOException { 174 if (fOptions.getOmitDocType()) { 175 return; } 177 String internalSubset = documentType.getInternalSubset(); 178 if ((fPublicId == null) && (fSystemId == null) 179 && (internalSubset == null)) { 180 return; } 182 Element docElement = fDocument.getDocumentElement(); 183 if (docElement == null) { 184 throw new XMLIOError("Document has DocumentType, with out having a root element"); 185 } 186 187 fOut.write("<!DOCTYPE "); 188 fOut.write(docElement.getTagName()); 189 190 if (fPublicId != null) { 191 if (fSystemId == null) { 193 throw new XMLIOError("No SYSTEM id to accompany PUBLIC id: " 194 + fPublicId); 195 } 196 fOut.write(" PUBLIC \""); 197 fOut.write(fPublicId); 198 fOut.write("\" \""); 199 fOut.write(fSystemId); 200 fOut.write("\""); 201 } else if (fSystemId != null) { 202 fOut.write(" SYSTEM \""); 203 fOut.write(fSystemId); 204 fOut.write("\""); 205 } 206 207 if (internalSubset != null) { 208 writeln(); 209 fOut.write(" ["); 210 fOut.write(internalSubset); 211 fOut.write("]"); 212 } 213 fOut.write('>'); 214 writeln(); 215 } 216 217 221 public void handleDocumentFragment(DocumentFragment documentFragment) { 222 fTraverser.processChildren(documentFragment); 223 } 224 225 229 public void handleAttr(Attr attr) throws IOException { 230 fOut.write(' '); 231 fOut.write(attr.getName()); 232 writeAttributeValue(attr); 233 } 234 235 238 protected final void writeOpenTag(Element element, 239 String tagName, 240 boolean hasChildren) throws IOException { 241 String formattedTag = null; 242 if (fPrettyPrinting) { 243 if (fNextSiblingText) { 244 fOut.write('\n'); 245 } 246 fNextSiblingText = (element.getNextSibling() instanceof Text ); 247 } 249 if (fUsePreFormattedElements && (element instanceof PreFormattedText)) { 250 formattedTag = ((PreFormattedText)element).getPreFormattedText(); 251 } 252 if (formattedTag != null) { 253 fOut.write(formattedTag); 254 fPreFormattedElementCount++; 255 } else { 256 if (fPrettyPrinting && !(element.getPreviousSibling() instanceof Text )) { 257 printIndent(); 258 } 260 fOut.write('<'); 261 fOut.write(tagName); 262 fTraverser.processAttributes(element); 263 if (!hasChildren && !(fOptions.getEnableXHTMLCompatibility() && !isXHTMLContentModelEmpty(tagName))) { 264 if (fOptions.getEnableXHTMLCompatibility()) fOut.write(" /"); 266 else fOut.write('/'); 267 } 268 fOut.write('>'); 269 fDynamicFormattedElementCount++; 270 if (fPrettyPrinting && !(element.getFirstChild() instanceof Text )) { 271 fOut.write('\n'); 272 } } 274 } 275 276 280 public void handleElement(Element element) throws IOException { 281 String tagName = element.getTagName(); 282 283 Attr attr = DOMAccess.accessAttribute(fDocument, element, null, "visdom"); 291 if (attr != null && !(Boolean.valueOf(attr.getValue()).booleanValue())) return; 292 294 boolean hasChildren = element.hasChildNodes(); 295 296 writeOpenTag(element, tagName, hasChildren); 297 298 if (hasChildren || (fOptions.getEnableXHTMLCompatibility() && !isXHTMLContentModelEmpty(tagName))) { 300 fTraverser.processChildren(element); 301 302 if (fHandleText) { 303 fHandleText = false; 304 } else { 305 printIndent(); 306 } 308 fOut.write("</"); 309 fOut.write(tagName); 310 fOut.write('>'); 311 312 if (fPrettyPrinting && !fNextSiblingText) { 313 fOut.write('\n'); 314 } } 316 } 317 318 322 public void handleProcessingInstruction(ProcessingInstruction pi) throws IOException { 323 fOut.write("<?"); 324 fOut.write(pi.getTarget()); 325 String data = pi.getData(); 326 if (data != null) { 327 fOut.write(' '); 328 fOut.write(data); 329 } 330 fOut.write("?>"); 331 } 332 333 337 public void handleCDATASection(CDATASection cdata) throws IOException { 338 fOut.write("<![CDATA["); 340 fOut.write(cdata.getNodeValue()); 341 fOut.write("]]>"); 342 } 343 344 348 public final void handleText(Text text) throws IOException { 349 fHandleText = true; 350 super.handleText(text); 351 } 352 353 364 private static boolean isXHTMLContentModelEmpty(String tagName) { 365 String [] emptyTags = {"br","area","link","img","param","hr","input","col","base","meta"}; 366 boolean isEmptyTag = false; 367 for (int i = 0; i < emptyTags.length; i++) { 368 if (emptyTags[i].equalsIgnoreCase(tagName)) { 369 isEmptyTag = true; 370 break; 371 } 372 } 373 return isEmptyTag; 374 } 375 376 } 377 | Popular Tags |