public static String resolveEntity( String entity )
{
- String result = (String) defaultEntityMap.get( entity );
+ if ( entity == null )
+ {
+ // Invalid. null.
+ return entity;
+ }
+
+ if ( entity.trim().length() <= 0 )
+ {
+ // Invalid. empty.
+ return entity;
+ }
+
+ if ( !( entity.charAt( 0 ) == '&' ) && ( entity.charAt( entity.length() ) == ';' ) )
+ {
+ // Invalid, not an entity.
+ return entity;
+ }
+
+ String result = (String) defaultEntityMap.get( entity.substring( 1, entity.length() - 1 ) );
if ( result == null )
{
return entity;
import java.io.BufferedReader;
import java.io.IOException;
-import java.io.PushbackReader;
import java.io.Reader;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
/**
- * LatinEntityResolutionReader
+ * LatinEntityResolutionReader - Read a Character Stream.
*
* @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
* @version $Id$
public class LatinEntityResolutionReader
extends Reader
{
- private PushbackReader originalReader;
+ private BufferedReader originalReader;
+
+ private char leftover[];
+
+ private Pattern entityPattern;
public LatinEntityResolutionReader( Reader reader )
{
- this.originalReader = new PushbackReader( 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.
+ */
+ public int read( char[] destbuf, int offset, int length )
+ throws IOException
+ {
+ int tmp_length;
+ int current_requested_offset = offset;
+ int current_requested_length = length;
+
+ // Drain leftover from last read request.
+ if ( leftover != null )
+ {
+ if ( leftover.length > length )
+ {
+ // Copy partial leftover.
+ System.arraycopy( leftover, 0, destbuf, current_requested_offset, length );
+
+ // Create new leftover of remaining.
+ char tmp[] = new char[length];
+ System.arraycopy( leftover, length, tmp, 0, length );
+ leftover = new char[tmp.length];
+ System.arraycopy( tmp, 0, leftover, 0, length );
+
+ // Return len
+ return length;
+ }
+ else
+ {
+ tmp_length = leftover.length;
+
+ // Copy full leftover
+ System.arraycopy( leftover, 0, destbuf, current_requested_offset, tmp_length );
+
+ // Empty out leftover (as there is now none left)
+ leftover = null;
+
+ // Adjust offset and lengths.
+ current_requested_offset += tmp_length;
+ current_requested_length -= tmp_length;
+ }
+ }
+
+ StringBuffer sbuf = getExpandedBuffer( current_requested_length );
+
+ // Have we reached the end of the buffer?
+ if ( sbuf == null )
+ {
+ // Do we have content?
+ if ( current_requested_offset > offset )
+ {
+ // Signal that we do, by calculating length.
+ return ( current_requested_offset - offset );
+ }
+
+ // No content. signal end of buffer.
+ return -1;
+ }
+
+ // Copy from expanded buf whatever length we can accomodate.
+ tmp_length = Math.min( sbuf.length(), current_requested_length );
+ sbuf.getChars( 0, tmp_length, destbuf, current_requested_offset );
+
+ // Create the leftover (if any)
+ if ( tmp_length < sbuf.length() )
+ {
+ leftover = new char[sbuf.length() - tmp_length];
+ sbuf.getChars( tmp_length, tmp_length + leftover.length, leftover, 0 );
+ }
+
+ // Calculate Actual Length and return.
+ return ( current_requested_offset - offset ) + tmp_length;
}
- public int read( char[] cbuf, int off, int len )
+ private StringBuffer getExpandedBuffer( int minimum_length )
throws IOException
{
- char tmpbuf[] = new char[cbuf.length];
- int count = this.originalReader.read( tmpbuf, off, len );
-
- StringBuffer sb = new StringBuffer();
-
-
- return count;
+ StringBuffer buf = null;
+ String line = this.originalReader.readLine();
+ boolean done = ( line == null );
+
+ while ( !done )
+ {
+ if ( buf == null )
+ {
+ buf = new StringBuffer();
+ }
+
+ buf.append( expandLine( line ) );
+
+ // Add newline only if there is more data.
+ if ( this.originalReader.ready() )
+ {
+ buf.append( "\n" );
+ }
+
+ if ( buf.length() > minimum_length )
+ {
+ done = true;
+ }
+ else
+ {
+ line = this.originalReader.readLine();
+ done = ( line == null );
+ }
+ }
+
+ return buf;
+ }
+
+ private String expandLine( String line )
+ {
+ StringBuffer ret = new StringBuffer();
+
+ int offset = 0;
+ String entity;
+ Matcher mat = this.entityPattern.matcher( line );
+ while ( mat.find() )
+ {
+ 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();
}
public void close()
--- /dev/null
+<basic>
+ <names>
+ <name>Trygve Laugstøl</name>
+ <name>The ∞ Archiva</name>
+ </names>
+</basic>
\ No newline at end of file
--- /dev/null
+package org.apache.maven.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.File;
+
+import junit.framework.TestCase;
+
+/**
+ * AbstractArchivaXmlTestCase
+ *
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public abstract class AbstractArchivaXmlTestCase
+ extends TestCase
+{
+
+ protected File getExampleXml( String filename )
+ {
+ File examplesDir = new File( "src/test/examples" );
+ if ( !examplesDir.exists() )
+ {
+ fail( "Missing the examples directory: " + examplesDir.getAbsolutePath() );
+ }
+ File exampleFile = new File( examplesDir, filename );
+ if ( !exampleFile.exists() )
+ {
+ fail( "Missing the example xml file: " + exampleFile.getAbsolutePath() );
+ }
+ return exampleFile;
+ }
+
+}
--- /dev/null
+package org.apache.maven.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 junit.framework.TestCase;
+
+/**
+ * LatinEntitiesTest
+ *
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class LatinEntitiesTest
+ extends TestCase
+{
+ public void testResolveEntity()
+ {
+ // Good Entities.
+ assertEquals( "©", LatinEntities.resolveEntity( "©" ) );
+ assertEquals( "∞", LatinEntities.resolveEntity( "∞" ) );
+ assertEquals( "ø", LatinEntities.resolveEntity( "ø" ) );
+
+ // Bad Entities.
+ assertEquals( "", LatinEntities.resolveEntity( "" ) );
+ assertEquals( "&", LatinEntities.resolveEntity( "&" ) );
+ assertEquals( null, LatinEntities.resolveEntity( null ) );
+ }
+}
--- /dev/null
+package org.apache.maven.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.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringWriter;
+
+/**
+ * LatinEntityResolutionReaderTest
+ *
+ * @author <a href="mailto:joakim@erdfelt.com">Joakim Erdfelt</a>
+ * @version $Id$
+ */
+public class LatinEntityResolutionReaderTest
+ extends AbstractArchivaXmlTestCase
+{
+ /**
+ * A method to obtain the content of a reader as a String,
+ * while allowing for specifing the buffer size of the operation.
+ *
+ * This method is only really useful for testing a Reader implementation.
+ *
+ * @param input the reader to get the input from.
+ * @param bufsize the buffer size to use.
+ * @return the contents of the reader as a String.
+ * @throws IOException if there was an I/O error.
+ */
+ private String toStringFromReader( Reader input, int bufsize )
+ throws IOException
+ {
+ StringWriter output = new StringWriter();
+
+ final char[] buffer = new char[bufsize];
+ int n = 0;
+ while ( -1 != ( n = input.read( buffer ) ) )
+ {
+ output.write( buffer, 0, n );
+ }
+ output.flush();
+
+ return output.toString();
+ }
+
+ /**
+ * This reads a text file from the src/test/examples directory,
+ * normalizes the end of lines, and returns the contents as a big String.
+ *
+ * @param examplePath the name of the file in the src/test/examples directory.
+ * @return the contents of the provided file
+ * @throws IOException if there was an I/O error.
+ */
+ private String toStringFromExample( String examplePath )
+ throws IOException
+ {
+ File exampleFile = getExampleXml( examplePath );
+ FileReader fileReader = new FileReader( exampleFile );
+ BufferedReader lineReader = new BufferedReader( fileReader );
+ StringBuffer sb = new StringBuffer();
+
+ boolean hasContent = false;
+
+ String line = lineReader.readLine();
+ while ( line != null )
+ {
+ if ( hasContent )
+ {
+ sb.append( "\n" );
+ }
+ sb.append( line );
+ hasContent = true;
+ line = lineReader.readLine();
+ }
+
+ return sb.toString();
+ }
+
+ public void assertProperRead( String sourcePath, String expectedPath, int bufsize )
+ {
+ try
+ {
+ File inputFile = getExampleXml( sourcePath );
+
+ FileReader fileReader = new FileReader( inputFile );
+ LatinEntityResolutionReader testReader = new LatinEntityResolutionReader( fileReader );
+
+ String actualOutput = toStringFromReader( testReader, bufsize );
+ String expectedOutput = toStringFromExample( expectedPath );
+
+ assertEquals( expectedOutput, actualOutput );
+ }
+ catch ( IOException e )
+ {
+ fail( "IOException: " + e.getMessage() );
+ }
+ }
+
+ public void testReaderNormalBufsize()
+ throws IOException
+ {
+ assertProperRead( "no-prolog-with-entities.xml", "no-prolog-with-entities.xml-resolved", 4096 );
+ }
+
+ public void testReaderSmallBufsize()
+ throws IOException
+ {
+ assertProperRead( "no-prolog-with-entities.xml", "no-prolog-with-entities.xml-resolved", 1024 );
+ }
+
+ public void testReaderRediculouslyTinyBufsize()
+ throws IOException
+ {
+ assertProperRead( "no-prolog-with-entities.xml", "no-prolog-with-entities.xml-resolved", 32 );
+ }
+
+ public void testReaderHugeBufsize()
+ throws IOException
+ {
+ assertProperRead( "no-prolog-with-entities.xml", "no-prolog-with-entities.xml-resolved", 409600 );
+ }
+}
import java.util.Iterator;
import java.util.List;
-import junit.framework.TestCase;
-
/**
* XMLReaderTest
*
* @version $Id$
*/
public class XMLReaderTest
- extends TestCase
+ extends AbstractArchivaXmlTestCase
{
- private File getExampleXml( String filename )
- {
- File examplesDir = new File( "src/test/examples" );
- if ( !examplesDir.exists() )
- {
- fail( "Missing the examples directory: " + examplesDir.getAbsolutePath() );
- }
- File exampleFile = new File( examplesDir, filename );
- if ( !exampleFile.exists() )
- {
- fail( "Missing the example xml file: " + exampleFile.getAbsolutePath() );
- }
- return exampleFile;
- }
-
private void assertElementTexts( List elementList, String[] expectedTexts )
{
assertEquals( "Element List Size", expectedTexts.length, elementList.size() );