1
12 package com.thoughtworks.xstream.io.path;
13
14 import com.thoughtworks.xstream.core.util.FastStack;
15
16 import java.util.ArrayList;
17 import java.util.List;
18
19
61 public class Path {
62
63 private final String[] chunks;
64 private transient String pathAsString;
65 private transient String pathExplicit;
66 private static final Path DOT = new Path(new String[] {"."});
67
68 public Path(String pathAsString) {
69
70 List result = new ArrayList();
71 int currentIndex = 0;
72 int nextSeparator;
73 this.pathAsString = pathAsString;
74 while ((nextSeparator = pathAsString.indexOf('/', currentIndex)) != -1) {
75
76 result.add(normalize(pathAsString, currentIndex, nextSeparator));
77 currentIndex = nextSeparator + 1;
78 }
79 result.add(normalize(pathAsString,currentIndex,pathAsString.length()));
80 String[] arr = new String[result.size()];
81 result.toArray(arr);
82 chunks = arr;
83 }
84
85 private String normalize(String s, int start, int end) {
86 if (end - start > 3
87 && s.charAt(end-3) == '['
88 && s.charAt(end-2) == '1'
89 && s.charAt(end-1) == ']') {
90 this.pathAsString = null;
91 return s.substring(start, end-3);
92 } else {
93 return s.substring(start, end);
94 }
95
96 }
97
98 public Path(String[] chunks) {
99 this.chunks = chunks;
100 }
101
102 public String toString() {
103 if (pathAsString == null) {
104 StringBuffer buffer = new StringBuffer();
105 for (int i = 0; i < chunks.length; i++) {
106 if (i > 0) buffer.append('/');
107 buffer.append(chunks[i]);
108 }
109 pathAsString = buffer.toString();
110 }
111 return pathAsString;
112 }
113
114 public String explicit() {
115 if (pathExplicit == null) {
116 StringBuffer buffer = new StringBuffer();
117 for (int i = 0; i < chunks.length; i++) {
118 if (i > 0) buffer.append('/');
119 String chunk = chunks[i];
120 buffer.append(chunk);
121 int length = chunk.length();
122 if (length > 0) {
123 char c = chunk.charAt(length-1);
124 if (c != ']' && c != '.') {
125 buffer.append("[1]");
126 }
127 }
128 }
129 pathExplicit = buffer.toString();
130 }
131 return pathExplicit;
132 }
133
134 public boolean equals(Object o) {
135 if (this == o) return true;
136 if (!(o instanceof Path)) return false;
137
138 final Path other = (Path) o;
139 if (chunks.length != other.chunks.length) return false;
140 for (int i = 0; i < chunks.length; i++) {
141 if (!chunks[i].equals(other.chunks[i])) return false;
142 }
143
144 return true;
145 }
146
147 public int hashCode() {
148 int result = 543645643;
149 for (int i = 0; i < chunks.length; i++) {
150 result = 29 * result + chunks[i].hashCode();
151 }
152 return result;
153 }
154
155 public Path relativeTo(Path that) {
156 int depthOfPathDivergence = depthOfPathDivergence(chunks, that.chunks);
157 String[] result = new String[chunks.length + that.chunks.length - 2 * depthOfPathDivergence];
158 int count = 0;
159
160 for (int i = depthOfPathDivergence; i < chunks.length; i++) {
161 result[count++] = "..";
162 }
163 for (int j = depthOfPathDivergence; j < that.chunks.length; j++) {
164 result[count++] = that.chunks[j];
165 }
166
167 if (count == 0) {
168 return DOT;
169 } else {
170 return new Path(result);
171 }
172 }
173
174 private int depthOfPathDivergence(String[] path1, String[] path2) {
175 int minLength = Math.min(path1.length, path2.length);
176 for (int i = 0; i < minLength; i++) {
177 if (!path1[i].equals(path2[i])) {
178 return i;
179 }
180 }
181 return minLength;
182 }
183
184 public Path apply(Path relativePath) {
185 FastStack absoluteStack = new FastStack(16);
186
187 for (int i = 0; i < chunks.length; i++) {
188 absoluteStack.push(chunks[i]);
189 }
190
191 for (int i = 0; i < relativePath.chunks.length; i++) {
192 String relativeChunk = relativePath.chunks[i];
193 if (relativeChunk.equals("..")) {
194 absoluteStack.pop();
195 } else if (!relativeChunk.equals(".")) {
196 absoluteStack.push(relativeChunk);
197 }
198 }
199
200 String[] result = new String[absoluteStack.size()];
201 for (int i = 0; i < result.length; i++) {
202 result[i] = (String) absoluteStack.get(i);
203 }
204
205 return new Path(result);
206 }
207
208 public boolean isAncestor(Path child) {
209 if (child == null || child.chunks.length < chunks.length) {
210 return false;
211 }
212 for (int i = 0; i < chunks.length; i++) {
213 if (!chunks[i].equals(child.chunks[i])) {
214 return false;
215 }
216 }
217 return true;
218 }
219 }
220