You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@velocity.apache.org by he...@apache.org on 2007/01/24 16:25:59 UTC

svn commit: r499441 - in /velocity/engine/trunk/src: java/org/apache/velocity/io/ java/org/apache/velocity/runtime/resource/loader/ test/org/apache/velocity/io/

Author: henning
Date: Wed Jan 24 07:25:58 2007
New Revision: 499441

URL: http://svn.apache.org/viewvc?view=rev&rev=499441
Log:
Get the UnicodeInputStream as proposed in VELOCITY-191 in. Rewrote the
code quite a bit, added some testing and especially renamed the
property to be called "unicode".

So 

resource.loader = file
file.resource.loader.class = org.apache.velocity.runtime.resource.loader.FileResourceLoader
file.resource.loader.path = ./templates
file.resource.loader.cache = true
file.resource.loader.modificationCheckInterval = 5

# Check the template files whether they contain an unicode BOM.
file.resource.loader.unicode = true

Should cover everything discussed in VELOCITY-191.


Added:
    velocity/engine/trunk/src/java/org/apache/velocity/io/UnicodeInputStream.java   (with props)
    velocity/engine/trunk/src/test/org/apache/velocity/io/
    velocity/engine/trunk/src/test/org/apache/velocity/io/UnicodeInputStreamTestCase.java   (with props)
Modified:
    velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/loader/FileResourceLoader.java

Added: velocity/engine/trunk/src/java/org/apache/velocity/io/UnicodeInputStream.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/io/UnicodeInputStream.java?view=auto&rev=499441
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/io/UnicodeInputStream.java (added)
+++ velocity/engine/trunk/src/java/org/apache/velocity/io/UnicodeInputStream.java Wed Jan 24 07:25:58 2007
@@ -0,0 +1,375 @@
+package org.apache.velocity.io;
+
+/*
+ * 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.IOException;
+import java.io.InputStream;
+import java.io.PushbackInputStream;
+
+import org.apache.velocity.util.ExceptionUtils;
+
+
+/**
+ * This is an input stream that is unicode BOM aware. This allows you to e.g. read
+ * Windows Notepad Unicode files as Velocity templates.
+ *
+ * It allows you to check the actual encoding of a file by calling {@link #getEncoding()} on
+ * the input stream reader.
+ *
+ * This class is not thread safe! When more than one thread wants to use an instance of UnicodeInputStream,
+ * the caller must provide synchronization.
+ *
+ * @author <a href="mailto:mailmur@yahoo.com">Aki Nieminen</a>
+ * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
+ * @version $Id$
+ */
+
+public class UnicodeInputStream
+    extends InputStream
+{
+
+    /** BOM Marker for UTF 8. See http://www.unicode.org/unicode/faq/utf_bom.html */
+    public static final UnicodeBOM UTF8_BOM = new UnicodeBOM("UTF-8", new byte [] { (byte)0xef, (byte)0xbb, (byte)0xbf });
+
+    /** BOM Marker for UTF 16, little endian. See http://www.unicode.org/unicode/faq/utf_bom.html */
+    public static final UnicodeBOM UTF16LE_BOM = new UnicodeBOM("UTF-16LE", new byte [] { (byte)0xff, (byte)0xfe });
+
+    /** BOM Marker for UTF 16, big endian. See http://www.unicode.org/unicode/faq/utf_bom.html */
+    public static final UnicodeBOM UTF16BE_BOM = new UnicodeBOM("UTF-16BE", new byte [] { (byte)0xfe, (byte)0xff });
+
+    /**
+     * BOM Marker for UTF 32, little endian. See http://www.unicode.org/unicode/faq/utf_bom.html
+     *
+     * TODO: Does Java actually support this?
+     */
+    public static final UnicodeBOM UTF32LE_BOM = new UnicodeBOM("UTF-32LE", new byte [] { (byte)0xff, (byte)0xfe, (byte)0x00, (byte)0x00 });
+
+    /**
+     * BOM Marker for UTF 32, big endian. See http://www.unicode.org/unicode/faq/utf_bom.html
+     *
+     * TODO: Does Java actually support this?
+     */
+    public static final UnicodeBOM UTF32BE_BOM = new UnicodeBOM("UTF-32BE", new byte [] { (byte)0x00, (byte)0x00, (byte)0xfe, (byte)0xff });
+
+    /** The maximum amount of bytes to read for a BOM */
+    private static final int MAX_BOM_SIZE = 4;
+
+    /** Buffer for BOM reading */
+    private byte [] buf = new byte[MAX_BOM_SIZE];
+
+    /** Buffer pointer. */
+    private int pos = 0;
+
+    /** The stream encoding as read from the BOM or null. */
+    private final String encoding;
+
+    /** True if the BOM itself should be skipped and not read. */
+    private final boolean skipBOM;
+
+    private final PushbackInputStream inputStream;
+
+    /**
+     * Creates a new UnicodeInputStream object. Skips a BOM which defines the file encoding.
+     *
+     * @param  inputStream The input stream to use for reading.
+     */
+    public UnicodeInputStream(final InputStream inputStream)
+            throws IllegalStateException, IOException
+    {
+        this(inputStream, true);
+    }
+
+    /**
+     * Creates a new UnicodeInputStream object.
+     *
+     * @param  inputStream The input stream to use for reading.
+     * @param skipBOM If this is set to true, a BOM read from the stream is discarded. This parameter should normally be true.
+     */
+    public UnicodeInputStream(final InputStream inputStream, boolean skipBOM)
+            throws IllegalStateException, IOException
+    {
+        super();
+
+        this.skipBOM = skipBOM;
+        this.inputStream = new PushbackInputStream(inputStream, MAX_BOM_SIZE);
+
+        try
+        {
+            this.encoding = readEncoding();
+        }
+        catch (IOException ioe)
+        {
+            IllegalStateException ex = new IllegalStateException("Could not read BOM from Stream");
+            ExceptionUtils.setCause(ex, ioe);
+            throw ex;
+        }
+    }
+
+    /**
+     * Returns true if the input stream discards the BOM.
+     *
+     * @return  True if the input stream discards the BOM.
+     */
+    public boolean isSkipBOM()
+    {
+        return skipBOM;
+    }
+
+    /**
+     * Read encoding based on BOM.
+     *
+     * @return  The encoding based on the BOM.
+     *
+     * @throws  IllegalStateException  When a problem reading the BOM occured.
+     */
+    public String getEncodingFromStream()
+    {
+        return encoding;
+    }
+
+    /**
+     * This method gets the encoding from the stream contents if a BOM exists. If no BOM exists, the encoding
+     * is undefined.
+     *
+     * @return The encoding of this streams contents as decided by the BOM or null if no BOM was found.
+     */
+    protected String readEncoding()
+        throws IOException
+    {
+        pos = 0;
+
+        UnicodeBOM encoding = null;
+
+        // read first byte.
+        if (readByte())
+        {
+            // Build a list of matches
+            //
+            // 00 00 FE FF --> UTF 32 BE
+            // EF BB BF    --> UTF 8
+            // FE FF       --> UTF 16 BE
+            // FF FE       --> UTF 16 LE
+            // FF FE 00 00 --> UTF 32 LE
+
+            switch (buf[0])
+            {
+            case (byte)0x00: // UTF32 BE
+                encoding = match(UTF32BE_BOM, null);
+                break;
+            case (byte)0xef: // UTF8
+                encoding = match(UTF8_BOM, null);
+                break;
+            case (byte)0xfe: // UTF16 BE
+                encoding = match(UTF16BE_BOM, null);
+                break;
+            case (byte)0xff: // UTF16/32 LE
+                encoding = match(UTF16LE_BOM, null);
+
+                if (encoding != null)
+                {
+                    encoding = match(UTF32LE_BOM, encoding);
+                }
+                break;
+
+            default:
+                encoding = null;
+                break;
+            }
+        }
+
+        pushback(encoding);
+
+        return (encoding != null) ? encoding.getEncoding() : null;
+    }
+
+    private final UnicodeBOM match(final UnicodeBOM matchEncoding, final UnicodeBOM noMatchEncoding)
+        throws IOException
+    {
+        byte [] bom = matchEncoding.getBytes();
+
+        for (int i = 0; i < bom.length; i++)
+        {
+            if (pos <= i) // Byte has not yet been read
+            {
+                if (!readByte())
+                {
+                    return noMatchEncoding;
+                }
+            }
+
+            if (bom[i] != buf[i])
+            {
+                return noMatchEncoding;
+            }
+        }
+
+        return matchEncoding;
+    }
+
+    private final boolean readByte()
+            throws IOException
+    {
+        int res = inputStream.read();
+        if (res == -1)
+        {
+            return false;
+        }
+
+        if (pos >= buf.length)
+        {
+            throw new IOException("BOM read error");
+        }
+
+        buf[pos++] = (byte) res;
+        return true;
+    }
+
+    private final void pushback(final UnicodeBOM matchBOM)
+        throws IOException
+    {
+        int count = pos; // By default, all bytes are pushed back.
+        int start = 0;
+
+        if (matchBOM != null && skipBOM)
+        {
+            // We have a match (some bytes are part of the BOM)
+            // and we want to skip the BOM. Push back only the bytes
+            // after the BOM.
+            start = matchBOM.getBytes().length;
+            count = (pos - start);
+
+            if (count < 0)
+            {
+                throw new IllegalStateException("Match has more bytes than available!");
+            }
+        }
+
+        inputStream.unread(buf, start, count);
+    }
+
+    /**
+     * @see java.io.InputStream#close()
+     */
+    public void close()
+        throws IOException
+    {
+        inputStream.close();
+    }
+
+    /**
+     * @see java.io.InputStream#available()
+     */
+    public int available()
+        throws IOException
+    {
+        return inputStream.available();
+    }
+
+    /**
+     * @see java.io.InputStream#mark(int)
+     */
+    public void mark(final int readlimit)
+    {
+        inputStream.mark(readlimit);
+    }
+
+    /**
+     * @see java.io.InputStream#markSupported()
+     */
+    public boolean markSupported()
+    {
+        return inputStream.markSupported();
+    }
+
+    /**
+     * @see java.io.InputStream#read()
+     */
+    public int read()
+        throws IOException
+    {
+        return inputStream.read();
+    }
+
+    /**
+     * @see java.io.InputStream#read(byte[])
+     */
+    public int read(final byte [] b)
+        throws IOException
+    {
+        return inputStream.read(b);
+    }
+
+    /**
+     * @see java.io.InputStream#read(byte[], int, int)
+     */
+    public int read(final byte [] b, final int off, final int len)
+        throws IOException
+    {
+        return inputStream.read(b, off, len);
+    }
+
+    /**
+     * @see java.io.InputStream#reset()
+     */
+    public void reset()
+        throws IOException
+    {
+        inputStream.reset();
+    }
+
+    /**
+     * @see java.io.InputStream#skip(long)
+     */
+    public long skip(final long n)
+        throws IOException
+    {
+        return inputStream.skip(n);
+    }
+
+    /**
+     * Helper class to bundle encoding and BOM marker.
+     *
+     * @author <a href="mailto:henning@apache.org">Henning P. Schmiedehausen</a>
+     * @version $Id$
+     */
+    static final class UnicodeBOM
+    {
+        private final String encoding;
+
+        private final byte [] bytes;
+
+        private UnicodeBOM(final String encoding, final byte [] bytes)
+        {
+            this.encoding = encoding;
+            this.bytes = bytes;
+        }
+
+        String getEncoding()
+        {
+            return encoding;
+        }
+
+        byte [] getBytes()
+        {
+            return bytes;
+        }
+    }
+}

Propchange: velocity/engine/trunk/src/java/org/apache/velocity/io/UnicodeInputStream.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/engine/trunk/src/java/org/apache/velocity/io/UnicodeInputStream.java
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision

Modified: velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/loader/FileResourceLoader.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/loader/FileResourceLoader.java?view=diff&rev=499441&r1=499440&r2=499441
==============================================================================
--- velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/loader/FileResourceLoader.java (original)
+++ velocity/engine/trunk/src/java/org/apache/velocity/runtime/resource/loader/FileResourceLoader.java Wed Jan 24 07:25:58 2007
@@ -16,13 +16,14 @@
  * "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.    
+ * under the License.
  */
 
 import java.io.BufferedInputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
+import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -32,6 +33,7 @@
 
 import org.apache.commons.collections.ExtendedProperties;
 import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.io.UnicodeInputStream;
 import org.apache.velocity.runtime.resource.Resource;
 import org.apache.velocity.util.StringUtils;
 
@@ -60,6 +62,9 @@
      */
     private Map templatePaths = Collections.synchronizedMap(new HashMap());
 
+    /** Shall we inspect unicode files to see what encoding they contain?. */
+    private boolean unicode = false;
+
     /**
      * @see org.apache.velocity.runtime.resource.loader.ResourceLoader#init(org.apache.commons.collections.ExtendedProperties)
      */
@@ -72,6 +77,16 @@
 
         paths.addAll( configuration.getVector("path") );
 
+        // unicode files may have a BOM marker at the start, but Java
+        // has problems recognizing the UTF-8 bom. Enabling unicode will
+        // recognize all unicode boms.
+        unicode = configuration.getBoolean("unicode", false);
+
+        if (log.isDebugEnabled())
+        {
+            log.debug("Do unicode file recognition:  " + unicode);
+        }
+
         // trim spaces from all paths
         StringUtils.trimStrings(paths);
         if (log.isInfoEnabled())
@@ -128,7 +143,16 @@
         for (int i = 0; i < size; i++)
         {
             String path = (String) paths.get(i);
-            InputStream inputStream = findTemplate(path, template);
+            InputStream inputStream = null;
+
+            try
+            {
+                inputStream = findTemplate(path, template);
+            }
+            catch (IOException ioe)
+            {
+                log.error("While loading Template " + template + ": ", ioe);
+            }
 
             if (inputStream != null)
             {
@@ -158,28 +182,78 @@
      * @return InputStream input stream that will be parsed
      *
      */
-    private InputStream findTemplate(String path, String template)
+    private InputStream findTemplate(final String path, final String template)
+        throws IOException
     {
         try
         {
             File file = getFile(path,template);
 
-            if ( file.canRead() )
+            if (file.canRead())
             {
-                return new BufferedInputStream(
-                    new FileInputStream(file.getAbsolutePath()));
+                FileInputStream fis = null;
+                try
+                {
+                    fis = new FileInputStream(file.getAbsolutePath());
+
+                    if (unicode)
+                    {
+                        UnicodeInputStream uis = null;
+
+                        try
+                        {
+                            uis = new UnicodeInputStream(fis, true);
+
+                            if (log.isDebugEnabled())
+                            {
+                                log.debug("File Encoding for " + file + " is: " + uis.getEncodingFromStream());
+                            }
+
+                            return new BufferedInputStream(uis);
+                        }
+                        catch(IOException e)
+                        {
+                            closeQuiet(uis);
+                            throw e;
+                        }
+                    }
+                    else
+                    {
+                        return new BufferedInputStream(fis);
+                    }
+                }
+                catch (IOException e)
+                {
+                    closeQuiet(fis);
+                    throw e;
+                }
             }
             else
             {
                 return null;
             }
         }
-        catch( FileNotFoundException fnfe )
+        catch(FileNotFoundException fnfe)
         {
             /*
              *  log and convert to a general Velocity ResourceNotFoundException
              */
             return null;
+        }
+    }
+
+    private void closeQuiet(final InputStream is)
+    {
+        if (is != null)
+        {
+            try
+            {
+                is.close();
+            }
+            catch(IOException ioe)
+            {
+                // Ignore
+            }
         }
     }
 

Added: velocity/engine/trunk/src/test/org/apache/velocity/io/UnicodeInputStreamTestCase.java
URL: http://svn.apache.org/viewvc/velocity/engine/trunk/src/test/org/apache/velocity/io/UnicodeInputStreamTestCase.java?view=auto&rev=499441
==============================================================================
--- velocity/engine/trunk/src/test/org/apache/velocity/io/UnicodeInputStreamTestCase.java (added)
+++ velocity/engine/trunk/src/test/org/apache/velocity/io/UnicodeInputStreamTestCase.java Wed Jan 24 07:25:58 2007
@@ -0,0 +1,226 @@
+package org.apache.velocity.io;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestSuite;
+
+import org.apache.commons.lang.ArrayUtils;
+
+
+/**
+ * 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.
+ *
+ * @author  $author$
+ * @version  $Revision$, $Date$
+ */
+public class UnicodeInputStreamTestCase
+    extends TestCase
+{
+
+    public UnicodeInputStreamTestCase(final String name)
+    {
+        super(name);
+    }
+
+    public static Test suite()
+    {
+        return new TestSuite(UnicodeInputStreamTestCase.class);
+    }
+
+    public void testSimpleStream()
+        throws Exception
+    {
+        testRun(null, "Ich bin zwei Oeltanks", "US-ASCII", true);
+        testRun(null, "Ich bin zwei Oeltanks", "US-ASCII", false);
+    }
+
+    public void testSimpleUTF8()
+        throws Exception
+    {
+        testRun(null, "Ich bin zwei Oeltanks", "UTF-8", true);
+        testRun(null, "Ich bin zwei Oeltanks", "UTF-8", false);
+    }
+
+    public void testRealUTF8()
+        throws Exception
+    {
+        testRun(null, "Ich bin zwei \u00d6ltanks", "UTF-8", true);
+        testRun(null, "Ich bin zwei \u00d6ltanks", "UTF-8", false);
+    }
+
+    public void testRealUTF8WithBOM()
+        throws Exception
+    {
+        testRun(UnicodeInputStream.UTF8_BOM, "Ich bin ein Test",
+                "UTF-8", true);
+        testRun(UnicodeInputStream.UTF8_BOM, "Ich bin ein Test",
+                "UTF-8", false);
+    }
+
+    public void testRealUTF16BEWithBOM()
+        throws Exception
+    {
+        testRun(UnicodeInputStream.UTF16BE_BOM, "Ich bin ein Test",
+                "UTF-16BE", true);
+        testRun(UnicodeInputStream.UTF16BE_BOM, "Ich bin ein Test",
+                "UTF-16BE", false);
+    }
+
+    public void testRealUTF16LEWithBOM()
+        throws Exception
+    {
+        testRun(UnicodeInputStream.UTF16LE_BOM, "Ich bin ein Test",
+                "UTF-16LE", true);
+        testRun(UnicodeInputStream.UTF16LE_BOM, "Ich bin ein Test",
+                "UTF-16LE", false);
+    }
+
+    public void testRealUTF32BEWithBOM()
+        throws Exception
+    {
+        testRun(UnicodeInputStream.UTF32BE_BOM, null,
+                "UTF-32BE", true);
+        testRun(UnicodeInputStream.UTF32BE_BOM, null,
+                "UTF-32BE", false);
+    }
+
+    public void testRealUTF32LEWithBOM()
+        throws Exception
+    {
+        testRun(UnicodeInputStream.UTF32LE_BOM, null,
+                "UTF-32LE", true);
+        testRun(UnicodeInputStream.UTF32LE_BOM, null,
+                "UTF-32LE", false);
+    }
+
+
+    protected void testRun(final UnicodeInputStream.UnicodeBOM bom, final String str, final String testEncoding, final boolean skipBOM)
+        throws Exception
+    {
+
+        byte [] testString = buildTestString(bom, str, testEncoding, skipBOM);
+
+        InputStream is = null;
+        UnicodeInputStream uis = null;
+
+        try
+        {
+            is = createInputStream(bom, str, testEncoding);
+            uis = new UnicodeInputStream(is, skipBOM);
+
+            assertEquals("BOM Skipping problem", skipBOM, uis.isSkipBOM());
+
+            if (bom != null)
+            {
+                assertEquals("Wrong Encoding detected", testEncoding, uis.getEncodingFromStream());
+            }
+
+            byte [] result = readAllBytes(uis, testEncoding);
+
+            assertNotNull(testString);
+            assertNotNull(result);
+            assertEquals("Wrong result length", testString.length, result.length);
+
+            for (int i = 0; i < result.length; i++)
+            {
+                assertEquals("Wrong Byte at " + i, testString[i], result[i]);
+            }
+        }
+        finally
+        {
+
+            if (uis != null)
+            {
+                uis.close();
+            }
+
+            if (is != null)
+            {
+                is.close();
+            }
+        }
+    }
+
+    protected InputStream createInputStream(final UnicodeInputStream.UnicodeBOM bom, final String str, final String enc)
+        throws Exception
+    {
+
+        if (bom == null)
+        {
+            if (str != null)
+            {
+                return new ByteArrayInputStream(str.getBytes(enc));
+            }
+            else
+            {
+                return new ByteArrayInputStream(new byte[0]);
+            }
+        }
+        else
+        {
+            if (str != null)
+            {
+                return new ByteArrayInputStream(ArrayUtils.addAll(bom.getBytes(), str.getBytes(enc)));
+            }
+            else
+            {
+                return new ByteArrayInputStream(ArrayUtils.addAll(bom.getBytes(), new byte[0]));
+            }
+        }
+    }
+
+    protected byte [] buildTestString(final UnicodeInputStream.UnicodeBOM bom, final String str, final String enc, final boolean skipBOM)
+        throws Exception
+    {
+
+        byte [] strBytes = (str != null) ? str.getBytes(enc) : new byte[0];
+
+        if ((bom == null) || skipBOM)
+        {
+                return strBytes;
+        }
+        else
+        {
+            return ArrayUtils.addAll(bom.getBytes(), strBytes);
+        }
+    }
+
+    protected byte [] readAllBytes(final InputStream inputStream, final String enc)
+        throws Exception
+    {
+        InputStreamReader isr = null;
+
+        byte [] res = new byte[0];
+
+        try
+        {
+            byte[] buf = new byte[1024];
+            int read = 0;
+
+            while ((read = inputStream.read(buf)) >= 0)
+            {
+                res = ArrayUtils.addAll(res, ArrayUtils.subarray(buf, 0, read));
+            }
+        }
+        finally
+        {
+
+            if (isr != null)
+            {
+                isr.close();
+            }
+        }
+
+        return res;
+    }
+
+}

Propchange: velocity/engine/trunk/src/test/org/apache/velocity/io/UnicodeInputStreamTestCase.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: velocity/engine/trunk/src/test/org/apache/velocity/io/UnicodeInputStreamTestCase.java
------------------------------------------------------------------------------
    svn:keywords = Id Author Date Revision