summaryrefslogtreecommitdiff
path: root/filter/source/xsltfilter/XSLTFilterOLEExtracter.java
blob: f91146b45bb9e81866e482473ab598c128c51fd9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
/************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

//Standard Java classes
import java.io.FileWriter;
import java.util.zip.Inflater;
import java.util.zip.Deflater;

//StarOffice Interfaces and UNO
import com.sun.star.bridge.XBridgeFactory;
import com.sun.star.bridge.XBridge;
import com.sun.star.connection.XConnector;
import com.sun.star.connection.XConnection;
import com.sun.star.container.XNameContainer;
import com.sun.star.embed.XTransactedObject;
import com.sun.star.io.XStream;
import com.sun.star.io.XSeekable;
import com.sun.star.io.XInputStream;
import com.sun.star.io.XOutputStream;
import com.sun.star.lang.XMultiServiceFactory;
import com.sun.star.lang.XComponent;
import com.sun.star.uno.XComponentContext;
import com.sun.star.uno.UnoRuntime;

/** This class is an xalan extension class. It provider 2 elements
 *  and 2 functions to used in xslt script. With this elements and functions
 *  we can convert between oledata between Wordml and OOo flat.
 *  To use it, we need a running OOo. There are two ways to get the XMultiServiceFactory.
 *  When called by OOo xslt filter, an XMultiServiceFactory will be add to the transformer
 *  by setParameter(), then we can get it using getParameter(). Another way is using an
 *  XConnection to connect to a running OOo. We connect to a running OOo, we need know the
 *  uno url. It can be set in the xslt script. The default uno url is:
 *  "uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager"
 *  see XSLTXalanOLEExtracter.java
 */
public class XSLTFilterOLEExtracter {

    protected XMultiServiceFactory m_xMSF;
    protected XNameContainer m_Storage;
    protected XStream m_RootStream;
    protected XConnection m_Connection;
    protected String sConnectionString;
    private static final String UNO_URL = "uno:socket,host=localhost,port=8100;urp;StarOffice.ServiceManager";

    public XSLTFilterOLEExtracter() {
    }

    public void init(String unoUrl) {
        if (unoUrl == null || unoUrl.equals("")) {
            unoUrl = UNO_URL;
        }
        debugln("Init with uno url=" + unoUrl);
        if (null == m_xMSF) {
            try {
                m_xMSF = connectAwareGetServiceFactory();
            } catch (Exception ex) {
                System.err.println("Could not connect to the office '" + unoUrl + "'\n" + ex.getMessage());
            }
        }
    }

    public void exit() {
        m_Storage = null;
        m_xMSF = null;
        if (null != m_Connection) {
            try {
                m_Connection.close();
            } catch (Exception ex) {
                System.err.println("Could not close connection to the office.\n" + ex.getMessage());
            }
        }
    }
    //If aName = "oledata.mso" then we load the root storage from the given base64 string
    //Otherwise we compress the stream and add it to the root storage under the name of aName
    public void insertByName(String aName, String aBase64) {
        debugln("insertByName(" + aName + " : " + aBase64 + ")");
        if (aName.equals("oledata.mso")) {
            loadRootStorageFromBase64(aBase64);
        } else {
            ensureCreateRootStorage();
            insertSubStorage(aName, aBase64);
        }
    }
    //If aName = "oledata.mso" then we return the base64 encoded string of the root storage
    //Otherwise we return the base64 encoded string of the sub stream under the name of aName
    public String getByName(String aName) {
        if (aName.equals("oledata.mso")) {
            try {
                //get the length and seek to 0
                XSeekable xSeek = (XSeekable) UnoRuntime.queryInterface(XSeekable.class, m_RootStream);
                int oleLength = (int) xSeek.getLength();
                xSeek.seek(0);
                xSeek = null;
                //read all bytes
                XInputStream xInput = m_RootStream.getInputStream();
                byte oledata[][] = new byte[1][oleLength];
                xInput.readBytes(oledata, oleLength);
                //return the base64 encoded string
                return Base64.encodeBytes(oledata[0]);
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        } else {
            return getEncodedSubStorage(aName);
        }
        return "";
    }
    //get the sub stream which name = aName, decompress it and return the base64 encoded string
    public String getEncodedSubStorage(String aName) {
        debugln("getByName(" + aName + ")");
        try {
            if (!m_Storage.hasByName(aName)) {
                return "Not Found:" + aName;
            }
            Object oSubStream = m_Storage.getByName(aName);
            if (oSubStream == null) {
                return "Not Found:" + aName;
            }
            XInputStream xSubStream = (XInputStream) UnoRuntime.queryInterface(XInputStream.class,
                    oSubStream);
            if (xSubStream == null) {
                return "Not Found:" + aName;
            }
            //The first four byte are the length of the uncompressed data
            byte pLength[][] = new byte[1][4];
            XSeekable xSeek = (XSeekable) UnoRuntime.queryInterface(XSeekable.class, xSubStream);
            xSeek.seek(0);
            xSeek = null;
            //Get the uncompressed length
            int readbytes = xSubStream.readBytes(pLength, 4);
            if (4 != readbytes) {
                System.out.println("readbytes:" + readbytes);
                return "Can not read the length.";
            }
            int oleLength = (pLength[0][0] << 0) + (pLength[0][1] << 8) + (pLength[0][2] << 16) + (pLength[0][3] << 24);
            byte pContents[][] = new byte[1][oleLength];
            //Read all bytes. The compressed length should less then the uncompressed length
            readbytes = xSubStream.readBytes(pContents, oleLength);
            if (oleLength < readbytes) {
                return "oleLength :" + oleLength + " readbytes: " + readbytes;
            }

            // Decompress the bytes
            Inflater decompresser = new Inflater();
            decompresser.setInput(pContents[0], 0, readbytes);
            byte[] result = new byte[oleLength];
            int resultLength = decompresser.inflate(result);
            decompresser.end();

            //return the base64 string of the uncompressed data
            return Base64.encodeBytes(result);
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return "";
    }

    public XStream CreateTempFileStream(XMultiServiceFactory xMSF) {
        // try to get temporary file representation
        XStream xTempFileStream = null;
        try {
            Object oTempFile = xMSF.createInstance("com.sun.star.io.TempFile");
            xTempFileStream = (XStream) UnoRuntime.queryInterface(XStream.class, oTempFile);
        } catch (Exception e) {
        }

        if (xTempFileStream == null) {
            System.out.println("Can't create temporary file!");
        }

        return xTempFileStream;
    }
    //decode the base64 string and create an com.sun.star.embed.OLESimpleStorage from it
    public void loadRootStorageFromBase64(String aBase64) {
        try {
            //Decode and write the data to an temp stream
            byte[] oledata = Base64.decode(aBase64);
            m_RootStream = CreateTempFileStream(m_xMSF);
            XOutputStream xOutput = m_RootStream.getOutputStream();
            xOutput.writeBytes(oledata);
            xOutput.flush();
            //Get the input stream and seek to begin
            XInputStream xInput = m_RootStream.getInputStream();
            XSeekable xSeek = (XSeekable) UnoRuntime.queryInterface(XSeekable.class, xInput);
            xSeek.seek(0);
            oledata = null;
            xSeek = null;

            //create an com.sun.star.embed.OLESimpleStorage from the temp stream
            Object pArgs[] = new Object[1];
            pArgs[0] = (Object) xInput;
            Object oTempStorage = m_xMSF.createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", pArgs);
            pArgs = null;

            m_Storage = (XNameContainer) UnoRuntime.queryInterface(XNameContainer.class, oTempStorage);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    //Create a empty OLESimpleStorage if there is not one
    public void ensureCreateRootStorage() {
        if (null == m_RootStream || null == m_Storage) {
            try {
                m_RootStream = CreateTempFileStream(m_xMSF);

                Object pArgs[] = new Object[1];
                pArgs[0] = (Object) m_RootStream;
                Object oTempStorage = m_xMSF.createInstanceWithArguments("com.sun.star.embed.OLESimpleStorage", pArgs);
                pArgs = null;

                m_Storage = (XNameContainer) UnoRuntime.queryInterface(XNameContainer.class, oTempStorage);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    //decode the base64 string and insert the length and the compressed data of it to
    //the root storage as a sub stream under aName
    public void insertSubStorage(String aName, String aBase64) {
        try {
            //decode the base64 string
            byte[] oledata = Base64.decode(aBase64);
            //create a temp stream to write data to
            XStream subStream = CreateTempFileStream(m_xMSF);
            XInputStream xInput = subStream.getInputStream();
            XOutputStream xOutput = subStream.getOutputStream();
            //write the length to the temp stream
            byte oleHead[] = new byte[4];
            oleHead[0] = (byte) ((oledata.length >>> 0) & 0xFF);
            oleHead[1] = (byte) ((oledata.length >>> 8) & 0xFF);
            oleHead[2] = (byte) ((oledata.length >>> 16) & 0xFF);
            oleHead[3] = (byte) ((oledata.length >>> 24) & 0xFF);
            xOutput.writeBytes(oleHead);

            // Compress the bytes
            byte[] output = new byte[oledata.length];
            Deflater compresser = new Deflater();
            compresser.setInput(oledata);
            compresser.finish();
            int compressedDataLength = compresser.deflate(output);
            //realloc the data length
            byte[] compressedBytes = new byte[compressedDataLength];
            for (int i = 0; i < compressedDataLength; i++) {
                compressedBytes[i] = output[i];
            }

            //write the compressed data to the temp stream
            xOutput.writeBytes(compressedBytes);
            //seek to 0
            XSeekable xSeek = (XSeekable) UnoRuntime.queryInterface(XSeekable.class, xInput);
            xSeek.seek(0);
            xSeek = null;
            oledata = null;

            //insert the temp stream as a sub stream and use an XTransactedObject to commit it immediately
            XTransactedObject xTransact = (XTransactedObject) UnoRuntime.queryInterface(XTransactedObject.class, m_Storage);
            m_Storage.insertByName(aName, xInput);
            xTransact.commit();
            xTransact = null;

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /** separtates the uno-url into 3 different parts.
     */
    protected static String[] parseUnoUrl(String url) {
        String[] aRet = new String[3];

        if (!url.startsWith("uno:")) {
            return null;
        }

        int semicolon = url.indexOf(';');
        if (semicolon == -1) {
            return null;
        }

        aRet[0] = url.substring(4, semicolon);
        int nextSemicolon = url.indexOf(';', semicolon + 1);

        if (semicolon == -1) {
            return null;
        }
        aRet[1] = url.substring(semicolon + 1, nextSemicolon);

        aRet[2] = url.substring(nextSemicolon + 1);
        return aRet;
    }
    //connect to running OOo and keep an XConnection object so that we can disconnect from OOo as we wish
    protected XMultiServiceFactory connectAwareGetServiceFactory() throws com.sun.star.uno.Exception,
            com.sun.star.uno.RuntimeException,
            Exception {

        // Get component context
        XComponentContext xComponentContext =
                com.sun.star.comp.helper.Bootstrap.createInitialComponentContext(null);

        // instantiate connector service
        Object x = xComponentContext.getServiceManager().createInstanceWithContext(
                "com.sun.star.connection.Connector", xComponentContext);

        XConnector xConnector = (XConnector) UnoRuntime.queryInterface(XConnector.class, x);

        String a[] = parseUnoUrl(sConnectionString);
        if (null == a) {
            throw new com.sun.star.uno.Exception("Couldn't parse uno-url " + sConnectionString);
        }

        // connect using the connection string part of the uno-url only.
        m_Connection = xConnector.connect(a[0]);

        x = xComponentContext.getServiceManager().createInstanceWithContext(
                "com.sun.star.bridge.BridgeFactory", xComponentContext);

        XBridgeFactory xBridgeFactory = (XBridgeFactory) UnoRuntime.queryInterface(
                XBridgeFactory.class, x);

        // create a nameless bridge with no instance provider
        // using the middle part of the uno-url
        XBridge bridge = xBridgeFactory.createBridge("", a[1], m_Connection, null);

        // query for the XComponent interface and add this as event listener
        XComponent xComponent = (XComponent) UnoRuntime.queryInterface(
                XComponent.class, bridge);

        // get the remote instance
        x = bridge.getInstance(a[2]);

        // Did the remote server export this object ?
        if (null == x) {
            throw new com.sun.star.uno.Exception(
                    "Server didn't provide an instance for" + a[2], null);
        }

        XMultiServiceFactory xFac = (XMultiServiceFactory) UnoRuntime.queryInterface(XMultiServiceFactory.class, x);
        return xFac;
    }
    protected static boolean DEBUG = false;
    protected static boolean DEBUGCHK = false;
    protected static String debugfile;

    protected static void debugln(String s) {
        debug(s + "\n");
    }

    protected static void debug(String s) {
        if (!DEBUGCHK) {
            if (System.getProperty("xsltfilter.debug") == null) {
                DEBUGCHK = true;
                return;
            } else {
                debugfile = System.getProperty("xsltfilter.debug");
                DEBUG = true;
            }
        }
        if (!DEBUG) {
            return;
        }
        try {
            FileWriter dbgwriter = new FileWriter(debugfile, true);
            dbgwriter.write(s);
            dbgwriter.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}