1 /** 2 * Copyright (c) 2006-2007, 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.android.mail.common.base; 18 19 import java.util.HashMap; 20 import java.util.Map; 21 22 /** 23 * Simple helper class to build a "sparse" array of objects based on the indexes that were added to 24 * it. The array will be from 0 to the maximum index given. All non-set indexes will contain null 25 * (so it's not really a sparse array, just a pseudo sparse array). The builder can also return a 26 * CharEscaper based on the generated array. 27 * 28 * @author sven@google.com (Sven Mawson) 29 */ 30 public class CharEscaperBuilder { 31 /** 32 * Simple decorator that turns an array of replacement char[]s into a CharEscaper, this results in 33 * a very fast escape method. 34 */ 35 private static class CharArrayDecorator extends CharEscaper { 36 private final char[][] replacements; 37 private final int replaceLength; 38 CharArrayDecorator(char[][] replacements)39 CharArrayDecorator(char[][] replacements) { 40 this.replacements = replacements; 41 this.replaceLength = replacements.length; 42 } 43 44 /* 45 * Overriding escape method to be slightly faster for this decorator. We test the replacements 46 * array directly, saving a method call. 47 */ escape(String s)48 @Override public String escape(String s) { 49 int slen = s.length(); 50 for (int index = 0; index < slen; index++) { 51 char c = s.charAt(index); 52 if (c < replacements.length && replacements[c] != null) { 53 return escapeSlow(s, index); 54 } 55 } 56 return s; 57 } 58 escape(char c)59 @Override protected char[] escape(char c) { 60 return c < replaceLength ? replacements[c] : null; 61 } 62 } 63 64 // Replacement mappings. 65 private final Map<Character, String> map; 66 67 // The highest index we've seen so far. 68 private int max = -1; 69 70 /** 71 * Construct a new sparse array builder. 72 */ CharEscaperBuilder()73 public CharEscaperBuilder() { 74 this.map = new HashMap<Character, String>(); 75 } 76 77 /** 78 * Add a new mapping from an index to an object to the escaping. 79 */ addEscape(char c, String r)80 public CharEscaperBuilder addEscape(char c, String r) { 81 map.put(c, r); 82 if (c > max) { 83 max = c; 84 } 85 return this; 86 } 87 88 /** 89 * Add multiple mappings at once for a particular index. 90 */ addEscapes(char[] cs, String r)91 public CharEscaperBuilder addEscapes(char[] cs, String r) { 92 for (char c : cs) { 93 addEscape(c, r); 94 } 95 return this; 96 } 97 98 /** 99 * Convert this builder into an array of char[]s where the maximum index is the value of the 100 * highest character that has been seen. The array will be sparse in the sense that any unseen 101 * index will default to null. 102 * 103 * @return a "sparse" array that holds the replacement mappings. 104 */ toArray()105 public char[][] toArray() { 106 char[][] result = new char[max + 1][]; 107 for (Map.Entry<Character, String> entry : map.entrySet()) { 108 result[entry.getKey()] = entry.getValue().toCharArray(); 109 } 110 return result; 111 } 112 113 /** 114 * Convert this builder into a char escaper which is just a decorator around the underlying array 115 * of replacement char[]s. 116 * 117 * @return an escaper that escapes based on the underlying array. 118 */ toEscaper()119 public CharEscaper toEscaper() { 120 return new CharArrayDecorator(toArray()); 121 } 122 }