• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2013 Google Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.common.jimfs;
18 
19 import com.google.common.base.Ascii;
20 import com.google.common.base.Function;
21 import com.ibm.icu.lang.UCharacter;
22 import java.text.Normalizer;
23 import java.util.regex.Pattern;
24 
25 /**
26  * Normalizations that can be applied to names in paths. Includes Unicode normalizations and
27  * normalizations for case insensitive paths. These normalizations can be set in {@code
28  * Configuration.Builder} when creating a Jimfs file system instance and are automatically applied
29  * to paths in the file system.
30  *
31  * @author Colin Decker
32  */
33 public enum PathNormalization implements Function<String, String> {
34 
35   /** No normalization. */
36   NONE(0) {
37     @Override
apply(String string)38     public String apply(String string) {
39       return string;
40     }
41   },
42 
43   /** Unicode composed normalization (form {@linkplain java.text.Normalizer.Form#NFC NFC}). */
NFC(Pattern.CANON_EQ)44   NFC(Pattern.CANON_EQ) {
45     @Override
46     public String apply(String string) {
47       return Normalizer.normalize(string, Normalizer.Form.NFC);
48     }
49   },
50 
51   /** Unicode decomposed normalization (form {@linkplain java.text.Normalizer.Form#NFD NFD}). */
NFD(Pattern.CANON_EQ)52   NFD(Pattern.CANON_EQ) {
53     @Override
54     public String apply(String string) {
55       return Normalizer.normalize(string, Normalizer.Form.NFD);
56     }
57   },
58 
59   /*
60    * Some notes on case folding/case insensitivity of file systems:
61    *
62    * In general (I don't have any counterexamples) case-insensitive file systems handle
63    * their case insensitivity in a locale-independent way. NTFS, for example, writes a
64    * special case mapping file ($UpCase) to the file system when it's first initialized,
65    * and this is not affected by the locale of either the user or the copy of Windows
66    * being used. This means that it will NOT handle i/I-variants in filenames as you'd
67    * expect for Turkic languages, even for a Turkish user who has installed a Turkish
68    * copy of Windows.
69    */
70 
71   /** Unicode case folding for case insensitive paths. Requires ICU4J on the classpath. */
72   CASE_FOLD_UNICODE(Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE) {
73     @Override
apply(String string)74     public String apply(String string) {
75       try {
76         return UCharacter.foldCase(string, true);
77       } catch (NoClassDefFoundError e) {
78         NoClassDefFoundError error =
79             new NoClassDefFoundError(
80                 "PathNormalization.CASE_FOLD_UNICODE requires ICU4J. "
81                     + "Did you forget to include it on your classpath?");
82         error.initCause(e);
83         throw error;
84       }
85     }
86   },
87 
88   /** ASCII case folding for simple case insensitive paths. */
CASE_FOLD_ASCII(Pattern.CASE_INSENSITIVE)89   CASE_FOLD_ASCII(Pattern.CASE_INSENSITIVE) {
90     @Override
91     public String apply(String string) {
92       return Ascii.toLowerCase(string);
93     }
94   };
95 
96   private final int patternFlags;
97 
PathNormalization(int patternFlags)98   private PathNormalization(int patternFlags) {
99     this.patternFlags = patternFlags;
100   }
101 
102   /** Applies this normalization to the given string, returning the normalized result. */
103   @Override
apply(String string)104   public abstract String apply(String string);
105 
106   /**
107    * Returns the flags that should be used when creating a regex {@link Pattern} in order to
108    * approximate this normalization.
109    */
patternFlags()110   public int patternFlags() {
111     return patternFlags;
112   }
113 
114   /**
115    * Applies the given normalizations to the given string in order, returning the normalized result.
116    */
normalize(String string, Iterable<PathNormalization> normalizations)117   public static String normalize(String string, Iterable<PathNormalization> normalizations) {
118     String result = string;
119     for (PathNormalization normalization : normalizations) {
120       result = normalization.apply(result);
121     }
122     return result;
123   }
124 
125   /** Compiles a regex pattern using flags based on the given normalizations. */
compilePattern(String regex, Iterable<PathNormalization> normalizations)126   public static Pattern compilePattern(String regex, Iterable<PathNormalization> normalizations) {
127     int flags = 0;
128     for (PathNormalization normalization : normalizations) {
129       flags |= normalization.patternFlags();
130     }
131     return Pattern.compile(regex, flags);
132   }
133 }
134