Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/test/javax/xml/jaxp/Encodings/CheckEncodingPropertiesFile.java
38854 views
/*1* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223/**24* @test25* @bug 800873826* @summary checks that the mapping implemented by27* com.sun.org.apache.xml.internal.serializer.Encodings28* correctly identifies valid Charset names and29* correctly maps them to their preferred mime names.30* Also checks that the Encodings.properties resource file31* is consistent.32* @compile -XDignore.symbol.file CheckEncodingPropertiesFile.java33* @run main CheckEncodingPropertiesFile34* @author Daniel Fuchs35*/3637import com.sun.org.apache.xml.internal.serializer.EncodingInfo;38import com.sun.org.apache.xml.internal.serializer.Encodings;39import java.io.InputStreamReader;40import java.lang.reflect.Method;41import java.nio.charset.Charset;42import java.util.ArrayList;43import java.util.Arrays;44import java.util.Collection;45import java.util.Collections;46import java.util.HashMap;47import java.util.HashSet;48import java.util.LinkedHashSet;49import java.util.List;50import java.util.Map;51import java.util.Map.Entry;52import java.util.Properties;53import java.util.Set;54import java.util.StringTokenizer;5556public class CheckEncodingPropertiesFile {5758private static final String ENCODINGS_FILE = "com/sun/org/apache/xml/internal/serializer/Encodings.properties";5960public static void main(String[] args) throws Exception {61Properties props = new Properties();62try (InputStreamReader is = new InputStreamReader(ClassLoader.getSystemResourceAsStream(ENCODINGS_FILE))) {63props.load(is);64}6566//printAllCharsets();6768test(props);69}707172private static final class CheckCharsetMapping {7374/**75* A map that maps Java or XML name to canonical charset names.76* key: upper cased value of Java or XML name.77* value: case-sensitive canonical name of charset.78*/79private final Map<String, String> charsetMap = new HashMap<>();8081private final Map<String, String> preferredMime = new HashMap<>();8283/**84* Unresolved alias names.85* For a given set of names pointing to the same unresolved charset,86* this map will contain, for each alias in the set, a mapping87* with the alias.toUpperValue() as key and the set of known aliases88* as value.89*/90private final Map<String, Collection<String>> unresolved = new HashMap<>();9192public final static class ConflictingCharsetError extends Error {93ConflictingCharsetError(String a, String cs1, String cs2) {94super("Conflicting charset mapping for '"+a+"': '"+cs1+"' and '"+cs2+"'");95}96}9798public final static class MissingValidCharsetNameError extends Error {99MissingValidCharsetNameError(String name, Collection<String> aliases) {100super(name+": Line "+aliases+" has no recognized charset alias");101}102}103104public final static class ConflictingPreferredMimeNameError extends Error {105ConflictingPreferredMimeNameError(String a, String cs1, String cs2) {106super("Conflicting preferred mime name for '"+a+"': '"+cs1+"' and '"+cs2+"'");107}108}109110/**111* For each alias in aliases, attempt to find the canonical112* charset name.113* All names in aliases are supposed to point to the same charset.114* Names in aliases can be java names or XML names, indifferently.115* @param aliases list of names (aliases) for a given charset.116* @return The canonical name of the charset, if found, null otherwise.117*/118private String findCharsetNameFor(String[] aliases) {119String cs = null;120String res = null;121for (String a : aliases) {122final String k = a.toUpperCase();123String cachedCs = charsetMap.get(k);124if (cs == null) {125cs = cachedCs;126}127if (cachedCs != null && cs != null128&& !Charset.forName(cachedCs).name().equals(Charset.forName(cs).name())) {129throw new ConflictingCharsetError(a,cs,cachedCs);130}131try {132final String rcs = Charset.forName(a).name();133if (cs != null && !Charset.forName(cs).name().equals(rcs)) {134throw new ConflictingCharsetError(a,cs,rcs);135}136if (res == null) {137if (a.equals(aliases[0])) {138res = a;139} else {140res = cs;141}142}143cs = rcs;144charsetMap.put(k, res == null ? cs : res);145} catch (Exception x) {146continue;147}148}149return res == null ? cs : res;150}151152/**153* Register a canonical charset name for a given set of aliases.154*155* @param charsetName the canonical charset name.156* @param aliases a list of aliases for the given charset.157*/158private void registerCharsetNameFor(String charsetName, String[] aliases) {159if (charsetName == null) throw new NullPointerException();160161for (String a : aliases) {162String k = a.toUpperCase();163String csv = charsetMap.get(k);164if (csv == null) {165charsetMap.put(k, charsetName);166csv = charsetName;167} else if (!csv.equals(charsetName)) {168throw new ConflictingCharsetError(a,charsetName,csv);169}170171final Collection<String> c = unresolved.get(k);172if (c != null) {173for (String aa : c) {174k = aa.toUpperCase();175String csvv = charsetMap.get(k);176if (csvv == null) charsetMap.put(k, csv);177unresolved.remove(k);178}179throw new MissingValidCharsetNameError(charsetName,c);180}181}182}183184/**185* Register a set of aliases as being unresolved.186* @param names the list of names - this should be what is returned by187* nameSet.toArray(new String[nameSet.size()])188* @param nameSet the set of unresolved aliases.189*/190private void registerUnresolvedNamesFor(String[] names, Collection<String> nameSet) {191// This is not necessarily an error: it could happen that some192// charsets are simply not supported on some OS/Arch193System.err.println("Warning: unresolved charset names: '"+ nameSet194+ "' This is not necessarily an error "195+ "- this charset may not be supported on this platform.");196for (String a : names) {197final String k = a.toUpperCase();198final Collection<String> c = unresolved.get(k);199if (c != null) {200//System.out.println("Found: "+a+" -> "+c);201//System.out.println("\t merging "+ c + " with " + nameSet);202nameSet.addAll(c);203for (String aa : c) {204unresolved.put(aa.toUpperCase(), nameSet);205}206}207unresolved.put(k, nameSet);208}209}210211212/**213* Add a new charset name mapping214* @param javaName the (supposedly) java name of the charset.215* @param xmlNames a list of corresponding XML names for that charset.216*/217void addMapping(String javaName, Collection<String> xmlNames) {218final LinkedHashSet<String> aliasNames = new LinkedHashSet<>();219aliasNames.add(javaName);220aliasNames.addAll(xmlNames);221final String[] aliases = aliasNames.toArray(new String[aliasNames.size()]);222final String cs = findCharsetNameFor(aliases);223if (cs != null) {224registerCharsetNameFor(cs, aliases);225if (xmlNames.size() > 0) {226String preferred = xmlNames.iterator().next();227String cachedPreferred = preferredMime.get(cs.toUpperCase());228if (cachedPreferred != null && !cachedPreferred.equals(preferred)) {229throw new ConflictingPreferredMimeNameError(cs, cachedPreferred, preferred);230}231preferredMime.put(cs.toUpperCase(), preferred);232}233} else {234registerUnresolvedNamesFor(aliases, aliasNames);235}236}237238/**239* Returns the canonical name of the charset for the given Java or XML240* alias name.241* @param alias the alias name242* @return the canonical charset name - or null if unknown.243*/244public String getCharsetNameFor(String alias) {245return charsetMap.get(alias.toUpperCase());246}247248}249250public static void test(Properties props) throws Exception {251252// First, build a mapping from the properties read from the resource253// file.254// We're going to check the consistency of the resource file255// while building this mapping, and throw errors if the file256// does not meet our assumptions.257//258Map<String, Collection<String>> lines = new HashMap<>();259final CheckCharsetMapping mapping = new CheckCharsetMapping();260261for (String key : props.stringPropertyNames()) {262Collection<String> values = getValues(props.getProperty(key));263lines.put(key, values);264mapping.addMapping(key, values);265}266267// Then build maps of EncodingInfos, and print along debugging268// information that should help understand the content of the269// resource file and the mapping it defines.270//271Map<String, EncodingInfo> javaInfos = new HashMap<>(); // Map indexed by java names272Map<String, EncodingInfo> xmlMap = new HashMap<>(); // Map indexed by XML names273Map<String, String> preferred =274new HashMap<>(mapping.preferredMime); // Java Name -> Preferred Mime Name275List<EncodingInfo> all = new ArrayList<>(); // unused...276for (Entry<String, Collection<String>> e : lines.entrySet()) {277final String charsetName = mapping.getCharsetNameFor(e.getKey());278if (charsetName == null) {279System.out.println("!! No charset for: "+e.getKey()+ " "+ e.getValue());280continue;281}282Charset c = Charset.forName(charsetName);283EncodingInfo info;284final String k = e.getKey().toUpperCase();285final String kc = charsetName.toUpperCase();286StringBuilder sb = new StringBuilder();287for (String xml : e.getValue()) {288final String kx = xml.toUpperCase();289info = xmlMap.get(kx);290if (info == null) {291info = new EncodingInfo(xml, charsetName);292System.out.println("** XML: "+xml+" -> "+charsetName);293xmlMap.put(kx, info);294all.add(info);295}296if (!javaInfos.containsKey(k)) {297javaInfos.put(k, info);298if (!preferred.containsKey(k)) {299preferred.put(k, xml);300}301sb.append("** Java: ").append(k).append(" -> ")302.append(xml).append(" (charset: ")303.append(charsetName).append(")\n");304}305if (!javaInfos.containsKey(kc)) {306if (!preferred.containsKey(kc)) {307preferred.put(kc, xml);308}309javaInfos.put(kc, info);310sb.append("** Java: ").append(kc).append(" -> ")311.append(xml).append(" (charset: ")312.append(charsetName).append(")\n");313}314if (!javaInfos.containsKey(c.name().toUpperCase())) {315if (!preferred.containsKey(c.name().toUpperCase())) {316preferred.put(c.name().toUpperCase(), xml);317}318javaInfos.put(c.name().toUpperCase(), info);319sb.append("** Java: ").append(c.name().toUpperCase()).append(" -> ")320.append(xml).append(" (charset: ")321.append(charsetName).append(")\n");322}323}324if (sb.length() == 0) {325System.out.println("Nothing new for "+charsetName+": "+e.getKey()+" -> "+e.getValue());326} else {327System.out.print(sb);328}329330}331332// Now we're going to verify that Encodings.java has done its job333// correctly. We're going to ask Encodings to convert java names to mime334// names and mime names to java names - and verify that the returned335// java names do map to recognized charsets.336//337// We're also going to verify that Encodings has recorded the preferred338// mime name correctly.339340Method m = Encodings.class.getDeclaredMethod("getMimeEncoding", String.class);341m.setAccessible(true);342343Set<String> xNames = new HashSet<>();344Set<String> jNames = new HashSet<>();345for (String name: xmlMap.keySet()) {346final String javaName = checkConvertMime2Java(name);347checkPreferredMime(m, javaName, preferred);348jNames.add(javaName);349xNames.add(name);350}351352353for (String javaName : lines.keySet()) {354final String javaCharsetName = mapping.getCharsetNameFor(javaName.toUpperCase());355if (javaCharsetName == null) continue;356if (!jNames.contains(javaName)) {357checkPreferredMime(m, javaName, preferred);358jNames.add(javaName);359}360for (String xml : lines.get(javaName)) {361if (xNames.contains(xml)) continue;362final String jName = checkConvertMime2Java(xml);363xNames.add(xml);364if (jNames.contains(jName)) continue;365checkPreferredMime(m, jName, preferred);366}367}368}369370private static String checkConvertMime2Java(String xml) {371final String jName = Encodings.convertMime2JavaEncoding(xml);372final String jCharsetName;373try {374jCharsetName = Charset.forName(jName).name();375} catch (Exception x) {376throw new Error("Unrecognized charset returned by Encodings.convertMime2JavaEncoding(\""+xml+"\")", x);377}378System.out.println("Encodings.convertMime2JavaEncoding(\""+xml+"\") = \""+jName+"\" ("+jCharsetName+")");379return jName;380}381382private static void checkPreferredMime(Method m, String javaName, Map<String,String> preferred)383throws Exception {384final String mime = (String) m.invoke(null, javaName);385final String expected = preferred.get(javaName.toUpperCase());386if (Arrays.deepEquals(new String[] {mime}, new String[] {expected})) {387System.out.println("Encodings.getMimeEncoding(\""+javaName+"\") = \""+mime+"\"");388} else {389throw new Error("Bad preferred mime type for: '"+javaName+"': expected '"+390expected+"' but got '"+mime+"'");391}392}393394private static Collection<String> getValues(String val) {395int pos = val.indexOf(' ');396if (pos < 0) {397return Collections.singletonList(val);398}399//lastPrintable =400// Integer.decode(val.substring(pos).trim()).intValue();401StringTokenizer st =402new StringTokenizer(val.substring(0, pos), ",");403final List<String> values = new ArrayList<>(st.countTokens());404while (st.hasMoreTokens()) {405values.add(st.nextToken());406}407return values;408}409410// can be called in main() to help debugging.411// Prints out all available charsets and their recognized aliases412// as returned by the Charset API.413private static void printAllCharsets() {414Map<String, Charset> all = Charset.availableCharsets();415System.out.println("\n=========================================\n");416for (String can : all.keySet()) {417System.out.println(can + ": " + all.get(can).aliases());418}419}420}421422423