View Javadoc

    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    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      // Cached stuff
    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         // return absPath.compareTo(name.absPath);
    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             // No parent
    189             return null;
    190         }
    191         else if (idx == 0)
    192         {
    193             // Root is the parent
    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         // Calculate the common prefix
    253         final int basePathLen = getPath().length();
    254         final int pathLen = path.length();
    255
    256         // Deal with root
    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             // Same names
    275             return ".";
    276         }
    277         else if (pos == basePathLen && pos < pathLen && path.charAt(pos) == SEPARATOR_CHAR)
    278         {
    279             // A descendent of the base path
    280             return path.substring(pos + 1);
    281         }
    282
    283         // Strip the common prefix off the path
    284         final StringBuffer buffer = new StringBuffer();
    285         if (pathLen > 1 && (pos < pathLen || getPath().charAt(pos) != SEPARATOR_CHAR))
    286         {
    287             // Not a direct ancestor, need to back up
    288             pos = getPath().lastIndexOf(SEPARATOR_CHAR, pos);
    289             buffer.append(path.substring(pos));
    290         }
    291
    292         // Prepend a '../' for each element in the base path past the common
    293         // prefix
    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             // if ((pos == -1) || (pos == baseName.length() - 1))
    348             // imario@ops.co.at: Review of patch from adagoubard@chello.nl
    349             // do not treat filenames like
    350             // .bashrc c:\windows\.java c:\windows\.javaws c:\windows\.jedit c:\windows\.appletviewer
    351             // as extension
    352             if ((pos < 1) || (pos == baseName.length() - 1))
    353             {
    354                 // No extension
    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             // All good
    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             // strip the trailing "/"
    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 }