[14982] | 1 | /*
|
---|
| 2 | * Copyright 2002-2004 The Apache Software Foundation
|
---|
| 3 | *
|
---|
| 4 | * Licensed under the Apache License, Version 2.0 (the "License");
|
---|
| 5 | * you may not use this file except in compliance with the License.
|
---|
| 6 | * You may obtain a copy of the License at
|
---|
| 7 | *
|
---|
| 8 | * http://www.apache.org/licenses/LICENSE-2.0
|
---|
| 9 | *
|
---|
| 10 | * Unless required by applicable law or agreed to in writing, software
|
---|
| 11 | * distributed under the License is distributed on an "AS IS" BASIS,
|
---|
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
---|
| 13 | * See the License for the specific language governing permissions and
|
---|
| 14 | * limitations under the License.
|
---|
| 15 | *
|
---|
| 16 | */
|
---|
| 17 |
|
---|
| 18 | package org.apache.tools.ant.types.selectors;
|
---|
| 19 |
|
---|
| 20 | import java.io.File;
|
---|
| 21 | import java.util.StringTokenizer;
|
---|
| 22 | import java.util.Vector;
|
---|
| 23 |
|
---|
| 24 | import org.apache.tools.ant.types.Resource;
|
---|
| 25 |
|
---|
| 26 | /**
|
---|
| 27 | * <p>This is a utility class used by selectors and DirectoryScanner. The
|
---|
| 28 | * functionality more properly belongs just to selectors, but unfortunately
|
---|
| 29 | * DirectoryScanner exposed these as protected methods. Thus we have to
|
---|
| 30 | * support any subclasses of DirectoryScanner that may access these methods.
|
---|
| 31 | * </p>
|
---|
| 32 | * <p>This is a Singleton.</p>
|
---|
| 33 | *
|
---|
| 34 | * @since 1.5
|
---|
| 35 | */
|
---|
| 36 | public final class SelectorUtils {
|
---|
| 37 |
|
---|
| 38 | private static SelectorUtils instance = new SelectorUtils();
|
---|
| 39 |
|
---|
| 40 | /**
|
---|
| 41 | * Private Constructor
|
---|
| 42 | */
|
---|
| 43 | private SelectorUtils() {
|
---|
| 44 | }
|
---|
| 45 |
|
---|
| 46 | /**
|
---|
| 47 | * Retrieves the instance of the Singleton.
|
---|
| 48 | * @return singleton instance
|
---|
| 49 | */
|
---|
| 50 | public static SelectorUtils getInstance() {
|
---|
| 51 | return instance;
|
---|
| 52 | }
|
---|
| 53 |
|
---|
| 54 | /**
|
---|
| 55 | * Tests whether or not a given path matches the start of a given
|
---|
| 56 | * pattern up to the first "**".
|
---|
| 57 | * <p>
|
---|
| 58 | * This is not a general purpose test and should only be used if you
|
---|
| 59 | * can live with false positives. For example, <code>pattern=**\a</code>
|
---|
| 60 | * and <code>str=b</code> will yield <code>true</code>.
|
---|
| 61 | *
|
---|
| 62 | * @param pattern The pattern to match against. Must not be
|
---|
| 63 | * <code>null</code>.
|
---|
| 64 | * @param str The path to match, as a String. Must not be
|
---|
| 65 | * <code>null</code>.
|
---|
| 66 | *
|
---|
| 67 | * @return whether or not a given path matches the start of a given
|
---|
| 68 | * pattern up to the first "**".
|
---|
| 69 | */
|
---|
| 70 | public static boolean matchPatternStart(String pattern, String str) {
|
---|
| 71 | return matchPatternStart(pattern, str, true);
|
---|
| 72 | }
|
---|
| 73 |
|
---|
| 74 | /**
|
---|
| 75 | * Tests whether or not a given path matches the start of a given
|
---|
| 76 | * pattern up to the first "**".
|
---|
| 77 | * <p>
|
---|
| 78 | * This is not a general purpose test and should only be used if you
|
---|
| 79 | * can live with false positives. For example, <code>pattern=**\a</code>
|
---|
| 80 | * and <code>str=b</code> will yield <code>true</code>.
|
---|
| 81 | *
|
---|
| 82 | * @param pattern The pattern to match against. Must not be
|
---|
| 83 | * <code>null</code>.
|
---|
| 84 | * @param str The path to match, as a String. Must not be
|
---|
| 85 | * <code>null</code>.
|
---|
| 86 | * @param isCaseSensitive Whether or not matching should be performed
|
---|
| 87 | * case sensitively.
|
---|
| 88 | *
|
---|
| 89 | * @return whether or not a given path matches the start of a given
|
---|
| 90 | * pattern up to the first "**".
|
---|
| 91 | */
|
---|
| 92 | public static boolean matchPatternStart(String pattern, String str,
|
---|
| 93 | boolean isCaseSensitive) {
|
---|
| 94 | // When str starts with a File.separator, pattern has to start with a
|
---|
| 95 | // File.separator.
|
---|
| 96 | // When pattern starts with a File.separator, str has to start with a
|
---|
| 97 | // File.separator.
|
---|
| 98 | if (str.startsWith(File.separator)
|
---|
| 99 | != pattern.startsWith(File.separator)) {
|
---|
| 100 | return false;
|
---|
| 101 | }
|
---|
| 102 |
|
---|
| 103 | String[] patDirs = tokenizePathAsArray(pattern);
|
---|
| 104 | String[] strDirs = tokenizePathAsArray(str);
|
---|
| 105 |
|
---|
| 106 | int patIdxStart = 0;
|
---|
| 107 | int patIdxEnd = patDirs.length - 1;
|
---|
| 108 | int strIdxStart = 0;
|
---|
| 109 | int strIdxEnd = strDirs.length - 1;
|
---|
| 110 |
|
---|
| 111 | // up to first '**'
|
---|
| 112 | while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
|
---|
| 113 | String patDir = patDirs[patIdxStart];
|
---|
| 114 | if (patDir.equals("**")) {
|
---|
| 115 | break;
|
---|
| 116 | }
|
---|
| 117 | if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) {
|
---|
| 118 | return false;
|
---|
| 119 | }
|
---|
| 120 | patIdxStart++;
|
---|
| 121 | strIdxStart++;
|
---|
| 122 | }
|
---|
| 123 |
|
---|
| 124 | if (strIdxStart > strIdxEnd) {
|
---|
| 125 | // String is exhausted
|
---|
| 126 | return true;
|
---|
| 127 | } else if (patIdxStart > patIdxEnd) {
|
---|
| 128 | // String not exhausted, but pattern is. Failure.
|
---|
| 129 | return false;
|
---|
| 130 | } else {
|
---|
| 131 | // pattern now holds ** while string is not exhausted
|
---|
| 132 | // this will generate false positives but we can live with that.
|
---|
| 133 | return true;
|
---|
| 134 | }
|
---|
| 135 | }
|
---|
| 136 |
|
---|
| 137 | /**
|
---|
| 138 | * Tests whether or not a given path matches a given pattern.
|
---|
| 139 | *
|
---|
| 140 | * @param pattern The pattern to match against. Must not be
|
---|
| 141 | * <code>null</code>.
|
---|
| 142 | * @param str The path to match, as a String. Must not be
|
---|
| 143 | * <code>null</code>.
|
---|
| 144 | *
|
---|
| 145 | * @return <code>true</code> if the pattern matches against the string,
|
---|
| 146 | * or <code>false</code> otherwise.
|
---|
| 147 | */
|
---|
| 148 | public static boolean matchPath(String pattern, String str) {
|
---|
| 149 | return matchPath(pattern, str, true);
|
---|
| 150 | }
|
---|
| 151 |
|
---|
| 152 | /**
|
---|
| 153 | * Tests whether or not a given path matches a given pattern.
|
---|
| 154 | *
|
---|
| 155 | * @param pattern The pattern to match against. Must not be
|
---|
| 156 | * <code>null</code>.
|
---|
| 157 | * @param str The path to match, as a String. Must not be
|
---|
| 158 | * <code>null</code>.
|
---|
| 159 | * @param isCaseSensitive Whether or not matching should be performed
|
---|
| 160 | * case sensitively.
|
---|
| 161 | *
|
---|
| 162 | * @return <code>true</code> if the pattern matches against the string,
|
---|
| 163 | * or <code>false</code> otherwise.
|
---|
| 164 | */
|
---|
| 165 | public static boolean matchPath(String pattern, String str,
|
---|
| 166 | boolean isCaseSensitive) {
|
---|
| 167 | // When str starts with a File.separator, pattern has to start with a
|
---|
| 168 | // File.separator.
|
---|
| 169 | // When pattern starts with a File.separator, str has to start with a
|
---|
| 170 | // File.separator.
|
---|
| 171 | if (str.startsWith(File.separator)
|
---|
| 172 | != pattern.startsWith(File.separator)) {
|
---|
| 173 | return false;
|
---|
| 174 | }
|
---|
| 175 |
|
---|
| 176 | String[] patDirs = tokenizePathAsArray(pattern);
|
---|
| 177 | String[] strDirs = tokenizePathAsArray(str);
|
---|
| 178 |
|
---|
| 179 | int patIdxStart = 0;
|
---|
| 180 | int patIdxEnd = patDirs.length - 1;
|
---|
| 181 | int strIdxStart = 0;
|
---|
| 182 | int strIdxEnd = strDirs.length - 1;
|
---|
| 183 |
|
---|
| 184 | // up to first '**'
|
---|
| 185 | while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
|
---|
| 186 | String patDir = patDirs[patIdxStart];
|
---|
| 187 | if (patDir.equals("**")) {
|
---|
| 188 | break;
|
---|
| 189 | }
|
---|
| 190 | if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) {
|
---|
| 191 | patDirs = null;
|
---|
| 192 | strDirs = null;
|
---|
| 193 | return false;
|
---|
| 194 | }
|
---|
| 195 | patIdxStart++;
|
---|
| 196 | strIdxStart++;
|
---|
| 197 | }
|
---|
| 198 | if (strIdxStart > strIdxEnd) {
|
---|
| 199 | // String is exhausted
|
---|
| 200 | for (int i = patIdxStart; i <= patIdxEnd; i++) {
|
---|
| 201 | if (!patDirs[i].equals("**")) {
|
---|
| 202 | patDirs = null;
|
---|
| 203 | strDirs = null;
|
---|
| 204 | return false;
|
---|
| 205 | }
|
---|
| 206 | }
|
---|
| 207 | return true;
|
---|
| 208 | } else {
|
---|
| 209 | if (patIdxStart > patIdxEnd) {
|
---|
| 210 | // String not exhausted, but pattern is. Failure.
|
---|
| 211 | patDirs = null;
|
---|
| 212 | strDirs = null;
|
---|
| 213 | return false;
|
---|
| 214 | }
|
---|
| 215 | }
|
---|
| 216 |
|
---|
| 217 | // up to last '**'
|
---|
| 218 | while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
|
---|
| 219 | String patDir = patDirs[patIdxEnd];
|
---|
| 220 | if (patDir.equals("**")) {
|
---|
| 221 | break;
|
---|
| 222 | }
|
---|
| 223 | if (!match(patDir, strDirs[strIdxEnd], isCaseSensitive)) {
|
---|
| 224 | patDirs = null;
|
---|
| 225 | strDirs = null;
|
---|
| 226 | return false;
|
---|
| 227 | }
|
---|
| 228 | patIdxEnd--;
|
---|
| 229 | strIdxEnd--;
|
---|
| 230 | }
|
---|
| 231 | if (strIdxStart > strIdxEnd) {
|
---|
| 232 | // String is exhausted
|
---|
| 233 | for (int i = patIdxStart; i <= patIdxEnd; i++) {
|
---|
| 234 | if (!patDirs[i].equals("**")) {
|
---|
| 235 | patDirs = null;
|
---|
| 236 | strDirs = null;
|
---|
| 237 | return false;
|
---|
| 238 | }
|
---|
| 239 | }
|
---|
| 240 | return true;
|
---|
| 241 | }
|
---|
| 242 |
|
---|
| 243 | while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
|
---|
| 244 | int patIdxTmp = -1;
|
---|
| 245 | for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
|
---|
| 246 | if (patDirs[i].equals("**")) {
|
---|
| 247 | patIdxTmp = i;
|
---|
| 248 | break;
|
---|
| 249 | }
|
---|
| 250 | }
|
---|
| 251 | if (patIdxTmp == patIdxStart + 1) {
|
---|
| 252 | // '**/**' situation, so skip one
|
---|
| 253 | patIdxStart++;
|
---|
| 254 | continue;
|
---|
| 255 | }
|
---|
| 256 | // Find the pattern between padIdxStart & padIdxTmp in str between
|
---|
| 257 | // strIdxStart & strIdxEnd
|
---|
| 258 | int patLength = (patIdxTmp - patIdxStart - 1);
|
---|
| 259 | int strLength = (strIdxEnd - strIdxStart + 1);
|
---|
| 260 | int foundIdx = -1;
|
---|
| 261 | strLoop:
|
---|
| 262 | for (int i = 0; i <= strLength - patLength; i++) {
|
---|
| 263 | for (int j = 0; j < patLength; j++) {
|
---|
| 264 | String subPat = patDirs[patIdxStart + j + 1];
|
---|
| 265 | String subStr = strDirs[strIdxStart + i + j];
|
---|
| 266 | if (!match(subPat, subStr, isCaseSensitive)) {
|
---|
| 267 | continue strLoop;
|
---|
| 268 | }
|
---|
| 269 | }
|
---|
| 270 |
|
---|
| 271 | foundIdx = strIdxStart + i;
|
---|
| 272 | break;
|
---|
| 273 | }
|
---|
| 274 |
|
---|
| 275 | if (foundIdx == -1) {
|
---|
| 276 | patDirs = null;
|
---|
| 277 | strDirs = null;
|
---|
| 278 | return false;
|
---|
| 279 | }
|
---|
| 280 |
|
---|
| 281 | patIdxStart = patIdxTmp;
|
---|
| 282 | strIdxStart = foundIdx + patLength;
|
---|
| 283 | }
|
---|
| 284 |
|
---|
| 285 | for (int i = patIdxStart; i <= patIdxEnd; i++) {
|
---|
| 286 | if (!patDirs[i].equals("**")) {
|
---|
| 287 | patDirs = null;
|
---|
| 288 | strDirs = null;
|
---|
| 289 | return false;
|
---|
| 290 | }
|
---|
| 291 | }
|
---|
| 292 |
|
---|
| 293 | return true;
|
---|
| 294 | }
|
---|
| 295 |
|
---|
| 296 | /**
|
---|
| 297 | * Tests whether or not a string matches against a pattern.
|
---|
| 298 | * The pattern may contain two special characters:<br>
|
---|
| 299 | * '*' means zero or more characters<br>
|
---|
| 300 | * '?' means one and only one character
|
---|
| 301 | *
|
---|
| 302 | * @param pattern The pattern to match against.
|
---|
| 303 | * Must not be <code>null</code>.
|
---|
| 304 | * @param str The string which must be matched against the pattern.
|
---|
| 305 | * Must not be <code>null</code>.
|
---|
| 306 | *
|
---|
| 307 | * @return <code>true</code> if the string matches against the pattern,
|
---|
| 308 | * or <code>false</code> otherwise.
|
---|
| 309 | */
|
---|
| 310 | public static boolean match(String pattern, String str) {
|
---|
| 311 | return match(pattern, str, true);
|
---|
| 312 | }
|
---|
| 313 |
|
---|
| 314 | /**
|
---|
| 315 | * Tests whether or not a string matches against a pattern.
|
---|
| 316 | * The pattern may contain two special characters:<br>
|
---|
| 317 | * '*' means zero or more characters<br>
|
---|
| 318 | * '?' means one and only one character
|
---|
| 319 | *
|
---|
| 320 | * @param pattern The pattern to match against.
|
---|
| 321 | * Must not be <code>null</code>.
|
---|
| 322 | * @param str The string which must be matched against the pattern.
|
---|
| 323 | * Must not be <code>null</code>.
|
---|
| 324 | * @param isCaseSensitive Whether or not matching should be performed
|
---|
| 325 | * case sensitively.
|
---|
| 326 | *
|
---|
| 327 | *
|
---|
| 328 | * @return <code>true</code> if the string matches against the pattern,
|
---|
| 329 | * or <code>false</code> otherwise.
|
---|
| 330 | */
|
---|
| 331 | public static boolean match(String pattern, String str,
|
---|
| 332 | boolean isCaseSensitive) {
|
---|
| 333 | char[] patArr = pattern.toCharArray();
|
---|
| 334 | char[] strArr = str.toCharArray();
|
---|
| 335 | int patIdxStart = 0;
|
---|
| 336 | int patIdxEnd = patArr.length - 1;
|
---|
| 337 | int strIdxStart = 0;
|
---|
| 338 | int strIdxEnd = strArr.length - 1;
|
---|
| 339 | char ch;
|
---|
| 340 |
|
---|
| 341 | boolean containsStar = false;
|
---|
| 342 | for (int i = 0; i < patArr.length; i++) {
|
---|
| 343 | if (patArr[i] == '*') {
|
---|
| 344 | containsStar = true;
|
---|
| 345 | break;
|
---|
| 346 | }
|
---|
| 347 | }
|
---|
| 348 |
|
---|
| 349 | if (!containsStar) {
|
---|
| 350 | // No '*'s, so we make a shortcut
|
---|
| 351 | if (patIdxEnd != strIdxEnd) {
|
---|
| 352 | return false; // Pattern and string do not have the same size
|
---|
| 353 | }
|
---|
| 354 | for (int i = 0; i <= patIdxEnd; i++) {
|
---|
| 355 | ch = patArr[i];
|
---|
| 356 | if (ch != '?') {
|
---|
| 357 | if (isCaseSensitive && ch != strArr[i]) {
|
---|
| 358 | return false; // Character mismatch
|
---|
| 359 | }
|
---|
| 360 | if (!isCaseSensitive && Character.toUpperCase(ch)
|
---|
| 361 | != Character.toUpperCase(strArr[i])) {
|
---|
| 362 | return false; // Character mismatch
|
---|
| 363 | }
|
---|
| 364 | }
|
---|
| 365 | }
|
---|
| 366 | return true; // String matches against pattern
|
---|
| 367 | }
|
---|
| 368 |
|
---|
| 369 | if (patIdxEnd == 0) {
|
---|
| 370 | return true; // Pattern contains only '*', which matches anything
|
---|
| 371 | }
|
---|
| 372 |
|
---|
| 373 | // Process characters before first star
|
---|
| 374 | while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) {
|
---|
| 375 | if (ch != '?') {
|
---|
| 376 | if (isCaseSensitive && ch != strArr[strIdxStart]) {
|
---|
| 377 | return false; // Character mismatch
|
---|
| 378 | }
|
---|
| 379 | if (!isCaseSensitive && Character.toUpperCase(ch)
|
---|
| 380 | != Character.toUpperCase(strArr[strIdxStart])) {
|
---|
| 381 | return false; // Character mismatch
|
---|
| 382 | }
|
---|
| 383 | }
|
---|
| 384 | patIdxStart++;
|
---|
| 385 | strIdxStart++;
|
---|
| 386 | }
|
---|
| 387 | if (strIdxStart > strIdxEnd) {
|
---|
| 388 | // All characters in the string are used. Check if only '*'s are
|
---|
| 389 | // left in the pattern. If so, we succeeded. Otherwise failure.
|
---|
| 390 | for (int i = patIdxStart; i <= patIdxEnd; i++) {
|
---|
| 391 | if (patArr[i] != '*') {
|
---|
| 392 | return false;
|
---|
| 393 | }
|
---|
| 394 | }
|
---|
| 395 | return true;
|
---|
| 396 | }
|
---|
| 397 |
|
---|
| 398 | // Process characters after last star
|
---|
| 399 | while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) {
|
---|
| 400 | if (ch != '?') {
|
---|
| 401 | if (isCaseSensitive && ch != strArr[strIdxEnd]) {
|
---|
| 402 | return false; // Character mismatch
|
---|
| 403 | }
|
---|
| 404 | if (!isCaseSensitive && Character.toUpperCase(ch)
|
---|
| 405 | != Character.toUpperCase(strArr[strIdxEnd])) {
|
---|
| 406 | return false; // Character mismatch
|
---|
| 407 | }
|
---|
| 408 | }
|
---|
| 409 | patIdxEnd--;
|
---|
| 410 | strIdxEnd--;
|
---|
| 411 | }
|
---|
| 412 | if (strIdxStart > strIdxEnd) {
|
---|
| 413 | // All characters in the string are used. Check if only '*'s are
|
---|
| 414 | // left in the pattern. If so, we succeeded. Otherwise failure.
|
---|
| 415 | for (int i = patIdxStart; i <= patIdxEnd; i++) {
|
---|
| 416 | if (patArr[i] != '*') {
|
---|
| 417 | return false;
|
---|
| 418 | }
|
---|
| 419 | }
|
---|
| 420 | return true;
|
---|
| 421 | }
|
---|
| 422 |
|
---|
| 423 | // process pattern between stars. padIdxStart and patIdxEnd point
|
---|
| 424 | // always to a '*'.
|
---|
| 425 | while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
|
---|
| 426 | int patIdxTmp = -1;
|
---|
| 427 | for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
|
---|
| 428 | if (patArr[i] == '*') {
|
---|
| 429 | patIdxTmp = i;
|
---|
| 430 | break;
|
---|
| 431 | }
|
---|
| 432 | }
|
---|
| 433 | if (patIdxTmp == patIdxStart + 1) {
|
---|
| 434 | // Two stars next to each other, skip the first one.
|
---|
| 435 | patIdxStart++;
|
---|
| 436 | continue;
|
---|
| 437 | }
|
---|
| 438 | // Find the pattern between padIdxStart & padIdxTmp in str between
|
---|
| 439 | // strIdxStart & strIdxEnd
|
---|
| 440 | int patLength = (patIdxTmp - patIdxStart - 1);
|
---|
| 441 | int strLength = (strIdxEnd - strIdxStart + 1);
|
---|
| 442 | int foundIdx = -1;
|
---|
| 443 | strLoop:
|
---|
| 444 | for (int i = 0; i <= strLength - patLength; i++) {
|
---|
| 445 | for (int j = 0; j < patLength; j++) {
|
---|
| 446 | ch = patArr[patIdxStart + j + 1];
|
---|
| 447 | if (ch != '?') {
|
---|
| 448 | if (isCaseSensitive && ch != strArr[strIdxStart + i
|
---|
| 449 | + j]) {
|
---|
| 450 | continue strLoop;
|
---|
| 451 | }
|
---|
| 452 | if (!isCaseSensitive
|
---|
| 453 | && Character.toUpperCase(ch)
|
---|
| 454 | != Character.toUpperCase(strArr[strIdxStart + i + j])) {
|
---|
| 455 | continue strLoop;
|
---|
| 456 | }
|
---|
| 457 | }
|
---|
| 458 | }
|
---|
| 459 |
|
---|
| 460 | foundIdx = strIdxStart + i;
|
---|
| 461 | break;
|
---|
| 462 | }
|
---|
| 463 |
|
---|
| 464 | if (foundIdx == -1) {
|
---|
| 465 | return false;
|
---|
| 466 | }
|
---|
| 467 |
|
---|
| 468 | patIdxStart = patIdxTmp;
|
---|
| 469 | strIdxStart = foundIdx + patLength;
|
---|
| 470 | }
|
---|
| 471 |
|
---|
| 472 | // All characters in the string are used. Check if only '*'s are left
|
---|
| 473 | // in the pattern. If so, we succeeded. Otherwise failure.
|
---|
| 474 | for (int i = patIdxStart; i <= patIdxEnd; i++) {
|
---|
| 475 | if (patArr[i] != '*') {
|
---|
| 476 | return false;
|
---|
| 477 | }
|
---|
| 478 | }
|
---|
| 479 | return true;
|
---|
| 480 | }
|
---|
| 481 |
|
---|
| 482 | /**
|
---|
| 483 | * Breaks a path up into a Vector of path elements, tokenizing on
|
---|
| 484 | * <code>File.separator</code>.
|
---|
| 485 | *
|
---|
| 486 | * @param path Path to tokenize. Must not be <code>null</code>.
|
---|
| 487 | *
|
---|
| 488 | * @return a Vector of path elements from the tokenized path
|
---|
| 489 | */
|
---|
| 490 | public static Vector tokenizePath (String path) {
|
---|
| 491 | return tokenizePath(path, File.separator);
|
---|
| 492 | }
|
---|
| 493 |
|
---|
| 494 | /**
|
---|
| 495 | * Breaks a path up into a Vector of path elements, tokenizing on
|
---|
| 496 | *
|
---|
| 497 | * @param path Path to tokenize. Must not be <code>null</code>.
|
---|
| 498 | * @param separator the separator against which to tokenize.
|
---|
| 499 | *
|
---|
| 500 | * @return a Vector of path elements from the tokenized path
|
---|
| 501 | * @since Ant 1.6
|
---|
| 502 | */
|
---|
| 503 | public static Vector tokenizePath (String path, String separator) {
|
---|
| 504 | Vector ret = new Vector();
|
---|
| 505 | StringTokenizer st = new StringTokenizer(path, separator);
|
---|
| 506 | while (st.hasMoreTokens()) {
|
---|
| 507 | ret.addElement(st.nextToken());
|
---|
| 508 | }
|
---|
| 509 | return ret;
|
---|
| 510 | }
|
---|
| 511 |
|
---|
| 512 | /**
|
---|
| 513 | * Same as {@link #tokenizePath tokenizePath} but hopefully faster.
|
---|
| 514 | */
|
---|
| 515 | private static String[] tokenizePathAsArray(String path) {
|
---|
| 516 | char sep = File.separatorChar;
|
---|
| 517 | int start = 0;
|
---|
| 518 | int len = path.length();
|
---|
| 519 | int count = 0;
|
---|
| 520 | for (int pos = 0; pos < len; pos++) {
|
---|
| 521 | if (path.charAt(pos) == sep) {
|
---|
| 522 | if (pos != start) {
|
---|
| 523 | count++;
|
---|
| 524 | }
|
---|
| 525 | start = pos + 1;
|
---|
| 526 | }
|
---|
| 527 | }
|
---|
| 528 | if (len != start) {
|
---|
| 529 | count++;
|
---|
| 530 | }
|
---|
| 531 | String[] l = new String[count];
|
---|
| 532 | count = 0;
|
---|
| 533 | start = 0;
|
---|
| 534 | for (int pos = 0; pos < len; pos++) {
|
---|
| 535 | if (path.charAt(pos) == sep) {
|
---|
| 536 | if (pos != start) {
|
---|
| 537 | String tok = path.substring(start, pos);
|
---|
| 538 | l[count++] = tok;
|
---|
| 539 | }
|
---|
| 540 | start = pos + 1;
|
---|
| 541 | }
|
---|
| 542 | }
|
---|
| 543 | if (len != start) {
|
---|
| 544 | String tok = path.substring(start);
|
---|
| 545 | l[count/*++*/] = tok;
|
---|
| 546 | }
|
---|
| 547 | return l;
|
---|
| 548 | }
|
---|
| 549 |
|
---|
| 550 |
|
---|
| 551 | /**
|
---|
| 552 | * Returns dependency information on these two files. If src has been
|
---|
| 553 | * modified later than target, it returns true. If target doesn't exist,
|
---|
| 554 | * it likewise returns true. Otherwise, target is newer than src and
|
---|
| 555 | * is not out of date, thus the method returns false. It also returns
|
---|
| 556 | * false if the src file doesn't even exist, since how could the
|
---|
| 557 | * target then be out of date.
|
---|
| 558 | *
|
---|
| 559 | * @param src the original file
|
---|
| 560 | * @param target the file being compared against
|
---|
| 561 | * @param granularity the amount in seconds of slack we will give in
|
---|
| 562 | * determining out of dateness
|
---|
| 563 | * @return whether the target is out of date
|
---|
| 564 | */
|
---|
| 565 | public static boolean isOutOfDate(File src, File target, int granularity) {
|
---|
| 566 | if (!src.exists()) {
|
---|
| 567 | return false;
|
---|
| 568 | }
|
---|
| 569 | if (!target.exists()) {
|
---|
| 570 | return true;
|
---|
| 571 | }
|
---|
| 572 | if ((src.lastModified() - granularity) > target.lastModified()) {
|
---|
| 573 | return true;
|
---|
| 574 | }
|
---|
| 575 | return false;
|
---|
| 576 | }
|
---|
| 577 |
|
---|
| 578 | /**
|
---|
| 579 | * Returns dependency information on these two resources. If src has been
|
---|
| 580 | * modified later than target, it returns true. If target doesn't exist,
|
---|
| 581 | * it likewise returns true. Otherwise, target is newer than src and
|
---|
| 582 | * is not out of date, thus the method returns false. It also returns
|
---|
| 583 | * false if the src file doesn't even exist, since how could the
|
---|
| 584 | * target then be out of date.
|
---|
| 585 | *
|
---|
| 586 | * @param src the original resource
|
---|
| 587 | * @param target the resource being compared against
|
---|
| 588 | * @param granularity the amount in seconds of slack we will give in
|
---|
| 589 | * determining out of dateness
|
---|
| 590 | * @return whether the target is out of date
|
---|
| 591 | */
|
---|
| 592 | public static boolean isOutOfDate(Resource src, Resource target,
|
---|
| 593 | int granularity) {
|
---|
| 594 | if (!src.isExists()) {
|
---|
| 595 | return false;
|
---|
| 596 | }
|
---|
| 597 | if (!target.isExists()) {
|
---|
| 598 | return true;
|
---|
| 599 | }
|
---|
| 600 | if ((src.getLastModified() - granularity) > target.getLastModified()) {
|
---|
| 601 | return true;
|
---|
| 602 | }
|
---|
| 603 | return false;
|
---|
| 604 | }
|
---|
| 605 |
|
---|
| 606 | /**
|
---|
| 607 | * "Flattens" a string by removing all whitespace (space, tab, linefeed,
|
---|
| 608 | * carriage return, and formfeed). This uses StringTokenizer and the
|
---|
| 609 | * default set of tokens as documented in the single arguement constructor.
|
---|
| 610 | *
|
---|
| 611 | * @param input a String to remove all whitespace.
|
---|
| 612 | * @return a String that has had all whitespace removed.
|
---|
| 613 | */
|
---|
| 614 | public static String removeWhitespace(String input) {
|
---|
| 615 | StringBuffer result = new StringBuffer();
|
---|
| 616 | if (input != null) {
|
---|
| 617 | StringTokenizer st = new StringTokenizer(input);
|
---|
| 618 | while (st.hasMoreTokens()) {
|
---|
| 619 | result.append(st.nextToken());
|
---|
| 620 | }
|
---|
| 621 | }
|
---|
| 622 | return result.toString();
|
---|
| 623 | }
|
---|
| 624 |
|
---|
| 625 | /**
|
---|
| 626 | * Tests if a string contains stars or question marks
|
---|
| 627 | * @param input a String which one wants to test for containing wildcard
|
---|
| 628 | * @return true if the string contains at least a star or a question mark
|
---|
| 629 | */
|
---|
| 630 | public static boolean hasWildcards(String input) {
|
---|
| 631 | return (input.indexOf('*') != -1 || input.indexOf('?') != -1);
|
---|
| 632 | }
|
---|
| 633 |
|
---|
| 634 | /**
|
---|
| 635 | * removes from a pattern all tokens to the right containing wildcards
|
---|
| 636 | * @param input the input string
|
---|
| 637 | * @return the leftmost part of the pattern without wildcards
|
---|
| 638 | */
|
---|
| 639 | public static String rtrimWildcardTokens(String input) {
|
---|
| 640 | Vector v = tokenizePath(input, File.separator);
|
---|
| 641 | StringBuffer sb = new StringBuffer();
|
---|
| 642 | for (int counter = 0; counter < v.size(); counter++) {
|
---|
| 643 | if (hasWildcards((String) v.elementAt(counter))) {
|
---|
| 644 | break;
|
---|
| 645 | }
|
---|
| 646 | if (counter > 0) {
|
---|
| 647 | sb.append(File.separator);
|
---|
| 648 | }
|
---|
| 649 | sb.append((String) v.elementAt(counter));
|
---|
| 650 | }
|
---|
| 651 | return sb.toString();
|
---|
| 652 | }
|
---|
| 653 | }
|
---|
| 654 |
|
---|