Convert for to while loop to avoid the empty control statement.
diff --git a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/AbstractFileName.java b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/AbstractFileName.java
index ccff027..1ba9b27 100644
--- a/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/AbstractFileName.java
+++ b/commons-vfs2/src/main/java/org/apache/commons/vfs2/provider/AbstractFileName.java
@@ -1,535 +1,535 @@
-/*
- * 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.commons.vfs2.provider;
-
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.vfs2.FileName;
-import org.apache.commons.vfs2.FileSystemException;
-import org.apache.commons.vfs2.FileType;
-import org.apache.commons.vfs2.NameScope;
-import org.apache.commons.vfs2.VFS;
-
-/**
- * A default file name implementation.
- */
-public abstract class AbstractFileName implements FileName {
-
- // URI Characters that are possible in local file names, but must be escaped
- // for proper URI handling.
- //
- // How reserved URI chars were selected:
- //
- // URIs can contain :, /, ?, #, @
- // See https://docs.oracle.com/javase/8/docs/api/java/net/URI.html
- // http://tools.ietf.org/html/rfc3986#section-2.2
- //
- // Since : and / occur before the path, only chars after path are escaped (i.e., # and ?)
- // ? is a reserved filesystem character for Windows and Unix, so can't be part of a file name.
- // Therefore only # is a reserved char in a URI as part of the path that can be in the file name.
- private static final char[] RESERVED_URI_CHARS = {'#', ' '};
-
- private final String scheme;
- private final String absPath;
- private FileType type;
-
- // Cached attributes
- private String uriString;
- private String baseName;
- private String rootUri;
- private String extension;
- private String decodedAbsPath;
-
- private String key;
-
- /**
- * Constructs a new instance.
- *
- * @param scheme The scheme.
- * @param absolutePath the absolute path, maybe empty or null.
- * @param type the file type.
- */
- public AbstractFileName(final String scheme, final String absolutePath, final FileType type) {
- this.rootUri = null;
- this.scheme = scheme;
- this.type = type;
- if (StringUtils.isEmpty(absolutePath)) {
- this.absPath = ROOT_PATH;
- } else if (absolutePath.length() > 1 && absolutePath.endsWith("/")) {
- this.absPath = absolutePath.substring(0, absolutePath.length() - 1);
- } else {
- this.absPath = absolutePath;
- }
- }
-
- /**
- * Checks whether a path fits in a particular scope of another path.
- *
- * @param basePath An absolute, normalised path.
- * @param path An absolute, normalised path.
- * @param scope The NameScope.
- * @return true if the path fits in the scope, false otherwise.
- */
- public static boolean checkName(final String basePath, final String path, final NameScope scope) {
- if (scope == NameScope.FILE_SYSTEM) {
- // All good
- return true;
- }
-
- if (!path.startsWith(basePath)) {
- return false;
- }
-
- int baseLen = basePath.length();
- if (VFS.isUriStyle()) {
- // strip the trailing "/"
- baseLen--;
- }
-
- if (scope == NameScope.CHILD) {
- return path.length() != baseLen && (baseLen <= 1 || path.charAt(baseLen) == SEPARATOR_CHAR)
- && path.indexOf(SEPARATOR_CHAR, baseLen + 1) == -1;
- }
- if (scope == NameScope.DESCENDENT) {
- return path.length() != baseLen && (baseLen <= 1 || path.charAt(baseLen) == SEPARATOR_CHAR);
- }
- if (scope == NameScope.DESCENDENT_OR_SELF) {
- return baseLen <= 1 || path.length() <= baseLen || path.charAt(baseLen) == SEPARATOR_CHAR;
- }
- throw new IllegalArgumentException();
- }
-
- /**
- * Builds the root URI for this file name. Note that the root URI must not end with a separator character.
- *
- * @param buffer A StringBuilder to use to construct the URI.
- * @param addPassword true if the password should be added, false otherwise.
- */
- protected abstract void appendRootUri(StringBuilder buffer, boolean addPassword);
-
- /**
- * Implement Comparable.
- *
- * @param obj another abstract file name
- * @return negative number if less than, 0 if equal, positive if greater than.
- */
- @Override
- public int compareTo(final FileName obj) {
- final AbstractFileName name = (AbstractFileName) obj;
- return getKey().compareTo(name.getKey());
- }
-
- /**
- * Factory method for creating name instances.
- *
- * @param absolutePath The absolute path.
- * @param fileType The FileType.
- * @return The FileName.
- */
- public abstract FileName createName(String absolutePath, FileType fileType);
-
- protected String createURI() {
- return createURI(false, true);
- }
-
- private String createURI(final boolean useAbsolutePath, final boolean usePassword) {
- final StringBuilder buffer = new StringBuilder();
- appendRootUri(buffer, usePassword);
- buffer.append(handleURISpecialCharacters(useAbsolutePath ? absPath : getPath()));
- return buffer.toString();
- }
-
- @Override
- public boolean equals(final Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
-
- final AbstractFileName that = (AbstractFileName) o;
-
- return getKey().equals(that.getKey());
- }
-
- /**
- * Returns the base name of the file.
- *
- * @return The base name of the file.
- */
- @Override
- public String getBaseName() {
- if (baseName == null) {
- final int idx = getPath().lastIndexOf(SEPARATOR_CHAR);
- if (idx == -1) {
- baseName = getPath();
- } else {
- baseName = getPath().substring(idx + 1);
- }
- }
-
- return baseName;
- }
-
- /**
- * Returns the depth of this file name, within its file system.
- *
- * @return The depth of the file name.
- */
- @Override
- public int getDepth() {
- final int len = getPath().length();
- if (len == 0 || len == 1 && getPath().charAt(0) == SEPARATOR_CHAR) {
- return 0;
- }
- int depth = 1;
- for (int pos = 0; pos > -1 && pos < len; depth++) {
- pos = getPath().indexOf(SEPARATOR_CHAR, pos + 1);
- }
- return depth;
- }
-
- /**
- * Returns the extension of this file name.
- *
- * @return The file extension.
- */
- @Override
- public String getExtension() {
- if (extension == null) {
- getBaseName();
- final int pos = baseName.lastIndexOf('.');
- // if ((pos == -1) || (pos == baseName.length() - 1))
- // imario@ops.co.at: Review of patch from adagoubard@chello.nl
- // do not treat file names like
- // .bashrc c:\windows\.java c:\windows\.javaws c:\windows\.jedit c:\windows\.appletviewer
- // as extension
- if (pos < 1 || pos == baseName.length() - 1) {
- // No extension
- extension = "";
- } else {
- extension = baseName.substring(pos + 1).intern();
- }
- }
- return extension;
- }
-
- /**
- * Returns the URI without a password.
- *
- * @return Returns the URI without a password.
- */
- @Override
- public String getFriendlyURI() {
- return createURI(false, false);
- }
-
- /**
- * Create a path that does not use the FileType since that field is not immutable.
- *
- * @return The key.
- */
- private String getKey() {
- if (key == null) {
- key = getURI();
- }
- return key;
- }
-
- /**
- * Returns the name of the parent of the file.
- *
- * @return the FileName of the parent.
- */
- @Override
- public FileName getParent() {
- final String parentPath;
- final int idx = getPath().lastIndexOf(SEPARATOR_CHAR);
- if (idx == -1 || idx == getPath().length() - 1) {
- // No parent
- return null;
- }
- if (idx == 0) {
- // Root is the parent
- parentPath = SEPARATOR;
- } else {
- parentPath = getPath().substring(0, idx);
- }
- return createName(parentPath, FileType.FOLDER);
- }
-
- /**
- * Returns the absolute path of the file, relative to the root of the file system that the file belongs to.
- *
- * @return The path String.
- */
- @Override
- public String getPath() {
- if (VFS.isUriStyle()) {
- return absPath + getUriTrailer();
- }
- return absPath;
- }
-
- /**
- * Returns the decoded path.
- *
- * @return The decoded path String.
- * @throws FileSystemException If an error occurs.
- */
- @Override
- public String getPathDecoded() throws FileSystemException {
- if (decodedAbsPath == null) {
- decodedAbsPath = UriParser.decode(getPath());
- }
-
- return decodedAbsPath;
- }
-
- /**
- * Converts a file name to a relative name, relative to this file name.
- *
- * @param name The FileName.
- * @return The relative path to the file.
- * @throws FileSystemException if an error occurs.
- */
- @Override
- public String getRelativeName(final FileName name) throws FileSystemException {
- final String path = name.getPath();
-
- // Calculate the common prefix
- final int basePathLen = getPath().length();
- final int pathLen = path.length();
-
- // Deal with root
- if (basePathLen == 1 && pathLen == 1) {
- return ".";
- }
- if (basePathLen == 1) {
- return path.substring(1);
- }
-
- final int maxlen = Math.min(basePathLen, pathLen);
- int pos = 0;
- for (; pos < maxlen && getPath().charAt(pos) == path.charAt(pos); pos++) {
- // empty
- }
-
- if (pos == basePathLen && pos == pathLen) {
- // Same names
- return ".";
- }
- if (pos == basePathLen && pos < pathLen && path.charAt(pos) == SEPARATOR_CHAR) {
- // A descendent of the base path
- return path.substring(pos + 1);
- }
-
- // Strip the common prefix off the path
- final StringBuilder buffer = new StringBuilder();
- if (pathLen > 1 && (pos < pathLen || getPath().charAt(pos) != SEPARATOR_CHAR)) {
- // Not a direct ancestor, need to back up
- pos = getPath().lastIndexOf(SEPARATOR_CHAR, pos);
- buffer.append(path.substring(pos));
- }
-
- // Prepend a '../' for each element in the base path past the common
- // prefix
- buffer.insert(0, "..");
- pos = getPath().indexOf(SEPARATOR_CHAR, pos + 1);
- while (pos != -1) {
- buffer.insert(0, "../");
- pos = getPath().indexOf(SEPARATOR_CHAR, pos + 1);
- }
-
- return buffer.toString();
- }
-
- /**
- * find the root of the file system.
- *
- * @return The root FileName.
- */
- @Override
- public FileName getRoot() {
- FileName root = this;
- while (root.getParent() != null) {
- root = root.getParent();
- }
-
- return root;
- }
-
- /**
- * Returns the root URI of the file system this file belongs to.
- *
- * @return The URI of the root.
- */
- @Override
- public String getRootURI() {
- if (rootUri == null) {
- final StringBuilder buffer = new StringBuilder();
- appendRootUri(buffer, true);
- buffer.append(SEPARATOR_CHAR);
- rootUri = buffer.toString().intern();
- }
- return rootUri;
- }
-
- /**
- * Returns the URI scheme of this file.
- *
- * @return The protocol used to access the file.
- */
- @Override
- public String getScheme() {
- return scheme;
- }
-
- /**
- * Returns the requested or current type of this name.
- * <p>
- * The "requested" type is the one determined during resolving the name. n this case the name is a
- * {@link FileType#FOLDER} if it ends with an "/" else it will be a {@link FileType#FILE}.
- * </p>
- * <p>
- * Once attached it will be changed to reflect the real type of this resource.
- * </p>
- *
- * @return {@link FileType#FOLDER} or {@link FileType#FILE}
- */
- @Override
- public FileType getType() {
- return type;
- }
-
- /**
- * Returns the absolute URI of the file.
- *
- * @return The absolute URI of the file.
- */
- @Override
- public String getURI() {
- if (uriString == null) {
- uriString = createURI();
- }
- return uriString;
- }
-
- protected String getUriTrailer() {
- return getType().hasChildren() ? "/" : "";
- }
-
- private String handleURISpecialCharacters(String uri) {
- if (!StringUtils.isEmpty(uri)) {
- try {
- // VFS-325: Handle URI special characters in file name
- // Decode the base URI and re-encode with URI special characters
- uri = UriParser.decode(uri);
-
- return UriParser.encode(uri, RESERVED_URI_CHARS);
- } catch (final FileSystemException e) {
- // Default to base URI value?
- return uri;
- }
- }
-
- return uri;
- }
-
- @Override
- public int hashCode() {
- return getKey().hashCode();
- }
-
- /**
- * Determines if another file name is an ancestor of this file name.
- *
- * @param ancestor The FileName to check.
- * @return true if the FileName is an ancestor, false otherwise.
- */
- @Override
- public boolean isAncestor(final FileName ancestor) {
- if (!ancestor.getRootURI().equals(getRootURI())) {
- return false;
- }
- return checkName(ancestor.getPath(), getPath(), NameScope.DESCENDENT);
- }
-
- /**
- * Determines if another file name is a descendent of this file name.
- *
- * @param descendent The FileName to check.
- * @return true if the FileName is a descendent, false otherwise.
- */
- @Override
- public boolean isDescendent(final FileName descendent) {
- return isDescendent(descendent, NameScope.DESCENDENT);
- }
-
- /**
- * Determines if another file name is a descendent of this file name.
- *
- * @param descendent The FileName to check.
- * @param scope The NameScope.
- * @return true if the FileName is a descendent, false otherwise.
- */
- @Override
- public boolean isDescendent(final FileName descendent, final NameScope scope) {
- if (!descendent.getRootURI().equals(getRootURI())) {
- return false;
- }
- return checkName(getPath(), descendent.getPath(), scope);
- }
-
- /**
- * Checks if this file name is a name for a regular file by using its type.
- *
- * @return true if this file is a regular file.
- * @throws FileSystemException may be thrown by subclasses.
- * @see #getType()
- * @see FileType#FILE
- */
- @Override
- public boolean isFile() throws FileSystemException {
- // Use equals instead of == to avoid any class loader worries.
- return FileType.FILE.equals(this.getType());
- }
-
- /**
- * Sets the type of this file e.g. when it will be attached.
- *
- * @param type {@link FileType#FOLDER} or {@link FileType#FILE}
- * @throws FileSystemException if an error occurs.
- */
- void setType(final FileType type) throws FileSystemException {
- if (type != FileType.FOLDER && type != FileType.FILE && type != FileType.FILE_OR_FOLDER) {
- throw new FileSystemException("vfs.provider/filename-type.error");
- }
-
- this.type = type;
- }
-
- /**
- * Returns the URI of the file.
- *
- * @return the FileName as a URI.
- */
- @Override
- public String toString() {
- return getURI();
- }
-}
+/*
+ * 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.commons.vfs2.provider;
+
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.vfs2.FileName;
+import org.apache.commons.vfs2.FileSystemException;
+import org.apache.commons.vfs2.FileType;
+import org.apache.commons.vfs2.NameScope;
+import org.apache.commons.vfs2.VFS;
+
+/**
+ * A default file name implementation.
+ */
+public abstract class AbstractFileName implements FileName {
+
+ // URI Characters that are possible in local file names, but must be escaped
+ // for proper URI handling.
+ //
+ // How reserved URI chars were selected:
+ //
+ // URIs can contain :, /, ?, #, @
+ // See https://docs.oracle.com/javase/8/docs/api/java/net/URI.html
+ // http://tools.ietf.org/html/rfc3986#section-2.2
+ //
+ // Since : and / occur before the path, only chars after path are escaped (i.e., # and ?)
+ // ? is a reserved filesystem character for Windows and Unix, so can't be part of a file name.
+ // Therefore only # is a reserved char in a URI as part of the path that can be in the file name.
+ private static final char[] RESERVED_URI_CHARS = {'#', ' '};
+
+ private final String scheme;
+ private final String absPath;
+ private FileType type;
+
+ // Cached attributes
+ private String uriString;
+ private String baseName;
+ private String rootUri;
+ private String extension;
+ private String decodedAbsPath;
+
+ private String key;
+
+ /**
+ * Constructs a new instance.
+ *
+ * @param scheme The scheme.
+ * @param absolutePath the absolute path, maybe empty or null.
+ * @param type the file type.
+ */
+ public AbstractFileName(final String scheme, final String absolutePath, final FileType type) {
+ this.rootUri = null;
+ this.scheme = scheme;
+ this.type = type;
+ if (StringUtils.isEmpty(absolutePath)) {
+ this.absPath = ROOT_PATH;
+ } else if (absolutePath.length() > 1 && absolutePath.endsWith("/")) {
+ this.absPath = absolutePath.substring(0, absolutePath.length() - 1);
+ } else {
+ this.absPath = absolutePath;
+ }
+ }
+
+ /**
+ * Checks whether a path fits in a particular scope of another path.
+ *
+ * @param basePath An absolute, normalised path.
+ * @param path An absolute, normalised path.
+ * @param scope The NameScope.
+ * @return true if the path fits in the scope, false otherwise.
+ */
+ public static boolean checkName(final String basePath, final String path, final NameScope scope) {
+ if (scope == NameScope.FILE_SYSTEM) {
+ // All good
+ return true;
+ }
+
+ if (!path.startsWith(basePath)) {
+ return false;
+ }
+
+ int baseLen = basePath.length();
+ if (VFS.isUriStyle()) {
+ // strip the trailing "/"
+ baseLen--;
+ }
+
+ if (scope == NameScope.CHILD) {
+ return path.length() != baseLen && (baseLen <= 1 || path.charAt(baseLen) == SEPARATOR_CHAR)
+ && path.indexOf(SEPARATOR_CHAR, baseLen + 1) == -1;
+ }
+ if (scope == NameScope.DESCENDENT) {
+ return path.length() != baseLen && (baseLen <= 1 || path.charAt(baseLen) == SEPARATOR_CHAR);
+ }
+ if (scope == NameScope.DESCENDENT_OR_SELF) {
+ return baseLen <= 1 || path.length() <= baseLen || path.charAt(baseLen) == SEPARATOR_CHAR;
+ }
+ throw new IllegalArgumentException();
+ }
+
+ /**
+ * Builds the root URI for this file name. Note that the root URI must not end with a separator character.
+ *
+ * @param buffer A StringBuilder to use to construct the URI.
+ * @param addPassword true if the password should be added, false otherwise.
+ */
+ protected abstract void appendRootUri(StringBuilder buffer, boolean addPassword);
+
+ /**
+ * Implement Comparable.
+ *
+ * @param obj another abstract file name
+ * @return negative number if less than, 0 if equal, positive if greater than.
+ */
+ @Override
+ public int compareTo(final FileName obj) {
+ final AbstractFileName name = (AbstractFileName) obj;
+ return getKey().compareTo(name.getKey());
+ }
+
+ /**
+ * Factory method for creating name instances.
+ *
+ * @param absolutePath The absolute path.
+ * @param fileType The FileType.
+ * @return The FileName.
+ */
+ public abstract FileName createName(String absolutePath, FileType fileType);
+
+ protected String createURI() {
+ return createURI(false, true);
+ }
+
+ private String createURI(final boolean useAbsolutePath, final boolean usePassword) {
+ final StringBuilder buffer = new StringBuilder();
+ appendRootUri(buffer, usePassword);
+ buffer.append(handleURISpecialCharacters(useAbsolutePath ? absPath : getPath()));
+ return buffer.toString();
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+
+ final AbstractFileName that = (AbstractFileName) o;
+
+ return getKey().equals(that.getKey());
+ }
+
+ /**
+ * Returns the base name of the file.
+ *
+ * @return The base name of the file.
+ */
+ @Override
+ public String getBaseName() {
+ if (baseName == null) {
+ final int idx = getPath().lastIndexOf(SEPARATOR_CHAR);
+ if (idx == -1) {
+ baseName = getPath();
+ } else {
+ baseName = getPath().substring(idx + 1);
+ }
+ }
+
+ return baseName;
+ }
+
+ /**
+ * Returns the depth of this file name, within its file system.
+ *
+ * @return The depth of the file name.
+ */
+ @Override
+ public int getDepth() {
+ final int len = getPath().length();
+ if (len == 0 || len == 1 && getPath().charAt(0) == SEPARATOR_CHAR) {
+ return 0;
+ }
+ int depth = 1;
+ for (int pos = 0; pos > -1 && pos < len; depth++) {
+ pos = getPath().indexOf(SEPARATOR_CHAR, pos + 1);
+ }
+ return depth;
+ }
+
+ /**
+ * Returns the extension of this file name.
+ *
+ * @return The file extension.
+ */
+ @Override
+ public String getExtension() {
+ if (extension == null) {
+ getBaseName();
+ final int pos = baseName.lastIndexOf('.');
+ // if ((pos == -1) || (pos == baseName.length() - 1))
+ // imario@ops.co.at: Review of patch from adagoubard@chello.nl
+ // do not treat file names like
+ // .bashrc c:\windows\.java c:\windows\.javaws c:\windows\.jedit c:\windows\.appletviewer
+ // as extension
+ if (pos < 1 || pos == baseName.length() - 1) {
+ // No extension
+ extension = "";
+ } else {
+ extension = baseName.substring(pos + 1).intern();
+ }
+ }
+ return extension;
+ }
+
+ /**
+ * Returns the URI without a password.
+ *
+ * @return Returns the URI without a password.
+ */
+ @Override
+ public String getFriendlyURI() {
+ return createURI(false, false);
+ }
+
+ /**
+ * Create a path that does not use the FileType since that field is not immutable.
+ *
+ * @return The key.
+ */
+ private String getKey() {
+ if (key == null) {
+ key = getURI();
+ }
+ return key;
+ }
+
+ /**
+ * Returns the name of the parent of the file.
+ *
+ * @return the FileName of the parent.
+ */
+ @Override
+ public FileName getParent() {
+ final String parentPath;
+ final int idx = getPath().lastIndexOf(SEPARATOR_CHAR);
+ if (idx == -1 || idx == getPath().length() - 1) {
+ // No parent
+ return null;
+ }
+ if (idx == 0) {
+ // Root is the parent
+ parentPath = SEPARATOR;
+ } else {
+ parentPath = getPath().substring(0, idx);
+ }
+ return createName(parentPath, FileType.FOLDER);
+ }
+
+ /**
+ * Returns the absolute path of the file, relative to the root of the file system that the file belongs to.
+ *
+ * @return The path String.
+ */
+ @Override
+ public String getPath() {
+ if (VFS.isUriStyle()) {
+ return absPath + getUriTrailer();
+ }
+ return absPath;
+ }
+
+ /**
+ * Returns the decoded path.
+ *
+ * @return The decoded path String.
+ * @throws FileSystemException If an error occurs.
+ */
+ @Override
+ public String getPathDecoded() throws FileSystemException {
+ if (decodedAbsPath == null) {
+ decodedAbsPath = UriParser.decode(getPath());
+ }
+
+ return decodedAbsPath;
+ }
+
+ /**
+ * Converts a file name to a relative name, relative to this file name.
+ *
+ * @param name The FileName.
+ * @return The relative path to the file.
+ * @throws FileSystemException if an error occurs.
+ */
+ @Override
+ public String getRelativeName(final FileName name) throws FileSystemException {
+ final String path = name.getPath();
+
+ // Calculate the common prefix
+ final int basePathLen = getPath().length();
+ final int pathLen = path.length();
+
+ // Deal with root
+ if (basePathLen == 1 && pathLen == 1) {
+ return ".";
+ }
+ if (basePathLen == 1) {
+ return path.substring(1);
+ }
+
+ final int maxlen = Math.min(basePathLen, pathLen);
+ int pos = 0;
+ while (pos < maxlen && getPath().charAt(pos) == path.charAt(pos)) {
+ pos++;
+ }
+
+ if (pos == basePathLen && pos == pathLen) {
+ // Same names
+ return ".";
+ }
+ if (pos == basePathLen && pos < pathLen && path.charAt(pos) == SEPARATOR_CHAR) {
+ // A descendent of the base path
+ return path.substring(pos + 1);
+ }
+
+ // Strip the common prefix off the path
+ final StringBuilder buffer = new StringBuilder();
+ if (pathLen > 1 && (pos < pathLen || getPath().charAt(pos) != SEPARATOR_CHAR)) {
+ // Not a direct ancestor, need to back up
+ pos = getPath().lastIndexOf(SEPARATOR_CHAR, pos);
+ buffer.append(path.substring(pos));
+ }
+
+ // Prepend a '../' for each element in the base path past the common
+ // prefix
+ buffer.insert(0, "..");
+ pos = getPath().indexOf(SEPARATOR_CHAR, pos + 1);
+ while (pos != -1) {
+ buffer.insert(0, "../");
+ pos = getPath().indexOf(SEPARATOR_CHAR, pos + 1);
+ }
+
+ return buffer.toString();
+ }
+
+ /**
+ * find the root of the file system.
+ *
+ * @return The root FileName.
+ */
+ @Override
+ public FileName getRoot() {
+ FileName root = this;
+ while (root.getParent() != null) {
+ root = root.getParent();
+ }
+
+ return root;
+ }
+
+ /**
+ * Returns the root URI of the file system this file belongs to.
+ *
+ * @return The URI of the root.
+ */
+ @Override
+ public String getRootURI() {
+ if (rootUri == null) {
+ final StringBuilder buffer = new StringBuilder();
+ appendRootUri(buffer, true);
+ buffer.append(SEPARATOR_CHAR);
+ rootUri = buffer.toString().intern();
+ }
+ return rootUri;
+ }
+
+ /**
+ * Returns the URI scheme of this file.
+ *
+ * @return The protocol used to access the file.
+ */
+ @Override
+ public String getScheme() {
+ return scheme;
+ }
+
+ /**
+ * Returns the requested or current type of this name.
+ * <p>
+ * The "requested" type is the one determined during resolving the name. n this case the name is a
+ * {@link FileType#FOLDER} if it ends with an "/" else it will be a {@link FileType#FILE}.
+ * </p>
+ * <p>
+ * Once attached it will be changed to reflect the real type of this resource.
+ * </p>
+ *
+ * @return {@link FileType#FOLDER} or {@link FileType#FILE}
+ */
+ @Override
+ public FileType getType() {
+ return type;
+ }
+
+ /**
+ * Returns the absolute URI of the file.
+ *
+ * @return The absolute URI of the file.
+ */
+ @Override
+ public String getURI() {
+ if (uriString == null) {
+ uriString = createURI();
+ }
+ return uriString;
+ }
+
+ protected String getUriTrailer() {
+ return getType().hasChildren() ? "/" : "";
+ }
+
+ private String handleURISpecialCharacters(String uri) {
+ if (!StringUtils.isEmpty(uri)) {
+ try {
+ // VFS-325: Handle URI special characters in file name
+ // Decode the base URI and re-encode with URI special characters
+ uri = UriParser.decode(uri);
+
+ return UriParser.encode(uri, RESERVED_URI_CHARS);
+ } catch (final FileSystemException e) {
+ // Default to base URI value?
+ return uri;
+ }
+ }
+
+ return uri;
+ }
+
+ @Override
+ public int hashCode() {
+ return getKey().hashCode();
+ }
+
+ /**
+ * Determines if another file name is an ancestor of this file name.
+ *
+ * @param ancestor The FileName to check.
+ * @return true if the FileName is an ancestor, false otherwise.
+ */
+ @Override
+ public boolean isAncestor(final FileName ancestor) {
+ if (!ancestor.getRootURI().equals(getRootURI())) {
+ return false;
+ }
+ return checkName(ancestor.getPath(), getPath(), NameScope.DESCENDENT);
+ }
+
+ /**
+ * Determines if another file name is a descendent of this file name.
+ *
+ * @param descendent The FileName to check.
+ * @return true if the FileName is a descendent, false otherwise.
+ */
+ @Override
+ public boolean isDescendent(final FileName descendent) {
+ return isDescendent(descendent, NameScope.DESCENDENT);
+ }
+
+ /**
+ * Determines if another file name is a descendent of this file name.
+ *
+ * @param descendent The FileName to check.
+ * @param scope The NameScope.
+ * @return true if the FileName is a descendent, false otherwise.
+ */
+ @Override
+ public boolean isDescendent(final FileName descendent, final NameScope scope) {
+ if (!descendent.getRootURI().equals(getRootURI())) {
+ return false;
+ }
+ return checkName(getPath(), descendent.getPath(), scope);
+ }
+
+ /**
+ * Checks if this file name is a name for a regular file by using its type.
+ *
+ * @return true if this file is a regular file.
+ * @throws FileSystemException may be thrown by subclasses.
+ * @see #getType()
+ * @see FileType#FILE
+ */
+ @Override
+ public boolean isFile() throws FileSystemException {
+ // Use equals instead of == to avoid any class loader worries.
+ return FileType.FILE.equals(this.getType());
+ }
+
+ /**
+ * Sets the type of this file e.g. when it will be attached.
+ *
+ * @param type {@link FileType#FOLDER} or {@link FileType#FILE}
+ * @throws FileSystemException if an error occurs.
+ */
+ void setType(final FileType type) throws FileSystemException {
+ if (type != FileType.FOLDER && type != FileType.FILE && type != FileType.FILE_OR_FOLDER) {
+ throw new FileSystemException("vfs.provider/filename-type.error");
+ }
+
+ this.type = type;
+ }
+
+ /**
+ * Returns the URI of the file.
+ *
+ * @return the FileName as a URI.
+ */
+ @Override
+ public String toString() {
+ return getURI();
+ }
+}