001    package org.apache.archiva.xml;
002    
003    /*
004     * Licensed to the Apache Software Foundation (ASF) under one
005     * or more contributor license agreements.  See the NOTICE file
006     * distributed with this work for additional information
007     * regarding copyright ownership.  The ASF licenses this file
008     * to you under the Apache License, Version 2.0 (the
009     * "License"); you may not use this file except in compliance
010     * with the License.  You may obtain a copy of the License at
011     *
012     *  http://www.apache.org/licenses/LICENSE-2.0
013     *
014     * Unless required by applicable law or agreed to in writing,
015     * software distributed under the License is distributed on an
016     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017     * KIND, either express or implied.  See the License for the
018     * specific language governing permissions and limitations
019     * under the License.
020     */
021    
022    import java.io.BufferedReader;
023    import java.io.IOException;
024    import java.io.Reader;
025    import java.util.regex.Matcher;
026    import java.util.regex.Pattern;
027    
028    /**
029     * LatinEntityResolutionReader - Read a Character Stream.
030     *
031     *
032     */
033    public class LatinEntityResolutionReader
034        extends Reader
035    {
036        private BufferedReader originalReader;
037    
038        private char leftover[];
039    
040        private Pattern entityPattern;
041    
042        public LatinEntityResolutionReader( Reader reader )
043        {
044            this.originalReader = new BufferedReader( reader );
045            this.entityPattern = Pattern.compile( "\\&[a-zA-Z]+\\;" );
046        }
047    
048        /**
049         * Read characters into a portion of an array. This method will block until some input is available, 
050         * an I/O error occurs, or the end of the stream is reached.
051         * 
052         * @param destbuf Destination buffer
053         * @param offset Offset (in destination buffer) at which to start storing characters
054         * @param length Maximum number of characters to read
055         * @return The number of characters read, or -1 if the end of the stream has been reached
056         * @throws IOException if an I/O error occurs.
057         */
058        public int read( char[] destbuf, int offset, int length )
059            throws IOException
060        {
061            int tmpLength;
062            int currentRequestedOffset = offset;
063            int currentRequestedLength = length;
064    
065            // Drain leftover from last read request.
066            if ( leftover != null )
067            {
068                if ( leftover.length > length )
069                {
070                    // Copy partial leftover.
071                    System.arraycopy( leftover, 0, destbuf, currentRequestedOffset, length );
072                    int copyLeftOverLength = leftover.length - length;
073    
074                    // Create new leftover of remaining.
075                    char tmp[] = new char[copyLeftOverLength];
076                    System.arraycopy( leftover, length, tmp, 0, copyLeftOverLength );
077                    leftover = new char[tmp.length];
078                    System.arraycopy( tmp, 0, leftover, 0, copyLeftOverLength );
079    
080                    // Return len
081                    return length;
082                }
083                else
084                {
085                    tmpLength = leftover.length;
086    
087                    // Copy full leftover
088                    System.arraycopy( leftover, 0, destbuf, currentRequestedOffset, tmpLength );
089    
090                    // Empty out leftover (as there is now none left)
091                    leftover = null;
092    
093                    // Adjust offset and lengths.
094                    currentRequestedOffset += tmpLength;
095                    currentRequestedLength -= tmpLength;
096                }
097            }
098    
099            StringBuilder sbuf = getExpandedBuffer( currentRequestedLength );
100    
101            // Have we reached the end of the buffer?
102            if ( sbuf == null )
103            {
104                // Do we have content?
105                if ( currentRequestedOffset > offset )
106                {
107                    // Signal that we do, by calculating length.
108                    return ( currentRequestedOffset - offset );
109                }
110    
111                // No content. signal end of buffer.
112                return -1;
113            }
114    
115            // Copy from expanded buf whatever length we can accomodate.
116            tmpLength = Math.min( sbuf.length(), currentRequestedLength );
117            sbuf.getChars( 0, tmpLength, destbuf, currentRequestedOffset );
118    
119            // Create the leftover (if any)
120            if ( tmpLength < sbuf.length() )
121            {
122                leftover = new char[sbuf.length() - tmpLength];
123                sbuf.getChars( tmpLength, tmpLength + leftover.length, leftover, 0 );
124            }
125    
126            // Calculate Actual Length and return.
127            return ( currentRequestedOffset - offset ) + tmpLength;
128        }
129    
130        private StringBuilder getExpandedBuffer( int minimumLength )
131            throws IOException
132        {
133            StringBuilder buf = null;
134            String line = this.originalReader.readLine();
135            boolean done = ( line == null );
136    
137            while ( !done )
138            {
139                if ( buf == null )
140                {
141                    buf = new StringBuilder();
142                }
143    
144                buf.append( expandLine( line ) );
145    
146                // Add newline only if there is more data.
147                if ( this.originalReader.ready() )
148                {
149                    buf.append( "\n" );
150                }
151    
152                if ( buf.length() > minimumLength )
153                {
154                    done = true;
155                }
156                else
157                {
158                    line = this.originalReader.readLine();
159                    done = ( line == null );
160                }
161            }
162    
163            return buf;
164        }
165    
166        private String expandLine( String line )
167        {
168            StringBuilder ret = new StringBuilder();
169    
170            int offset = 0;
171            String entity;
172            Matcher mat = this.entityPattern.matcher( line );
173            while ( mat.find( offset ) )
174            {
175                ret.append( line.substring( offset, mat.start() ) );
176                entity = mat.group();
177                ret.append( LatinEntities.resolveEntity( entity ) );
178                offset = mat.start() + entity.length();
179            }
180            ret.append( line.substring( offset ) );
181    
182            return ret.toString();
183        }
184    
185        public void close()
186            throws IOException
187        {
188            this.originalReader.close();
189        }
190    }