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 }