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();

+    }

+}