Java/File Input Output/Path

Материал из Java эксперт
Перейти к: навигация, поиск

Содержание

Absolutize a relative resource path on the given absolute base path.

      

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
 Derby - Class org.apache.derby.iapi.util.PropertyUtil
 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.
 */
public class Main {

  /**
   * Absolutize a relative resource path on the given absolute base path.
   *
   * @param path The absolute base path
   * @param resource The relative resource path
   * @return The absolutized resource path
   */
  public static String absolutize(String path, String resource) {
      if (path == null || path.isEmpty()) {
          return resource;
      } else if (resource == null || resource.isEmpty()) {
          return path;
      } else if (resource.charAt(0) == "/") {
          // Resource path is already absolute
          return resource;
      }
      boolean slash = (path.charAt(path.length() - 1) == "/");
      
      StringBuffer b = new StringBuffer(path.length() + 1 + resource.length());
      b.append(path);
      if (!slash) {
          b.append("/");
      } 
      b.append(resource);
      return b.toString();
  }

}





A utility class for manipulating paths

     
/*
 * Copyright (C) 2008 Rob Manning
 * manningr@users.sourceforge.net
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
/**
 * A utility class for manipulating paths
 * 
 * @author manningr
 */
public class PathUtilsImpl 
{
  /**
   * Constructs a path out of the pathElements.
   * 
   * @param prependSlash
   *           if true this will ensure that the result begins with "/".
   * @param pathElements
   *           the strings to connect. They can have "/" in them which will be de-duped in the result, if
   *           necessary.
   * @return the path that was constructed.
   */
  public String buildPath(boolean prependSlash, String... pathElements)
  {
    StringBuilder result = new StringBuilder("/");
    for (String pathElement : pathElements)
    {
      result.append(pathElement).append("/");
    }
    String retVal = result.toString().replace("//", "/").replace("//", "/");
    if (retVal.endsWith("/"))
    {
      retVal = retVal.substring(0, retVal.length() - 1);
    }
    return retVal;
  }
  /**
   * Returns the file part of the specified path.
   * 
   * @param path
   *           the path
   * @return the file part of the path
   */
  public String getFileFromPath(String path)
  {
    if (path.indexOf("/") == -1) { return path; }
    int slashIndex = path.lastIndexOf("/");
    return path.substring(slashIndex+1);
  }
}





Build a directory path - creating directories if neccesary

 
import java.io.File;
import java.io.IOException;
public class Utils {
  /**
   * Build a directory path - creating directories if neccesary
   * 
   * @param file
   * @return true if the directory exists, or making it was successful
   */
  public static boolean buildDirectory(File file) {
      return file.exists() || file.mkdirs();
  }
}





Build a path, but do not create it

 
import java.io.File;
import java.io.IOException;
public class Utils {
  /**
   * Build a path- but do not create it
   * 
   * @param parent
   * @param subDirectory
   * @return a File representing the path
   */
  public static File getDirectoryPath(File parent, String subDirectory) {
      File result = null;
      if (parent != null) {
          result = new File(parent, subDirectory);
      }
      return result;
  }
}





Build a relative path to the given base path

  
/*
  * JBoss, Home of Professional Open Source
  * Copyright 2005, JBoss Inc., and individual contributors as indicated
  * by the @authors tag. See the copyright.txt in the distribution for a
  * full listing of individual contributors.
  *
  * This is free software; you can redistribute it and/or modify it
  * under the terms of the GNU Lesser General Public License as
  * published by the Free Software Foundation; either version 2.1 of
  * the License, or (at your option) any later version.
  *
  * This software is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  * Lesser General Public License for more details.
  *
  * You should have received a copy of the GNU Lesser General Public
  * License along with this software; if not, write to the Free
  * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  */

import java.io.File;
import java.io.IOException;
public class Main{
  
  /**
   * Build a relative path to the given base path.
   * @param base - the path used as the base
   * @param path - the path to compute relative to the base path
   * @return A relative path from base to path
   * @throws IOException
   */ 
  public static String findRelativePath(String base, String path)
     throws IOException
  {
     String a = new File(base).getCanonicalFile().toURI().getPath();
     String b = new File(path).getCanonicalFile().toURI().getPath();
     String[] basePaths = a.split("/");
     String[] otherPaths = b.split("/");
     int n = 0;
     for(; n < basePaths.length && n < otherPaths.length; n ++)
     {
        if( basePaths[n].equals(otherPaths[n]) == false )
           break;
     }
     System.out.println("Common length: "+n);
     StringBuffer tmp = new StringBuffer("../");
     for(int m = n; m < basePaths.length - 1; m ++)
        tmp.append("../");
     for(int m = n; m < otherPaths.length; m ++)
     {
        tmp.append(otherPaths[m]);
        tmp.append("/");
     }
     return tmp.toString();
  }
}





Change File Name To Class Name

      
/*
 * Copyright (C) 2001-2003 Colin Bell
 * colbell@users.sourceforge.net
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
import java.io.*;
import java.text.NumberFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * General purpose utilities functions.
 *
 * @author 
 */
public class Utilities
{
  /**
   * Change the passed file name to its corresponding class name. E.G.
   * change &quot;Utilities.class&quot; to &quot;Utilities&quot;.
   *
   * @param  name  Class name to be changed. If this does not represent
   *         a Java class then <TT>null</TT> is returned.
   *
   * @throws IllegalArgumentException  If a null <TT>name</TT> passed.
   */
  public static String changeFileNameToClassName(String name)
  {
     if (name == null)
     {
        throw new IllegalArgumentException("File Name == null");
     }
     String className = null;
     if (name.toLowerCase().endsWith(".class"))
     {
        className = name.replace("/", ".");
        className = className.replace("\\", ".");
        className = className.substring(0, className.length() - 6);
     }
     return className;
  }
}





Checks, whether the child directory is a subdirectory of the base directory.

  
import java.io.File;
import java.io.IOException;
/* 
 * JCommon : a free general purpose class library for the Java(tm) platform
 * 
 *
 * (C) Copyright 2000-2005, by Object Refinery Limited and Contributors.
 * 
 * Project Info:  http://www.jfree.org/jcommon/index.html
 *
 * This library is free software; you can redistribute it and/or modify it 
 * under the terms of the GNU Lesser General Public License as published by 
 * the Free Software Foundation; either version 2.1 of the License, or 
 * (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY 
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public 
 * License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, 
 * USA.  
 *
 * [Java is a trademark or registered trademark of Sun Microsystems, Inc. 
 * in the United States and other countries.]
 * 
 * ------------
 * IOUtils.java
 * ------------
 * (C)opyright 2002-2004, by Thomas Morgner and Contributors.
 *
 * Original Author:  Thomas Morgner;
 * Contributor(s):   David Gilbert (for Object Refinery Limited);
 *
 * $Id: IOUtils.java,v 1.8 2009/01/22 08:34:58 taqua Exp $
 *
 * Changes
 * -------
 * 26-Jan-2003 : Initial version
 * 23-Feb-2003 : Documentation
 * 25-Feb-2003 : Fixed Checkstyle issues (DG);
 * 29-Apr-2003 : Moved to jcommon
 * 04-Jan-2004 : Fixed JDK 1.2.2 issues with createRelativeURL;
 *               added support for query strings within these urls (TM);
 */

/**
 * The IOUtils provide some IO related helper methods.
 *
 * @author Thomas Morgner.
 */
public class Main {
  /**
   * Checks, whether the child directory is a subdirectory of the base 
   * directory.
   *
   * @param base the base directory.
   * @param child the suspected child directory.
   * @return true, if the child is a subdirectory of the base directory.
   * @throws IOException if an IOError occured during the test.
   */
  public boolean isSubDirectory(File base, File child)
      throws IOException {
      base = base.getCanonicalFile();
      child = child.getCanonicalFile();
      File parentFile = child;
      while (parentFile != null) {
          if (base.equals(parentFile)) {
              return true;
          }
          parentFile = parentFile.getParentFile();
      }
      return false;
  }
}





Concatenates an array of string using a seperator.

    
import java.io.File;
/* 
 * 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.
 *
 *@author 
 */
public class Main {
  private static final String SINGLE_QUOTE = "\"";
  private static final String DOUBLE_QUOTE = "\"";
  private static final char SLASH_CHAR = "/";
  private static final char BACKSLASH_CHAR = "\\";
 
  /**
   * Concatenates an array of string using a seperator.
   *
   * @param strings the strings to concatenate
   * @param separator the seperator between two strings
   * @return the concatened strings
   */
  public static String toString(String[] strings, String separator) {
      StringBuffer sb = new StringBuffer();
      for (int i = 0; i < strings.length; i++) {
          if (i > 0) {
              sb.append(separator);
          }
          sb.append(strings[i]);
      }
      return sb.toString();
  }
}





Convert a list of path elements to a platform-specific path.

      
/* 
 * 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.
 *
 */
import java.io.File;
/**
 * 
 * FileUtils is a collection of routines for common file system operations.
 * 
 * @author Dan Jemiolo (danj)
 * 
 */
public final class FileUtils {
  /**
   * Convert a list of path elements to a platform-specific path.
   * 
   * @param strings
   *          Elements in a path
   * @return an absolute path using the current platform"s
   *         <code>File.separator</code>
   */
  public static String makePath(String[] strings) {
    String result = "";
    for (int i = 0; i < strings.length; i++)
      result += File.separator + strings[i];
    return result;
  }
}





Convert a path to a cananonical form

 
// 
// Copyright 2004-2005 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// Licensed 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.
// 
/**
 * URI Holder. This class assists with the decoding and encoding or HTTP URI"s.
 * It differs from the java.net.URL class as it does not provide communications
 * ability, but it does assist with query string formatting.
 * <P>
 * UTF-8 encoding is used by default for % encoded characters. This may be
 * overridden with the org.mortbay.util.URI.charset system property.
 * 
 * @see UrlEncoded
 * @author Greg Wilkins (gregw)
 */
public class Utils {
  /**
   * Convert a path to a cananonical form. All instances of "." and ".." are
   * factored out. Null is returned if the path tries to .. above its root.
   * 
   * @param path
   * @return path or null.
   */
  public static String canonicalPath(String path) {
    if (path == null || path.length() == 0)
      return path;
    int end = path.length();
    int queryIdx = path.indexOf("?");
    int start = path.lastIndexOf("/", (queryIdx > 0 ? queryIdx : end));
    search: while (end > 0) {
      switch (end - start) {
      case 2: // possible single dot
        if (path.charAt(start + 1) != ".")
          break;
        break search;
      case 3: // possible double dot
        if (path.charAt(start + 1) != "." || path.charAt(start + 2) != ".")
          break;
        break search;
      }
      end = start;
      start = path.lastIndexOf("/", end - 1);
    }
    // If we have checked the entire string
    if (start >= end)
      return path;
    StringBuffer buf = new StringBuffer(path);
    int delStart = -1;
    int delEnd = -1;
    int skip = 0;
    while (end > 0) {
      switch (end - start) {
      case 2: // possible single dot
        if (buf.charAt(start + 1) != ".") {
          if (skip > 0 && --skip == 0) {
            delStart = start >= 0 ? start : 0;
            if (delStart > 0 && delEnd == buf.length() && buf.charAt(delEnd - 1) == ".")
              delStart++;
          }
          break;
        }
        if (start < 0 && buf.length() > 2 && buf.charAt(1) == "/" && buf.charAt(2) == "/")
          break;
        if (delEnd < 0)
          delEnd = end;
        delStart = start;
        if (delStart < 0 || delStart == 0 && buf.charAt(delStart) == "/") {
          delStart++;
          if (delEnd < buf.length() && buf.charAt(delEnd) == "/")
            delEnd++;
          break;
        }
        if (end == buf.length())
          delStart++;
        end = start--;
        while (start >= 0 && buf.charAt(start) != "/")
          start--;
        continue;
      case 3: // possible double dot
        if (buf.charAt(start + 1) != "." || buf.charAt(start + 2) != ".") {
          if (skip > 0 && --skip == 0) {
            delStart = start >= 0 ? start : 0;
            if (delStart > 0 && delEnd == buf.length() && buf.charAt(delEnd - 1) == ".")
              delStart++;
          }
          break;
        }
        delStart = start;
        if (delEnd < 0)
          delEnd = end;
        skip++;
        end = start--;
        while (start >= 0 && buf.charAt(start) != "/")
          start--;
        continue;
      default:
        if (skip > 0 && --skip == 0) {
          delStart = start >= 0 ? start : 0;
          if (delEnd == buf.length() && buf.charAt(delEnd - 1) == ".")
            delStart++;
        }
      }
      // Do the delete
      if (skip <= 0 && delStart >= 0 && delStart >= 0) {
        buf.delete(delStart, delEnd);
        delStart = delEnd = -1;
        if (skip > 0)
          delEnd = end;
      }
      end = start--;
      while (start >= 0 && buf.charAt(start) != "/")
        start--;
    }
    // Too many ..
    if (skip > 0)
      return null;
    // Do the delete
    if (delEnd >= 0)
      buf.delete(delStart, delEnd);
    return buf.toString();
  }
}





Create File Name with specified white space character

    
/*
 *  soapUI, copyright (C) 2004-2009 eviware.ru 
 *
 *  soapUI is free software; you can redistribute it and/or modify it under the 
 *  terms of version 2.1 of the GNU Lesser General Public License as published by 
 *  the Free Software Foundation.
 *
 *  soapUI is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without 
 *  even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 *  See the GNU Lesser General Public License for more details at gnu.org.
 */

public class Utils {
  
  public static String createFileName( String str, char whitespaceChar )
  {
    StringBuffer result = new StringBuffer();
    for( int c = 0; c < str.length(); c++ )
    {
      char ch = str.charAt( c );
      if( Character.isWhitespace( ch ) && whitespaceChar != 0 )
        result.append( whitespaceChar );
      else if( Character.isLetterOrDigit( ch ) )
        result.append( ch );
    }
    return result.toString();
  }
}





Decode a path.

  
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.util.BitSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map;
import java.util.StringTokenizer;
/*
 Derby - Class org.apache.derby.iapi.util.PropertyUtil
 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.
 */
public class Main {

  /**
   * Array containing the safe characters set as defined by RFC 1738
   */
  private static BitSet safeCharacters;
  private static final char[] hexadecimal =
  {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
   "A", "B", "C", "D", "E", "F"};
  static {
      safeCharacters = new BitSet(256);
      int i;
      // "lowalpha" rule
      for (i = "a"; i <= "z"; i++) {
          safeCharacters.set(i);
      }
      // "hialpha" rule
      for (i = "A"; i <= "Z"; i++) {
          safeCharacters.set(i);
      }
      // "digit" rule
      for (i = "0"; i <= "9"; i++) {
          safeCharacters.set(i);
      }
      // "safe" rule
      safeCharacters.set("$");
      safeCharacters.set("-");
      safeCharacters.set("_");
      safeCharacters.set(".");
      safeCharacters.set("+");
      // "extra" rule
      safeCharacters.set("!");
      safeCharacters.set("*");
      safeCharacters.set("\"");
      safeCharacters.set("(");
      safeCharacters.set(")");
      safeCharacters.set(",");
      // special characters common to http: file: and ftp: URLs ("fsegment" and "hsegment" rules)
      safeCharacters.set("/");
      safeCharacters.set(":");
      safeCharacters.set("@");
      safeCharacters.set("&");
      safeCharacters.set("=");
  }
  /**
   * Decode a path.
   *
   * <p>Interprets %XX (where XX is hexadecimal number) as UTF-8 encoded bytes.
   * <p>The validity of the input path is not checked (i.e. characters that were not encoded will
   * not be reported as errors).
   * <p>This method differs from URLDecoder.decode in that it always uses UTF-8 (while URLDecoder
   * uses the platform default encoding, often ISO-8859-1), and doesn"t translate + characters to spaces.
   *
   * @param path the path to decode
   * @return the decoded path
   */
  public static String decodePath(String path) {
      StringBuffer translatedPath = new StringBuffer(path.length());
      byte[] encodedchars = new byte[path.length() / 3];
      int i = 0;
      int length = path.length();
      int encodedcharsLength = 0;
      while (i < length) {
          if (path.charAt(i) == "%") {
              // we must process all consecutive %-encoded characters in one go, because they represent
              // an UTF-8 encoded string, and in UTF-8 one character can be encoded as multiple bytes
              while (i < length && path.charAt(i) == "%") {
                  if (i + 2 < length) {
                      try {
                          byte x = (byte)Integer.parseInt(path.substring(i + 1, i + 3), 16);
                          encodedchars[encodedcharsLength] = x;
                      } catch (NumberFormatException e) {
                          throw new IllegalArgumentException("NetUtils.decodePath: " +
                                                             "Illegal hex characters in pattern %" + path.substring(i + 1, i + 3));
                      }
                      encodedcharsLength++;
                      i += 3;
                  } else {
                      throw new IllegalArgumentException("NetUtils.decodePath: " +
                                                         "% character should be followed by 2 hexadecimal characters.");
                  }
              }
              try {
                  String translatedPart = new String(encodedchars, 0, encodedcharsLength, "UTF-8");
                  translatedPath.append(translatedPart);
              } catch (UnsupportedEncodingException e) {
                  // the situation that UTF-8 is not supported is quite theoretical, so throw a runtime exception
                  throw new RuntimeException("Problem in decodePath: UTF-8 encoding not supported.");
              }
              encodedcharsLength = 0;
          } else {
              // a normal character
              translatedPath.append(path.charAt(i));
              i++;
          }
      }
      return translatedPath.toString();
  }

}





Extract File Extension

      
/** 
 * 
 * The ObjectStyle Group Software License, version 1.1
 * ObjectStyle Group - http://objectstyle.org/
 * 
 * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
 * of the software. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 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 end-user documentation included with the redistribution, if any,
 *    must include the following acknowlegement:
 *    "This product includes software developed by independent contributors
 *    and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 * 
 * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
 *    or promote products derived from this software without prior written
 *    permission. For written permission, email
 *    "andrus at objectstyle dot org".
 * 
 * 5. Products derived from this software may not be called "ObjectStyle"
 *    or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
 *    names without prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED ``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 THE OBJECTSTYLE GROUP 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.
 * 
 * 
 * This software consists of voluntary contributions made by many
 * individuals and hosted on ObjectStyle Group web site.  For more
 * information on the ObjectStyle Group, please see
 * <http://objectstyle.org/>.
 */
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.ruparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
/**
 * Contains various unorganized static utility methods used across Cayenne.
 * 
 * @author Andrei Adamchik
 */
public class Util {
  /**
   * Extracts extension from the file name. Dot is not included in the returned string.
   */
  public static String extractFileExtension(String fileName) {
      int dotInd = fileName.lastIndexOf(".");
      // if dot is in the first position,
      // we are dealing with a hidden file rather than an extension
      return (dotInd > 0 && dotInd < fileName.length()) ? fileName
              .substring(dotInd + 1) : null;
  }
}





Extract file name (without path and suffix) from file name with path and suffix

 
/*******************************************************************************
 * Copyright (c) 2004 Actuate Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Actuate Corporation  - initial API and implementation
 *******************************************************************************/
/**
 * Collection of string utilities.
 * 
 */
public class StringUtil
{
  /**
   * Extract file name (without path and suffix) from file name with path and
   * suffix.
   * <p>
   * For example:
   * <p>
   * <ul>
   * <li>"c:\home\abc.xml" => "abc"
   * <li>"c:\home\abc" => "abc"
   * <li>"/home/user/abc.xml" => "abc"
   * <li>"/home/user/abc" => "abc"
   * </ul>
   * 
   * @param filePathName
   *            the file name with path and suffix
   * @return the file name without path and suffix
   */
  public static String extractFileName( String filePathName )
  {
    if ( filePathName == null )
      return null;
    int dotPos = filePathName.lastIndexOf( "." );
    int slashPos = filePathName.lastIndexOf( "\\" );
    if ( slashPos == -1 )
      slashPos = filePathName.lastIndexOf( "/" );
    if ( dotPos > slashPos )
    {
      return filePathName.substring( slashPos > 0 ? slashPos + 1 : 0,
          dotPos );
    }
    return filePathName.substring( slashPos > 0 ? slashPos + 1 : 0 );
  }
}





Extract file name (without path but with suffix) from file name with path and suffix

 
/*******************************************************************************
 * Copyright (c) 2004 Actuate Corporation.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *  Actuate Corporation  - initial API and implementation
 *******************************************************************************/
/**
 * Collection of string utilities.
 * 
 */
public class StringUtil
{
  /**
   * Extract file name (without path but with suffix) from file name with path
   * and suffix.
   * <p>
   * For example:
   * <p>
   * <ul>
   * <li>"c:\home\abc.xml" => "abc.xml"
   * <li>"c:\home\abc" => "abc"
   * <li>"/home/user/abc.xml" => "abc.xml"
   * <li>"/home/user/abc" => "abc"
   * </ul>
   * 
   * @param filePathName
   *            the file name with path and suffix
   * @return the file name without path but with suffix
   */
  public static String extractFileNameWithSuffix( String filePathName )
  {
    if ( filePathName == null )
      return null;
    int slashPos = filePathName.lastIndexOf( "\\" );
    if ( slashPos == -1 )
      slashPos = filePathName.lastIndexOf( "/" );
    return filePathName.substring( slashPos > 0 ? slashPos + 1 : 0 );
  }
}





Extract the page path from the given request path

      
/*
Copyright (c) 2003 eInnovation Inc. All rights reserved
This library is free software; you can redistribute it and/or modify it under the terms
of the GNU Lesser General Public License as published by the Free Software Foundation;
either version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
*/
/*--
 Copyright (C) 2001-2002 Anthony Eden.
 All rights reserved.
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:
 1. Redistributions of source code must retain the above copyright
    notice, this list of conditions, and the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions, and the disclaimer that follows
    these conditions in the documentation and/or other materials
    provided with the distribution.
 3. The name "JPublish" must not be used to endorse or promote products
    derived from this software without prior written permission.  For
    written permission, please contact me@anthonyeden.ru.
 4. Products derived from this software may not be called "JPublish", nor
    may "JPublish" appear in their name, without prior written permission
    from Anthony Eden (me@anthonyeden.ru).
 In addition, I request (but do not require) that you include in the
 end-user documentation provided with the redistribution and/or in the
 software itself an acknowledgement equivalent to the following:
     "This product includes software developed by
      Anthony Eden (http://www.anthonyeden.ru/)."
 THIS SOFTWARE IS PROVIDED ``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 THE AUTHOR(S) 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.
 For more information on JPublish, please see <http://www.jpublish.org/>.
 */

/**
 * Utility class for working with request paths.
 *
 * @author Anthony Eden
 */
public final class PathUtilities
{
  private static final String WILDCARD = "*";
  /**
   * Extract the page path from the given request path.  This method will return the path from
   * the page root to the page descriptor file.
   *
   * /some/sub/dir/test.html -> /some/sub/dir/test
   *
   * @param path The request path
   * @see extractDirectory
   * @return The page path
   */
  public static String extractPagePath(String path)
  {
    if ( path != null && path.length() > 0)
    {
      int lastDot = path.lastIndexOf(".");
      if( lastDot > 1)
      {
        String pagePath = path.substring(0,lastDot);    
        return pagePath;
      }
    }
    return path;
  }
}





File name Utils

      
/**
 * Created Apr 6, 2007
 */
/*
 Copyright 2007 Robert C. Ilardi
 Licensed 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.
 */

/**
 * @author Robert C. Ilardi
 *
 */
public class FilenameUtils {
  public static String GetDeepestDirectory(String path) {
    String dir = null;
    int lastIndex;
    if (path != null && path.trim().length() > 0) {
      path = path.trim();
      if (path.endsWith("/") && path.length() > 1) {
        path = path.substring(0, path.length() - 1);
      }
      dir = path;
      if (path.length() > 1) {
        lastIndex = path.lastIndexOf("/");
        if (lastIndex >= 0) {
          dir = path.substring(lastIndex + 1);
        }
      }
    }
    return dir;
  }
  public static String GetParentDirectory(String path) {
    String parentDir = "/";
    int lastIndex;
    if (path != null && path.trim().length() > 0) {
      path = path.trim();
      if (path.endsWith("/") && path.length() > 1) {
        path = path.substring(0, path.length() - 1);
      }
      if (path.length() > 1) {
        lastIndex = path.lastIndexOf("/");
        if (lastIndex > 0) {
          parentDir = path.substring(0, lastIndex);
        }
      }
    }
    return parentDir;
  }
  public static String GetFilename(String path) {
    String file = null;
    int lastIndex;
    if (path != null && path.trim().length() > 0) {
      path = path.trim();
      if (!path.endsWith("/")) {
        file = path;
        lastIndex = path.lastIndexOf("/");
        if (lastIndex >= 0) {
          file = path.substring(lastIndex + 1, path.length());
        }
      }
    }
    return file;
  }
  public static String[] SplitDirAndFile(String path) {
    String[] arr = new String[2];
    int lastIndex;
    if (path != null && path.trim().length() > 0) {
      path = path.trim();
      if (!path.endsWith("/")) {
        lastIndex = path.lastIndexOf("/");
        if (lastIndex >= 0) {
          arr[0] = path.substring(0, lastIndex);
          arr[1] = path.substring(lastIndex + 1, path.length());
        }
        else {
          arr[0] = "/"; //Assume Root Directory
          arr[1] = path;
        }
      }
      else if (path.length() > 1) {
        arr[0] = path.substring(0, path.length() - 1);
      }
      else {
        arr[0] = path;
      }
    }
    return arr;
  }
  public static String GetFileExt(String filename) {
    String ext = null;
    int i = filename.lastIndexOf(".");
    if (i > 0 && i < filename.length() - 1) {
      ext = filename.substring(i + 1).toLowerCase();
    }
    return ext;
  }
}





Fixes the file sperator char for the target platform using the following replacement.

    
   
import java.io.File;
/* 
 * 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.
 *
 *@author 
 */
public class Main {
  private static final String SINGLE_QUOTE = "\"";
  private static final String DOUBLE_QUOTE = "\"";
  private static final char SLASH_CHAR = "/";
  private static final char BACKSLASH_CHAR = "\\";
 
  /**
   * Fixes the file sperator char for the target platform
   * using the following replacement.
   * 
   * <ul>
   *  <li> "/" ==>  File.separatorChar
   *  <li> "\\" ==>  File.separatorChar
   * </ul>
   *
   * @param arg the argument to fix
   * @return the transformed argument 
   */
  public static String fixFileSeparatorChar(String arg) {
      return arg.replace(SLASH_CHAR, File.separatorChar).replace(
              BACKSLASH_CHAR, File.separatorChar);
  }
}





General filename and filepath manipulation utilities

      
/*
 * 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.
 */
import java.io.File;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.Stack;
/**
 * General filename and filepath manipulation utilities.
 * <p>
 * When dealing with filenames you can hit problems when moving from a Windows
 * based development machine to a Unix based production machine.
 * This class aims to help avoid those problems.
 * <p>
 * <b>NOTE</b>: You may be able to avoid using this class entirely simply by
 * using JDK {@link java.io.File File} objects and the two argument constructor
 * {@link java.io.File#File(java.io.File, java.lang.String) File(File,String)}.
 * <p>
 * Most methods on this class are designed to work the same on both Unix and Windows.
 * Those that don"t include "System", "Unix" or "Windows" in their name.
 * <p>
 * Most methods recognise both separators (forward and back), and both
 * sets of prefixes. See the javadoc of each method for details.
 * <p>
 * This class defines six components within a filename
 * (example C:\dev\project\file.txt):
 * <ul>
 * <li>the prefix - C:\</li>
 * <li>the path - dev\project\</li>
 * <li>the full path - C:\dev\project\</li>
 * <li>the name - file.txt</li>
 * <li>the base name - file</li>
 * <li>the extension - txt</li>
 * </ul>
 * Note that this class works best if directory filenames end with a separator.
 * If you omit the last separator, it is impossible to determine if the filename
 * corresponds to a file or a directory. As a result, we have chosen to say
 * it corresponds to a file.
 * <p>
 * This class only supports Unix and Windows style names.
 * Prefixes are matched as follows:
 * <pre>
 * Windows:
 * a\b\c.txt           --> ""          --> relative
 * \a\b\c.txt          --> "\"         --> current drive absolute
 * C:a\b\c.txt         --> "C:"        --> drive relative
 * C:\a\b\c.txt        --> "C:\"       --> absolute
 * \\server\a\b\c.txt  --> "\\server\" --> UNC
 *
 * Unix:
 * a/b/c.txt           --> ""          --> relative
 * /a/b/c.txt          --> "/"         --> absolute
 * ~/a/b/c.txt         --> "~/"        --> current user
 * ~                   --> "~/"        --> current user (slash added)
 * ~user/a/b/c.txt     --> "~user/"    --> named user
 * ~user               --> "~user/"    --> named user (slash added)
 * </pre>
 * Both prefix styles are matched always, irrespective of the machine that you are
 * currently running on.
 * <p>
 * Origin of code: Excalibur, Alexandria, Tomcat, Commons-Utils.
 *
 * @author 
 * @author Stephen Colebourne
 * @version $Id: FilenameUtils.java 609870 2008-01-08 04:46:26Z niallp $
 * @since Commons IO 1.1
 */
public class FilenameUtils {
    /**
     * The extension separator character.
     * @since Commons IO 1.4
     */
    public static final char EXTENSION_SEPARATOR = ".";
    /**
     * The extension separator String.
     * @since Commons IO 1.4
     */
    public static final String EXTENSION_SEPARATOR_STR = (new Character(EXTENSION_SEPARATOR)).toString();
    /**
     * The Unix separator character.
     */
    private static final char UNIX_SEPARATOR = "/";
    /**
     * The Windows separator character.
     */
    private static final char WINDOWS_SEPARATOR = "\\";
    /**
     * The system separator character.
     */
    private static final char SYSTEM_SEPARATOR = File.separatorChar;
    /**
     * The separator character that is the opposite of the system separator.
     */
    private static final char OTHER_SEPARATOR;
    static {
        if (isSystemWindows()) {
            OTHER_SEPARATOR = UNIX_SEPARATOR;
        } else {
            OTHER_SEPARATOR = WINDOWS_SEPARATOR;
        }
    }
    /**
     * Instances should NOT be constructed in standard programming.
     */
    public FilenameUtils() {
        super();
    }
    //-----------------------------------------------------------------------
    /**
     * Determines if Windows file system is in use.
     * 
     * @return true if the system is Windows
     */
    static boolean isSystemWindows() {
        return SYSTEM_SEPARATOR == WINDOWS_SEPARATOR;
    }
    //-----------------------------------------------------------------------
    /**
     * Checks if the character is a separator.
     * 
     * @param ch  the character to check
     * @return true if it is a separator character
     */
    private static boolean isSeparator(char ch) {
        return (ch == UNIX_SEPARATOR) || (ch == WINDOWS_SEPARATOR);
    }
    //-----------------------------------------------------------------------
    /**
     * Normalizes a path, removing double and single dot path steps.
     * <p>
     * This method normalizes a path to a standard format.
     * The input may contain separators in either Unix or Windows format.
     * The output will contain separators in the format of the system.
     * <p>
     * A trailing slash will be retained.
     * A double slash will be merged to a single slash (but UNC names are handled).
     * A single dot path segment will be removed.
     * A double dot will cause that path segment and the one before to be removed.
     * If the double dot has no parent path segment to work with, <code>null</code>
     * is returned.
     * <p>
     * The output will be the same on both Unix and Windows except
     * for the separator character.
     * <pre>
     * /foo//               -->   /foo/
     * /foo/./              -->   /foo/
     * /foo/../bar          -->   /bar
     * /foo/../bar/         -->   /bar/
     * /foo/../bar/../baz   -->   /baz
     * //foo//./bar         -->   /foo/bar
     * /../                 -->   null
     * ../foo               -->   null
     * foo/bar/..           -->   foo/
     * foo/../../bar        -->   null
     * foo/../bar           -->   bar
     * //server/foo/../bar  -->   //server/bar
     * //server/../bar      -->   null
     * C:\foo\..\bar        -->   C:\bar
     * C:\..\bar            -->   null
     * ~/foo/../bar/        -->   ~/bar/
     * ~/../bar             -->   null
     * </pre>
     * (Note the file separator returned will be correct for Windows/Unix)
     *
     * @param filename  the filename to normalize, null returns null
     * @return the normalized filename, or null if invalid
     */
    public static String normalize(String filename) {
        return doNormalize(filename, true);
    }
    //-----------------------------------------------------------------------
    /**
     * Normalizes a path, removing double and single dot path steps,
     * and removing any final directory separator.
     * <p>
     * This method normalizes a path to a standard format.
     * The input may contain separators in either Unix or Windows format.
     * The output will contain separators in the format of the system.
     * <p>
     * A trailing slash will be removed.
     * A double slash will be merged to a single slash (but UNC names are handled).
     * A single dot path segment will be removed.
     * A double dot will cause that path segment and the one before to be removed.
     * If the double dot has no parent path segment to work with, <code>null</code>
     * is returned.
     * <p>
     * The output will be the same on both Unix and Windows except
     * for the separator character.
     * <pre>
     * /foo//               -->   /foo
     * /foo/./              -->   /foo
     * /foo/../bar          -->   /bar
     * /foo/../bar/         -->   /bar
     * /foo/../bar/../baz   -->   /baz
     * //foo//./bar         -->   /foo/bar
     * /../                 -->   null
     * ../foo               -->   null
     * foo/bar/..           -->   foo
     * foo/../../bar        -->   null
     * foo/../bar           -->   bar
     * //server/foo/../bar  -->   //server/bar
     * //server/../bar      -->   null
     * C:\foo\..\bar        -->   C:\bar
     * C:\..\bar            -->   null
     * ~/foo/../bar/        -->   ~/bar
     * ~/../bar             -->   null
     * </pre>
     * (Note the file separator returned will be correct for Windows/Unix)
     *
     * @param filename  the filename to normalize, null returns null
     * @return the normalized filename, or null if invalid
     */
    public static String normalizeNoEndSeparator(String filename) {
        return doNormalize(filename, false);
    }
    /**
     * Internal method to perform the normalization.
     *
     * @param filename  the filename
     * @param keepSeparator  true to keep the final separator
     * @return the normalized filename
     */
    private static String doNormalize(String filename, boolean keepSeparator) {
        if (filename == null) {
            return null;
        }
        int size = filename.length();
        if (size == 0) {
            return filename;
        }
        int prefix = getPrefixLength(filename);
        if (prefix < 0) {
            return null;
        }
        
        char[] array = new char[size + 2];  // +1 for possible extra slash, +2 for arraycopy
        filename.getChars(0, filename.length(), array, 0);
        
        // fix separators throughout
        for (int i = 0; i < array.length; i++) {
            if (array[i] == OTHER_SEPARATOR) {
                array[i] = SYSTEM_SEPARATOR;
            }
        }
        
        // add extra separator on the end to simplify code below
        boolean lastIsDirectory = true;
        if (array[size - 1] != SYSTEM_SEPARATOR) {
            array[size++] = SYSTEM_SEPARATOR;
            lastIsDirectory = false;
        }
        
        // adjoining slashes
        for (int i = prefix + 1; i < size; i++) {
            if (array[i] == SYSTEM_SEPARATOR && array[i - 1] == SYSTEM_SEPARATOR) {
                System.arraycopy(array, i, array, i - 1, size - i);
                size--;
                i--;
            }
        }
        
        // dot slash
        for (int i = prefix + 1; i < size; i++) {
            if (array[i] == SYSTEM_SEPARATOR && array[i - 1] == "." &&
                    (i == prefix + 1 || array[i - 2] == SYSTEM_SEPARATOR)) {
                if (i == size - 1) {
                    lastIsDirectory = true;
                }
                System.arraycopy(array, i + 1, array, i - 1, size - i);
                size -=2;
                i--;
            }
        }
        
        // double dot slash
        outer:
        for (int i = prefix + 2; i < size; i++) {
            if (array[i] == SYSTEM_SEPARATOR && array[i - 1] == "." && array[i - 2] == "." &&
                    (i == prefix + 2 || array[i - 3] == SYSTEM_SEPARATOR)) {
                if (i == prefix + 2) {
                    return null;
                }
                if (i == size - 1) {
                    lastIsDirectory = true;
                }
                int j;
                for (j = i - 4 ; j >= prefix; j--) {
                    if (array[j] == SYSTEM_SEPARATOR) {
                        // remove b/../ from a/b/../c
                        System.arraycopy(array, i + 1, array, j + 1, size - i);
                        size -= (i - j);
                        i = j + 1;
                        continue outer;
                    }
                }
                // remove a/../ from a/../c
                System.arraycopy(array, i + 1, array, prefix, size - i);
                size -= (i + 1 - prefix);
                i = prefix + 1;
            }
        }
        
        if (size <= 0) {  // should never be less than 0
            return "";
        }
        if (size <= prefix) {  // should never be less than prefix
            return new String(array, 0, size);
        }
        if (lastIsDirectory && keepSeparator) {
            return new String(array, 0, size);  // keep trailing separator
        }
        return new String(array, 0, size - 1);  // lose trailing separator
    }
    //-----------------------------------------------------------------------
    /**
     * Concatenates a filename to a base path using normal command line style rules.
     * <p>
     * The effect is equivalent to resultant directory after changing
     * directory to the first argument, followed by changing directory to
     * the second argument.
     * <p>
     * The first argument is the base path, the second is the path to concatenate.
     * The returned path is always normalized via {@link #normalize(String)},
     * thus <code>..</code> is handled.
     * <p>
     * If <code>pathToAdd</code> is absolute (has an absolute prefix), then
     * it will be normalized and returned.
     * Otherwise, the paths will be joined, normalized and returned.
     * <p>
     * The output will be the same on both Unix and Windows except
     * for the separator character.
     * <pre>
     * /foo/ + bar          -->   /foo/bar
     * /foo + bar           -->   /foo/bar
     * /foo + /bar          -->   /bar
     * /foo + C:/bar        -->   C:/bar
     * /foo + C:bar         -->   C:bar (*)
     * /foo/a/ + ../bar     -->   foo/bar
     * /foo/ + ../../bar    -->   null
     * /foo/ + /bar         -->   /bar
     * /foo/.. + /bar       -->   /bar
     * /foo + bar/c.txt     -->   /foo/bar/c.txt
     * /foo/c.txt + bar     -->   /foo/c.txt/bar (!)
     * </pre>
     * (*) Note that the Windows relative drive prefix is unreliable when
     * used with this method.
     * (!) Note that the first parameter must be a path. If it ends with a name, then
     * the name will be built into the concatenated path. If this might be a problem,
     * use {@link #getFullPath(String)} on the base path argument.
     *
     * @param basePath  the base path to attach to, always treated as a path
     * @param fullFilenameToAdd  the filename (or path) to attach to the base
     * @return the concatenated path, or null if invalid
     */
    public static String concat(String basePath, String fullFilenameToAdd) {
        int prefix = getPrefixLength(fullFilenameToAdd);
        if (prefix < 0) {
            return null;
        }
        if (prefix > 0) {
            return normalize(fullFilenameToAdd);
        }
        if (basePath == null) {
            return null;
        }
        int len = basePath.length();
        if (len == 0) {
            return normalize(fullFilenameToAdd);
        }
        char ch = basePath.charAt(len - 1);
        if (isSeparator(ch)) {
            return normalize(basePath + fullFilenameToAdd);
        } else {
            return normalize(basePath + "/" + fullFilenameToAdd);
        }
    }
    //-----------------------------------------------------------------------
    /**
     * Converts all separators to the Unix separator of forward slash.
     * 
     * @param path  the path to be changed, null ignored
     * @return the updated path
     */
    public static String separatorsToUnix(String path) {
        if (path == null || path.indexOf(WINDOWS_SEPARATOR) == -1) {
            return path;
        }
        return path.replace(WINDOWS_SEPARATOR, UNIX_SEPARATOR);
    }
    /**
     * Converts all separators to the Windows separator of backslash.
     * 
     * @param path  the path to be changed, null ignored
     * @return the updated path
     */
    public static String separatorsToWindows(String path) {
        if (path == null || path.indexOf(UNIX_SEPARATOR) == -1) {
            return path;
        }
        return path.replace(UNIX_SEPARATOR, WINDOWS_SEPARATOR);
    }
    /**
     * Converts all separators to the system separator.
     * 
     * @param path  the path to be changed, null ignored
     * @return the updated path
     */
    public static String separatorsToSystem(String path) {
        if (path == null) {
            return null;
        }
        if (isSystemWindows()) {
            return separatorsToWindows(path);
        } else {
            return separatorsToUnix(path);
        }
    }
    //-----------------------------------------------------------------------
    /**
     * Returns the length of the filename prefix, such as <code>C:/</code> or <code>~/</code>.
     * <p>
     * This method will handle a file in either Unix or Windows format.
     * <p>
     * The prefix length includes the first slash in the full filename
     * if applicable. Thus, it is possible that the length returned is greater
     * than the length of the input string.
     * <pre>
     * Windows:
     * a\b\c.txt           --> ""          --> relative
     * \a\b\c.txt          --> "\"         --> current drive absolute
     * C:a\b\c.txt         --> "C:"        --> drive relative
     * C:\a\b\c.txt        --> "C:\"       --> absolute
     * \\server\a\b\c.txt  --> "\\server\" --> UNC
     *
     * Unix:
     * a/b/c.txt           --> ""          --> relative
     * /a/b/c.txt          --> "/"         --> absolute
     * ~/a/b/c.txt         --> "~/"        --> current user
     * ~                   --> "~/"        --> current user (slash added)
     * ~user/a/b/c.txt     --> "~user/"    --> named user
     * ~user               --> "~user/"    --> named user (slash added)
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is running on.
     * ie. both Unix and Windows prefixes are matched regardless.
     *
     * @param filename  the filename to find the prefix in, null returns -1
     * @return the length of the prefix, -1 if invalid or null
     */
    public static int getPrefixLength(String filename) {
        if (filename == null) {
            return -1;
        }
        int len = filename.length();
        if (len == 0) {
            return 0;
        }
        char ch0 = filename.charAt(0);
        if (ch0 == ":") {
            return -1;
        }
        if (len == 1) {
            if (ch0 == "~") {
                return 2;  // return a length greater than the input
            }
            return (isSeparator(ch0) ? 1 : 0);
        } else {
            if (ch0 == "~") {
                int posUnix = filename.indexOf(UNIX_SEPARATOR, 1);
                int posWin = filename.indexOf(WINDOWS_SEPARATOR, 1);
                if (posUnix == -1 && posWin == -1) {
                    return len + 1;  // return a length greater than the input
                }
                posUnix = (posUnix == -1 ? posWin : posUnix);
                posWin = (posWin == -1 ? posUnix : posWin);
                return Math.min(posUnix, posWin) + 1;
            }
            char ch1 = filename.charAt(1);
            if (ch1 == ":") {
                ch0 = Character.toUpperCase(ch0);
                if (ch0 >= "A" && ch0 <= "Z") {
                    if (len == 2 || isSeparator(filename.charAt(2)) == false) {
                        return 2;
                    }
                    return 3;
                }
                return -1;
                
            } else if (isSeparator(ch0) && isSeparator(ch1)) {
                int posUnix = filename.indexOf(UNIX_SEPARATOR, 2);
                int posWin = filename.indexOf(WINDOWS_SEPARATOR, 2);
                if ((posUnix == -1 && posWin == -1) || posUnix == 2 || posWin == 2) {
                    return -1;
                }
                posUnix = (posUnix == -1 ? posWin : posUnix);
                posWin = (posWin == -1 ? posUnix : posWin);
                return Math.min(posUnix, posWin) + 1;
            } else {
                return (isSeparator(ch0) ? 1 : 0);
            }
        }
    }
    /**
     * Returns the index of the last directory separator character.
     * <p>
     * This method will handle a file in either Unix or Windows format.
     * The position of the last forward or backslash is returned.
     * <p>
     * The output will be the same irrespective of the machine that the code is running on.
     * 
     * @param filename  the filename to find the last path separator in, null returns -1
     * @return the index of the last separator character, or -1 if there
     * is no such character
     */
    public static int indexOfLastSeparator(String filename) {
        if (filename == null) {
            return -1;
        }
        int lastUnixPos = filename.lastIndexOf(UNIX_SEPARATOR);
        int lastWindowsPos = filename.lastIndexOf(WINDOWS_SEPARATOR);
        return Math.max(lastUnixPos, lastWindowsPos);
    }
    /**
     * Returns the index of the last extension separator character, which is a dot.
     * <p>
     * This method also checks that there is no directory separator after the last dot.
     * To do this it uses {@link #indexOfLastSeparator(String)} which will
     * handle a file in either Unix or Windows format.
     * <p>
     * The output will be the same irrespective of the machine that the code is running on.
     * 
     * @param filename  the filename to find the last path separator in, null returns -1
     * @return the index of the last separator character, or -1 if there
     * is no such character
     */
    public static int indexOfExtension(String filename) {
        if (filename == null) {
            return -1;
        }
        int extensionPos = filename.lastIndexOf(EXTENSION_SEPARATOR);
        int lastSeparator = indexOfLastSeparator(filename);
        return (lastSeparator > extensionPos ? -1 : extensionPos);
    }
    //-----------------------------------------------------------------------
    /**
     * Gets the prefix from a full filename, such as <code>C:/</code>
     * or <code>~/</code>.
     * <p>
     * This method will handle a file in either Unix or Windows format.
     * The prefix includes the first slash in the full filename where applicable.
     * <pre>
     * Windows:
     * a\b\c.txt           --> ""          --> relative
     * \a\b\c.txt          --> "\"         --> current drive absolute
     * C:a\b\c.txt         --> "C:"        --> drive relative
     * C:\a\b\c.txt        --> "C:\"       --> absolute
     * \\server\a\b\c.txt  --> "\\server\" --> UNC
     *
     * Unix:
     * a/b/c.txt           --> ""          --> relative
     * /a/b/c.txt          --> "/"         --> absolute
     * ~/a/b/c.txt         --> "~/"        --> current user
     * ~                   --> "~/"        --> current user (slash added)
     * ~user/a/b/c.txt     --> "~user/"    --> named user
     * ~user               --> "~user/"    --> named user (slash added)
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is running on.
     * ie. both Unix and Windows prefixes are matched regardless.
     *
     * @param filename  the filename to query, null returns null
     * @return the prefix of the file, null if invalid
     */
    public static String getPrefix(String filename) {
        if (filename == null) {
            return null;
        }
        int len = getPrefixLength(filename);
        if (len < 0) {
            return null;
        }
        if (len > filename.length()) {
            return filename + UNIX_SEPARATOR;  // we know this only happens for unix
        }
        return filename.substring(0, len);
    }
    /**
     * Gets the path from a full filename, which excludes the prefix.
     * <p>
     * This method will handle a file in either Unix or Windows format.
     * The method is entirely text based, and returns the text before and
     * including the last forward or backslash.
     * <pre>
     * C:\a\b\c.txt --> a\b\
     * ~/a/b/c.txt  --> a/b/
     * a.txt        --> ""
     * a/b/c        --> a/b/
     * a/b/c/       --> a/b/c/
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is running on.
     * <p>
     * This method drops the prefix from the result.
     * See {@link #getFullPath(String)} for the method that retains the prefix.
     *
     * @param filename  the filename to query, null returns null
     * @return the path of the file, an empty string if none exists, null if invalid
     */
    public static String getPath(String filename) {
        return doGetPath(filename, 1);
    }
    /**
     * Gets the path from a full filename, which excludes the prefix, and
     * also excluding the final directory separator.
     * <p>
     * This method will handle a file in either Unix or Windows format.
     * The method is entirely text based, and returns the text before the
     * last forward or backslash.
     * <pre>
     * C:\a\b\c.txt --> a\b
     * ~/a/b/c.txt  --> a/b
     * a.txt        --> ""
     * a/b/c        --> a/b
     * a/b/c/       --> a/b/c
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is running on.
     * <p>
     * This method drops the prefix from the result.
     * See {@link #getFullPathNoEndSeparator(String)} for the method that retains the prefix.
     *
     * @param filename  the filename to query, null returns null
     * @return the path of the file, an empty string if none exists, null if invalid
     */
    public static String getPathNoEndSeparator(String filename) {
        return doGetPath(filename, 0);
    }
    /**
     * Does the work of getting the path.
     * 
     * @param filename  the filename
     * @param separatorAdd  0 to omit the end separator, 1 to return it
     * @return the path
     */
    private static String doGetPath(String filename, int separatorAdd) {
        if (filename == null) {
            return null;
        }
        int prefix = getPrefixLength(filename);
        if (prefix < 0) {
            return null;
        }
        int index = indexOfLastSeparator(filename);
        if (prefix >= filename.length() || index < 0) {
            return "";
        }
        return filename.substring(prefix, index + separatorAdd);
    }
    /**
     * Gets the full path from a full filename, which is the prefix + path.
     * <p>
     * This method will handle a file in either Unix or Windows format.
     * The method is entirely text based, and returns the text before and
     * including the last forward or backslash.
     * <pre>
     * C:\a\b\c.txt --> C:\a\b\
     * ~/a/b/c.txt  --> ~/a/b/
     * a.txt        --> ""
     * a/b/c        --> a/b/
     * a/b/c/       --> a/b/c/
     * C:           --> C:
     * C:\          --> C:\
     * ~            --> ~/
     * ~/           --> ~/
     * ~user        --> ~user/
     * ~user/       --> ~user/
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is running on.
     *
     * @param filename  the filename to query, null returns null
     * @return the path of the file, an empty string if none exists, null if invalid
     */
    public static String getFullPath(String filename) {
        return doGetFullPath(filename, true);
    }
    /**
     * Gets the full path from a full filename, which is the prefix + path,
     * and also excluding the final directory separator.
     * <p>
     * This method will handle a file in either Unix or Windows format.
     * The method is entirely text based, and returns the text before the
     * last forward or backslash.
     * <pre>
     * C:\a\b\c.txt --> C:\a\b
     * ~/a/b/c.txt  --> ~/a/b
     * a.txt        --> ""
     * a/b/c        --> a/b
     * a/b/c/       --> a/b/c
     * C:           --> C:
     * C:\          --> C:\
     * ~            --> ~
     * ~/           --> ~
     * ~user        --> ~user
     * ~user/       --> ~user
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is running on.
     *
     * @param filename  the filename to query, null returns null
     * @return the path of the file, an empty string if none exists, null if invalid
     */
    public static String getFullPathNoEndSeparator(String filename) {
        return doGetFullPath(filename, false);
    }
    /**
     * Does the work of getting the path.
     * 
     * @param filename  the filename
     * @param includeSeparator  true to include the end separator
     * @return the path
     */
    private static String doGetFullPath(String filename, boolean includeSeparator) {
        if (filename == null) {
            return null;
        }
        int prefix = getPrefixLength(filename);
        if (prefix < 0) {
            return null;
        }
        if (prefix >= filename.length()) {
            if (includeSeparator) {
                return getPrefix(filename);  // add end slash if necessary
            } else {
                return filename;
            }
        }
        int index = indexOfLastSeparator(filename);
        if (index < 0) {
            return filename.substring(0, prefix);
        }
        int end = index + (includeSeparator ?  1 : 0);
        return filename.substring(0, end);
    }
    /**
     * Gets the name minus the path from a full filename.
     * <p>
     * This method will handle a file in either Unix or Windows format.
     * The text after the last forward or backslash is returned.
     * <pre>
     * a/b/c.txt --> c.txt
     * a.txt     --> a.txt
     * a/b/c     --> c
     * a/b/c/    --> ""
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is running on.
     *
     * @param filename  the filename to query, null returns null
     * @return the name of the file without the path, or an empty string if none exists
     */
    public static String getName(String filename) {
        if (filename == null) {
            return null;
        }
        int index = indexOfLastSeparator(filename);
        return filename.substring(index + 1);
    }
    /**
     * Gets the base name, minus the full path and extension, from a full filename.
     * <p>
     * This method will handle a file in either Unix or Windows format.
     * The text after the last forward or backslash and before the last dot is returned.
     * <pre>
     * a/b/c.txt --> c
     * a.txt     --> a
     * a/b/c     --> c
     * a/b/c/    --> ""
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is running on.
     *
     * @param filename  the filename to query, null returns null
     * @return the name of the file without the path, or an empty string if none exists
     */
    public static String getBaseName(String filename) {
        return removeExtension(getName(filename));
    }
    /**
     * Gets the extension of a filename.
     * <p>
     * This method returns the textual part of the filename after the last dot.
     * There must be no directory separator after the dot.
     * <pre>
     * foo.txt      --> "txt"
     * a/b/c.jpg    --> "jpg"
     * a/b.txt/c    --> ""
     * a/b/c        --> ""
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is running on.
     *
     * @param filename the filename to retrieve the extension of.
     * @return the extension of the file or an empty string if none exists.
     */
    public static String getExtension(String filename) {
        if (filename == null) {
            return null;
        }
        int index = indexOfExtension(filename);
        if (index == -1) {
            return "";
        } else {
            return filename.substring(index + 1);
        }
    }
    //-----------------------------------------------------------------------
    /**
     * Removes the extension from a filename.
     * <p>
     * This method returns the textual part of the filename before the last dot.
     * There must be no directory separator after the dot.
     * <pre>
     * foo.txt    --> foo
     * a\b\c.jpg  --> a\b\c
     * a\b\c      --> a\b\c
     * a.b\c      --> a.b\c
     * </pre>
     * <p>
     * The output will be the same irrespective of the machine that the code is running on.
     *
     * @param filename  the filename to query, null returns null
     * @return the filename minus the extension
     */
    public static String removeExtension(String filename) {
        if (filename == null) {
            return null;
        }
        int index = indexOfExtension(filename);
        if (index == -1) {
            return filename;
        } else {
            return filename.substring(0, index);
        }
    }
    //-----------------------------------------------------------------------
    /**
     * Checks whether two filenames are equal exactly.
     * <p>
     * No processing is performed on the filenames other than comparison,
     * thus this is merely a null-safe case-sensitive equals.
     *
     * @param filename1  the first filename to query, may be null
     * @param filename2  the second filename to query, may be null
     * @return true if the filenames are equal, null equals null
     * @see IOCase#SENSITIVE
     */
    public static boolean equals(String filename1, String filename2) {
        return equals(filename1, filename2, false, IOCase.SENSITIVE);
    }
    /**
     * Checks whether two filenames are equal using the case rules of the system.
     * <p>
     * No processing is performed on the filenames other than comparison.
     * The check is case-sensitive on Unix and case-insensitive on Windows.
     *
     * @param filename1  the first filename to query, may be null
     * @param filename2  the second filename to query, may be null
     * @return true if the filenames are equal, null equals null
     * @see IOCase#SYSTEM
     */
    public static boolean equalsOnSystem(String filename1, String filename2) {
        return equals(filename1, filename2, false, IOCase.SYSTEM);
    }
    //-----------------------------------------------------------------------
    /**
     * Checks whether two filenames are equal after both have been normalized.
     * <p>
     * Both filenames are first passed to {@link #normalize(String)}.
     * The check is then performed in a case-sensitive manner.
     *
     * @param filename1  the first filename to query, may be null
     * @param filename2  the second filename to query, may be null
     * @return true if the filenames are equal, null equals null
     * @see IOCase#SENSITIVE
     */
    public static boolean equalsNormalized(String filename1, String filename2) {
        return equals(filename1, filename2, true, IOCase.SENSITIVE);
    }
    /**
     * Checks whether two filenames are equal after both have been normalized
     * and using the case rules of the system.
     * <p>
     * Both filenames are first passed to {@link #normalize(String)}.
     * The check is then performed case-sensitive on Unix and
     * case-insensitive on Windows.
     *
     * @param filename1  the first filename to query, may be null
     * @param filename2  the second filename to query, may be null
     * @return true if the filenames are equal, null equals null
     * @see IOCase#SYSTEM
     */
    public static boolean equalsNormalizedOnSystem(String filename1, String filename2) {
        return equals(filename1, filename2, true, IOCase.SYSTEM);
    }
    /**
     * Checks whether two filenames are equal, optionally normalizing and providing
     * control over the case-sensitivity.
     *
     * @param filename1  the first filename to query, may be null
     * @param filename2  the second filename to query, may be null
     * @param normalized  whether to normalize the filenames
     * @param caseSensitivity  what case sensitivity rule to use, null means case-sensitive
     * @return true if the filenames are equal, null equals null
     * @since Commons IO 1.3
     */
    public static boolean equals(
            String filename1, String filename2,
            boolean normalized, IOCase caseSensitivity) {
        
        if (filename1 == null || filename2 == null) {
            return filename1 == filename2;
        }
        if (normalized) {
            filename1 = normalize(filename1);
            filename2 = normalize(filename2);
            if (filename1 == null || filename2 == null) {
                throw new NullPointerException(
                    "Error normalizing one or both of the file names");
            }
        }
        if (caseSensitivity == null) {
            caseSensitivity = IOCase.SENSITIVE;
        }
        return caseSensitivity.checkEquals(filename1, filename2);
    }
    //-----------------------------------------------------------------------
    /**
     * Checks whether the extension of the filename is that specified.
     * <p>
     * This method obtains the extension as the textual part of the filename
     * after the last dot. There must be no directory separator after the dot.
     * The extension check is case-sensitive on all platforms.
     *
     * @param filename  the filename to query, null returns false
     * @param extension  the extension to check for, null or empty checks for no extension
     * @return true if the filename has the specified extension
     */
    public static boolean isExtension(String filename, String extension) {
        if (filename == null) {
            return false;
        }
        if (extension == null || extension.length() == 0) {
            return (indexOfExtension(filename) == -1);
        }
        String fileExt = getExtension(filename);
        return fileExt.equals(extension);
    }
    /**
     * Checks whether the extension of the filename is one of those specified.
     * <p>
     * This method obtains the extension as the textual part of the filename
     * after the last dot. There must be no directory separator after the dot.
     * The extension check is case-sensitive on all platforms.
     *
     * @param filename  the filename to query, null returns false
     * @param extensions  the extensions to check for, null checks for no extension
     * @return true if the filename is one of the extensions
     */
    public static boolean isExtension(String filename, String[] extensions) {
        if (filename == null) {
            return false;
        }
        if (extensions == null || extensions.length == 0) {
            return (indexOfExtension(filename) == -1);
        }
        String fileExt = getExtension(filename);
        for (int i = 0; i < extensions.length; i++) {
            if (fileExt.equals(extensions[i])) {
                return true;
            }
        }
        return false;
    }
    /**
     * Checks whether the extension of the filename is one of those specified.
     * <p>
     * This method obtains the extension as the textual part of the filename
     * after the last dot. There must be no directory separator after the dot.
     * The extension check is case-sensitive on all platforms.
     *
     * @param filename  the filename to query, null returns false
     * @param extensions  the extensions to check for, null checks for no extension
     * @return true if the filename is one of the extensions
     */
    public static boolean isExtension(String filename, Collection extensions) {
        if (filename == null) {
            return false;
        }
        if (extensions == null || extensions.isEmpty()) {
            return (indexOfExtension(filename) == -1);
        }
        String fileExt = getExtension(filename);
        for (Iterator it = extensions.iterator(); it.hasNext();) {
            if (fileExt.equals(it.next())) {
                return true;
            }
        }
        return false;
    }
    //-----------------------------------------------------------------------
    /**
     * Checks a filename to see if it matches the specified wildcard matcher,
     * always testing case-sensitive.
     * <p>
     * The wildcard matcher uses the characters "?" and "*" to represent a
     * single or multiple wildcard characters.
     * This is the same as often found on Dos/Unix command lines.
     * The check is case-sensitive always.
     * <pre>
     * wildcardMatch("c.txt", "*.txt")      --> true
     * wildcardMatch("c.txt", "*.jpg")      --> false
     * wildcardMatch("a/b/c.txt", "a/b/*")  --> true
     * wildcardMatch("c.txt", "*.???")      --> true
     * wildcardMatch("c.txt", "*.????")     --> false
     * </pre>
     * 
     * @param filename  the filename to match on
     * @param wildcardMatcher  the wildcard string to match against
     * @return true if the filename matches the wilcard string
     * @see IOCase#SENSITIVE
     */
    public static boolean wildcardMatch(String filename, String wildcardMatcher) {
        return wildcardMatch(filename, wildcardMatcher, IOCase.SENSITIVE);
    }
    /**
     * Checks a filename to see if it matches the specified wildcard matcher
     * using the case rules of the system.
     * <p>
     * The wildcard matcher uses the characters "?" and "*" to represent a
     * single or multiple wildcard characters.
     * This is the same as often found on Dos/Unix command lines.
     * The check is case-sensitive on Unix and case-insensitive on Windows.
     * <pre>
     * wildcardMatch("c.txt", "*.txt")      --> true
     * wildcardMatch("c.txt", "*.jpg")      --> false
     * wildcardMatch("a/b/c.txt", "a/b/*")  --> true
     * wildcardMatch("c.txt", "*.???")      --> true
     * wildcardMatch("c.txt", "*.????")     --> false
     * </pre>
     * 
     * @param filename  the filename to match on
     * @param wildcardMatcher  the wildcard string to match against
     * @return true if the filename matches the wilcard string
     * @see IOCase#SYSTEM
     */
    public static boolean wildcardMatchOnSystem(String filename, String wildcardMatcher) {
        return wildcardMatch(filename, wildcardMatcher, IOCase.SYSTEM);
    }
    /**
     * Checks a filename to see if it matches the specified wildcard matcher
     * allowing control over case-sensitivity.
     * <p>
     * The wildcard matcher uses the characters "?" and "*" to represent a
     * single or multiple wildcard characters.
     * 
     * @param filename  the filename to match on
     * @param wildcardMatcher  the wildcard string to match against
     * @param caseSensitivity  what case sensitivity rule to use, null means case-sensitive
     * @return true if the filename matches the wilcard string
     * @since Commons IO 1.3
     */
    public static boolean wildcardMatch(String filename, String wildcardMatcher, IOCase caseSensitivity) {
        if (filename == null && wildcardMatcher == null) {
            return true;
        }
        if (filename == null || wildcardMatcher == null) {
            return false;
        }
        if (caseSensitivity == null) {
            caseSensitivity = IOCase.SENSITIVE;
        }
        filename = caseSensitivity.convertCase(filename);
        wildcardMatcher = caseSensitivity.convertCase(wildcardMatcher);
        String[] wcs = splitOnTokens(wildcardMatcher);
        boolean anyChars = false;
        int textIdx = 0;
        int wcsIdx = 0;
        Stack backtrack = new Stack();
        
        // loop around a backtrack stack, to handle complex * matching
        do {
            if (backtrack.size() > 0) {
                int[] array = (int[]) backtrack.pop();
                wcsIdx = array[0];
                textIdx = array[1];
                anyChars = true;
            }
            
            // loop whilst tokens and text left to process
            while (wcsIdx < wcs.length) {
      
                if (wcs[wcsIdx].equals("?")) {
                    // ? so move to next text char
                    textIdx++;
                    anyChars = false;
                    
                } else if (wcs[wcsIdx].equals("*")) {
                    // set any chars status
                    anyChars = true;
                    if (wcsIdx == wcs.length - 1) {
                        textIdx = filename.length();
                    }
                    
                } else {
                    // matching text token
                    if (anyChars) {
                        // any chars then try to locate text token
                        textIdx = filename.indexOf(wcs[wcsIdx], textIdx);
                        if (textIdx == -1) {
                            // token not found
                            break;
                        }
                        int repeat = filename.indexOf(wcs[wcsIdx], textIdx + 1);
                        if (repeat >= 0) {
                            backtrack.push(new int[] {wcsIdx, repeat});
                        }
                    } else {
                        // matching from current position
                        if (!filename.startsWith(wcs[wcsIdx], textIdx)) {
                            // couldnt match token
                            break;
                        }
                    }
      
                    // matched text token, move text index to end of matched token
                    textIdx += wcs[wcsIdx].length();
                    anyChars = false;
                }
      
                wcsIdx++;
            }
            
            // full match
            if (wcsIdx == wcs.length && textIdx == filename.length()) {
                return true;
            }
            
        } while (backtrack.size() > 0);
  
        return false;
    }
    /**
     * Splits a string into a number of tokens.
     * 
     * @param text  the text to split
     * @return the tokens, never null
     */
    static String[] splitOnTokens(String text) {
        // used by wildcardMatch
        // package level so a unit test may run on this
        
        if (text.indexOf("?") == -1 && text.indexOf("*") == -1) {
            return new String[] { text };
        }
        char[] array = text.toCharArray();
        ArrayList list = new ArrayList();
        StringBuffer buffer = new StringBuffer();
        for (int i = 0; i < array.length; i++) {
            if (array[i] == "?" || array[i] == "*") {
                if (buffer.length() != 0) {
                    list.add(buffer.toString());
                    buffer.setLength(0);
                }
                if (array[i] == "?") {
                    list.add("?");
                } else if (list.size() == 0 ||
                        (i > 0 && list.get(list.size() - 1).equals("*") == false)) {
                    list.add("*");
                }
            } else {
                buffer.append(array[i]);
            }
        }
        if (buffer.length() != 0) {
            list.add(buffer.toString());
        }
        return (String[]) list.toArray( new String[ list.size() ] );
    }
}
/*
 * 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.
 */

/**
 * Enumeration of IO case sensitivity.
 * <p>
 * Different filing systems have different rules for case-sensitivity.
 * Windows is case-insensitive, Unix is case-sensitive.
 * <p>
 * This class captures that difference, providing an enumeration to
 * control how filename comparisons should be performed. It also provides
 * methods that use the enumeration to perform comparisons.
 * <p>
 * Wherever possible, you should use the <code>check</code> methods in this
 * class to compare filenames.
 *
 * @author Stephen Colebourne
 * @version $Id: IOCase.java 606345 2007-12-21 23:43:01Z ggregory $
 * @since Commons IO 1.3
 */
 final class IOCase implements Serializable {
    /**
     * The constant for case sensitive regardless of operating system.
     */
    public static final IOCase SENSITIVE = new IOCase("Sensitive", true);
    
    /**
     * The constant for case insensitive regardless of operating system.
     */
    public static final IOCase INSENSITIVE = new IOCase("Insensitive", false);
    
    /**
     * The constant for case sensitivity determined by the current operating system.
     * Windows is case-insensitive when comparing filenames, Unix is case-sensitive.
     * <p>
     * If you derialize this constant of Windows, and deserialize on Unix, or vice
     * versa, then the value of the case-sensitivity flag will change.
     */
    public static final IOCase SYSTEM = new IOCase("System", !FilenameUtils.isSystemWindows());
    /** Serialization version. */
    private static final long serialVersionUID = -6343169151696340687L;
    /** The enumeration name. */
    private final String name;
    
    /** The sensitivity flag. */
    private final transient boolean sensitive;
    //-----------------------------------------------------------------------
    /**
     * Factory method to create an IOCase from a name.
     * 
     * @param name  the name to find
     * @return the IOCase object
     * @throws IllegalArgumentException if the name is invalid
     */
    public static IOCase forName(String name) {
        if (IOCase.SENSITIVE.name.equals(name)){
            return IOCase.SENSITIVE;
        }
        if (IOCase.INSENSITIVE.name.equals(name)){
            return IOCase.INSENSITIVE;
        }
        if (IOCase.SYSTEM.name.equals(name)){
            return IOCase.SYSTEM;
        }
        throw new IllegalArgumentException("Invalid IOCase name: " + name);
    }
    //-----------------------------------------------------------------------
    /**
     * Private constructor.
     * 
     * @param name  the name
     * @param sensitive  the sensitivity
     */
    private IOCase(String name, boolean sensitive) {
        this.name = name;
        this.sensitive = sensitive;
    }
    /**
     * Replaces the enumeration from the stream with a real one.
     * This ensures that the correct flag is set for SYSTEM.
     * 
     * @return the resolved object
     */
    private Object readResolve() {
        return forName(name);
    }
    //-----------------------------------------------------------------------
    /**
     * Gets the name of the constant.
     * 
     * @return the name of the constant
     */
    public String getName() {
        return name;
    }
    /**
     * Does the object represent case sensitive comparison.
     * 
     * @return true if case sensitive
     */
    public boolean isCaseSensitive() {
        return sensitive;
    }
    //-----------------------------------------------------------------------
    /**
     * Compares two strings using the case-sensitivity rule.
     * <p>
     * This method mimics {@link String#compareTo} but takes case-sensitivity
     * into account.
     * 
     * @param str1  the first string to compare, not null
     * @param str2  the second string to compare, not null
     * @return true if equal using the case rules
     * @throws NullPointerException if either string is null
     */
    public int checkCompareTo(String str1, String str2) {
        if (str1 == null || str2 == null) {
            throw new NullPointerException("The strings must not be null");
        }
        return sensitive ? str1.rupareTo(str2) : str1.rupareToIgnoreCase(str2);
    }
    /**
     * Compares two strings using the case-sensitivity rule.
     * <p>
     * This method mimics {@link String#equals} but takes case-sensitivity
     * into account.
     * 
     * @param str1  the first string to compare, not null
     * @param str2  the second string to compare, not null
     * @return true if equal using the case rules
     * @throws NullPointerException if either string is null
     */
    public boolean checkEquals(String str1, String str2) {
        if (str1 == null || str2 == null) {
            throw new NullPointerException("The strings must not be null");
        }
        return sensitive ? str1.equals(str2) : str1.equalsIgnoreCase(str2);
    }
    /**
     * Checks if one string starts with another using the case-sensitivity rule.
     * <p>
     * This method mimics {@link String#startsWith(String)} but takes case-sensitivity
     * into account.
     * 
     * @param str  the string to check, not null
     * @param start  the start to compare against, not null
     * @return true if equal using the case rules
     * @throws NullPointerException if either string is null
     */
    public boolean checkStartsWith(String str, String start) {
        return str.regionMatches(!sensitive, 0, start, 0, start.length());
    }
    /**
     * Checks if one string ends with another using the case-sensitivity rule.
     * <p>
     * This method mimics {@link String#endsWith} but takes case-sensitivity
     * into account.
     * 
     * @param str  the string to check, not null
     * @param end  the end to compare against, not null
     * @return true if equal using the case rules
     * @throws NullPointerException if either string is null
     */
    public boolean checkEndsWith(String str, String end) {
        int endLen = end.length();
        return str.regionMatches(!sensitive, str.length() - endLen, end, 0, endLen);
    }
    /**
     * Checks if one string contains another at a specific index using the case-sensitivity rule.
     * <p>
     * This method mimics parts of {@link String#regionMatches(boolean, int, String, int, int)} 
     * but takes case-sensitivity into account.
     * 
     * @param str  the string to check, not null
     * @param strStartIndex  the index to start at in str
     * @param search  the start to search for, not null
     * @return true if equal using the case rules
     * @throws NullPointerException if either string is null
     */
    public boolean checkRegionMatches(String str, int strStartIndex, String search) {
        return str.regionMatches(!sensitive, strStartIndex, search, 0, search.length());
    }
    /**
     * Converts the case of the input String to a standard format.
     * Subsequent operations can then use standard String methods.
     * 
     * @param str  the string to convert, null returns null
     * @return the lower-case version if case-insensitive
     */
    String convertCase(String str) {
        if (str == null) {
            return null;
        }
        return sensitive ? str : str.toLowerCase();
    }
    //-----------------------------------------------------------------------
    /**
     * Gets a string describing the sensitivity.
     * 
     * @return a string describing the sensitivity
     */
    public String toString() {
        return name;
    }
}





Get File Name Suffix

      
/*
 * Copyright (C) 2001-2003 Colin Bell
 * colbell@users.sourceforge.net
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
import java.io.*;
import java.text.NumberFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * General purpose utilities functions.
 *
 * @author 
 */
public class Utilities
{
  /**
   * Return the suffix of the passed file name.
   *
   * @param  fileName  File name to retrieve suffix for.
   *
   * @return Suffix for <TT>fileName</TT> or an empty string
   *       if unable to get the suffix.
   *
   * @throws IllegalArgumentException  if <TT>null</TT> file name passed.
   */
  public static String getFileNameSuffix(String fileName)
  {
     if (fileName == null)
     {
        throw new IllegalArgumentException("file name == null");
     }
     int pos = fileName.lastIndexOf(".");
     if (pos > 0 && pos < fileName.length() - 1)
     {
        return fileName.substring(pos + 1);
     }
     return "";
  }
}





Get relative path

    
import java.io.File;

public class Main {
  
  public static String relative( final File base, final File file ) {
    final int rootLength = base.getAbsolutePath().length();
    final String absFileName = file.getAbsolutePath();
    final String relFileName = absFileName.substring(rootLength + 1);
    return relFileName;
}
}





Match a path which may contain a wildcard

      
/*
Copyright (c) 2003 eInnovation Inc. All rights reserved
This library is free software; you can redistribute it and/or modify it under the terms
of the GNU Lesser General Public License as published by the Free Software Foundation;
either version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
*/
/*--
 Copyright (C) 2001-2002 Anthony Eden.
 All rights reserved.
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions
 are met:
 1. Redistributions of source code must retain the above copyright
    notice, this list of conditions, and the following disclaimer.
 2. Redistributions in binary form must reproduce the above copyright
    notice, this list of conditions, and the disclaimer that follows
    these conditions in the documentation and/or other materials
    provided with the distribution.
 3. The name "JPublish" must not be used to endorse or promote products
    derived from this software without prior written permission.  For
    written permission, please contact me@anthonyeden.ru.
 4. Products derived from this software may not be called "JPublish", nor
    may "JPublish" appear in their name, without prior written permission
    from Anthony Eden (me@anthonyeden.ru).
 In addition, I request (but do not require) that you include in the
 end-user documentation provided with the redistribution and/or in the
 software itself an acknowledgement equivalent to the following:
     "This product includes software developed by
      Anthony Eden (http://www.anthonyeden.ru/)."
 THIS SOFTWARE IS PROVIDED ``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 THE AUTHOR(S) 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.
 For more information on JPublish, please see <http://www.jpublish.org/>.
 */

/**
 * Utility class for working with request paths.
 *
 * @author Anthony Eden
 */
public final class PathUtilities
{
  private static final String WILDCARD = "*";
  /**
   * Match a path which may contain a wildcard.
   *
   * @param requestPath The request path submitted by the client
   * @param exPath The match path with * wildcard
   *
   * @return DOCME
   */
  public static boolean match(String requestPath, String wildcardPath)
  {
    //  *somestuffhereverylong*  != stuff
    if( wildcardPath.length() - 2 > requestPath.length())
    {
      return false;
    }
    
    //log.debug("match(" + requestPath + "," + exPath + ")");
    int wildcardIndex = wildcardPath.indexOf(WILDCARD);
    if (wildcardIndex == -1)
    {
      return requestPath.equalsIgnoreCase(wildcardPath);
    }
    else if( wildcardPath.charAt(0) == "*" && wildcardPath.charAt(wildcardPath.length()-1) == "*" )
    {
      String path = wildcardPath.substring(1,wildcardPath.length()-1);
      return requestPath.indexOf(path) > -1;
    }
    else if (wildcardIndex == (wildcardPath.length() - 1)) //ends with *
    {
        //log.debug("Wildcard appears at end of match path.");
        String checkString = wildcardPath.substring(0, wildcardPath.length() - 1);
        //  /stuff/* -> /stuff     /stuff/abc* != /stuff/ab
        
        if( checkString.charAt(checkString.length()-1) == "/")
        {
          checkString = checkString.substring(0,checkString.length() - 1);
        }
        //log.debug("String after wildcard removed: " + checkString);
        boolean answer = requestPath.startsWith(checkString);
        //log.debug("Does " + requestPath + " start with " + checkString + "? " + answer);
        return answer;
    }
    else if( wildcardPath.charAt(0) == "*")
    {
      String checkString = wildcardPath.substring(1);
      //log.debug("String after wildcard removed: " + checkString);
      boolean answer = requestPath.endsWith(checkString);
      return answer;
    }
    else
    {
      //log.debug("Wildcard appears in the middle of the string");
      String preMatch = wildcardPath.substring(0, wildcardIndex);
      String postMatch = wildcardPath.substring(wildcardIndex + 1);
      return requestPath.startsWith(preMatch) && requestPath.endsWith(postMatch);
    }
  }
}





Merges the two paths to create a valid version of the second path

      
/* 
 * 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.
 *
 */
import java.io.File;
/**
 * 
 * FileUtils is a collection of routines for common file system operations.
 * 
 * @author Dan Jemiolo (danj)
 * 
 */
public final class FileUtils {
  /**
   * 
   * Merges the two paths to create a valid version of the second path. This
   * method should be used when you encounter a relative path in a document and
   * must resolve it based on the path of the current document. An example would
   * be: <br>
   * <br>
   * <b>original path</b> - files/customers/Orders.xml <br>
   * <br>
   * <b>relative path</b> - ../Accounts.xml <br>
   * <br>
   * <b>result</b> - files/customers/Accounts.xml <br>
   * <br>
   * The only time this method cannot be used is if the original path is for a
   * file that is in the root (has no directory as part of its path) and the
   * relative path starts with "..".
   * 
   * @param originalPath
   *          The path of the file that references another file.
   * 
   * @param relativePath
   *          The path of the other file, which is relative to the original.
   * 
   * @return A proper path for the other file, one that can be used to open and
   *         verify the file.
   * 
   */
  public static String createRelativePath(String originalPath, String relativePath) {
    if (originalPath == null)
      throw new NullPointerException("NullOriginalPath");
    if (relativePath == null)
      throw new NullPointerException("NullRelativePath");
    //
    // remove ./ if present
    //
    if (relativePath.startsWith("./"))
      relativePath = relativePath.substring(2);
    //
    // remove any .. reference by taking off the last section/ of
    // the original path
    //
    if (relativePath.startsWith("../")) {
      int slash = originalPath.lastIndexOf("/");
      originalPath = originalPath.substring(0, slash);
      relativePath = relativePath.substring(3);
    }
    int slash = originalPath.lastIndexOf("/");
    if (slash < 0)
      return relativePath;
    String dir = originalPath.substring(0, slash + 1);
    return dir + relativePath;
  }
}





Remove File Name Suffix

     
/*
 * Copyright (C) 2001-2003 Colin Bell
 * colbell@users.sourceforge.net
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
import java.io.*;
import java.text.NumberFormat;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * General purpose utilities functions.
 *
 * @author 
 */
public class Utilities
{
  /**
   * Remove the suffix from the passed file name.
   *
   * @param  fileName  File name to remove suffix from.
   *
   * @return <TT>fileName</TT> without a suffix.
   *
   * @throws IllegalArgumentException  if <TT>null</TT> file name passed.
   */
  public static String removeFileNameSuffix(String fileName)
  {
     if (fileName == null)
     {
        throw new IllegalArgumentException("file name == null");
     }
     int pos = fileName.lastIndexOf(".");
     if (pos > 0 && pos < fileName.length() - 1)
     {
        return fileName.substring(0, pos);
     }
     return fileName;
  }
}





Remove path and file information from a filename returning only its extension component

      
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
 Derby - Class org.apache.derby.iapi.util.PropertyUtil
 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.
 */
public class Main {

  /**
   * Remove path and file information from a filename returning only its
   * extension  component
   *
   * @param uri The filename
   * @return The filename extension (with starting dot!) or null if filename extension is not found
   */
  public static String getExtension(String uri) {
      int dot = uri.lastIndexOf(".");
      if (dot > -1) {
          uri = uri.substring(dot);
          int slash = uri.lastIndexOf("/");
          if (slash > -1) {
              return null;
          } else {
              int sharp = uri.lastIndexOf("#");
              if (sharp > -1) {
                  // uri starts with dot already
                  return uri.substring(0, sharp);
              } else {
                  int mark = uri.lastIndexOf("?");
                  if (mark > -1) {
                      // uri starts with dot already
                      return uri.substring(0, mark);
                  } else {
                      return uri;
                  }
              }
          }
      } else {
          return null;
      }
  }

}





Rename To Temporary Name

      
/**
 * Copyright (c) 2003 - 2007 OpenSubsystems s.r.o. Slovak Republic. All rights reserved.
 * 
 * Project: OpenSubsystems
 * 
 * $Id: FileUtils.java,v 1.12 2007/02/01 07:18:32 bastafidli Exp $
 * 
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; version 2 of the License. 
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
 */

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
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.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.logging.Logger;

/**
 * Collection of methods to make work with files easier.
 * 
 * @version $Id: FileUtils.java,v 1.12 2007/02/01 07:18:32 bastafidli Exp $
 * @author Miro Halas
 * @code.reviewer Miro Halas
 * @code.reviewed 1.7 2006/05/21 03:45:37 bastafidli
 */
public class FileUtils
{
   // Configuration settings ///////////////////////////////////////////////////
   
   /**
    * Default 10 digit file storage distribution array. This means that if I 
    * want to name file as 10 digit number e.g. number 123 as 0000000123 or 
    * number 123456789 as 01234567890. Then the path constructed from number 
    * 1234567890 using distribution 2/2/2/4 would be 12/34/56/0123456789 
    */
   public static final int[] DEFAULT_STRORAGE_TREE_DISTRIBUTION = {2, 2, 2, 4};
   
   /**
    * How big buffer to use to process files.
    */
   public static final int BUFFER_SIZE = 65536;
   
   // Cached values ////////////////////////////////////////////////////////////
   
   /**
    * Temporary directory to use. It is guarantee that it ends with \ (or /)
    */
   protected static String s_strTempDirectory;
   
   // Constructors /////////////////////////////////////////////////////////////
   
  
   
   /**
    * Move file to a new location. If the destination is on different volume,
    * this file will be copied and then original file will be deleted.
    * If the destination already exists, this method renames it with different
    * name and leaves it in that directory and moves the new file along side 
    * the renamed one.
    * 
    * @param flCurrent - file to move
    * @param flDestination - destination file
    * @throws IOException - error message
    * @throws OSSException - error message
    */
   public static void moveFile(
      File flCurrent, 
      File flDestination
   ) throws IOException
   {
      // Make sure that the source exist, it might be already moved from 
      // a directory and we just don"t know about it
      if (flCurrent.exists())
      {
         // Next check if the destination file exists
         if (flDestination.exists())
         {
            // If the destination exists, that means something went wrong
            // Rename the destination file under temporaty name and try to  
            // move the new file instead of it
          
            renameToTemporaryName(flDestination, "old");
         }
      
         // Make sure the directory exists and if not create it
         File flFolder;
         
         flFolder = flDestination.getParentFile();
         if ((flFolder != null) && (!flFolder.exists()))
         {
            if (!flFolder.mkdirs())
            {
               // Do not throw the exception if the directory already exists
               // because it was created meanwhile for example by a different 
               // thread
               if (!flFolder.exists())
               {
                  throw new IOException("Cannot create directory " + flFolder);
               }
            }
         }
         
         // Now everything should exist so try to rename the file first
         // After testing, this renames files even between volumes C to H 
         // so we don"t have to do anything else on Windows but we still
         // have to handle erro on Unix 
         if (!flCurrent.renameTo(flDestination))
         {
            // Try to copy and delete since the rename doesn"t work on Solaris
            // between file systems
            copyFile(flCurrent, flDestination);
            
            // Now delete the file
            if (!flCurrent.delete())
            {
               // Delete the destination file first since we haven"t really moved
               // the file
               flDestination.delete();
               throw new IOException("Cannot delete already copied file " + flCurrent);
            }
         }
      }   
   }
 
   /**
    * Copy the current file to the destination file.
    * 
    * @param flCurrent - source file
    * @param flDestination - destination file
    * @throws IOException - error message
    * @throws OSSException - error message
    */  
   public static void copyFile(
      File flCurrent,
      File flDestination
   ) throws IOException
   {
     // Make sure the directory exists and if not create it
     File flFolder;
     
     flFolder = flDestination.getParentFile();
     if ((flFolder != null) && (!flFolder.exists()))
     {
        if (!flFolder.mkdirs())
        {
           // Do not throw the exception if the directory already exists
           // because it was created meanwhile for example by a different 
           // thread
           if (!flFolder.exists())
           {
              throw new IOException("Cannot create directory " + flFolder);
           }
        }
     }
      // FileChannel srcChannel = null;
      // FileChannel dstChannel = null;
      FileInputStream finInput = null;
      
      //MHALAS: This code is not working reliably on Solaris 8 with 1.4.1_01
      // Getting exceptions from native code
      /*
      // Create channel on the source
      srcChannel = new FileInputStream(flCurrent).getChannel();
      // Create channel on the destination
      dstChannel = new FileOutputStream(flDestination).getChannel();
 
      // Copy file contents from source to destination
      dstChannel.transferFrom(srcChannel, 0, srcChannel.size());   
         
      Don"t forget to close the channels if you enable this code again
      */
      try
      {
         finInput = new FileInputStream(flCurrent);
      }
      catch (IOException ioExec)
      {
         if (finInput != null)
         {
            try
            {
               finInput.close();
            }
            catch (Throwable thr)
            {
              
            }
         }
         throw ioExec;
      }
      FileUtils.copyStreamToFile(finInput, flDestination);
   }
   
   /**
    * Rename the file to temporaty name with given prefix
    * 
    * @param flFileToRename - file to rename
    * @param strPrefix - prefix to use
    * @throws IOException - error message
    */
   public static void renameToTemporaryName(
      File   flFileToRename,
      String strPrefix
   ) throws IOException
   {
      assert strPrefix != null : "Prefix cannot be null.";
      
      String       strParent;
      StringBuffer sbBuffer = new StringBuffer();
      File         flTemp;
      int          iIndex = 0;
      
      strParent = flFileToRename.getParent();
      // Generate new name for the file in a deterministic way
      do
      {
         iIndex++;
         sbBuffer.delete(0, sbBuffer.length());
         if (strParent != null) 
         {
            sbBuffer.append(strParent);
            sbBuffer.append(File.separatorChar);
         }
         
         sbBuffer.append(strPrefix);
         sbBuffer.append("_");
         sbBuffer.append(iIndex);
         sbBuffer.append("_");
         sbBuffer.append(flFileToRename.getName());
               
         flTemp = new File(sbBuffer.toString());
      }      
      while (flTemp.exists());
      
      // Now we should have unique name
      if (!flFileToRename.renameTo(flTemp))
      {
         throw new IOException("Cannot rename " + flFileToRename.getAbsolutePath()
                               + " to " + flTemp.getAbsolutePath());
      }
   }
   /** 
    * Delete all files and directories in directory but do not delete the
    * directory itself.
    * 
    * @param strDir - string that specifies directory to delete
    * @return boolean - sucess flag
    */
   public static boolean deleteDirectoryContent(
      String strDir
   )
   {
      return ((strDir != null) && (strDir.length() > 0)) 
              ? deleteDirectoryContent(new File(strDir)) : false;
   }
   /** 
    * Delete all files and directories in directory but do not delete the
    * directory itself.
    * 
    * @param fDir - directory to delete
    * @return boolean - sucess flag
    */
   public static boolean deleteDirectoryContent(
      File fDir
   )
   {
      boolean bRetval = false;
      if (fDir != null && fDir.isDirectory()) 
      {
         File[] files = fDir.listFiles();
   
         if (files != null)
         {
            bRetval = true;
            boolean dirDeleted;
            
            for (int index = 0; index < files.length; index++)
            {
               if (files[index].isDirectory())
               {
                  // TODO: Performance: Implement this as a queue where you add to
                  // the end and take from the beginning, it will be more efficient
                  // than the recursion
                  dirDeleted = deleteDirectoryContent(files[index]);
                  if (dirDeleted)
                  {
                     bRetval = bRetval && files[index].delete();
                  }
                  else
                  {
                     bRetval = false;
                  }
               }
               else
               {
                  bRetval = bRetval && files[index].delete();
               }
            }
         }
      }
      return bRetval;
   }
   /**
    * Deletes all files and subdirectories under the specified directory including 
    * the specified directory
    * 
    * @param strDir - string that specifies directory to be deleted
    * @return boolean - true if directory was successfully deleted
    */
   public static boolean deleteDir(
      String strDir
   ) 
   {
      return ((strDir != null) && (strDir.length() > 0)) 
                ? deleteDir(new File(strDir)) : false;
   }
   
   /**
    * Deletes all files and subdirectories under the specified directory including 
    * the specified directory
    * 
    * @param fDir - directory to be deleted
    * @return boolean - true if directory was successfully deleted
    */
   public static boolean deleteDir(
      File fDir
   ) 
   {
      boolean bRetval = false;
      if (fDir != null && fDir.exists())
      {
         bRetval = deleteDirectoryContent(fDir);
         if (bRetval)
         {
            bRetval = bRetval && fDir.delete();         
         }
      }
      return bRetval;
   }
   
   /**
    * Compare binary files. Both files must be files (not directories) and exist.
    * 
    * @param first  - first file
    * @param second - second file
    * @return boolean - true if files are binery equal
    * @throws IOException - error in function
    */
   public boolean isFileBinaryEqual(
      File first,
      File second
   ) throws IOException
   {
      // TODO: Test: Missing test
      boolean retval = false;
      
      if ((first.exists()) && (second.exists()) 
         && (first.isFile()) && (second.isFile()))
      {
         if (first.getCanonicalPath().equals(second.getCanonicalPath()))
         {
            retval = true;
         }
         else
         {
            FileInputStream firstInput = null;
            FileInputStream secondInput = null;
            BufferedInputStream bufFirstInput = null;
            BufferedInputStream bufSecondInput = null;
            try
            {            
               firstInput = new FileInputStream(first); 
               secondInput = new FileInputStream(second);
               bufFirstInput = new BufferedInputStream(firstInput, BUFFER_SIZE); 
               bufSecondInput = new BufferedInputStream(secondInput, BUFFER_SIZE);
   
               int firstByte;
               int secondByte;
               
               while (true)
               {
                  firstByte = bufFirstInput.read();
                  secondByte = bufSecondInput.read();
                  if (firstByte != secondByte)
                  {
                     break;
                  }
                  if ((firstByte < 0) && (secondByte < 0))
                  {
                     retval = true;
                     break;
                  }
               }
            }
            finally
            {
               try
               {
                  if (bufFirstInput != null)
                  {
                     bufFirstInput.close();
                  }
               }
               finally
               {
                  if (bufSecondInput != null)
                  {
                     bufSecondInput.close();
                  }
               }
            }
         }
      }
      
      return retval;
   }

      
   /**
    * Get path which represents temporary directory. It is guarantee that it 
    * ends with \ (or /).
    * 
    * @return String
    */
   public static String getTemporaryDirectory(
   )
   {
      return s_strTempDirectory;
   }
   
   /**
    * Copy any input stream to output file. Once the data will be copied
    * the stream will be closed.
    * 
    * @param input  - InputStream to copy from
    * @param output - File to copy to
    * @throws IOException - error in function
    * @throws OSSMultiException - double error in function
    */
   public static void copyStreamToFile(
      InputStream input,
      File        output
   ) throws IOException
   {
      FileOutputStream foutOutput = null;
      // open input file as stream safe - it can throw some IOException
      try
      {
         foutOutput = new FileOutputStream(output);
      }
      catch (IOException ioExec)
      {
         if (foutOutput != null)
         {
            try
            {
               foutOutput.close();
            }
            catch (IOException ioExec2)
            {
               
            }
         }            
         
         throw ioExec;
      }
      // all streams including os are closed in copyStreamToStream function 
      // in any case
      copyStreamToStream(input, foutOutput);
   }
   /**
    * Copy any input stream to output stream. Once the data will be copied
    * both streams will be closed.
    * 
    * @param input  - InputStream to copy from
    * @param output - OutputStream to copy to
    * @throws IOException - io error in function
    * @throws OSSMultiException - double error in function
    */
   public static void copyStreamToStream(
      InputStream input,
      OutputStream output
   ) throws IOException
   {
      InputStream is = null;
      OutputStream os = null;
      int                 ch;
      try
      {
         if (input instanceof BufferedInputStream)
         {
            is = input;
         }
         else
         {
            is = new BufferedInputStream(input);
         }
         if (output instanceof BufferedOutputStream)
         {
            os = output;
         }
         else
         {
            os = new BufferedOutputStream(output);
         }
   
         while ((ch = is.read()) != -1)
         {
            os.write(ch);
         }
         os.flush();
      }
      finally
      {
         IOException exec1 = null;
         IOException exec2 = null;
         try
         {
            // because this close can throw exception we do next close in 
            // finally statement
            if (os != null)
            {
               try
               {
                  os.close();
               }
               catch (IOException exec)
               {
                  exec1 = exec;
               }
            }
         }
         finally
         {
            if (is != null)
            {
               try
               {
                  is.close();
               }
               catch (IOException exec)
               {
                  exec2 = exec;
               }
            }
         }
         if ((exec1 != null) && (exec2 != null))
         {
           throw exec1;
         }
         else if (exec1 != null)
         {
            throw exec1;
         }
         else if (exec2 != null)
         {
            throw exec2;
         }
      }
   }
}





Return a context-relative path, beginning with a "/", that represents the canonical version of the specified path after ".." and "." elements are resolved out.

  
/*
 * 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.    
 */
/**
 * 
 * 
 * @author 
 * @version $Id: StringUtils.java 685685 2008-08-13 21:43:27Z nbubna $
 */
public class Main {
  /**
   * Return a context-relative path, beginning with a "/", that represents the
   * canonical version of the specified path after ".." and "." elements are
   * resolved out. If the specified path attempts to go outside the boundaries
   * of the current context (i.e. too many ".." path elements are present),
   * return <code>null</code> instead.
   * 
   * @param path
   *          Path to be normalized
   * @return String normalized path
   */
  public static final String normalizePath(String path) {
    // Normalize the slashes and add leading slash if necessary
    String normalized = path;
    if (normalized.indexOf("\\") >= 0) {
      normalized = normalized.replace("\\", "/");
    }
    if (!normalized.startsWith("/")) {
      normalized = "/" + normalized;
    }
    // Resolve occurrences of "//" in the normalized path
    while (true) {
      int index = normalized.indexOf("//");
      if (index < 0)
        break;
      normalized = normalized.substring(0, index) + normalized.substring(index + 1);
    }
    // Resolve occurrences of "%20" in the normalized path
    while (true) {
      int index = normalized.indexOf("%20");
      if (index < 0)
        break;
      normalized = normalized.substring(0, index) + " " + normalized.substring(index + 3);
    }
    // Resolve occurrences of "/./" in the normalized path
    while (true) {
      int index = normalized.indexOf("/./");
      if (index < 0)
        break;
      normalized = normalized.substring(0, index) + normalized.substring(index + 2);
    }
    // Resolve occurrences of "/../" in the normalized path
    while (true) {
      int index = normalized.indexOf("/../");
      if (index < 0)
        break;
      if (index == 0)
        return (null); // Trying to go outside our context
      int index2 = normalized.lastIndexOf("/", index - 1);
      normalized = normalized.substring(0, index2) + normalized.substring(index + 3);
    }
    // Return the normalized path that we have completed
    return (normalized);
  }
}





Returns the path of the given resource.

      
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/*
 Derby - Class org.apache.derby.iapi.util.PropertyUtil
 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.
 */
public class Main {
  /**
   * Returns the path of the given resource.
   *
   * @param uri The URI of the resource
   * @return the resource path
   */
  public static String getPath(String uri) {
      int i = uri.lastIndexOf("/");
      if (i > -1) {
          return uri.substring(0, i);
      }
      i = uri.indexOf(":");
      return (i > -1) ? uri.substring(i + 1, uri.length()) : "";
  }
}





Return the path within a base directory

  
/*
 * 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.
 */
import java.io.File;

public class Main {

  /**
   * Return the path within a base directory
   */
  public static String getContextFilePath(String directoryPath, String filePath) {
      try {
          File directory = new File(directoryPath);
          File file = new File(filePath);
          directoryPath = directory.getCanonicalPath();
          filePath = file.getCanonicalPath();
          // If the context directory does not have a File.separator
          // at the end then add one explicitly
          if(!directoryPath.endsWith(File.separator)){
              directoryPath += File.separator;
          }
          // If the context dir contains both kinds of separator
          // then standardize on using the File.separator
          if ((directoryPath.indexOf("/") !=-1) && (directoryPath.indexOf("\\") !=-1)) {
              directoryPath = directoryPath.replace("\\", File.separator.charAt(0));
              directoryPath = directoryPath.replace("/", File.separator.charAt(0));
          }
          // If the file path contains both kinds of separator
          // then standardize on using the File.separator
          if ((filePath.indexOf("/") !=-1) && (filePath.indexOf("\\") !=-1)) {
              filePath = filePath.replace("\\", File.separator.charAt(0));
              filePath = filePath.replace("/", File.separator.charAt(0));
          }
          if (filePath.startsWith(directoryPath)) {
              filePath = filePath.substring(directoryPath.length());
          }
      } catch (Exception e){
          // ignore
      }
      return filePath;
  }
}





strip Extension name

   
import java.io.File;

public class Main {
  

  /**
   * org/my/Class.xxx -> org/my/Class
   */
  public static String stripExtension( final String pResourceName ) {
      final int i = pResourceName.lastIndexOf(".");
      if (i < 0) {
          return pResourceName;
      }
      final String withoutExtension = pResourceName.substring(0, i);
      return withoutExtension;
  }
}





Strip File Extension

      
/** 
 * 
 * The ObjectStyle Group Software License, version 1.1
 * ObjectStyle Group - http://objectstyle.org/
 * 
 * Copyright (c) 2002-2005, Andrei (Andrus) Adamchik and individual authors
 * of the software. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 
 * 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 end-user documentation included with the redistribution, if any,
 *    must include the following acknowlegement:
 *    "This product includes software developed by independent contributors
 *    and hosted on ObjectStyle Group web site (http://objectstyle.org/)."
 *    Alternately, this acknowlegement may appear in the software itself,
 *    if and wherever such third-party acknowlegements normally appear.
 * 
 * 4. The names "ObjectStyle Group" and "Cayenne" must not be used to endorse
 *    or promote products derived from this software without prior written
 *    permission. For written permission, email
 *    "andrus at objectstyle dot org".
 * 
 * 5. Products derived from this software may not be called "ObjectStyle"
 *    or "Cayenne", nor may "ObjectStyle" or "Cayenne" appear in their
 *    names without prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED ``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 THE OBJECTSTYLE GROUP 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.
 * 
 * 
 * This software consists of voluntary contributions made by many
 * individuals and hosted on ObjectStyle Group web site.  For more
 * information on the ObjectStyle Group, please see
 * <http://objectstyle.org/>.
 */
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.reflect.Member;
import java.lang.reflect.Modifier;
import java.net.URL;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.ruparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
/**
 * Contains various unorganized static utility methods used across Cayenne.
 * 
 * @author Andrei Adamchik
 */
public class Util {
  /**
   * Strips extension from the file name.
   */
  public static String stripFileExtension(String fileName) {
      int dotInd = fileName.lastIndexOf(".");
      // if dot is in the first position,
      // we are dealing with a hidden file rather than an extension
      return (dotInd > 0) ? fileName.substring(0, dotInd) : fileName;
  }
}