1 /* 2 * reserved comment block 3 * DO NOT REMOVE OR ALTER! 4 */ 5 /* 6 * Copyright 2000-2002,2004,2005 The Apache Software Foundation. 7 * 8 * Licensed under the Apache License, Version 2.0 (the "License"); 9 * you may not use this file except in compliance with the License. 10 * You may obtain a copy of the License at 11 * 12 * http://www.apache.org/licenses/LICENSE-2.0 13 * 14 * Unless required by applicable law or agreed to in writing, software 15 * distributed under the License is distributed on an "AS IS" BASIS, 16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 * See the License for the specific language governing permissions and 18 * limitations under the License. 19 */ 20 21 package com.sun.org.apache.xml.internal.serialize; 22 23 import java.io.OutputStream; 24 import java.io.OutputStreamWriter; 25 import java.io.UnsupportedEncodingException; 26 import java.io.Writer; 27 import com.sun.org.apache.xerces.internal.util.EncodingMap; 28 29 /** 30 * This class represents an encoding. 31 * 32 * @version $Id: EncodingInfo.java,v 1.6 2007/10/18 03:39:08 joehw Exp $ 33 */ 34 public class EncodingInfo { 35 36 // An array to hold the argument for a method of Charset, CharsetEncoder or CharToByteConverter. 37 private Object [] fArgsForMethod = null; 38 39 // name of encoding as registered with IANA; 40 // preferably a MIME name, but aliases are fine too. 41 String ianaName; 42 String javaName; 43 int lastPrintable; 44 45 // The CharsetEncoder with which we test unusual characters. 46 Object fCharsetEncoder = null; 47 48 // The CharToByteConverter with which we test unusual characters. 49 Object fCharToByteConverter = null; 50 51 // Is the converter null because it can't be instantiated 52 // for some reason (perhaps we're running with insufficient authority as 53 // an applet? 54 boolean fHaveTriedCToB = false; 55 56 // Is the charset encoder usable or available. 57 boolean fHaveTriedCharsetEncoder = false; 58 59 /** 60 * Creates new <code>EncodingInfo</code> instance. 61 */ 62 public EncodingInfo(String ianaName, String javaName, int lastPrintable) { 63 this.ianaName = ianaName; 64 this.javaName = EncodingMap.getIANA2JavaMapping(ianaName); 65 this.lastPrintable = lastPrintable; 66 } 67 68 /** 69 * Returns a MIME charset name of this encoding. 70 */ 71 public String getIANAName() { 72 return this.ianaName; 73 } 74 75 /** 76 * Returns a writer for this encoding based on 77 * an output stream. 78 * 79 * @return A suitable writer 80 * @exception UnsupportedEncodingException There is no convertor 81 * to support this encoding 82 */ 83 public Writer getWriter(OutputStream output) 84 throws UnsupportedEncodingException { 85 // this should always be true! 86 if (javaName != null) 87 return new OutputStreamWriter(output, javaName); 88 javaName = EncodingMap.getIANA2JavaMapping(ianaName); 89 if(javaName == null) 90 // use UTF-8 as preferred encoding 91 return new OutputStreamWriter(output, "UTF8"); 92 return new OutputStreamWriter(output, javaName); 93 } 94 95 /** 96 * Checks whether the specified character is printable or not in this encoding. 97 * 98 * @param ch a code point (0-0x10ffff) 99 */ 100 public boolean isPrintable(char ch) { 101 if (ch <= this.lastPrintable) { 102 return true; 103 } 104 return isPrintable0(ch); 105 } 106 107 /** 108 * Checks whether the specified character is printable or not in this encoding. 109 * This method accomplishes this using a java.nio.CharsetEncoder. If NIO isn't 110 * available it will attempt use a sun.io.CharToByteConverter. 111 * 112 * @param ch a code point (0-0x10ffff) 113 */ 114 private boolean isPrintable0(char ch) { 115 116 // Attempt to get a CharsetEncoder for this encoding. 117 if (fCharsetEncoder == null && CharsetMethods.fgNIOCharsetAvailable && !fHaveTriedCharsetEncoder) { 118 if (fArgsForMethod == null) { 119 fArgsForMethod = new Object [1]; 120 } 121 // try and create the CharsetEncoder 122 try { 123 fArgsForMethod[0] = javaName; 124 Object charset = CharsetMethods.fgCharsetForNameMethod.invoke(null, fArgsForMethod); 125 if (((Boolean) CharsetMethods.fgCharsetCanEncodeMethod.invoke(charset, (Object[]) null)).booleanValue()) { 126 fCharsetEncoder = CharsetMethods.fgCharsetNewEncoderMethod.invoke(charset, (Object[]) null); 127 } 128 // This charset cannot be used for encoding, don't try it again... 129 else { 130 fHaveTriedCharsetEncoder = true; 131 } 132 } 133 catch (Exception e) { 134 // don't try it again... 135 fHaveTriedCharsetEncoder = true; 136 } 137 } 138 // Attempt to use the CharsetEncoder to determine whether the character is printable. 139 if (fCharsetEncoder != null) { 140 try { 141 fArgsForMethod[0] = new Character(ch); 142 return ((Boolean) CharsetMethods.fgCharsetEncoderCanEncodeMethod.invoke(fCharsetEncoder, fArgsForMethod)).booleanValue(); 143 } 144 catch (Exception e) { 145 // obviously can't use this charset encoder; possibly a JDK bug 146 fCharsetEncoder = null; 147 fHaveTriedCharsetEncoder = false; 148 } 149 } 150 151 // As a last resort try to use a sun.io.CharToByteConverter to 152 // determine whether this character is printable. We will always 153 // reach here on JDK 1.3 or below. 154 if (fCharToByteConverter == null) { 155 if (fHaveTriedCToB || !CharToByteConverterMethods.fgConvertersAvailable) { 156 // forget it; nothing we can do... 157 return false; 158 } 159 if (fArgsForMethod == null) { 160 fArgsForMethod = new Object [1]; 161 } 162 // try and create the CharToByteConverter 163 try { 164 fArgsForMethod[0] = javaName; 165 fCharToByteConverter = CharToByteConverterMethods.fgGetConverterMethod.invoke(null, fArgsForMethod); 166 } 167 catch (Exception e) { 168 // don't try it again... 169 fHaveTriedCToB = true; 170 return false; 171 } 172 } 173 try { 174 fArgsForMethod[0] = new Character(ch); 175 return ((Boolean) CharToByteConverterMethods.fgCanConvertMethod.invoke(fCharToByteConverter, fArgsForMethod)).booleanValue(); 176 } 177 catch (Exception e) { 178 // obviously can't use this converter; probably some kind of 179 // security restriction 180 fCharToByteConverter = null; 181 fHaveTriedCToB = false; 182 return false; 183 } 184 } 185 186 // is this an encoding name recognized by this JDK? 187 // if not, will throw UnsupportedEncodingException 188 public static void testJavaEncodingName(String name) throws UnsupportedEncodingException { 189 final byte [] bTest = {(byte)'v', (byte)'a', (byte)'l', (byte)'i', (byte)'d'}; 190 String s = new String(bTest, name); 191 } 192 193 /** 194 * Holder of methods from java.nio.charset.Charset and java.nio.charset.CharsetEncoder. 195 */ 196 static class CharsetMethods { 197 198 // Method: java.nio.charset.Charset.forName(java.lang.String) 199 private static java.lang.reflect.Method fgCharsetForNameMethod = null; 200 201 // Method: java.nio.charset.Charset.canEncode() 202 private static java.lang.reflect.Method fgCharsetCanEncodeMethod = null; 203 204 // Method: java.nio.charset.Charset.newEncoder() 205 private static java.lang.reflect.Method fgCharsetNewEncoderMethod = null; 206 207 // Method: java.nio.charset.CharsetEncoder.canEncode(char) 208 private static java.lang.reflect.Method fgCharsetEncoderCanEncodeMethod = null; 209 210 // Flag indicating whether or not java.nio.charset.* is available. 211 private static boolean fgNIOCharsetAvailable = false; 212 213 private CharsetMethods() {} 214 215 // Attempt to get methods for Charset and CharsetEncoder on class initialization. 216 static { 217 try { 218 Class charsetClass = Class.forName("java.nio.charset.Charset"); 219 Class charsetEncoderClass = Class.forName("java.nio.charset.CharsetEncoder"); 220 fgCharsetForNameMethod = charsetClass.getMethod("forName", new Class [] {String.class}); 221 fgCharsetCanEncodeMethod = charsetClass.getMethod("canEncode", new Class [] {}); 222 fgCharsetNewEncoderMethod = charsetClass.getMethod("newEncoder", new Class [] {}); 223 fgCharsetEncoderCanEncodeMethod = charsetEncoderClass.getMethod("canEncode", new Class [] {Character.TYPE}); 224 fgNIOCharsetAvailable = true; 225 } 226 // ClassNotFoundException, NoSuchMethodException or SecurityException 227 // Whatever the case, we cannot use java.nio.charset.*. 228 catch (Exception exc) { 229 fgCharsetForNameMethod = null; 230 fgCharsetCanEncodeMethod = null; 231 fgCharsetEncoderCanEncodeMethod = null; 232 fgCharsetNewEncoderMethod = null; 233 fgNIOCharsetAvailable = false; 234 } 235 } 236 } 237 238 /** 239 * Holder of methods from sun.io.CharToByteConverter. 240 */ 241 static class CharToByteConverterMethods { 242 243 // Method: sun.io.CharToByteConverter.getConverter(java.lang.String) 244 private static java.lang.reflect.Method fgGetConverterMethod = null; 245 246 // Method: sun.io.CharToByteConverter.canConvert(char) 247 private static java.lang.reflect.Method fgCanConvertMethod = null; 248 249 // Flag indicating whether or not sun.io.CharToByteConverter is available. 250 private static boolean fgConvertersAvailable = false; 251 252 private CharToByteConverterMethods() {} 253 254 // Attempt to get methods for char to byte converter on class initialization. 255 static { 256 try { 257 Class clazz = Class.forName("sun.io.CharToByteConverter"); 258 fgGetConverterMethod = clazz.getMethod("getConverter", new Class [] {String.class}); 259 fgCanConvertMethod = clazz.getMethod("canConvert", new Class [] {Character.TYPE}); 260 fgConvertersAvailable = true; 261 } 262 // ClassNotFoundException, NoSuchMethodException or SecurityException 263 // Whatever the case, we cannot use sun.io.CharToByteConverter. 264 catch (Exception exc) { 265 fgGetConverterMethod = null; 266 fgCanConvertMethod = null; 267 fgConvertersAvailable = false; 268 } 269 } 270 } 271 }