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