Return-Path: X-Original-To: apmail-flex-commits-archive@www.apache.org Delivered-To: apmail-flex-commits-archive@www.apache.org Received: from mail.apache.org (hermes.apache.org [140.211.11.3]) by minotaur.apache.org (Postfix) with SMTP id CE9B9187C5 for ; Mon, 15 Jun 2015 06:30:35 +0000 (UTC) Received: (qmail 39489 invoked by uid 500); 15 Jun 2015 06:30:35 -0000 Delivered-To: apmail-flex-commits-archive@flex.apache.org Received: (qmail 39332 invoked by uid 500); 15 Jun 2015 06:30:35 -0000 Mailing-List: contact commits-help@flex.apache.org; run by ezmlm Precedence: bulk List-Help: List-Unsubscribe: List-Post: List-Id: Reply-To: dev@flex.apache.org Delivered-To: mailing list commits@flex.apache.org Received: (qmail 39299 invoked by uid 99); 15 Jun 2015 06:30:35 -0000 Received: from git1-us-west.apache.org (HELO git1-us-west.apache.org) (140.211.11.23) by apache.org (qpsmtpd/0.29) with ESMTP; Mon, 15 Jun 2015 06:30:35 +0000 Received: by git1-us-west.apache.org (ASF Mail Server at git1-us-west.apache.org, from userid 33) id 72866DFF90; Mon, 15 Jun 2015 06:30:35 +0000 (UTC) Content-Type: text/plain; charset="us-ascii" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit From: aharui@apache.org To: commits@flex.apache.org Date: Mon, 15 Jun 2015 06:30:37 -0000 Message-Id: In-Reply-To: <61f970aa8a47448a9b2548b1b297fecb@git.apache.org> References: <61f970aa8a47448a9b2548b1b297fecb@git.apache.org> X-Mailer: ASF-Git Admin Mailer Subject: [03/48] git commit: [flex-utilities] [refs/heads/develop] - move ant_on_air into flex-installer http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/f954e6f6/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/FileUtils.as ---------------------------------------------------------------------- diff --git a/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/FileUtils.as b/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/FileUtils.as new file mode 100644 index 0000000..ea3166b --- /dev/null +++ b/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/FileUtils.as @@ -0,0 +1,657 @@ +/* +* 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.flex.ant.tags.filesetClasses +{ + import flash.filesystem.File; + import flash.system.Capabilities; + + import org.apache.flex.ant.Ant; + import org.apache.flex.ant.tags.filesetClasses.exceptions.BuildException; + import org.apache.flex.ant.tags.filesetClasses.exceptions.IOException; + + /** + * Ported from org.apache.tools.ant.util on 12/3/13. File copying + * was removed. So was tempfile, newfile, readfully and contentEquals. + * + * 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 const PRIMARY_INSTANCE:FileUtils = new FileUtils(); + + private static const ON_WINDOWS:Boolean = Capabilities.os.indexOf("Win") != -1; + private static const ON_DOS:Boolean = Capabilities.os.indexOf("Win") != -1; + + /** + * The granularity of timestamps under Unix. + */ + public static const UNIX_FILE_TIMESTAMP_GRANULARITY:int = 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 const NTFS_FILE_TIMESTAMP_GRANULARITY:int = 1; + + /** + * 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 function getFileUtils():FileUtils { + return PRIMARY_INSTANCE; + } + + /** + * Empty constructor. + */ + public function FileUtils() { + } + + /** + * Get the URL for a file taking into account # characters. + * + * @param file the file whose URL representation is required. + * @return The FileURL value. + * @throws MalformedURLException if the URL representation cannot be + * formed. + public URL getFileURL(File file) throws MalformedURLException { + return new URL(file.toURI().toASCIIString()); + } + */ + + /** + * Calls File.setLastModified(long time). Originally written to + * to dynamically bind to that call on Java1.2+. + * + * @param file the file whose modified time is to be set + * @param time the time to which the last modified time is to be set. + * if this is -1, the current time is used. + */ + public function setFileLastModified(file:File, time:Number):void { + throw new Error("not supported in AIR"); + } + + /** + * Interpret the filename as a file relative to the given file + * unless the filename already represents an absolute filename. + * Differs from new File(file, filename) in that + * the resulting File's path will always be a normalized, + * absolute pathname. Also, if it is determined that + * filename is context-relative, file + * will be discarded and the reference will be resolved using + * available context/state information about the filesystem. + * + * @param file the "reference" file for relative paths. This + * instance must be an absolute file and must not contain + * "./" or "../" sequences (same for \ instead + * of /). If it is null, this call is equivalent to + * new java.io.File(filename).getAbsoluteFile(). + * + * @param filename a file name. + * + * @return an absolute file. + * @throws java.lang.NullPointerException if filename is null. + */ + public function resolveFile(file:File, filename:String):File { + if (!isAbsolutePath(filename)) { + var sep:String = File.separator; + filename = filename.replace(/\//g, sep).replace(/\\/g, sep); + if (isContextRelativePath(filename)) { + file = null; + // on cygwin, our current directory can be a UNC; + // assume user.dir is absolute or all hell breaks loose... + var udir:String = File.userDirectory.nativePath; + if (filename.charAt(0) == sep && udir.charAt(0) == sep) { + filename = dissect(udir)[0] + filename.substring(1); + } + } + filename = new File(file.nativePath + File.separator + filename).nativePath; + } + return normalize(filename); + } + + /** + * On DOS and NetWare, the evaluation of certain file + * specifications is context-dependent. These are filenames + * beginning with a single separator (relative to current root directory) + * and filenames with a drive specification and no intervening separator + * (relative to current directory of the specified root). + * @param filename the filename to evaluate. + * @return true if the filename is relative to system context. + * @throws java.lang.NullPointerException if filename is null. + * @since Ant 1.7 + */ + public static function isContextRelativePath(filename:String):Boolean { + if (!ON_DOS || filename.length == 0) { + return false; + } + var sep:String = File.separator; + filename = filename.replace(/\//g, sep).replace(/\\/g, sep); + var c:String = filename.charAt(0); + var len:int = filename.length; + return (c == sep && (len == 1 || filename.charAt(1) != sep)) + || (Character.isLetter(c) && len > 1 + && filename.indexOf(':') == 1 + && (len == 2 || filename.charAt(2) != sep)); + } + + + /** + * 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 function isAbsolutePath(filename:String):Boolean { + var len:int = filename.length; + if (len == 0) { + return false; + } + var sep:String = File.separator; + filename = filename.replace(/\//g, sep).replace(/\\/g, sep); + var c:String = filename.charAt(0); + if (!ON_DOS) + return (c == sep); + + if (c == sep) { + // CheckStyle:MagicNumber OFF + if (!(ON_DOS && len > 4 && filename.charAt(1) == sep)) { + return false; + } + // CheckStyle:MagicNumber ON + var nextsep:int = filename.indexOf(sep, 2); + return nextsep > 2 && nextsep + 1 < len; + } + var colon:int = filename.indexOf(':'); + return (Character.isLetter(c) && colon == 1 + && filename.length > 2 && filename.charAt(2) == sep); + } + + /** + * Translate a path into its native (platform specific) format. + *

+ * This method uses PathTokenizer to separate the input path + * into its components. This handles DOS style paths in a relatively + * sensible way. The file separators are then converted to their platform + * specific versions. + * + * @param toProcess The path to be translated. + * May be null. + * + * @return the native version of the specified path or + * an empty string if the path is null or empty. + * + * @since ant 1.7 + * @see PathTokenizer + */ + public static function translatePath(toProcess:String):String { + if (toProcess == null || toProcess.length == 0) { + return ""; + } + var path:String = ""; + var tokenizer:PathTokenizer = new PathTokenizer(toProcess); + while (tokenizer.hasMoreTokens()) { + var pathComponent:String = tokenizer.nextToken(); + pathComponent = pathComponent.replace(/\//g, File.separator); + pathComponent = pathComponent.replace(/\\/g, File.separator); + if (path.length != 0) { + path += File.separator; + } + path += pathComponent; + } + return path; + } + + /** + * "Normalize" the given absolute path. + * + *

This includes: + *

    + *
  • Uppercase the drive letter if there is one.
  • + *
  • Remove redundant slashes after the drive spec.
  • + *
  • Resolve all ./, .\, ../ and ..\ sequences.
  • + *
  • DOS style paths that start with a drive letter will have + * \ as the separator.
  • + *
+ * Unlike {@link File#getCanonicalPath()} this method + * specifically does not resolve symbolic links. + * + * @param path the path to be normalized. + * @return the normalized version of the path. + * + * @throws java.lang.NullPointerException if path is null. + */ + public function normalize(path:String):File { + var s:Array = new Array(); + var dissect:Vector. = dissect(path); + s.push(dissect[0]); + + var tok:StringTokenizer = new StringTokenizer(dissect[1], File.separator); + while (tok.hasMoreTokens()) { + var thisToken:String = tok.nextToken(); + if ("." == thisToken) { + continue; + } + if (".." == thisToken) { + if (s.length < 2) { + // Cannot resolve it, so skip it. + return new File(path); + } + s.pop(); + } else { // plain component + s.push(thisToken); + } + } + var sb:String = ""; + var size:int = s.length; + for (var i:int = 0; i < size; i++) { + if (i > 1) { + // not before the filesystem root and not after it, since root + // already contains one + sb += File.separator; + } + sb += s[i]; + } + return new File(sb); + } + + /** + * 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 function dissect(path:String):Vector. { + var sep:String = File.separator; + path = path.replace(/\//g, sep).replace(/\\/g, sep); + + // make sure we are dealing with an absolute path + if (!isAbsolutePath(path)) { + throw new BuildException(path + " is not an absolute path"); + } + var root:String = null; + var colon:int = path.indexOf(':'); + if (colon > 0 && ON_DOS) { + + var next:int = colon + 1; + root = path.substring(0, next); + var ca:Vector. = Vector.(path.split("")); + root += sep; + //remove the initial separator; the root has it. + next = (ca[next] == sep) ? next + 1 : next; + + var sbPath:String = ""; + // Eliminate consecutive slashes after the drive spec: + for (var i:int = next; i < ca.length; i++) { + if (ca[i] != sep || ca[i - 1] != sep) { + sbPath += ca[i]; + } + } + path = sbPath; + } else if (path.length > 1 && path.charAt(1) == sep) { + // UNC drive + var nextsep:int = 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 Vector.([root, path]); + } + + + /** + * This was originally an emulation of {@link File#getParentFile} for JDK 1.1, but it is now + * implemented using that method (Ant 1.6.3 onwards). + * + * @param f the file whose parent is required. + * @return the given file's parent, or null if the file does not have a parent. + * @since 1.10 + * @deprecated since 1.7. Just use {@link File#getParentFile} directly. + */ + public function getParentFile(f:File):File { + return (f == null) ? null : f.parent; + } + + /** + * Checks whether a given file is a symbolic link. + * + *

It doesn't really test for symbolic links but whether the + * canonical and absolute paths of the file are identical--this + * may lead to false positives on some platforms.

+ * + * @param parent the parent directory of the file to test + * @param name the name of the file to test. + * + * @return true if the file is a symbolic link. + * @throws IOException on error. + * @since Ant 1.5 + * @deprecated use SymbolicLinkUtils instead + */ + public function isSymbolicLink(parent:File, name:String):Boolean + { + if (parent == null) { + return new File(name).isSymbolicLink; + } + return new File(parent.nativePath + File.separator + name).isSymbolicLink; + } + + /** + * Removes a leading path from a second path. + * + * @param leading The leading path, must not be null, must be absolute. + * @param path The path to remove from, must not be null, must be absolute. + * + * @return path's normalized absolute if it doesn't start with + * leading; path's path with leading's path removed otherwise. + * + * @since Ant 1.5 + */ + public function removeLeadingPath(leading:File, path:File):String { + var l:String = normalize(leading.nativePath).nativePath; + var p:String = normalize(path.nativePath).nativePath; + if (l == p) { + return ""; + } + // ensure that l ends with a / + // so we never think /foo was a parent directory of /foobar + if (l.charAt(l.length - 1) != File.separator) { + l += File.separator; + } + return (p.indexOf(l) == 0) ? p.substring(l.length) : p; + } + + /** + * Learn whether one path "leads" another. + * @param leading The leading path, must not be null, must be absolute. + * @param path The path to remove from, must not be null, must be absolute. + * @return true if path starts with leading; false otherwise. + * @since Ant 1.7 + */ + public function isLeadingPath(leading:File, path:File):Boolean { + var l:String = normalize(leading.nativePath).nativePath; + var p:String = normalize(path.nativePath).nativePath; + if (l == p) { + return true; + } + // ensure that l ends with a / + // so we never think /foo was a parent directory of /foobar + if (l.charAt(l.length - 1) != File.separator) { + l += File.separator; + } + return p.indexOf(l) == 0; + } + + /** + * Compares two filenames. + * + *

Unlike java.io.File#equals this method will try to compare + * the absolute paths and "normalize" the filenames + * before comparing them.

+ * + * @param f1 the file whose name is to be compared. + * @param f2 the other file whose name is to be compared. + * + * @return true if the file are for the same file. + * + * @since Ant 1.5.3 + */ + public function fileNameEquals(f1:File, f2:File):Boolean { + return normalize(f1.nativePath).nativePath == + normalize(f2.nativePath).nativePath; + } + + /** + * Are the two File instances pointing to the same object on the + * file system? + * @since Ant 1.8.2 + */ + public function areSame(f1:File, f2:File):Boolean { + if (f1 == null && f2 == null) { + return true; + } + if (f1 == null || f2 == null) { + return false; + } + var f1Normalized:File = normalize(f1.nativePath); + var f2Normalized:File = normalize(f2.nativePath); + if (f1Normalized.nativePath == f2Normalized.nativePath) + return true; + f1Normalized.canonicalize(); + f2Normalized.canonicalize(); + return f1Normalized.nativePath == f2Normalized.nativePath; + } + + /** + * Renames a file, even if that involves crossing file system boundaries. + * + *

This will remove to (if it exists), ensure that + * to's parent directory exists and move + * from, which involves deleting from as + * well.

+ * + * @param from the file to move. + * @param to the new file name. + * + * @throws IOException if anything bad happens during this + * process. Note that to may have been deleted + * already when this happens. + * + * @since Ant 1.6 + */ + public function rename(from:File, to:File):void { + // identical logic lives in Move.renameFile(): + from.canonicalize() + from = normalize(from.nativePath); + to = normalize(to.nativePath); + if (!from.exists) { + Ant.currentAnt.output("Cannot rename nonexistent file " + from); + return; + } + if (from.nativePath == to.nativePath) { + Ant.currentAnt.output("Rename of " + from + " to " + to + " is a no-op."); + return; + } + if (to.exists) + to.deleteFile(); + if (to.exists && !(areSame(from, to))) { + throw new IOException("Failed to delete " + to + " while trying to rename " + from); + } + var parent:File = to.parent; + if (parent != null && !parent.exists && !parent.createDirectory()) { + throw new IOException("Failed to create directory " + parent + + " while trying to rename " + from); + } + if (!from.moveTo(to)) { + from.copyTo(to); + from.deleteFile(); + if (from.exists) { + throw new IOException("Failed to delete " + from + " while trying to rename it."); + } + } + } + + /** + * Get the granularity of file timestamps. The choice is made based on OS, which is + * incorrect--it should really be by filesystem. We do not have an easy way to probe for file + * systems, however, so this heuristic gives us a decent default. + * + * @return the difference, in milliseconds, which two file timestamps must have in order for the + * two files to be considered to have different timestamps. + */ + public function getFileTimestampGranularity():Number { + if (ON_WINDOWS) { + return NTFS_FILE_TIMESTAMP_GRANULARITY; + } + return UNIX_FILE_TIMESTAMP_GRANULARITY; + } + + + /** + * Returns true if the source is older than the dest. + * If the dest file does not exist, then the test returns false; it is + * implicitly not up do date. + * @param source source file (should be the older). + * @param dest dest file (should be the newer). + * @param granularity an offset added to the source time. + * @return true if the source is older than the dest after accounting + * for granularity. + * @since Ant 1.6.3 + */ + public function isUpToDate(source:File, dest:File, granularity:Object = null):Boolean { + if (granularity == null) + granularity = getFileTimestampGranularity(); + //do a check for the destination file existing + if (!dest.exists) { + //if it does not, then the file is not up to date. + return false; + } + var sourceTime:Date = source.modificationDate; + var destTime:Date = dest.modificationDate; + return isUpToDateDate(sourceTime.time, destTime.time, granularity as Number); + } + + /** + * Compare two timestamps for being up to date using + * the specified granularity. + * + * @param sourceTime timestamp of source file. + * @param destTime timestamp of dest file. + * @param granularity os/filesys granularity. + * @return true if the dest file is considered up to date. + */ + public function isUpToDateDate(sourceTime:Number, destTime:Number, granularity:Number = NaN):Boolean { + if (isNaN(granularity)) + granularity = getFileTimestampGranularity(); + return destTime != -1 && destTime >= sourceTime + granularity; + } + + + /** + * Calculates the relative path between two files. + *

+ * Implementation note:
This function may throw an IOException if an I/O error occurs + * because its use of the canonical pathname may require filesystem queries. + *

+ * + * @param fromFile the File to calculate the path from + * @param toFile the File to calculate the path to + * @return the relative path between the files + * @throws Exception for undocumented reasons + * @see File#getCanonicalPath() + * + * @since Ant 1.7 + */ + public static function getRelativePath(fromFile:File, toFile:File):String { + fromFile.canonicalize(); + toFile.canonicalize(); + var fromPath:String = fromFile.nativePath; + var toPath:String = toFile.nativePath; + + // build the path stack info to compare + var fromPathStack:Vector. = getPathStack(fromPath); + var toPathStack:Vector. = getPathStack(toPath); + + if (0 < toPathStack.length && 0 < fromPathStack.length) { + if (!fromPathStack[0] == toPathStack[0]) { + // not the same device (would be "" on Linux/Unix) + + return getPath(toPathStack); + } + } else { + // no comparison possible + return getPath(toPathStack); + } + + var minLength:int = Math.min(fromPathStack.length, toPathStack.length); + var same:int = 1; // Used outside the for loop + + // get index of parts which are equal + for (; + same < minLength && fromPathStack[same] == toPathStack[same]; + same++) { + // Do nothing + } + + var relativePathStack:Vector. = new Vector.(); + + // if "from" part is longer, fill it up with ".." + // to reach path which is equal to both paths + for (var i:int = same; i < fromPathStack.length; i++) { + relativePathStack.push(".."); + } + + // fill it up path with parts which were not equal + for (i = same; i < toPathStack.length; i++) { + relativePathStack.push(toPathStack[i]); + } + + return getPath(relativePathStack); + } + + /** + * Gets all names of the path as an array of Strings. + * + * @param path to get names from + * @return Strings, never null + * + * @since Ant 1.7 + */ + public static function getPathStack(path:String):Vector. { + var normalizedPath:String = path.replace(new RegExp(File.separator, "g"), '/'); + + return Vector.(normalizedPath.split("/")); + } + + /** + * Gets path from a List of Strings. + * + * @param pathStack List of Strings to be concated as a path. + * @param separatorChar char to be used as separator between names in path + * @return String, never null + * + * @since Ant 1.7 + */ + public static function getPath(pathStack:Vector., separatorChar:String = "/"):String { + var buffer:String = "" + + if (pathStack.length) { + buffer += pathStack.shift(); + } + while (pathStack.length) { + buffer += separatorChar; + buffer += pathStack.shift(); + } + return buffer; + } + + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/f954e6f6/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/PathTokenizer.as ---------------------------------------------------------------------- diff --git a/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/PathTokenizer.as b/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/PathTokenizer.as new file mode 100644 index 0000000..3e04911 --- /dev/null +++ b/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/PathTokenizer.as @@ -0,0 +1,118 @@ +/* +* 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.flex.ant.tags.filesetClasses +{ + import flash.system.Capabilities; + + import mx.utils.StringUtil; + + /** + * Ported from org.apache.tools.ant.PathTokenizer.java on 12/3/13. + * A Path tokenizer takes a path and returns the components that make up + * that path. + * + * The path can use path separators of either ':' or ';' and file separators + * of either '/' or '\'. + * + */ + public class PathTokenizer { + /** + * A tokenizer to break the string up based on the ':' or ';' separators. + */ + private var tokenizer:StringTokenizer; + + /** + * A String which stores any path components which have been read ahead + * due to DOS filesystem compensation. + */ + private var lookahead:String = null; + + /** + * Flag to indicate whether or not we are running on a platform with a + * DOS style filesystem + */ + private var dosStyleFilesystem:Boolean; + + /** + * Constructs a path tokenizer for the specified path. + * + * @param path The path to tokenize. Must not be null. + */ + public function PathTokenizer(path:String) { + // on Windows and Unix, we can ignore delimiters and still have + // enough information to tokenize correctly. + tokenizer = new StringTokenizer(path, ":;", false); + dosStyleFilesystem = Capabilities.os.indexOf("Win") != -1; + } + + /** + * Tests if there are more path elements available from this tokenizer's + * path. If this method returns true, then a subsequent call + * to nextToken will successfully return a token. + * + * @return true if and only if there is at least one token + * in the string after the current position; false otherwise. + */ + public function hasMoreTokens():Boolean { + if (lookahead != null) { + return true; + } + + return tokenizer.hasMoreTokens(); + } + + /** + * Returns the next path element from this tokenizer. + * + * @return the next path element from this tokenizer. + * + * @exception NoSuchElementException if there are no more elements in this + * tokenizer's path. + */ + public function nextToken():String { + var token:String = null; + if (lookahead != null) { + token = lookahead; + lookahead = null; + } else { + token = StringUtil.trim(tokenizer.nextToken()); + } + + if (token.length == 1 && Character.isLetter(token.charAt(0)) + && dosStyleFilesystem + && tokenizer.hasMoreTokens()) { + // we are on a dos style system so this path could be a drive + // spec. We look at the next token + var nextToken:String = StringUtil.trim(tokenizer.nextToken()); + if (nextToken.indexOf("\\") == 0 || nextToken.indexOf("/") == 0) { + // we know we are on a DOS style platform and the next path + // starts with a slash or backslash, so we know this is a + // drive spec + token += ":" + nextToken; + } else { + // store the token just read for next time + lookahead = nextToken; + } + } + return token; + } + } + + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/f954e6f6/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/Reference.as ---------------------------------------------------------------------- diff --git a/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/Reference.as b/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/Reference.as new file mode 100644 index 0000000..1e9ce09 --- /dev/null +++ b/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/Reference.as @@ -0,0 +1,106 @@ +/* +* 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.flex.ant.tags.filesetClasses +{ + import org.apache.flex.ant.tags.Project; + import org.apache.flex.ant.tags.filesetClasses.exceptions.BuildException; + + /** + * Ported from org.apache.tools.ant.types.Reference.java on 12/3/13. + * Class to hold a reference to another object in the project. + * + */ + public class Reference { + + private var refid:String; + private var project:Project; + + /** + * Create a reference to a named ID in a particular project. + * @param p the project this reference is associated with + * @param id the name of this reference + * @since Ant 1.6.3 + */ + public function Reference(p:Project, id:String) { + setRefId(id); + setProject(p); + } + + /** + * Set the reference id. Should not normally be necessary; + * use {@link Reference#Reference(Project, String)}. + * @param id the reference id to use + */ + public function setRefId(id:String):void { + refid = id; + } + + /** + * Get the reference id of this reference. + * @return the reference id + */ + public function getRefId():String { + return refid; + } + + /** + * Set the associated project. Should not normally be necessary; + * use {@link Reference#Reference(Project,String)}. + * @param p the project to use + * @since Ant 1.6.3 + */ + public function setProject(p:Project):void { + this.project = p; + } + + /** + * Get the associated project, if any; may be null. + * @return the associated project + * @since Ant 1.6.3 + */ + public function getProject():Project { + return project; + } + + /** + * Resolve the reference, using the associated project if + * it set, otherwise use the passed in project. + * @param fallback the fallback project to use if the project attribute of + * reference is not set. + * @return the dereferenced object. + * @throws BuildException if the reference cannot be dereferenced. + */ + public function getReferencedObject(fallback:Project = null):Object { + if (refid == null) { + throw new BuildException("No reference specified"); + } + if (fallback == null) + { + if (project == null) { + throw new BuildException("No project set on reference to " + refid); + } + } + + var o:Object = project == null ? fallback.getReference(refid) : project.getReference(refid); + if (o == null) { + throw new BuildException("Reference " + refid + " not found."); + } + return o; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/f954e6f6/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/Resource.as ---------------------------------------------------------------------- diff --git a/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/Resource.as b/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/Resource.as new file mode 100644 index 0000000..0ea7b85 --- /dev/null +++ b/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/Resource.as @@ -0,0 +1,349 @@ +/* +* 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.flex.ant.tags.filesetClasses +{ + import flash.filesystem.FileStream; + import org.apache.flex.ant.tags.filesetClasses.exceptions.UnsupportedOperationException; + + /** + * Ported from org.apache.tools.ant.types.Resource.java on 12/3/13. + * Describes a "File-like" resource (File, ZipEntry, etc.). + * + * This class is meant to be used by classes needing to record path + * and date/time information about a file, a zip entry or some similar + * resource (URL, archive in a version control repository, ...). + * + * @since Ant 1.5.2 + * @see org.apache.tools.ant.types.resources.Touchable + */ + public class Resource extends DataType + { + + /** Constant unknown size */ + public static const UNKNOWN_SIZE:int = -1; + + /** Constant unknown datetime for getLastModified */ + public static const UNKNOWN_DATETIME:int = 0; + + private var name:String; + private var exists:Boolean = true; + private var lastmodified:Number; + private var directory:Boolean; + private var _size:int; + private var nameSet:Boolean; + private var existsSet:Boolean; + private var lastmodifiedSet:Boolean; + private var directorySet:Boolean; + private var sizeSet:Boolean; + + + /** + * Sets the name, lastmodified flag, exists flag, directory flag, and size. + * + * @param name relative path of the resource. Expects + * "/" to be used as the directory separator. + * @param exists if true the resource exists + * @param lastmodified the last modification time of the resource + * @param directory if true, this resource is a directory + * @param size the size of this resource. + */ + public function Resource(name:String, exists:Boolean = false, lastmodified:Number = NaN, + directory:Boolean = false, size:int = UNKNOWN_SIZE) { + this.name = name; + setName(name); + setExists(exists); + setLastModified(lastmodified); + setDirectory(directory); + setSize(size); + } + + /** + * Name attribute will contain the path of a file relative to the + * root directory of its fileset or the recorded path of a zip + * entry. + * + *

example for a file with fullpath /var/opt/adm/resource.txt + * in a file set with root dir /var/opt it will be + * adm/resource.txt.

+ * + *

"/" will be used as the directory separator.

+ * @return the name of this resource. + */ + public function getName():String + { + return isReference() ? Resource(getCheckedRef()).getName() : name; + } + + /** + * Set the name of this Resource. + * @param name relative path of the resource. Expects + * "/" to be used as the directory separator. + */ + public function setName(name:String):void + { + checkAttributesAllowed(); + this.name = name; + nameSet = true; + } + + /** + * The exists attribute tells whether a resource exists. + * @return true if this resource exists. + */ + public function isExists():Boolean + { + if (isReference()) { + return Resource(getCheckedRef()).isExists(); + } + //default true: + return exists; + } + + /** + * Set the exists attribute. + * @param exists if true, this resource exists. + */ + public function setExists(exists:Boolean):void + { + checkAttributesAllowed(); + this.exists = exists; + existsSet = true; + } + + /** + * Tells the modification time in milliseconds since 01.01.1970 (the "epoch"). + * + * @return the modification time, if that is meaningful + * (e.g. for a file resource which exists); + * 0 if the resource does not exist, to mirror the behavior + * of {@link java.io.File#lastModified}; + * or 0 if the notion of modification time is meaningless for this class + * of resource (e.g. an inline string) + */ + public function getLastModified():Number + { + if (isReference()) { + return Resource(getCheckedRef()).getLastModified(); + } + if (!isExists() || !lastmodifiedSet) { + return UNKNOWN_DATETIME; + } + var result:Number = lastmodified; + return result < UNKNOWN_DATETIME ? UNKNOWN_DATETIME : result; + } + + /** + * Set the last modification attribute. + * @param lastmodified the modification time in milliseconds since 01.01.1970. + */ + public function setLastModified(lastmodified:Number):void + { + checkAttributesAllowed(); + this.lastmodified = lastmodified; + lastmodifiedSet = true; + } + + /** + * Tells if the resource is a directory. + * @return boolean flag indicating if the resource is a directory. + */ + public function isDirectory():Boolean + { + if (isReference()) { + return Resource(getCheckedRef()).isDirectory(); + } + //default false: + return directory; + } + + /** + * Set the directory attribute. + * @param directory if true, this resource is a directory. + */ + public function setDirectory(directory:Boolean):void + { + checkAttributesAllowed(); + this.directory = directory; + directorySet = true; + } + + /** + * Set the size of this Resource. + * @param size the size, as a long. + * @since Ant 1.6.3 + */ + public function setSize(size:int):void + { + checkAttributesAllowed(); + _size = size > UNKNOWN_SIZE ? size : UNKNOWN_SIZE; + sizeSet = true; + } + + /** + * Get the size of this Resource. + * @return the size, as a long, 0 if the Resource does not exist (for + * compatibility with java.io.File), or UNKNOWN_SIZE if not known. + * @since Ant 1.6.3 + */ + public function getSize():int + { + if (isReference()) { + return Resource(getCheckedRef()).getSize(); + } + return isExists() + ? (sizeSet ? _size : UNKNOWN_SIZE) + : 0; + } + + /** + * Delegates to a comparison of names. + * @param other the object to compare to. + * @return a negative integer, zero, or a positive integer as this Resource + * is less than, equal to, or greater than the specified Resource. + * @since Ant 1.6 + */ + public function compareTo(other:Resource):int { + if (isReference()) { + return Resource(getCheckedRef()).compareTo(other); + } + return toString().localeCompare(other.toString()); + } + + /** + * Implement basic Resource equality. + * @param other the object to check against. + * @return true if the specified Object is equal to this Resource. + * @since Ant 1.7 + */ + public function equals(other:Object):Boolean { + if (isReference()) { + return getCheckedRef().equals(other); + } + return compareTo(Resource(other)) == 0; + } + + /** + * Get an InputStream for the Resource. + * @return an InputStream containing this Resource's content. + * @throws IOException if unable to provide the content of this + * Resource as a stream. + * @throws UnsupportedOperationException if InputStreams are not + * supported for this Resource type. + * @since Ant 1.7 + */ + public function getInputStream():FileStream + { + if (isReference()) { + return Resource(getCheckedRef()).getInputStream(); + } + throw new UnsupportedOperationException(); + } + + /** + * Get an OutputStream for the Resource. + * @return an OutputStream to which content can be written. + * @throws IOException if unable to provide the content of this + * Resource as a stream. + * @throws UnsupportedOperationException if OutputStreams are not + * supported for this Resource type. + * @since Ant 1.7 + */ + public function getOutputStream():FileStream + { + if (isReference()) { + return Resource(getCheckedRef()).getOutputStream(); + } + throw new UnsupportedOperationException(); + } + + /** + * Fulfill the ResourceCollection contract. + * @return an Iterator of Resources. + * @since Ant 1.7 + */ + public function iterator():Vector. + { + return isReference() ? Resource(getCheckedRef()).iterator() + : Vector.([this]); + } + + /** + * Fulfill the ResourceCollection contract. + * @return the size of this ResourceCollection. + * @since Ant 1.7 + */ + public function size():int { + return isReference() ? Resource(getCheckedRef()).size() : 1; + } + + /** + * Fulfill the ResourceCollection contract. + * @return whether this Resource is a FileProvider. + * @since Ant 1.7 + */ + public function isFilesystemOnly():Boolean + { + return (isReference() && Resource(getCheckedRef()).isFilesystemOnly()) + || this is FileProvider; + } + + /** + * Get the string representation of this Resource. + * @return this Resource formatted as a String. + * @since Ant 1.7 + */ + override public function toString():String + { + if (isReference()) { + return getCheckedRef().toString(); + } + var n:String = getName(); + return n == null ? "(anonymous)" : n; + } + + /** + * Get a long String representation of this Resource. + * This typically should be the value of toString() + * prefixed by a type description. + * @return this Resource formatted as a long String. + * @since Ant 1.7 + */ + public function toLongString():String + { + return isReference() ? Resource(getCheckedRef()).toLongString() + : getDataTypeName() + " \"" + toString() + '"'; + } + + /** + * Overrides the base version. + * @param r the Reference to set. + */ + override public function setRefid(r:Reference):void + { + if (nameSet + || existsSet + || lastmodifiedSet + || directorySet + || sizeSet) { + throw tooManyAttributes(); + } + super.setRefid(r); + } + + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/f954e6f6/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/SelectorUtils.as ---------------------------------------------------------------------- diff --git a/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/SelectorUtils.as b/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/SelectorUtils.as new file mode 100644 index 0000000..de28992 --- /dev/null +++ b/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/SelectorUtils.as @@ -0,0 +1,638 @@ +/* +* 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.flex.ant.tags.filesetClasses +{ + import flash.filesystem.File; + + /** + * Ported from org.apache.tools.ant.types.selectors.SelectorUtils.java on 12/03/13. + *

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 const DEEP_TREE_MATCH:String = "**"; + + private static const instance:SelectorUtils = new SelectorUtils(); + private static const FILE_UTILS:FileUtils = FileUtils.getFileUtils(); + + /** + * Private Constructor + */ + public function SelectorUtils() { + } + + /** + * Retrieves the instance of the Singleton. + * @return singleton instance + */ + public static function getInstance():SelectorUtils { + 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. + * @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 function matchPatternStart(pattern:String, str:String, + isCaseSensitive:Boolean = true):Boolean + { + // 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.indexOf(File.separator) == 0 + != pattern.indexOf(File.separator) == 0) { + return false; + } + + var patDirs:Vector. = tokenizePathAsArray(pattern); + var strDirs:Vector. = tokenizePathAsArray(str); + return matchPatternStartVectors(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 "**". + */ + public static function matchPatternStartVectors(patDirs:Vector., + strDirs:Vector., + isCaseSensitive:Boolean):Boolean { + var patIdxStart:int = 0; + var patIdxEnd:int = patDirs.length - 1; + var strIdxStart:int = 0; + var strIdxEnd:int = strDirs.length - 1; + + // up to first '**' + while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { + var patDir:String = patDirs[patIdxStart]; + if (patDir == 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 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 function matchPath(pattern:String, str:String, + isCaseSensitive:Boolean = true):Boolean + { + var patDirs:Vector. = tokenizePathAsArray(pattern); + return matchPathVectors(patDirs, tokenizePathAsArray(str), isCaseSensitive); + } + + /** + * Core implementation of matchPath. It is isolated so that it + * can be called from TokenizedPattern. + */ + public static function matchPathVectors(tokenizedPattern:Vector., + strDirs:Vector., + isCaseSensitive:Boolean):Boolean + { + var patIdxStart:int = 0; + var patIdxEnd:int = tokenizedPattern.length - 1; + var strIdxStart:int = 0; + var strIdxEnd:int = strDirs.length - 1; + + // up to first '**' + while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) { + var patDir:String = tokenizedPattern[patIdxStart]; + if (patDir == DEEP_TREE_MATCH) { + break; + } + if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) { + return false; + } + patIdxStart++; + strIdxStart++; + } + if (strIdxStart > strIdxEnd) { + // String is exhausted + for (i = patIdxStart; i <= patIdxEnd; i++) { + if (tokenizedPattern[i] != 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) { + patDir = tokenizedPattern[patIdxEnd]; + if (patDir == DEEP_TREE_MATCH) { + break; + } + if (!match(patDir, strDirs[strIdxEnd], isCaseSensitive)) { + return false; + } + patIdxEnd--; + strIdxEnd--; + } + if (strIdxStart > strIdxEnd) { + // String is exhausted + for (i = patIdxStart; i <= patIdxEnd; i++) { + if (!tokenizedPattern[i] == DEEP_TREE_MATCH) { + return false; + } + } + return true; + } + + while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) { + var patIdxTmp:int = -1; + for (i = patIdxStart + 1; i <= patIdxEnd; i++) { + if (tokenizedPattern[i] == 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 + var patLength:int = (patIdxTmp - patIdxStart - 1); + var strLength:int = (strIdxEnd - strIdxStart + 1); + var foundIdx:int = -1; + strLoop: + for (i = 0; i <= strLength - patLength; i++) { + for (var j:int = 0; j < patLength; j++) { + var subPat:String = tokenizedPattern[patIdxStart + j + 1]; + var subStr:String = 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 (var i:int = patIdxStart; i <= patIdxEnd; i++) { + if (!tokenizedPattern[i] == 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. + * @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 function match(pattern:String, str:String, + caseSensitive:Boolean = true):Boolean + { + var patArr:Vector. = Vector.(pattern.split("")); + var strArr:Vector. = Vector.(str.split("")); + var patIdxStart:int = 0; + var patIdxEnd:int = patArr.length - 1; + var strIdxStart:int = 0; + var strIdxEnd:int = strArr.length - 1; + var ch:String; + + var containsStar:Boolean = false; + for (var i:int = 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 (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) { + var patIdxTmp:int = -1; + for (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 + var patLength:int = (patIdxTmp - patIdxStart - 1); + var strLength:int = (strIdxEnd - strIdxStart + 1); + var foundIdx:int = -1; + strLoop: + for (i = 0; i <= strLength - patLength; i++) { + for (var j:int = 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 function allStars(chars:Vector., start:int, end:int):Boolean + { + for (var i:int = start; i <= end; ++i) { + if (chars[i] != '*') { + return false; + } + } + return true; + } + + private static function different( + caseSensitive:Boolean, ch:String, other:String):Boolean + { + return caseSensitive + ? ch != other + : ch.toUpperCase() != other.toUpperCase(); + } + + /** + * 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 + */ + public static function tokenizePath(path:String, + separator:String = null):Vector. + { + if (separator == null) + separator = File.separator; + + var ret:Vector. = new Vector.(); + if (FileUtils.isAbsolutePath(path)) { + var s:Vector. = FILE_UTILS.dissect(path); + ret.push(s[0]); + path = s[1]; + } + var st:StringTokenizer = new StringTokenizer(path, separator); + while (st.hasMoreTokens()) { + ret.push(st.nextToken()); + } + return ret; + } + + /** + * Same as {@link #tokenizePath tokenizePath} but hopefully faster. + */ + /*package*/public static function tokenizePathAsArray(path:String):Vector. + { + var root:String = null; + if (FileUtils.isAbsolutePath(path)) { + var s:Vector. = FILE_UTILS.dissect(path); + root = s[0]; + path = s[1]; + } + var sep:String = File.separator; + var start:int = 0; + var len:int = path.length; + var count:int = 0; + for (var pos:int = 0; pos < len; pos++) { + if (path.charAt(pos) == sep) { + if (pos != start) { + count++; + } + start = pos + 1; + } + } + if (len != start) { + count++; + } + var l:Vector. = Vector.(new Array(count + ((root == null) ? 0 : 1))); + + if (root != null) { + l[0] = root; + count = 1; + } else { + count = 0; + } + start = 0; + for (pos = 0; pos < len; pos++) { + if (path.charAt(pos) == sep) { + if (pos != start) { + var tok:String = path.substring(start, pos); + l[count++] = tok; + } + start = pos + 1; + } + } + if (len != start) { + 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 + */ + private static function isOutOfDateFile(src:File, target:File, granularity:int):Boolean { + if (!src.exists) { + return false; + } + if (!target.exists) { + return true; + } + if ((src.modificationDate.time - granularity) > target.modificationDate.time) { + return true; + } + return false; + } + + /** + * Returns dependency information on these two resources. 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 resource + * @param target the resource being compared against + * @param granularity the int amount in seconds of slack we will give in + * determining out of dateness + * @return whether the target is out of date + */ + public static function isOutOfDate(src:Object, target:Object, + granularity:int):Boolean { + if (src is File) + return isOutOfDateFile(src as File, target as File, granularity); + return isOutOfDateResource(src as Resource, target as Resource, granularity); + } + + /** + * Returns dependency information on these two resources. 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 resource + * @param target the resource being compared against + * @param granularity the long amount in seconds of slack we will give in + * determining out of dateness + * @return whether the target is out of date + */ + private static function isOutOfDateResource(src:Resource, target:Resource, + granularity:int):Boolean { + var sourceLastModified:Number = src.getLastModified(); + var targetLastModified:Number = target.getLastModified(); + return src.isExists() + && (sourceLastModified == Resource.UNKNOWN_DATETIME + || targetLastModified == Resource.UNKNOWN_DATETIME + || (sourceLastModified - granularity) > targetLastModified); + } + + /** + * "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 argument constructor. + * + * @param input a String to remove all whitespace. + * @return a String that has had all whitespace removed. + */ + public static function removeWhitespace(input:String):String { + var result:String = ""; + if (input != null) { + var st:StringTokenizer = new StringTokenizer(input); + while (st.hasMoreTokens()) { + result += st.nextToken(); + } + } + return result; + } + + /** + * 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 function hasWildcards(input:String):Boolean { + return (input.indexOf('*') != -1 || input.indexOf('?') != -1); + } + + /** + * removes from a pattern all tokens to the right containing wildcards + * @param input the input string + * @return the leftmost part of the pattern without wildcards + */ + public static function rtrimWildcardTokens(input:String):String { + return new TokenizedPattern(input).rtrimWildcardTokens().toString(); + } + } +} http://git-wip-us.apache.org/repos/asf/flex-utilities/blob/f954e6f6/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/StringTokenizer.as ---------------------------------------------------------------------- diff --git a/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/StringTokenizer.as b/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/StringTokenizer.as new file mode 100644 index 0000000..02ddff5 --- /dev/null +++ b/flex-installer/ant_on_air/src/org/apache/flex/ant/tags/filesetClasses/StringTokenizer.as @@ -0,0 +1,74 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// 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.flex.ant.tags.filesetClasses +{ + // an approximation of the Java StringTokenizer + public class StringTokenizer + { + public function StringTokenizer(s:String, delims:String = "\t\r\n\f", returnDelims:Boolean = false) + { + tokens = new Vector.(); + var c1:int = 0; + var c2:int = 0; + var n:int = s.length; + while (c2 < n) + { + var c:String = s.charAt(c2); + if (delims.indexOf(c) != -1) + { + tokens.push(s.substring(c1, c2)); + c1 = c2; + while (c2 < n) + { + c = s.charAt(c2); + if (delims.indexOf(c) == -1) + { + if (returnDelims) + tokens.push(s.substring(c1, c2)) + c1 = c2; + break; + } + c2++; + } + if (returnDelims && c1 < c2) + { + tokens.push(s.substring(c1, c2)); + c1 = c2; + } + } + c2++; + } + if (c1 < n) + tokens.push(s.substring(c1)) + } + + private var tokens:Vector.; + private var index:int = 0; + + public function hasMoreTokens():Boolean + { + return index < tokens.length; + } + + public function nextToken():String + { + return tokens[index++]; + } + } +} \ No newline at end of file