001    /**
002     * Copyright (c) 2000-2012 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portlet.documentlibrary.util;
016    
017    import com.artofsolving.jodconverter.DefaultDocumentFormatRegistry;
018    import com.artofsolving.jodconverter.DocumentConverter;
019    import com.artofsolving.jodconverter.DocumentFormat;
020    import com.artofsolving.jodconverter.DocumentFormatRegistry;
021    import com.artofsolving.jodconverter.openoffice.connection.OpenOfficeConnection;
022    import com.artofsolving.jodconverter.openoffice.connection.SocketOpenOfficeConnection;
023    import com.artofsolving.jodconverter.openoffice.converter.OpenOfficeDocumentConverter;
024    import com.artofsolving.jodconverter.openoffice.converter.StreamOpenOfficeDocumentConverter;
025    
026    import com.liferay.portal.kernel.configuration.Filter;
027    import com.liferay.portal.kernel.exception.SystemException;
028    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream;
029    import com.liferay.portal.kernel.log.Log;
030    import com.liferay.portal.kernel.log.LogFactoryUtil;
031    import com.liferay.portal.kernel.util.ArrayUtil;
032    import com.liferay.portal.kernel.util.FileUtil;
033    import com.liferay.portal.kernel.util.PropsKeys;
034    import com.liferay.portal.kernel.util.SortedArrayList;
035    import com.liferay.portal.kernel.util.StringBundler;
036    import com.liferay.portal.kernel.util.StringPool;
037    import com.liferay.portal.kernel.util.SystemProperties;
038    import com.liferay.portal.kernel.util.Validator;
039    import com.liferay.portal.util.PrefsPropsUtil;
040    import com.liferay.portal.util.PropsUtil;
041    import com.liferay.portal.util.PropsValues;
042    
043    import java.io.File;
044    import java.io.IOException;
045    import java.io.InputStream;
046    
047    import java.util.ArrayList;
048    import java.util.HashMap;
049    import java.util.List;
050    import java.util.Map;
051    
052    /**
053     * @author Bruno Farache
054     * @author Alexander Chow
055     */
056    public class DocumentConversionUtil {
057    
058            public static File convert(
059                            String id, InputStream inputStream, String sourceExtension,
060                            String targetExtension)
061                    throws IOException, SystemException {
062    
063                    return _instance._convert(
064                            id, inputStream, sourceExtension, targetExtension);
065            }
066    
067            public static void disconnect() {
068                    _instance._disconnect();
069            }
070    
071            public static String[] getConversions(String extension) {
072                    return _instance._getConversions(extension);
073            }
074    
075            public static String getFilePath(String id, String targetExtension) {
076                    StringBundler sb = new StringBundler(5);
077    
078                    sb.append(SystemProperties.get(SystemProperties.TMP_DIR));
079                    sb.append("/liferay/document_conversion/");
080                    sb.append(id);
081                    sb.append(StringPool.PERIOD);
082                    sb.append(targetExtension);
083    
084                    return sb.toString();
085            }
086    
087            public static boolean isComparableVersion(String extension) {
088                    boolean enabled = false;
089    
090                    String dotExtension = StringPool.PERIOD + extension;
091    
092                    for (int i = 0; i < _COMPARABLE_FILE_EXTENSIONS.length; i++) {
093                            if (StringPool.STAR.equals(_COMPARABLE_FILE_EXTENSIONS[i]) ||
094                                    dotExtension.equals(_COMPARABLE_FILE_EXTENSIONS[i])) {
095    
096                                    enabled = true;
097    
098                                    break;
099                            }
100                    }
101    
102                    if (!enabled) {
103                            return false;
104                    }
105    
106                    if (extension.equals("css") || extension.equals("js") ||
107                            extension.equals("htm") || extension.equals("html") ||
108                            extension.equals("txt") || extension.equals("xml")) {
109    
110                            return true;
111                    }
112    
113                    try {
114                            if (isEnabled() && isConvertBeforeCompare(extension)) {
115                                    return true;
116                            }
117                    }
118                    catch (Exception e) {
119                            if (_log.isErrorEnabled()) {
120                                    _log.error(e, e);
121                            }
122                    }
123    
124                    return false;
125            }
126    
127            public static boolean isConvertBeforeCompare(String extension) {
128                    if (extension.equals("txt")) {
129                            return false;
130                    }
131    
132                    String[] conversions = getConversions(extension);
133    
134                    for (int i = 0; i < conversions.length; i++) {
135                            if (conversions[i].equals("txt")) {
136                                    return true;
137                            }
138                    }
139    
140                    return false;
141            }
142    
143            public static boolean isEnabled() {
144                    try {
145                            return PrefsPropsUtil.getBoolean(
146                                    PropsKeys.OPENOFFICE_SERVER_ENABLED,
147                                    PropsValues.OPENOFFICE_SERVER_ENABLED);
148                    }
149                    catch (Exception e) {
150                    }
151    
152                    return false;
153            }
154    
155            private DocumentConversionUtil() {
156                    _populateConversionsMap("drawing");
157                    _populateConversionsMap("presentation");
158                    _populateConversionsMap("spreadsheet");
159                    _populateConversionsMap("text");
160            }
161    
162            private File _convert(
163                            String id, InputStream inputStream, String sourceExtension,
164                            String targetExtension)
165                    throws IOException, SystemException {
166    
167                    if (!isEnabled()) {
168                            return null;
169                    }
170    
171                    sourceExtension = _fixExtension(sourceExtension);
172                    targetExtension = _fixExtension(targetExtension);
173    
174                    String fileName = getFilePath(id, targetExtension);
175    
176                    File file = new File(fileName);
177    
178                    if (!PropsValues.OPENOFFICE_CACHE_ENABLED || !file.exists()) {
179                            DocumentFormatRegistry documentFormatRegistry =
180                                    new DefaultDocumentFormatRegistry();
181    
182                            DocumentFormat inputDocumentFormat =
183                                    documentFormatRegistry.getFormatByFileExtension(
184                                            sourceExtension);
185                            DocumentFormat outputDocumentFormat =
186                                    documentFormatRegistry.getFormatByFileExtension(
187                                            targetExtension);
188    
189                            if (!inputDocumentFormat.isImportable()) {
190                                    throw new SystemException(
191                                            "Conversion is not supported from " +
192                                                    inputDocumentFormat.getName());
193                            }
194                            else if (!inputDocumentFormat.isExportableTo(
195                                                    outputDocumentFormat)) {
196    
197                                    throw new SystemException(
198                                            "Conversion is not supported from " +
199                                                    inputDocumentFormat.getName() + " to " +
200                                                            outputDocumentFormat.getName());
201                            }
202    
203                            UnsyncByteArrayOutputStream unsyncByteArrayOutputStream =
204                                    new UnsyncByteArrayOutputStream();
205    
206                            DocumentConverter documentConverter = _getDocumentConverter();
207    
208                            documentConverter.convert(
209                                    inputStream, inputDocumentFormat, unsyncByteArrayOutputStream,
210                                    outputDocumentFormat);
211    
212                            FileUtil.write(
213                                    file, unsyncByteArrayOutputStream.unsafeGetByteArray(), 0,
214                                    unsyncByteArrayOutputStream.size());
215                    }
216    
217                    return file;
218            }
219    
220            private void _disconnect() {
221                    if (_openOfficeConnection != null) {
222                            _openOfficeConnection.disconnect();
223                    }
224            }
225    
226            private String _fixExtension(String extension) {
227                    if (extension.equals("htm")) {
228                            extension = "html";
229                    }
230    
231                    return extension;
232            }
233    
234            private String[] _getConversions(String extension) {
235                    extension = _fixExtension(extension);
236    
237                    String[] conversions = _conversionsMap.get(extension);
238    
239                    if (conversions == null) {
240                            conversions = _DEFAULT_CONVERSIONS;
241                    }
242                    else {
243                            if (ArrayUtil.contains(conversions, extension)) {
244                                    List<String> conversionsList = new ArrayList<String>();
245    
246                                    for (int i = 0; i < conversions.length; i++) {
247                                            String conversion = conversions[i];
248    
249                                            if (!conversion.equals(extension)) {
250                                                    conversionsList.add(conversion);
251                                            }
252                                    }
253    
254                                    conversions = conversionsList.toArray(
255                                            new String[conversionsList.size()]);
256                            }
257                    }
258    
259                    return conversions;
260            }
261    
262            private DocumentConverter _getDocumentConverter() throws SystemException {
263                    if ((_openOfficeConnection != null) && (_documentConverter != null)) {
264                            return _documentConverter;
265                    }
266    
267                    String host = PrefsPropsUtil.getString(
268                            PropsKeys.OPENOFFICE_SERVER_HOST);
269                    int port = PrefsPropsUtil.getInteger(
270                            PropsKeys.OPENOFFICE_SERVER_PORT,
271                            PropsValues.OPENOFFICE_SERVER_PORT);
272    
273                    if (_isRemoteOpenOfficeHost(host)) {
274                            _openOfficeConnection = new SocketOpenOfficeConnection(host, port);
275                            _documentConverter = new StreamOpenOfficeDocumentConverter(
276                                    _openOfficeConnection);
277                    }
278                    else {
279                            _openOfficeConnection = new SocketOpenOfficeConnection(port);
280                            _documentConverter = new OpenOfficeDocumentConverter(
281                                    _openOfficeConnection);
282                    }
283    
284                    return _documentConverter;
285            }
286    
287            private boolean _isRemoteOpenOfficeHost(String host) {
288                    if (Validator.isNotNull(host) && !host.equals(_LOCALHOST_IP) &&
289                            !host.startsWith(_LOCALHOST)) {
290    
291                            return true;
292                    }
293                    else {
294                            return false;
295                    }
296            }
297    
298            private void _populateConversionsMap(String documentFamily) {
299                    Filter filter = new Filter(documentFamily);
300    
301                    DocumentFormatRegistry documentFormatRegistry =
302                            new DefaultDocumentFormatRegistry();
303    
304                    String[] sourceExtensions = PropsUtil.getArray(
305                            PropsKeys.OPENOFFICE_CONVERSION_SOURCE_EXTENSIONS, filter);
306                    String[] targetExtensions = PropsUtil.getArray(
307                            PropsKeys.OPENOFFICE_CONVERSION_TARGET_EXTENSIONS, filter);
308    
309                    for (String sourceExtension : sourceExtensions) {
310                            List<String> conversions = new SortedArrayList<String>();
311    
312                            DocumentFormat sourceDocumentFormat =
313                                    documentFormatRegistry.getFormatByFileExtension(
314                                            sourceExtension);
315    
316                            if (sourceDocumentFormat == null) {
317                                    if (_log.isWarnEnabled()) {
318                                            _log.warn("Invalid source extension " + sourceExtension);
319                                    }
320    
321                                    continue;
322                            }
323    
324                            for (String targetExtension : targetExtensions) {
325                                    DocumentFormat targetDocumentFormat =
326                                            documentFormatRegistry.getFormatByFileExtension(
327                                                    targetExtension);
328    
329                                    if (targetDocumentFormat == null) {
330                                            if (_log.isWarnEnabled()) {
331                                                    _log.warn(
332                                                            "Invalid target extension " + targetDocumentFormat);
333                                            }
334    
335                                            continue;
336                                    }
337    
338                                    if (sourceDocumentFormat.isExportableTo(targetDocumentFormat)) {
339                                            conversions.add(targetExtension);
340                                    }
341                            }
342    
343                            if (conversions.isEmpty()) {
344                                    if (_log.isInfoEnabled()) {
345                                            _log.info(
346                                                    "There are no conversions supported from " +
347                                                            sourceExtension);
348                                    }
349                            }
350                            else {
351                                    if (_log.isInfoEnabled()) {
352                                            _log.info(
353                                                    "Conversions supported from " + sourceExtension +
354                                                            " to " + conversions);
355                                    }
356    
357                                    _conversionsMap.put(
358                                            sourceExtension,
359                                            conversions.toArray(new String[conversions.size()]));
360                            }
361                    }
362            }
363    
364            private static final String[] _COMPARABLE_FILE_EXTENSIONS =
365                    PropsValues.DL_COMPARABLE_FILE_EXTENSIONS;
366    
367            private static final String[] _DEFAULT_CONVERSIONS = new String[0];
368    
369            private static final String _LOCALHOST = "localhost";
370    
371            private static final String _LOCALHOST_IP = "127.0.0.1";
372    
373            private static Log _log = LogFactoryUtil.getLog(
374                    DocumentConversionUtil.class);
375    
376            private static DocumentConversionUtil _instance =
377                    new DocumentConversionUtil();
378    
379            private Map<String, String[]> _conversionsMap =
380                    new HashMap<String, String[]>();
381            private DocumentConverter _documentConverter;
382            private OpenOfficeConnection _openOfficeConnection;
383    
384    }