1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.commons.vfs.provider;
18
19 import org.apache.commons.vfs.FileName;
20 import org.apache.commons.vfs.FileSystemException;
21 import org.apache.commons.vfs.FileType;
22 import org.apache.commons.vfs.NameScope;
23 import org.apache.commons.vfs.VFS;
24
25 /***
26 * A default file name implementation.
27 *
28 * @author <a href="mailto:adammurdoch@apache.org">Adam Murdoch</a>
29 * @version $Revision: 480428 $ $Date: 2006-11-29 07:15:24 +0100 (Mi, 29 Nov 2006) $
30 */
31 public abstract class AbstractFileName
32 implements FileName
33 {
34
35 private final String scheme;
36 private final String absPath;
37 private FileType type;
38
39
40 private String uri;
41 private String baseName;
42 private String rootUri;
43 private String extension;
44 private String decodedAbsPath;
45
46 public AbstractFileName(final String scheme,
47 final String absPath, FileType type)
48 {
49 this.rootUri = null;
50 this.scheme = scheme;
51 this.type = type;
52 if (absPath != null && absPath.length() > 0)
53 {
54 if (absPath.length() > 1 && absPath.endsWith("/"))
55 {
56 this.absPath = absPath.substring(0, absPath.length() - 1);
57 }
58 else
59 {
60 this.absPath = absPath;
61 }
62 }
63 else
64 {
65 this.absPath = ROOT_PATH;
66 }
67 }
68
69 /***
70 * Returns the hashcode for this name.
71 */
72 public int hashCode()
73 {
74 return (getRootURI().hashCode() ^ getPath().hashCode());
75 }
76
77 /***
78 * Determines if this object is equal to another.
79 */
80 public boolean equals(final Object obj)
81 {
82 final AbstractFileName name = (AbstractFileName) obj;
83 return (getRootURI().equals(name.getRootURI()) && getPath().equals(name.getPath()));
84 }
85
86 /***
87 * Implement Comparable
88 *
89 * @param obj another abstractfilename
90 */
91 public int compareTo(Object obj)
92 {
93 final AbstractFileName name = (AbstractFileName) obj;
94 int ret = getRootURI().compareTo(name.getRootURI());
95 if (ret != 0)
96 {
97 return ret;
98 }
99
100
101 try
102 {
103 return getPathDecoded().compareTo(name.getPathDecoded());
104 }
105 catch (FileSystemException e)
106 {
107 throw new RuntimeException(e.getMessage());
108 }
109 }
110
111 /***
112 * Returns the URI of the file.
113 */
114 public String toString()
115 {
116 return getURI();
117 }
118
119 /***
120 * Factory method for creating name instances.
121 */
122 public abstract FileName createName(String absPath, FileType type);
123
124 /***
125 * Builds the root URI for this file name. Note that the root URI must not
126 * end with a separator character.
127 */
128 protected abstract void appendRootUri(StringBuffer buffer, boolean addPassword);
129
130 /***
131 * Returns the base name of the file.
132 */
133 public String getBaseName()
134 {
135 if (baseName == null)
136 {
137 final int idx = getPath().lastIndexOf(SEPARATOR_CHAR);
138 if (idx == -1)
139 {
140 baseName = getPath();
141 }
142 else
143 {
144 baseName = getPath().substring(idx + 1);
145 }
146 }
147
148 return baseName;
149 }
150
151 /***
152 * Returns the absolute path of the file, relative to the root of the
153 * file system that the file belongs to.
154 */
155 public String getPath()
156 {
157 if (VFS.isUriStyle())
158 {
159 return absPath + getUriTrailer();
160 }
161 return absPath;
162 }
163
164 protected String getUriTrailer()
165 {
166 return getType().hasChildren() ? "/" : "";
167 }
168
169 public String getPathDecoded() throws FileSystemException
170 {
171 if (decodedAbsPath == null)
172 {
173 decodedAbsPath = UriParser.decode(getPath());
174 }
175
176 return decodedAbsPath;
177 }
178
179 /***
180 * Returns the name of the parent of the file.
181 */
182 public FileName getParent()
183 {
184 final String parentPath;
185 final int idx = getPath().lastIndexOf(SEPARATOR_CHAR);
186 if (idx == -1 || idx == getPath().length() - 1)
187 {
188
189 return null;
190 }
191 else if (idx == 0)
192 {
193
194 parentPath = SEPARATOR;
195 }
196 else
197 {
198 parentPath = getPath().substring(0, idx);
199 }
200 return createName(parentPath, FileType.FOLDER);
201 }
202
203 /***
204 * find the root of the filesystem
205 */
206 public FileName getRoot()
207 {
208 FileName root = this;
209 while (root.getParent() != null)
210 {
211 root = root.getParent();
212 }
213
214 return root;
215 }
216
217 /***
218 * Returns the URI scheme of this file.
219 */
220 public String getScheme()
221 {
222 return scheme;
223 }
224
225 /***
226 * Returns the absolute URI of the file.
227 */
228 public String getURI()
229 {
230 if (uri == null)
231 {
232 uri = createURI();
233 }
234 return uri;
235 }
236
237 protected String createURI()
238 {
239 final StringBuffer buffer = new StringBuffer();
240 appendRootUri(buffer, true);
241 buffer.append(getPath());
242 return buffer.toString();
243 }
244
245 /***
246 * Converts a file name to a relative name, relative to this file name.
247 */
248 public String getRelativeName(final FileName name) throws FileSystemException
249 {
250 final String path = name.getPath();
251
252
253 final int basePathLen = getPath().length();
254 final int pathLen = path.length();
255
256
257 if (basePathLen == 1 && pathLen == 1)
258 {
259 return ".";
260 }
261 else if (basePathLen == 1)
262 {
263 return path.substring(1);
264 }
265
266 final int maxlen = Math.min(basePathLen, pathLen);
267 int pos = 0;
268 for (; pos < maxlen && getPath().charAt(pos) == path.charAt(pos); pos++)
269 {
270 }
271
272 if (pos == basePathLen && pos == pathLen)
273 {
274
275 return ".";
276 }
277 else if (pos == basePathLen && pos < pathLen && path.charAt(pos) == SEPARATOR_CHAR)
278 {
279
280 return path.substring(pos + 1);
281 }
282
283
284 final StringBuffer buffer = new StringBuffer();
285 if (pathLen > 1 && (pos < pathLen || getPath().charAt(pos) != SEPARATOR_CHAR))
286 {
287
288 pos = getPath().lastIndexOf(SEPARATOR_CHAR, pos);
289 buffer.append(path.substring(pos));
290 }
291
292
293
294 buffer.insert(0, "..");
295 pos = getPath().indexOf(SEPARATOR_CHAR, pos + 1);
296 while (pos != -1)
297 {
298 buffer.insert(0, "../");
299 pos = getPath().indexOf(SEPARATOR_CHAR, pos + 1);
300 }
301
302 return buffer.toString();
303 }
304
305 /***
306 * Returns the root URI of the file system this file belongs to.
307 */
308 public String getRootURI()
309 {
310 if (rootUri == null)
311 {
312 final StringBuffer buffer = new StringBuffer();
313 appendRootUri(buffer, true);
314 buffer.append(SEPARATOR_CHAR);
315 rootUri = buffer.toString().intern();
316 }
317 return rootUri;
318 }
319
320 /***
321 * Returns the depth of this file name, within its file system.
322 */
323 public int getDepth()
324 {
325 final int len = getPath().length();
326 if (len == 0 || (len == 1 && getPath().charAt(0) == SEPARATOR_CHAR))
327 {
328 return 0;
329 }
330 int depth = 1;
331 for (int pos = 0; pos > -1 && pos < len; depth++)
332 {
333 pos = getPath().indexOf(SEPARATOR_CHAR, pos + 1);
334 }
335 return depth;
336 }
337
338 /***
339 * Returns the extension of this file name.
340 */
341 public String getExtension()
342 {
343 if (extension == null)
344 {
345 getBaseName();
346 final int pos = baseName.lastIndexOf('.');
347
348
349
350
351
352 if ((pos < 1) || (pos == baseName.length() - 1))
353 {
354
355 extension = "";
356 }
357 else
358 {
359 extension = baseName.substring(pos + 1).intern();
360 }
361 }
362 return extension;
363 }
364
365 /***
366 * Determines if another file name is an ancestor of this file name.
367 */
368 public boolean isAncestor(final FileName ancestor)
369 {
370 if (!ancestor.getRootURI().equals(getRootURI()))
371 {
372 return false;
373 }
374 return checkName(ancestor.getPath(), getPath(), NameScope.DESCENDENT);
375 }
376
377 /***
378 * Determines if another file name is a descendent of this file name.
379 */
380 public boolean isDescendent(final FileName descendent)
381 {
382 return isDescendent(descendent, NameScope.DESCENDENT);
383 }
384
385 /***
386 * Determines if another file name is a descendent of this file name.
387 */
388 public boolean isDescendent(final FileName descendent,
389 final NameScope scope)
390 {
391 if (!descendent.getRootURI().equals(getRootURI()))
392 {
393 return false;
394 }
395 return checkName(getPath(), descendent.getPath(), scope);
396 }
397
398 /***
399 * Returns the requested or current type of this name. <br />
400 * <p/>
401 * The "requested" type is the one determined during resolving the name. <br/>
402 * In this case the name is a {@link FileType#FOLDER} if it ends with an "/" else
403 * it will be a {@link FileType#FILE}<br/>
404 * </p>
405 * <p/>
406 * Once attached it will be changed to reflect the real type of this resource.
407 * </p>
408 *
409 * @return {@link FileType#FOLDER} or {@link FileType#FILE}
410 */
411 public FileType getType()
412 {
413 return type;
414 }
415
416 /***
417 * sets the type of this file e.g. when it will be attached.
418 *
419 * @param type {@link FileType#FOLDER} or {@link FileType#FILE}
420 */
421 void setType(FileType type) throws FileSystemException
422 {
423 if (type != FileType.FOLDER && type != FileType.FILE && type != FileType.FILE_OR_FOLDER)
424 {
425 throw new FileSystemException("vfs.provider/filename-type.error");
426 }
427
428 this.type = type;
429 }
430
431 /***
432 * Checks whether a path fits in a particular scope of another path.
433 *
434 * @param basePath An absolute, normalised path.
435 * @param path An absolute, normalised path.
436 */
437 public static boolean checkName(final String basePath,
438 final String path,
439 final NameScope scope)
440 {
441 if (scope == NameScope.FILE_SYSTEM)
442 {
443
444 return true;
445 }
446
447 if (!path.startsWith(basePath))
448 {
449 return false;
450 }
451
452 int baseLen = basePath.length();
453 if (VFS.isUriStyle())
454 {
455
456 baseLen--;
457 }
458
459 if (scope == NameScope.CHILD)
460 {
461 if (path.length() == baseLen
462 || (baseLen > 1 && path.charAt(baseLen) != SEPARATOR_CHAR)
463 || path.indexOf(SEPARATOR_CHAR, baseLen + 1) != -1)
464 {
465 return false;
466 }
467 }
468 else if (scope == NameScope.DESCENDENT)
469 {
470 if (path.length() == baseLen
471 || (baseLen > 1 && path.charAt(baseLen) != SEPARATOR_CHAR))
472 {
473 return false;
474 }
475 }
476 else if (scope == NameScope.DESCENDENT_OR_SELF)
477 {
478 if (baseLen > 1
479 && path.length() > baseLen
480 && path.charAt(baseLen) != SEPARATOR_CHAR)
481 {
482 return false;
483 }
484 }
485 else if (scope != NameScope.FILE_SYSTEM)
486 {
487 throw new IllegalArgumentException();
488 }
489
490 return true;
491 }
492
493 /***
494 * returns a "friendly path", this is a path without a password.
495 */
496 public String getFriendlyURI()
497 {
498 final StringBuffer buffer = new StringBuffer();
499 appendRootUri(buffer, false);
500 buffer.append(getPath());
501 return buffer.toString();
502 }
503 }