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