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