/*
 * Redistribution and use of this software and associated documentation ("Software"), with or
 * without modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain copyright statements and notices. Redistributions
 * must also contain a copy of this document.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice, this list of
 * conditions and the following disclaimer in the documentation and/or other materials provided with
 * the distribution.
 *
 * 3. The name "Exolab" must not be used to endorse or promote products derived from this Software
 * without prior written permission of Intalio, Inc. For written permission, please contact
 * info@exolab.org.
 *
 * 4. Products derived from this Software may not be called "Exolab" nor may "Exolab" appear in
 * their names without prior written permission of Intalio, Inc. Exolab is a registered trademark of
 * Intalio, Inc.
 *
 * 5. Due credit should be given to the Exolab Project (http://www.exolab.org/).
 *
 * THIS SOFTWARE IS PROVIDED BY INTALIO, INC. AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL INTALIO, INC. OR ITS
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
 * WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Copyright 2002 (C) Intalio, Inc. All Rights Reserved.
 *
 * $Id: FileServices.java 6787 2007-01-29 06:00:49Z ekuns $
 */
package org.castor.xmlctf.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Enumeration;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;

/**
 * This class is a set of tools for manipulating files needed by the CTF.
 *
 * @author <a href="mailto:blandin@intalio.com">Arnaud Blandin</a>
 * @version $Revision: 6787 $ $Date: 2005-03-05 06:42:06 -0700 (Sat, 05 Mar 2005) $
 */
public final class FileServices {

  /** The string for a CVS subdirectory. */
  public static final String CVS = "CVS";
  /** The string for a Subversion subdirectory. */
  public static final String SVN = ".svn";
  /** The string for a git subdirectory. */
  public static final String GIT = ".git";
  /** The string for an extension for a XSD file. */
  public static final String XSD = ".xsd";
  /** The string for an extension for a XML file. */
  public static final String XML = ".xml";
  /** The string for an extension for a Java file. */
  public static final String JAVA = ".java";
  /** The string for an extension for a JAR file. */
  public static final String JAR = ".jar";

  /** Buffer size used for copying files. */
  private static final int BUF_SIZE = 16 * 1024;

  /**
   * No-arg constructor. Private as we're a utility class.
   */
  private FileServices() {
    // Nothing to do
  }

  /**
   * Copy all the needed documents (java, xsd, xml file) of given file (jar or directory) to a
   * specified directory.
   *
   * @param file the file that contains the entries to copy
   * @param root the destination directory to copy files to
   * @throws IOException if an error occurs while copying files
   */
  public static void copySupportFiles(final File file, final File root) throws IOException {
    if (file == null) {
      throw new IllegalArgumentException("The Entry file is null");
    }
    if (root == null) {
      throw new IllegalArgumentException("The destination directory is null");
    }

    if (file.isDirectory()) {
      copySupportFilesForDirectory(file, root);
    } else if (file.getName().endsWith(JAR)) {
      copySupportFilesForJarFile(file, root);
    } else {
      // ignore other file type
    }
  }

  /**
   * Unzips support files out of the JAR to our output directory. See {@link #isSupportFile(String)}
   * for the definition of support file. This method does not totally unzip the JAR file. It only
   * extracts certain important files out of the JAR.
   *
   * @param file the file that contains the entries to copy
   * @param root the destination directory to copy files to
   * @throws IOException if an error occurs while copying files
   */
  private static void copySupportFilesForJarFile(final File file, final File root)
      throws IOException {
    JarFile jar = new JarFile(file);
    for (Enumeration e = jar.entries(); e.hasMoreElements();) {
      ZipEntry entry = (ZipEntry) e.nextElement();
      if (isSupportFile(entry.getName())) {
        InputStream src = jar.getInputStream(entry);
        File out = new File(root, entry.getName());
        out.getParentFile().mkdirs();
        copy(src, new FileOutputStream(out));
        src.close();
      }
    }
  }

  /**
   * Copies support files from one directory to our output directory, recursing into other
   * directories. See {@link #isSupportFile(String)} for the definition of support file.
   *
   * @param file the file that contains the entries to copy
   * @param root the destination directory to copy files to
   * @throws IOException if an error occurs while copying files
   */
  private static void copySupportFilesForDirectory(final File file, final File root)
      throws IOException {
    File[] entries = file.listFiles();
    for (int i = 0; i < entries.length; i++) {
      File tempEntry = entries[i];
      if (isSupportFile(tempEntry.getName())) {
        File out = new File(root, tempEntry.getName());
        out.getParentFile().mkdir();
        try (FileOutputStream dest = new FileOutputStream(out);
            InputStream src = new FileInputStream(tempEntry);) {
          copy(src, dest);
        }
      } else if (tempEntry.isDirectory()) {
        File out = new File(root, tempEntry.getName());
        out.mkdir();
        copySupportFiles(tempEntry, out);
      }
    }
  }

  /**
   * Copies an InputStream into a OutputStream.
   *
   * @param src Source input stream
   * @param dest Destination output stream
   * @throws IOException if an error occurs while copying files
   */
  private static void copy(final InputStream src, final OutputStream dest) throws IOException {
    byte[] buf = new byte[BUF_SIZE];
    int read;
    while (true) {
      read = src.read(buf, 0, BUF_SIZE);
      if (read == -1) {
        break;
      }
      dest.write(buf, 0, read);
    }
  }

  /**
   * Return true if the file is a support file for the test. A support file is a schema or a java
   * file.
   *
   * @param name File name to check to see if it represents a support file
   * @return true if the file is a support file for the test.
   */
  private static boolean isSupportFile(final String name) {
    return name.endsWith(XSD) || name.endsWith(JAVA) || name.endsWith(XML);
  }

  /**
   * Return true if the file provided is a special source control management directory that we want
   * to ignore.
   * 
   * @param name Name of a directory
   * @return true if the file provided is an SCM directory
   */
  public static boolean isScmDirectory(final String name) {
    return name.endsWith(FileServices.CVS) || name.equals(FileServices.SVN);
  }

}
