001    /**
002     * Copyright (c) 2000-2013 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("htm") ||
107                            extension.equals("html") || extension.equals("js") ||
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                    _validate(targetExtension, id);
175    
176                    String fileName = getFilePath(id, targetExtension);
177    
178                    File file = new File(fileName);
179    
180                    if (PropsValues.OPENOFFICE_CACHE_ENABLED && file.exists()) {
181                            return file;
182                    }
183    
184                    DocumentFormatRegistry documentFormatRegistry =
185                            new DefaultDocumentFormatRegistry();
186    
187                    DocumentFormat inputDocumentFormat =
188                            documentFormatRegistry.getFormatByFileExtension(sourceExtension);
189                    DocumentFormat outputDocumentFormat =
190                            documentFormatRegistry.getFormatByFileExtension(targetExtension);
191    
192                    if (inputDocumentFormat == null) {
193                            throw new SystemException(
194                                    "Conversion is not supported from ." + sourceExtension);
195                    }
196                    else if (!inputDocumentFormat.isImportable()) {
197                            throw new SystemException(
198                                    "Conversion is not supported from " +
199                                            inputDocumentFormat.getName());
200                    }
201                    else if (outputDocumentFormat == null) {
202                            throw new SystemException(
203                                    "Conversion is not supported from " +
204                                            inputDocumentFormat.getName() + " to ." +
205                                                    targetExtension);
206                    }
207                    else if (!inputDocumentFormat.isExportableTo(outputDocumentFormat)) {
208                            throw new SystemException(
209                                    "Conversion is not supported from " +
210                                            inputDocumentFormat.getName() + " to " +
211                                                    outputDocumentFormat.getName());
212                    }
213    
214                    UnsyncByteArrayOutputStream unsyncByteArrayOutputStream =
215                            new UnsyncByteArrayOutputStream();
216    
217                    DocumentConverter documentConverter = _getDocumentConverter();
218    
219                    documentConverter.convert(
220                            inputStream, inputDocumentFormat, unsyncByteArrayOutputStream,
221                            outputDocumentFormat);
222    
223                    FileUtil.write(
224                            file, unsyncByteArrayOutputStream.unsafeGetByteArray(), 0,
225                            unsyncByteArrayOutputStream.size());
226    
227                    return file;
228            }
229    
230            private void _disconnect() {
231                    if (_openOfficeConnection != null) {
232                            _openOfficeConnection.disconnect();
233                    }
234            }
235    
236            private String _fixExtension(String extension) {
237                    if (extension.equals("htm")) {
238                            extension = "html";
239                    }
240    
241                    return extension;
242            }
243    
244            private String[] _getConversions(String extension) {
245                    extension = _fixExtension(extension);
246    
247                    String[] conversions = _conversionsMap.get(extension);
248    
249                    if (conversions == null) {
250                            conversions = _DEFAULT_CONVERSIONS;
251                    }
252                    else {
253                            if (ArrayUtil.contains(conversions, extension)) {
254                                    List<String> conversionsList = new ArrayList<String>();
255    
256                                    for (int i = 0; i < conversions.length; i++) {
257                                            String conversion = conversions[i];
258    
259                                            if (!conversion.equals(extension)) {
260                                                    conversionsList.add(conversion);
261                                            }
262                                    }
263    
264                                    conversions = conversionsList.toArray(
265                                            new String[conversionsList.size()]);
266                            }
267                    }
268    
269                    return conversions;
270            }
271    
272            private DocumentConverter _getDocumentConverter() throws SystemException {
273                    if ((_openOfficeConnection != null) && (_documentConverter != null)) {
274                            return _documentConverter;
275                    }
276    
277                    String host = PrefsPropsUtil.getString(
278                            PropsKeys.OPENOFFICE_SERVER_HOST);
279                    int port = PrefsPropsUtil.getInteger(
280                            PropsKeys.OPENOFFICE_SERVER_PORT,
281                            PropsValues.OPENOFFICE_SERVER_PORT);
282    
283                    if (_isRemoteOpenOfficeHost(host)) {
284                            _openOfficeConnection = new SocketOpenOfficeConnection(host, port);
285                            _documentConverter = new StreamOpenOfficeDocumentConverter(
286                                    _openOfficeConnection);
287                    }
288                    else {
289                            _openOfficeConnection = new SocketOpenOfficeConnection(port);
290                            _documentConverter = new OpenOfficeDocumentConverter(
291                                    _openOfficeConnection);
292                    }
293    
294                    return _documentConverter;
295            }
296    
297            private boolean _isRemoteOpenOfficeHost(String host) {
298                    if (Validator.isNotNull(host) && !host.equals(_LOCALHOST_IP) &&
299                            !host.startsWith(_LOCALHOST)) {
300    
301                            return true;
302                    }
303                    else {
304                            return false;
305                    }
306            }
307    
308            private void _populateConversionsMap(String documentFamily) {
309                    Filter filter = new Filter(documentFamily);
310    
311                    DocumentFormatRegistry documentFormatRegistry =
312                            new DefaultDocumentFormatRegistry();
313    
314                    String[] sourceExtensions = PropsUtil.getArray(
315                            PropsKeys.OPENOFFICE_CONVERSION_SOURCE_EXTENSIONS, filter);
316                    String[] targetExtensions = PropsUtil.getArray(
317                            PropsKeys.OPENOFFICE_CONVERSION_TARGET_EXTENSIONS, filter);
318    
319                    for (String sourceExtension : sourceExtensions) {
320                            List<String> conversions = new SortedArrayList<String>();
321    
322                            DocumentFormat sourceDocumentFormat =
323                                    documentFormatRegistry.getFormatByFileExtension(
324                                            sourceExtension);
325    
326                            if (sourceDocumentFormat == null) {
327                                    if (_log.isWarnEnabled()) {
328                                            _log.warn("Invalid source extension " + sourceExtension);
329                                    }
330    
331                                    continue;
332                            }
333    
334                            for (String targetExtension : targetExtensions) {
335                                    DocumentFormat targetDocumentFormat =
336                                            documentFormatRegistry.getFormatByFileExtension(
337                                                    targetExtension);
338    
339                                    if (targetDocumentFormat == null) {
340                                            if (_log.isWarnEnabled()) {
341                                                    _log.warn(
342                                                            "Invalid target extension " + targetDocumentFormat);
343                                            }
344    
345                                            continue;
346                                    }
347    
348                                    if (sourceDocumentFormat.isExportableTo(targetDocumentFormat)) {
349                                            conversions.add(targetExtension);
350                                    }
351                            }
352    
353                            if (conversions.isEmpty()) {
354                                    if (_log.isInfoEnabled()) {
355                                            _log.info(
356                                                    "There are no conversions supported from " +
357                                                            sourceExtension);
358                                    }
359                            }
360                            else {
361                                    if (_log.isInfoEnabled()) {
362                                            _log.info(
363                                                    "Conversions supported from " + sourceExtension +
364                                                            " to " + conversions);
365                                    }
366    
367                                    _conversionsMap.put(
368                                            sourceExtension,
369                                            conversions.toArray(new String[conversions.size()]));
370                            }
371                    }
372            }
373    
374            private void _validate(String targetExtension, String id)
375                    throws SystemException {
376    
377                    if (!Validator.isFileExtension(targetExtension)) {
378                            throw new SystemException("Invalid extension: " + targetExtension);
379                    }
380    
381                    if (!Validator.isFileName(id)) {
382                            throw new SystemException("Invalid file name: " + id);
383                    }
384            }
385    
386            private static final String[] _COMPARABLE_FILE_EXTENSIONS =
387                    PropsValues.DL_COMPARABLE_FILE_EXTENSIONS;
388    
389            private static final String[] _DEFAULT_CONVERSIONS = new String[0];
390    
391            private static final String _LOCALHOST = "localhost";
392    
393            private static final String _LOCALHOST_IP = "127.0.0.1";
394    
395            private static Log _log = LogFactoryUtil.getLog(
396                    DocumentConversionUtil.class);
397    
398            private static DocumentConversionUtil _instance =
399                    new DocumentConversionUtil();
400    
401            private Map<String, String[]> _conversionsMap =
402                    new HashMap<String, String[]>();
403            private DocumentConverter _documentConverter;
404            private OpenOfficeConnection _openOfficeConnection;
405    
406    }