1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 package org.openoffice.xmerge.converter.dom; 25 26 import java.io.InputStream; 27 import java.io.OutputStream; 28 import java.io.StringWriter; 29 import java.io.ByteArrayOutputStream; 30 import java.io.IOException; 31 32 33 import javax.xml.parsers.DocumentBuilderFactory; 34 import javax.xml.parsers.DocumentBuilder; 35 import javax.xml.parsers.ParserConfigurationException; 36 37 import javax.xml.transform.TransformerFactory; 38 import javax.xml.transform.Transformer; 39 import javax.xml.transform.stream.StreamResult; 40 import javax.xml.transform.dom.DOMSource; 41 42 import org.w3c.dom.Node; 43 import org.w3c.dom.Element; 44 import org.w3c.dom.Document; 45 import org.xml.sax.SAXException; 46 47 import org.openoffice.xmerge.util.Resources; 48 import org.openoffice.xmerge.util.Debug; 49 50 /** 51 * An implementation of <code>Document</code> for 52 * StarOffice documents. 53 */ 54 public class DOMDocument 55 implements org.openoffice.xmerge.Document { 56 57 /** Factory for <code>DocumentBuilder</code> objects. */ 58 private static DocumentBuilderFactory factory = 59 DocumentBuilderFactory.newInstance(); 60 61 /** DOM <code>Document</code> of content.xml. */ 62 private Document contentDoc = null; 63 64 private String documentName = null; 65 private String fileName = null; 66 private String fileExt = null; 67 68 /** Resources object. */ 69 private Resources res = null; 70 71 72 /** 73 * Default constructor. 74 * 75 * @param name <code>Document</code> name. 76 * @param ext <code>Document</code> extension. 77 */ DOMDocument(String name,String ext)78 public DOMDocument(String name,String ext) 79 { 80 this(name,ext,true, false); 81 } 82 83 /** 84 * Returns the file extension of the <code>Document</code> 85 * represented. 86 * 87 * @return file extension of the <code>Document</code>. 88 */ getFileExtension()89 protected String getFileExtension() { 90 return fileExt; 91 } 92 93 94 /** 95 * Constructor with arguments to set <code>namespaceAware</code> 96 * and <code>validating</code> flags. 97 * 98 * @param name <code>Document</code> name (may or may not 99 * contain extension). 100 * @param ext <code>Document</code> extension. 101 * @param namespaceAware Value for <code>namespaceAware</code> flag. 102 * @param validating Value for <code>validating</code> flag. 103 */ DOMDocument(String name, String ext,boolean namespaceAware, boolean validating)104 public DOMDocument(String name, String ext,boolean namespaceAware, boolean validating) { 105 106 res = Resources.getInstance(); 107 factory.setValidating(validating); 108 factory.setNamespaceAware(namespaceAware); 109 this.fileExt = ext; 110 this.documentName = trimDocumentName(name); 111 this.fileName = documentName + getFileExtension(); 112 } 113 114 115 /** 116 * Removes the file extension from the <code>Document</code> 117 * name. 118 * 119 * @param name Full <code>Document</code> name with extension. 120 * 121 * @return Name of <code>Document</code> without the extension. 122 */ trimDocumentName(String name)123 private String trimDocumentName(String name) { 124 String temp = name.toLowerCase(); 125 String ext = getFileExtension(); 126 127 if (temp.endsWith(ext)) { 128 // strip the extension 129 int nlen = name.length(); 130 int endIndex = nlen - ext.length(); 131 name = name.substring(0,endIndex); 132 } 133 134 return name; 135 } 136 137 138 /** 139 * Return a DOM <code>Document</code> object of the document content 140 * file. Note that a content DOM is not created when the constructor 141 * is called. So, either the <code>read</code> method or the 142 * <code>initContentDOM</code> method will need to be called ahead 143 * on this object before calling this method. 144 * 145 * @return DOM <code>Document</code> object. 146 */ getContentDOM()147 public Document getContentDOM() { 148 149 return contentDoc; 150 } 151 152 /** 153 * Sets the Content of the <code>Document</code> to the contents of the 154 * supplied <code>Node</code> list. 155 * 156 * @return DOM <code>Document</code> object. 157 */ setContentDOM( Node newDom)158 public void setContentDOM( Node newDom) { 159 contentDoc=(Document)newDom; 160 } 161 162 163 /** 164 * Return the name of the <code>Document</code>. 165 * 166 * @return The name of <code>Document</code>. 167 */ getName()168 public String getName() { 169 170 return documentName; 171 } 172 173 174 /** 175 * Return the file name of the <code>Document</code>, possibly 176 * with the standard extension. 177 * 178 * @return The file name of <code>Document</code>. 179 */ getFileName()180 public String getFileName() { 181 182 return fileName; 183 } 184 185 186 /** 187 * Read the Office <code>Document</code> from the specified 188 * <code>InputStream</code>. 189 * 190 * @param is Office document <code>InputStream</code>. 191 * 192 * @throws IOException If any I/O error occurs. 193 */ read(InputStream is)194 public void read(InputStream is) throws IOException { 195 Debug.log(Debug.INFO, "reading file"); 196 DocumentBuilder builder = null; 197 try { 198 builder = factory.newDocumentBuilder(); 199 } catch (ParserConfigurationException ex) { 200 System.out.println("Error:"+ ex); 201 //throw new OfficeDocumentException(ex); 202 } 203 try { 204 205 contentDoc= builder.parse(is); 206 207 208 } catch (SAXException ex) { 209 System.out.println("Error:"+ ex); 210 //throw new OfficeDocumentException(ex); 211 } 212 } 213 214 215 /** 216 * Write out content to the supplied <code>OutputStream</code>. 217 * 218 * @param os XML <code>OutputStream</code>. 219 * 220 * @throws IOException If any I/O error occurs. 221 */ write(OutputStream os)222 public void write(OutputStream os) throws IOException { 223 224 // set bytes for writing to output stream 225 byte contentBytes[] = docToBytes(contentDoc); 226 227 os.write(contentBytes); 228 } 229 230 231 /** 232 * <p>Write out a <code>org.w3c.dom.Document</code> object into a 233 * <code>byte</code> array.</p> 234 * 235 * <p>TODO: remove dependency on com.sun.xml.tree.XmlDocument 236 * package!</p> 237 * 238 * @param doc DOM <code>Document</code> object. 239 * 240 * @return <code>byte</code> array of DOM <code>Document</code> 241 * object. 242 * 243 * @throws IOException If any I/O error occurs. 244 */ docToBytes(Document doc)245 private byte[] docToBytes(Document doc) 246 throws IOException { 247 248 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 249 250 java.lang.reflect.Constructor con; 251 java.lang.reflect.Method meth; 252 253 String domImpl = doc.getClass().getName(); 254 255 System.err.println("type b " + domImpl); 256 257 /* 258 * We may have multiple XML parsers in the Classpath. 259 * Depending on which one is first, the actual type of 260 * doc may vary. Need a way to find out which API is being 261 * used and use an appropriate serialization method. 262 */ 263 try { 264 // First of all try for JAXP 1.0 265 if (domImpl.equals("com.sun.xml.tree.XmlDocument")) { 266 System.out.println("Using JAXP"); 267 Class jaxpDoc = Class.forName("com.sun.xml.tree.XmlDocument"); 268 269 // The method is in the XMLDocument class itself, not a helper 270 meth = jaxpDoc.getMethod("write", 271 new Class[] { Class.forName("java.io.OutputStream") } ); 272 273 meth.invoke(doc, new Object [] { baos } ); 274 } 275 else if (domImpl.equals("org.apache.crimson.tree.XmlDocument")) 276 { 277 System.out.println("Using Crimson"); 278 Class crimsonDoc = Class.forName("org.apache.crimson.tree.XmlDocument"); 279 // The method is in the XMLDocument class itself, not a helper 280 meth = crimsonDoc.getMethod("write", 281 new Class[] { Class.forName("java.io.OutputStream") } ); 282 283 meth.invoke(doc, new Object [] { baos } ); 284 } 285 else if (domImpl.equals("org.apache.xerces.dom.DocumentImpl") 286 || domImpl.equals("org.apache.xerces.dom.DeferredDocumentImpl")) { 287 System.out.println("Using Xerces"); 288 // Try for Xerces 289 Class xercesSer = 290 Class.forName("org.apache.xml.serialize.XMLSerializer"); 291 292 // Get the OutputStream constructor 293 // May want to use the OutputFormat parameter at some stage too 294 con = xercesSer.getConstructor(new Class [] 295 { Class.forName("java.io.OutputStream"), 296 Class.forName("org.apache.xml.serialize.OutputFormat") } ); 297 298 299 // Get the serialize method 300 meth = xercesSer.getMethod("serialize", 301 new Class [] { Class.forName("org.w3c.dom.Document") } ); 302 303 304 // Get an instance 305 Object serializer = con.newInstance(new Object [] { baos, null } ); 306 307 308 // Now call serialize to write the document 309 meth.invoke(serializer, new Object [] { doc } ); 310 } 311 else if (domImpl.equals("gnu.xml.dom.DomDocument")) { 312 System.out.println("Using GNU"); 313 314 Class gnuSer = Class.forName("gnu.xml.dom.ls.DomLSSerializer"); 315 316 // Get the serialize method 317 meth = gnuSer.getMethod("serialize", 318 new Class [] { Class.forName("org.w3c.dom.Node"), 319 Class.forName("java.io.OutputStream") } ); 320 321 // Get an instance 322 Object serializer = gnuSer.newInstance(); 323 324 // Now call serialize to write the document 325 meth.invoke(serializer, new Object [] { doc, baos } ); 326 } 327 else { 328 // We dont have another parser 329 try { 330 DOMSource domSource = new DOMSource(doc); 331 StringWriter writer = new StringWriter(); 332 StreamResult result = new StreamResult(writer); 333 TransformerFactory tf = TransformerFactory.newInstance(); 334 Transformer transformer = tf.newTransformer(); 335 transformer.transform(domSource, result); 336 return writer.toString().getBytes(); 337 } 338 catch (Exception e) { 339 // We don't have another parser 340 throw new IOException("No appropriate API (JAXP/Xerces) to serialize XML document: " + domImpl); 341 } 342 } 343 } 344 catch (ClassNotFoundException cnfe) { 345 throw new IOException(cnfe.toString()); 346 } 347 catch (Exception e) { 348 // We may get some other errors, but the bottom line is that 349 // the steps being executed no longer work 350 throw new IOException(e.toString()); 351 } 352 353 byte bytes[] = baos.toByteArray(); 354 355 return bytes; 356 } 357 358 359 /** 360 * Initializes a new DOM <code>Document</code> with the content 361 * containing minimum XML tags. 362 * 363 * @throws IOException If any I/O error occurs. 364 */ initContentDOM()365 public final void initContentDOM() throws IOException { 366 contentDoc = createDOM(""); 367 368 } 369 370 /** 371 * <p>Creates a new DOM <code>Document</code> containing minimum 372 * OpenOffice.org XML tags.</p> 373 * 374 * <p>This method uses the subclass 375 * <code>getOfficeClassAttribute</code> method to get the 376 * attribute for <i>office:class</i>.</p> 377 * 378 * @param rootName root name of <code>Document</code>. 379 * 380 * @throws IOException If any I/O error occurs. 381 */ createDOM(String rootName)382 private final Document createDOM(String rootName) throws IOException { 383 384 Document doc = null; 385 386 try { 387 388 DocumentBuilder builder = factory.newDocumentBuilder(); 389 doc = builder.newDocument(); 390 391 } catch (ParserConfigurationException ex) { 392 System.out.println("Error:"+ ex); 393 394 395 } 396 397 Element root = (Element) doc.createElement(rootName); 398 doc.appendChild(root); 399 400 401 return doc; 402 } 403 404 } 405 406 407 408 409