001package 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 022import java.io.BufferedReader; 023import java.io.IOException; 024import java.io.Reader; 025import java.util.regex.Matcher; 026import java.util.regex.Pattern; 027 028/** 029 * LatinEntityResolutionReader - Read a Character Stream. 030 * 031 * 032 */ 033public 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 @Override 059 public int read( char[] destbuf, int offset, int length ) 060 throws IOException 061 { 062 int tmpLength; 063 int currentRequestedOffset = offset; 064 int currentRequestedLength = length; 065 066 // Drain leftover from last read request. 067 if ( leftover != null ) 068 { 069 if ( leftover.length > length ) 070 { 071 // Copy partial leftover. 072 System.arraycopy( leftover, 0, destbuf, currentRequestedOffset, length ); 073 int copyLeftOverLength = leftover.length - length; 074 075 // Create new leftover of remaining. 076 char tmp[] = new char[copyLeftOverLength]; 077 System.arraycopy( leftover, length, tmp, 0, copyLeftOverLength ); 078 leftover = new char[tmp.length]; 079 System.arraycopy( tmp, 0, leftover, 0, copyLeftOverLength ); 080 081 // Return len 082 return length; 083 } 084 else 085 { 086 tmpLength = leftover.length; 087 088 // Copy full leftover 089 System.arraycopy( leftover, 0, destbuf, currentRequestedOffset, tmpLength ); 090 091 // Empty out leftover (as there is now none left) 092 leftover = null; 093 094 // Adjust offset and lengths. 095 currentRequestedOffset += tmpLength; 096 currentRequestedLength -= tmpLength; 097 } 098 } 099 100 StringBuilder sbuf = getExpandedBuffer( currentRequestedLength ); 101 102 // Have we reached the end of the buffer? 103 if ( sbuf == null ) 104 { 105 // Do we have content? 106 if ( currentRequestedOffset > offset ) 107 { 108 // Signal that we do, by calculating length. 109 return ( currentRequestedOffset - offset ); 110 } 111 112 // No content. signal end of buffer. 113 return -1; 114 } 115 116 // Copy from expanded buf whatever length we can accomodate. 117 tmpLength = Math.min( sbuf.length(), currentRequestedLength ); 118 sbuf.getChars( 0, tmpLength, destbuf, currentRequestedOffset ); 119 120 // Create the leftover (if any) 121 if ( tmpLength < sbuf.length() ) 122 { 123 leftover = new char[sbuf.length() - tmpLength]; 124 sbuf.getChars( tmpLength, tmpLength + leftover.length, leftover, 0 ); 125 } 126 127 // Calculate Actual Length and return. 128 return ( currentRequestedOffset - offset ) + tmpLength; 129 } 130 131 private StringBuilder getExpandedBuffer( int minimumLength ) 132 throws IOException 133 { 134 StringBuilder buf = null; 135 String line = this.originalReader.readLine(); 136 boolean done = ( line == null ); 137 138 while ( !done ) 139 { 140 if ( buf == null ) 141 { 142 buf = new StringBuilder(); 143 } 144 145 buf.append( expandLine( line ) ); 146 147 // Add newline only if there is more data. 148 if ( this.originalReader.ready() ) 149 { 150 buf.append( "\n" ); 151 } 152 153 if ( buf.length() > minimumLength ) 154 { 155 done = true; 156 } 157 else 158 { 159 line = this.originalReader.readLine(); 160 done = ( line == null ); 161 } 162 } 163 164 return buf; 165 } 166 167 private String expandLine( String line ) 168 { 169 StringBuilder ret = new StringBuilder(); 170 171 int offset = 0; 172 String entity; 173 Matcher mat = this.entityPattern.matcher( line ); 174 while ( mat.find( offset ) ) 175 { 176 ret.append( line.substring( offset, mat.start() ) ); 177 entity = mat.group(); 178 ret.append( LatinEntities.resolveEntity( entity ) ); 179 offset = mat.start() + entity.length(); 180 } 181 ret.append( line.substring( offset ) ); 182 183 return ret.toString(); 184 } 185 186 @Override 187 public void close() 188 throws IOException 189 { 190 this.originalReader.close(); 191 } 192}