xref: /aosp_15_r20/external/apache-commons-io/src/test/java/org/apache/commons/io/file/PathUtilsTest.java (revision 0c4d7b72e49a04598d65c566f44504b95342d75a)
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package org.apache.commons.io.file;
19 
20 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21 import static org.junit.jupiter.api.Assertions.assertEquals;
22 import static org.junit.jupiter.api.Assertions.assertFalse;
23 import static org.junit.jupiter.api.Assertions.assertNotEquals;
24 import static org.junit.jupiter.api.Assertions.assertNotNull;
25 import static org.junit.jupiter.api.Assertions.assertNull;
26 import static org.junit.jupiter.api.Assertions.assertThrows;
27 import static org.junit.jupiter.api.Assertions.assertTrue;
28 import static org.junit.jupiter.api.Assumptions.assumeFalse;
29 
30 import java.io.File;
31 import java.io.IOException;
32 import java.io.OutputStream;
33 import java.net.URI;
34 import java.net.URISyntaxException;
35 import java.net.URL;
36 import java.nio.charset.StandardCharsets;
37 import java.nio.file.DirectoryStream;
38 import java.nio.file.FileSystem;
39 import java.nio.file.FileSystems;
40 import java.nio.file.Files;
41 import java.nio.file.LinkOption;
42 import java.nio.file.Path;
43 import java.nio.file.Paths;
44 import java.nio.file.attribute.DosFileAttributeView;
45 import java.nio.file.attribute.FileTime;
46 import java.nio.file.attribute.PosixFileAttributes;
47 import java.util.GregorianCalendar;
48 import java.util.HashMap;
49 import java.util.Iterator;
50 import java.util.Map;
51 
52 import org.apache.commons.io.FileUtils;
53 import org.apache.commons.io.FilenameUtils;
54 import org.apache.commons.io.filefilter.NameFileFilter;
55 import org.apache.commons.io.test.TestUtils;
56 import org.apache.commons.lang3.ArrayUtils;
57 import org.apache.commons.lang3.StringUtils;
58 import org.apache.commons.lang3.SystemUtils;
59 import org.junit.jupiter.api.Test;
60 
61 /**
62  * Tests {@link PathUtils}.
63  */
64 public class PathUtilsTest extends AbstractTempDirTest {
65 
66     private static final String STRING_FIXTURE = "Hello World";
67 
68     private static final byte[] BYTE_ARRAY_FIXTURE = STRING_FIXTURE.getBytes(StandardCharsets.UTF_8);
69 
70     private static final String TEST_JAR_NAME = "test.jar";
71 
72     private static final String TEST_JAR_PATH = "src/test/resources/org/apache/commons/io/test.jar";
73 
74     private static final String PATH_FIXTURE = "NOTICE.txt";
75 
76     /**
77      * Creates directory test fixtures.
78      * <ol>
79      * <li>tempDirPath/subdir</li>
80      * <li>tempDirPath/symlinked-dir -> tempDirPath/subdir</li>
81      * </ol>
82      *
83      * @return Path to tempDirPath/subdir
84      * @throws IOException if an I/O error occurs or the parent directory does not exist.
85      */
createTempSymlinkedRelativeDir()86     private Path createTempSymlinkedRelativeDir() throws IOException {
87         final Path targetDir = tempDirPath.resolve("subdir");
88         final Path symlinkDir = tempDirPath.resolve("symlinked-dir");
89         Files.createDirectory(targetDir);
90         Files.createSymbolicLink(symlinkDir, targetDir);
91         return symlinkDir;
92     }
93 
current()94     private Path current() {
95         return PathUtils.current();
96     }
97 
getLastModifiedMillis(final Path file)98     private Long getLastModifiedMillis(final Path file) throws IOException {
99         return Files.getLastModifiedTime(file).toMillis();
100     }
101 
getNonExistentPath()102     private Path getNonExistentPath() {
103         return Paths.get("/does not exist/for/certain");
104     }
105 
openArchive(final Path p, final boolean createNew)106     private FileSystem openArchive(final Path p, final boolean createNew) throws IOException {
107         if (createNew) {
108             final Map<String, String> env = new HashMap<>();
109             env.put("create", "true");
110             final URI fileUri = p.toAbsolutePath().toUri();
111             final URI uri = URI.create("jar:" + fileUri.toASCIIString());
112             return FileSystems.newFileSystem(uri, env, null);
113         }
114         return FileSystems.newFileSystem(p, (ClassLoader) null);
115     }
116 
setLastModifiedMillis(final Path file, final long millis)117     private void setLastModifiedMillis(final Path file, final long millis) throws IOException {
118         Files.setLastModifiedTime(file, FileTime.fromMillis(millis));
119     }
120 
121     @Test
testCopyDirectoryForDifferentFilesystemsWithAbsolutePath()122     public void testCopyDirectoryForDifferentFilesystemsWithAbsolutePath() throws IOException {
123         final Path archivePath = Paths.get(TEST_JAR_PATH);
124         try (FileSystem archive = openArchive(archivePath, false)) {
125             // relative jar -> absolute dir
126             Path sourceDir = archive.getPath("dir1");
127             PathUtils.copyDirectory(sourceDir, tempDirPath);
128             assertTrue(Files.exists(tempDirPath.resolve("f1")));
129 
130             // absolute jar -> absolute dir
131             sourceDir = archive.getPath("/next");
132             PathUtils.copyDirectory(sourceDir, tempDirPath);
133             assertTrue(Files.exists(tempDirPath.resolve("dir")));
134         }
135     }
136 
137     @Test
testCopyDirectoryForDifferentFilesystemsWithAbsolutePathReverse()138     public void testCopyDirectoryForDifferentFilesystemsWithAbsolutePathReverse() throws IOException {
139         try (FileSystem archive = openArchive(tempDirPath.resolve(TEST_JAR_NAME), true)) {
140             // absolute dir -> relative jar
141             Path targetDir = archive.getPath("target");
142             Files.createDirectory(targetDir);
143             final Path sourceDir = Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2").toAbsolutePath();
144             PathUtils.copyDirectory(sourceDir, targetDir);
145             assertTrue(Files.exists(targetDir.resolve("dirs-a-file-size-1")));
146 
147             // absolute dir -> absolute jar
148             targetDir = archive.getPath("/");
149             PathUtils.copyDirectory(sourceDir, targetDir);
150             assertTrue(Files.exists(targetDir.resolve("dirs-a-file-size-1")));
151         }
152     }
153 
154     @Test
testCopyDirectoryForDifferentFilesystemsWithRelativePath()155     public void testCopyDirectoryForDifferentFilesystemsWithRelativePath() throws IOException {
156         final Path archivePath = Paths.get(TEST_JAR_PATH);
157         try (FileSystem archive = openArchive(archivePath, false); final FileSystem targetArchive = openArchive(tempDirPath.resolve(TEST_JAR_NAME), true)) {
158             final Path targetDir = targetArchive.getPath("targetDir");
159             Files.createDirectory(targetDir);
160             // relative jar -> relative dir
161             Path sourceDir = archive.getPath("next");
162             PathUtils.copyDirectory(sourceDir, targetDir);
163             assertTrue(Files.exists(targetDir.resolve("dir")));
164 
165             // absolute jar -> relative dir
166             sourceDir = archive.getPath("/dir1");
167             PathUtils.copyDirectory(sourceDir, targetDir);
168             assertTrue(Files.exists(targetDir.resolve("f1")));
169         }
170     }
171 
172     @Test
testCopyDirectoryForDifferentFilesystemsWithRelativePathReverse()173     public void testCopyDirectoryForDifferentFilesystemsWithRelativePathReverse() throws IOException {
174         try (FileSystem archive = openArchive(tempDirPath.resolve(TEST_JAR_NAME), true)) {
175             // relative dir -> relative jar
176             Path targetDir = archive.getPath("target");
177             Files.createDirectory(targetDir);
178             final Path sourceDir = Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2");
179             PathUtils.copyDirectory(sourceDir, targetDir);
180             assertTrue(Files.exists(targetDir.resolve("dirs-a-file-size-1")));
181 
182             // relative dir -> absolute jar
183             targetDir = archive.getPath("/");
184             PathUtils.copyDirectory(sourceDir, targetDir);
185             assertTrue(Files.exists(targetDir.resolve("dirs-a-file-size-1")));
186         }
187     }
188 
189     @Test
testCopyFile()190     public void testCopyFile() throws IOException {
191         final Path sourceFile = Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1/file-size-1.bin");
192         final Path targetFile = PathUtils.copyFileToDirectory(sourceFile, tempDirPath);
193         assertTrue(Files.exists(targetFile));
194         assertEquals(Files.size(sourceFile), Files.size(targetFile));
195     }
196 
197     @Test
testCopyURL()198     public void testCopyURL() throws IOException {
199         final Path sourceFile = Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1/file-size-1.bin");
200         final URL url = new URL("file:///" + FilenameUtils.getPath(sourceFile.toAbsolutePath().toString()) + sourceFile.getFileName());
201         final Path targetFile = PathUtils.copyFileToDirectory(url, tempDirPath);
202         assertTrue(Files.exists(targetFile));
203         assertEquals(Files.size(sourceFile), Files.size(targetFile));
204     }
205 
206     @Test
testCreateDirectoriesAlreadyExists()207     public void testCreateDirectoriesAlreadyExists() throws IOException {
208         assertEquals(tempDirPath.getParent(), PathUtils.createParentDirectories(tempDirPath));
209     }
210 
211     @SuppressWarnings("resource") // FileSystems.getDefault() is a singleton
212     @Test
testCreateDirectoriesForRoots()213     public void testCreateDirectoriesForRoots() throws IOException {
214         for (final Path path : FileSystems.getDefault().getRootDirectories()) {
215             final Path parent = path.getParent();
216             assertNull(parent);
217             assertEquals(parent, PathUtils.createParentDirectories(path));
218         }
219     }
220 
221     @Test
testCreateDirectoriesForRootsLinkOptionNull()222     public void testCreateDirectoriesForRootsLinkOptionNull() throws IOException {
223         for (final File f : File.listRoots()) {
224             final Path path = f.toPath();
225             assertEquals(path.getParent(), PathUtils.createParentDirectories(path, (LinkOption) null));
226         }
227     }
228 
229     @Test
testCreateDirectoriesNew()230     public void testCreateDirectoriesNew() throws IOException {
231         assertEquals(tempDirPath, PathUtils.createParentDirectories(tempDirPath.resolve("child")));
232     }
233 
234     @Test
testCreateDirectoriesSymlink()235     public void testCreateDirectoriesSymlink() throws IOException {
236         final Path symlinkedDir = createTempSymlinkedRelativeDir();
237         final String leafDirName = "child";
238         final Path newDirFollowed = PathUtils.createParentDirectories(symlinkedDir.resolve(leafDirName), PathUtils.NULL_LINK_OPTION);
239         assertEquals(Files.readSymbolicLink(symlinkedDir), newDirFollowed);
240     }
241 
242     @Test
testCreateDirectoriesSymlinkClashing()243     public void testCreateDirectoriesSymlinkClashing() throws IOException {
244         final Path symlinkedDir = createTempSymlinkedRelativeDir();
245         assertEquals(symlinkedDir, PathUtils.createParentDirectories(symlinkedDir.resolve("child")));
246     }
247 
248     @Test
testGetLastModifiedFileTime_File_Present()249     public void testGetLastModifiedFileTime_File_Present() throws IOException {
250         assertNotNull(PathUtils.getLastModifiedFileTime(current().toFile()));
251     }
252 
253     @Test
testGetLastModifiedFileTime_Path_Absent()254     public void testGetLastModifiedFileTime_Path_Absent() throws IOException {
255         assertNull(PathUtils.getLastModifiedFileTime(getNonExistentPath()));
256     }
257 
258     @Test
testGetLastModifiedFileTime_Path_FileTime_Absent()259     public void testGetLastModifiedFileTime_Path_FileTime_Absent() throws IOException {
260         final FileTime fromMillis = FileTime.fromMillis(0);
261         assertEquals(fromMillis, PathUtils.getLastModifiedFileTime(getNonExistentPath(), fromMillis));
262     }
263 
264     @Test
testGetLastModifiedFileTime_Path_Present()265     public void testGetLastModifiedFileTime_Path_Present() throws IOException {
266         assertNotNull(PathUtils.getLastModifiedFileTime(current()));
267     }
268 
269     @Test
testGetLastModifiedFileTime_URI_Present()270     public void testGetLastModifiedFileTime_URI_Present() throws IOException {
271         assertNotNull(PathUtils.getLastModifiedFileTime(current().toUri()));
272     }
273 
274     @Test
testGetLastModifiedFileTime_URL_Present()275     public void testGetLastModifiedFileTime_URL_Present() throws IOException, URISyntaxException {
276         assertNotNull(PathUtils.getLastModifiedFileTime(current().toUri().toURL()));
277     }
278 
279     @Test
testGetTempDirectory()280     public void testGetTempDirectory() {
281         final Path tempDirectory = Paths.get(System.getProperty("java.io.tmpdir"));
282         assertEquals(tempDirectory, PathUtils.getTempDirectory());
283     }
284 
285     @Test
testIsDirectory()286     public void testIsDirectory() throws IOException {
287         assertFalse(PathUtils.isDirectory(null));
288 
289         assertTrue(PathUtils.isDirectory(tempDirPath));
290         try (TempFile testFile1 = TempFile.create(tempDirPath, "prefix", null)) {
291             assertFalse(PathUtils.isDirectory(testFile1.get()));
292 
293             Path ref = null;
294             try (TempDirectory tempDir = TempDirectory.create(getClass().getCanonicalName())) {
295                 ref = tempDir.get();
296                 assertTrue(PathUtils.isDirectory(tempDir.get()));
297             }
298             assertFalse(PathUtils.isDirectory(ref));
299         }
300     }
301 
302     @Test
testIsPosix()303     public void testIsPosix() throws IOException {
304         boolean isPosix;
305         try {
306             Files.getPosixFilePermissions(current());
307             isPosix = true;
308         } catch (final UnsupportedOperationException e) {
309             isPosix = false;
310         }
311         assertEquals(isPosix, PathUtils.isPosix(current()));
312     }
313 
314     @Test
testIsRegularFile()315     public void testIsRegularFile() throws IOException {
316         assertFalse(PathUtils.isRegularFile(null));
317 
318         assertFalse(PathUtils.isRegularFile(tempDirPath));
319         try (TempFile testFile1 = TempFile.create(tempDirPath, "prefix", null)) {
320             assertTrue(PathUtils.isRegularFile(testFile1.get()));
321 
322             Files.delete(testFile1.get());
323             assertFalse(PathUtils.isRegularFile(testFile1.get()));
324         }
325     }
326 
327     @Test
testNewDirectoryStream()328     public void testNewDirectoryStream() throws Exception {
329         final PathFilter pathFilter = new NameFileFilter(PATH_FIXTURE);
330         try (DirectoryStream<Path> stream = PathUtils.newDirectoryStream(current(), pathFilter)) {
331             final Iterator<Path> iterator = stream.iterator();
332             final Path path = iterator.next();
333             assertEquals(PATH_FIXTURE, path.getFileName().toString());
334             assertFalse(iterator.hasNext());
335         }
336     }
337 
338     @Test
testNewOutputStreamExistingFileAppendFalse()339     public void testNewOutputStreamExistingFileAppendFalse() throws IOException {
340         testNewOutputStreamNewFile(false);
341         testNewOutputStreamNewFile(false);
342     }
343 
344     @Test
testNewOutputStreamExistingFileAppendTrue()345     public void testNewOutputStreamExistingFileAppendTrue() throws IOException {
346         testNewOutputStreamNewFile(true);
347         final Path file = writeToNewOutputStream(true);
348         assertArrayEquals(ArrayUtils.addAll(BYTE_ARRAY_FIXTURE, BYTE_ARRAY_FIXTURE), Files.readAllBytes(file));
349     }
350 
testNewOutputStreamNewFile(final boolean append)351     public void testNewOutputStreamNewFile(final boolean append) throws IOException {
352         final Path file = writeToNewOutputStream(append);
353         assertArrayEquals(BYTE_ARRAY_FIXTURE, Files.readAllBytes(file));
354     }
355 
356     @Test
testNewOutputStreamNewFileAppendFalse()357     public void testNewOutputStreamNewFileAppendFalse() throws IOException {
358         testNewOutputStreamNewFile(false);
359     }
360 
361     @Test
testNewOutputStreamNewFileAppendTrue()362     public void testNewOutputStreamNewFileAppendTrue() throws IOException {
363         testNewOutputStreamNewFile(true);
364     }
365 
366     @Test
testNewOutputStreamNewFileInsideExistingSymlinkedDir()367     public void testNewOutputStreamNewFileInsideExistingSymlinkedDir() throws IOException {
368         final Path symlinkDir = createTempSymlinkedRelativeDir();
369         final Path file = symlinkDir.resolve("test.txt");
370         try (OutputStream outputStream = PathUtils.newOutputStream(file, new LinkOption[] {})) {
371             // empty
372         }
373         try (OutputStream outputStream = PathUtils.newOutputStream(file, null)) {
374             // empty
375         }
376         try (OutputStream outputStream = PathUtils.newOutputStream(file, true)) {
377             // empty
378         }
379         try (OutputStream outputStream = PathUtils.newOutputStream(file, false)) {
380             // empty
381         }
382     }
383 
384     @Test
testReadAttributesPosix()385     public void testReadAttributesPosix() throws IOException {
386         boolean isPosix;
387         try {
388             Files.getPosixFilePermissions(current());
389             isPosix = true;
390         } catch (final UnsupportedOperationException e) {
391             isPosix = false;
392         }
393         assertEquals(isPosix, PathUtils.readAttributes(current(), PosixFileAttributes.class) != null);
394     }
395 
396     @Test
testReadStringEmptyFile()397     public void testReadStringEmptyFile() throws IOException {
398         final Path path = Paths.get("src/test/resources/org/apache/commons/io/test-file-empty.bin");
399         assertEquals(StringUtils.EMPTY, PathUtils.readString(path, StandardCharsets.UTF_8));
400         assertEquals(StringUtils.EMPTY, PathUtils.readString(path, null));
401     }
402 
403     @Test
testReadStringSimpleUtf8()404     public void testReadStringSimpleUtf8() throws IOException {
405         final Path path = Paths.get("src/test/resources/org/apache/commons/io/test-file-simple-utf8.bin");
406         final String expected = "ABC\r\n";
407         assertEquals(expected, PathUtils.readString(path, StandardCharsets.UTF_8));
408         assertEquals(expected, PathUtils.readString(path, null));
409     }
410 
411     @Test
testSetReadOnlyFile()412     public void testSetReadOnlyFile() throws IOException {
413         final Path resolved = tempDirPath.resolve("testSetReadOnlyFile.txt");
414         // Ask now, as we are allowed before editing parent permissions.
415         final boolean isPosix = PathUtils.isPosix(tempDirPath);
416 
417         // TEMP HACK
418         assumeFalse(SystemUtils.IS_OS_LINUX);
419 
420         PathUtils.writeString(resolved, "test", StandardCharsets.UTF_8);
421         final boolean readable = Files.isReadable(resolved);
422         final boolean writable = Files.isWritable(resolved);
423         final boolean regularFile = Files.isRegularFile(resolved);
424         final boolean executable = Files.isExecutable(resolved);
425         final boolean hidden = Files.isHidden(resolved);
426         final boolean directory = Files.isDirectory(resolved);
427         final boolean symbolicLink = Files.isSymbolicLink(resolved);
428         // Sanity checks
429         assertTrue(readable);
430         assertTrue(writable);
431         // Test A
432         PathUtils.setReadOnly(resolved, false);
433         assertTrue(Files.isReadable(resolved), "isReadable");
434         assertTrue(Files.isWritable(resolved), "isWritable");
435         // Again, shouldn't blow up.
436         PathUtils.setReadOnly(resolved, false);
437         assertTrue(Files.isReadable(resolved), "isReadable");
438         assertTrue(Files.isWritable(resolved), "isWritable");
439         //
440         assertEquals(regularFile, Files.isReadable(resolved));
441         assertEquals(executable, Files.isExecutable(resolved));
442         assertEquals(hidden, Files.isHidden(resolved));
443         assertEquals(directory, Files.isDirectory(resolved));
444         assertEquals(symbolicLink, Files.isSymbolicLink(resolved));
445         // Test B
446         PathUtils.setReadOnly(resolved, true);
447         if (isPosix) {
448             // On POSIX, now that the parent is not WX, the file is not readable.
449             assertFalse(Files.isReadable(resolved), "isReadable");
450         } else {
451             assertTrue(Files.isReadable(resolved), "isReadable");
452         }
453         assertFalse(Files.isWritable(resolved), "isWritable");
454         final DosFileAttributeView dosFileAttributeView = PathUtils.getDosFileAttributeView(resolved);
455         if (dosFileAttributeView != null) {
456             assertTrue(dosFileAttributeView.readAttributes().isReadOnly());
457         }
458         if (isPosix) {
459             assertFalse(Files.isReadable(resolved));
460         } else {
461             assertEquals(regularFile, Files.isReadable(resolved));
462         }
463         assertEquals(executable, Files.isExecutable(resolved));
464         assertEquals(hidden, Files.isHidden(resolved));
465         assertEquals(directory, Files.isDirectory(resolved));
466         assertEquals(symbolicLink, Files.isSymbolicLink(resolved));
467         //
468         PathUtils.setReadOnly(resolved, false);
469         PathUtils.deleteFile(resolved);
470     }
471 
472     @Test
testTouch()473     public void testTouch() throws IOException {
474         assertThrows(NullPointerException.class, () -> FileUtils.touch(null));
475 
476         final Path file = managedTempDirPath.resolve("touch.txt");
477         Files.deleteIfExists(file);
478         assertFalse(Files.exists(file), "Bad test: test file still exists");
479         PathUtils.touch(file);
480         assertTrue(Files.exists(file), "touch() created file");
481         try (OutputStream out = Files.newOutputStream(file)) {
482             assertEquals(0, Files.size(file), "Created empty file.");
483             out.write(0);
484         }
485         assertEquals(1, Files.size(file), "Wrote one byte to file");
486         final long y2k = new GregorianCalendar(2000, 0, 1).getTime().getTime();
487         setLastModifiedMillis(file, y2k); // 0L fails on Win98
488         assertEquals(y2k, getLastModifiedMillis(file), "Bad test: set lastModified set incorrect value");
489         final long nowMillis = System.currentTimeMillis();
490         PathUtils.touch(file);
491         assertEquals(1, Files.size(file), "FileUtils.touch() didn't empty the file.");
492         assertNotEquals(y2k, getLastModifiedMillis(file), "FileUtils.touch() changed lastModified");
493         final int delta = 3000;
494         assertTrue(getLastModifiedMillis(file) >= nowMillis - delta, "FileUtils.touch() changed lastModified to more than now-3s");
495         assertTrue(getLastModifiedMillis(file) <= nowMillis + delta, "FileUtils.touch() changed lastModified to less than now+3s");
496     }
497 
498     @Test
testWriteStringToFile1()499     public void testWriteStringToFile1() throws Exception {
500         final Path file = tempDirPath.resolve("write.txt");
501         PathUtils.writeString(file, "Hello /u1234", StandardCharsets.UTF_8);
502         final byte[] text = "Hello /u1234".getBytes(StandardCharsets.UTF_8);
503         TestUtils.assertEqualContent(text, file);
504     }
505 
506     /**
507      * Tests newOutputStream() here and don't use Files.write obviously.
508      */
writeToNewOutputStream(final boolean append)509     private Path writeToNewOutputStream(final boolean append) throws IOException {
510         final Path file = tempDirPath.resolve("test1.txt");
511         try (OutputStream os = PathUtils.newOutputStream(file, append)) {
512             os.write(BYTE_ARRAY_FIXTURE);
513         }
514         return file;
515     }
516 
517 }
518