001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License.
016 */
017package org.apache.juneau.internal;
018
019import static org.apache.juneau.common.utils.StringUtils.*;
020import static org.apache.juneau.common.utils.ThrowableUtils.*;
021
022import java.io.*;
023import java.nio.file.*;
024
025import org.apache.juneau.*;
026import org.apache.juneau.common.utils.*;
027
028/**
029 * File utilities.
030 *
031 * <h5 class='section'>See Also:</h5><ul>
032
033 * </ul>
034 */
035public class FileUtils {
036
037   /**
038    * Same as {@link File#mkdirs()} except throws a RuntimeExeption if directory could not be created.
039    *
040    * @param f The directory to create.  Must not be <jk>null</jk>.
041    * @param clean If <jk>true</jk>, deletes the contents of the directory if it already exists.
042    * @return The same file.
043    * @throws RuntimeException if directory could not be created.
044    */
045   public static File mkdirs(File f, boolean clean) {
046      Utils.assertArgNotNull("f", f);
047      if (f.exists()) {
048         if (clean) {
049            if (! delete(f))
050               throw new BasicRuntimeException("Could not clean directory ''{0}''", f.getAbsolutePath());
051         } else {
052            return f;
053         }
054      }
055      if (! f.mkdirs())
056         throw new BasicRuntimeException("Could not create directory ''{0}''", f.getAbsolutePath());
057      return f;
058   }
059
060   /**
061    * Same as {@link #mkdirs(String, boolean)} but uses String path.
062    *
063    * @param path The path of the directory to create.  Must not be <jk>null</jk>
064    * @param clean If <jk>true</jk>, deletes the contents of the directory if it already exists.
065    * @return The directory.
066    */
067   public static File mkdirs(String path, boolean clean) {
068      Utils.assertArgNotNull("path", path);
069      return mkdirs(new File(path), clean);
070   }
071
072   /**
073    * Recursively deletes a file or directory.
074    *
075    * @param f The file or directory to delete.
076    * @return <jk>true</jk> if file or directory was successfully deleted.
077    */
078   public static boolean delete(File f) {
079      if (f == null)
080         return true;
081      if (f.isDirectory()) {
082         File[] cf = f.listFiles();
083         if (cf != null)
084            for (File c : cf)
085               delete(c);
086      }
087      return f.delete();
088   }
089
090   /**
091    * Creates a file if it doesn't already exist using {@link File#createNewFile()}.
092    *
093    * <p>
094    * Throws a {@link RuntimeException} if the file could not be created.
095    *
096    * @param f The file to create.
097    */
098   public static void create(File f) {
099      if (f.exists())
100         return;
101      try {
102         if (! f.createNewFile())
103            throw new BasicRuntimeException("Could not create file ''{0}''", f.getAbsolutePath());
104      } catch (IOException e) {
105         throw asRuntimeException(e);
106      }
107   }
108
109   /**
110    * Updates the modified timestamp on the specified file.
111    *
112    * <p>
113    * Method ensures that the timestamp changes even if it's been modified within the past millisecond.
114    *
115    * @param f The file to modify the modified timestamp on.
116    */
117   public static void modifyTimestamp(File f) {
118      long lm = f.lastModified();
119      long l = System.currentTimeMillis();
120      if (lm == l)
121         l++;
122      if (! f.setLastModified(l))
123         throw new BasicRuntimeException("Could not modify timestamp on file ''{0}''", f.getAbsolutePath());
124
125      // Linux only gives 1s precision, so set the date 1s into the future.
126      if (lm == f.lastModified()) {
127         l += 1000;
128         if (! f.setLastModified(l))
129            throw new BasicRuntimeException("Could not modify timestamp on file ''{0}''", f.getAbsolutePath());
130      }
131   }
132
133   /**
134    * Create a temporary file with the specified name.
135    *
136    * <p>
137    * The name is broken into file name and suffix, and the parts are passed to
138    * {@link File#createTempFile(String, String)}.
139    *
140    * <p>
141    * {@link File#deleteOnExit()} is called on the resulting file before being returned by this method.
142    *
143    * @param name The file name
144    * @return A newly-created temporary file.
145    * @throws IOException Thrown by underlying stream.
146    */
147   public static File createTempFile(String name) throws IOException {
148      String[] parts = name.split("\\.");
149      File f = File.createTempFile(parts[0], "." + parts[1]);
150      f.deleteOnExit();
151      return f;
152   }
153
154   /**
155    * Create a temporary file with the specified name and specified contents.
156    *
157    * <p>
158    * The name is broken into file name and suffix, and the parts are passed to
159    * {@link File#createTempFile(String, String)}.
160    *
161    * <p>
162    * {@link File#deleteOnExit()} is called on the resulting file before being returned by this method.
163    *
164    * @param name The file name
165    * @param contents The file contents.
166    * @return A newly-created temporary file.
167    * @throws IOException Thrown by underlying stream.
168    */
169   public static File createTempFile(String name, String contents) throws IOException {
170      File f = createTempFile(name);
171      try (Reader r = new StringReader(contents); Writer w = new FileWriter(f)) {
172         IOUtils.pipe(r, w);
173         w.flush();
174      }
175      return f;
176   }
177
178   /**
179    * Strips the extension from a file name.
180    *
181    * @param name The file name.
182    * @return The file name without the extension, or <jk>null</jk> if name was <jk>null</jk>.
183    */
184   public static String getBaseName(String name) {
185      if (name == null)
186         return null;
187      int i = name.lastIndexOf('.');
188      if (i == -1)
189         return name;
190      return name.substring(0, i);
191   }
192
193   /**
194    * Returns the extension from a file name.
195    *
196    * @param name The file name.
197    * @return The the extension, or <jk>null</jk> if name was <jk>null</jk>.
198    */
199   public static String getExtension(String name) {
200      if (name == null)
201         return null;
202      int i = name.lastIndexOf('.');
203      if (i == -1)
204         return "";
205      return name.substring(i+1);
206   }
207
208   /**
209    * Returns <jk>true</jk> if the specified file exists in the specified directory.
210    *
211    * @param dir The directory.
212    * @param fileName The file name.
213    * @return <jk>true</jk> if the specified file exists in the specified directory.
214    */
215   public static boolean exists(File dir, String fileName) {
216      if (dir == null || fileName == null)
217         return false;
218      return Files.exists(dir.toPath().resolve(fileName));
219   }
220
221   /**
222    * Returns <jk>true</jk> if the specified file name contains the specified extension.
223    *
224    * @param name The file name.
225    * @param ext The extension.
226    * @return <jk>true</jk> if the specified file name contains the specified extension.
227    */
228   public static boolean hasExtension(String name, String ext) {
229      if (name == null || ext == null)
230         return false;
231      return ext.equals(getExtension(name));
232   }
233
234   /**
235    * Given an arbitrary path, returns the file name portion of that path.
236    *
237    * @param path The path to check.
238    * @return The file name.
239    */
240   public static String getFileName(String path) {
241      if (Utils.isEmpty(path))
242         return null;
243      path = trimTrailingSlashes(path);
244      int i = path.lastIndexOf('/');
245      return i == -1 ? path : path.substring(i+1);
246   }
247}