1 /* 2 * Copyright (C) 2006 The Guava Authors 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14 15 package com.google.common.escape; 16 17 import static com.google.common.base.Preconditions.checkNotNull; 18 19 import com.google.common.annotations.GwtCompatible; 20 import com.google.errorprone.annotations.CanIgnoreReturnValue; 21 import java.util.HashMap; 22 import java.util.Map; 23 import java.util.Map.Entry; 24 import javax.annotation.CheckForNull; 25 import org.checkerframework.checker.nullness.qual.Nullable; 26 27 /** 28 * Simple helper class to build a "sparse" array of objects based on the indexes that were added to 29 * it. The array will be from 0 to the maximum index given. All non-set indexes will contain null 30 * (so it's not really a sparse array, just a pseudo sparse array). The builder can also return a 31 * CharEscaper based on the generated array. 32 * 33 * @author Sven Mawson 34 * @since 15.0 35 */ 36 @GwtCompatible 37 @ElementTypesAreNonnullByDefault 38 public final class CharEscaperBuilder { 39 /** 40 * Simple decorator that turns an array of replacement char[]s into a CharEscaper, this results in 41 * a very fast escape method. 42 */ 43 private static class CharArrayDecorator extends CharEscaper { 44 private final char[] @Nullable [] replacements; 45 private final int replaceLength; 46 CharArrayDecorator(char[] @Nullable [] replacements)47 CharArrayDecorator(char[] @Nullable [] replacements) { 48 this.replacements = replacements; 49 this.replaceLength = replacements.length; 50 } 51 52 /* 53 * Overriding escape method to be slightly faster for this decorator. We test the replacements 54 * array directly, saving a method call. 55 */ 56 @Override escape(String s)57 public String escape(String s) { 58 int slen = s.length(); 59 for (int index = 0; index < slen; index++) { 60 char c = s.charAt(index); 61 if (c < replacements.length && replacements[c] != null) { 62 return escapeSlow(s, index); 63 } 64 } 65 return s; 66 } 67 68 @Override 69 @CheckForNull escape(char c)70 protected char[] escape(char c) { 71 return c < replaceLength ? replacements[c] : null; 72 } 73 } 74 75 // Replacement mappings. 76 private final Map<Character, String> map; 77 78 // The highest index we've seen so far. 79 private int max = -1; 80 81 /** Construct a new sparse array builder. */ CharEscaperBuilder()82 public CharEscaperBuilder() { 83 this.map = new HashMap<>(); 84 } 85 86 /** Add a new mapping from an index to an object to the escaping. */ 87 @CanIgnoreReturnValue addEscape(char c, String r)88 public CharEscaperBuilder addEscape(char c, String r) { 89 map.put(c, checkNotNull(r)); 90 if (c > max) { 91 max = c; 92 } 93 return this; 94 } 95 96 /** Add multiple mappings at once for a particular index. */ 97 @CanIgnoreReturnValue addEscapes(char[] cs, String r)98 public CharEscaperBuilder addEscapes(char[] cs, String r) { 99 checkNotNull(r); 100 for (char c : cs) { 101 addEscape(c, r); 102 } 103 return this; 104 } 105 106 /** 107 * Convert this builder into an array of char[]s where the maximum index is the value of the 108 * highest character that has been seen. The array will be sparse in the sense that any unseen 109 * index will default to null. 110 * 111 * @return a "sparse" array that holds the replacement mappings. 112 */ toArray()113 public char[] @Nullable [] toArray() { 114 char[][] result = new char[max + 1][]; 115 for (Entry<Character, String> entry : map.entrySet()) { 116 result[entry.getKey()] = entry.getValue().toCharArray(); 117 } 118 return result; 119 } 120 121 /** 122 * Convert this builder into a char escaper which is just a decorator around the underlying array 123 * of replacement char[]s. 124 * 125 * @return an escaper that escapes based on the underlying array. 126 */ toEscaper()127 public Escaper toEscaper() { 128 return new CharArrayDecorator(toArray()); 129 } 130 } 131