1. /*
  2. * Copyright 2000,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. package org.apache.tools.ant;
  18. import java.io.File;
  19. import java.util.NoSuchElementException;
  20. import java.util.StringTokenizer;
  21. import org.apache.tools.ant.taskdefs.condition.Os;
  22. /**
  23. * A Path tokenizer takes a path and returns the components that make up
  24. * that path.
  25. *
  26. * The path can use path separators of either ':' or ';' and file separators
  27. * of either '/' or '\'.
  28. *
  29. */
  30. public class PathTokenizer {
  31. /**
  32. * A tokenizer to break the string up based on the ':' or ';' separators.
  33. */
  34. private StringTokenizer tokenizer;
  35. /**
  36. * A String which stores any path components which have been read ahead
  37. * due to DOS filesystem compensation.
  38. */
  39. private String lookahead = null;
  40. /**
  41. * A boolean that determines if we are running on Novell NetWare, which
  42. * exhibits slightly different path name characteristics (multi-character
  43. * volume / drive names)
  44. */
  45. private boolean onNetWare = Os.isFamily("netware");
  46. /**
  47. * Flag to indicate whether or not we are running on a platform with a
  48. * DOS style filesystem
  49. */
  50. private boolean dosStyleFilesystem;
  51. /**
  52. * Constructs a path tokenizer for the specified path.
  53. *
  54. * @param path The path to tokenize. Must not be <code>null</code>.
  55. */
  56. public PathTokenizer(String path) {
  57. if (onNetWare) {
  58. // For NetWare, use the boolean=true mode, so we can use delimiter
  59. // information to make a better decision later.
  60. tokenizer = new StringTokenizer(path, ":;", true);
  61. } else {
  62. // on Windows and Unix, we can ignore delimiters and still have
  63. // enough information to tokenize correctly.
  64. tokenizer = new StringTokenizer(path, ":;", false);
  65. }
  66. dosStyleFilesystem = File.pathSeparatorChar == ';';
  67. }
  68. /**
  69. * Tests if there are more path elements available from this tokenizer's
  70. * path. If this method returns <code>true</code>, then a subsequent call
  71. * to nextToken will successfully return a token.
  72. *
  73. * @return <code>true</code> if and only if there is at least one token
  74. * in the string after the current position; <code>false</code> otherwise.
  75. */
  76. public boolean hasMoreTokens() {
  77. if (lookahead != null) {
  78. return true;
  79. }
  80. return tokenizer.hasMoreTokens();
  81. }
  82. /**
  83. * Returns the next path element from this tokenizer.
  84. *
  85. * @return the next path element from this tokenizer.
  86. *
  87. * @exception NoSuchElementException if there are no more elements in this
  88. * tokenizer's path.
  89. */
  90. public String nextToken() throws NoSuchElementException {
  91. String token = null;
  92. if (lookahead != null) {
  93. token = lookahead;
  94. lookahead = null;
  95. } else {
  96. token = tokenizer.nextToken().trim();
  97. }
  98. if (!onNetWare) {
  99. if (token.length() == 1 && Character.isLetter(token.charAt(0))
  100. && dosStyleFilesystem
  101. && tokenizer.hasMoreTokens()) {
  102. // we are on a dos style system so this path could be a drive
  103. // spec. We look at the next token
  104. String nextToken = tokenizer.nextToken().trim();
  105. if (nextToken.startsWith("\\") || nextToken.startsWith("/")) {
  106. // we know we are on a DOS style platform and the next path
  107. // starts with a slash or backslash, so we know this is a
  108. // drive spec
  109. token += ":" + nextToken;
  110. } else {
  111. // store the token just read for next time
  112. lookahead = nextToken;
  113. }
  114. }
  115. } else {
  116. // we are on NetWare, tokenizing is handled a little differently,
  117. // due to the fact that NetWare has multiple-character volume names.
  118. if (token.equals(File.pathSeparator) || token.equals(":")) {
  119. // ignore ";" and get the next token
  120. token = tokenizer.nextToken().trim();
  121. }
  122. if (tokenizer.hasMoreTokens()) {
  123. // this path could be a drive spec, so look at the next token
  124. String nextToken = tokenizer.nextToken().trim();
  125. // make sure we aren't going to get the path separator next
  126. if (!nextToken.equals(File.pathSeparator)) {
  127. if (nextToken.equals(":")) {
  128. if (!token.startsWith("/") && !token.startsWith("\\")
  129. && !token.startsWith(".")
  130. && !token.startsWith("..")) {
  131. // it indeed is a drive spec, get the next bit
  132. String oneMore = tokenizer.nextToken().trim();
  133. if (!oneMore.equals(File.pathSeparator)) {
  134. token += ":" + oneMore;
  135. } else {
  136. token += ":";
  137. lookahead = oneMore;
  138. }
  139. }
  140. // implicit else: ignore the ':' since we have either a
  141. // UNIX or a relative path
  142. } else {
  143. // store the token just read for next time
  144. lookahead = nextToken;
  145. }
  146. }
  147. }
  148. }
  149. return token;
  150. }
  151. }