View Javadoc
1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty5.util;
17  
18  import io.netty5.util.concurrent.FastThreadLocal;
19  
20  import java.nio.charset.Charset;
21  import java.nio.charset.CharsetDecoder;
22  import java.nio.charset.CharsetEncoder;
23  import java.nio.charset.CodingErrorAction;
24  import java.nio.charset.StandardCharsets;
25  import java.util.IdentityHashMap;
26  import java.util.Map;
27  
28  import static java.util.Objects.requireNonNull;
29  
30  /**
31   * A utility class that provides various common operations and constants
32   * related with {@link Charset} and its relevant classes.
33   */
34  public final class CharsetUtil {
35  
36      private static final FastThreadLocal<Map<Charset, CharsetEncoder>> ENCODER_CACHE = new FastThreadLocal<>() {
37          @Override
38          protected Map<Charset, CharsetEncoder> initialValue() {
39              return new IdentityHashMap<>();
40          }
41      };
42  
43      private static final FastThreadLocal<Map<Charset, CharsetDecoder>> DECODER_CACHE = new FastThreadLocal<>() {
44          @Override
45          protected Map<Charset, CharsetDecoder> initialValue() {
46              return new IdentityHashMap<>();
47          }
48      };
49  
50      /**
51       * 16-bit UTF (UCS Transformation Format) whose byte order is identified by
52       * an optional byte-order mark
53       */
54      public static final Charset UTF_16 = StandardCharsets.UTF_16;
55  
56      /**
57       * 16-bit UTF (UCS Transformation Format) whose byte order is big-endian
58       */
59      public static final Charset UTF_16BE = StandardCharsets.UTF_16BE;
60  
61      /**
62       * 16-bit UTF (UCS Transformation Format) whose byte order is little-endian
63       */
64      public static final Charset UTF_16LE = StandardCharsets.UTF_16LE;
65  
66      /**
67       * 8-bit UTF (UCS Transformation Format)
68       */
69      public static final Charset UTF_8 = StandardCharsets.UTF_8;
70  
71      /**
72       * ISO Latin Alphabet No. 1, as known as <tt>ISO-LATIN-1</tt>
73       */
74      public static final Charset ISO_8859_1 = StandardCharsets.ISO_8859_1;
75  
76      /**
77       * 7-bit ASCII, as known as ISO646-US or the Basic Latin block of the
78       * Unicode character set
79       */
80      public static final Charset US_ASCII = StandardCharsets.US_ASCII;
81  
82      /**
83       * @deprecated Use {@link #encoder(Charset)}.
84       */
85      @Deprecated
86      public static CharsetEncoder getEncoder(Charset charset) {
87          return encoder(charset);
88      }
89  
90      /**
91       * Returns a new {@link CharsetEncoder} for the {@link Charset} with specified error actions.
92       *
93       * @param charset The specified charset
94       * @param malformedInputAction The encoder's action for malformed-input errors
95       * @param unmappableCharacterAction The encoder's action for unmappable-character errors
96       * @return The encoder for the specified {@code charset}
97       */
98      public static CharsetEncoder encoder(Charset charset, CodingErrorAction malformedInputAction,
99                                           CodingErrorAction unmappableCharacterAction) {
100         requireNonNull(charset, "charset");
101         CharsetEncoder e = charset.newEncoder();
102         e.onMalformedInput(malformedInputAction).onUnmappableCharacter(unmappableCharacterAction);
103         return e;
104     }
105 
106     /**
107      * Returns a new {@link CharsetEncoder} for the {@link Charset} with the specified error action.
108      *
109      * @param charset The specified charset
110      * @param codingErrorAction The encoder's action for malformed-input and unmappable-character errors
111      * @return The encoder for the specified {@code charset}
112      */
113     public static CharsetEncoder encoder(Charset charset, CodingErrorAction codingErrorAction) {
114         return encoder(charset, codingErrorAction, codingErrorAction);
115     }
116 
117     /**
118      * Returns a cached thread-local {@link CharsetEncoder} for the specified {@link Charset}.
119      *
120      * @param charset The specified charset
121      * @return The encoder for the specified {@code charset}
122      */
123     public static CharsetEncoder encoder(Charset charset) {
124         requireNonNull(charset, "charset");
125 
126         Map<Charset, CharsetEncoder> map = ENCODER_CACHE.get();
127         CharsetEncoder e = map.get(charset);
128         if (e != null) {
129             e.reset().onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE);
130             return e;
131         }
132 
133         e = encoder(charset, CodingErrorAction.REPLACE, CodingErrorAction.REPLACE);
134         map.put(charset, e);
135         return e;
136     }
137 
138     /**
139      * @deprecated Use {@link #decoder(Charset)}.
140      */
141     @Deprecated
142     public static CharsetDecoder getDecoder(Charset charset) {
143         return decoder(charset);
144     }
145 
146     /**
147      * Returns a new {@link CharsetDecoder} for the {@link Charset} with specified error actions.
148      *
149      * @param charset The specified charset
150      * @param malformedInputAction The decoder's action for malformed-input errors
151      * @param unmappableCharacterAction The decoder's action for unmappable-character errors
152      * @return The decoder for the specified {@code charset}
153      */
154     public static CharsetDecoder decoder(Charset charset, CodingErrorAction malformedInputAction,
155                                          CodingErrorAction unmappableCharacterAction) {
156         requireNonNull(charset, "charset");
157         CharsetDecoder d = charset.newDecoder();
158         d.onMalformedInput(malformedInputAction).onUnmappableCharacter(unmappableCharacterAction);
159         return d;
160     }
161 
162     /**
163      * Returns a new {@link CharsetDecoder} for the {@link Charset} with the specified error action.
164      *
165      * @param charset The specified charset
166      * @param codingErrorAction The decoder's action for malformed-input and unmappable-character errors
167      * @return The decoder for the specified {@code charset}
168      */
169     public static CharsetDecoder decoder(Charset charset, CodingErrorAction codingErrorAction) {
170         return decoder(charset, codingErrorAction, codingErrorAction);
171     }
172 
173     /**
174      * Returns a cached thread-local {@link CharsetDecoder} for the specified {@link Charset}.
175      *
176      * @param charset The specified charset
177      * @return The decoder for the specified {@code charset}
178      */
179     public static CharsetDecoder decoder(Charset charset) {
180         requireNonNull(charset, "charset");
181 
182         Map<Charset, CharsetDecoder> map = DECODER_CACHE.get();
183         CharsetDecoder d = map.get(charset);
184         if (d != null) {
185             d.reset().onMalformedInput(CodingErrorAction.REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE);
186             return d;
187         }
188 
189         d = decoder(charset, CodingErrorAction.REPLACE, CodingErrorAction.REPLACE);
190         map.put(charset, d);
191         return d;
192     }
193 
194     private CharsetUtil() { }
195 }