001package org.junit.rules;
002
003import java.io.File;
004import java.io.IOException;
005
006import org.junit.Rule;
007
008/**
009 * The TemporaryFolder Rule allows creation of files and folders that should
010 * be deleted when the test method finishes (whether it passes or
011 * fails). Whether the deletion is successful or not is not checked by this rule.
012 * No exception will be thrown in case the deletion fails.
013 *
014 * <p>Example of usage:
015 * <pre>
016 * public static class HasTempFolder {
017 *  &#064;Rule
018 *  public TemporaryFolder folder= new TemporaryFolder();
019 *
020 *  &#064;Test
021 *  public void testUsingTempFolder() throws IOException {
022 *      File createdFile= folder.newFile(&quot;myfile.txt&quot;);
023 *      File createdFolder= folder.newFolder(&quot;subfolder&quot;);
024 *      // ...
025 *     }
026 * }
027 * </pre>
028 *
029 * @since 4.7
030 */
031public class TemporaryFolder extends ExternalResource {
032    private final File parentFolder;
033    private File folder;
034
035    public TemporaryFolder() {
036        this(null);
037    }
038
039    public TemporaryFolder(File parentFolder) {
040        this.parentFolder = parentFolder;
041    }
042
043    @Override
044    protected void before() throws Throwable {
045        create();
046    }
047
048    @Override
049    protected void after() {
050        delete();
051    }
052
053    // testing purposes only
054
055    /**
056     * for testing purposes only. Do not use.
057     */
058    public void create() throws IOException {
059        folder = createTemporaryFolderIn(parentFolder);
060    }
061
062    /**
063     * Returns a new fresh file with the given name under the temporary folder.
064     */
065    public File newFile(String fileName) throws IOException {
066        File file = new File(getRoot(), fileName);
067        if (!file.createNewFile()) {
068            throw new IOException(
069                    "a file with the name \'" + fileName + "\' already exists in the test folder");
070        }
071        return file;
072    }
073
074    /**
075     * Returns a new fresh file with a random name under the temporary folder.
076     */
077    public File newFile() throws IOException {
078        return File.createTempFile("junit", null, getRoot());
079    }
080
081    /**
082     * Returns a new fresh folder with the given name under the temporary
083     * folder.
084     */
085    public File newFolder(String folder) throws IOException {
086        return newFolder(new String[]{folder});
087    }
088
089    /**
090     * Returns a new fresh folder with the given name(s) under the temporary
091     * folder.
092     */
093    public File newFolder(String... folderNames) throws IOException {
094        File file = getRoot();
095        for (int i = 0; i < folderNames.length; i++) {
096            String folderName = folderNames[i];
097            validateFolderName(folderName);
098            file = new File(file, folderName);
099            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}