blob: 7ee2c3aa8e55b70ca25fe8c6bfd108ed1a5f1bdd [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2001-2005 Sun Microsystems, Inc. All Rights Reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.nio.cs.ext;
27
28import java.util.Collections;
29import java.util.ArrayList;
30import java.util.HashMap;
31import java.util.List;
32import java.util.Map;
33import java.nio.charset.*;
34
35final class CompoundTextSupport {
36
37 private static final class ControlSequence {
38
39 final int hash;
40 final byte[] escSequence;
41 final byte[] encoding;
42
43 ControlSequence(byte[] escSequence) {
44 this(escSequence, null);
45 }
46 ControlSequence(byte[] escSequence, byte[] encoding) {
47 if (escSequence == null) {
48 throw new NullPointerException();
49 }
50
51 this.escSequence = escSequence;
52 this.encoding = encoding;
53
54 int hash = 0;
55 int length = escSequence.length;
56
57 for (int i = 0; i < escSequence.length; i++) {
58 hash += (((int)escSequence[i]) & 0xff) << (i % 4);
59 }
60 if (encoding != null) {
61 for (int i = 0; i < encoding.length; i++) {
62 hash += (((int)encoding[i]) & 0xff) << (i % 4);
63 }
64 length += 2 /* M L */ + encoding.length + 1 /* 0x02 */;
65 }
66
67 this.hash = hash;
68
69 if (MAX_CONTROL_SEQUENCE_LEN < length) {
70 MAX_CONTROL_SEQUENCE_LEN = length;
71 }
72 }
73
74 public boolean equals(Object obj) {
75 if (this == obj) {
76 return true;
77 }
78 if (!(obj instanceof ControlSequence)) {
79 return false;
80 }
81 ControlSequence rhs = (ControlSequence)obj;
82 if (escSequence != rhs.escSequence) {
83 if (escSequence.length != rhs.escSequence.length) {
84 return false;
85 }
86 for (int i = 0; i < escSequence.length; i++) {
87 if (escSequence[i] != rhs.escSequence[i]) {
88 return false;
89 }
90 }
91 }
92 if (encoding != rhs.encoding) {
93 if (encoding == null || rhs.encoding == null ||
94 encoding.length != rhs.encoding.length)
95 {
96 return false;
97 }
98 for (int i = 0; i < encoding.length; i++) {
99 if (encoding[i] != rhs.encoding[i]) {
100 return false;
101 }
102 }
103 }
104 return true;
105 }
106
107 public int hashCode() {
108 return hash;
109 }
110
111 ControlSequence concatenate(ControlSequence rhs) {
112 if (encoding != null) {
113 throw new IllegalArgumentException
114 ("cannot concatenate to a non-standard charset escape " +
115 "sequence");
116 }
117
118 int len = escSequence.length + rhs.escSequence.length;
119 byte[] newEscSequence = new byte[len];
120 System.arraycopy(escSequence, 0, newEscSequence, 0,
121 escSequence.length);
122 System.arraycopy(rhs.escSequence, 0, newEscSequence,
123 escSequence.length, rhs.escSequence.length);
124 return new ControlSequence(newEscSequence, rhs.encoding);
125 }
126 }
127
128 static int MAX_CONTROL_SEQUENCE_LEN;
129
130 /**
131 * Maps a GL or GR escape sequence to an encoding.
132 */
133 private static final Map sequenceToEncodingMap;
134
135 /**
136 * Indicates whether a particular encoding wants the high bit turned on
137 * or off.
138 */
139 private static final Map highBitsMap;
140
141 /**
142 * Maps an encoding to an escape sequence. Rather than manage two
143 * converters in CharToByteCOMPOUND_TEXT, we output escape sequences which
144 * modify both GL and GR if necessary. This makes the output slightly less
145 * efficient, but our code much simpler.
146 */
147 private static final Map encodingToSequenceMap;
148
149 /**
150 * The keys of 'encodingToSequenceMap', sorted in preferential order.
151 */
152 private static final List encodings;
153
154 static {
155 HashMap tSequenceToEncodingMap = new HashMap(33, 1.0f);
156 HashMap tHighBitsMap = new HashMap(31, 1.0f);
157 HashMap tEncodingToSequenceMap = new HashMap(21, 1.0f);
158 ArrayList tEncodings = new ArrayList(21);
159
160 if (!(isEncodingSupported("US-ASCII") &&
161 isEncodingSupported("ISO-8859-1")))
162 {
163 throw new ExceptionInInitializerError
164 ("US-ASCII and ISO-8859-1 unsupported");
165 }
166
167 ControlSequence leftAscii = // high bit off, leave off
168 new ControlSequence(new byte[] { 0x1B, 0x28, 0x42 });
169 tSequenceToEncodingMap.put(leftAscii, "US-ASCII");
170 tHighBitsMap.put(leftAscii, Boolean.FALSE);
171
172 {
173 ControlSequence rightAscii = // high bit on, turn off
174 new ControlSequence(new byte[] { 0x1B, 0x29, 0x42 });
175 tSequenceToEncodingMap.put(rightAscii, "US-ASCII");
176 tHighBitsMap.put(rightAscii, Boolean.FALSE);
177 }
178
179 {
180 ControlSequence rightHalf = // high bit on, leave on
181 new ControlSequence(new byte[] { 0x1B, 0x2D, 0x41 });
182 tSequenceToEncodingMap.put(rightHalf, "ISO-8859-1");
183 tHighBitsMap.put(rightHalf, Boolean.TRUE);
184
185 ControlSequence fullSet = leftAscii.concatenate(rightHalf);
186 tEncodingToSequenceMap.put("ISO-8859-1", fullSet);
187 tEncodings.add("ISO-8859-1");
188 }
189 if (isEncodingSupported("ISO-8859-2")) {
190 ControlSequence rightHalf = // high bit on, leave on
191 new ControlSequence(new byte[] { 0x1B, 0x2D, 0x42 });
192 tSequenceToEncodingMap.put(rightHalf, "ISO-8859-2");
193 tHighBitsMap.put(rightHalf, Boolean.TRUE);
194
195 ControlSequence fullSet = leftAscii.concatenate(rightHalf);
196 tEncodingToSequenceMap.put("ISO-8859-2", fullSet);
197 tEncodings.add("ISO-8859-2");
198 }
199 if (isEncodingSupported("ISO-8859-3")) {
200 ControlSequence rightHalf = // high bit on, leave on
201 new ControlSequence(new byte[] { 0x1B, 0x2D, 0x43 });
202 tSequenceToEncodingMap.put(rightHalf, "ISO-8859-3");
203 tHighBitsMap.put(rightHalf, Boolean.TRUE);
204
205 ControlSequence fullSet = leftAscii.concatenate(rightHalf);
206 tEncodingToSequenceMap.put("ISO-8859-3", fullSet);
207 tEncodings.add("ISO-8859-3");
208 }
209 if (isEncodingSupported("ISO-8859-4")) {
210 ControlSequence rightHalf = // high bit on, leave on
211 new ControlSequence(new byte[] { 0x1B, 0x2D, 0x44 });
212 tSequenceToEncodingMap.put(rightHalf, "ISO-8859-4");
213 tHighBitsMap.put(rightHalf, Boolean.TRUE);
214
215 ControlSequence fullSet = leftAscii.concatenate(rightHalf);
216 tEncodingToSequenceMap.put("ISO-8859-4", fullSet);
217 tEncodings.add("ISO-8859-4");
218 }
219 if (isEncodingSupported("ISO-8859-5")) {
220 ControlSequence rightHalf = // high bit on, leave on
221 new ControlSequence(new byte[] { 0x1B, 0x2D, 0x4C });
222 tSequenceToEncodingMap.put(rightHalf, "ISO-8859-5");
223 tHighBitsMap.put(rightHalf, Boolean.TRUE);
224
225 ControlSequence fullSet = leftAscii.concatenate(rightHalf);
226 tEncodingToSequenceMap.put("ISO-8859-5", fullSet);
227 tEncodings.add("ISO-8859-5");
228 }
229 if (isEncodingSupported("ISO-8859-6")) {
230 ControlSequence rightHalf = // high bit on, leave on
231 new ControlSequence(new byte[] { 0x1B, 0x2D, 0x47 });
232 tSequenceToEncodingMap.put(rightHalf, "ISO-8859-6");
233 tHighBitsMap.put(rightHalf, Boolean.TRUE);
234
235 ControlSequence fullSet = leftAscii.concatenate(rightHalf);
236 tEncodingToSequenceMap.put("ISO-8859-6", fullSet);
237 tEncodings.add("ISO-8859-6");
238 }
239 if (isEncodingSupported("ISO-8859-7")) {
240 ControlSequence rightHalf = // high bit on, leave on
241 new ControlSequence(new byte[] { 0x1B, 0x2D, 0x46 });
242 tSequenceToEncodingMap.put(rightHalf, "ISO-8859-7");
243 tHighBitsMap.put(rightHalf, Boolean.TRUE);
244
245 ControlSequence fullSet = leftAscii.concatenate(rightHalf);
246 tEncodingToSequenceMap.put("ISO-8859-7", fullSet);
247 tEncodings.add("ISO-8859-7");
248 }
249 if (isEncodingSupported("ISO-8859-8")) {
250 ControlSequence rightHalf = // high bit on, leave on
251 new ControlSequence(new byte[] { 0x1B, 0x2D, 0x48 });
252 tSequenceToEncodingMap.put(rightHalf, "ISO-8859-8");
253 tHighBitsMap.put(rightHalf, Boolean.TRUE);
254
255 ControlSequence fullSet = leftAscii.concatenate(rightHalf);
256 tEncodingToSequenceMap.put("ISO-8859-8", fullSet);
257 tEncodings.add("ISO-8859-8");
258 }
259 if (isEncodingSupported("ISO-8859-9")) {
260 ControlSequence rightHalf = // high bit on, leave on
261 new ControlSequence(new byte[] { 0x1B, 0x2D, 0x4D });
262 tSequenceToEncodingMap.put(rightHalf, "ISO-8859-9");
263 tHighBitsMap.put(rightHalf, Boolean.TRUE);
264
265 ControlSequence fullSet = leftAscii.concatenate(rightHalf);
266 tEncodingToSequenceMap.put("ISO-8859-9", fullSet);
267 tEncodings.add("ISO-8859-9");
268 }
269 if (isEncodingSupported("JIS_X0201")) {
270 ControlSequence glLeft = // high bit off, leave off
271 new ControlSequence(new byte[] { 0x1B, 0x28, 0x4A });
272 ControlSequence glRight = // high bit off, turn on
273 new ControlSequence(new byte[] { 0x1B, 0x28, 0x49 });
274 ControlSequence grLeft = // high bit on, turn off
275 new ControlSequence(new byte[] { 0x1B, 0x29, 0x4A });
276 ControlSequence grRight = // high bit on, leave on
277 new ControlSequence(new byte[] { 0x1B, 0x29, 0x49 });
278 tSequenceToEncodingMap.put(glLeft, "JIS_X0201");
279 tSequenceToEncodingMap.put(glRight, "JIS_X0201");
280 tSequenceToEncodingMap.put(grLeft, "JIS_X0201");
281 tSequenceToEncodingMap.put(grRight, "JIS_X0201");
282 tHighBitsMap.put(glLeft, Boolean.FALSE);
283 tHighBitsMap.put(glRight, Boolean.TRUE);
284 tHighBitsMap.put(grLeft, Boolean.FALSE);
285 tHighBitsMap.put(grRight, Boolean.TRUE);
286
287 ControlSequence fullSet = glLeft.concatenate(grRight);
288 tEncodingToSequenceMap.put("JIS_X0201", fullSet);
289 tEncodings.add("JIS_X0201");
290 }
291 if (isEncodingSupported("X11GB2312")) {
292 ControlSequence leftHalf = // high bit off, leave off
293 new ControlSequence(new byte[] { 0x1B, 0x24, 0x28, 0x41 });
294 ControlSequence rightHalf = // high bit on, turn off
295 new ControlSequence(new byte[] { 0x1B, 0x24, 0x29, 0x41 });
296 tSequenceToEncodingMap.put(leftHalf, "X11GB2312");
297 tSequenceToEncodingMap.put(rightHalf, "X11GB2312");
298 tHighBitsMap.put(leftHalf, Boolean.FALSE);
299 tHighBitsMap.put(rightHalf, Boolean.FALSE);
300
301 tEncodingToSequenceMap.put("X11GB2312", leftHalf);
302 tEncodings.add("X11GB2312");
303 }
304 if (isEncodingSupported("x-JIS0208")) {
305 ControlSequence leftHalf = // high bit off, leave off
306 new ControlSequence(new byte[] { 0x1B, 0x24, 0x28, 0x42 });
307 ControlSequence rightHalf = // high bit on, turn off
308 new ControlSequence(new byte[] { 0x1B, 0x24, 0x29, 0x42 });
309 tSequenceToEncodingMap.put(leftHalf, "x-JIS0208");
310 tSequenceToEncodingMap.put(rightHalf, "x-JIS0208");
311 tHighBitsMap.put(leftHalf, Boolean.FALSE);
312 tHighBitsMap.put(rightHalf, Boolean.FALSE);
313
314 tEncodingToSequenceMap.put("x-JIS0208", leftHalf);
315 tEncodings.add("x-JIS0208");
316 }
317 if (isEncodingSupported("X11KSC5601")) {
318 ControlSequence leftHalf = // high bit off, leave off
319 new ControlSequence(new byte[] { 0x1B, 0x24, 0x28, 0x43 });
320 ControlSequence rightHalf = // high bit on, turn off
321 new ControlSequence(new byte[] { 0x1B, 0x24, 0x29, 0x43 });
322 tSequenceToEncodingMap.put(leftHalf, "X11KSC5601");
323 tSequenceToEncodingMap.put(rightHalf, "X11KSC5601");
324 tHighBitsMap.put(leftHalf, Boolean.FALSE);
325 tHighBitsMap.put(rightHalf, Boolean.FALSE);
326
327 tEncodingToSequenceMap.put("X11KSC5601", leftHalf);
328 tEncodings.add("X11KSC5601");
329 }
330
331 // Encodings not listed in Compound Text Encoding spec
332
333 // Esc seq: -b
334 if (isEncodingSupported("ISO-8859-15")) {
335 ControlSequence rightHalf = // high bit on, leave on
336 new ControlSequence(new byte[] { 0x1B, 0x2D, 0x62 });
337 tSequenceToEncodingMap.put(rightHalf, "ISO-8859-15");
338 tHighBitsMap.put(rightHalf, Boolean.TRUE);
339
340 ControlSequence fullSet = leftAscii.concatenate(rightHalf);
341 tEncodingToSequenceMap.put("ISO-8859-15", fullSet);
342 tEncodings.add("ISO-8859-15");
343 }
344 // Esc seq: -T
345 if (isEncodingSupported("TIS-620")) {
346 ControlSequence rightHalf = // high bit on, leave on
347 new ControlSequence(new byte[] { 0x1B, 0x2D, 0x54 });
348 tSequenceToEncodingMap.put(rightHalf, "TIS-620");
349 tHighBitsMap.put(rightHalf, Boolean.TRUE);
350
351 ControlSequence fullSet = leftAscii.concatenate(rightHalf);
352 tEncodingToSequenceMap.put("TIS-620", fullSet);
353 tEncodings.add("TIS-620");
354 }
355 if (isEncodingSupported("JIS_X0212-1990")) {
356 ControlSequence leftHalf = // high bit off, leave off
357 new ControlSequence(new byte[] { 0x1B, 0x24, 0x28, 0x44 });
358 ControlSequence rightHalf = // high bit on, turn off
359 new ControlSequence(new byte[] { 0x1B, 0x24, 0x29, 0x44 });
360 tSequenceToEncodingMap.put(leftHalf, "JIS_X0212-1990");
361 tSequenceToEncodingMap.put(rightHalf, "JIS_X0212-1990");
362 tHighBitsMap.put(leftHalf, Boolean.FALSE);
363 tHighBitsMap.put(rightHalf, Boolean.FALSE);
364
365 tEncodingToSequenceMap.put("JIS_X0212-1990", leftHalf);
366 tEncodings.add("JIS_X0212-1990");
367 }
368 if (isEncodingSupported("X11CNS11643P1")) {
369 ControlSequence leftHalf = // high bit off, leave off
370 new ControlSequence(new byte[] { 0x1B, 0x24, 0x28, 0x47 });
371 ControlSequence rightHalf = // high bit on, turn off
372 new ControlSequence(new byte[] { 0x1B, 0x24, 0x29, 0x47 });
373 tSequenceToEncodingMap.put(leftHalf, "X11CNS11643P1");
374 tSequenceToEncodingMap.put(rightHalf, "X11CNS11643P1");
375 tHighBitsMap.put(leftHalf, Boolean.FALSE);
376 tHighBitsMap.put(rightHalf, Boolean.FALSE);
377
378 tEncodingToSequenceMap.put("X11CNS11643P1", leftHalf);
379 tEncodings.add("X11CNS11643P1");
380 }
381 if (isEncodingSupported("X11CNS11643P2")) {
382 ControlSequence leftHalf = // high bit off, leave off
383 new ControlSequence(new byte[] { 0x1B, 0x24, 0x28, 0x48 });
384 ControlSequence rightHalf = // high bit on, turn off
385 new ControlSequence(new byte[] { 0x1B, 0x24, 0x29, 0x48 });
386 tSequenceToEncodingMap.put(leftHalf, "X11CNS11643P2");
387 tSequenceToEncodingMap.put(rightHalf, "X11CNS11643P2");
388 tHighBitsMap.put(leftHalf, Boolean.FALSE);
389 tHighBitsMap.put(rightHalf, Boolean.FALSE);
390
391 tEncodingToSequenceMap.put("X11CNS11643P2", leftHalf);
392 tEncodings.add("X11CNS11643P2");
393 }
394 if (isEncodingSupported("X11CNS11643P3")) {
395 ControlSequence leftHalf = // high bit off, leave off
396 new ControlSequence(new byte[] { 0x1B, 0x24, 0x28, 0x49 });
397 ControlSequence rightHalf = // high bit on, turn off
398 new ControlSequence(new byte[] { 0x1B, 0x24, 0x29, 0x49 });
399 tSequenceToEncodingMap.put(leftHalf, "X11CNS11643P3");
400 tSequenceToEncodingMap.put(rightHalf, "X11CNS11643P3");
401 tHighBitsMap.put(leftHalf, Boolean.FALSE);
402 tHighBitsMap.put(rightHalf, Boolean.FALSE);
403
404 tEncodingToSequenceMap.put("X11CNS11643P3", leftHalf);
405 tEncodings.add("X11CNS11643P3");
406 }
407 // Esc seq: %/2??SUN-KSC5601.1992-3
408 if (isEncodingSupported("x-Johab")) {
409 // 0x32 looks wrong. It's copied from the Sun X11 Compound Text
410 // support code. It implies that all Johab characters comprise two
411 // octets, which isn't true. Johab supports the ASCII/KS-Roman
412 // characters from 0x21-0x7E with single-byte representations.
413 ControlSequence johab = new ControlSequence(
414 new byte[] { 0x1b, 0x25, 0x2f, 0x32 },
415 new byte[] { 0x53, 0x55, 0x4e, 0x2d, 0x4b, 0x53, 0x43, 0x35,
416 0x36, 0x30, 0x31, 0x2e, 0x31, 0x39, 0x39, 0x32,
417 0x2d, 0x33 });
418 tSequenceToEncodingMap.put(johab, "x-Johab");
419 tEncodingToSequenceMap.put("x-Johab", johab);
420 tEncodings.add("x-Johab");
421 }
422 // Esc seq: %/2??SUN-BIG5-1
423 if (isEncodingSupported("Big5")) {
424 // 0x32 looks wrong. It's copied from the Sun X11 Compound Text
425 // support code. It implies that all Big5 characters comprise two
426 // octets, which isn't true. Big5 supports the ASCII/CNS-Roman
427 // characters from 0x21-0x7E with single-byte representations.
428 ControlSequence big5 = new ControlSequence(
429 new byte[] { 0x1b, 0x25, 0x2f, 0x32 },
430 new byte[] { 0x53, 0x55, 0x4e, 0x2d, 0x42, 0x49, 0x47, 0x35,
431 0x2d, 0x31 });
432 tSequenceToEncodingMap.put(big5, "Big5");
433 tEncodingToSequenceMap.put("Big5", big5);
434 tEncodings.add("Big5");
435 }
436
437 sequenceToEncodingMap =
438 Collections.unmodifiableMap(tSequenceToEncodingMap);
439 highBitsMap = Collections.unmodifiableMap(tHighBitsMap);
440 encodingToSequenceMap =
441 Collections.unmodifiableMap(tEncodingToSequenceMap);
442 encodings = Collections.unmodifiableList(tEncodings);
443 }
444
445 private static boolean isEncodingSupported(String encoding) {
446 try {
447 if (Charset.isSupported(encoding))
448 return true;
449 } catch (IllegalArgumentException x) { }
450 return (getDecoder(encoding) != null &&
451 getEncoder(encoding) != null);
452 }
453
454
455 // For Decoder
456 static CharsetDecoder getStandardDecoder(byte[] escSequence) {
457 return getNonStandardDecoder(escSequence, null);
458 }
459 static boolean getHighBit(byte[] escSequence) {
460 Boolean bool = (Boolean)highBitsMap.get
461 (new ControlSequence(escSequence));
462 return (bool == Boolean.TRUE);
463 }
464 static CharsetDecoder getNonStandardDecoder(byte[] escSequence,
465 byte[] encoding) {
466 return getDecoder((String)sequenceToEncodingMap.get
467 (new ControlSequence(escSequence, encoding)));
468 }
469 static CharsetDecoder getDecoder(String enc) {
470 if (enc == null) {
471 return null;
472 }
473 Charset cs = null;
474 try {
475 cs = Charset.forName(enc);
476 } catch (IllegalArgumentException e) {
477 Class cls;
478 try {
479 cls = Class.forName("sun.awt.motif." + enc);
480 } catch (ClassNotFoundException ee) {
481 return null;
482 }
483 try {
484 cs = (Charset)cls.newInstance();
485 } catch (InstantiationException ee) {
486 return null;
487 } catch (IllegalAccessException ee) {
488 return null;
489 }
490 }
491 try {
492 return cs.newDecoder();
493 } catch (UnsupportedOperationException e) {}
494 return null;
495 }
496
497
498 // For Encoder
499 static byte[] getEscapeSequence(String encoding) {
500 ControlSequence seq = (ControlSequence)
501 encodingToSequenceMap.get(encoding);
502 if (seq != null) {
503 return seq.escSequence;
504 }
505 return null;
506 }
507 static byte[] getEncoding(String encoding) {
508 ControlSequence seq = (ControlSequence)
509 encodingToSequenceMap.get(encoding);
510 if (seq != null) {
511 return seq.encoding;
512 }
513 return null;
514 }
515 static List getEncodings() {
516 return encodings;
517 }
518 static CharsetEncoder getEncoder(String enc) {
519 if (enc == null) {
520 return null;
521 }
522 Charset cs = null;
523 try {
524 cs = Charset.forName(enc);
525 } catch (IllegalArgumentException e) {
526 Class cls;
527 try {
528 cls = Class.forName("sun.awt.motif." + enc);
529 } catch (ClassNotFoundException ee) {
530 return null;
531 }
532 try {
533 cs = (Charset)cls.newInstance();
534 } catch (InstantiationException ee) {
535 return null;
536 } catch (IllegalAccessException ee) {
537 return null;
538 }
539 }
540 try {
541 return cs.newEncoder();
542 } catch (Throwable e) {}
543 return null;
544 }
545
546 // Not an instantiable class
547 private CompoundTextSupport() {}
548}