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    
018    /*
019     * This code has been borrowed from the Apache Xerces project. We're copying the code to
020     * keep from adding a dependency on Xerces in the Geronimo kernel.
021     */
022    package org.apache.geronimo.system.configuration;
023    
024    import java.io.ByteArrayOutputStream;
025    import java.io.IOException;
026    import java.io.OutputStreamWriter;
027    import java.io.Writer;
028    
029    /**
030     * This class represents an encoding.
031     *
032     * @version $Id: SieveEncodingInfo.java 476049 2006-11-17 04:35:17Z kevan $
033     */
034    public class SieveEncodingInfo extends EncodingInfo {
035    
036        BAOutputStream checkerStream = null;
037        Writer checkerWriter = null;
038        String dangerChars = null;
039    
040        /**
041         * Creates new <code>SeiveEncodingInfo</code> instance.
042         *
043         * @param dangers A sorted characters that are always printed as character references.
044         */
045        public SieveEncodingInfo(String mimeName, String javaName,
046                                 int lastPrintable, String dangers) {
047            super(mimeName, javaName, lastPrintable);
048            this.dangerChars = dangers;
049        }
050    
051        /**
052         * Creates new <code>SeiveEncodingInfo</code> instance.
053         */
054        public SieveEncodingInfo(String mimeName, int lastPrintable) {
055            this(mimeName, mimeName, lastPrintable, null);
056        }
057    
058        /**
059         * Checks whether the specified character is printable or not.
060         *
061         * @param ch a code point (0-0x10ffff)
062         */
063        public boolean isPrintable(int ch) {
064            if (this.dangerChars != null && ch <= 0xffff) {
065                /**
066                 * Searches this.dangerChars for ch.
067                 * TODO: Use binary search.
068                 */
069                if (this.dangerChars.indexOf(ch) >= 0)
070                    return false;
071            }
072    
073            if (ch <= this.lastPrintable)
074                return true;
075    
076            boolean printable = true;
077            synchronized (this) {
078                try {
079                    if (this.checkerWriter == null) {
080                        this.checkerStream = new BAOutputStream(10);
081                        this.checkerWriter = new OutputStreamWriter(this.checkerStream, this.javaName);
082                    }
083    
084                    if (ch > 0xffff) {
085                        this.checkerWriter.write(((ch-0x10000)>>10)+0xd800);
086                        this.checkerWriter.write(((ch-0x10000)&0x3ff)+0xdc00);
087                        byte[] result = this.checkerStream.getBuffer();
088                        if (this.checkerStream.size() == 2 && result[0] == '?' && result[1] == '?')
089                            printable = false;
090                    } else {
091                        this.checkerWriter.write(ch);
092                        this.checkerWriter.flush();
093                        byte[] result = this.checkerStream.getBuffer();
094                        if (this.checkerStream.size() == 1 && result[0] == '?')
095                            printable = false;
096                    }
097                    this.checkerStream.reset();
098                } catch (IOException ioe) {
099                    printable = false;
100                }
101            }
102    
103            return printable;
104        }
105    
106        /**
107         * Why don't we use the original ByteArrayOutputStream?
108         * - Because the toByteArray() method of the ByteArrayOutputStream
109         * creates new byte[] instances for each call.
110         */
111        static class BAOutputStream extends ByteArrayOutputStream {
112            BAOutputStream() {
113                super();
114            }
115    
116            BAOutputStream(int size) {
117                super(size);
118            }
119    
120            byte[] getBuffer() {
121                return this.buf;
122            }
123        }
124    
125    }