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 }