1package org.junit.rules;
2
3import java.io.File;
4import java.io.IOException;
5
6import org.junit.Rule;
7
8/**
9 * The TemporaryFolder Rule allows creation of files and folders that should
10 * be deleted when the test method finishes (whether it passes or
11 * fails). Whether the deletion is successful or not is not checked by this rule.
12 * No exception will be thrown in case the deletion fails.
13 *
14 * <p>Example of usage:
15 * <pre>
16 * public static class HasTempFolder {
17 *  &#064;Rule
18 *  public TemporaryFolder folder= new TemporaryFolder();
19 *
20 *  &#064;Test
21 *  public void testUsingTempFolder() throws IOException {
22 *      File createdFile= folder.newFile(&quot;myfile.txt&quot;);
23 *      File createdFolder= folder.newFolder(&quot;subfolder&quot;);
24 *      // ...
25 *     }
26 * }
27 * </pre>
28 *
29 * @since 4.7
30 */
31public class TemporaryFolder extends ExternalResource {
32    private final File parentFolder;
33    private File folder;
34
35    public TemporaryFolder() {
36        this(null);
37    }
38
39    public TemporaryFolder(File parentFolder) {
40        this.parentFolder = parentFolder;
41    }
42
43    @Override
44    protected void before() throws Throwable {
45        create();
46    }
47
48    @Override
49    protected void after() {
50        delete();
51    }
52
53    // testing purposes only
54
55    /**
56     * for testing purposes only. Do not use.
57     */
58    public void create() throws IOException {
59        folder = createTemporaryFolderIn(parentFolder);
60    }
61
62    /**
63     * Returns a new fresh file with the given name under the temporary folder.
64     */
65    public File newFile(String fileName) throws IOException {
66        File file = new File(getRoot(), fileName);
67        if (!file.createNewFile()) {
68            throw new IOException(
69                    "a file with the name \'" + fileName + "\' already exists in the test folder");
70        }
71        return file;
72    }
73
74    /**
75     * Returns a new fresh file with a random name under the temporary folder.
76     */
77    public File newFile() throws IOException {
78        return File.createTempFile("junit", null, getRoot());
79    }
80
81    /**
82     * Returns a new fresh folder with the given name under the temporary
83     * folder.
84     */
85    public File newFolder(String folder) throws IOException {
86        return newFolder(new String[]{folder});
87    }
88
89    /**
90     * Returns a new fresh folder with the given name(s) under the temporary
91     * folder.
92     */
93    public File newFolder(String... folderNames) throws IOException {
94        File file = getRoot();
95        for (int i = 0; i < folderNames.length; i++) {
96            String folderName = folderNames[i];
97            validateFolderName(folderName);
98            file = new File(file, folderName);
99            if (!file.mkdir() && isLastElementInArray(i, folderNames)) {
100                throw new IOException(
101                        "a folder with the name \'" + folderName + "\' already exists");
102            }
103        }
104        return file;
105    }
106
107    /**
108     * Validates if multiple path components were used while creating a folder.
109     *
110     * @param folderName
111     *            Name of the folder being created
112     */
113    private void validateFolderName(String folderName) throws IOException {
114        File tempFile = new File(folderName);
115        if (tempFile.getParent() != null) {
116            String errorMsg = "Folder name cannot consist of multiple path components separated by a file separator."
117                    + " Please use newFolder('MyParentFolder','MyFolder') to create hierarchies of folders";
118            throw new IOException(errorMsg);
119        }
120    }
121
122    private boolean isLastElementInArray(int index, String[] array) {
123        return index == array.length - 1;
124    }
125
126    /**
127     * Returns a new fresh folder with a random name under the temporary folder.
128     */
129    public File newFolder() throws IOException {
130        return createTemporaryFolderIn(getRoot());
131    }
132
133    private File createTemporaryFolderIn(File parentFolder) throws IOException {
134        File createdFolder = File.createTempFile("junit", "", parentFolder);
135        createdFolder.delete();
136        createdFolder.mkdir();
137        return createdFolder;
138    }
139
140    /**
141     * @return the location of this temporary folder.
142     */
143    public File getRoot() {
144        if (folder == null) {
145            throw new IllegalStateException(
146                    "the temporary folder has not yet been created");
147        }
148        return folder;
149    }
150
151    /**
152     * Delete all files and folders under the temporary folder. Usually not
153     * called directly, since it is automatically applied by the {@link Rule}
154     */
155    public void delete() {
156        if (folder != null) {
157            recursiveDelete(folder);
158        }
159    }
160
161    private void recursiveDelete(File file) {
162        File[] files = file.listFiles();
163        if (files != null) {
164            for (File each : files) {
165                recursiveDelete(each);
166            }
167        }
168        file.delete();
169    }
170}
171