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