001/* 002 * Copyright (C) 2009 The Guava Authors 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package com.google.common.escape; 018 019import static com.google.common.base.Preconditions.checkNotNull; 020 021import com.google.common.annotations.Beta; 022import com.google.common.annotations.GwtCompatible; 023import com.google.common.annotations.VisibleForTesting; 024 025import java.util.Collections; 026import java.util.Map; 027 028/** 029 * An implementation-specific parameter class suitable for initializing 030 * {@link ArrayBasedCharEscaper} or {@link ArrayBasedUnicodeEscaper} instances. 031 * This class should be used when more than one escaper is created using the 032 * same character replacement mapping to allow the underlying (implementation 033 * specific) data structures to be shared. 034 * 035 * <p>The size of the data structure used by ArrayBasedCharEscaper and 036 * ArrayBasedUnicodeEscaper is proportional to the highest valued character that 037 * has a replacement. For example a replacement map containing the single 038 * character '{@literal \}u1000' will require approximately 16K of memory. 039 * As such sharing this data structure between escaper instances is the primary 040 * goal of this class. 041 * 042 * @author David Beaumont 043 * @since 15.0 044 */ 045@Beta 046@GwtCompatible 047public final class ArrayBasedEscaperMap { 048 /** 049 * Returns a new ArrayBasedEscaperMap for creating ArrayBasedCharEscaper or 050 * ArrayBasedUnicodeEscaper instances. 051 * 052 * @param replacements a map of characters to their escaped representations 053 */ 054 public static ArrayBasedEscaperMap create( 055 Map<Character, String> replacements) { 056 return new ArrayBasedEscaperMap(createReplacementArray(replacements)); 057 } 058 059 // The underlying replacement array we can share between multiple escaper 060 // instances. 061 private final char[][] replacementArray; 062 063 private ArrayBasedEscaperMap(char[][] replacementArray) { 064 this.replacementArray = replacementArray; 065 } 066 067 // Returns the non-null array of replacements for fast lookup. 068 char[][] getReplacementArray() { 069 return replacementArray; 070 } 071 072 // Creates a replacement array from the given map. The returned array is a 073 // linear lookup table of replacement character sequences indexed by the 074 // original character value. 075 @VisibleForTesting 076 static char[][] createReplacementArray(Map<Character, String> map) { 077 checkNotNull(map); // GWT specific check (do not optimize) 078 if (map.isEmpty()) { 079 return EMPTY_REPLACEMENT_ARRAY; 080 } 081 char max = Collections.max(map.keySet()); 082 char[][] replacements = new char[max + 1][]; 083 for (char c : map.keySet()) { 084 replacements[c] = map.get(c).toCharArray(); 085 } 086 return replacements; 087 } 088 089 // Immutable empty array for when there are no replacements. 090 private static final char[][] EMPTY_REPLACEMENT_ARRAY = new char[0][0]; 091}