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.portal.util;
016    
017    import com.liferay.portal.kernel.io.unsync.UnsyncBufferedReader;
018    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayInputStream;
019    import com.liferay.portal.kernel.io.unsync.UnsyncByteArrayOutputStream;
020    import com.liferay.portal.kernel.log.Log;
021    import com.liferay.portal.kernel.log.LogFactoryUtil;
022    import com.liferay.portal.kernel.nio.charset.CharsetEncoderUtil;
023    import com.liferay.portal.kernel.process.ClassPathUtil;
024    import com.liferay.portal.kernel.process.ProcessCallable;
025    import com.liferay.portal.kernel.process.ProcessException;
026    import com.liferay.portal.kernel.process.ProcessExecutor;
027    import com.liferay.portal.kernel.security.pacl.DoPrivileged;
028    import com.liferay.portal.kernel.util.ArrayUtil;
029    import com.liferay.portal.kernel.util.CharPool;
030    import com.liferay.portal.kernel.util.Digester;
031    import com.liferay.portal.kernel.util.DigesterUtil;
032    import com.liferay.portal.kernel.util.FileComparator;
033    import com.liferay.portal.kernel.util.StreamUtil;
034    import com.liferay.portal.kernel.util.StringBundler;
035    import com.liferay.portal.kernel.util.StringPool;
036    import com.liferay.portal.kernel.util.StringUtil;
037    import com.liferay.portal.kernel.util.SystemProperties;
038    import com.liferay.portal.kernel.util.Time;
039    import com.liferay.portal.kernel.util.Validator;
040    import com.liferay.util.PwdGenerator;
041    import com.liferay.util.ant.ExpandTask;
042    
043    import java.io.File;
044    import java.io.FileInputStream;
045    import java.io.FileOutputStream;
046    import java.io.FileReader;
047    import java.io.IOException;
048    import java.io.InputStream;
049    import java.io.OutputStreamWriter;
050    import java.io.RandomAccessFile;
051    import java.io.Reader;
052    import java.io.Writer;
053    
054    import java.nio.ByteBuffer;
055    import java.nio.channels.FileChannel;
056    
057    import java.util.ArrayList;
058    import java.util.Arrays;
059    import java.util.List;
060    import java.util.Properties;
061    import java.util.concurrent.Future;
062    
063    import org.apache.commons.compress.archivers.zip.UnsupportedZipFeatureException;
064    import org.apache.commons.io.FileUtils;
065    import org.apache.commons.lang.exception.ExceptionUtils;
066    import org.apache.pdfbox.exceptions.CryptographyException;
067    import org.apache.poi.EncryptedDocumentException;
068    import org.apache.tika.Tika;
069    import org.apache.tika.exception.TikaException;
070    import org.apache.tools.ant.DirectoryScanner;
071    
072    import org.mozilla.intl.chardet.nsDetector;
073    import org.mozilla.intl.chardet.nsPSMDetector;
074    
075    /**
076     * @author Brian Wing Shun Chan
077     * @author Alexander Chow
078     */
079    @DoPrivileged
080    public class FileImpl implements com.liferay.portal.kernel.util.File {
081    
082            public static FileImpl getInstance() {
083                    return _instance;
084            }
085    
086            @Override
087            public void copyDirectory(File source, File destination)
088                    throws IOException {
089    
090                    if (!source.exists() || !source.isDirectory()) {
091                            return;
092                    }
093    
094                    if (!destination.exists()) {
095                            destination.mkdirs();
096                    }
097    
098                    File[] fileArray = source.listFiles();
099    
100                    for (int i = 0; i < fileArray.length; i++) {
101                            if (fileArray[i].isDirectory()) {
102                                    copyDirectory(
103                                            fileArray[i],
104                                            new File(
105                                                    destination.getPath() + File.separator +
106                                                            fileArray[i].getName()));
107                            }
108                            else {
109                                    copyFile(
110                                            fileArray[i],
111                                            new File(
112                                                    destination.getPath() + File.separator +
113                                                            fileArray[i].getName()));
114                            }
115                    }
116            }
117    
118            @Override
119            public void copyDirectory(String sourceDirName, String destinationDirName)
120                    throws IOException {
121    
122                    copyDirectory(new File(sourceDirName), new File(destinationDirName));
123            }
124    
125            @Override
126            public void copyFile(File source, File destination) throws IOException {
127                    copyFile(source, destination, false);
128            }
129    
130            @Override
131            public void copyFile(File source, File destination, boolean lazy)
132                    throws IOException {
133    
134                    if (!source.exists()) {
135                            return;
136                    }
137    
138                    if (lazy) {
139                            String oldContent = null;
140    
141                            try {
142                                    oldContent = read(source);
143                            }
144                            catch (Exception e) {
145                                    return;
146                            }
147    
148                            String newContent = null;
149    
150                            try {
151                                    newContent = read(destination);
152                            }
153                            catch (Exception e) {
154                            }
155    
156                            if ((oldContent == null) || !oldContent.equals(newContent)) {
157                                    copyFile(source, destination, false);
158                            }
159                    }
160                    else {
161                            mkdirsParentFile(destination);
162    
163                            StreamUtil.transfer(
164                                    new FileInputStream(source), new FileOutputStream(destination));
165                    }
166            }
167    
168            @Override
169            public void copyFile(String source, String destination) throws IOException {
170                    copyFile(source, destination, false);
171            }
172    
173            @Override
174            public void copyFile(String source, String destination, boolean lazy)
175                    throws IOException {
176    
177                    copyFile(new File(source), new File(destination), lazy);
178            }
179    
180            @Override
181            public File createTempFile() {
182                    return createTempFile(StringPool.BLANK);
183            }
184    
185            @Override
186            public File createTempFile(byte[] bytes) throws IOException {
187                    File file = createTempFile(StringPool.BLANK);
188    
189                    write(file, bytes, false);
190    
191                    return file;
192            }
193    
194            @Override
195            public File createTempFile(InputStream is) throws IOException {
196                    File file = createTempFile(StringPool.BLANK);
197    
198                    write(file, is);
199    
200                    return file;
201            }
202    
203            @Override
204            public File createTempFile(String extension) {
205                    return new File(createTempFileName(extension));
206            }
207    
208            @Override
209            public String createTempFileName() {
210                    return createTempFileName(null);
211            }
212    
213            @Override
214            public String createTempFileName(String extension) {
215                    StringBundler sb = new StringBundler();
216    
217                    sb.append(SystemProperties.get(SystemProperties.TMP_DIR));
218                    sb.append(StringPool.SLASH);
219                    sb.append(Time.getTimestamp());
220                    sb.append(PwdGenerator.getPassword(8, PwdGenerator.KEY2));
221    
222                    if (Validator.isFileExtension(extension)) {
223                            sb.append(StringPool.PERIOD);
224                            sb.append(extension);
225                    }
226    
227                    return sb.toString();
228            }
229    
230            @Override
231            public File createTempFolder() {
232                    File file = new File(createTempFileName());
233    
234                    file.mkdirs();
235    
236                    return file;
237            }
238    
239            @Override
240            public String decodeSafeFileName(String fileName) {
241                    return StringUtil.replace(
242                            fileName, _SAFE_FILE_NAME_2, _SAFE_FILE_NAME_1);
243            }
244    
245            @Override
246            public boolean delete(File file) {
247                    if (file != null) {
248                            boolean exists = true;
249    
250                            try {
251                                    exists = file.exists();
252                            }
253                            catch (SecurityException se) {
254    
255                                    // We may have the permission to delete a specific file without
256                                    // having the permission to check if the file exists
257    
258                            }
259    
260                            if (exists) {
261                                    return file.delete();
262                            }
263                    }
264    
265                    return false;
266            }
267    
268            @Override
269            public boolean delete(String file) {
270                    return delete(new File(file));
271            }
272    
273            @Override
274            public void deltree(File directory) {
275                    if (directory.exists() && directory.isDirectory()) {
276                            File[] fileArray = directory.listFiles();
277    
278                            for (int i = 0; i < fileArray.length; i++) {
279                                    if (fileArray[i].isDirectory()) {
280                                            deltree(fileArray[i]);
281                                    }
282                                    else {
283                                            fileArray[i].delete();
284                                    }
285                            }
286    
287                            directory.delete();
288                    }
289            }
290    
291            @Override
292            public void deltree(String directory) {
293                    deltree(new File(directory));
294            }
295    
296            @Override
297            public String encodeSafeFileName(String fileName) {
298                    if (fileName == null) {
299                            return StringPool.BLANK;
300                    }
301    
302                    return StringUtil.replace(
303                            fileName, _SAFE_FILE_NAME_1, _SAFE_FILE_NAME_2);
304            }
305    
306            @Override
307            public boolean exists(File file) {
308                    return file.exists();
309            }
310    
311            @Override
312            public boolean exists(String fileName) {
313                    return exists(new File(fileName));
314            }
315    
316            @Override
317            public String extractText(InputStream is, String fileName) {
318                    String text = null;
319    
320                    ClassLoader portalClassLoader = ClassLoaderUtil.getPortalClassLoader();
321    
322                    ClassLoader contextClassLoader =
323                            ClassLoaderUtil.getContextClassLoader();
324    
325                    try {
326                            if (contextClassLoader != portalClassLoader) {
327                                    ClassLoaderUtil.setContextClassLoader(portalClassLoader);
328                            }
329    
330                            Tika tika = new Tika();
331    
332                            tika.setMaxStringLength(-1);
333    
334                            boolean forkProcess = false;
335    
336                            if (PropsValues.TEXT_EXTRACTION_FORK_PROCESS_ENABLED) {
337                                    String mimeType = tika.detect(is);
338    
339                                    if (ArrayUtil.contains(
340                                                    PropsValues.TEXT_EXTRACTION_FORK_PROCESS_MIME_TYPES,
341                                                    mimeType)) {
342    
343                                            forkProcess = true;
344                                    }
345                            }
346    
347                            if (forkProcess) {
348                                    Future<String> future = ProcessExecutor.execute(
349                                            ClassPathUtil.getPortalClassPath(),
350                                            new ExtractTextProcessCallable(getBytes(is)));
351    
352                                    text = future.get();
353                            }
354                            else {
355                                    text = tika.parseToString(is);
356                            }
357                    }
358                    catch (Exception e) {
359                            Throwable throwable = ExceptionUtils.getRootCause(e);
360    
361                            if ((throwable instanceof CryptographyException) ||
362                                    (throwable instanceof EncryptedDocumentException) ||
363                                    (throwable instanceof UnsupportedZipFeatureException)) {
364    
365                                    if (_log.isWarnEnabled()) {
366                                            _log.warn(
367                                                    "Unable to extract text from an encrypted file " +
368                                                            fileName);
369                                    }
370                            }
371                            else if (e instanceof TikaException) {
372                                    if (_log.isWarnEnabled()) {
373                                            _log.warn("Unable to extract text from " + fileName);
374                                    }
375                            }
376                            else {
377                                    _log.error(e, e);
378                            }
379                    }
380                    finally {
381                            if (contextClassLoader != portalClassLoader) {
382                                    ClassLoaderUtil.setContextClassLoader(contextClassLoader);
383                            }
384                    }
385    
386                    if (_log.isInfoEnabled()) {
387                            if (text == null) {
388                                    _log.info("Text extraction failed for " + fileName);
389                            }
390                            else {
391                                    _log.info("Text was extracted for " + fileName);
392                            }
393                    }
394    
395                    if (_log.isDebugEnabled()) {
396                            _log.debug("Extractor returned text:\n\n" + text);
397                    }
398    
399                    if (text == null) {
400                            text = StringPool.BLANK;
401                    }
402    
403                    return text;
404            }
405    
406            @Override
407            public String[] find(String directory, String includes, String excludes) {
408                    if (directory.length() > 0) {
409                            directory = replaceSeparator(directory);
410    
411                            if (directory.charAt(directory.length() - 1) == CharPool.SLASH) {
412                                    directory = directory.substring(0, directory.length() - 1);
413                            }
414                    }
415    
416                    if (!exists(directory)) {
417                            if (_log.isWarnEnabled()) {
418                                    _log.warn("Directory " + directory + " does not exist");
419                            }
420    
421                            return new String[0];
422                    }
423    
424                    DirectoryScanner directoryScanner = new DirectoryScanner();
425    
426                    directoryScanner.setBasedir(directory);
427                    directoryScanner.setExcludes(StringUtil.split(excludes));
428                    directoryScanner.setIncludes(StringUtil.split(includes));
429    
430                    directoryScanner.scan();
431    
432                    String[] includedFiles = directoryScanner.getIncludedFiles();
433    
434                    for (int i = 0; i < includedFiles.length; i++) {
435                            includedFiles[i] = directory.concat(
436                                    StringPool.SLASH).concat(replaceSeparator(includedFiles[i]));
437                    }
438    
439                    return includedFiles;
440            }
441    
442            @Override
443            public String getAbsolutePath(File file) {
444                    return StringUtil.replace(
445                            file.getAbsolutePath(), CharPool.BACK_SLASH, CharPool.SLASH);
446            }
447    
448            @Override
449            public byte[] getBytes(File file) throws IOException {
450                    if ((file == null) || !file.exists()) {
451                            return null;
452                    }
453    
454                    RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
455    
456                    byte[] bytes = new byte[(int)randomAccessFile.length()];
457    
458                    randomAccessFile.readFully(bytes);
459    
460                    randomAccessFile.close();
461    
462                    return bytes;
463            }
464    
465            @Override
466            public byte[] getBytes(InputStream is) throws IOException {
467                    return getBytes(is, -1);
468            }
469    
470            @Override
471            public byte[] getBytes(InputStream inputStream, int bufferSize)
472                    throws IOException {
473    
474                    return getBytes(inputStream, bufferSize, true);
475            }
476    
477            @Override
478            public byte[] getBytes(
479                            InputStream inputStream, int bufferSize, boolean cleanUpStream)
480                    throws IOException {
481    
482                    if (inputStream == null) {
483                            return null;
484                    }
485    
486                    UnsyncByteArrayOutputStream unsyncByteArrayOutputStream =
487                            new UnsyncByteArrayOutputStream();
488    
489                    StreamUtil.transfer(
490                            inputStream, unsyncByteArrayOutputStream, bufferSize,
491                            cleanUpStream);
492    
493                    return unsyncByteArrayOutputStream.toByteArray();
494            }
495    
496            @Override
497            public String getExtension(String fileName) {
498                    if (fileName == null) {
499                            return null;
500                    }
501    
502                    int pos = fileName.lastIndexOf(CharPool.PERIOD);
503    
504                    if (pos > 0) {
505                            return fileName.substring(pos + 1, fileName.length()).toLowerCase();
506                    }
507                    else {
508                            return StringPool.BLANK;
509                    }
510            }
511    
512            @Override
513            public String getMD5Checksum(File file) throws IOException {
514                    FileInputStream fileInputStream = null;
515    
516                    try {
517                            fileInputStream = new FileInputStream(file);
518    
519                            return DigesterUtil.digestHex(Digester.MD5, fileInputStream);
520                    }
521                    finally {
522                            StreamUtil.cleanUp(fileInputStream);
523                    }
524            }
525    
526            @Override
527            public String getPath(String fullFileName) {
528                    int x = fullFileName.lastIndexOf(CharPool.SLASH);
529                    int y = fullFileName.lastIndexOf(CharPool.BACK_SLASH);
530    
531                    if ((x == -1) && (y == -1)) {
532                            return StringPool.SLASH;
533                    }
534    
535                    String shortFileName = fullFileName.substring(0, Math.max(x, y));
536    
537                    return shortFileName;
538            }
539    
540            @Override
541            public String getShortFileName(String fullFileName) {
542                    int x = fullFileName.lastIndexOf(CharPool.SLASH);
543                    int y = fullFileName.lastIndexOf(CharPool.BACK_SLASH);
544    
545                    String shortFileName = fullFileName.substring(Math.max(x, y) + 1);
546    
547                    return shortFileName;
548            }
549    
550            @Override
551            public boolean isAscii(File file) throws IOException {
552                    boolean ascii = true;
553    
554                    nsDetector detector = new nsDetector(nsPSMDetector.ALL);
555    
556                    InputStream inputStream = new FileInputStream(file);
557    
558                    byte[] buffer = new byte[1024];
559    
560                    int len = 0;
561    
562                    while ((len = inputStream.read(buffer, 0, buffer.length)) != -1) {
563                            if (ascii) {
564                                    ascii = detector.isAscii(buffer, len);
565    
566                                    if (!ascii) {
567                                            break;
568                                    }
569                            }
570                    }
571    
572                    detector.DataEnd();
573    
574                    inputStream.close();
575    
576                    return ascii;
577            }
578    
579            @Override
580            public boolean isSameContent(File file, byte[] bytes, int length) {
581                    FileChannel fileChannel = null;
582    
583                    try {
584                            FileInputStream fileInputStream = new FileInputStream(file);
585    
586                            fileChannel = fileInputStream.getChannel();
587    
588                            if (fileChannel.size() != length) {
589                                    return false;
590                            }
591    
592                            byte[] buffer = new byte[1024];
593    
594                            ByteBuffer byteBuffer = ByteBuffer.wrap(buffer);
595    
596                            int bufferIndex = 0;
597                            int bufferLength = -1;
598    
599                            while (((bufferLength = fileChannel.read(byteBuffer)) > 0) &&
600                                       (bufferIndex < length)) {
601    
602                                    for (int i = 0; i < bufferLength; i++) {
603                                            if (buffer[i] != bytes[bufferIndex++]) {
604                                                    return false;
605                                            }
606                                    }
607    
608                                    byteBuffer.clear();
609                            }
610    
611                            if ((bufferIndex != length) || (bufferLength != -1)) {
612                                    return false;
613                            }
614                            else {
615                                    return true;
616                            }
617                    }
618                    catch (Exception e) {
619                            return false;
620                    }
621                    finally {
622                            if (fileChannel != null) {
623                                    try {
624                                            fileChannel.close();
625                                    }
626                                    catch (IOException ioe) {
627                                    }
628                            }
629                    }
630            }
631    
632            @Override
633            public boolean isSameContent(File file, String s) {
634                    ByteBuffer byteBuffer = CharsetEncoderUtil.encode(StringPool.UTF8, s);
635    
636                    return isSameContent(file, byteBuffer.array(), byteBuffer.limit());
637            }
638    
639            @Override
640            public String[] listDirs(File file) {
641                    List<String> dirs = new ArrayList<String>();
642    
643                    File[] fileArray = file.listFiles();
644    
645                    for (int i = 0; (fileArray != null) && (i < fileArray.length); i++) {
646                            if (fileArray[i].isDirectory()) {
647                                    dirs.add(fileArray[i].getName());
648                            }
649                    }
650    
651                    return dirs.toArray(new String[dirs.size()]);
652            }
653    
654            @Override
655            public String[] listDirs(String fileName) {
656                    return listDirs(new File(fileName));
657            }
658    
659            @Override
660            public String[] listFiles(File file) {
661                    List<String> files = new ArrayList<String>();
662    
663                    File[] fileArray = file.listFiles();
664    
665                    for (int i = 0; (fileArray != null) && (i < fileArray.length); i++) {
666                            if (fileArray[i].isFile()) {
667                                    files.add(fileArray[i].getName());
668                            }
669                    }
670    
671                    return files.toArray(new String[files.size()]);
672            }
673    
674            @Override
675            public String[] listFiles(String fileName) {
676                    if (Validator.isNull(fileName)) {
677                            return new String[0];
678                    }
679    
680                    return listFiles(new File(fileName));
681            }
682    
683            @Override
684            public void mkdirs(String pathName) {
685                    File file = new File(pathName);
686    
687                    file.mkdirs();
688            }
689    
690            @Override
691            public boolean move(File source, File destination) {
692                    if (!source.exists()) {
693                            return false;
694                    }
695    
696                    destination.delete();
697    
698                    try {
699                            if (source.isDirectory()) {
700                                    FileUtils.moveDirectory(source, destination);
701                            }
702                            else {
703                                    FileUtils.moveFile(source, destination);
704                            }
705                    }
706                    catch (IOException ioe) {
707                            return false;
708                    }
709    
710                    return true;
711            }
712    
713            @Override
714            public boolean move(String sourceFileName, String destinationFileName) {
715                    return move(new File(sourceFileName), new File(destinationFileName));
716            }
717    
718            @Override
719            public String read(File file) throws IOException {
720                    return read(file, false);
721            }
722    
723            @Override
724            public String read(File file, boolean raw) throws IOException {
725                    byte[] bytes = getBytes(file);
726    
727                    if (bytes == null) {
728                            return null;
729                    }
730    
731                    String s = new String(bytes, StringPool.UTF8);
732    
733                    if (raw) {
734                            return s;
735                    }
736                    else {
737                            return StringUtil.replace(
738                                    s, StringPool.RETURN_NEW_LINE, StringPool.NEW_LINE);
739                    }
740            }
741    
742            @Override
743            public String read(String fileName) throws IOException {
744                    return read(new File(fileName));
745            }
746    
747            @Override
748            public String replaceSeparator(String fileName) {
749                    return StringUtil.replace(
750                            fileName, CharPool.BACK_SLASH, CharPool.SLASH);
751            }
752    
753            @Override
754            public File[] sortFiles(File[] files) {
755                    if (files == null) {
756                            return null;
757                    }
758    
759                    Arrays.sort(files, new FileComparator());
760    
761                    List<File> directoryList = new ArrayList<File>();
762                    List<File> fileList = new ArrayList<File>();
763    
764                    for (int i = 0; i < files.length; i++) {
765                            if (files[i].isDirectory()) {
766                                    directoryList.add(files[i]);
767                            }
768                            else {
769                                    fileList.add(files[i]);
770                            }
771                    }
772    
773                    directoryList.addAll(fileList);
774    
775                    return directoryList.toArray(new File[directoryList.size()]);
776            }
777    
778            @Override
779            public String stripExtension(String fileName) {
780                    if (fileName == null) {
781                            return null;
782                    }
783    
784                    String ext = getExtension(fileName);
785    
786                    if (ext.length() > 0) {
787                            return fileName.substring(0, fileName.length() - ext.length() - 1);
788                    }
789                    else {
790                            return fileName;
791                    }
792            }
793    
794            @Override
795            public List<String> toList(Reader reader) {
796                    List<String> list = new ArrayList<String>();
797    
798                    try {
799                            UnsyncBufferedReader unsyncBufferedReader =
800                                    new UnsyncBufferedReader(reader);
801    
802                            String line = null;
803    
804                            while ((line = unsyncBufferedReader.readLine()) != null) {
805                                    list.add(line);
806                            }
807    
808                            unsyncBufferedReader.close();
809                    }
810                    catch (IOException ioe) {
811                    }
812    
813                    return list;
814            }
815    
816            @Override
817            public List<String> toList(String fileName) {
818                    try {
819                            return toList(new FileReader(fileName));
820                    }
821                    catch (IOException ioe) {
822                            return new ArrayList<String>();
823                    }
824            }
825    
826            @Override
827            public Properties toProperties(FileInputStream fis) {
828                    Properties properties = new Properties();
829    
830                    try {
831                            properties.load(fis);
832                    }
833                    catch (IOException ioe) {
834                    }
835    
836                    return properties;
837            }
838    
839            @Override
840            public Properties toProperties(String fileName) {
841                    try {
842                            return toProperties(new FileInputStream(fileName));
843                    }
844                    catch (IOException ioe) {
845                            return new Properties();
846                    }
847            }
848    
849            @Override
850            public void touch(File file) throws IOException {
851                    FileUtils.touch(file);
852            }
853    
854            @Override
855            public void touch(String fileName) throws IOException {
856                    touch(new File(fileName));
857            }
858    
859            @Override
860            public void unzip(File source, File destination) {
861                    ExpandTask.expand(source, destination);
862            }
863    
864            @Override
865            public void write(File file, byte[] bytes) throws IOException {
866                    write(file, bytes, 0, bytes.length, false);
867            }
868    
869            @Override
870            public void write(File file, byte[] bytes, boolean append)
871                    throws IOException {
872    
873                    write(file, bytes, 0, bytes.length, append);
874            }
875    
876            @Override
877            public void write(File file, byte[] bytes, int offset, int length)
878                    throws IOException {
879    
880                    write(file, bytes, offset, bytes.length, false);
881            }
882    
883            @Override
884            public void write(
885                            File file, byte[] bytes, int offset, int length, boolean append)
886                    throws IOException {
887    
888                    mkdirsParentFile(file);
889    
890                    FileOutputStream fileOutputStream = new FileOutputStream(file, append);
891    
892                    fileOutputStream.write(bytes, offset, length);
893    
894                    fileOutputStream.close();
895            }
896    
897            @Override
898            public void write(File file, InputStream is) throws IOException {
899                    mkdirsParentFile(file);
900    
901                    StreamUtil.transfer(is, new FileOutputStream(file));
902            }
903    
904            @Override
905            public void write(File file, String s) throws IOException {
906                    write(file, s, false);
907            }
908    
909            @Override
910            public void write(File file, String s, boolean lazy) throws IOException {
911                    write(file, s, lazy, false);
912            }
913    
914            @Override
915            public void write(File file, String s, boolean lazy, boolean append)
916                    throws IOException {
917    
918                    if (s == null) {
919                            return;
920                    }
921    
922                    mkdirsParentFile(file);
923    
924                    if (lazy && file.exists()) {
925                            String content = read(file);
926    
927                            if (content.equals(s)) {
928                                    return;
929                            }
930                    }
931    
932                    Writer writer = new OutputStreamWriter(
933                            new FileOutputStream(file, append), StringPool.UTF8);
934    
935                    writer.write(s);
936    
937                    writer.close();
938            }
939    
940            @Override
941            public void write(String fileName, byte[] bytes) throws IOException {
942                    write(new File(fileName), bytes);
943            }
944    
945            @Override
946            public void write(String fileName, InputStream is) throws IOException {
947                    write(new File(fileName), is);
948            }
949    
950            @Override
951            public void write(String fileName, String s) throws IOException {
952                    write(new File(fileName), s);
953            }
954    
955            @Override
956            public void write(String fileName, String s, boolean lazy)
957                    throws IOException {
958    
959                    write(new File(fileName), s, lazy);
960            }
961    
962            @Override
963            public void write(String fileName, String s, boolean lazy, boolean append)
964                    throws IOException {
965    
966                    write(new File(fileName), s, lazy, append);
967            }
968    
969            @Override
970            public void write(String pathName, String fileName, String s)
971                    throws IOException {
972    
973                    write(new File(pathName, fileName), s);
974            }
975    
976            @Override
977            public void write(String pathName, String fileName, String s, boolean lazy)
978                    throws IOException {
979    
980                    write(new File(pathName, fileName), s, lazy);
981            }
982    
983            @Override
984            public void write(
985                            String pathName, String fileName, String s, boolean lazy,
986                            boolean append)
987                    throws IOException {
988    
989                    write(new File(pathName, fileName), s, lazy, append);
990            }
991    
992            protected void mkdirsParentFile(File file) {
993                    File parentFile = file.getParentFile();
994    
995                    if (parentFile == null) {
996                            return;
997                    }
998    
999                    try {
1000                            if (!parentFile.exists()) {
1001                                    parentFile.mkdirs();
1002                            }
1003                    }
1004                    catch (SecurityException se) {
1005    
1006                            // We may have the permission to write a specific file without
1007                            // having the permission to check if the parent file exists
1008    
1009                    }
1010            }
1011    
1012            private static final String[] _SAFE_FILE_NAME_1 = {
1013                    StringPool.AMPERSAND, StringPool.CLOSE_PARENTHESIS,
1014                    StringPool.OPEN_PARENTHESIS, StringPool.SEMICOLON
1015            };
1016    
1017            private static final String[] _SAFE_FILE_NAME_2 = {
1018                    "_AMP_", "_CP_", "_OP_", "_SEM_"
1019            };
1020    
1021            private static Log _log = LogFactoryUtil.getLog(FileImpl.class);
1022    
1023            private static FileImpl _instance = new FileImpl();
1024    
1025            private static class ExtractTextProcessCallable
1026                    implements ProcessCallable<String> {
1027    
1028                    public ExtractTextProcessCallable(byte[] data) {
1029                            _data = data;
1030                    }
1031    
1032                    @Override
1033                    public String call() throws ProcessException {
1034                            Tika tika = new Tika();
1035    
1036                            try {
1037                                    return tika.parseToString(
1038                                            new UnsyncByteArrayInputStream(_data));
1039                            }
1040                            catch (Exception e) {
1041                                    throw new ProcessException(e);
1042                            }
1043                    }
1044    
1045                    private static final long serialVersionUID = 1L;
1046    
1047                    private byte[] _data;
1048    
1049            }
1050    
1051    }