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 }