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