/* * Copyright (C) 2006 The Guava Authors * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.google.common.escape; import static com.google.common.base.Preconditions.checkNotNull; import com.google.common.annotations.Beta; import com.google.common.annotations.GwtCompatible; import java.util.HashMap; import java.util.Map; /** * Simple helper class to build a "sparse" array of objects based on the indexes that were added to * it. The array will be from 0 to the maximum index given. All non-set indexes will contain null * (so it's not really a sparse array, just a pseudo sparse array). The builder can also return a * CharEscaper based on the generated array. * * @author Sven Mawson * @since 15.0 */ @Beta @GwtCompatible public final class CharEscaperBuilder { /** * Simple decorator that turns an array of replacement char[]s into a CharEscaper, this results in * a very fast escape method. */ private static class CharArrayDecorator extends CharEscaper { private final char[][] replacements; private final int replaceLength; CharArrayDecorator(char[][] replacements) { this.replacements = replacements; this.replaceLength = replacements.length; } /* * Overriding escape method to be slightly faster for this decorator. We test the replacements * array directly, saving a method call. */ @Override public String escape(String s) { int slen = s.length(); for (int index = 0; index < slen; index++) { char c = s.charAt(index); if (c < replacements.length && replacements[c] != null) { return escapeSlow(s, index); } } return s; } @Override protected char[] escape(char c) { return c < replaceLength ? replacements[c] : null; } } // Replacement mappings. private final Map<Character, String> map; // The highest index we've seen so far. private int max = -1; /** * Construct a new sparse array builder. */ public CharEscaperBuilder() { this.map = new HashMap<Character, String>(); } /** * Add a new mapping from an index to an object to the escaping. */ public CharEscaperBuilder addEscape(char c, String r) { map.put(c, checkNotNull(r)); if (c > max) { max = c; } return this; } /** * Add multiple mappings at once for a particular index. */ public CharEscaperBuilder addEscapes(char[] cs, String r) { checkNotNull(r); for (char c : cs) { addEscape(c, r); } return this; } /** * Convert this builder into an array of char[]s where the maximum index is the value of the * highest character that has been seen. The array will be sparse in the sense that any unseen * index will default to null. * * @return a "sparse" array that holds the replacement mappings. */ public char[][] toArray() { char[][] result = new char[max + 1][]; for (Map.Entry<Character, String> entry : map.entrySet()) { result[entry.getKey()] = entry.getValue().toCharArray(); } return result; } /** * Convert this builder into a char escaper which is just a decorator around the underlying array * of replacement char[]s. * * @return an escaper that escapes based on the underlying array. */ public Escaper toEscaper() { return new CharArrayDecorator(toArray()); } }