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