10888a09821a98ac0680fad765217302858e70fa4Paul Duffin/* 20888a09821a98ac0680fad765217302858e70fa4Paul Duffin * Copyright (C) 2009 The Guava Authors 30888a09821a98ac0680fad765217302858e70fa4Paul Duffin * 40888a09821a98ac0680fad765217302858e70fa4Paul Duffin * Licensed under the Apache License, Version 2.0 (the "License"); 50888a09821a98ac0680fad765217302858e70fa4Paul Duffin * you may not use this file except in compliance with the License. 60888a09821a98ac0680fad765217302858e70fa4Paul Duffin * You may obtain a copy of the License at 70888a09821a98ac0680fad765217302858e70fa4Paul Duffin * 80888a09821a98ac0680fad765217302858e70fa4Paul Duffin * http://www.apache.org/licenses/LICENSE-2.0 90888a09821a98ac0680fad765217302858e70fa4Paul Duffin * 100888a09821a98ac0680fad765217302858e70fa4Paul Duffin * Unless required by applicable law or agreed to in writing, software 110888a09821a98ac0680fad765217302858e70fa4Paul Duffin * distributed under the License is distributed on an "AS IS" BASIS, 120888a09821a98ac0680fad765217302858e70fa4Paul Duffin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 130888a09821a98ac0680fad765217302858e70fa4Paul Duffin * See the License for the specific language governing permissions and 140888a09821a98ac0680fad765217302858e70fa4Paul Duffin * limitations under the License. 150888a09821a98ac0680fad765217302858e70fa4Paul Duffin */ 160888a09821a98ac0680fad765217302858e70fa4Paul Duffin 170888a09821a98ac0680fad765217302858e70fa4Paul Duffinpackage com.google.common.escape; 180888a09821a98ac0680fad765217302858e70fa4Paul Duffin 190888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport static com.google.common.base.Preconditions.checkNotNull; 200888a09821a98ac0680fad765217302858e70fa4Paul Duffin 210888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport com.google.common.annotations.Beta; 220888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport com.google.common.annotations.GwtCompatible; 230888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport com.google.common.annotations.VisibleForTesting; 240888a09821a98ac0680fad765217302858e70fa4Paul Duffin 250888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport java.util.Collections; 260888a09821a98ac0680fad765217302858e70fa4Paul Duffinimport java.util.Map; 270888a09821a98ac0680fad765217302858e70fa4Paul Duffin 280888a09821a98ac0680fad765217302858e70fa4Paul Duffin/** 290888a09821a98ac0680fad765217302858e70fa4Paul Duffin * An implementation-specific parameter class suitable for initializing 300888a09821a98ac0680fad765217302858e70fa4Paul Duffin * {@link ArrayBasedCharEscaper} or {@link ArrayBasedUnicodeEscaper} instances. 310888a09821a98ac0680fad765217302858e70fa4Paul Duffin * This class should be used when more than one escaper is created using the 320888a09821a98ac0680fad765217302858e70fa4Paul Duffin * same character replacement mapping to allow the underlying (implementation 330888a09821a98ac0680fad765217302858e70fa4Paul Duffin * specific) data structures to be shared. 340888a09821a98ac0680fad765217302858e70fa4Paul Duffin * 350888a09821a98ac0680fad765217302858e70fa4Paul Duffin * <p>The size of the data structure used by ArrayBasedCharEscaper and 360888a09821a98ac0680fad765217302858e70fa4Paul Duffin * ArrayBasedUnicodeEscaper is proportional to the highest valued character that 370888a09821a98ac0680fad765217302858e70fa4Paul Duffin * has a replacement. For example a replacement map containing the single 380888a09821a98ac0680fad765217302858e70fa4Paul Duffin * character '{@literal \}u1000' will require approximately 16K of memory. 390888a09821a98ac0680fad765217302858e70fa4Paul Duffin * As such sharing this data structure between escaper instances is the primary 400888a09821a98ac0680fad765217302858e70fa4Paul Duffin * goal of this class. 410888a09821a98ac0680fad765217302858e70fa4Paul Duffin * 420888a09821a98ac0680fad765217302858e70fa4Paul Duffin * @author David Beaumont 430888a09821a98ac0680fad765217302858e70fa4Paul Duffin * @since 15.0 440888a09821a98ac0680fad765217302858e70fa4Paul Duffin */ 450888a09821a98ac0680fad765217302858e70fa4Paul Duffin@Beta 460888a09821a98ac0680fad765217302858e70fa4Paul Duffin@GwtCompatible 470888a09821a98ac0680fad765217302858e70fa4Paul Duffinpublic final class ArrayBasedEscaperMap { 480888a09821a98ac0680fad765217302858e70fa4Paul Duffin /** 490888a09821a98ac0680fad765217302858e70fa4Paul Duffin * Returns a new ArrayBasedEscaperMap for creating ArrayBasedCharEscaper or 500888a09821a98ac0680fad765217302858e70fa4Paul Duffin * ArrayBasedUnicodeEscaper instances. 510888a09821a98ac0680fad765217302858e70fa4Paul Duffin * 520888a09821a98ac0680fad765217302858e70fa4Paul Duffin * @param replacements a map of characters to their escaped representations 530888a09821a98ac0680fad765217302858e70fa4Paul Duffin */ 540888a09821a98ac0680fad765217302858e70fa4Paul Duffin public static ArrayBasedEscaperMap create( 550888a09821a98ac0680fad765217302858e70fa4Paul Duffin Map<Character, String> replacements) { 560888a09821a98ac0680fad765217302858e70fa4Paul Duffin return new ArrayBasedEscaperMap(createReplacementArray(replacements)); 570888a09821a98ac0680fad765217302858e70fa4Paul Duffin } 580888a09821a98ac0680fad765217302858e70fa4Paul Duffin 590888a09821a98ac0680fad765217302858e70fa4Paul Duffin // The underlying replacement array we can share between multiple escaper 600888a09821a98ac0680fad765217302858e70fa4Paul Duffin // instances. 610888a09821a98ac0680fad765217302858e70fa4Paul Duffin private final char[][] replacementArray; 620888a09821a98ac0680fad765217302858e70fa4Paul Duffin 630888a09821a98ac0680fad765217302858e70fa4Paul Duffin private ArrayBasedEscaperMap(char[][] replacementArray) { 640888a09821a98ac0680fad765217302858e70fa4Paul Duffin this.replacementArray = replacementArray; 650888a09821a98ac0680fad765217302858e70fa4Paul Duffin } 660888a09821a98ac0680fad765217302858e70fa4Paul Duffin 670888a09821a98ac0680fad765217302858e70fa4Paul Duffin // Returns the non-null array of replacements for fast lookup. 680888a09821a98ac0680fad765217302858e70fa4Paul Duffin char[][] getReplacementArray() { 690888a09821a98ac0680fad765217302858e70fa4Paul Duffin return replacementArray; 700888a09821a98ac0680fad765217302858e70fa4Paul Duffin } 710888a09821a98ac0680fad765217302858e70fa4Paul Duffin 720888a09821a98ac0680fad765217302858e70fa4Paul Duffin // Creates a replacement array from the given map. The returned array is a 730888a09821a98ac0680fad765217302858e70fa4Paul Duffin // linear lookup table of replacement character sequences indexed by the 740888a09821a98ac0680fad765217302858e70fa4Paul Duffin // original character value. 750888a09821a98ac0680fad765217302858e70fa4Paul Duffin @VisibleForTesting 760888a09821a98ac0680fad765217302858e70fa4Paul Duffin static char[][] createReplacementArray(Map<Character, String> map) { 770888a09821a98ac0680fad765217302858e70fa4Paul Duffin checkNotNull(map); // GWT specific check (do not optimize) 780888a09821a98ac0680fad765217302858e70fa4Paul Duffin if (map.isEmpty()) { 790888a09821a98ac0680fad765217302858e70fa4Paul Duffin return EMPTY_REPLACEMENT_ARRAY; 800888a09821a98ac0680fad765217302858e70fa4Paul Duffin } 810888a09821a98ac0680fad765217302858e70fa4Paul Duffin char max = Collections.max(map.keySet()); 820888a09821a98ac0680fad765217302858e70fa4Paul Duffin char[][] replacements = new char[max + 1][]; 830888a09821a98ac0680fad765217302858e70fa4Paul Duffin for (char c : map.keySet()) { 840888a09821a98ac0680fad765217302858e70fa4Paul Duffin replacements[c] = map.get(c).toCharArray(); 850888a09821a98ac0680fad765217302858e70fa4Paul Duffin } 860888a09821a98ac0680fad765217302858e70fa4Paul Duffin return replacements; 870888a09821a98ac0680fad765217302858e70fa4Paul Duffin } 880888a09821a98ac0680fad765217302858e70fa4Paul Duffin 890888a09821a98ac0680fad765217302858e70fa4Paul Duffin // Immutable empty array for when there are no replacements. 900888a09821a98ac0680fad765217302858e70fa4Paul Duffin private static final char[][] EMPTY_REPLACEMENT_ARRAY = new char[0][0]; 910888a09821a98ac0680fad765217302858e70fa4Paul Duffin} 92