content: /**
   * Licensed to the Apache Software Foundation (ASF) under one or more
   * contributor license agreements.  See the NOTICE file distributed with
   * this work for additional information regarding copyright ownership.
   * The ASF licenses this file to You under the Apache License, Version 2.0
   * (the "License"); you may not use this file except in compliance with
   * the License.  You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.activemq.util;
  
  import java.io.File;
  import java.io.FileInputStream;
  import java.io.FileOutputStream;
  import java.io.FilenameFilter;
  import java.io.IOException;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.nio.file.Files;
  import java.nio.file.Path;
  import java.nio.file.StandardCopyOption;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.Stack;
  
  /**
   * Collection of File and Folder utility methods.
   */
  public final class IOHelper {
  
      protected static final int MAX_DIR_NAME_LENGTH;
      protected static final int MAX_FILE_NAME_LENGTH;
      private static final int DEFAULT_BUFFER_SIZE = 4096;
  
      private IOHelper() {
      }
  
      public static String getDefaultDataDirectory() {
          return getDefaultDirectoryPrefix() + "activemq-data";
      }
  
      public static String getDefaultStoreDirectory() {
          return getDefaultDirectoryPrefix() + "amqstore";
      }
  
      /**
       * Allows a system property to be used to overload the default data
       * directory which can be useful for forcing the test cases to use a target/
       * prefix
       */
      public static String getDefaultDirectoryPrefix() {
          try {
              return System.getProperty("org.apache.activemq.default.directory.prefix", "");
          } catch (Exception e) {
              return "";
          }
      }
  
      /**
       * Converts any string into a string that is safe to use as a file name. The
       * result will only include ascii characters and numbers, and the "-","_",
       * and "." characters.
       *
       * @param name
       * @return safe name of the directory
       */
      public static String toFileSystemDirectorySafeName(String name) {
          return toFileSystemSafeName(name, true, MAX_DIR_NAME_LENGTH);
      }
  
      public static String toFileSystemSafeName(String name) {
          return toFileSystemSafeName(name, false, MAX_FILE_NAME_LENGTH);
      }
  
      /**
       * Converts any string into a string that is safe to use as a file name. The
       * result will only include ascii characters and numbers, and the "-","_",
       * and "." characters.
       *
       * @param name
       * @param dirSeparators
       * @param maxFileLength
       * @return file system safe name
       */
      public static String toFileSystemSafeName(String name, boolean dirSeparators, int maxFileLength) {
          int size = name.length();
          StringBuilder rc = new StringBuilder(size * 2);
          for (int i = 0; i < size; i++) {
              char c = name.charAt(i);
              boolean valid = c >= 'a' && c <= 'z';
              valid = valid || (c >= 'A' && c <= 'Z');
              valid = valid || (c >= '0' && c <= '9');
              valid = valid || (c == '_') || (c == '-') || (c == '.') || (c == '#') || (dirSeparators && ((c == '/') || (c == '\\')));
  
              if (valid) {
                  rc.append(c);
              } else {
                  // Encode the character using hex notation
                  rc.append('#');
                  rc.append(HexSupport.toHexFromInt(c, true));
              }
          }
          String result = rc.toString();
          if (result.length() > maxFileLength) {
              result = result.substring(result.length() - maxFileLength, result.length());
          }
          return result;
      }
  
      public static boolean delete(File top) {
          boolean result = true;
          Stack<File> files = new Stack<File>();
          // Add file to the stack to be processed...
          files.push(top);
          // Process all files until none remain...
          while (!files.isEmpty()) {
              File file = files.pop();
              if (file.isDirectory()) {
                  File list[] = file.listFiles();
                  if (list == null || list.length == 0) {
                      // The current directory contains no entries...
                      // delete directory and continue...
                      result &= file.delete();
                  } else {
                      // Add back the directory since it is not empty....
                      // and when we process it again it will be empty and can be
                      // deleted safely...
                      files.push(file);
                      for (File dirFile : list) {
                          if (dirFile.isDirectory()) {
                              // Place the directory on the stack...
                              files.push(dirFile);
                          } else {
                              // This is a simple file, delete it...
                              result &= dirFile.delete();
                          }
                      }
                  }
              } else {
                  // This is a simple file, delete it...
                  result &= file.delete();
              }
          }
          return result;
      }
  
      public static boolean deleteFile(File fileToDelete) {
          if (fileToDelete == null || !fileToDelete.exists()) {
              return true;
          }
          boolean result = deleteChildren(fileToDelete);
          result &= fileToDelete.delete();
          return result;
      }
  
      public static boolean deleteChildren(File parent) {
          if (parent == null || !parent.exists()) {
              return false;
          }
          boolean result = true;
          if (parent.isDirectory()) {
              File[] files = parent.listFiles();
              if (files == null) {
                  result = false;
              } else {
                  for (int i = 0; i < files.length; i++) {
                      File file = files[i];
                      if (file.getName().equals(".") || file.getName().equals("..")) {
                          continue;
                      }
                      if (file.isDirectory()) {
                          result &= deleteFile(file);
                      } else {
                          result &= file.delete();
                      }
                  }
              }
          }
  
          return result;
      }
  
      public static void moveFile(File src, File targetDirectory) throws IOException {
          if (!src.renameTo(new File(targetDirectory, src.getName()))) {
  
              // If rename fails we must do a true deep copy instead.
              Path sourcePath = src.toPath();
              Path targetDirPath = targetDirectory.toPath();
  
              try {
                  Files.move(sourcePath, targetDirPath.resolve(sourcePath.getFileName()), StandardCopyOption.REPLACE_EXISTING);
              } catch (IOException ex) {
                  throw new IOException("Failed to move " + src + " to " + targetDirectory + " - " + ex.getMessage());
              }
          }
      }
  
      public static void moveFiles(File srcDirectory, File targetDirectory, FilenameFilter filter) throws IOException {
          if (!srcDirectory.isDirectory()) {
              throw new IOException("source is not a directory");
          }
  
          if (targetDirectory.exists() && !targetDirectory.isDirectory()) {
              throw new IOException("target exists and is not a directory");
          } else {
              mkdirs(targetDirectory);
          }
  
          List<File> filesToMove = new ArrayList<File>();
          getFiles(srcDirectory, filesToMove, filter);
  
          for (File file : filesToMove) {
              if (!file.isDirectory()) {
                  moveFile(file, targetDirectory);
              }
          }
      }
  
      public static void copyFile(File src, File dest) throws IOException {
          copyFile(src, dest, null);
      }
  
      public static void copyFile(File src, File dest, FilenameFilter filter) throws IOException {
          if (src.getCanonicalPath().equals(dest.getCanonicalPath()) == false) {
              if (src.isDirectory()) {
  
                  mkdirs(dest);
                  List<File> list = getFiles(src, filter);
                  for (File f : list) {
                      if (f.isFile()) {
                          File target = new File(getCopyParent(src, dest, f), f.getName());
                          copySingleFile(f, target);
                      }
                  }
  
              } else if (dest.isDirectory()) {
                  mkdirs(dest);
                  File target = new File(dest, src.getName());
                  copySingleFile(src, target);
              } else {
                  copySingleFile(src, dest);
              }
          }
      }
  
      static File getCopyParent(File from, File to, File src) {
          File result = null;
          File parent = src.getParentFile();
          String fromPath = from.getAbsolutePath();
          if (parent.getAbsolutePath().equals(fromPath)) {
              // one level down
              result = to;
          } else {
              String parentPath = parent.getAbsolutePath();
              String path = parentPath.substring(fromPath.length());
              result = new File(to.getAbsolutePath() + File.separator + path);
          }
          return result;
      }
  
      static List<File> getFiles(File dir, FilenameFilter filter) {
          List<File> result = new ArrayList<File>();
          getFiles(dir, result, filter);
          return result;
      }
  
      static void getFiles(File dir, List<File> list, FilenameFilter filter) {
          if (!list.contains(dir)) {
              list.add(dir);
              String[] fileNames = dir.list(filter);
              for (int i = 0; i < fileNames.length; i++) {
                  File f = new File(dir, fileNames[i]);
                  if (f.isFile()) {
                      list.add(f);
                  } else {
                      getFiles(dir, list, filter);
                  }
              }
          }
      }
  
      public static void copySingleFile(File src, File dest) throws IOException {
          FileInputStream fileSrc = new FileInputStream(src);
          FileOutputStream fileDest = new FileOutputStream(dest);
          copyInputStream(fileSrc, fileDest);
      }
  
      public static void copyInputStream(InputStream in, OutputStream out) throws IOException {
          try {
              byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
              int len = in.read(buffer);
              while (len >= 0) {
                  out.write(buffer, 0, len);
                  len = in.read(buffer);
              }
          } finally {
              in.close();
              out.close();
          }
      }
  
      static {
          MAX_DIR_NAME_LENGTH = Integer.getInteger("MaximumDirNameLength", 200);
          MAX_FILE_NAME_LENGTH = Integer.getInteger("MaximumFileNameLength", 64);
      }
  
      public static int getMaxDirNameLength() {
          return MAX_DIR_NAME_LENGTH;
      }
  
      public static int getMaxFileNameLength() {
          return MAX_FILE_NAME_LENGTH;
      }
  
      public static void mkdirs(File dir) throws IOException {
          if (dir.exists()) {
              if (!dir.isDirectory()) {
                  throw new IOException("Failed to create directory '" + dir +
                                        "', regular file already existed with that name");
              }
  
          } else {
              if (!dir.mkdirs()) {
                  if ( dir.exists() && dir.isDirectory() ) {
                      // Directory created in parallel
                      return;
                  }
                  throw new IOException("Failed to create directory '" + dir + "'");
              }
          }
      }
  }
  