Home | History | Annotate | Download | only in escape
      1 /*
      2  * Copyright (C) 2009 The Guava Authors
      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.common.escape;
     18 
     19 import static com.google.common.base.Preconditions.checkNotNull;
     20 
     21 import com.google.common.annotations.Beta;
     22 import com.google.common.annotations.GwtCompatible;
     23 import com.google.common.annotations.VisibleForTesting;
     24 
     25 import java.util.Collections;
     26 import java.util.Map;
     27 
     28 /**
     29  * An implementation-specific parameter class suitable for initializing
     30  * {@link ArrayBasedCharEscaper} or {@link ArrayBasedUnicodeEscaper} instances.
     31  * This class should be used when more than one escaper is created using the
     32  * same character replacement mapping to allow the underlying (implementation
     33  * specific) data structures to be shared.
     34  *
     35  * <p>The size of the data structure used by ArrayBasedCharEscaper and
     36  * ArrayBasedUnicodeEscaper is proportional to the highest valued character that
     37  * has a replacement. For example a replacement map containing the single
     38  * character '{@literal \}u1000' will require approximately 16K of memory.
     39  * As such sharing this data structure between escaper instances is the primary
     40  * goal of this class.
     41  *
     42  * @author David Beaumont
     43  * @since 15.0
     44  */
     45 @Beta
     46 @GwtCompatible
     47 public final class ArrayBasedEscaperMap {
     48   /**
     49    * Returns a new ArrayBasedEscaperMap for creating ArrayBasedCharEscaper or
     50    * ArrayBasedUnicodeEscaper instances.
     51    *
     52    * @param replacements a map of characters to their escaped representations
     53    */
     54   public static ArrayBasedEscaperMap create(
     55       Map<Character, String> replacements) {
     56     return new ArrayBasedEscaperMap(createReplacementArray(replacements));
     57   }
     58 
     59   // The underlying replacement array we can share between multiple escaper
     60   // instances.
     61   private final char[][] replacementArray;
     62 
     63   private ArrayBasedEscaperMap(char[][] replacementArray) {
     64     this.replacementArray = replacementArray;
     65   }
     66 
     67   // Returns the non-null array of replacements for fast lookup.
     68   char[][] getReplacementArray() {
     69     return replacementArray;
     70   }
     71 
     72   // Creates a replacement array from the given map. The returned array is a
     73   // linear lookup table of replacement character sequences indexed by the
     74   // original character value.
     75   @VisibleForTesting
     76   static char[][] createReplacementArray(Map<Character, String> map) {
     77     checkNotNull(map);  // GWT specific check (do not optimize)
     78     if (map.isEmpty()) {
     79       return EMPTY_REPLACEMENT_ARRAY;
     80     }
     81     char max = Collections.max(map.keySet());
     82     char[][] replacements = new char[max + 1][];
     83     for (char c : map.keySet()) {
     84       replacements[c] = map.get(c).toCharArray();
     85     }
     86     return replacements;
     87   }
     88 
     89   // Immutable empty array for when there are no replacements.
     90   private static final char[][] EMPTY_REPLACEMENT_ARRAY = new char[0][0];
     91 }
     92