jdk/src/share/demo/nio/zipfs/src/com/sun/nio/zipfs/ZipUtils.java
changeset 24414 2061862eb57c
parent 24413 1d117d2dfe92
parent 24077 0809c9a4d36e
child 24415 43aa54df554d
equal deleted inserted replaced
24413:1d117d2dfe92 24414:2061862eb57c
     1 /*
       
     2  * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
       
     3  *
       
     4  * Redistribution and use in source and binary forms, with or without
       
     5  * modification, are permitted provided that the following conditions
       
     6  * are met:
       
     7  *
       
     8  *   - Redistributions of source code must retain the above copyright
       
     9  *     notice, this list of conditions and the following disclaimer.
       
    10  *
       
    11  *   - Redistributions in binary form must reproduce the above copyright
       
    12  *     notice, this list of conditions and the following disclaimer in the
       
    13  *     documentation and/or other materials provided with the distribution.
       
    14  *
       
    15  *   - Neither the name of Oracle nor the names of its
       
    16  *     contributors may be used to endorse or promote products derived
       
    17  *     from this software without specific prior written permission.
       
    18  *
       
    19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
       
    20  * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
       
    21  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
       
    22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
       
    23  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
       
    24  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
       
    25  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
       
    26  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
       
    27  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
       
    28  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
       
    29  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
       
    30  */
       
    31 
       
    32 /*
       
    33  * This source code is provided to illustrate the usage of a given feature
       
    34  * or technique and has been deliberately simplified. Additional steps
       
    35  * required for a production-quality application, such as security checks,
       
    36  * input validation and proper error handling, might not be present in
       
    37  * this sample code.
       
    38  */
       
    39 
       
    40 
       
    41 package com.sun.nio.zipfs;
       
    42 
       
    43 import java.io.IOException;
       
    44 import java.io.OutputStream;
       
    45 import java.util.Arrays;
       
    46 import java.util.Date;
       
    47 import java.util.regex.PatternSyntaxException;
       
    48 import java.util.concurrent.TimeUnit;
       
    49 
       
    50 /**
       
    51  *
       
    52  * @author Xueming Shen
       
    53  */
       
    54 
       
    55 class ZipUtils {
       
    56 
       
    57     /*
       
    58      * Writes a 16-bit short to the output stream in little-endian byte order.
       
    59      */
       
    60     public static void writeShort(OutputStream os, int v) throws IOException {
       
    61         os.write(v & 0xff);
       
    62         os.write((v >>> 8) & 0xff);
       
    63     }
       
    64 
       
    65     /*
       
    66      * Writes a 32-bit int to the output stream in little-endian byte order.
       
    67      */
       
    68     public static void writeInt(OutputStream os, long v) throws IOException {
       
    69         os.write((int)(v & 0xff));
       
    70         os.write((int)((v >>>  8) & 0xff));
       
    71         os.write((int)((v >>> 16) & 0xff));
       
    72         os.write((int)((v >>> 24) & 0xff));
       
    73     }
       
    74 
       
    75     /*
       
    76      * Writes a 64-bit int to the output stream in little-endian byte order.
       
    77      */
       
    78     public static void writeLong(OutputStream os, long v) throws IOException {
       
    79         os.write((int)(v & 0xff));
       
    80         os.write((int)((v >>>  8) & 0xff));
       
    81         os.write((int)((v >>> 16) & 0xff));
       
    82         os.write((int)((v >>> 24) & 0xff));
       
    83         os.write((int)((v >>> 32) & 0xff));
       
    84         os.write((int)((v >>> 40) & 0xff));
       
    85         os.write((int)((v >>> 48) & 0xff));
       
    86         os.write((int)((v >>> 56) & 0xff));
       
    87     }
       
    88 
       
    89     /*
       
    90      * Writes an array of bytes to the output stream.
       
    91      */
       
    92     public static void writeBytes(OutputStream os, byte[] b)
       
    93         throws IOException
       
    94     {
       
    95         os.write(b, 0, b.length);
       
    96     }
       
    97 
       
    98     /*
       
    99      * Writes an array of bytes to the output stream.
       
   100      */
       
   101     public static void writeBytes(OutputStream os, byte[] b, int off, int len)
       
   102         throws IOException
       
   103     {
       
   104         os.write(b, off, len);
       
   105     }
       
   106 
       
   107     /*
       
   108      * Append a slash at the end, if it does not have one yet
       
   109      */
       
   110     public static byte[] toDirectoryPath(byte[] dir) {
       
   111         if (dir.length != 0 && dir[dir.length - 1] != '/') {
       
   112             dir = Arrays.copyOf(dir, dir.length + 1);
       
   113             dir[dir.length - 1] = '/';
       
   114         }
       
   115         return dir;
       
   116     }
       
   117 
       
   118     /*
       
   119      * Converts DOS time to Java time (number of milliseconds since epoch).
       
   120      */
       
   121     public static long dosToJavaTime(long dtime) {
       
   122         Date d = new Date((int)(((dtime >> 25) & 0x7f) + 80),
       
   123                           (int)(((dtime >> 21) & 0x0f) - 1),
       
   124                           (int)((dtime >> 16) & 0x1f),
       
   125                           (int)((dtime >> 11) & 0x1f),
       
   126                           (int)((dtime >> 5) & 0x3f),
       
   127                           (int)((dtime << 1) & 0x3e));
       
   128         return d.getTime();
       
   129     }
       
   130 
       
   131     /*
       
   132      * Converts Java time to DOS time.
       
   133      */
       
   134     public static long javaToDosTime(long time) {
       
   135         Date d = new Date(time);
       
   136         int year = d.getYear() + 1900;
       
   137         if (year < 1980) {
       
   138             return (1 << 21) | (1 << 16);
       
   139         }
       
   140         return (year - 1980) << 25 | (d.getMonth() + 1) << 21 |
       
   141                d.getDate() << 16 | d.getHours() << 11 | d.getMinutes() << 5 |
       
   142                d.getSeconds() >> 1;
       
   143     }
       
   144 
       
   145 
       
   146     // used to adjust values between Windows and java epoch
       
   147     private static final long WINDOWS_EPOCH_IN_MICROSECONDS = -11644473600000000L;
       
   148     public static final long winToJavaTime(long wtime) {
       
   149         return TimeUnit.MILLISECONDS.convert(
       
   150                wtime / 10 + WINDOWS_EPOCH_IN_MICROSECONDS, TimeUnit.MICROSECONDS);
       
   151     }
       
   152 
       
   153     public static final long javaToWinTime(long time) {
       
   154         return (TimeUnit.MICROSECONDS.convert(time, TimeUnit.MILLISECONDS)
       
   155                - WINDOWS_EPOCH_IN_MICROSECONDS) * 10;
       
   156     }
       
   157 
       
   158     public static final long unixToJavaTime(long utime) {
       
   159         return TimeUnit.MILLISECONDS.convert(utime, TimeUnit.SECONDS);
       
   160     }
       
   161 
       
   162     public static final long javaToUnixTime(long time) {
       
   163         return TimeUnit.SECONDS.convert(time, TimeUnit.MILLISECONDS);
       
   164     }
       
   165 
       
   166     private static final String regexMetaChars = ".^$+{[]|()";
       
   167     private static final String globMetaChars = "\\*?[{";
       
   168     private static boolean isRegexMeta(char c) {
       
   169         return regexMetaChars.indexOf(c) != -1;
       
   170     }
       
   171     private static boolean isGlobMeta(char c) {
       
   172         return globMetaChars.indexOf(c) != -1;
       
   173     }
       
   174     private static char EOL = 0;  //TBD
       
   175     private static char next(String glob, int i) {
       
   176         if (i < glob.length()) {
       
   177             return glob.charAt(i);
       
   178         }
       
   179         return EOL;
       
   180     }
       
   181 
       
   182     /*
       
   183      * Creates a regex pattern from the given glob expression.
       
   184      *
       
   185      * @throws  PatternSyntaxException
       
   186      */
       
   187     public static String toRegexPattern(String globPattern) {
       
   188         boolean inGroup = false;
       
   189         StringBuilder regex = new StringBuilder("^");
       
   190 
       
   191         int i = 0;
       
   192         while (i < globPattern.length()) {
       
   193             char c = globPattern.charAt(i++);
       
   194             switch (c) {
       
   195                 case '\\':
       
   196                     // escape special characters
       
   197                     if (i == globPattern.length()) {
       
   198                         throw new PatternSyntaxException("No character to escape",
       
   199                                 globPattern, i - 1);
       
   200                     }
       
   201                     char next = globPattern.charAt(i++);
       
   202                     if (isGlobMeta(next) || isRegexMeta(next)) {
       
   203                         regex.append('\\');
       
   204                     }
       
   205                     regex.append(next);
       
   206                     break;
       
   207                 case '/':
       
   208                     regex.append(c);
       
   209                     break;
       
   210                 case '[':
       
   211                     // don't match name separator in class
       
   212                     regex.append("[[^/]&&[");
       
   213                     if (next(globPattern, i) == '^') {
       
   214                         // escape the regex negation char if it appears
       
   215                         regex.append("\\^");
       
   216                         i++;
       
   217                     } else {
       
   218                         // negation
       
   219                         if (next(globPattern, i) == '!') {
       
   220                             regex.append('^');
       
   221                             i++;
       
   222                         }
       
   223                         // hyphen allowed at start
       
   224                         if (next(globPattern, i) == '-') {
       
   225                             regex.append('-');
       
   226                             i++;
       
   227                         }
       
   228                     }
       
   229                     boolean hasRangeStart = false;
       
   230                     char last = 0;
       
   231                     while (i < globPattern.length()) {
       
   232                         c = globPattern.charAt(i++);
       
   233                         if (c == ']') {
       
   234                             break;
       
   235                         }
       
   236                         if (c == '/') {
       
   237                             throw new PatternSyntaxException("Explicit 'name separator' in class",
       
   238                                     globPattern, i - 1);
       
   239                         }
       
   240                         // TBD: how to specify ']' in a class?
       
   241                         if (c == '\\' || c == '[' ||
       
   242                                 c == '&' && next(globPattern, i) == '&') {
       
   243                             // escape '\', '[' or "&&" for regex class
       
   244                             regex.append('\\');
       
   245                         }
       
   246                         regex.append(c);
       
   247 
       
   248                         if (c == '-') {
       
   249                             if (!hasRangeStart) {
       
   250                                 throw new PatternSyntaxException("Invalid range",
       
   251                                         globPattern, i - 1);
       
   252                             }
       
   253                             if ((c = next(globPattern, i++)) == EOL || c == ']') {
       
   254                                 break;
       
   255                             }
       
   256                             if (c < last) {
       
   257                                 throw new PatternSyntaxException("Invalid range",
       
   258                                         globPattern, i - 3);
       
   259                             }
       
   260                             regex.append(c);
       
   261                             hasRangeStart = false;
       
   262                         } else {
       
   263                             hasRangeStart = true;
       
   264                             last = c;
       
   265                         }
       
   266                     }
       
   267                     if (c != ']') {
       
   268                         throw new PatternSyntaxException("Missing ']", globPattern, i - 1);
       
   269                     }
       
   270                     regex.append("]]");
       
   271                     break;
       
   272                 case '{':
       
   273                     if (inGroup) {
       
   274                         throw new PatternSyntaxException("Cannot nest groups",
       
   275                                 globPattern, i - 1);
       
   276                     }
       
   277                     regex.append("(?:(?:");
       
   278                     inGroup = true;
       
   279                     break;
       
   280                 case '}':
       
   281                     if (inGroup) {
       
   282                         regex.append("))");
       
   283                         inGroup = false;
       
   284                     } else {
       
   285                         regex.append('}');
       
   286                     }
       
   287                     break;
       
   288                 case ',':
       
   289                     if (inGroup) {
       
   290                         regex.append(")|(?:");
       
   291                     } else {
       
   292                         regex.append(',');
       
   293                     }
       
   294                     break;
       
   295                 case '*':
       
   296                     if (next(globPattern, i) == '*') {
       
   297                         // crosses directory boundaries
       
   298                         regex.append(".*");
       
   299                         i++;
       
   300                     } else {
       
   301                         // within directory boundary
       
   302                         regex.append("[^/]*");
       
   303                     }
       
   304                     break;
       
   305                 case '?':
       
   306                    regex.append("[^/]");
       
   307                    break;
       
   308                 default:
       
   309                     if (isRegexMeta(c)) {
       
   310                         regex.append('\\');
       
   311                     }
       
   312                     regex.append(c);
       
   313             }
       
   314         }
       
   315         if (inGroup) {
       
   316             throw new PatternSyntaxException("Missing '}", globPattern, i - 1);
       
   317         }
       
   318         return regex.append('$').toString();
       
   319     }
       
   320 }