From 5716c15248acdb7dba42d951cf8273ee87cc6846 Mon Sep 17 00:00:00 2001 From: Xavier Ducrohet Date: Mon, 14 May 2012 17:53:39 -0700 Subject: Original upload of the glob library This is a few extracted classes from Ant, under Apache License Public 2.0 Change-Id: Id1b91ebab95e8d746787d40d5a1a820bd77bff8d --- .classpath | 6 + .gitignore | 6 + .project | 17 + Android.mk | 27 + MODULE_LICENSE_APL | 0 NOTICE | 272 +++++++++ README.txt | 27 + src/org/apache/tools/ant/BuildException.java | 79 +++ .../tools/ant/taskdefs/condition/Condition.java | 35 ++ .../apache/tools/ant/taskdefs/condition/Os.java | 322 ++++++++++ .../tools/ant/types/selectors/SelectorUtils.java | 647 +++++++++++++++++++++ src/org/apache/tools/ant/util/FileUtils.java | 189 ++++++ 12 files changed, 1627 insertions(+) create mode 100644 .classpath create mode 100644 .gitignore create mode 100644 .project create mode 100644 Android.mk create mode 100644 MODULE_LICENSE_APL create mode 100644 NOTICE create mode 100644 README.txt create mode 100644 src/org/apache/tools/ant/BuildException.java create mode 100644 src/org/apache/tools/ant/taskdefs/condition/Condition.java create mode 100644 src/org/apache/tools/ant/taskdefs/condition/Os.java create mode 100644 src/org/apache/tools/ant/types/selectors/SelectorUtils.java create mode 100644 src/org/apache/tools/ant/util/FileUtils.java diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..fb50116 --- /dev/null +++ b/.classpath @@ -0,0 +1,6 @@ + + + + + + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3826f65 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +bin +*~ +*.bak +Thumbs.db +*.class +*.DS_Store diff --git a/.project b/.project new file mode 100644 index 0000000..342bc23 --- /dev/null +++ b/.project @@ -0,0 +1,17 @@ + + + ant-glob + + + + + + org.eclipse.jdt.core.javabuilder + + + + + + org.eclipse.jdt.core.javanature + + diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..4089edf --- /dev/null +++ b/Android.mk @@ -0,0 +1,27 @@ +# Copyright (C) 2012 The Android Open Source Project +# +# 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +# Only compile source java files in this lib. +LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_JAVA_RESOURCE_DIRS := src + +LOCAL_MODULE := ant-glob + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_HOST_JAVA_LIBRARY) diff --git a/MODULE_LICENSE_APL b/MODULE_LICENSE_APL new file mode 100644 index 0000000..e69de29 diff --git a/NOTICE b/NOTICE new file mode 100644 index 0000000..cdf6ff8 --- /dev/null +++ b/NOTICE @@ -0,0 +1,272 @@ +/* + * Apache License + * Version 2.0, January 2004 + * http://www.apache.org/licenses/ + * + * TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + * + * 1. Definitions. + * + * "License" shall mean the terms and conditions for use, reproduction, + * and distribution as defined by Sections 1 through 9 of this document. + * + * "Licensor" shall mean the copyright owner or entity authorized by + * the copyright owner that is granting the License. + * + * "Legal Entity" shall mean the union of the acting entity and all + * other entities that control, are controlled by, or are under common + * control with that entity. For the purposes of this definition, + * "control" means (i) the power, direct or indirect, to cause the + * direction or management of such entity, whether by contract or + * otherwise, or (ii) ownership of fifty percent (50%) or more of the + * outstanding shares, or (iii) beneficial ownership of such entity. + * + * "You" (or "Your") shall mean an individual or Legal Entity + * exercising permissions granted by this License. + * + * "Source" form shall mean the preferred form for making modifications, + * including but not limited to software source code, documentation + * source, and configuration files. + * + * "Object" form shall mean any form resulting from mechanical + * transformation or translation of a Source form, including but + * not limited to compiled object code, generated documentation, + * and conversions to other media types. + * + * "Work" shall mean the work of authorship, whether in Source or + * Object form, made available under the License, as indicated by a + * copyright notice that is included in or attached to the work + * (an example is provided in the Appendix below). + * + * "Derivative Works" shall mean any work, whether in Source or Object + * form, that is based on (or derived from) the Work and for which the + * editorial revisions, annotations, elaborations, or other modifications + * represent, as a whole, an original work of authorship. For the purposes + * of this License, Derivative Works shall not include works that remain + * separable from, or merely link (or bind by name) to the interfaces of, + * the Work and Derivative Works thereof. + * + * "Contribution" shall mean any work of authorship, including + * the original version of the Work and any modifications or additions + * to that Work or Derivative Works thereof, that is intentionally + * submitted to Licensor for inclusion in the Work by the copyright owner + * or by an individual or Legal Entity authorized to submit on behalf of + * the copyright owner. For the purposes of this definition, "submitted" + * means any form of electronic, verbal, or written communication sent + * to the Licensor or its representatives, including but not limited to + * communication on electronic mailing lists, source code control systems, + * and issue tracking systems that are managed by, or on behalf of, the + * Licensor for the purpose of discussing and improving the Work, but + * excluding communication that is conspicuously marked or otherwise + * designated in writing by the copyright owner as "Not a Contribution." + * + * "Contributor" shall mean Licensor and any individual or Legal Entity + * on behalf of whom a Contribution has been received by Licensor and + * subsequently incorporated within the Work. + * + * 2. Grant of Copyright License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * copyright license to reproduce, prepare Derivative Works of, + * publicly display, publicly perform, sublicense, and distribute the + * Work and such Derivative Works in Source or Object form. + * + * 3. Grant of Patent License. Subject to the terms and conditions of + * this License, each Contributor hereby grants to You a perpetual, + * worldwide, non-exclusive, no-charge, royalty-free, irrevocable + * (except as stated in this section) patent license to make, have made, + * use, offer to sell, sell, import, and otherwise transfer the Work, + * where such license applies only to those patent claims licensable + * by such Contributor that are necessarily infringed by their + * Contribution(s) alone or by combination of their Contribution(s) + * with the Work to which such Contribution(s) was submitted. If You + * institute patent litigation against any entity (including a + * cross-claim or counterclaim in a lawsuit) alleging that the Work + * or a Contribution incorporated within the Work constitutes direct + * or contributory patent infringement, then any patent licenses + * granted to You under this License for that Work shall terminate + * as of the date such litigation is filed. + * + * 4. Redistribution. You may reproduce and distribute copies of the + * Work or Derivative Works thereof in any medium, with or without + * modifications, and in Source or Object form, provided that You + * meet the following conditions: + * + * (a) You must give any other recipients of the Work or + * Derivative Works a copy of this License; and + * + * (b) You must cause any modified files to carry prominent notices + * stating that You changed the files; and + * + * (c) You must retain, in the Source form of any Derivative Works + * that You distribute, all copyright, patent, trademark, and + * attribution notices from the Source form of the Work, + * excluding those notices that do not pertain to any part of + * the Derivative Works; and + * + * (d) If the Work includes a "NOTICE" text file as part of its + * distribution, then any Derivative Works that You distribute must + * include a readable copy of the attribution notices contained + * within such NOTICE file, excluding those notices that do not + * pertain to any part of the Derivative Works, in at least one + * of the following places: within a NOTICE text file distributed + * as part of the Derivative Works; within the Source form or + * documentation, if provided along with the Derivative Works; or, + * within a display generated by the Derivative Works, if and + * wherever such third-party notices normally appear. The contents + * of the NOTICE file are for informational purposes only and + * do not modify the License. You may add Your own attribution + * notices within Derivative Works that You distribute, alongside + * or as an addendum to the NOTICE text from the Work, provided + * that such additional attribution notices cannot be construed + * as modifying the License. + * + * You may add Your own copyright statement to Your modifications and + * may provide additional or different license terms and conditions + * for use, reproduction, or distribution of Your modifications, or + * for any such Derivative Works as a whole, provided Your use, + * reproduction, and distribution of the Work otherwise complies with + * the conditions stated in this License. + * + * 5. Submission of Contributions. Unless You explicitly state otherwise, + * any Contribution intentionally submitted for inclusion in the Work + * by You to the Licensor shall be under the terms and conditions of + * this License, without any additional terms or conditions. + * Notwithstanding the above, nothing herein shall supersede or modify + * the terms of any separate license agreement you may have executed + * with Licensor regarding such Contributions. + * + * 6. Trademarks. This License does not grant permission to use the trade + * names, trademarks, service marks, or product names of the Licensor, + * except as required for reasonable and customary use in describing the + * origin of the Work and reproducing the content of the NOTICE file. + * + * 7. Disclaimer of Warranty. Unless required by applicable law or + * agreed to in writing, Licensor provides the Work (and each + * Contributor provides its Contributions) on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + * implied, including, without limitation, any warranties or conditions + * of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + * PARTICULAR PURPOSE. You are solely responsible for determining the + * appropriateness of using or redistributing the Work and assume any + * risks associated with Your exercise of permissions under this License. + * + * 8. Limitation of Liability. In no event and under no legal theory, + * whether in tort (including negligence), contract, or otherwise, + * unless required by applicable law (such as deliberate and grossly + * negligent acts) or agreed to in writing, shall any Contributor be + * liable to You for damages, including any direct, indirect, special, + * incidental, or consequential damages of any character arising as a + * result of this License or out of the use or inability to use the + * Work (including but not limited to damages for loss of goodwill, + * work stoppage, computer failure or malfunction, or any and all + * other commercial damages or losses), even if such Contributor + * has been advised of the possibility of such damages. + * + * 9. Accepting Warranty or Additional Liability. While redistributing + * the Work or Derivative Works thereof, You may choose to offer, + * and charge a fee for, acceptance of support, warranty, indemnity, + * or other liability obligations and/or rights consistent with this + * License. However, in accepting such obligations, You may act only + * on Your own behalf and on Your sole responsibility, not on behalf + * of any other Contributor, and only if You agree to indemnify, + * defend, and hold each Contributor harmless for any liability + * incurred by, or claims asserted against, such Contributor by reason + * of your accepting any such warranty or additional liability. + * + * END OF TERMS AND CONDITIONS + * + * APPENDIX: How to apply the Apache License to your work. + * + * To apply the Apache License to your work, attach the following + * boilerplate notice, with the fields enclosed by brackets "[]" + * replaced with your own identifying information. (Don't include + * the brackets!) The text should be enclosed in the appropriate + * comment syntax for the file format. We also recommend that a + * file or class name and description of purpose be included on the + * same "printed page" as the copyright notice for easier + * identification within third-party archives. + * + * Copyright [yyyy] [name of copyright owner] + * + * 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. + */ + +W3CŪ SOFTWARE NOTICE AND LICENSE +http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 + +This work (and included software, documentation such as READMEs, or other +related items) is being provided by the copyright holders under the following +license. By obtaining, using and/or copying this work, you (the licensee) agree +that you have read, understood, and will comply with the following terms and +conditions. + +Permission to copy, modify, and distribute this software and its documentation, +with or without modification, for any purpose and without fee or royalty is +hereby granted, provided that you include the following on ALL copies of the +software and documentation or portions thereof, including modifications: + + 1. The full text of this NOTICE in a location viewable to users of the + redistributed or derivative work. + 2. Any pre-existing intellectual property disclaimers, notices, or terms + and conditions. If none exist, the W3C Software Short Notice should be + included (hypertext is preferred, text is permitted) within the body + of any redistributed or derivative code. + 3. Notice of any changes or modifications to the files, including the date + changes were made. (We recommend you provide URIs to the location from + which the code is derived.) + +THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE +NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT +THE USE OF THE SOFTWARE OR DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY +PATENTS, COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. + +COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENTATION. + +The name and trademarks of copyright holders may NOT be used in advertising or +publicity pertaining to the software without specific, written prior permission. +Title to copyright in this software and any associated documentation will at +all times remain with copyright holders. + +____________________________________ + +This formulation of W3C's notice and license became active on December 31 2002. +This version removes the copyright ownership notice such that this license can +be used with materials other than those owned by the W3C, reflects that ERCIM +is now a host of the W3C, includes references to this specific dated version of +the license, and removes the ambiguous grant of "use". Otherwise, this version +is the same as the previous version and is written so as to preserve the Free +Software Foundation's assessment of GPL compatibility and OSI's certification +under the Open Source Definition. Please see our Copyright FAQ for common +questions about using materials from our site, including specific terms and +conditions for packages like libwww, Amaya, and Jigsaw. Other questions about +this notice can be directed to site-policy@w3.org. + +Joseph Reagle + +This license came from: http://www.megginson.com/SAX/copying.html + However please note future versions of SAX may be covered + under http://saxproject.org/?selected=pd + +SAX2 is Free! + +I hereby abandon any property rights to SAX 2.0 (the Simple API for +XML), and release all of the SAX 2.0 source code, compiled code, and +documentation contained in this distribution into the Public Domain. +SAX comes with NO WARRANTY or guarantee of fitness for any +purpose. + +David Megginson, david@megginson.com +2000-05-05 diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..3e26409 --- /dev/null +++ b/README.txt @@ -0,0 +1,27 @@ +Ant Glob support + +Apache Ant +Copyright 1999-2012 The Apache Software Foundation +------------------------------------- + +This project is a fork of a subset of Ant 1.8.3: +http://ant.apache.com/ + +Specifically, it contains the subset of Ant related to matching glob patterns +to paths + +The fork was modified as follows: +* Started with version 1.8.3 + +* Extracted + org.apache.tools.ant.types.selectors.SelectorUtils + and then everything it transitively references that was truly needed: + org.apache.tools.ant.util.FileUtils, + org.apache.tools.ant.taskdefs.condition.Condition, + org.apache.tools.ant.taskdefs.condition.OS, + org.apache.tools.ant.BuildException + +* FileUtils was pruned to keep only 2 methods, which brought OS (which + brought Condition). In turn this brought in BuildException which was trimmed + up a bit (removing references to Location) + diff --git a/src/org/apache/tools/ant/BuildException.java b/src/org/apache/tools/ant/BuildException.java new file mode 100644 index 0000000..f547cd7 --- /dev/null +++ b/src/org/apache/tools/ant/BuildException.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.tools.ant; + +/** + * Signals an error condition during a build + */ +public class BuildException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + /** + * Constructs a build exception with no descriptive information. + */ + public BuildException() { + super(); + } + + /** + * Constructs an exception with the given descriptive message. + * + * @param message A description of or information about the exception. + * Should not be null. + */ + public BuildException(String message) { + super(message); + } + + /** + * Constructs an exception with the given message and exception as + * a root cause. + * + * @param message A description of or information about the exception. + * Should not be null unless a cause is specified. + * @param cause The exception that might have caused this one. + * May be null. + */ + public BuildException(String message, Throwable cause) { + super(message); + initCause(cause); + } + + /** + * Constructs an exception with the given exception as a root cause. + * + * @param cause The exception that might have caused this one. + * Should not be null. + */ + public BuildException(Throwable cause) { + super(cause); + } + + /** + * Returns the nested exception, if any. + * + * @return the nested exception, or null if no + * exception is associated with this one + * @deprecated Use {@link #getCause} instead. + */ + @Deprecated + public Throwable getException() { + return getCause(); + } +} diff --git a/src/org/apache/tools/ant/taskdefs/condition/Condition.java b/src/org/apache/tools/ant/taskdefs/condition/Condition.java new file mode 100644 index 0000000..62adbf3 --- /dev/null +++ b/src/org/apache/tools/ant/taskdefs/condition/Condition.java @@ -0,0 +1,35 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant.taskdefs.condition; + +import org.apache.tools.ant.BuildException; + +/** + * Interface for conditions to use inside the <condition> task. + * + */ +public interface Condition { + /** + * Is this condition true? + * @return true if the condition is true + * @exception BuildException if an error occurs + */ + boolean eval() throws BuildException; +} + diff --git a/src/org/apache/tools/ant/taskdefs/condition/Os.java b/src/org/apache/tools/ant/taskdefs/condition/Os.java new file mode 100644 index 0000000..4e90e35 --- /dev/null +++ b/src/org/apache/tools/ant/taskdefs/condition/Os.java @@ -0,0 +1,322 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant.taskdefs.condition; + +import org.apache.tools.ant.BuildException; + +import java.util.Locale; + +/** + * Condition that tests the OS type. + * + * @since Ant 1.4 + */ +public class Os implements Condition { + private static final String OS_NAME = + System.getProperty("os.name").toLowerCase(Locale.ENGLISH); + private static final String OS_ARCH = + System.getProperty("os.arch").toLowerCase(Locale.ENGLISH); + private static final String OS_VERSION = + System.getProperty("os.version").toLowerCase(Locale.ENGLISH); + private static final String PATH_SEP = + System.getProperty("path.separator"); + + /** + * OS family to look for + */ + private String family; + /** + * Name of OS + */ + private String name; + /** + * version of OS + */ + private String version; + /** + * OS architecture + */ + private String arch; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_WINDOWS = "windows"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_9X = "win9x"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NT = "winnt"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_OS2 = "os/2"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_NETWARE = "netware"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_DOS = "dos"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_MAC = "mac"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_TANDEM = "tandem"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_UNIX = "unix"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_VMS = "openvms"; + /** + * OS family that can be tested for. {@value} + */ + public static final String FAMILY_ZOS = "z/os"; + /** OS family that can be tested for. {@value} */ + public static final String FAMILY_OS400 = "os/400"; + + /** + * OpenJDK is reported to call MacOS X "Darwin" + * @see https://issues.apache.org/bugzilla/show_bug.cgi?id=44889 + * @see https://issues.apache.org/jira/browse/HADOOP-3318 + */ + private static final String DARWIN = "darwin"; + + /** + * Default constructor + * + */ + public Os() { + //default + } + + /** + * Constructor that sets the family attribute + * @param family a String value + */ + public Os(String family) { + setFamily(family); + } + + /** + * Sets the desired OS family type + * + * @param f The OS family type desired
+ * Possible values:
+ * + */ + public void setFamily(String f) { + family = f.toLowerCase(Locale.ENGLISH); + } + + /** + * Sets the desired OS name + * + * @param name The OS name + */ + public void setName(String name) { + this.name = name.toLowerCase(Locale.ENGLISH); + } + + /** + * Sets the desired OS architecture + * + * @param arch The OS architecture + */ + public void setArch(String arch) { + this.arch = arch.toLowerCase(Locale.ENGLISH); + } + + /** + * Sets the desired OS version + * + * @param version The OS version + */ + public void setVersion(String version) { + this.version = version.toLowerCase(Locale.ENGLISH); + } + + /** + * Determines if the OS on which Ant is executing matches the type of + * that set in setFamily. + * @return true if the os matches. + * @throws BuildException if there is an error. + * @see Os#setFamily(String) + */ + @Override + public boolean eval() throws BuildException { + return isOs(family, name, arch, version); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family. + * @param family the family to check for + * @return true if the OS matches + * @since 1.5 + */ + public static boolean isFamily(String family) { + return isOs(family, null, null, null); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS name. + * + * @param name the OS name to check for + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isName(String name) { + return isOs(null, name, null, null); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS architecture. + * + * @param arch the OS architecture to check for + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isArch(String arch) { + return isOs(null, null, arch, null); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS version. + * + * @param version the OS version to check for + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isVersion(String version) { + return isOs(null, null, null, version); + } + + /** + * Determines if the OS on which Ant is executing matches the + * given OS family, name, architecture and version + * + * @param family The OS family + * @param name The OS name + * @param arch The OS architecture + * @param version The OS version + * @return true if the OS matches + * @since 1.7 + */ + public static boolean isOs(String family, String name, String arch, + String version) { + boolean retValue = false; + + if (family != null || name != null || arch != null + || version != null) { + + boolean isFamily = true; + boolean isName = true; + boolean isArch = true; + boolean isVersion = true; + + if (family != null) { + + //windows probing logic relies on the word 'windows' in + //the OS + boolean isWindows = OS_NAME.indexOf(FAMILY_WINDOWS) > -1; + boolean is9x = false; + boolean isNT = false; + if (isWindows) { + //there are only four 9x platforms that we look for + is9x = (OS_NAME.indexOf("95") >= 0 + || OS_NAME.indexOf("98") >= 0 + || OS_NAME.indexOf("me") >= 0 + //wince isn't really 9x, but crippled enough to + //be a muchness. Ant doesnt run on CE, anyway. + || OS_NAME.indexOf("ce") >= 0); + isNT = !is9x; + } + if (family.equals(FAMILY_WINDOWS)) { + isFamily = isWindows; + } else if (family.equals(FAMILY_9X)) { + isFamily = isWindows && is9x; + } else if (family.equals(FAMILY_NT)) { + isFamily = isWindows && isNT; + } else if (family.equals(FAMILY_OS2)) { + isFamily = OS_NAME.indexOf(FAMILY_OS2) > -1; + } else if (family.equals(FAMILY_NETWARE)) { + isFamily = OS_NAME.indexOf(FAMILY_NETWARE) > -1; + } else if (family.equals(FAMILY_DOS)) { + isFamily = PATH_SEP.equals(";") && !isFamily(FAMILY_NETWARE); + } else if (family.equals(FAMILY_MAC)) { + isFamily = OS_NAME.indexOf(FAMILY_MAC) > -1 + || OS_NAME.indexOf(DARWIN) > -1; + } else if (family.equals(FAMILY_TANDEM)) { + isFamily = OS_NAME.indexOf("nonstop_kernel") > -1; + } else if (family.equals(FAMILY_UNIX)) { + isFamily = PATH_SEP.equals(":") + && !isFamily(FAMILY_VMS) + && (!isFamily(FAMILY_MAC) || OS_NAME.endsWith("x") + || OS_NAME.indexOf(DARWIN) > -1); + } else if (family.equals(FAMILY_ZOS)) { + isFamily = OS_NAME.indexOf(FAMILY_ZOS) > -1 + || OS_NAME.indexOf("os/390") > -1; + } else if (family.equals(FAMILY_OS400)) { + isFamily = OS_NAME.indexOf(FAMILY_OS400) > -1; + } else if (family.equals(FAMILY_VMS)) { + isFamily = OS_NAME.indexOf(FAMILY_VMS) > -1; + } else { + throw new BuildException( + "Don\'t know how to detect os family \"" + + family + "\""); + } + } + if (name != null) { + isName = name.equals(OS_NAME); + } + if (arch != null) { + isArch = arch.equals(OS_ARCH); + } + if (version != null) { + isVersion = version.equals(OS_VERSION); + } + retValue = isFamily && isName && isArch && isVersion; + } + return retValue; + } +} diff --git a/src/org/apache/tools/ant/types/selectors/SelectorUtils.java b/src/org/apache/tools/ant/types/selectors/SelectorUtils.java new file mode 100644 index 0000000..388d4d5 --- /dev/null +++ b/src/org/apache/tools/ant/types/selectors/SelectorUtils.java @@ -0,0 +1,647 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package org.apache.tools.ant.types.selectors; + +import org.apache.tools.ant.util.FileUtils; + +import java.io.File; +import java.util.StringTokenizer; +import java.util.Vector; + +/** + *

This is a utility class used by selectors and DirectoryScanner. The + * functionality more properly belongs just to selectors, but unfortunately + * DirectoryScanner exposed these as protected methods. Thus we have to + * support any subclasses of DirectoryScanner that may access these methods. + *

+ *

This is a Singleton.

+ * + * @since 1.5 + */ +public final class SelectorUtils { + + /** + * The pattern that matches an arbitrary number of directories. + * @since Ant 1.8.0 + */ + public static final String DEEP_TREE_MATCH = "**"; + + private static final SelectorUtils instance = new SelectorUtils(); + private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); + + /** + * Private Constructor + */ + private SelectorUtils() { + } + + /** + * Retrieves the instance of the Singleton. + * @return singleton instance + */ + public static SelectorUtils getInstance() { + return instance; + } + + /** + * Tests whether or not a given path matches the start of a given + * pattern up to the first "**". + *

+ * This is not a general purpose test and should only be used if you + * can live with false positives. For example, pattern=**\a + * and str=b will yield true. + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * + * @return whether or not a given path matches the start of a given + * pattern up to the first "**". + */ + public static boolean matchPatternStart(String pattern, String str) { + return matchPatternStart(pattern, str, true); + } + + /** + * Tests whether or not a given path matches the start of a given + * pattern up to the first "**". + *

+ * This is not a general purpose test and should only be used if you + * can live with false positives. For example, pattern=**\a + * and str=b will yield true. + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * + * @return whether or not a given path matches the start of a given + * pattern up to the first "**". + */ + public static boolean matchPatternStart(String pattern, String str, + boolean isCaseSensitive) { + // When str starts with a File.separator, pattern has to start with a + // File.separator. + // When pattern starts with a File.separator, str has to start with a + // File.separator. + if (str.startsWith(File.separator) + != pattern.startsWith(File.separator)) { + return false; + } + + String[] patDirs = tokenizePathAsArray(pattern); + String[] strDirs = tokenizePathAsArray(str); + return matchPatternStart(patDirs, strDirs, isCaseSensitive); + } + + + /** + * Tests whether or not a given path matches the start of a given + * pattern up to the first "**". + *

+ * This is not a general purpose test and should only be used if you + * can live with false positives. For example, pattern=**\a + * and str=b will yield true. + * + * @param patDirs The tokenized pattern to match against. Must not be + * null. + * @param strDirs The tokenized path to match. Must not be + * null. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * + * @return whether or not a given path matches the start of a given + * pattern up to the first "**". + */ + static boolean matchPatternStart(String[] patDirs, String[] strDirs, + boolean isCaseSensitive) { + int patIdxStart = 0; + int patIdxEnd = patDirs.length - 1; + int strIdxStart = 0; + int strIdxEnd = strDirs.length - 1; + + // up to first '**' + while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { + String patDir = patDirs[patIdxStart]; + if (patDir.equals(DEEP_TREE_MATCH)) { + break; + } + if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) { + return false; + } + patIdxStart++; + strIdxStart++; + } + + // CheckStyle:SimplifyBooleanReturnCheck OFF + // Check turned off as the code needs the comments for the various + // code paths. + if (strIdxStart > strIdxEnd) { + // String is exhausted + return true; + } else if (patIdxStart > patIdxEnd) { + // String not exhausted, but pattern is. Failure. + return false; + } else { + // pattern now holds ** while string is not exhausted + // this will generate false positives but we can live with that. + return true; + } + } + + /** + * Tests whether or not a given path matches a given pattern. + * + * If you need to call this method multiple times with the same + * pattern you should rather use TokenizedPath + * + * @see TokenizedPath + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * + * @return true if the pattern matches against the string, + * or false otherwise. + */ + public static boolean matchPath(String pattern, String str) { + String[] patDirs = tokenizePathAsArray(pattern); + return matchPath(patDirs, tokenizePathAsArray(str), true); + } + + /** + * Tests whether or not a given path matches a given pattern. + * + * If you need to call this method multiple times with the same + * pattern you should rather use TokenizedPattern + * + * @see TokenizedPattern + * + * @param pattern The pattern to match against. Must not be + * null. + * @param str The path to match, as a String. Must not be + * null. + * @param isCaseSensitive Whether or not matching should be performed + * case sensitively. + * + * @return true if the pattern matches against the string, + * or false otherwise. + */ + public static boolean matchPath(String pattern, String str, + boolean isCaseSensitive) { + String[] patDirs = tokenizePathAsArray(pattern); + return matchPath(patDirs, tokenizePathAsArray(str), isCaseSensitive); + } + + /** + * Core implementation of matchPath. It is isolated so that it + * can be called from TokenizedPattern. + */ + static boolean matchPath(String[] tokenizedPattern, String[] strDirs, + boolean isCaseSensitive) { + int patIdxStart = 0; + int patIdxEnd = tokenizedPattern.length - 1; + int strIdxStart = 0; + int strIdxEnd = strDirs.length - 1; + + // up to first '**' + while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { + String patDir = tokenizedPattern[patIdxStart]; + if (patDir.equals(DEEP_TREE_MATCH)) { + break; + } + if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) { + return false; + } + patIdxStart++; + strIdxStart++; + } + if (strIdxStart > strIdxEnd) { + // String is exhausted + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + return false; + } + } + return true; + } else { + if (patIdxStart > patIdxEnd) { + // String not exhausted, but pattern is. Failure. + return false; + } + } + + // up to last '**' + while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { + String patDir = tokenizedPattern[patIdxEnd]; + if (patDir.equals(DEEP_TREE_MATCH)) { + break; + } + if (!match(patDir, strDirs[strIdxEnd], isCaseSensitive)) { + return false; + } + patIdxEnd--; + strIdxEnd--; + } + if (strIdxStart > strIdxEnd) { + // String is exhausted + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + return false; + } + } + return true; + } + + while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) { + int patIdxTmp = -1; + for (int i = patIdxStart + 1; i <= patIdxEnd; i++) { + if (tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + patIdxTmp = i; + break; + } + } + if (patIdxTmp == patIdxStart + 1) { + // '**/**' situation, so skip one + patIdxStart++; + continue; + } + // Find the pattern between padIdxStart & padIdxTmp in str between + // strIdxStart & strIdxEnd + int patLength = (patIdxTmp - patIdxStart - 1); + int strLength = (strIdxEnd - strIdxStart + 1); + int foundIdx = -1; + strLoop: + for (int i = 0; i <= strLength - patLength; i++) { + for (int j = 0; j < patLength; j++) { + String subPat = tokenizedPattern[patIdxStart + j + 1]; + String subStr = strDirs[strIdxStart + i + j]; + if (!match(subPat, subStr, isCaseSensitive)) { + continue strLoop; + } + } + + foundIdx = strIdxStart + i; + break; + } + + if (foundIdx == -1) { + return false; + } + + patIdxStart = patIdxTmp; + strIdxStart = foundIdx + patLength; + } + + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) { + return false; + } + } + + return true; + } + + /** + * Tests whether or not a string matches against a pattern. + * The pattern may contain two special characters:
+ * '*' means zero or more characters
+ * '?' means one and only one character + * + * @param pattern The pattern to match against. + * Must not be null. + * @param str The string which must be matched against the pattern. + * Must not be null. + * + * @return true if the string matches against the pattern, + * or false otherwise. + */ + public static boolean match(String pattern, String str) { + return match(pattern, str, true); + } + + /** + * Tests whether or not a string matches against a pattern. + * The pattern may contain two special characters:
+ * '*' means zero or more characters
+ * '?' means one and only one character + * + * @param pattern The pattern to match against. + * Must not be null. + * @param str The string which must be matched against the pattern. + * Must not be null. + * @param caseSensitive Whether or not matching should be performed + * case sensitively. + * + * + * @return true if the string matches against the pattern, + * or false otherwise. + */ + public static boolean match(String pattern, String str, + boolean caseSensitive) { + char[] patArr = pattern.toCharArray(); + char[] strArr = str.toCharArray(); + int patIdxStart = 0; + int patIdxEnd = patArr.length - 1; + int strIdxStart = 0; + int strIdxEnd = strArr.length - 1; + char ch; + + boolean containsStar = false; + for (int i = 0; i < patArr.length; i++) { + if (patArr[i] == '*') { + containsStar = true; + break; + } + } + + if (!containsStar) { + // No '*'s, so we make a shortcut + if (patIdxEnd != strIdxEnd) { + return false; // Pattern and string do not have the same size + } + for (int i = 0; i <= patIdxEnd; i++) { + ch = patArr[i]; + if (ch != '?') { + if (different(caseSensitive, ch, strArr[i])) { + return false; // Character mismatch + } + } + } + return true; // String matches against pattern + } + + if (patIdxEnd == 0) { + return true; // Pattern contains only '*', which matches anything + } + + // Process characters before first star + while (true) { + ch = patArr[patIdxStart]; + if (ch == '*' || strIdxStart > strIdxEnd) { + break; + } + if (ch != '?') { + if (different(caseSensitive, ch, strArr[strIdxStart])) { + return false; // Character mismatch + } + } + patIdxStart++; + strIdxStart++; + } + if (strIdxStart > strIdxEnd) { + // All characters in the string are used. Check if only '*'s are + // left in the pattern. If so, we succeeded. Otherwise failure. + return allStars(patArr, patIdxStart, patIdxEnd); + } + + // Process characters after last star + while (true) { + ch = patArr[patIdxEnd]; + if (ch == '*' || strIdxStart > strIdxEnd) { + break; + } + if (ch != '?') { + if (different(caseSensitive, ch, strArr[strIdxEnd])) { + return false; // Character mismatch + } + } + patIdxEnd--; + strIdxEnd--; + } + if (strIdxStart > strIdxEnd) { + // All characters in the string are used. Check if only '*'s are + // left in the pattern. If so, we succeeded. Otherwise failure. + return allStars(patArr, patIdxStart, patIdxEnd); + } + + // process pattern between stars. padIdxStart and patIdxEnd point + // always to a '*'. + while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) { + int patIdxTmp = -1; + for (int i = patIdxStart + 1; i <= patIdxEnd; i++) { + if (patArr[i] == '*') { + patIdxTmp = i; + break; + } + } + if (patIdxTmp == patIdxStart + 1) { + // Two stars next to each other, skip the first one. + patIdxStart++; + continue; + } + // Find the pattern between padIdxStart & padIdxTmp in str between + // strIdxStart & strIdxEnd + int patLength = (patIdxTmp - patIdxStart - 1); + int strLength = (strIdxEnd - strIdxStart + 1); + int foundIdx = -1; + strLoop: + for (int i = 0; i <= strLength - patLength; i++) { + for (int j = 0; j < patLength; j++) { + ch = patArr[patIdxStart + j + 1]; + if (ch != '?') { + if (different(caseSensitive, ch, + strArr[strIdxStart + i + j])) { + continue strLoop; + } + } + } + + foundIdx = strIdxStart + i; + break; + } + + if (foundIdx == -1) { + return false; + } + + patIdxStart = patIdxTmp; + strIdxStart = foundIdx + patLength; + } + + // All characters in the string are used. Check if only '*'s are left + // in the pattern. If so, we succeeded. Otherwise failure. + return allStars(patArr, patIdxStart, patIdxEnd); + } + + private static boolean allStars(char[] chars, int start, int end) { + for (int i = start; i <= end; ++i) { + if (chars[i] != '*') { + return false; + } + } + return true; + } + + private static boolean different( + boolean caseSensitive, char ch, char other) { + return caseSensitive + ? ch != other + : Character.toUpperCase(ch) != Character.toUpperCase(other); + } + + /** + * Breaks a path up into a Vector of path elements, tokenizing on + * File.separator. + * + * @param path Path to tokenize. Must not be null. + * + * @return a Vector of path elements from the tokenized path + */ + @SuppressWarnings("rawtypes") + public static Vector tokenizePath (String path) { + return tokenizePath(path, File.separator); + } + + /** + * Breaks a path up into a Vector of path elements, tokenizing on + * + * @param path Path to tokenize. Must not be null. + * @param separator the separator against which to tokenize. + * + * @return a Vector of path elements from the tokenized path + * @since Ant 1.6 + */ + @SuppressWarnings({ + "rawtypes", "unchecked" + }) + public static Vector tokenizePath (String path, String separator) { + Vector ret = new Vector(); + if (FileUtils.isAbsolutePath(path)) { + String[] s = FILE_UTILS.dissect(path); + ret.add(s[0]); + path = s[1]; + } + StringTokenizer st = new StringTokenizer(path, separator); + while (st.hasMoreTokens()) { + ret.addElement(st.nextToken()); + } + return ret; + } + + /** + * Same as {@link #tokenizePath tokenizePath} but hopefully faster. + */ + /*package*/ static String[] tokenizePathAsArray(String path) { + String root = null; + if (FileUtils.isAbsolutePath(path)) { + String[] s = FILE_UTILS.dissect(path); + root = s[0]; + path = s[1]; + } + char sep = File.separatorChar; + int start = 0; + int len = path.length(); + int count = 0; + for (int pos = 0; pos < len; pos++) { + if (path.charAt(pos) == sep) { + if (pos != start) { + count++; + } + start = pos + 1; + } + } + if (len != start) { + count++; + } + String[] l = new String[count + ((root == null) ? 0 : 1)]; + + if (root != null) { + l[0] = root; + count = 1; + } else { + count = 0; + } + start = 0; + for (int pos = 0; pos < len; pos++) { + if (path.charAt(pos) == sep) { + if (pos != start) { + String tok = path.substring(start, pos); + l[count++] = tok; + } + start = pos + 1; + } + } + if (len != start) { + String tok = path.substring(start); + l[count/*++*/] = tok; + } + return l; + } + + /** + * Returns dependency information on these two files. If src has been + * modified later than target, it returns true. If target doesn't exist, + * it likewise returns true. Otherwise, target is newer than src and + * is not out of date, thus the method returns false. It also returns + * false if the src file doesn't even exist, since how could the + * target then be out of date. + * + * @param src the original file + * @param target the file being compared against + * @param granularity the amount in seconds of slack we will give in + * determining out of dateness + * @return whether the target is out of date + */ + public static boolean isOutOfDate(File src, File target, int granularity) { + if (!src.exists()) { + return false; + } + if (!target.exists()) { + return true; + } + if ((src.lastModified() - granularity) > target.lastModified()) { + return true; + } + return false; + } + + /** + * "Flattens" a string by removing all whitespace (space, tab, linefeed, + * carriage return, and formfeed). This uses StringTokenizer and the + * default set of tokens as documented in the single arguement constructor. + * + * @param input a String to remove all whitespace. + * @return a String that has had all whitespace removed. + */ + public static String removeWhitespace(String input) { + StringBuffer result = new StringBuffer(); + if (input != null) { + StringTokenizer st = new StringTokenizer(input); + while (st.hasMoreTokens()) { + result.append(st.nextToken()); + } + } + return result.toString(); + } + + /** + * Tests if a string contains stars or question marks + * @param input a String which one wants to test for containing wildcard + * @return true if the string contains at least a star or a question mark + */ + public static boolean hasWildcards(String input) { + return (input.indexOf('*') != -1 || input.indexOf('?') != -1); + } +} + diff --git a/src/org/apache/tools/ant/util/FileUtils.java b/src/org/apache/tools/ant/util/FileUtils.java new file mode 100644 index 0000000..2774a17 --- /dev/null +++ b/src/org/apache/tools/ant/util/FileUtils.java @@ -0,0 +1,189 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package org.apache.tools.ant.util; + +import org.apache.tools.ant.BuildException; +import org.apache.tools.ant.taskdefs.condition.Os; + +import java.io.File; +import java.util.Random; + +/** + * This class also encapsulates methods which allow Files to be + * referred to using abstract path names which are translated to native + * system file paths at runtime as well as copying files or setting + * their last modification time. + * + */ +public class FileUtils { + private static final int DELETE_RETRY_SLEEP_MILLIS = 10; + private static final int EXPAND_SPACE = 50; + private static final FileUtils PRIMARY_INSTANCE = new FileUtils(); + + //get some non-crypto-grade randomness from various places. + private static Random rand = new Random(System.currentTimeMillis() + + Runtime.getRuntime().freeMemory()); + + private static final boolean ON_NETWARE = Os.isFamily("netware"); + private static final boolean ON_DOS = Os.isFamily("dos"); + private static final boolean ON_WIN9X = Os.isFamily("win9x"); + private static final boolean ON_WINDOWS = Os.isFamily("windows"); + + static final int BUF_SIZE = 8192; + + + /** + * The granularity of timestamps under FAT. + */ + public static final long FAT_FILE_TIMESTAMP_GRANULARITY = 2000; + + /** + * The granularity of timestamps under Unix. + */ + public static final long UNIX_FILE_TIMESTAMP_GRANULARITY = 1000; + + /** + * The granularity of timestamps under the NT File System. + * NTFS has a granularity of 100 nanoseconds, which is less + * than 1 millisecond, so we round this up to 1 millisecond. + */ + public static final long NTFS_FILE_TIMESTAMP_GRANULARITY = 1; + + /** + * A one item cache for fromUri. + * fromUri is called for each element when parseing ant build + * files. It is a costly operation. This just caches the result + * of the last call. + */ + private Object cacheFromUriLock = new Object(); + private String cacheFromUriRequest = null; + private String cacheFromUriResponse = null; + + /** + * Factory method. + * + * @return a new instance of FileUtils. + * @deprecated since 1.7. + * Use getFileUtils instead, + * FileUtils do not have state. + */ + @Deprecated + public static FileUtils newFileUtils() { + return new FileUtils(); + } + + /** + * Method to retrieve The FileUtils, which is shared by all users of this + * method. + * @return an instance of FileUtils. + * @since Ant 1.6.3 + */ + public static FileUtils getFileUtils() { + return PRIMARY_INSTANCE; + } + + /** + * Empty constructor. + */ + protected FileUtils() { + } + + /** + * Verifies that the specified filename represents an absolute path. + * Differs from new java.io.File("filename").isAbsolute() in that a path + * beginning with a double file separator--signifying a Windows UNC--must + * at minimum match "\\a\b" to be considered an absolute path. + * @param filename the filename to be checked. + * @return true if the filename represents an absolute path. + * @throws java.lang.NullPointerException if filename is null. + * @since Ant 1.6.3 + */ + public static boolean isAbsolutePath(String filename) { + int len = filename.length(); + if (len == 0) { + return false; + } + char sep = File.separatorChar; + filename = filename.replace('/', sep).replace('\\', sep); + char c = filename.charAt(0); + if (!(ON_DOS || ON_NETWARE)) { + return (c == sep); + } + if (c == sep) { + // CheckStyle:MagicNumber OFF + if (!(ON_DOS && len > 4 && filename.charAt(1) == sep)) { + return false; + } + // CheckStyle:MagicNumber ON + int nextsep = filename.indexOf(sep, 2); + return nextsep > 2 && nextsep + 1 < len; + } + int colon = filename.indexOf(':'); + return (Character.isLetter(c) && colon == 1 + && filename.length() > 2 && filename.charAt(2) == sep) + || (ON_NETWARE && colon > 0); + } + + /** + * Dissect the specified absolute path. + * @param path the path to dissect. + * @return String[] {root, remaining path}. + * @throws java.lang.NullPointerException if path is null. + * @since Ant 1.7 + */ + public String[] dissect(String path) { + char sep = File.separatorChar; + path = path.replace('/', sep).replace('\\', sep); + + // make sure we are dealing with an absolute path + if (!isAbsolutePath(path)) { + throw new BuildException(path + " is not an absolute path"); + } + String root = null; + int colon = path.indexOf(':'); + if (colon > 0 && (ON_DOS || ON_NETWARE)) { + + int next = colon + 1; + root = path.substring(0, next); + char[] ca = path.toCharArray(); + root += sep; + //remove the initial separator; the root has it. + next = (ca[next] == sep) ? next + 1 : next; + + StringBuffer sbPath = new StringBuffer(); + // Eliminate consecutive slashes after the drive spec: + for (int i = next; i < ca.length; i++) { + if (ca[i] != sep || ca[i - 1] != sep) { + sbPath.append(ca[i]); + } + } + path = sbPath.toString(); + } else if (path.length() > 1 && path.charAt(1) == sep) { + // UNC drive + int nextsep = path.indexOf(sep, 2); + nextsep = path.indexOf(sep, nextsep + 1); + root = (nextsep > 2) ? path.substring(0, nextsep + 1) : path; + path = path.substring(root.length()); + } else { + root = File.separator; + path = path.substring(1); + } + return new String[] {root, path}; + } + +} -- cgit v1.2.3