001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.serializers.encoding;
018:
019: import java.io.ByteArrayOutputStream;
020: import java.io.File;
021: import java.io.PrintWriter;
022: import java.io.PrintStream;
023: import java.io.IOException;
024: import java.io.OutputStreamWriter;
025: import java.io.UnsupportedEncodingException;
026: import java.net.URL;
027: import java.util.Enumeration;
028: import java.util.HashMap;
029: import java.util.zip.ZipEntry;
030: import java.util.zip.ZipFile;
031:
032: /**
033: *
034: *
035: * @author <a href="mailto:pier@apache.org">Pier Fumagalli</a>, February 2003
036: * @version CVS $Id: CharsetFactory.java 433543 2006-08-22 06:22:54Z crossley $
037: */
038: public final class CharsetFactory {
039:
040: /** The lookup class name for the encodings. */
041: private static final String CHARSET_LOOKUP_CLASS = "org/apache/cocoon/components/serializers/encoding/cs_US_ASCII.class";
042:
043: /** Our private instance. */
044: private static CharsetFactory instance = new CharsetFactory();
045:
046: /** The instance of the JVM default <code>Charset</code>. */
047: private Charset defaultCharset = null;
048:
049: /** The instance of the JVM unknown <code>Charset</code>. */
050: private Charset unknownCharset = null;
051:
052: /** All our charsets, mapped by their name and aliases. */
053: private HashMap charsets = new HashMap();
054:
055: /**
056: * Create a new instance of this <code>CharsetFactory</code>.
057: */
058: private CharsetFactory() {
059: super ();
060: this .unknownCharset = new UnknownCharset();
061:
062: ClassLoader loader = Thread.currentThread()
063: .getContextClassLoader();
064: URL url = loader.getResource(CHARSET_LOOKUP_CLASS);
065:
066: if (url == null) {
067: throw new CharsetFactoryException(
068: "Unable to load charsets "
069: + "because their classes are not present",
070: null);
071: }
072:
073: if ("jar".equals(url.getProtocol())) {
074: this .loadCharsetsFromJar(url);
075: } else if ("file".equals(url.getProtocol())) {
076: this .loadCharsetsFromFile(url);
077: } else {
078: throw new CharsetFactoryException(
079: "Unable to load charsets " + "from protocol \""
080: + url.getProtocol() + "\"", null);
081: }
082:
083: ByteArrayOutputStream otmp = new ByteArrayOutputStream();
084: OutputStreamWriter wtmp = new OutputStreamWriter(otmp);
085: String etmp = wtmp.getEncoding();
086: try {
087: defaultCharset = this .getCharset(etmp);
088: } catch (UnsupportedEncodingException exception) {
089: throw new CharsetFactoryException(
090: "The default encoding of this " + "JVM \"" + etmp
091: + "\" is not supported", exception);
092: }
093:
094: }
095:
096: /**
097: * Load a <code>Charset</code> into this factory.
098: */
099: private void loadCharset(Charset charset) {
100: this .charsets.put(charset.getName().toLowerCase(), charset);
101: String aliases[] = charset.getAliases();
102: for (int x = 0; x < aliases.length; x++) {
103: this .charsets.put(aliases[x].toLowerCase(), charset);
104: }
105: }
106:
107: /**
108: * Instantiate and load a <code>Charset</code> into this factory.
109: */
110: private void loadCharset(String clazz) {
111: try {
112: Class c = Class.forName(clazz);
113: Object o = c.newInstance();
114: if (o instanceof Charset) {
115: loadCharset((Charset) o);
116: }
117: } catch (Exception exception) {
118: throw new CharsetFactoryException(
119: "Unable to instantiate class \"" + clazz + "\"",
120: exception);
121: }
122: }
123:
124: /**
125: * Load all <code>Charset</code> available if this class was loaded
126: * from a JAR file.
127: */
128: private void loadCharsetsFromJar(URL url) {
129: try {
130: String file = url.getFile();
131: String mtch = file.substring(file.indexOf('!'));
132: file = file.substring(5, file.indexOf('!'));
133: mtch = mtch.substring(2, mtch.lastIndexOf('/') + 1) + "cs_";
134:
135: ZipFile zip = new ZipFile(file);
136: Enumeration enumeration = zip.entries();
137: while (enumeration.hasMoreElements()) {
138: ZipEntry entry = (ZipEntry) enumeration.nextElement();
139: String name = entry.getName();
140: if ((!name.startsWith(mtch))
141: || (!name.endsWith(".class")))
142: continue;
143: name = name.substring(mtch.length());
144: name = ".cs_" + name.substring(0, name.length() - 6);
145: name = this .getClass().getPackage().getName() + name;
146: loadCharset(name);
147: }
148: } catch (IOException exception) {
149: throw new CharsetFactoryException("Unable to access JAR \""
150: + url.toString() + "\"", exception);
151: }
152: }
153:
154: /**
155: * Load all <code>Charset</code> available if this class was loaded
156: * from a plain file on disk.
157: */
158: private void loadCharsetsFromFile(URL url) {
159: File file = new File(url.getFile()).getParentFile();
160: String children[] = file.list();
161: for (int x = 0; x < children.length; x++) {
162: String child = children[x];
163: if ((!child.startsWith("cs_"))
164: || (!child.endsWith(".class")))
165: continue;
166: child = '.' + child.substring(0, child.length() - 6);
167: child = this .getClass().getPackage().getName() + child;
168: this .loadCharset(child);
169: }
170: }
171:
172: /**
173: * Return an instance of this <code>CharsetFactory</code>.
174: */
175: public static CharsetFactory newInstance() {
176: if (instance != null)
177: return (instance);
178: synchronized (CharsetFactory.class) {
179: if (instance != null)
180: return (instance);
181: instance = new CharsetFactory();
182: }
183: return (instance);
184: }
185:
186: /**
187: * Return the <code>Charset</code> instance for the unknown charset.
188: * <br />
189: * All calls to the <code>allows(...)</code> method of the returned
190: * <code>Charset</code> will return <b>true</b>.
191: */
192: public Charset getCharset() {
193: return (unknownCharset);
194: }
195:
196: /**
197: * Return the <code>Charset</code> instance for the default charset.
198: *
199: */
200: public Charset getDefaultCharset() {
201: return (defaultCharset);
202: }
203:
204: /**
205: * Return the <code>Charset</code> instance for a specifc charset.
206: *
207: * @throws UnsupportedEncodingException If the specified is invalid or
208: * cannot be accessed.
209: */
210: public Charset getCharset(String name)
211: throws UnsupportedEncodingException {
212: if (name == null)
213: return (this .getDefaultCharset());
214: Charset charset = (Charset) this .charsets.get(name
215: .toLowerCase());
216: if (charset != null)
217: return (charset);
218: throw new UnsupportedEncodingException("Unsupported charset \""
219: + name + "\"");
220: }
221:
222: /**
223: * An <code>RuntimeException</code> thrown if something bad happens
224: * while initializing our factory.
225: */
226: private static class CharsetFactoryException extends
227: RuntimeException {
228:
229: /** The root cause of this exception. */
230: private Exception exception = null;
231:
232: /**
233: * Create a new <code>CharsetFactoryException</code> instance.
234: */
235: private CharsetFactoryException(String message,
236: Exception exception) {
237: super (message == null ? exception.getMessage() : message);
238: this .exception = exception;
239: }
240:
241: /**
242: * Return the <code>Exception</code> cause of this exception.
243: */
244: public Exception getException() {
245: return (this .exception);
246: }
247:
248: /**
249: * Print this <code>Exception</code> stacktrace to a specified
250: * <code>PrintWriter</code>.
251: */
252: public void printStackTrace(PrintWriter out) {
253: super .printStackTrace(out);
254: if (this .exception != null) {
255: out.print("Root cause: ");
256: this .exception.printStackTrace(out);
257: }
258: }
259:
260: /**
261: * Print this <code>Exception</code> stacktrace to a specified
262: * <code>PrintStream</code>.
263: */
264: public void printStackTrace(PrintStream out) {
265: super .printStackTrace(out);
266: if (this .exception != null) {
267: out.print("Root cause: ");
268: this.exception.printStackTrace(out);
269: }
270: }
271: }
272: }
|