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