jdk/src/windows/classes/sun/nio/fs/WindowsPathParser.java
changeset 2057 3acf8e5e2ca0
child 5506 202f599c92aa
equal deleted inserted replaced
2056:115e09b7a004 2057:3acf8e5e2ca0
       
     1 /*
       
     2  * Copyright 2008-2009 Sun Microsystems, Inc.  All Rights Reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.  Sun designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Sun in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General Public License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
       
    22  * CA 95054 USA or visit www.sun.com if you need additional information or
       
    23  * have any questions.
       
    24  */
       
    25 
       
    26 package sun.nio.fs;
       
    27 
       
    28 import java.nio.file.InvalidPathException;
       
    29 
       
    30 /**
       
    31  * A parser of Windows path strings
       
    32  */
       
    33 
       
    34 class WindowsPathParser {
       
    35     private WindowsPathParser() { }
       
    36 
       
    37     /**
       
    38      * The result of a parse operation
       
    39      */
       
    40     static class Result {
       
    41         private final WindowsPathType type;
       
    42         private final String root;
       
    43         private final String path;
       
    44 
       
    45         Result(WindowsPathType type, String root, String path) {
       
    46             this.type = type;
       
    47             this.root = root;
       
    48             this.path = path;
       
    49         }
       
    50 
       
    51         /**
       
    52          * The path type
       
    53          */
       
    54         WindowsPathType type() {
       
    55             return type;
       
    56         }
       
    57 
       
    58         /**
       
    59          * The root component
       
    60          */
       
    61         String root() {
       
    62             return root;
       
    63         }
       
    64 
       
    65         /**
       
    66          * The normalized path (includes root)
       
    67          */
       
    68         String path() {
       
    69             return path;
       
    70         }
       
    71     }
       
    72 
       
    73     /**
       
    74      * Parses the given input as a Windows path
       
    75      */
       
    76     static Result parse(String input) {
       
    77         if (input == null || input.length() == 0)
       
    78             throw new InvalidPathException(input, "Empty or null path");
       
    79         return parse(input, true);
       
    80     }
       
    81 
       
    82     /**
       
    83      * Parses the given input as a Windows path where it is known that the
       
    84      * path is already normalized.
       
    85      */
       
    86     static Result parseNormalizedPath(String input) {
       
    87         return parse(input, false);
       
    88     }
       
    89 
       
    90     /**
       
    91      * Parses the given input as a Windows path.
       
    92      *
       
    93      * @param   requireToNormalize
       
    94      *          Indicates if the path requires to be normalized
       
    95      */
       
    96     private static Result parse(String input, boolean requireToNormalize) {
       
    97         String root = "";
       
    98         WindowsPathType type = null;
       
    99 
       
   100         int len = input.length();
       
   101         int off = 0;
       
   102         if (len > 1) {
       
   103             char c0 = input.charAt(0);
       
   104             char c1 = input.charAt(1);
       
   105             char c = 0;
       
   106             int next = 2;
       
   107             if (isSlash(c0) && isSlash(c1)) {
       
   108                 // UNC: We keep the first two slash, collapse all the
       
   109                 // following, then take the hostname and share name out,
       
   110                 // meanwhile collapsing all the redundant slashes.
       
   111                 type = WindowsPathType.UNC;
       
   112                 off = nextNonSlash(input, next, len);
       
   113                 next = nextSlash(input, off, len);
       
   114                 if (off == next)
       
   115                     throw new InvalidPathException(input, "UNC path is missing hostname");
       
   116                 String host = input.substring(off, next);  //host
       
   117                 off = nextNonSlash(input, next, len);
       
   118                 next = nextSlash(input, off, len);
       
   119                 if (off == next)
       
   120                     throw new InvalidPathException(input, "UNC path is missing sharename");
       
   121                 root = "\\\\" + host + "\\" + input.substring(off, next) + "\\";
       
   122                 off = next;
       
   123             } else {
       
   124                 if (isLetter(c0) && c1 == ':') {
       
   125                     root = input.substring(0, 2);
       
   126                     if (len > 2 && isSlash(input.charAt(2))) {
       
   127                         off = 3;
       
   128                         root += "\\";
       
   129                         type = WindowsPathType.ABSOLUTE;
       
   130                     } else {
       
   131                         off = 2;
       
   132                         type = WindowsPathType.DRIVE_RELATIVE;
       
   133                     }
       
   134                 }
       
   135             }
       
   136         }
       
   137         if (off == 0) {
       
   138             if (isSlash(input.charAt(0))) {
       
   139                 type = WindowsPathType.DIRECTORY_RELATIVE;
       
   140                 root = "\\";
       
   141             } else {
       
   142                 type = WindowsPathType.RELATIVE;
       
   143             }
       
   144         }
       
   145 
       
   146         if (requireToNormalize) {
       
   147             StringBuilder sb = new StringBuilder(input.length());
       
   148             sb.append(root);
       
   149             return new Result(type, root, normalize(sb, input, off));
       
   150         } else {
       
   151             return new Result(type, root, input);
       
   152         }
       
   153     }
       
   154 
       
   155     /**
       
   156      * Remove redundant slashes from the rest of the path, forcing all slashes
       
   157      * into the preferred slash.
       
   158     */
       
   159     private static String normalize(StringBuilder sb, String path, int off) {
       
   160         int len = path.length();
       
   161         off = nextNonSlash(path, off, len);
       
   162         int start = off;
       
   163         char lastC = 0;
       
   164         while (off < len) {
       
   165             char c = path.charAt(off);
       
   166             if (isSlash(c)) {
       
   167                 if (lastC == ' ')
       
   168                     throw new InvalidPathException(path,
       
   169                                                    "Trailing char <" + lastC + ">",
       
   170                                                    off - 1);
       
   171                 sb.append(path, start, off);
       
   172                 off = nextNonSlash(path, off, len);
       
   173                 if (off != len)   //no slash at the end of normalized path
       
   174                     sb.append('\\');
       
   175                 start = off;
       
   176             } else {
       
   177                 if (isInvalidPathChar(c))
       
   178                     throw new InvalidPathException(path,
       
   179                                                    "Illegal char <" + c + ">",
       
   180                                                    off);
       
   181                 lastC = c;
       
   182                 off++;
       
   183             }
       
   184         }
       
   185         if (start != off) {
       
   186             if (lastC == ' ')
       
   187                 throw new InvalidPathException(path,
       
   188                                                "Trailing char <" + lastC + ">",
       
   189                                                off - 1);
       
   190             sb.append(path, start, off);
       
   191         }
       
   192         return sb.toString();
       
   193     }
       
   194 
       
   195     private static final boolean isSlash(char c) {
       
   196         return (c == '\\') || (c == '/');
       
   197     }
       
   198 
       
   199     private static final int nextNonSlash(String path, int off, int end) {
       
   200         while (off < end && isSlash(path.charAt(off))) { off++; }
       
   201         return off;
       
   202     }
       
   203 
       
   204     private static final int nextSlash(String path, int off, int end) {
       
   205         char c;
       
   206         while (off < end && !isSlash(c=path.charAt(off))) {
       
   207             if (isInvalidPathChar(c))
       
   208                 throw new InvalidPathException(path,
       
   209                                                "Illegal character [" + c + "] in path",
       
   210                                                off);
       
   211             off++;
       
   212         }
       
   213         return off;
       
   214     }
       
   215 
       
   216     private static final boolean isLetter(char c) {
       
   217         return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
       
   218     }
       
   219 
       
   220     // Reserved characters for window path name
       
   221     private static final String reservedChars = "<>:\"|?*";
       
   222     private static final boolean isInvalidPathChar(char ch) {
       
   223         return ch < '\u0020' || reservedChars.indexOf(ch) != -1;
       
   224     }
       
   225 }