• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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