content: package org.apache.archiva.xml;
  
  /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   *  http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing,
   * software distributed under the License is distributed on an
   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   * KIND, either express or implied.  See the License for the
   * specific language governing permissions and limitations
   * under the License.
   */
  
  import java.io.BufferedReader;
  import java.io.IOException;
  import java.io.Reader;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  /**
   * LatinEntityResolutionReader - Read a Character Stream.
   *
   *
   */
  public class LatinEntityResolutionReader
      extends Reader
  {
      private BufferedReader originalReader;
  
      private char leftover[];
  
      private Pattern entityPattern;
  
      public LatinEntityResolutionReader( Reader reader )
      {
          this.originalReader = new BufferedReader( reader );
          this.entityPattern = Pattern.compile( "\\&[a-zA-Z]+\\;" );
      }
  
      /**
       * Read characters into a portion of an array. This method will block until some input is available, 
       * an I/O error occurs, or the end of the stream is reached.
       * 
       * @param destbuf Destination buffer
       * @param offset Offset (in destination buffer) at which to start storing characters
       * @param length Maximum number of characters to read
       * @return The number of characters read, or -1 if the end of the stream has been reached
       * @throws IOException if an I/O error occurs.
       */
      @Override
      public int read( char[] destbuf, int offset, int length )
          throws IOException
      {
          int tmpLength;
          int currentRequestedOffset = offset;
          int currentRequestedLength = length;
  
          // Drain leftover from last read request.
          if ( leftover != null )
          {
              if ( leftover.length > length )
              {
                  // Copy partial leftover.
                  System.arraycopy( leftover, 0, destbuf, currentRequestedOffset, length );
                  int copyLeftOverLength = leftover.length - length;
  
                  // Create new leftover of remaining.
                  char tmp[] = new char[copyLeftOverLength];
                  System.arraycopy( leftover, length, tmp, 0, copyLeftOverLength );
                  leftover = new char[tmp.length];
                  System.arraycopy( tmp, 0, leftover, 0, copyLeftOverLength );
  
                  // Return len
                  return length;
              }
              else
              {
                  tmpLength = leftover.length;
  
                  // Copy full leftover
                  System.arraycopy( leftover, 0, destbuf, currentRequestedOffset, tmpLength );
  
                  // Empty out leftover (as there is now none left)
                  leftover = null;
  
                  // Adjust offset and lengths.
                  currentRequestedOffset += tmpLength;
                  currentRequestedLength -= tmpLength;
              }
          }
  
          StringBuilder sbuf = getExpandedBuffer( currentRequestedLength );
  
          // Have we reached the end of the buffer?
          if ( sbuf == null )
          {
              // Do we have content?
              if ( currentRequestedOffset > offset )
              {
                  // Signal that we do, by calculating length.
                  return ( currentRequestedOffset - offset );
              }
  
              // No content. signal end of buffer.
              return -1;
          }
  
          // Copy from expanded buf whatever length we can accomodate.
          tmpLength = Math.min( sbuf.length(), currentRequestedLength );
          sbuf.getChars( 0, tmpLength, destbuf, currentRequestedOffset );
  
          // Create the leftover (if any)
          if ( tmpLength < sbuf.length() )
          {
              leftover = new char[sbuf.length() - tmpLength];
              sbuf.getChars( tmpLength, tmpLength + leftover.length, leftover, 0 );
          }
  
          // Calculate Actual Length and return.
          return ( currentRequestedOffset - offset ) + tmpLength;
      }
  
      private StringBuilder getExpandedBuffer( int minimumLength )
          throws IOException
      {
          StringBuilder buf = null;
          String line = this.originalReader.readLine();
          boolean done = ( line == null );
  
          while ( !done )
          {
              if ( buf == null )
              {
                  buf = new StringBuilder();
              }
  
              buf.append( expandLine( line ) );
  
              // Add newline only if there is more data.
              if ( this.originalReader.ready() )
              {
                  buf.append( "\n" );
              }
  
              if ( buf.length() > minimumLength )
              {
                  done = true;
              }
              else
              {
                  line = this.originalReader.readLine();
                  done = ( line == null );
              }
          }
  
          return buf;
      }
  
      private String expandLine( String line )
      {
          StringBuilder ret = new StringBuilder();
  
          int offset = 0;
          String entity;
          Matcher mat = this.entityPattern.matcher( line );
          while ( mat.find( offset ) )
          {
              ret.append( line.substring( offset, mat.start() ) );
              entity = mat.group();
              ret.append( LatinEntities.resolveEntity( entity ) );
              offset = mat.start() + entity.length();
          }
          ret.append( line.substring( offset ) );
  
          return ret.toString();
      }
  
      @Override
      public void close()
          throws IOException
      {
          this.originalReader.close();
      }
  }
  