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 package com.sun.star.comp.xsltfilter; 23 24 25 26 //Standard Java classes 27 import java.io.FileWriter; 28 import java.util.zip.Inflater; 29 import java.util.zip.Deflater; 30 31 //StarOffice Interfaces and UNO 32 import com.sun.star.bridge.XBridgeFactory; 33 import com.sun.star.bridge.XBridge; 34 import com.sun.star.connection.XConnector; 35 import com.sun.star.connection.XConnection; 36 import com.sun.star.container.XNameContainer; 37 import com.sun.star.embed.XTransactedObject; 38 import com.sun.star.io.XStream; 39 import com.sun.star.io.XSeekable; 40 import com.sun.star.io.XInputStream; 41 import com.sun.star.io.XOutputStream; 42 import com.sun.star.lang.XMultiServiceFactory; 43 import com.sun.star.lang.XComponent; 44 import com.sun.star.uno.XComponentContext; 45 import com.sun.star.uno.UnoRuntime; 46 47 /** This class is an xalan extension class. It provider 2 elements 48 * and 2 functions to used in xslt script. With this elements and functions 49 * we can convert between oledata between Wordml and OOo flat. 50 * To use it, we need a running OOo. There are two ways to get the XMultiServiceFactory. 51 * When called by OOo xslt filter, an XMultiServiceFactory will be add to the transformer 52 * by setParameter(), then we can get it using getParameter(). Another way is using an 53 * XConnection to connect to a running OOo. We connect to a running OOo, we need know the 54 * uno url. It can be set in the xslt script. The default uno url is: 55 * "uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager" 56 * see XSLTXalanOLEExtracter.java 57 */ 58 public class XSLTFilterOLEExtracter { 59 60 protected XMultiServiceFactory m_xMSF; 61 protected XNameContainer m_Storage; 62 protected XStream m_RootStream; 63 protected XConnection m_Connection; 64 protected String sConnectionString; 65 private static final String UNO_URL = "uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager"; 66 XSLTFilterOLEExtracter()67 public XSLTFilterOLEExtracter() { 68 } 69 init(String unoUrl)70 public void init(String unoUrl) { 71 if (unoUrl == null || unoUrl.equals("")) { 72 unoUrl = UNO_URL; 73 } 74 debugln("Init with uno url=" + unoUrl); 75 if (null == m_xMSF) { 76 try { 77 m_xMSF = connectAwareGetServiceFactory(); 78 } catch (Exception ex) { 79 System.err.println("Could not connect to the office '" + unoUrl + "'\n" + ex.getMessage()); 80 } 81 } 82 } 83 exit()84 public void exit() { 85 m_Storage = null; 86 m_xMSF = null; 87 if (null != m_Connection) { 88 try { 89 m_Connection.close(); 90 } catch (Exception ex) { 91 System.err.println("Could not close connection to the office.\n" + ex.getMessage()); 92 } 93 } 94 } 95 //If aName = "oledata.mso" then we load the root storage from the given base64 string 96 //Otherwise we compress the stream and add it to the root storage under the name of aName insertByName(String aName, String aBase64)97 public void insertByName(String aName, String aBase64) { 98 debugln("insertByName(" + aName + " : " + aBase64 + ")"); 99 if (aName.equals("oledata.mso")) { 100 loadRootStorageFromBase64(aBase64); 101 } else { 102 ensureCreateRootStorage(); 103 insertSubStorage(aName, aBase64); 104 } 105 } 106 //If aName = "oledata.mso" then we return the base64 encoded string of the root storage 107 //Otherwise we return the base64 encoded string of the sub stream under the name of aName getByName(String aName)108 public String getByName(String aName) { 109 if (aName.equals("oledata.mso")) { 110 try { 111 //get the length and seek to 0 112 XSeekable xSeek = (XSeekable) UnoRuntime.queryInterface(XSeekable.class, m_RootStream); 113 int oleLength = (int) xSeek.getLength(); 114 xSeek.seek(0); 115 xSeek = null; 116 //read all bytes 117 XInputStream xInput = m_RootStream.getInputStream(); 118 byte oledata[][] = new byte[1][oleLength]; 119 xInput.readBytes(oledata, oleLength); 120 //return the base64 encoded string 121 return Base64.encodeBytes(oledata[0]); 122 } catch (Exception ex) { 123 ex.printStackTrace(); 124 } 125 } else { 126 return getEncodedSubStorage(aName); 127 } 128 return ""; 129 } 130 //get the sub stream which name = aName, decompress it and return the base64 encoded string getEncodedSubStorage(String aName)131 public String getEncodedSubStorage(String aName) { 132 debugln("getByName(" + aName + ")"); 133 try { 134 if (!m_Storage.hasByName(aName)) { 135 return "Not Found:" + aName; 136 } 137 Object oSubStream = m_Storage.getByName(aName); 138 if (oSubStream == null) { 139 return "Not Found:" + aName; 140 } 141 XInputStream xSubStream = (XInputStream) UnoRuntime.queryInterface(XInputStream.class, 142 oSubStream); 143 if (xSubStream == null) { 144 return "Not Found:" + aName; 145 } 146 //The first four byte are the length of the uncompressed data 147 byte pLength[][] = new byte[1][4]; 148 XSeekable xSeek = (XSeekable) UnoRuntime.queryInterface(XSeekable.class, xSubStream); 149 xSeek.seek(0); 150 xSeek = null; 151 //Get the uncompressed length 152 int readbytes = xSubStream.readBytes(pLength, 4); 153 if (4 != readbytes) { 154 System.out.println("readbytes:" + readbytes); 155 return "Can not read the length."; 156 } 157 int oleLength = (pLength[0][0] << 0) + (pLength[0][1] << 8) + (pLength[0][2] << 16) + (pLength[0][3] << 24); 158 byte pContents[][] = new byte[1][oleLength]; 159 //Read all bytes. The compressed length should less then the uncompressed length 160 readbytes = xSubStream.readBytes(pContents, oleLength); 161 if (oleLength < readbytes) { 162 return "oleLength :" + oleLength + " readbytes: " + readbytes; 163 } 164 165 // Decompress the bytes 166 Inflater decompresser = new Inflater(); 167 decompresser.setInput(pContents[0], 0, readbytes); 168 byte[] result = new byte[oleLength]; 169 int resultLength = decompresser.inflate(result); 170 decompresser.end(); 171 172 //return the base64 string of the uncompressed data 173 return Base64.encodeBytes(result); 174 } catch (Exception ex) { 175 ex.printStackTrace(); 176 } 177 return ""; 178 } 179 CreateTempFileStream(XMultiServiceFactory xMSF)180 public XStream CreateTempFileStream(XMultiServiceFactory xMSF) { 181 // try to get temporary file representation 182 XStream xTempFileStream = null; 183 try { 184 Object oTempFile = xMSF.createInstance("com.sun.star.io.TempFile"); 185 xTempFileStream = (XStream) UnoRuntime.queryInterface(XStream.class, oTempFile); 186 } catch (Exception e) { 187 } 188 189 if (xTempFileStream == null) { 190 System.out.println("Can't create temporary file!"); 191 } 192 193 return xTempFileStream; 194 } 195 //decode the base64 string and create an com.sun.star.embed.OLESimpleStorage from it loadRootStorageFromBase64(String aBase64)196 public void loadRootStorageFromBase64(String aBase64) { 197 try { 198 //Decode and write the data to an temp stream 199 byte[] oledata = Base64.decode(aBase64); 200 m_RootStream = CreateTempFileStream(m_xMSF); 201 XOutputStream xOutput = m_RootStream.getOutputStream(); 202 xOutput.writeBytes(oledata); 203 xOutput.flush(); 204 //Get the input stream and seek to begin 205 XInputStream xInput = m_RootStream.getInputStream(); 206 XSeekable xSeek = (XSeekable) UnoRuntime.queryInterface(XSeekable.class, xInput); 207 xSeek.seek(0); 208 oledata = null; 209 xSeek = null; 210 211 //create an com.sun.star.embed.OLESimpleStorage from the temp stream 212 Object pArgs[] = new Object[1]; 213 pArgs[0] = (Object) xInput; 214 Object oTempStorage = m_xMSF.createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", pArgs); 215 pArgs = null; 216 217 m_Storage = (XNameContainer) UnoRuntime.queryInterface(XNameContainer.class, oTempStorage); 218 } catch (Exception e) { 219 e.printStackTrace(); 220 } 221 } 222 //Create a empty OLESimpleStorage if there is not one ensureCreateRootStorage()223 public void ensureCreateRootStorage() { 224 if (null == m_RootStream || null == m_Storage) { 225 try { 226 m_RootStream = CreateTempFileStream(m_xMSF); 227 228 Object pArgs[] = new Object[1]; 229 pArgs[0] = (Object) m_RootStream; 230 Object oTempStorage = m_xMSF.createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", pArgs); 231 pArgs = null; 232 233 m_Storage = (XNameContainer) UnoRuntime.queryInterface(XNameContainer.class, oTempStorage); 234 } catch (Exception e) { 235 e.printStackTrace(); 236 } 237 } 238 } 239 //decode the base64 string and insert the length and the compressed data of it to 240 //the root storage as a sub stream under aName insertSubStorage(String aName, String aBase64)241 public void insertSubStorage(String aName, String aBase64) { 242 try { 243 //decode the base64 string 244 byte[] oledata = Base64.decode(aBase64); 245 //create a temp stream to write data to 246 XStream subStream = CreateTempFileStream(m_xMSF); 247 XInputStream xInput = subStream.getInputStream(); 248 XOutputStream xOutput = subStream.getOutputStream(); 249 //write the length to the temp stream 250 byte oleHead[] = new byte[4]; 251 oleHead[0] = (byte) ((oledata.length >>> 0) & 0xFF); 252 oleHead[1] = (byte) ((oledata.length >>> 8) & 0xFF); 253 oleHead[2] = (byte) ((oledata.length >>> 16) & 0xFF); 254 oleHead[3] = (byte) ((oledata.length >>> 24) & 0xFF); 255 xOutput.writeBytes(oleHead); 256 257 // Compress the bytes 258 byte[] output = new byte[oledata.length]; 259 Deflater compresser = new Deflater(); 260 compresser.setInput(oledata); 261 compresser.finish(); 262 int compressedDataLength = compresser.deflate(output); 263 //realloc the data length 264 byte[] compressedBytes = new byte[compressedDataLength]; 265 for (int i = 0; i < compressedDataLength; i++) { 266 compressedBytes[i] = output[i]; 267 } 268 269 //write the compressed data to the temp stream 270 xOutput.writeBytes(compressedBytes); 271 //seek to 0 272 XSeekable xSeek = (XSeekable) UnoRuntime.queryInterface(XSeekable.class, xInput); 273 xSeek.seek(0); 274 xSeek = null; 275 oledata = null; 276 277 //insert the temp stream as a sub stream and use an XTransactedObject to commit it immediately 278 XTransactedObject xTransact = (XTransactedObject) UnoRuntime.queryInterface(XTransactedObject.class, m_Storage); 279 m_Storage.insertByName(aName, xInput); 280 xTransact.commit(); 281 xTransact = null; 282 283 } catch (Exception e) { 284 e.printStackTrace(); 285 } 286 } 287 288 /** separtates the uno-url into 3 different parts. 289 */ parseUnoUrl(String url)290 protected static String[] parseUnoUrl(String url) { 291 String[] aRet = new String[3]; 292 293 if (!url.startsWith("uno:")) { 294 return null; 295 } 296 297 int semicolon = url.indexOf(';'); 298 if (semicolon == -1) { 299 return null; 300 } 301 302 aRet[0] = url.substring(4, semicolon); 303 int nextSemicolon = url.indexOf(';', semicolon + 1); 304 305 if (semicolon == -1) { 306 return null; 307 } 308 aRet[1] = url.substring(semicolon + 1, nextSemicolon); 309 310 aRet[2] = url.substring(nextSemicolon + 1); 311 return aRet; 312 } 313 //connect to running OOo and keep an XConnection object so that we can disconnect from OOo as we wish connectAwareGetServiceFactory()314 protected XMultiServiceFactory connectAwareGetServiceFactory() throws com.sun.star.uno.Exception, 315 com.sun.star.uno.RuntimeException, 316 Exception { 317 318 // Get component context 319 XComponentContext xComponentContext = 320 com.sun.star.comp.helper.Bootstrap.createInitialComponentContext(null); 321 322 // instantiate connector service 323 Object x = xComponentContext.getServiceManager().createInstanceWithContext( 324 "com.sun.star.connection.Connector", xComponentContext); 325 326 XConnector xConnector = (XConnector) UnoRuntime.queryInterface(XConnector.class, x); 327 328 String a[] = parseUnoUrl(sConnectionString); 329 if (null == a) { 330 throw new com.sun.star.uno.Exception("Couldn't parse uno-url " + sConnectionString); 331 } 332 333 // connect using the connection string part of the uno-url only. 334 m_Connection = xConnector.connect(a[0]); 335 336 x = xComponentContext.getServiceManager().createInstanceWithContext( 337 "com.sun.star.bridge.BridgeFactory", xComponentContext); 338 339 XBridgeFactory xBridgeFactory = (XBridgeFactory) UnoRuntime.queryInterface( 340 XBridgeFactory.class, x); 341 342 // create a nameless bridge with no instance provider 343 // using the middle part of the uno-url 344 XBridge bridge = xBridgeFactory.createBridge("", a[1], m_Connection, null); 345 346 // query for the XComponent interface and add this as event listener 347 XComponent xComponent = (XComponent) UnoRuntime.queryInterface( 348 XComponent.class, bridge); 349 350 // get the remote instance 351 x = bridge.getInstance(a[2]); 352 353 // Did the remote server export this object ? 354 if (null == x) { 355 throw new com.sun.star.uno.Exception( 356 "Server didn't provide an instance for" + a[2], null); 357 } 358 359 XMultiServiceFactory xFac = (XMultiServiceFactory) UnoRuntime.queryInterface(XMultiServiceFactory.class, x); 360 return xFac; 361 } 362 protected static boolean DEBUG = false; 363 protected static boolean DEBUGCHK = false; 364 protected static String debugfile; 365 debugln(String s)366 protected static void debugln(String s) { 367 debug(s + "\n"); 368 } 369 debug(String s)370 protected static void debug(String s) { 371 if (!DEBUGCHK) { 372 if (System.getProperty("xsltfilter.debug") == null) { 373 DEBUGCHK = true; 374 return; 375 } else { 376 debugfile = System.getProperty("xsltfilter.debug"); 377 DEBUG = true; 378 } 379 } 380 if (!DEBUG) { 381 return; 382 } 383 try { 384 FileWriter dbgwriter = new FileWriter(debugfile, true); 385 dbgwriter.write(s); 386 dbgwriter.close(); 387 } catch (Exception e) { 388 e.printStackTrace(); 389 } 390 } 391 } 392