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 static java.nio.charset.StandardCharsets.ISO_8859_1;
29 
30 import java.nio.ByteBuffer;
31 import java.nio.CharBuffer;
32 import java.nio.charset.Charset;
33 import java.nio.charset.CharsetDecoder;
34 import java.nio.charset.CharsetEncoder;
35 import java.nio.charset.CoderResult;
36 import java.nio.charset.CodingErrorAction;
37 import java.nio.charset.StandardCharsets;
38 import java.util.Objects;
39 
40 /**
41  * A Charset implementation for reading PropertyResourceBundle, in order
42  * for loading properties files. This first tries to load the properties
43  * file with UTF-8 encoding). If it fails, then load the file with ISO-8859-1
44  */
45 public class PropertyResourceBundleCharset extends Charset {
46 
47     private boolean strictUTF8 = false;
48 
PropertyResourceBundleCharset(boolean strictUTF8)49     public PropertyResourceBundleCharset(boolean strictUTF8) {
50         this(PropertyResourceBundleCharset.class.getCanonicalName(), null);
51         this.strictUTF8 = strictUTF8;
52     }
53 
PropertyResourceBundleCharset(String canonicalName, String[] aliases)54     public PropertyResourceBundleCharset(String canonicalName, String[] aliases) {
55         super(canonicalName, aliases);
56     }
57 
58     @Override
contains(Charset cs)59     public boolean contains(Charset cs) {
60         return false;
61     }
62 
63     @Override
newDecoder()64     public CharsetDecoder newDecoder() {
65         return new PropertiesFileDecoder(this, 1.0f, 1.0f);
66     }
67 
68     @Override
newEncoder()69     public CharsetEncoder newEncoder() {
70         throw new UnsupportedOperationException("Encoding is not supported");
71     }
72 
73     private final class PropertiesFileDecoder extends CharsetDecoder {
74         // Android-changed: use StandardCharsets.
75         // private CharsetDecoder cdUTF_8 = UTF_8.INSTANCE.newDecoder()
76         private CharsetDecoder cdUTF_8 = StandardCharsets.UTF_8.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             // Android-changed: use StandardCharsets.
105             // cdISO_8859_1 = ISO_8859_1.INSTANCE.newDecoder();
106             cdISO_8859_1 = ISO_8859_1.newDecoder();
107             return cdISO_8859_1.decode(in, out, false);
108         }
109     }
110 }
111