1 /*
2  * Copyright (c) 2015, 2019, Oracle and/or its affiliates. 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.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.util;
27 
28 import sun.nio.cs.ISO_8859_1;
29 import sun.nio.cs.UTF_8;
30 
31 import java.nio.ByteBuffer;
32 import java.nio.CharBuffer;
33 import java.nio.charset.Charset;
34 import java.nio.charset.CharsetDecoder;
35 import java.nio.charset.CharsetEncoder;
36 import java.nio.charset.CoderResult;
37 import java.nio.charset.CodingErrorAction;
38 import java.nio.charset.StandardCharsets;
39 import java.util.Objects;
40 
41 /**
42  * A Charset implementation for reading PropertyResourceBundle, in order
43  * for loading properties files. This first tries to load the properties
44  * file with UTF-8 encoding). If it fails, then load the file with ISO-8859-1
45  */
46 public class PropertyResourceBundleCharset extends Charset {
47 
48     private boolean strictUTF8 = false;
49 
PropertyResourceBundleCharset(boolean strictUTF8)50     public PropertyResourceBundleCharset(boolean strictUTF8) {
51         this(PropertyResourceBundleCharset.class.getCanonicalName(), null);
52         this.strictUTF8 = strictUTF8;
53     }
54 
PropertyResourceBundleCharset(String canonicalName, String[] aliases)55     public PropertyResourceBundleCharset(String canonicalName, String[] aliases) {
56         super(canonicalName, aliases);
57     }
58 
59     @Override
contains(Charset cs)60     public boolean contains(Charset cs) {
61         return false;
62     }
63 
64     @Override
newDecoder()65     public CharsetDecoder newDecoder() {
66         return new PropertiesFileDecoder(this, 1.0f, 1.0f);
67     }
68 
69     @Override
newEncoder()70     public CharsetEncoder newEncoder() {
71         throw new UnsupportedOperationException("Encoding is not supported");
72     }
73 
74     private final class PropertiesFileDecoder extends CharsetDecoder {
75 
76         private CharsetDecoder cdUTF_8 = UTF_8.INSTANCE.newDecoder()
77                                 .onMalformedInput(CodingErrorAction.REPORT)
78                                 .onUnmappableCharacter(CodingErrorAction.REPORT);
79         private CharsetDecoder cdISO_8859_1 = null;
80 
PropertiesFileDecoder(Charset cs, float averageCharsPerByte, float maxCharsPerByte)81         protected PropertiesFileDecoder(Charset cs,
82                 float averageCharsPerByte, float maxCharsPerByte) {
83             super(cs, averageCharsPerByte, maxCharsPerByte);
84         }
85 
decodeLoop(ByteBuffer in, CharBuffer out)86         protected CoderResult decodeLoop(ByteBuffer in, CharBuffer out) {
87             if (Objects.nonNull(cdISO_8859_1)) {
88                 return cdISO_8859_1.decode(in, out, false);
89             }
90             in.mark();
91             out.mark();
92 
93             CoderResult cr = cdUTF_8.decode(in, out, false);
94             if (cr.isUnderflow() || cr.isOverflow() ||
95                 PropertyResourceBundleCharset.this.strictUTF8) {
96                 return cr;
97             }
98 
99             // Invalid or unmappable UTF-8 sequence detected.
100             // Switching to the ISO 8859-1 decorder.
101             assert cr.isMalformed() || cr.isUnmappable();
102             in.reset();
103             out.reset();
104             cdISO_8859_1 = ISO_8859_1.INSTANCE.newDecoder();
105             return cdISO_8859_1.decode(in, out, false);
106         }
107     }
108 }
109